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