CaffGeek/MBACNationals

View on GitHub
Web.Admin/2014/wordpress/wp-admin/js/wp-fullscreen.js

Summary

Maintainability
F
3 days
Test Coverage
/* 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( '<span id="wp-fullscreen-title-placeholder">' );
            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();
                }
            });

            $document.on( 'keydown.wp-fullscreen', function( event ) {
                if ( 27 === event.which && s.visible ) { // Esc
                    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 );