/* global deleteUserSetting, setUserSetting, switchEditors, tinymce, tinyMCEPreInit */ /** * Distraction-Free Writing * (wp-fullscreen) * * Access the API globally using the window.wp.editor.fullscreen variable. */ ( function( $, window ) { var api, ps, s, toggleUI, uiTimer, PubSub, uiScrollTop = 0, transitionend = 'transitionend webkitTransitionEnd', $body = $( document.body ), $document = $( document ); /** * PubSub * * A lightweight publish/subscribe implementation. * * @access private */ PubSub = function() { this.topics = {}; this.subscribe = function( topic, callback ) { if ( ! this.topics[ topic ] ) this.topics[ topic ] = []; this.topics[ topic ].push( callback ); return callback; }; this.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; } }; this.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; }; }; // Initialize the fullscreen/api object api = {}; // Create the PubSub (publish/subscribe) interface. ps = api.pubsub = new PubSub(); s = api.settings = { // Settings visible: false, mode: 'tinymce', id: '', title_id: '', timer: 0, toolbar_shown: false }; function _hideUI() { $body.removeClass('wp-dfw-show-ui'); } /** * toggleUI * * Toggle the CSS class to show/hide the toolbar, borders and statusbar. */ toggleUI = api.toggleUI = function( show ) { clearTimeout( uiTimer ); if ( ! $body.hasClass('wp-dfw-show-ui') || show === 'show' ) { $body.addClass('wp-dfw-show-ui'); } else if ( show !== 'autohide' ) { $body.removeClass('wp-dfw-show-ui'); } if ( show === 'autohide' ) { uiTimer = setTimeout( _hideUI, 2000 ); } }; function resetCssPosition( add ) { s.$dfwWrap.parents().each( function( i, parent ) { var cssPosition, $parent = $(parent); if ( add ) { if ( parent.style.position ) { $parent.data( 'wp-dfw-css-position', parent.style.position ); } $parent.css( 'position', 'static' ); } else { cssPosition = $parent.data( 'wp-dfw-css-position' ); cssPosition = cssPosition || ''; $parent.css( 'position', cssPosition ); } if ( parent.nodeName === 'BODY' ) { return false; } }); } /** * on() * * Turns fullscreen on. * * @param string mode Optional. Switch to the given mode before opening. */ api.on = function() { var id, $dfwWrap, titleId; if ( s.visible ) { return; } if ( ! s.$fullscreenFader ) { api.ui.init(); } // Settings can be added or changed by defining "wp_fullscreen_settings" JS object. if ( typeof window.wp_fullscreen_settings === 'object' ) $.extend( s, window.wp_fullscreen_settings ); id = s.id || window.wpActiveEditor; if ( ! id ) { if ( s.hasTinymce ) { id = tinymce.activeEditor.id; } else { return; } } s.id = id; $dfwWrap = s.$dfwWrap = $( '#wp-' + id + '-wrap' ); if ( ! $dfwWrap.length ) { return; } s.$dfwTextarea = $( '#' + id ); s.$editorContainer = $dfwWrap.find( '.wp-editor-container' ); uiScrollTop = $document.scrollTop(); if ( s.hasTinymce ) { s.editor = tinymce.get( id ); } if ( s.editor && ! s.editor.isHidden() ) { s.origHeight = $( '#' + id + '_ifr' ).height(); s.mode = 'tinymce'; } else { s.origHeight = s.$dfwTextarea.height(); s.mode = 'html'; } // Try to find title field if ( typeof window.adminpage !== 'undefined' && ( window.adminpage === 'post-php' || window.adminpage === 'post-new-php' ) ) { titleId = 'title'; } else { titleId = id + '-title'; } s.$dfwTitle = $( '#' + titleId ); if ( ! s.$dfwTitle.length ) { s.$dfwTitle = null; } 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.hasTinymce || typeof switchEditors === 'undefined' ) { return from; } // Don't switch if the mode is the same. if ( from == to ) return from; if ( to === 'tinymce' && ! s.editor ) { s.editor = tinymce.get( s.id ); if ( ! s.editor && typeof tinyMCEPreInit !== 'undefined' && tinyMCEPreInit.mceInit && tinyMCEPreInit.mceInit[ s.id ] ) { // If the TinyMCE instance hasn't been created, set the "wp_fulscreen" flag on creating it tinyMCEPreInit.mceInit[ s.id ].wp_fullscreen = true; } } s.mode = to; switchEditors.go( s.id, to ); api.refreshButtons( true ); if ( to === 'html' ) { setTimeout( api.resizeTextarea, 200 ); } return to; }; /** * General */ api.save = function() { var $hidden = $('#hiddenaction'), oldVal = $hidden.val(), $spinner = $('#wp-fullscreen-save .spinner'), $saveMessage = $('#wp-fullscreen-save .wp-fullscreen-saved-message'), $errorMessage = $('#wp-fullscreen-save .wp-fullscreen-error-message'); $spinner.show(); $errorMessage.hide(); $saveMessage.hide(); $hidden.val('wp-fullscreen-save-post'); if ( s.editor && ! s.editor.isHidden() ) { s.editor.save(); } $.ajax({ url: window.ajaxurl, type: 'post', data: $('form#post').serialize(), dataType: 'json' }).done( function( response ) { $spinner.hide(); if ( response && response.success ) { $saveMessage.show(); setTimeout( function() { $saveMessage.fadeOut(300); }, 3000 ); if ( response.data && response.data.last_edited ) { $('#wp-fullscreen-save input').attr( 'title', response.data.last_edited ); } } else { $errorMessage.show(); } }).fail( function() { $spinner.hide(); $errorMessage.show(); }); $hidden.val( oldVal ); }; api.dfwWidth = function( pixels, total ) { var width; if ( pixels && pixels.toString().indexOf('%') !== -1 ) { s.$editorContainer.css( 'width', pixels ); s.$statusbar.css( 'width', pixels ); if ( s.$dfwTitle ) { s.$dfwTitle.css( 'width', pixels ); } return; } if ( ! pixels ) { // Reset to theme width width = $('#wp-fullscreen-body').data('theme-width') || 800; s.$editorContainer.width( width ); s.$statusbar.width( width ); if ( s.$dfwTitle ) { s.$dfwTitle.width( width - 16 ); } deleteUserSetting('dfw_width'); return; } if ( total ) { width = pixels; } else { width = s.$editorContainer.width(); width += pixels; } if ( width < 200 || width > 1200 ) { // sanity check return; } s.$editorContainer.width( width ); s.$statusbar.width( width ); if ( s.$dfwTitle ) { s.$dfwTitle.width( width - 16 ); } setUserSetting( 'dfw_width', width ); }; // This event occurs before the overlay blocks the UI. ps.subscribe( 'show', function() { var title = $('#last-edit').text(); if ( title ) { $('#wp-fullscreen-save input').attr( 'title', title ); } }); // This event occurs while the overlay blocks the UI. ps.subscribe( 'showing', function() { $body.addClass( 'wp-fullscreen-active' ); s.$dfwWrap.addClass( 'wp-fullscreen-wrap' ); if ( s.$dfwTitle ) { s.$dfwTitle.after( '' ); s.$dfwWrap.prepend( s.$dfwTitle.addClass('wp-fullscreen-title') ); } api.refreshButtons(); resetCssPosition( true ); $('#wpadminbar').hide(); // Show the UI for 2 sec. when opening toggleUI('autohide'); api.bind_resize(); if ( s.editor ) { s.editor.execCommand( 'wpFullScreenOn' ); } if ( 'ontouchstart' in window ) { api.dfwWidth( '90%' ); } else { api.dfwWidth( $( '#wp-fullscreen-body' ).data('dfw-width') || 800, true ); } // scroll to top so the user is not disoriented scrollTo(0, 0); }); // This event occurs after the overlay unblocks the UI ps.subscribe( 'shown', function() { s.visible = true; if ( s.editor && ! s.editor.isHidden() ) { s.editor.execCommand( 'wpAutoResize' ); } else { api.resizeTextarea( 'force' ); } }); ps.subscribe( 'hide', function() { // This event occurs before the overlay blocks DFW. $document.unbind( '.fullscreen' ); s.$dfwTextarea.unbind('.wp-dfw-resize'); }); ps.subscribe( 'hiding', function() { // This event occurs while the overlay blocks the DFW UI. $body.removeClass( 'wp-fullscreen-active' ); if ( s.$dfwTitle ) { $( '#wp-fullscreen-title-placeholder' ).before( s.$dfwTitle.removeClass('wp-fullscreen-title').css( 'width', '' ) ).remove(); } s.$dfwWrap.removeClass( 'wp-fullscreen-wrap' ); s.$editorContainer.css( 'width', '' ); s.$dfwTextarea.add( '#' + s.id + '_ifr' ).height( s.origHeight ); if ( s.editor ) { s.editor.execCommand( 'wpFullScreenOff' ); } resetCssPosition( false ); window.scrollTo( 0, uiScrollTop ); $('#wpadminbar').show(); }); // This event occurs after DFW is removed. ps.subscribe( 'hidden', function() { s.visible = false; }); api.refreshButtons = function( fade ) { if ( s.mode === 'html' ) { $('#wp-fullscreen-mode-bar').removeClass('wp-tmce-mode').addClass('wp-html-mode') .find('a').removeClass( 'active' ).filter('.wp-fullscreen-mode-html').addClass( 'active' ); 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') .find('a').removeClass( 'active' ).filter('.wp-fullscreen-mode-tinymce').addClass( 'active' ); 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 toolbar; s.toolbar = toolbar = $('#fullscreen-topbar'); s.$fullscreenFader = $('#fullscreen-fader'); s.$statusbar = $('#wp-fullscreen-status'); s.hasTinymce = typeof tinymce !== 'undefined'; if ( ! s.hasTinymce ) $('#wp-fullscreen-mode-bar').hide(); $document.keyup( function(e) { var c = e.keyCode || e.charCode, modKey; if ( ! s.visible ) { return; } if ( navigator.platform && navigator.platform.indexOf('Mac') !== -1 ) { modKey = e.ctrlKey; // Ctrl key for Mac } else { modKey = e.altKey; // Alt key for Win & Linux } if ( modKey && ( 61 === c || 107 === c || 187 === c ) ) { // + api.dfwWidth( 25 ); e.preventDefault(); } if ( modKey && ( 45 === c || 109 === c || 189 === c ) ) { // - api.dfwWidth( -25 ); e.preventDefault(); } if ( modKey && 48 === c ) { // 0 api.dfwWidth( 0 ); e.preventDefault(); } }); $( window ).on( 'keydown.wp-fullscreen', function( event ) { // Turn fullscreen off when Esc is pressed. if ( 27 === event.keyCode && s.visible ) { api.off(); event.stopImmediatePropagation(); } }); if ( 'ontouchstart' in window ) { $body.addClass('wp-dfw-touch'); } toolbar.on( 'mouseenter', function() { toggleUI('show'); }).on( 'mouseleave', function() { toggleUI('autohide'); }); // Bind buttons $('#wp-fullscreen-buttons').on( 'click.wp-fullscreen', 'button', function( event ) { var command = event.currentTarget.id ? event.currentTarget.id.substr(6) : null; if ( s.editor && 'tinymce' === s.mode ) { switch( command ) { case 'bold': s.editor.execCommand('Bold'); break; case 'italic': s.editor.execCommand('Italic'); break; case 'bullist': s.editor.execCommand('InsertUnorderedList'); break; case 'numlist': s.editor.execCommand('InsertOrderedList'); break; case 'link': s.editor.execCommand('WP_Link'); break; case 'unlink': s.editor.execCommand('unlink'); break; case 'help': s.editor.execCommand('WP_Help'); break; case 'blockquote': s.editor.execCommand('mceBlockQuote'); break; } } else if ( command === 'link' && window.wpLink ) { window.wpLink.open(); } if ( command === 'wp-media-library' && typeof wp !== 'undefined' && wp.media && wp.media.editor ) { wp.media.editor.open( s.id ); } }); }, fade: function( before, during, after ) { if ( ! s.$fullscreenFader ) { api.ui.init(); } // If any callback bound to before returns false, bail. if ( before && ! ps.publish( before ) ) { return; } api.fade.In( s.$fullscreenFader, 200, function() { if ( during ) { ps.publish( during ); } api.fade.Out( s.$fullscreenFader, 200, function() { if ( after ) { ps.publish( after ); } }); }); } }; api.fade = { // 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( 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( 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; }, // Check if the browser supports CSS 3.0 transitions transitions: ( function() { var style = document.documentElement.style; return ( typeof style.WebkitTransition === 'string' || typeof style.MozTransition === 'string' || typeof style.OTransition === 'string' || typeof style.transition === 'string' ); })() }; /** * Resize API * * Automatically updates textarea height. */ api.bind_resize = function() { s.$dfwTextarea.on( 'keydown.wp-dfw-resize click.wp-dfw-resize paste.wp-dfw-resize', function() { api.resizeTextarea(); }); }; api.resizeTextarea = function() { var node = s.$dfwTextarea[0]; if ( node.scrollHeight > node.clientHeight ) { node.style.height = node.scrollHeight + 50 + 'px'; } }; // Export window.wp = window.wp || {}; window.wp.editor = window.wp.editor || {}; window.wp.editor.fullscreen = api; })( jQuery, window );