]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/js/wp-fullscreen.js
WordPress 3.9.1
[autoinstalls/wordpress.git] / wp-admin / js / wp-fullscreen.js
1 /* global deleteUserSetting, setUserSetting, switchEditors, tinymce, tinyMCEPreInit */
2 /**
3  * Distraction Free Writing
4  * (wp-fullscreen)
5  *
6  * Access the API globally using the window.wp.editor.fullscreen variable.
7  */
8 ( function( $, window ) {
9         var api, ps, s, toggleUI, uiTimer, PubSub,
10                 uiScrollTop = 0,
11                 transitionend = 'transitionend webkitTransitionEnd',
12                 $body = $( document.body ),
13                 $document = $( document );
14
15         /**
16          * PubSub
17          *
18          * A lightweight publish/subscribe implementation.
19          *
20          * @access private
21          */
22         PubSub = function() {
23                 this.topics = {};
24
25                 this.subscribe = function( topic, callback ) {
26                         if ( ! this.topics[ topic ] )
27                                 this.topics[ topic ] = [];
28
29                         this.topics[ topic ].push( callback );
30                         return callback;
31                 };
32
33                 this.unsubscribe = function( topic, callback ) {
34                         var i, l,
35                                 topics = this.topics[ topic ];
36
37                         if ( ! topics )
38                                 return callback || [];
39
40                         // Clear matching callbacks
41                         if ( callback ) {
42                                 for ( i = 0, l = topics.length; i < l; i++ ) {
43                                         if ( callback == topics[i] )
44                                                 topics.splice( i, 1 );
45                                 }
46                                 return callback;
47
48                         // Clear all callbacks
49                         } else {
50                                 this.topics[ topic ] = [];
51                                 return topics;
52                         }
53                 };
54
55                 this.publish = function( topic, args ) {
56                         var i, l, broken,
57                                 topics = this.topics[ topic ];
58
59                         if ( ! topics )
60                                 return;
61
62                         args = args || [];
63
64                         for ( i = 0, l = topics.length; i < l; i++ ) {
65                                 broken = ( topics[i].apply( null, args ) === false || broken );
66                         }
67                         return ! broken;
68                 };
69         };
70
71         // Initialize the fullscreen/api object
72         api = {};
73
74         // Create the PubSub (publish/subscribe) interface.
75         ps = api.pubsub = new PubSub();
76
77         s = api.settings = { // Settings
78                 visible: false,
79                 mode: 'tinymce',
80                 id: '',
81                 title_id: '',
82                 timer: 0,
83                 toolbar_shown: false
84         };
85
86         function _hideUI() {
87                 $body.removeClass('wp-dfw-show-ui');
88         }
89
90         /**
91          * toggleUI
92          *
93          * Toggle the CSS class to show/hide the toolbar, borders and statusbar.
94          */
95         toggleUI = api.toggleUI = function( show ) {
96                 clearTimeout( uiTimer );
97
98                 if ( ! $body.hasClass('wp-dfw-show-ui') || show === 'show' ) {
99                         $body.addClass('wp-dfw-show-ui');
100                 } else if ( show !== 'autohide' ) {
101                         $body.removeClass('wp-dfw-show-ui');
102                 }
103
104                 if ( show === 'autohide' ) {
105                         uiTimer = setTimeout( _hideUI, 2000 );
106                 }
107         };
108
109         function resetCssPosition( add ) {
110                 s.$dfwWrap.parents().each( function( i, parent ) {
111                         var cssPosition, $parent = $(parent);
112
113                         if ( add ) {
114                                 if ( parent.style.position ) {
115                                         $parent.data( 'wp-dfw-css-position', parent.style.position );
116                                 }
117
118                                 $parent.css( 'position', 'static' );
119                         } else {
120                                 cssPosition = $parent.data( 'wp-dfw-css-position' );
121                                 cssPosition = cssPosition || '';
122                                 $parent.css( 'position', cssPosition );
123                         }
124
125                         if ( parent.nodeName === 'BODY' ) {
126                                 return false;
127                         }
128                 });
129         }
130
131         /**
132          * on()
133          *
134          * Turns fullscreen on.
135          *
136          * @param string mode Optional. Switch to the given mode before opening.
137          */
138         api.on = function() {
139                 var id, $dfwWrap, titleId;
140
141                 if ( s.visible ) {
142                         return;
143                 }
144
145                 if ( ! s.$fullscreenFader ) {
146                         api.ui.init();
147                 }
148
149                 // Settings can be added or changed by defining "wp_fullscreen_settings" JS object.
150                 if ( typeof window.wp_fullscreen_settings === 'object' )
151                         $.extend( s, window.wp_fullscreen_settings );
152
153                 id = s.id || window.wpActiveEditor;
154
155                 if ( ! id ) {
156                         if ( s.hasTinymce ) {
157                                 id = tinymce.activeEditor.id;
158                         } else {
159                                 return;
160                         }
161                 }
162
163                 s.id = id;
164                 $dfwWrap = s.$dfwWrap = $( '#wp-' + id + '-wrap' );
165
166                 if ( ! $dfwWrap.length ) {
167                         return;
168                 }
169
170                 s.$dfwTextarea = $( '#' + id );
171                 s.$editorContainer = $dfwWrap.find( '.wp-editor-container' );
172                 uiScrollTop = $document.scrollTop();
173
174                 if ( s.hasTinymce ) {
175                         s.editor = tinymce.get( id );
176                 }
177
178                 if ( s.editor && ! s.editor.isHidden() ) {
179                         s.origHeight = $( '#' + id + '_ifr' ).height();
180                         s.mode = 'tinymce';
181                 } else {
182                         s.origHeight = s.$dfwTextarea.height();
183                         s.mode = 'html';
184                 }
185
186                 // Try to find title field
187                 if ( typeof window.adminpage !== 'undefined' &&
188                         ( window.adminpage === 'post-php' || window.adminpage === 'post-new-php' ) ) {
189
190                         titleId = 'title';
191                 } else {
192                         titleId = id + '-title';
193                 }
194
195                 s.$dfwTitle = $( '#' + titleId );
196
197                 if ( ! s.$dfwTitle.length ) {
198                         s.$dfwTitle = null;
199                 }
200
201                 api.ui.fade( 'show', 'showing', 'shown' );
202         };
203
204         /**
205          * off()
206          *
207          * Turns fullscreen off.
208          */
209         api.off = function() {
210                 if ( ! s.visible )
211                         return;
212
213                 api.ui.fade( 'hide', 'hiding', 'hidden' );
214         };
215
216         /**
217          * switchmode()
218          *
219          * @return string - The current mode.
220          *
221          * @param string to - The fullscreen mode to switch to.
222          * @event switchMode
223          * @eventparam string to   - The new mode.
224          * @eventparam string from - The old mode.
225          */
226         api.switchmode = function( to ) {
227                 var from = s.mode;
228
229                 if ( ! to || ! s.visible || ! s.hasTinymce || typeof switchEditors === 'undefined' ) {
230                         return from;
231                 }
232
233                 // Don't switch if the mode is the same.
234                 if ( from == to )
235                         return from;
236
237                 if ( to === 'tinymce' && ! s.editor ) {
238                         s.editor = tinymce.get( s.id );
239
240                         if ( ! s.editor &&  typeof tinyMCEPreInit !== 'undefined' &&
241                                 tinyMCEPreInit.mceInit && tinyMCEPreInit.mceInit[ s.id ] ) {
242
243                                 // If the TinyMCE instance hasn't been created, set the "wp_fulscreen" flag on creating it
244                                 tinyMCEPreInit.mceInit[ s.id ].wp_fullscreen = true;
245                         }
246                 }
247
248                 s.mode = to;
249                 switchEditors.go( s.id, to );
250                 api.refreshButtons( true );
251
252                 if ( to === 'html' ) {
253                         setTimeout( api.resizeTextarea, 200 );
254                 }
255
256                 return to;
257         };
258
259         /**
260          * General
261          */
262
263         api.save = function() {
264                 var $hidden = $('#hiddenaction'),
265                         oldVal = $hidden.val(),
266                         $spinner = $('#wp-fullscreen-save .spinner'),
267                         $saveMessage = $('#wp-fullscreen-save .wp-fullscreen-saved-message'),
268                         $errorMessage = $('#wp-fullscreen-save .wp-fullscreen-error-message');
269
270                 $spinner.show();
271                 $errorMessage.hide();
272                 $saveMessage.hide();
273                 $hidden.val('wp-fullscreen-save-post');
274
275                 if ( s.editor && ! s.editor.isHidden() ) {
276                         s.editor.save();
277                 }
278
279                 $.ajax({
280                         url: window.ajaxurl,
281                         type: 'post',
282                         data: $('form#post').serialize(),
283                         dataType: 'json'
284                 }).done( function( response ) {
285                         $spinner.hide();
286
287                         if ( response && response.success ) {
288                                 $saveMessage.show();
289
290                                 setTimeout( function() {
291                                         $saveMessage.fadeOut(300);
292                                 }, 3000 );
293
294                                 if ( response.data && response.data.last_edited ) {
295                                         $('#wp-fullscreen-save input').attr( 'title',  response.data.last_edited );
296                                 }
297                         } else {
298                                 $errorMessage.show();
299                         }
300                 }).fail( function() {
301                         $spinner.hide();
302                         $errorMessage.show();
303                 });
304
305                 $hidden.val( oldVal );
306         };
307
308         api.dfwWidth = function( pixels, total ) {
309                 var width;
310
311                 if ( pixels && pixels.toString().indexOf('%') !== -1 ) {
312                         s.$editorContainer.css( 'width', pixels );
313                         s.$statusbar.css( 'width', pixels );
314
315                         if ( s.$dfwTitle ) {
316                                 s.$dfwTitle.css( 'width', pixels );
317                         }
318                         return;
319                 }
320
321                 if ( ! pixels ) {
322                         // Reset to theme width
323                         width = $('#wp-fullscreen-body').data('theme-width') || 800;
324                         s.$editorContainer.width( width );
325                         s.$statusbar.width( width );
326
327                         if ( s.$dfwTitle ) {
328                                 s.$dfwTitle.width( width - 16 );
329                         }
330
331                         deleteUserSetting('dfw_width');
332                         return;
333                 }
334
335                 if ( total ) {
336                         width = pixels;
337                 } else {
338                         width = s.$editorContainer.width();
339                         width += pixels;
340                 }
341
342                 if ( width < 200 || width > 1200 ) {
343                         // sanity check
344                         return;
345                 }
346
347                 s.$editorContainer.width( width );
348                 s.$statusbar.width( width );
349
350                 if ( s.$dfwTitle ) {
351                         s.$dfwTitle.width( width - 16 );
352                 }
353
354                 setUserSetting( 'dfw_width', width );
355         };
356
357         // This event occurs before the overlay blocks the UI.
358         ps.subscribe( 'show', function() {
359                 var title = $('#last-edit').text();
360
361                 if ( title ) {
362                         $('#wp-fullscreen-save input').attr( 'title', title );
363                 }
364         });
365
366         // This event occurs while the overlay blocks the UI.
367         ps.subscribe( 'showing', function() {
368                 $body.addClass( 'wp-fullscreen-active' );
369                 s.$dfwWrap.addClass( 'wp-fullscreen-wrap' );
370
371                 if ( s.$dfwTitle ) {
372                         s.$dfwTitle.after( '<span id="wp-fullscreen-title-placeholder">' );
373                         s.$dfwWrap.prepend( s.$dfwTitle.addClass('wp-fullscreen-title') );
374                 }
375
376                 api.refreshButtons();
377                 resetCssPosition( true );
378                 $('#wpadminbar').hide();
379
380                 // Show the UI for 2 sec. when opening
381                 toggleUI('autohide');
382
383                 api.bind_resize();
384
385                 if ( s.editor ) {
386                         s.editor.execCommand( 'wpFullScreenOn' );
387                 }
388
389                 if ( 'ontouchstart' in window ) {
390                         api.dfwWidth( '90%' );
391                 } else {
392                         api.dfwWidth( $( '#wp-fullscreen-body' ).data('dfw-width') || 800, true );
393                 }
394
395                 // scroll to top so the user is not disoriented
396                 scrollTo(0, 0);
397         });
398
399         // This event occurs after the overlay unblocks the UI
400         ps.subscribe( 'shown', function() {
401                 s.visible = true;
402
403                 if ( s.editor && ! s.editor.isHidden() ) {
404                         s.editor.execCommand( 'wpAutoResize' );
405                 } else {
406                         api.resizeTextarea( 'force' );
407                 }
408         });
409
410         ps.subscribe( 'hide', function() { // This event occurs before the overlay blocks DFW.
411                 $document.unbind( '.fullscreen' );
412                 s.$dfwTextarea.unbind('.wp-dfw-resize');
413         });
414
415         ps.subscribe( 'hiding', function() { // This event occurs while the overlay blocks the DFW UI.
416                 $body.removeClass( 'wp-fullscreen-active' );
417
418                 if ( s.$dfwTitle ) {
419                         $( '#wp-fullscreen-title-placeholder' ).before( s.$dfwTitle.removeClass('wp-fullscreen-title').css( 'width', '' ) ).remove();
420                 }
421
422                 s.$dfwWrap.removeClass( 'wp-fullscreen-wrap' );
423                 s.$editorContainer.css( 'width', '' );
424                 s.$dfwTextarea.add( '#' + s.id + '_ifr' ).height( s.origHeight );
425
426                 if ( s.editor ) {
427                         s.editor.execCommand( 'wpFullScreenOff' );
428                 }
429
430                 resetCssPosition( false );
431
432                 window.scrollTo( 0, uiScrollTop );
433                 $('#wpadminbar').show();
434         });
435
436         // This event occurs after DFW is removed.
437         ps.subscribe( 'hidden', function() {
438                 s.visible = false;
439         });
440
441         api.refreshButtons = function( fade ) {
442                 if ( s.mode === 'html' ) {
443                         $('#wp-fullscreen-mode-bar').removeClass('wp-tmce-mode').addClass('wp-html-mode')
444                                 .find('a').removeClass( 'active' ).filter('.wp-fullscreen-mode-html').addClass( 'active' );
445
446                         if ( fade ) {
447                                 $('#wp-fullscreen-button-bar').fadeOut( 150, function(){
448                                         $(this).addClass('wp-html-mode').fadeIn( 150 );
449                                 });
450                         } else {
451                                 $('#wp-fullscreen-button-bar').addClass('wp-html-mode');
452                         }
453                 } else if ( s.mode === 'tinymce' ) {
454                         $('#wp-fullscreen-mode-bar').removeClass('wp-html-mode').addClass('wp-tmce-mode')
455                                 .find('a').removeClass( 'active' ).filter('.wp-fullscreen-mode-tinymce').addClass( 'active' );
456
457                         if ( fade ) {
458                                 $('#wp-fullscreen-button-bar').fadeOut( 150, function(){
459                                         $(this).removeClass('wp-html-mode').fadeIn( 150 );
460                                 });
461                         } else {
462                                 $('#wp-fullscreen-button-bar').removeClass('wp-html-mode');
463                         }
464                 }
465         };
466
467         /**
468          * UI Elements
469          *
470          * Used for transitioning between states.
471          */
472         api.ui = {
473                 init: function() {
474                         var toolbar;
475
476                         s.toolbar = toolbar = $('#fullscreen-topbar');
477                         s.$fullscreenFader = $('#fullscreen-fader');
478                         s.$statusbar = $('#wp-fullscreen-status');
479                         s.hasTinymce = typeof tinymce !== 'undefined';
480
481                         if ( ! s.hasTinymce )
482                                 $('#wp-fullscreen-mode-bar').hide();
483
484                         $document.keyup( function(e) {
485                                 var c = e.keyCode || e.charCode, modKey;
486
487                                 if ( ! s.visible ) {
488                                         return;
489                                 }
490
491                                 if ( navigator.platform && navigator.platform.indexOf('Mac') !== -1 ) {
492                                         modKey = e.ctrlKey; // Ctrl key for Mac
493                                 } else {
494                                         modKey = e.altKey; // Alt key for Win & Linux
495                                 }
496
497                                 if ( modKey && ( 61 === c || 107 === c || 187 === c ) ) { // +
498                                         api.dfwWidth( 25 );
499                                         e.preventDefault();
500                                 }
501
502                                 if ( modKey && ( 45 === c || 109 === c || 189 === c ) ) { // -
503                                         api.dfwWidth( -25 );
504                                         e.preventDefault();
505                                 }
506
507                                 if ( modKey && 48 === c ) { // 0
508                                         api.dfwWidth( 0 );
509                                         e.preventDefault();
510                                 }
511                         });
512
513                         $document.on( 'keydown.wp-fullscreen', function( event ) {
514                                 if ( 27 === event.which && s.visible ) { // Esc
515                                         api.off();
516                                         event.stopImmediatePropagation();
517                                 }
518                         });
519
520                         if ( 'ontouchstart' in window ) {
521                                 $body.addClass('wp-dfw-touch');
522                         }
523
524                         toolbar.on( 'mouseenter', function() {
525                                 toggleUI('show');
526                         }).on( 'mouseleave', function() {
527                                 toggleUI('autohide');
528                         });
529
530                         // Bind buttons
531                         $('#wp-fullscreen-buttons').on( 'click.wp-fullscreen', 'button', function( event ) {
532                                 var command = event.currentTarget.id ? event.currentTarget.id.substr(6) : null;
533
534                                 if ( s.editor && 'tinymce' === s.mode ) {
535                                         switch( command ) {
536                                                 case 'bold':
537                                                         s.editor.execCommand('Bold');
538                                                         break;
539                                                 case 'italic':
540                                                         s.editor.execCommand('Italic');
541                                                         break;
542                                                 case 'bullist':
543                                                         s.editor.execCommand('InsertUnorderedList');
544                                                         break;
545                                                 case 'numlist':
546                                                         s.editor.execCommand('InsertOrderedList');
547                                                         break;
548                                                 case 'link':
549                                                         s.editor.execCommand('WP_Link');
550                                                         break;
551                                                 case 'unlink':
552                                                         s.editor.execCommand('unlink');
553                                                         break;
554                                                 case 'help':
555                                                         s.editor.execCommand('WP_Help');
556                                                         break;
557                                                 case 'blockquote':
558                                                         s.editor.execCommand('mceBlockQuote');
559                                                         break;
560                                         }
561                                 } else if ( command === 'link' && window.wpLink ) {
562                                         window.wpLink.open();
563                                 }
564
565                                 if ( command === 'wp-media-library' && typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
566                                         wp.media.editor.open( s.id );
567                                 }
568                         });
569                 },
570
571                 fade: function( before, during, after ) {
572                         if ( ! s.$fullscreenFader ) {
573                                 api.ui.init();
574                         }
575
576                         // If any callback bound to before returns false, bail.
577                         if ( before && ! ps.publish( before ) ) {
578                                 return;
579                         }
580
581                         api.fade.In( s.$fullscreenFader, 200, function() {
582                                 if ( during ) {
583                                         ps.publish( during );
584                                 }
585
586                                 api.fade.Out( s.$fullscreenFader, 200, function() {
587                                         if ( after ) {
588                                                 ps.publish( after );
589                                         }
590                                 });
591                         });
592                 }
593         };
594
595         api.fade = {
596                 // Sensitivity to allow browsers to render the blank element before animating.
597                 sensitivity: 100,
598
599                 In: function( element, speed, callback, stop ) {
600
601                         callback = callback || $.noop;
602                         speed = speed || 400;
603                         stop = stop || false;
604
605                         if ( api.fade.transitions ) {
606                                 if ( element.is(':visible') ) {
607                                         element.addClass( 'fade-trigger' );
608                                         return element;
609                                 }
610
611                                 element.show();
612                                 element.first().one( transitionend, function() {
613                                         callback();
614                                 });
615
616                                 setTimeout( function() { element.addClass( 'fade-trigger' ); }, this.sensitivity );
617                         } else {
618                                 if ( stop ) {
619                                         element.stop();
620                                 }
621
622                                 element.css( 'opacity', 1 );
623                                 element.first().fadeIn( speed, callback );
624
625                                 if ( element.length > 1 ) {
626                                         element.not(':first').fadeIn( speed );
627                                 }
628                         }
629
630                         return element;
631                 },
632
633                 Out: function( element, speed, callback, stop ) {
634
635                         callback = callback || $.noop;
636                         speed = speed || 400;
637                         stop = stop || false;
638
639                         if ( ! element.is(':visible') ) {
640                                 return element;
641                         }
642
643                         if ( api.fade.transitions ) {
644                                 element.first().one( transitionend, function() {
645                                         if ( element.hasClass('fade-trigger') ) {
646                                                 return;
647                                         }
648
649                                         element.hide();
650                                         callback();
651                                 });
652                                 setTimeout( function() { element.removeClass( 'fade-trigger' ); }, this.sensitivity );
653                         } else {
654                                 if ( stop ) {
655                                         element.stop();
656                                 }
657
658                                 element.first().fadeOut( speed, callback );
659
660                                 if ( element.length > 1 ) {
661                                         element.not(':first').fadeOut( speed );
662                                 }
663                         }
664
665                         return element;
666                 },
667
668                 // Check if the browser supports CSS 3.0 transitions
669                 transitions: ( function() {
670                         var style = document.documentElement.style;
671
672                         return ( typeof style.WebkitTransition === 'string' ||
673                                 typeof style.MozTransition === 'string' ||
674                                 typeof style.OTransition === 'string' ||
675                                 typeof style.transition === 'string' );
676                 })()
677         };
678
679         /**
680          * Resize API
681          *
682          * Automatically updates textarea height.
683          */
684         api.bind_resize = function() {
685                 s.$dfwTextarea.on( 'keydown.wp-dfw-resize click.wp-dfw-resize paste.wp-dfw-resize', function() {
686                         api.resizeTextarea();
687                 });
688         };
689
690         api.resizeTextarea = function() {
691                 var node = s.$dfwTextarea[0];
692
693                 if ( node.scrollHeight > node.clientHeight ) {
694                         node.style.height = node.scrollHeight + 50 + 'px';
695                 }
696         };
697
698         // Export
699         window.wp = window.wp || {};
700         window.wp.editor = window.wp.editor || {};
701         window.wp.editor.fullscreen = api;
702
703 })( jQuery, window );