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