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