/** * PubSub * * A lightweight publish/subscribe implementation. * Private use only! */ var PubSub, fullscreen, wptitlehint; PubSub = function() { this.topics = {}; }; PubSub.prototype.subscribe = function( topic, callback ) { if ( ! this.topics[ topic ] ) this.topics[ topic ] = []; this.topics[ topic ].push( callback ); return callback; }; PubSub.prototype.unsubscribe = function( topic, callback ) { var i, l, topics = this.topics[ topic ]; if ( ! topics ) return callback || []; // Clear matching callbacks if ( callback ) { for ( i = 0, l = topics.length; i < l; i++ ) { if ( callback == topics[i] ) topics.splice( i, 1 ); } return callback; // Clear all callbacks } else { this.topics[ topic ] = []; return topics; } }; PubSub.prototype.publish = function( topic, args ) { var i, l, broken, topics = this.topics[ topic ]; if ( ! topics ) return; args = args || []; for ( i = 0, l = topics.length; i < l; i++ ) { broken = ( topics[i].apply( null, args ) === false || broken ); } return ! broken; }; /** * Distraction Free Writing * (wp-fullscreen) * * Access the API globally using the fullscreen variable. */ (function($){ var api, ps, bounder, s; // Initialize the fullscreen/api object fullscreen = api = {}; // Create the PubSub (publish/subscribe) interface. ps = api.pubsub = new PubSub(); timer = 0; block = false; s = api.settings = { // Settings visible : false, mode : 'tinymce', editor_id : 'content', title_id : '', timer : 0, toolbar_shown : false } /** * Bounder * * Creates a function that publishes start/stop topics. * Used to throttle events. */ bounder = api.bounder = function( start, stop, delay, e ) { var y, top; delay = delay || 1250; if ( e ) { y = e.pageY || e.clientY || e.offsetY; top = $(document).scrollTop(); if ( !e.isDefaultPrevented ) // test if e ic jQuery normalized y = 135 + y; if ( y - top > 120 ) return; } if ( block ) return; block = true; setTimeout( function() { block = false; }, 400 ); if ( s.timer ) clearTimeout( s.timer ); else ps.publish( start ); function timed() { ps.publish( stop ); s.timer = 0; } s.timer = setTimeout( timed, delay ); }; /** * on() * * Turns fullscreen on. * * @param string mode Optional. Switch to the given mode before opening. */ api.on = function() { if ( s.visible ) return; // Settings can be added or changed by defining "wp_fullscreen_settings" JS object. if ( typeof(wp_fullscreen_settings) == 'object' ) $.extend( s, wp_fullscreen_settings ); s.editor_id = wpActiveEditor || 'content'; if ( $('input#title').length && s.editor_id == 'content' ) s.title_id = 'title'; else if ( $('input#' + s.editor_id + '-title').length ) // the title input field should have [editor_id]-title HTML ID to be auto detected s.title_id = s.editor_id + '-title'; else $('#wp-fullscreen-title, #wp-fullscreen-title-prompt-text').hide(); s.mode = $('#' + s.editor_id).is(':hidden') ? 'tinymce' : 'html'; s.qt_canvas = $('#' + s.editor_id).get(0); if ( ! s.element ) api.ui.init(); s.is_mce_on = s.has_tinymce && typeof( tinyMCE.get(s.editor_id) ) != 'undefined'; api.ui.fade( 'show', 'showing', 'shown' ); }; /** * off() * * Turns fullscreen off. */ api.off = function() { if ( ! s.visible ) return; api.ui.fade( 'hide', 'hiding', 'hidden' ); }; /** * switchmode() * * @return string - The current mode. * * @param string to - The fullscreen mode to switch to. * @event switchMode * @eventparam string to - The new mode. * @eventparam string from - The old mode. */ api.switchmode = function( to ) { var from = s.mode; if ( ! to || ! s.visible || ! s.has_tinymce ) return from; // Don't switch if the mode is the same. if ( from == to ) return from; ps.publish( 'switchMode', [ from, to ] ); s.mode = to; ps.publish( 'switchedMode', [ from, to ] ); return to; }; /** * General */ api.save = function() { var hidden = $('#hiddenaction'), old = hidden.val(), spinner = $('#wp-fullscreen-save .spinner'), message = $('#wp-fullscreen-save span'); spinner.show(); api.savecontent(); hidden.val('wp-fullscreen-save-post'); $.post( ajaxurl, $('form#post').serialize(), function(r){ spinner.hide(); message.show(); setTimeout( function(){ message.fadeOut(1000); }, 3000 ); if ( r.last_edited ) $('#wp-fullscreen-save input').attr( 'title', r.last_edited ); }, 'json'); hidden.val(old); } api.savecontent = function() { var ed, content; if ( s.title_id ) $('#' + s.title_id).val( $('#wp-fullscreen-title').val() ); if ( s.mode === 'tinymce' && (ed = tinyMCE.get('wp_mce_fullscreen')) ) { content = ed.save(); } else { content = $('#wp_mce_fullscreen').val(); } $('#' + s.editor_id).val( content ); $(document).triggerHandler('wpcountwords', [ content ]); } set_title_hint = function( title ) { if ( ! title.val().length ) title.siblings('label').css( 'visibility', '' ); else title.siblings('label').css( 'visibility', 'hidden' ); } api.dfw_width = function(n) { var el = $('#wp-fullscreen-wrap'), w = el.width(); if ( !n ) { // reset to theme width el.width( $('#wp-fullscreen-central-toolbar').width() ); deleteUserSetting('dfw_width'); return; } w = n + w; if ( w < 200 || w > 1200 ) // sanity check return; el.width( w ); setUserSetting('dfw_width', w); } ps.subscribe( 'showToolbar', function() { s.toolbars.removeClass('fade-1000').addClass('fade-300'); api.fade.In( s.toolbars, 300, function(){ ps.publish('toolbarShown'); }, true ); $('#wp-fullscreen-body').addClass('wp-fullscreen-focus'); s.toolbar_shown = true; }); ps.subscribe( 'hideToolbar', function() { s.toolbars.removeClass('fade-300').addClass('fade-1000'); api.fade.Out( s.toolbars, 1000, function(){ ps.publish('toolbarHidden'); }, true ); $('#wp-fullscreen-body').removeClass('wp-fullscreen-focus'); }); ps.subscribe( 'toolbarShown', function() { s.toolbars.removeClass('fade-300'); }); ps.subscribe( 'toolbarHidden', function() { s.toolbars.removeClass('fade-1000'); s.toolbar_shown = false; }); ps.subscribe( 'show', function() { // This event occurs before the overlay blocks the UI. var title; if ( s.title_id ) { title = $('#wp-fullscreen-title').val( $('#' + s.title_id).val() ); set_title_hint( title ); } $('#wp-fullscreen-save input').attr( 'title', $('#last-edit').text() ); s.textarea_obj.value = s.qt_canvas.value; if ( s.has_tinymce && s.mode === 'tinymce' ) tinyMCE.execCommand('wpFullScreenInit'); s.orig_y = $(window).scrollTop(); }); ps.subscribe( 'showing', function() { // This event occurs while the DFW overlay blocks the UI. $( document.body ).addClass( 'fullscreen-active' ); api.refresh_buttons(); $( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } ); bounder( 'showToolbar', 'hideToolbar', 2000 ); api.bind_resize(); setTimeout( api.resize_textarea, 200 ); // scroll to top so the user is not disoriented scrollTo(0, 0); // needed it for IE7 and compat mode $('#wpadminbar').hide(); }); ps.subscribe( 'shown', function() { // This event occurs after the DFW overlay is shown var interim_init; s.visible = true; // init the standard TinyMCE instance if missing if ( s.has_tinymce && ! s.is_mce_on ) { interim_init = function(mce, ed) { var el = ed.getElement(), old_val = el.value, settings = tinyMCEPreInit.mceInit[s.editor_id]; if ( settings && settings.wpautop && typeof(switchEditors) != 'undefined' ) el.value = switchEditors.wpautop( el.value ); ed.onInit.add(function(ed) { ed.hide(); ed.getElement().value = old_val; tinymce.onAddEditor.remove(interim_init); }); }; tinymce.onAddEditor.add(interim_init); tinyMCE.init(tinyMCEPreInit.mceInit[s.editor_id]); s.is_mce_on = true; } wpActiveEditor = 'wp_mce_fullscreen'; }); ps.subscribe( 'hide', function() { // This event occurs before the overlay blocks DFW. var htmled_is_hidden = $('#' + s.editor_id).is(':hidden'); // Make sure the correct editor is displaying. if ( s.has_tinymce && s.mode === 'tinymce' && !htmled_is_hidden ) { switchEditors.go(s.editor_id, 'tmce'); } else if ( s.mode === 'html' && htmled_is_hidden ) { switchEditors.go(s.editor_id, 'html'); } // Save content must be after switchEditors or content will be overwritten. See #17229. api.savecontent(); $( document ).unbind( '.fullscreen' ); $(s.textarea_obj).unbind('.grow'); if ( s.has_tinymce && s.mode === 'tinymce' ) tinyMCE.execCommand('wpFullScreenSave'); if ( s.title_id ) set_title_hint( $('#' + s.title_id) ); s.qt_canvas.value = s.textarea_obj.value; }); ps.subscribe( 'hiding', function() { // This event occurs while the overlay blocks the DFW UI. $( document.body ).removeClass( 'fullscreen-active' ); scrollTo(0, s.orig_y); $('#wpadminbar').show(); }); ps.subscribe( 'hidden', function() { // This event occurs after DFW is removed. s.visible = false; $('#wp_mce_fullscreen, #wp-fullscreen-title').removeAttr('style'); if ( s.has_tinymce && s.is_mce_on ) tinyMCE.execCommand('wpFullScreenClose'); s.textarea_obj.value = ''; api.oldheight = 0; wpActiveEditor = s.editor_id; }); ps.subscribe( 'switchMode', function( from, to ) { var ed; if ( !s.has_tinymce || !s.is_mce_on ) return; ed = tinyMCE.get('wp_mce_fullscreen'); if ( from === 'html' && to === 'tinymce' ) { if ( tinyMCE.get(s.editor_id).getParam('wpautop') && typeof(switchEditors) != 'undefined' ) s.textarea_obj.value = switchEditors.wpautop( s.textarea_obj.value ); if ( 'undefined' == typeof(ed) ) tinyMCE.execCommand('wpFullScreenInit'); else ed.show(); } else if ( from === 'tinymce' && to === 'html' ) { if ( ed ) ed.hide(); } }); ps.subscribe( 'switchedMode', function( from, to ) { api.refresh_buttons(true); if ( to === 'html' ) setTimeout( api.resize_textarea, 200 ); }); /** * Buttons */ api.b = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('Bold'); } api.i = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('Italic'); } api.ul = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('InsertUnorderedList'); } api.ol = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('InsertOrderedList'); } api.link = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('WP_Link'); else wpLink.open(); } api.unlink = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('unlink'); } api.atd = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('mceWritingImprovementTool'); } api.help = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('WP_Help'); } api.blockquote = function() { if ( s.has_tinymce && 'tinymce' === s.mode ) tinyMCE.execCommand('mceBlockQuote'); } api.medialib = function() { if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) wp.media.editor.open(s.editor_id); } api.refresh_buttons = function( fade ) { fade = fade || false; if ( s.mode === 'html' ) { $('#wp-fullscreen-mode-bar').removeClass('wp-tmce-mode').addClass('wp-html-mode'); if ( fade ) $('#wp-fullscreen-button-bar').fadeOut( 150, function(){ $(this).addClass('wp-html-mode').fadeIn( 150 ); }); else $('#wp-fullscreen-button-bar').addClass('wp-html-mode'); } else if ( s.mode === 'tinymce' ) { $('#wp-fullscreen-mode-bar').removeClass('wp-html-mode').addClass('wp-tmce-mode'); if ( fade ) $('#wp-fullscreen-button-bar').fadeOut( 150, function(){ $(this).removeClass('wp-html-mode').fadeIn( 150 ); }); else $('#wp-fullscreen-button-bar').removeClass('wp-html-mode'); } } /** * UI Elements * * Used for transitioning between states. */ api.ui = { init: function() { var topbar = $('#fullscreen-topbar'), txtarea = $('#wp_mce_fullscreen'), last = 0; s.toolbars = topbar.add( $('#wp-fullscreen-status') ); s.element = $('#fullscreen-fader'); s.textarea_obj = txtarea[0]; s.has_tinymce = typeof(tinymce) != 'undefined'; if ( !s.has_tinymce ) $('#wp-fullscreen-mode-bar').hide(); if ( wptitlehint && $('#wp-fullscreen-title').length ) wptitlehint('wp-fullscreen-title'); $(document).keyup(function(e){ var c = e.keyCode || e.charCode, a, data; if ( !fullscreen.settings.visible ) return true; if ( navigator.platform && navigator.platform.indexOf('Mac') != -1 ) a = e.ctrlKey; // Ctrl key for Mac else a = e.altKey; // Alt key for Win & Linux if ( 27 == c ) { // Esc data = { event: e, what: 'dfw', cb: fullscreen.off, condition: function(){ if ( $('#TB_window').is(':visible') || $('.wp-dialog').is(':visible') ) return false; return true; } }; if ( ! jQuery(document).triggerHandler( 'wp_CloseOnEscape', [data] ) ) fullscreen.off(); } if ( a && (61 == c || 107 == c || 187 == c) ) // + api.dfw_width(25); if ( a && (45 == c || 109 == c || 189 == c) ) // - api.dfw_width(-25); if ( a && 48 == c ) // 0 api.dfw_width(0); return false; }); // word count in Text mode if ( typeof(wpWordCount) != 'undefined' ) { txtarea.keyup( function(e) { var k = e.keyCode || e.charCode; if ( k == last ) return true; if ( 13 == k || 8 == last || 46 == last ) $(document).triggerHandler('wpcountwords', [ txtarea.val() ]); last = k; return true; }); } topbar.mouseenter(function(e){ s.toolbars.addClass('fullscreen-make-sticky'); $( document ).unbind( '.fullscreen' ); clearTimeout( s.timer ); s.timer = 0; }).mouseleave(function(e){ s.toolbars.removeClass('fullscreen-make-sticky'); if ( s.visible ) $( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } ); }); }, fade: function( before, during, after ) { if ( ! s.element ) api.ui.init(); // If any callback bound to before returns false, bail. if ( before && ! ps.publish( before ) ) return; api.fade.In( s.element, 600, function() { if ( during ) ps.publish( during ); api.fade.Out( s.element, 600, function() { if ( after ) ps.publish( after ); }) }); } }; api.fade = { transitionend: 'transitionend webkitTransitionEnd oTransitionEnd', // Sensitivity to allow browsers to render the blank element before animating. sensitivity: 100, In: function( element, speed, callback, stop ) { callback = callback || $.noop; speed = speed || 400; stop = stop || false; if ( api.fade.transitions ) { if ( element.is(':visible') ) { element.addClass( 'fade-trigger' ); return element; } element.show(); element.first().one( this.transitionend, function() { callback(); }); setTimeout( function() { element.addClass( 'fade-trigger' ); }, this.sensitivity ); } else { if ( stop ) element.stop(); element.css( 'opacity', 1 ); element.first().fadeIn( speed, callback ); if ( element.length > 1 ) element.not(':first').fadeIn( speed ); } return element; }, Out: function( element, speed, callback, stop ) { callback = callback || $.noop; speed = speed || 400; stop = stop || false; if ( ! element.is(':visible') ) return element; if ( api.fade.transitions ) { element.first().one( api.fade.transitionend, function() { if ( element.hasClass('fade-trigger') ) return; element.hide(); callback(); }); setTimeout( function() { element.removeClass( 'fade-trigger' ); }, this.sensitivity ); } else { if ( stop ) element.stop(); element.first().fadeOut( speed, callback ); if ( element.length > 1 ) element.not(':first').fadeOut( speed ); } return element; }, transitions: (function() { // Check if the browser supports CSS 3.0 transitions var s = document.documentElement.style; return ( typeof ( s.WebkitTransition ) == 'string' || typeof ( s.MozTransition ) == 'string' || typeof ( s.OTransition ) == 'string' || typeof ( s.transition ) == 'string' ); })() }; /** * Resize API * * Automatically updates textarea height. */ api.bind_resize = function() { $(s.textarea_obj).bind('keypress.grow click.grow paste.grow', function(){ setTimeout( api.resize_textarea, 200 ); }); } api.oldheight = 0; api.resize_textarea = function() { var txt = s.textarea_obj, newheight; newheight = txt.scrollHeight > 300 ? txt.scrollHeight : 300; if ( newheight != api.oldheight ) { txt.style.height = newheight + 'px'; api.oldheight = newheight; } }; })(jQuery);