WordPress.org

Make WordPress Core

Ticket #32425: 32425.3.patch

File 32425.3.patch, 82.7 KB (added by azaozz, 6 years ago)
  • src/wp-admin/css/edit.css

     
    425425        visibility: visible !important;
    426426}
    427427
     428.mce-fullscreen #wp-content-wrap .mce-tinymce .mce-wp-dfw {
     429        display: none;
     430}
     431
    428432.post-php.mce-fullscreen #wpadminbar,
    429433.mce-fullscreen #wp-content-wrap .mce-wp-dfw {
    430434        display: none;
  • src/wp-admin/js/editor.js

     
    1 /* global tinymce, tinyMCEPreInit, QTags, setUserSetting */
    21
    3 window.switchEditors = {
     2( function( $ ) {
     3        function SwitchEditors() {
     4                var tinymce, $$,
     5                        exports = {};
    46
    5         switchto: function( el ) {
    6                 var aid = el.id,
    7                         l = aid.length,
    8                         id = aid.substr( 0, l - 5 ),
    9                         mode = aid.substr( l - 4 );
     7                function init() {
     8                        if ( ! tinymce && window.tinymce ) {
     9                                tinymce = window.tinymce;
     10                                $$ = tinymce.$;
    1011
    11                 this.go( id, mode );
    12         },
     12                                $$( document ).on( 'click', function( event ) {
     13                                        var id, mode,
     14                                                target = $$( event.target );
    1315
    14         // mode can be 'html', 'tmce', or 'toggle'; 'html' is used for the 'Text' editor tab.
    15         go: function( id, mode ) {
    16                 var t = this, ed, wrap_id, txtarea_el, iframe, editorHeight, toolbarHeight,
    17                         DOM = tinymce.DOM; //DOMUtils outside the editor iframe
    18 
    19                 id = id || 'content';
    20                 mode = mode || 'toggle';
    21 
    22                 ed = tinymce.get( id );
    23                 wrap_id = 'wp-' + id + '-wrap';
    24                 txtarea_el = DOM.get( id );
    25 
    26                 if ( 'toggle' === mode ) {
    27                         if ( ed && ! ed.isHidden() ) {
    28                                 mode = 'html';
    29                         } else {
    30                                 mode = 'tmce';
     16                                        if ( target.hasClass( 'wp-switch-editor' ) ) {
     17                                                id = target.attr( 'data-wp-editor-id' );
     18                                                mode = target.hasClass( 'switch-tmce' ) ? 'tmce' : 'html';
     19                                                switchEditor( id, mode );
     20                                        }
     21                                });
    3122                        }
    3223                }
    3324
    34                 function getToolbarHeight() {
    35                         var node = DOM.select( '.mce-toolbar-grp', ed.getContainer() )[0],
     25                function getToolbarHeight( editor ) {
     26                        var node = $$( '.mce-toolbar-grp', editor.getContainer() )[0],
    3627                                height = node && node.clientHeight;
    3728
    3829                        if ( height && height > 10 && height < 200 ) {
     
    4233                        return 30;
    4334                }
    4435
    45                 if ( 'tmce' === mode || 'tinymce' === mode ) {
    46                         if ( ed && ! ed.isHidden() ) {
    47                                 return false;
    48                         }
     36                function switchEditor( id, mode ) {
     37                        id = id || 'content';
     38                        mode = mode || 'toggle';
    4939
    50                         if ( typeof( QTags ) !== 'undefined' ) {
    51                                 QTags.closeAllTags( id );
     40                        var editorHeight, toolbarHeight, iframe,
     41                                editor = tinymce.get( id ),
     42                                wrap = $$( '#wp-' + id + '-wrap' ),
     43                                $textarea = $$( '#' + id ),
     44                                textarea = $textarea[0];
     45
     46                        if ( 'toggle' === mode ) {
     47                                if ( editor && ! editor.isHidden() ) {
     48                                        mode = 'html';
     49                                } else {
     50                                        mode = 'tmce';
     51                                }
    5252                        }
    5353
    54                         editorHeight = txtarea_el ? parseInt( txtarea_el.style.height, 10 ) : 0;
     54                        if ( 'tmce' === mode || 'tinymce' === mode ) {
     55                                if ( editor && ! editor.isHidden() ) {
     56                                        return false;
     57                                }
    5558
    56                         if ( tinyMCEPreInit.mceInit[ id ] && tinyMCEPreInit.mceInit[ id ].wpautop ) {
    57                                 txtarea_el.value = t.wpautop( txtarea_el.value );
    58                         }
     59                                if ( typeof( window.QTags ) !== 'undefined' ) {
     60                                        window.QTags.closeAllTags( id );
     61                                }
    5962
    60                         if ( ed ) {
    61                                 ed.show();
     63                                editorHeight = parseInt( textarea.style.height, 10 ) || 0;
    6264
    63                                 // No point resizing the iframe in iOS
    64                                 if ( ! tinymce.Env.iOS && editorHeight ) {
    65                                         toolbarHeight = getToolbarHeight();
    66                                         editorHeight = editorHeight - toolbarHeight + 14;
     65                                if ( editor ) {
     66                                        editor.show();
    6767
    68                                         // height cannot be under 50 or over 5000
    69                                         if ( editorHeight > 50 && editorHeight < 5000 ) {
    70                                                 ed.theme.resizeTo( null, editorHeight );
     68                                        // No point resizing the iframe in iOS
     69                                        if ( ! tinymce.Env.iOS && editorHeight ) {
     70                                                toolbarHeight = getToolbarHeight( editor );
     71                                                editorHeight = editorHeight - toolbarHeight + 14;
     72
     73                                                // height cannot be under 50 or over 5000
     74                                                if ( editorHeight > 50 && editorHeight < 5000 ) {
     75                                                        editor.theme.resizeTo( null, editorHeight );
     76                                                }
    7177                                        }
     78                                } else {
     79                                        tinymce.init( window.tinyMCEPreInit.mceInit[id] );
    7280                                }
    73                         } else {
    74                                 tinymce.init( tinyMCEPreInit.mceInit[id] );
    75                         }
    7681
    77                         DOM.removeClass( wrap_id, 'html-active' );
    78                         DOM.addClass( wrap_id, 'tmce-active' );
    79                         DOM.setAttrib( txtarea_el, 'aria-hidden', true );
    80                         setUserSetting( 'editor', 'tinymce' );
     82                                wrap.removeClass( 'html-active' ).addClass( 'tmce-active' );
     83                                $textarea.attr( 'aria-hidden', true );
     84                                window.setUserSetting( 'editor', 'tinymce' );
    8185
    82                 } else if ( 'html' === mode ) {
     86                        } else if ( 'html' === mode ) {
     87                                if ( editor && editor.isHidden() ) {
     88                                        return false;
     89                                }
    8390
    84                         if ( ed && ed.isHidden() ) {
    85                                 return false;
    86                         }
     91                                if ( editor ) {
     92                                        if ( ! tinymce.Env.iOS ) {
     93                                                iframe = editor.iframeElement;
     94                                                editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
    8795
    88                         if ( ed ) {
    89                                 if ( ! tinymce.Env.iOS ) {
    90                                         iframe = DOM.get( id + '_ifr' );
    91                                         editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
     96                                                if ( editorHeight ) {
     97                                                        toolbarHeight = getToolbarHeight( editor );
     98                                                        editorHeight = editorHeight + toolbarHeight - 14;
    9299
    93                                         if ( editorHeight ) {
    94                                                 toolbarHeight = getToolbarHeight();
    95                                                 editorHeight = editorHeight + toolbarHeight - 14;
    96 
    97                                                 // height cannot be under 50 or over 5000
    98                                                 if ( editorHeight > 50 && editorHeight < 5000 ) {
    99                                                         txtarea_el.style.height = editorHeight + 'px';
     100                                                        // height cannot be under 50 or over 5000
     101                                                        if ( editorHeight > 50 && editorHeight < 5000 ) {
     102                                                                textarea.style.height = editorHeight + 'px';
     103                                                        }
    100104                                                }
    101105                                        }
    102                                 }
    103106
    104                                 ed.hide();
    105                         } else {
    106                                 // The TinyMCE instance doesn't exist, run the content through 'pre_wpautop()' and show the textarea
    107                                 if ( tinyMCEPreInit.mceInit[ id ] && tinyMCEPreInit.mceInit[ id ].wpautop ) {
    108                                         txtarea_el.value = t.pre_wpautop( txtarea_el.value );
     107                                        editor.hide();
     108                                } else {
     109                                        // The TinyMCE instance doesn't exist, show the textarea
     110                                        $textarea.css({ 'display': '', 'visibility': '' });
    109111                                }
    110112
    111                                 DOM.setStyles( txtarea_el, {'display': '', 'visibility': ''} );
     113                                wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
     114                                $textarea.attr( 'aria-hidden', false );
     115                                window.setUserSetting( 'editor', 'html' );
    112116                        }
    113 
    114                         DOM.removeClass( wrap_id, 'tmce-active' );
    115                         DOM.addClass( wrap_id, 'html-active' );
    116                         DOM.setAttrib( txtarea_el, 'aria-hidden', false );
    117                         setUserSetting( 'editor', 'html' );
    118117                }
    119                 return false;
    120         },
    121118
    122         _wp_Nop: function( content ) {
    123                 var blocklist1, blocklist2,
    124                         preserve_linebreaks = false,
    125                         preserve_br = false;
     119                // Replace paragraphs with double line breaks
     120                function removep( html ) {
     121                        var blocklist = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|h[1-6]|fieldset',
     122                                blocklist1 = blocklist + '|div|p',
     123                                blocklist2 = blocklist + '|pre',
     124                                preserve_linebreaks = false,
     125                                preserve_br = false;
    126126
    127                 // Protect pre|script tags
    128                 if ( content.indexOf( '<pre' ) !== -1 || content.indexOf( '<script' ) !== -1 ) {
    129                         preserve_linebreaks = true;
    130                         content = content.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
    131                                 a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
    132                                 a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
    133                                 return a.replace( /\r?\n/g, '<wp-line-break>' );
    134                         });
    135                 }
     127                        if ( ! html ) {
     128                                return '';
     129                        }
    136130
    137                 // keep <br> tags inside captions and remove line breaks
    138                 if ( content.indexOf( '[caption' ) !== -1 ) {
    139                         preserve_br = true;
    140                         content = content.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
    141                                 return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' );
    142                         });
    143                 }
     131                        // Protect pre|script tags
     132                        if ( html.indexOf( '<pre' ) !== -1 || html.indexOf( '<script' ) !== -1 ) {
     133                                preserve_linebreaks = true;
     134                                html = html.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     135                                        a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
     136                                        a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
     137                                        return a.replace( /\r?\n/g, '<wp-line-break>' );
     138                                });
     139                        }
    144140
    145                 // Pretty it up for the source editor
    146                 blocklist1 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|div|h[1-6]|p|fieldset';
    147                 content = content.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' );
    148                 content = content.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' );
     141                        // keep <br> tags inside captions and remove line breaks
     142                        if ( html.indexOf( '[caption' ) !== -1 ) {
     143                                preserve_br = true;
     144                                html = html.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     145                                        return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' );
     146                                });
     147                        }
    149148
    150                 // Mark </p> if it has any attributes.
    151                 content = content.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
     149                        // Pretty it up for the source editor
     150                        html = html.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' );
     151                        html = html.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' );
    152152
    153                 // Separate <div> containing <p>
    154                 content = content.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
     153                        // Mark </p> if it has any attributes.
     154                        html = html.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
    155155
    156                 // Remove <p> and <br />
    157                 content = content.replace( /\s*<p>/gi, '' );
    158                 content = content.replace( /\s*<\/p>\s*/gi, '\n\n' );
    159                 content = content.replace( /\n[\s\u00a0]+\n/g, '\n\n' );
    160                 content = content.replace( /\s*<br ?\/?>\s*/gi, '\n' );
     156                        // Separate <div> containing <p>
     157                        html = html.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
    161158
    162                 // Fix some block element newline issues
    163                 content = content.replace( /\s*<div/g, '\n<div' );
    164                 content = content.replace( /<\/div>\s*/g, '</div>\n' );
    165                 content = content.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' );
    166                 content = content.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' );
     159                        // Remove <p> and <br />
     160                        html = html.replace( /\s*<p>/gi, '' );
     161                        html = html.replace( /\s*<\/p>\s*/gi, '\n\n' );
     162                        html = html.replace( /\n[\s\u00a0]+\n/g, '\n\n' );
     163                        html = html.replace( /\s*<br ?\/?>\s*/gi, '\n' );
    167164
    168                 blocklist2 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|h[1-6]|pre|fieldset';
    169                 content = content.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' );
    170                 content = content.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' );
    171                 content = content.replace( /<li([^>]*)>/g, '\t<li$1>' );
     165                        // Fix some block element newline issues
     166                        html = html.replace( /\s*<div/g, '\n<div' );
     167                        html = html.replace( /<\/div>\s*/g, '</div>\n' );
     168                        html = html.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' );
     169                        html = html.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' );
    172170
    173                 if ( content.indexOf( '<option' ) !== -1 ) {
    174                         content = content.replace( /\s*<option/g, '\n<option' );
    175                         content = content.replace( /\s*<\/select>/g, '\n</select>' );
    176                 }
     171                        html = html.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' );
     172                        html = html.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' );
     173                        html = html.replace( /<li([^>]*)>/g, '\t<li$1>' );
    177174
    178                 if ( content.indexOf( '<hr' ) !== -1 ) {
    179                         content = content.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
    180                 }
     175                        if ( html.indexOf( '<option' ) !== -1 ) {
     176                                html = html.replace( /\s*<option/g, '\n<option' );
     177                                html = html.replace( /\s*<\/select>/g, '\n</select>' );
     178                        }
    181179
    182                 if ( content.indexOf( '<object' ) !== -1 ) {
    183                         content = content.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
    184                                 return a.replace( /[\r\n]+/g, '' );
    185                         });
    186                 }
     180                        if ( html.indexOf( '<hr' ) !== -1 ) {
     181                                html = html.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
     182                        }
    187183
    188                 // Unmark special paragraph closing tags
    189                 content = content.replace( /<\/p#>/g, '</p>\n' );
    190                 content = content.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' );
     184                        if ( html.indexOf( '<object' ) !== -1 ) {
     185                                html = html.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     186                                        return a.replace( /[\r\n]+/g, '' );
     187                                });
     188                        }
    191189
    192                 // Trim whitespace
    193                 content = content.replace( /^\s+/, '' );
    194                 content = content.replace( /[\s\u00a0]+$/, '' );
     190                        // Unmark special paragraph closing tags
     191                        html = html.replace( /<\/p#>/g, '</p>\n' );
     192                        html = html.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' );
    195193
    196                 // put back the line breaks in pre|script
    197                 if ( preserve_linebreaks ) {
    198                         content = content.replace( /<wp-line-break>/g, '\n' );
    199                 }
     194                        // Trim whitespace
     195                        html = html.replace( /^\s+/, '' );
     196                        html = html.replace( /[\s\u00a0]+$/, '' );
    200197
    201                 // and the <br> tags in captions
    202                 if ( preserve_br ) {
    203                         content = content.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     198                        // put back the line breaks in pre|script
     199                        if ( preserve_linebreaks ) {
     200                                html = html.replace( /<wp-line-break>/g, '\n' );
     201                        }
     202
     203                        // and the <br> tags in captions
     204                        if ( preserve_br ) {
     205                                html = html.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     206                        }
     207
     208                        return html;
    204209                }
    205210
    206                 return content;
    207         },
     211                // Similar to `wpautop()` in formatting.php
     212                function autop( text ) {
     213                        var preserve_linebreaks = false,
     214                                preserve_br = false,
     215                                blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
     216                                        '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' +
     217                                        '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary';
    208218
    209         _wp_Autop: function(pee) {
    210                 var preserve_linebreaks = false,
    211                         preserve_br = false,
    212                         blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
    213                                 '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' +
    214                                 '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary';
     219                        // Normalize line breaks
     220                        text = text.replace( /\r\n|\r/g, '\n' );
    215221
    216                 if ( pee.indexOf( '<object' ) !== -1 ) {
    217                         pee = pee.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
    218                                 return a.replace( /[\r\n]+/g, '' );
    219                         });
    220                 }
     222                        if ( text.indexOf( '\n' ) === -1 ) {
     223                                return text;
     224                        }
    221225
    222                 pee = pee.replace( /<[^<>]+>/g, function( a ){
    223                         return a.replace( /[\r\n]+/g, ' ' );
    224                 });
     226                        if ( text.indexOf( '<object' ) !== -1 ) {
     227                                text = text.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     228                                        return a.replace( /\n+/g, '' );
     229                                });
     230                        }
    225231
    226                 // Protect pre|script tags
    227                 if ( pee.indexOf( '<pre' ) !== -1 || pee.indexOf( '<script' ) !== -1 ) {
    228                         preserve_linebreaks = true;
    229                         pee = pee.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
    230                                 return a.replace( /(\r\n|\n)/g, '<wp-line-break>' );
     232                        text = text.replace( /<[^<>]+>/g, function( a ) {
     233                                return a.replace( /[\n\t ]+/g, ' ' );
    231234                        });
    232                 }
    233235
    234                 // keep <br> tags inside captions and convert line breaks
    235                 if ( pee.indexOf( '[caption' ) !== -1 ) {
    236                         preserve_br = true;
    237                         pee = pee.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
    238                                 // keep existing <br>
    239                                 a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' );
    240                                 // no line breaks inside HTML tags
    241                                 a = a.replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( b ) {
    242                                         return b.replace( /[\r\n\t]+/, ' ' );
     236                        // Protect pre|script tags
     237                        if ( text.indexOf( '<pre' ) !== -1 || text.indexOf( '<script' ) !== -1 ) {
     238                                preserve_linebreaks = true;
     239                                text = text.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     240                                        return a.replace( /\n/g, '<wp-line-break>' );
    243241                                });
    244                                 // convert remaining line breaks to <br>
    245                                 return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
     242                        }
     243
     244                        // keep <br> tags inside captions and convert line breaks
     245                        if ( text.indexOf( '[caption' ) !== -1 ) {
     246                                preserve_br = true;
     247                                text = text.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     248                                        // keep existing <br>
     249                                        a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' );
     250                                        // no line breaks inside HTML tags
     251                                        a = a.replace( /<[^<>]+>/g, function( b ) {
     252                                                return b.replace( /[\n\t ]+/, ' ' );
     253                                        });
     254                                        // convert remaining line breaks to <br>
     255                                        return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
     256                                });
     257                        }
     258
     259                        text = text + '\n\n';
     260                        text = text.replace( /<br \/>\s*<br \/>/gi, '\n\n' );
     261                        text = text.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n$1' );
     262                        text = text.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' );
     263                        text = text.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // hr is self closing block element
     264                        text = text.replace( /\s*<option/gi, '<option' ); // No <p> or <br> around <option>
     265                        text = text.replace( /<\/option>\s*/gi, '</option>' );
     266                        text = text.replace( /\n\s*\n+/g, '\n\n' );
     267                        text = text.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' );
     268                        text = text.replace( /<p>\s*?<\/p>/gi, '');
     269                        text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     270                        text = text.replace( /<p>(<li.+?)<\/p>/gi, '$1');
     271                        text = text.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>');
     272                        text = text.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>');
     273                        text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' );
     274                        text = text.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     275                        text = text.replace( /\s*\n/gi, '<br />\n');
     276                        text = text.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' );
     277                        text = text.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' );
     278                        text = text.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' );
     279
     280                        text = text.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
     281                                if ( c.match( /<p( [^>]*)?>/ ) ) {
     282                                        return a;
     283                                }
     284
     285                                return b + '<p>' + c + '</p>';
    246286                        });
    247                 }
    248287
    249                 pee = pee + '\n\n';
    250                 pee = pee.replace( /<br \/>\s*<br \/>/gi, '\n\n' );
    251                 pee = pee.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n$1' );
    252                 pee = pee.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' );
    253                 pee = pee.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // hr is self closing block element
    254                 pee = pee.replace( /\s*<option/gi, '<option' ); // No <p> or <br> around <option>
    255                 pee = pee.replace( /<\/option>\s*/gi, '</option>' );
    256                 pee = pee.replace( /\r\n|\r/g, '\n' );
    257                 pee = pee.replace( /\n\s*\n+/g, '\n\n' );
    258                 pee = pee.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' );
    259                 pee = pee.replace( /<p>\s*?<\/p>/gi, '');
    260                 pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
    261                 pee = pee.replace( /<p>(<li.+?)<\/p>/gi, '$1');
    262                 pee = pee.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>');
    263                 pee = pee.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>');
    264                 pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' );
    265                 pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
    266                 pee = pee.replace( /\s*\n/gi, '<br />\n');
    267                 pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' );
    268                 pee = pee.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' );
    269                 pee = pee.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' );
     288                        // put back the line breaks in pre|script
     289                        if ( preserve_linebreaks ) {
     290                                text = text.replace( /<wp-line-break>/g, '\n' );
     291                        }
    270292
    271                 pee = pee.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
    272                         if ( c.match( /<p( [^>]*)?>/ ) ) {
    273                                 return a;
     293                        if ( preserve_br ) {
     294                                text = text.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
    274295                        }
    275296
    276                         return b + '<p>' + c + '</p>';
    277                 });
    278 
    279                 // put back the line breaks in pre|script
    280                 if ( preserve_linebreaks ) {
    281                         pee = pee.replace( /<wp-line-break>/g, '\n' );
     297                        return text;
    282298                }
    283299
    284                 if ( preserve_br ) {
    285                         pee = pee.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
    286                 }
     300                // Add old events
     301                function pre_wpautop( html ) {
     302                        var obj = { o: exports, data: html, unfiltered: html };
    287303
    288                 return pee;
    289         },
     304                        if ( $ ) {
     305                                $( 'body' ).trigger( 'beforePreWpautop', [ obj ] );
     306                        }
    290307
    291         pre_wpautop: function( content ) {
    292                 var t = this, o = { o: t, data: content, unfiltered: content },
    293                         q = typeof( jQuery ) !== 'undefined';
     308                        obj.data = removep( obj.data );
    294309
    295                 if ( q ) {
    296                         jQuery( 'body' ).trigger( 'beforePreWpautop', [ o ] );
     310                        if ( $ ) {
     311                                $('body').trigger( 'afterPreWpautop', [ obj ] );
     312                        }
     313
     314                        return obj.data;
    297315                }
    298316
    299                 o.data = t._wp_Nop( o.data );
     317                function wpautop( text ) {
     318                        var obj = { o: exports, data: text, unfiltered: text };
    300319
    301                 if ( q ) {
    302                         jQuery('body').trigger('afterPreWpautop', [ o ] );
    303                 }
     320                        if ( $ ) {
     321                                $( 'body' ).trigger( 'beforeWpautop', [ obj ] );
     322                        }
    304323
    305                 return o.data;
    306         },
     324                        obj.data = autop( obj.data );
    307325
    308         wpautop: function( pee ) {
    309                 var t = this, o = { o: t, data: pee, unfiltered: pee },
    310                         q = typeof( jQuery ) !== 'undefined';
     326                        if ( $ ) {
     327                                $( 'body' ).trigger( 'afterWpautop', [ obj ] );
     328                        }
    311329
    312                 if ( q ) {
    313                         jQuery( 'body' ).trigger('beforeWpautop', [ o ] );
     330                        return obj.data;
    314331                }
    315332
    316                 o.data = t._wp_Autop( o.data );
    317 
    318                 if ( q ) {
    319                         jQuery( 'body' ).trigger('afterWpautop', [ o ] );
     333                if ( $ ) {
     334                        $( document ).ready( init );
     335                } else if ( document.addEventListener ) {
     336                        document.addEventListener( 'DOMContentLoaded', init, false );
     337                        window.addEventListener( 'load', init, false );
     338                } else if ( window.attachEvent ) {
     339                        window.attachEvent( 'onload', init );
     340                        document.attachEvent( 'onreadystatechange', function() {
     341                                if ( 'complete' === document.readyState ) {
     342                                        init();
     343                                }
     344                        } );
    320345                }
    321346
    322                 return o.data;
     347                window.wp = window.wp || {};
     348                window.wp.editor = window.wp.editor || {};
     349                window.wp.editor.autop = wpautop;
     350                window.wp.editor.removep = pre_wpautop;
     351
     352                exports = {
     353                        go: switchEditor,
     354                        wpautop: wpautop,
     355                        pre_wpautop: pre_wpautop,
     356                        _wp_Autop: autop,
     357                        _wp_Nop: removep
     358                };
     359
     360                return exports;
    323361        }
    324 };
     362
     363        window.switchEditors = new SwitchEditors();
     364}( window.jQuery ));
  • src/wp-includes/class-wp-editor.php

     
    143143         * @param array $settings See the _parse_settings() method for description.
    144144         */
    145145        public static function editor( $content, $editor_id, $settings = array() ) {
    146 
    147146                $set = self::parse_settings( $editor_id, $settings );
    148                 $editor_class = ' class="' . trim( $set['editor_class'] . ' wp-editor-area' ) . '"';
     147                $editor_class = ' class="' . trim( esc_attr( $set['editor_class'] ) . ' wp-editor-area' ) . '"';
    149148                $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
    150                 $switch_class = 'html-active';
     149                $default_editor = 'html';
    151150                $toolbar = $buttons = $autocomplete = '';
     151                $editor_id_attr = esc_attr( $editor_id );
    152152
    153153                if ( $set['drag_drop_upload'] ) {
    154154                        self::$drag_drop_upload = true;
    155155                }
    156156
    157                 if ( ! empty( $set['editor_height'] ) )
    158                         $height = ' style="height: ' . $set['editor_height'] . 'px"';
    159                 else
    160                         $height = ' rows="' . $set['textarea_rows'] . '"';
     157                if ( ! empty( $set['editor_height'] ) ) {
     158                        $height = ' style="height: ' . (int) $set['editor_height'] . 'px"';
     159                } else {
     160                        $height = ' rows="' . (int) $set['textarea_rows'] . '"';
     161                }
    161162
    162                 if ( !current_user_can( 'upload_files' ) )
     163                if ( ! current_user_can( 'upload_files' ) ) {
    163164                        $set['media_buttons'] = false;
     165                }
    164166
    165                 if ( ! self::$this_quicktags && self::$this_tinymce ) {
    166                         $switch_class = 'tmce-active';
     167                if ( self::$this_tinymce ) {
    167168                        $autocomplete = ' autocomplete="off"';
    168                 } elseif ( self::$this_quicktags && self::$this_tinymce ) {
    169                         $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
    170                         $autocomplete = ' autocomplete="off"';
    171169
    172                         // 'html' is used for the "Text" editor tab.
    173                         if ( 'html' === $default_editor ) {
    174                                 add_filter('the_editor_content', 'wp_htmledit_pre');
    175                                 $switch_class = 'html-active';
     170                        if ( self::$this_quicktags ) {
     171                                $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
     172                                // 'html' is used for the "Text" editor tab.
     173                                if ( 'html' !== $default_editor ) {
     174                                        $default_editor = 'tinymce';
     175                                }
     176
     177                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-tmce" class="wp-switch-editor switch-tmce"' .
     178                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . __('Visual') . "</button>\n";
     179                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-html" class="wp-switch-editor switch-html"' .
     180                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
    176181                        } else {
    177                                 add_filter('the_editor_content', 'wp_richedit_pre');
    178                                 $switch_class = 'tmce-active';
     182                                $default_editor = 'tinymce';
    179183                        }
    180 
    181                         $buttons .= '<button type="button" id="' . $editor_id . '-tmce" class="wp-switch-editor switch-tmce" onclick="switchEditors.switchto(this);">' . __('Visual') . "</button>\n";
    182                         $buttons .= '<button type="button" id="' . $editor_id . '-html" class="wp-switch-editor switch-html" onclick="switchEditors.switchto(this);">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
    183184                }
    184185
     186                $switch_class = 'html' === $default_editor ? 'html-active' : 'tmce-active';
    185187                $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;
    186188
    187189                if ( $set['_content_editor_dfw'] ) {
     
    188190                        $wrap_class .= ' has-dfw';
    189191                }
    190192
    191                 echo '<div id="wp-' . $editor_id . '-wrap" class="' . $wrap_class . '">';
     193                echo '<div id="wp-' . $editor_id_attr . '-wrap" class="' . $wrap_class . '">';
    192194
    193195                if ( self::$editor_buttons_css ) {
    194                         wp_print_styles('editor-buttons');
     196                        wp_print_styles( 'editor-buttons' );
    195197                        self::$editor_buttons_css = false;
    196198                }
    197199
    198                 if ( !empty($set['editor_css']) )
     200                if ( ! empty( $set['editor_css'] ) ) {
    199201                        echo $set['editor_css'] . "\n";
     202                }
    200203
    201                 if ( !empty($buttons) || $set['media_buttons'] ) {
    202                         echo '<div id="wp-' . $editor_id . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
     204                if ( ! empty( $buttons ) || $set['media_buttons'] ) {
     205                        echo '<div id="wp-' . $editor_id_attr . '-editor-tools" class="wp-editor-tools hide-if-no-js">';
    203206
    204207                        if ( $set['media_buttons'] ) {
    205208                                self::$has_medialib = true;
    206209
    207                                 if ( !function_exists('media_buttons') )
    208                                         include(ABSPATH . 'wp-admin/includes/media.php');
     210                                if ( ! function_exists( 'media_buttons' ) )
     211                                        include( ABSPATH . 'wp-admin/includes/media.php' );
    209212
    210                                 echo '<div id="wp-' . $editor_id . '-media-buttons" class="wp-media-buttons">';
     213                                echo '<div id="wp-' . $editor_id_attr . '-media-buttons" class="wp-media-buttons">';
    211214
    212215                                /**
    213216                                 * Fires after the default media button(s) are displayed.
     
    224227                        echo "</div>\n";
    225228                }
    226229
     230                $quicktags_toolbar = '';
     231
     232                if ( self::$this_quicktags ) {
     233                        if ( 'content' === $editor_id && ! empty( $GLOBALS['current_screen'] ) && $GLOBALS['current_screen']->base === 'post' ) {
     234                                $toolbar_id = 'ed_toolbar';
     235                        } else {
     236                                $toolbar_id = 'qt_' . $editor_id_attr . '_toolbar';
     237                        }
     238
     239                        $quicktags_toolbar = '<div id="' . $toolbar_id . '" class="quicktags-toolbar"></div>';
     240                }
     241
    227242                /**
    228243                 * Filter the HTML markup output that displays the editor.
    229244                 *
     
    231246                 *
    232247                 * @param string $output Editor's HTML markup.
    233248                 */
    234                 $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id . '-editor-container" class="wp-editor-container">' .
    235                         '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . $set['textarea_name'] . '" ' .
    236                         'id="' . $editor_id . '">%s</textarea></div>' );
     249                $the_editor = apply_filters( 'the_editor', '<div id="wp-' . $editor_id_attr . '-editor-container" class="wp-editor-container">' .
     250                        $quicktags_toolbar .
     251                        '<textarea' . $editor_class . $height . $tabindex . $autocomplete . ' cols="40" name="' . esc_attr( $set['textarea_name'] ) . '" ' .
     252                        'id="' . $editor_id_attr . '">%s</textarea></div>' );
    237253
    238254                /**
    239255                 * Filter the default editor content.
     
    242258                 *
    243259                 * @param string $content Default editor content.
    244260                 */
    245                 $content = apply_filters( 'the_editor_content', $content );
     261                $content = apply_filters( 'the_editor_content', $content, $default_editor );
    246262
     263                // Back-compat for the `htmledit_pre` and `richedit_pre` filters
     264                if ( 'html' === $default_editor && has_filter( 'htmledit_pre' ) ) {
     265                        // TODO: needs _deprecated_filter(), use _deprecated_function() as substitute for now
     266                        _deprecated_function( 'add_filter( htmledit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
     267                        $content = apply_filters( 'htmledit_pre', $content );
     268                } elseif ( 'tinymce' === $default_editor && has_filter( 'richedit_pre' ) ) {
     269                        _deprecated_function( 'add_filter( richedit_pre )', '4.3.0', 'add_filter( format_for_editor )' );
     270                        $content = apply_filters( 'richedit_pre', $content );
     271                }
     272
    247273                printf( $the_editor, $content );
    248274                echo "\n</div>\n\n";
    249275
    250                 self::editor_settings($editor_id, $set);
     276                self::editor_settings( $editor_id, $set );
    251277        }
    252278
    253279        /**
     
    478504                                        'theme' => 'modern',
    479505                                        'skin' => 'lightgray',
    480506                                        'language' => self::$mce_locale,
    481                                         'formats' => "{
    482                                                 alignleft: [
    483                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}},
    484                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignleft'}
    485                                                 ],
    486                                                 aligncenter: [
    487                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}},
    488                                                         {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'}
    489                                                 ],
    490                                                 alignright: [
    491                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}},
    492                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignright'}
    493                                                 ],
    494                                                 strikethrough: {inline: 'del'}
    495                                         }",
     507                                        'formats' => '{' .
     508                                                'alignleft: [' .
     509                                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
     510                                                        '{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
     511                                                '],' .
     512                                                'aligncenter: [' .
     513                                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
     514                                                        '{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
     515                                                '],' .
     516                                                'alignright: [' .
     517                                                        '{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
     518                                                        '{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
     519                                                '],' .
     520                                                'strikethrough: {inline: "del"}' .
     521                                        '}',
    496522                                        'relative_urls' => false,
    497523                                        'remove_script_host' => false,
    498524                                        'convert_urls' => false,
     
    650676                                'body_class' => $body_class
    651677                        );
    652678
    653                         if ( $first_run )
    654                                 $mceInit = array_merge( self::$first_init, $mceInit );
     679                        // Merge with the first part of the init array
     680                        $mceInit = array_merge( self::$first_init, $mceInit );
    655681
    656682                        if ( is_array( $set['tinymce'] ) )
    657683                                $mceInit = array_merge( $mceInit, $set['tinymce'] );
     
    662688                         * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
    663689                         * and "extended_valid_elements" can be done through this filter. Best
    664690                         * is to use the default cleanup by not specifying valid_elements,
    665                          * as TinyMCE contains full set of XHTML 1.0.
     691                         * as TinyMCE checks against the full set of HTML 5.0 elements and attributes.
    666692                         */
    667693                        if ( $set['teeny'] ) {
    668694
     
    11821208                ?>
    11831209
    11841210                ( function() {
    1185                         var init, edId, qtId, firstInit, wrapper;
     1211                        var init, id, $wrap;
    11861212
    11871213                        if ( typeof tinymce !== 'undefined' ) {
    1188                                 for ( edId in tinyMCEPreInit.mceInit ) {
    1189                                         if ( firstInit ) {
    1190                                                 init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] );
    1191                                         } else {
    1192                                                 init = firstInit = tinyMCEPreInit.mceInit[edId];
    1193                                         }
     1214                                for ( id in tinyMCEPreInit.mceInit ) {
     1215                                        init = tinyMCEPreInit.mceInit[id];
     1216                                        $wrap = tinymce.$( '#wp-' + id + '-wrap' );
    11941217
    1195                                         wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0];
     1218                                        if ( ( $wrap.hasClass( 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( id ) ) && ! init.wp_skip_init ) {
     1219                                                tinymce.init( init );
    11961220
    1197                                         if ( ( tinymce.DOM.hasClass( wrapper, 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) &&
    1198                                                 ! init.wp_skip_init ) {
    1199 
    1200                                                 try {
    1201                                                         tinymce.init( init );
    1202 
    1203                                                         if ( ! window.wpActiveEditor ) {
    1204                                                                 window.wpActiveEditor = edId;
    1205                                                         }
    1206                                                 } catch(e){}
     1221                                                if ( ! window.wpActiveEditor ) {
     1222                                                        window.wpActiveEditor = id;
     1223                                                }
    12071224                                        }
    12081225                                }
    12091226                        }
    12101227
    12111228                        if ( typeof quicktags !== 'undefined' ) {
    1212                                 for ( qtId in tinyMCEPreInit.qtInit ) {
    1213                                         try {
    1214                                                 quicktags( tinyMCEPreInit.qtInit[qtId] );
     1229                                for ( id in tinyMCEPreInit.qtInit ) {
     1230                                        quicktags( tinyMCEPreInit.qtInit[id] );
    12151231
    1216                                                 if ( ! window.wpActiveEditor ) {
    1217                                                         window.wpActiveEditor = qtId;
    1218                                                 }
    1219                                         } catch(e){};
    1220                                 }
    1221                         }
    1222 
    1223                         if ( typeof jQuery !== 'undefined' ) {
    1224                                 jQuery('.wp-editor-wrap').on( 'click.wp-editor', function() {
    1225                                         if ( this.id ) {
    1226                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
     1232                                        if ( ! window.wpActiveEditor ) {
     1233                                                window.wpActiveEditor = id;
    12271234                                        }
    1228                                 });
    1229                         } else {
    1230                                 for ( qtId in tinyMCEPreInit.qtInit ) {
    1231                                         document.getElementById( 'wp-' + qtId + '-wrap' ).onclick = function() {
    1232                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
    1233                                         }
    12341235                                }
    12351236                        }
    12361237                }());
  • src/wp-includes/css/editor.css

     
    286286.mce-path-item,
    287287.mce-path .mce-divider {
    288288        font-size: 12px;
    289         line-height: 18px;
    290289}
    291290
    292291.mce-toolbar .mce-btn,
     
    10591058        position: relative;
    10601059        border-bottom: 1px solid #dedede;
    10611060        background: #f5f5f5;
     1061        min-height: 30px;
    10621062}
    10631063
    10641064.has-dfw .quicktags-toolbar {
  • src/wp-includes/default-filters.php

     
    202202
    203203add_filter( 'http_request_host_is_external', 'allowed_http_request_hosts', 10, 2 );
    204204
     205// Prepare the content for the Visual or Text editor
     206add_filter( 'the_editor_content', 'format_for_editor', 10, 2 );
     207
    205208// Actions
    206209add_action( 'wp_head',             '_wp_render_title_tag',            1     );
    207210add_action( 'wp_head',             'wp_enqueue_scripts',              1     );
  • src/wp-includes/deprecated.php

     
    34913491
    34923492        return false;
    34933493}
     3494
     3495/**
     3496 * Formats text for the rich text editor.
     3497 *
     3498 * The filter 'richedit_pre' is applied here. If $text is empty the filter will
     3499 * be applied to an empty string.
     3500 *
     3501 * @since 2.0.0
     3502 * @deprecated 4.3.0
     3503 *
     3504 * @param string $text The text to be formatted.
     3505 * @return string The formatted text after filter is applied.
     3506 */
     3507function wp_richedit_pre($text) {
     3508        _deprecated_function( __FUNCTION__, '4.3', 'format_for_editor()' );
     3509
     3510        if ( empty( $text ) ) {
     3511                /**
     3512                 * Filter text returned for the rich text editor.
     3513                 *
     3514                 * This filter is first evaluated, and the value returned, if an empty string
     3515                 * is passed to wp_richedit_pre(). If an empty string is passed, it results
     3516                 * in a break tag and line feed.
     3517                 *
     3518                 * If a non-empty string is passed, the filter is evaluated on the wp_richedit_pre()
     3519                 * return after being formatted.
     3520                 *
     3521                 * @since 2.0.0
     3522                 * @deprecated 4.3.0
     3523                 *
     3524                 * @param string $output Text for the rich text editor.
     3525                 */
     3526                return apply_filters( 'richedit_pre', '' );
     3527        }
     3528
     3529        $output = convert_chars($text);
     3530        $output = wpautop($output);
     3531        $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) );
     3532
     3533        /** This filter is documented in wp-includes/deprecated.php */
     3534        return apply_filters( 'richedit_pre', $output );
     3535}
     3536
     3537/**
     3538 * Formats text for the HTML editor.
     3539 *
     3540 * Unless $output is empty it will pass through htmlspecialchars before the
     3541 * 'htmledit_pre' filter is applied.
     3542 *
     3543 * @since 2.5.0
     3544 * @deprecated 4.3.0
     3545 *
     3546 * @param string $output The text to be formatted.
     3547 * @return string Formatted text after filter applied.
     3548 */
     3549function wp_htmledit_pre($output) {
     3550        _deprecated_function( __FUNCTION__, '4.3', 'format_for_editor()' );
     3551
     3552        if ( !empty($output) )
     3553                $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > &
     3554
     3555        /**
     3556         * Filter the text before it is formatted for the HTML editor.
     3557         *
     3558         * @since 2.5.0
     3559         * @deprecated 4.3.0
     3560         *
     3561         * @param string $output The HTML-formatted text.
     3562         */
     3563        return apply_filters( 'htmledit_pre', $output );
     3564}
     3565
  • src/wp-includes/formatting.php

     
    30013001}
    30023002
    30033003/**
    3004  * Formats text for the rich text editor.
     3004 * Formats text for the editor.
    30053005 *
    3006  * The filter 'richedit_pre' is applied here. If $text is empty the filter will
    3007  * be applied to an empty string.
     3006 * Generally the browsers treat everything inside a textarea as text, but
     3007 * it is still a good idea to HTML entity encode `<`, `>` and `&` in the content.
    30083008 *
    3009  * @since 2.0.0
     3009 * The filter 'format_for_editor' is applied here. If $text is empty the filter will
     3010 * be applied to an empty string.
    30103011 *
     3012 * @since 4.3.0
     3013 *
    30113014 * @param string $text The text to be formatted.
    3012  * @return string The formatted text after filter is applied.
     3015 * @return string The formatted text after filter is applied. <?
    30133016 */
    3014 function wp_richedit_pre( $text ) {
    3015         if ( empty( $text ) ) {
    3016                 /**
    3017                  * Filter text returned for the rich text editor.
    3018                  *
    3019                  * This filter is first evaluated, and the value returned, if an empty string
    3020                  * is passed to wp_richedit_pre(). If an empty string is passed, it results
    3021                  * in a break tag and line feed.
    3022                  *
    3023                  * If a non-empty string is passed, the filter is evaluated on the wp_richedit_pre()
    3024                  * return after being formatted.
    3025                  *
    3026                  * @since 2.0.0
    3027                  *
    3028                  * @param string $output Text for the rich text editor.
    3029                  */
    3030                 return apply_filters( 'richedit_pre', '' );
     3017function format_for_editor( $text, $default_editor = null ) {
     3018        // Back-compat: check if any characters need encoding.
     3019        if ( ! empty( $text ) && ( false !== strpos( $text, '<' ) || false !== strpos( $text, '>' ) ||
     3020                preg_match( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', $text ) ) ) {
     3021
     3022                $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) );
    30313023        }
    30323024
    3033         $output = convert_chars($text);
    3034         $output = wpautop($output);
    3035         $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) );
    3036 
    3037         /** This filter is documented in wp-includes/formatting.php */
    3038         return apply_filters( 'richedit_pre', $output );
    3039 }
    3040 
    3041 /**
    3042  * Formats text for the HTML editor.
    3043  *
    3044  * Unless $output is empty it will pass through htmlspecialchars before the
    3045  * 'htmledit_pre' filter is applied.
    3046  *
    3047  * @since 2.5.0
    3048  *
    3049  * @param string $output The text to be formatted.
    3050  * @return string Formatted text after filter applied.
    3051  */
    3052 function wp_htmledit_pre( $output ) {
    3053         if ( !empty($output) )
    3054                 $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > &
    3055 
    30563025        /**
    3057          * Filter the text before it is formatted for the HTML editor.
     3026         * Filter the text after it is formatted for the editor.
    30583027         *
    3059          * @since 2.5.0
     3028         * @since 4.3.0
    30603029         *
    3061          * @param string $output The HTML-formatted text.
     3030         * @param string $text The formatted text.
    30623031         */
    3063         return apply_filters( 'htmledit_pre', $output );
     3032        return apply_filters( 'format_for_editor', $text, $default_editor );
    30643033}
    30653034
    30663035/**
  • src/wp-includes/js/quicktags.js

     
    163163                        id = settings.id,
    164164                        canvas = document.getElementById(id),
    165165                        name = 'qt_' + id,
    166                         tb, onclick, toolbar_id;
     166                        tb, onclick, toolbar_id, wrap, setActiveEditor;
    167167
    168168                if ( !id || !canvas ) {
    169169                        return false;
     
    182182                        toolbar_id = name + '_toolbar';
    183183                }
    184184
    185                 tb = document.createElement('div');
    186                 tb.id = toolbar_id;
    187                 tb.className = 'quicktags-toolbar';
    188                 tb.onclick = function() {
    189                         window.wpActiveEditor = id;
    190                 };
     185                tb = document.getElementById( toolbar_id );
    191186
     187                if ( ! tb ) {
     188                        tb = document.createElement('div');
     189                        tb.id = toolbar_id;
     190                        tb.className = 'quicktags-toolbar';
     191                }
     192
    192193                canvas.parentNode.insertBefore(tb, canvas);
    193194                t.toolbar = tb;
    194195
     
    214215                        }
    215216                };
    216217
     218                setActiveEditor = function() {
     219                        window.wpActiveEditor = id;
     220                };
     221
     222                wrap = document.getElementById( 'wp-' + id + '-wrap' );
     223
    217224                if ( tb.addEventListener ) {
    218                         tb.addEventListener('click', onclick, false);
     225                        tb.addEventListener( 'click', onclick, false );
     226                       
     227                        if ( wrap ) {
     228                                wrap.addEventListener( 'click', setActiveEditor, false );
     229                        }
    219230                } else if ( tb.attachEvent ) {
    220                         tb.attachEvent('onclick', onclick);
     231                        tb.attachEvent( 'onclick', onclick );
     232
     233                        if ( wrap ) {
     234                                wrap.attachEvent( 'onclick', setActiveEditor );
     235                        }
    221236                }
    222237
    223238                t.getButton = function(id) {
  • src/wp-includes/js/tinymce/plugins/wordpress/plugin.js

     
    1 /* global tinymce, getUserSetting, setUserSetting */
     1/* global getUserSetting, setUserSetting */
     2( function( tinymce, $, wp ) {
     3        // Set the minimum value for the modals z-index higher than #wpadminbar (100000)
     4        tinymce.ui.FloatPanel.zIndex = 100100;
    25
    3 // Set the minimum value for the modals z-index higher than #wpadminbar (100000)
    4 tinymce.ui.FloatPanel.zIndex = 100100;
     6        tinymce.PluginManager.add( 'wordpress', function( editor ) {
     7                var DOM = tinymce.DOM,
     8                        each = tinymce.each,
     9                        __ = editor.editorManager.i18n.translate,
     10                        wpAdvButton, style,
     11                        hasWpautop = ( wp && wp.editor && wp.editor.autop && editor.getParam( 'wpautop', true ) ),
     12                        last = 0;
    513
    6 tinymce.PluginManager.add( 'wordpress', function( editor ) {
    7         var DOM = tinymce.DOM,
    8                 each = tinymce.each,
    9                 __ = editor.editorManager.i18n.translate,
    10                 wpAdvButton, style,
    11                 last = 0;
     14                if ( $ ) {
     15                        $( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
     16                }
    1217
    13         if ( typeof window.jQuery !== 'undefined' ) {
    14                 window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
    15         }
     18                function toggleToolbars( state ) {
     19                        var iframe, initial, toolbars,
     20                                pixels = 0;
    1621
    17         function toggleToolbars( state ) {
    18                 var iframe, initial, toolbars,
    19                         pixels = 0;
     22                        initial = ( state === 'hide' );
    2023
    21                 initial = ( state === 'hide' );
     24                        if ( editor.theme.panel ) {
     25                                toolbars = editor.theme.panel.find('.toolbar:not(.menubar)');
     26                        }
    2227
    23                 if ( editor.theme.panel ) {
    24                         toolbars = editor.theme.panel.find('.toolbar:not(.menubar)');
    25                 }
     28                        if ( ! toolbars || toolbars.length < 2 || ( state === 'hide' && ! toolbars[1].visible() ) ) {
     29                                return;
     30                        }
    2631
    27                 if ( ! toolbars || toolbars.length < 2 || ( state === 'hide' && ! toolbars[1].visible() ) ) {
    28                         return;
    29                 }
     32                        if ( ! state && toolbars[1].visible() ) {
     33                                state = 'hide';
     34                        }
    3035
    31                 if ( ! state && toolbars[1].visible() ) {
    32                         state = 'hide';
    33                 }
     36                        each( toolbars, function( toolbar, i ) {
     37                                if ( i > 0 ) {
     38                                        if ( state === 'hide' ) {
     39                                                toolbar.hide();
     40                                                pixels += 30;
     41                                        } else {
     42                                                toolbar.show();
     43                                                pixels -= 30;
     44                                        }
     45                                }
     46                        });
    3447
    35                 each( toolbars, function( toolbar, i ) {
    36                         if ( i > 0 ) {
     48                        if ( pixels && ! initial ) {
     49                                // Resize iframe, not needed in iOS
     50                                if ( ! tinymce.Env.iOS ) {
     51                                        iframe = editor.getContentAreaContainer().firstChild;
     52                                        DOM.setStyle( iframe, 'height', iframe.clientHeight + pixels );
     53                                }
     54
    3755                                if ( state === 'hide' ) {
    38                                         toolbar.hide();
    39                                         pixels += 30;
     56                                        setUserSetting('hidetb', '0');
     57                                        wpAdvButton && wpAdvButton.active( false );
    4058                                } else {
    41                                         toolbar.show();
    42                                         pixels -= 30;
     59                                        setUserSetting('hidetb', '1');
     60                                        wpAdvButton && wpAdvButton.active( true );
    4361                                }
    4462                        }
    45                 });
    4663
    47                 if ( pixels && ! initial ) {
    48                         // Resize iframe, not needed in iOS
    49                         if ( ! tinymce.Env.iOS ) {
    50                                 iframe = editor.getContentAreaContainer().firstChild;
    51                                 DOM.setStyle( iframe, 'height', iframe.clientHeight + pixels );
     64                        editor.fire( 'wp-toolbar-toggle' );
     65                }
     66
     67                // Add the kitchen sink button :)
     68                editor.addButton( 'wp_adv', {
     69                        tooltip: 'Toolbar Toggle',
     70                        cmd: 'WP_Adv',
     71                        onPostRender: function() {
     72                                wpAdvButton = this;
     73                                wpAdvButton.active( getUserSetting( 'hidetb' ) === '1' ? true : false );
    5274                        }
     75                });
    5376
    54                         if ( state === 'hide' ) {
    55                                 setUserSetting('hidetb', '0');
    56                                 wpAdvButton && wpAdvButton.active( false );
    57                         } else {
    58                                 setUserSetting('hidetb', '1');
    59                                 wpAdvButton && wpAdvButton.active( true );
     77                // Hide the toolbars after loading
     78                editor.on( 'PostRender', function() {
     79                        if ( editor.getParam( 'wordpress_adv_hidden', true ) && getUserSetting( 'hidetb', '0' ) === '0' ) {
     80                                toggleToolbars( 'hide' );
    6081                        }
    61                 }
     82                });
    6283
    63                 editor.fire( 'wp-toolbar-toggle' );
    64         }
     84                editor.addCommand( 'WP_Adv', function() {
     85                        toggleToolbars();
     86                });
    6587
    66         // Add the kitchen sink button :)
    67         editor.addButton( 'wp_adv', {
    68                 tooltip: 'Toolbar Toggle',
    69                 cmd: 'WP_Adv',
    70                 onPostRender: function() {
    71                         wpAdvButton = this;
    72                         wpAdvButton.active( getUserSetting( 'hidetb' ) === '1' ? true : false );
    73                 }
    74         });
     88                editor.on( 'focus', function() {
     89                window.wpActiveEditor = editor.id;
     90            });
    7591
    76         // Hide the toolbars after loading
    77         editor.on( 'PostRender', function() {
    78                 if ( editor.getParam( 'wordpress_adv_hidden', true ) && getUserSetting( 'hidetb', '0' ) === '0' ) {
    79                         toggleToolbars( 'hide' );
    80                 }
    81         });
     92                // Replace Read More/Next Page tags with images
     93                editor.on( 'BeforeSetContent', function( event ) {
     94                        var title,
     95                                paragraph = tinymce.Env.webkit ? '<p><br /></p>' : '<p></p>';
    8296
    83         editor.addCommand( 'WP_Adv', function() {
    84                 toggleToolbars();
    85         });
     97                        if ( event.content ) {
     98                                if ( event.content.indexOf( '<!--more' ) !== -1 ) {
     99                                        title = __( 'Read more...' );
    86100
    87         editor.on( 'focus', function() {
    88         window.wpActiveEditor = editor.id;
    89     });
     101                                        event.content = event.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
     102                                                return '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="more" data-wp-more-text="' + moretext + '" ' +
     103                                                        'class="wp-more-tag mce-wp-more" title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />';
     104                                        });
     105                                }
    90106
    91         // Replace Read More/Next Page tags with images
    92         editor.on( 'BeforeSetContent', function( e ) {
    93                 var title;
     107                                if ( event.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
     108                                        title = __( 'Page break' );
    94109
    95                 if ( e.content ) {
    96                         if ( e.content.indexOf( '<!--more' ) !== -1 ) {
    97                                 title = __( 'Read more...' );
     110                                        event.content = event.content.replace( /<!--nextpage-->/g,
     111                                                '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="nextpage" class="wp-more-tag mce-wp-nextpage" ' +
     112                                                        'title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />' );
     113                                }
    98114
    99                                 e.content = e.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
    100                                         return '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="more" data-wp-more-text="' + moretext + '" ' +
    101                                                 'class="wp-more-tag mce-wp-more" title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />';
    102                                 });
     115                                // Remove spaces from empty paragraphs.
     116                                event.content = event.content.replace( /<p>(?:&nbsp;|\u00a0|\uFEFF|\s)+<\/p>/gi, paragraph );
     117
     118                                if ( event.load && event.format !== 'raw' && hasWpautop ) {
     119                                        event.content = wp.editor.autop( event.content );
     120                                }
    103121                        }
     122                });
    104123
    105                         if ( e.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
    106                                 title = __( 'Page break' );
     124                // Replace images with tags
     125                editor.on( 'PostProcess', function( e ) {
     126                        if ( e.get ) {
     127                                e.content = e.content.replace(/<img[^>]+>/g, function( image ) {
     128                                        var match, moretext = '';
    107129
    108                                 e.content = e.content.replace( /<!--nextpage-->/g,
    109                                         '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="nextpage" class="wp-more-tag mce-wp-nextpage" ' +
    110                                                 'title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />' );
    111                         }
    112                 }
    113         });
     130                                        if ( image.indexOf( 'data-wp-more="more"' ) !== -1 ) {
     131                                                if ( match = image.match( /data-wp-more-text="([^"]+)"/ ) ) {
     132                                                        moretext = match[1];
     133                                                }
    114134
    115         // Replace images with tags
    116         editor.on( 'PostProcess', function( e ) {
    117                 if ( e.get ) {
    118                         e.content = e.content.replace(/<img[^>]+>/g, function( image ) {
    119                                 var match, moretext = '';
    120 
    121                                 if ( image.indexOf( 'data-wp-more="more"' ) !== -1 ) {
    122                                         if ( match = image.match( /data-wp-more-text="([^"]+)"/ ) ) {
    123                                                 moretext = match[1];
     135                                                image = '<!--more' + moretext + '-->';
     136                                        } else if ( image.indexOf( 'data-wp-more="nextpage"' ) !== -1 ) {
     137                                                image = '<!--nextpage-->';
    124138                                        }
    125139
    126                                         image = '<!--more' + moretext + '-->';
    127                                 } else if ( image.indexOf( 'data-wp-more="nextpage"' ) !== -1 ) {
    128                                         image = '<!--nextpage-->';
    129                                 }
     140                                        return image;
     141                                });
     142                        }
     143                });
    130144
    131                                 return image;
    132                         });
    133                 }
    134         });
     145                // Display the tag name instead of img in element path
     146                editor.on( 'ResolveName', function( event ) {
     147                        var attr;
    135148
    136         // Display the tag name instead of img in element path
    137         editor.on( 'ResolveName', function( event ) {
    138                 var attr;
     149                        if ( event.target.nodeName === 'IMG' && ( attr = editor.dom.getAttrib( event.target, 'data-wp-more' ) ) ) {
     150                                event.name = attr;
     151                        }
     152                });
    139153
    140                 if ( event.target.nodeName === 'IMG' && ( attr = editor.dom.getAttrib( event.target, 'data-wp-more' ) ) ) {
    141                         event.name = attr;
    142                 }
    143         });
     154                // Register commands
     155                editor.addCommand( 'WP_More', function( tag ) {
     156                        var parent, html, title,
     157                                classname = 'wp-more-tag',
     158                                dom = editor.dom,
     159                                node = editor.selection.getNode();
    144160
    145         // Register commands
    146         editor.addCommand( 'WP_More', function( tag ) {
    147                 var parent, html, title,
    148                         classname = 'wp-more-tag',
    149                         dom = editor.dom,
    150                         node = editor.selection.getNode();
     161                        tag = tag || 'more';
     162                        classname += ' mce-wp-' + tag;
     163                        title = tag === 'more' ? 'Read more...' : 'Next page';
     164                        title = __( title );
     165                        html = '<img src="' + tinymce.Env.transparentSrc + '" title="' + title + '" class="' + classname + '" ' +
     166                                'data-wp-more="' + tag + '" data-mce-resize="false" data-mce-placeholder="1" />';
    151167
    152                 tag = tag || 'more';
    153                 classname += ' mce-wp-' + tag;
    154                 title = tag === 'more' ? 'Read more...' : 'Next page';
    155                 title = __( title );
    156                 html = '<img src="' + tinymce.Env.transparentSrc + '" title="' + title + '" class="' + classname + '" ' +
    157                         'data-wp-more="' + tag + '" data-mce-resize="false" data-mce-placeholder="1" />';
     168                        // Most common case
     169                        if ( node.nodeName === 'BODY' || ( node.nodeName === 'P' && node.parentNode.nodeName === 'BODY' ) ) {
     170                                editor.insertContent( html );
     171                                return;
     172                        }
    158173
    159                 // Most common case
    160                 if ( node.nodeName === 'BODY' || ( node.nodeName === 'P' && node.parentNode.nodeName === 'BODY' ) ) {
    161                         editor.insertContent( html );
    162                         return;
    163                 }
     174                        // Get the top level parent node
     175                        parent = dom.getParent( node, function( found ) {
     176                                if ( found.parentNode && found.parentNode.nodeName === 'BODY' ) {
     177                                        return true;
     178                                }
    164179
    165                 // Get the top level parent node
    166                 parent = dom.getParent( node, function( found ) {
    167                         if ( found.parentNode && found.parentNode.nodeName === 'BODY' ) {
    168                                 return true;
    169                         }
     180                                return false;
     181                        }, editor.getBody() );
    170182
    171                         return false;
    172                 }, editor.getBody() );
     183                        if ( parent ) {
     184                                if ( parent.nodeName === 'P' ) {
     185                                        parent.appendChild( dom.create( 'p', null, html ).firstChild );
     186                                } else {
     187                                        dom.insertAfter( dom.create( 'p', null, html ), parent );
     188                                }
    173189
    174                 if ( parent ) {
    175                         if ( parent.nodeName === 'P' ) {
    176                                 parent.appendChild( dom.create( 'p', null, html ).firstChild );
    177                         } else {
    178                                 dom.insertAfter( dom.create( 'p', null, html ), parent );
     190                                editor.nodeChanged();
    179191                        }
     192                });
    180193
    181                         editor.nodeChanged();
    182                 }
    183         });
     194                editor.addCommand( 'WP_Code', function() {
     195                        editor.formatter.toggle('code');
     196                });
    184197
    185         editor.addCommand( 'WP_Code', function() {
    186                 editor.formatter.toggle('code');
    187         });
     198                editor.addCommand( 'WP_Page', function() {
     199                        editor.execCommand( 'WP_More', 'nextpage' );
     200                });
    188201
    189         editor.addCommand( 'WP_Page', function() {
    190                 editor.execCommand( 'WP_More', 'nextpage' );
    191         });
     202                editor.addCommand( 'WP_Help', function() {
     203                        editor.windowManager.open({
     204                                url: tinymce.baseURL + '/wp-mce-help.php',
     205                                title: 'Keyboard Shortcuts',
     206                                width: 450,
     207                                height: 420,
     208                                classes: 'wp-help',
     209                                buttons: { text: 'Close', onclick: 'close' }
     210                        });
     211                });
    192212
    193         editor.addCommand( 'WP_Help', function() {
    194                 editor.windowManager.open({
    195                         url: tinymce.baseURL + '/wp-mce-help.php',
    196                         title: 'Keyboard Shortcuts',
    197                         width: 450,
    198                         height: 420,
    199                         classes: 'wp-help',
    200                         buttons: { text: 'Close', onclick: 'close' }
     213                editor.addCommand( 'WP_Medialib', function() {
     214                        if ( wp && wp.media && wp.media.editor ) {
     215                                wp.media.editor.open( editor.id );
     216                        }
    201217                });
    202         });
    203218
    204         editor.addCommand( 'WP_Medialib', function() {
    205                 if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
    206                         wp.media.editor.open( editor.id );
    207                 }
    208         });
     219                // Register buttons
     220                editor.addButton( 'wp_more', {
     221                        tooltip: 'Insert Read More tag',
     222                        onclick: function() {
     223                                editor.execCommand( 'WP_More', 'more' );
     224                        }
     225                });
    209226
    210         // Register buttons
    211         editor.addButton( 'wp_more', {
    212                 tooltip: 'Insert Read More tag',
    213                 onclick: function() {
    214                         editor.execCommand( 'WP_More', 'more' );
    215                 }
    216         });
     227                editor.addButton( 'wp_page', {
     228                        tooltip: 'Page break',
     229                        onclick: function() {
     230                                editor.execCommand( 'WP_More', 'nextpage' );
     231                        }
     232                });
    217233
    218         editor.addButton( 'wp_page', {
    219                 tooltip: 'Page break',
    220                 onclick: function() {
    221                         editor.execCommand( 'WP_More', 'nextpage' );
    222                 }
    223         });
     234                editor.addButton( 'wp_help', {
     235                        tooltip: 'Keyboard Shortcuts',
     236                        cmd: 'WP_Help'
     237                });
    224238
    225         editor.addButton( 'wp_help', {
    226                 tooltip: 'Keyboard Shortcuts',
    227                 cmd: 'WP_Help'
    228         });
     239                editor.addButton( 'wp_code', {
     240                        tooltip: 'Code',
     241                        cmd: 'WP_Code',
     242                        stateSelector: 'code'
     243                });
    229244
    230         editor.addButton( 'wp_code', {
    231                 tooltip: 'Code',
    232                 cmd: 'WP_Code',
    233                 stateSelector: 'code'
    234         });
     245                // Menubar
     246                // Insert->Add Media
     247                if ( wp && wp.media && wp.media.editor ) {
     248                        editor.addMenuItem( 'add_media', {
     249                                text: 'Add Media',
     250                                icon: 'wp-media-library',
     251                                context: 'insert',
     252                                cmd: 'WP_Medialib'
     253                        });
     254                }
    235255
    236         // Menubar
    237         // Insert->Add Media
    238         if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
    239                 editor.addMenuItem( 'add_media', {
    240                         text: 'Add Media',
    241                         icon: 'wp-media-library',
     256                // Insert "Read More..."
     257                editor.addMenuItem( 'wp_more', {
     258                        text: 'Insert Read More tag',
     259                        icon: 'wp_more',
    242260                        context: 'insert',
    243                         cmd: 'WP_Medialib'
     261                        onclick: function() {
     262                                editor.execCommand( 'WP_More', 'more' );
     263                        }
    244264                });
    245         }
    246265
    247         // Insert "Read More..."
    248         editor.addMenuItem( 'wp_more', {
    249                 text: 'Insert Read More tag',
    250                 icon: 'wp_more',
    251                 context: 'insert',
    252                 onclick: function() {
    253                         editor.execCommand( 'WP_More', 'more' );
    254                 }
    255         });
     266                // Insert "Next Page"
     267                editor.addMenuItem( 'wp_page', {
     268                        text: 'Page break',
     269                        icon: 'wp_page',
     270                        context: 'insert',
     271                        onclick: function() {
     272                                editor.execCommand( 'WP_More', 'nextpage' );
     273                        }
     274                });
    256275
    257         // Insert "Next Page"
    258         editor.addMenuItem( 'wp_page', {
    259                 text: 'Page break',
    260                 icon: 'wp_page',
    261                 context: 'insert',
    262                 onclick: function() {
    263                         editor.execCommand( 'WP_More', 'nextpage' );
    264                 }
    265         });
     276                editor.on( 'BeforeExecCommand', function(e) {
     277                        if ( tinymce.Env.webkit && ( e.command === 'InsertUnorderedList' || e.command === 'InsertOrderedList' ) ) {
     278                                if ( ! style ) {
     279                                        style = editor.dom.create( 'style', {'type': 'text/css'},
     280                                                '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}');
     281                                }
    266282
    267         editor.on( 'BeforeExecCommand', function(e) {
    268                 if ( tinymce.Env.webkit && ( e.command === 'InsertUnorderedList' || e.command === 'InsertOrderedList' ) ) {
    269                         if ( ! style ) {
    270                                 style = editor.dom.create( 'style', {'type': 'text/css'},
    271                                         '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}');
     283                                editor.getDoc().head.appendChild( style );
    272284                        }
     285                });
    273286
    274                         editor.getDoc().head.appendChild( style );
    275                 }
    276         });
     287                editor.on( 'ExecCommand', function( e ) {
     288                        if ( tinymce.Env.webkit && style &&
     289                                ( 'InsertUnorderedList' === e.command || 'InsertOrderedList' === e.command ) ) {
    277290
    278         editor.on( 'ExecCommand', function( e ) {
    279                 if ( tinymce.Env.webkit && style &&
    280                         ( 'InsertUnorderedList' === e.command || 'InsertOrderedList' === e.command ) ) {
     291                                editor.dom.remove( style );
     292                        }
     293                });
    281294
    282                         editor.dom.remove( style );
    283                 }
    284         });
     295                editor.on( 'init', function() {
     296                        var env = tinymce.Env,
     297                                bodyClass = ['mceContentBody'], // back-compat for themes that use this in editor-style.css...
     298                                doc = editor.getDoc(),
     299                                dom = editor.dom;
    285300
    286         editor.on( 'init', function() {
    287                 var env = tinymce.Env,
    288                         bodyClass = ['mceContentBody'], // back-compat for themes that use this in editor-style.css...
    289                         doc = editor.getDoc(),
    290                         dom = editor.dom;
    291 
    292                 if ( tinymce.Env.iOS ) {
    293                         dom.addClass( doc.documentElement, 'ios' );
    294                 }
    295 
    296                 if ( editor.getParam( 'directionality' ) === 'rtl' ) {
    297                         bodyClass.push('rtl');
    298                         dom.setAttrib( doc.documentElement, 'dir', 'rtl' );
    299                 }
    300 
    301                 if ( env.ie ) {
    302                         if ( parseInt( env.ie, 10 ) === 9 ) {
    303                                 bodyClass.push('ie9');
    304                         } else if ( parseInt( env.ie, 10 ) === 8 ) {
    305                                 bodyClass.push('ie8');
    306                         } else if ( env.ie < 8 ) {
    307                                 bodyClass.push('ie7');
     301                        if ( env.iOS ) {
     302                                dom.addClass( doc.documentElement, 'ios' );
    308303                        }
    309                 } else if ( env.webkit ) {
    310                         bodyClass.push('webkit');
    311                 }
    312304
    313                 bodyClass.push('wp-editor');
    314 
    315                 each( bodyClass, function( cls ) {
    316                         if ( cls ) {
    317                                 dom.addClass( doc.body, cls );
     305                        if ( editor.getParam( 'directionality' ) === 'rtl' ) {
     306                                bodyClass.push('rtl');
     307                                dom.setAttrib( doc.documentElement, 'dir', 'rtl' );
    318308                        }
    319                 });
    320309
    321                 // Remove invalid parent paragraphs when inserting HTML
    322                 // TODO: still needed?
    323                 editor.on( 'BeforeSetContent', function( e ) {
    324                         if ( e.content ) {
    325                                 e.content = e.content.replace(/<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)( [^>]*)?>/gi, '<$1$2>');
    326                                 e.content = e.content.replace(/<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)>\s*<\/p>/gi, '</$1>');
     310                        if ( env.ie ) {
     311                                if ( parseInt( env.ie, 10 ) === 9 ) {
     312                                        bodyClass.push('ie9');
     313                                } else if ( parseInt( env.ie, 10 ) === 8 ) {
     314                                        bodyClass.push('ie8');
     315                                } else if ( env.ie < 8 ) {
     316                                        bodyClass.push('ie7');
     317                                }
     318                        } else if ( env.webkit ) {
     319                                bodyClass.push('webkit');
    327320                        }
    328                 });
    329321
    330                 if ( typeof window.jQuery !== 'undefined' ) {
    331                         window.jQuery( document ).triggerHandler( 'tinymce-editor-init', [editor] );
    332                 }
     322                        bodyClass.push('wp-editor');
    333323
    334                 if ( window.tinyMCEPreInit && window.tinyMCEPreInit.dragDropUpload ) {
    335                         dom.bind( doc, 'dragstart dragend dragover drop', function( event ) {
    336                                 if ( typeof window.jQuery !== 'undefined' ) {
    337                                         // Trigger the jQuery handlers.
    338                                         window.jQuery( document ).trigger( new window.jQuery.Event( event ) );
     324                        each( bodyClass, function( cls ) {
     325                                if ( cls ) {
     326                                        dom.addClass( doc.body, cls );
    339327                                }
    340328                        });
    341                 }
    342329
    343                 if ( editor.getParam( 'wp_paste_filters', true ) ) {
    344                         if ( ! tinymce.Env.webkit ) {
    345                                 // In WebKit handled by removeWebKitStyles()
    346                                 editor.on( 'PastePreProcess', function( event ) {
    347                                         // Remove all inline styles
    348                                         event.content = event.content.replace( /(<[^>]+) style="[^"]*"([^>]*>)/gi, '$1$2' );
     330                        // Remove invalid parent paragraphs when inserting HTML
     331                        editor.on( 'BeforeSetContent', function( event ) {
     332                                if ( event.content ) {
     333                                        event.content = event.content.replace( /<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)( [^>]*)?>/gi, '<$1$2>' )
     334                                                .replace( /<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)>\s*<\/p>/gi, '</$1>' );
     335                                }
     336                        });
    349337
    350                                         // Put back the internal styles
    351                                         event.content = event.content.replace(/(<[^>]+) data-mce-style=([^>]+>)/gi, '$1 style=$2' );
    352                                 });
     338                        if ( $ ) {
     339                                $( document ).triggerHandler( 'tinymce-editor-init', [editor] );
    353340                        }
    354341
    355                         editor.on( 'PastePostProcess', function( event ) {
    356                                 // Remove empty paragraphs
    357                                 each( dom.select( 'p', event.node ), function( node ) {
    358                                         if ( dom.isEmpty( node ) ) {
    359                                                 dom.remove( node );
     342                        if ( window.tinyMCEPreInit && window.tinyMCEPreInit.dragDropUpload ) {
     343                                dom.bind( doc, 'dragstart dragend dragover drop', function( event ) {
     344                                        if ( $ ) {
     345                                                // Trigger the jQuery handlers.
     346                                                $( document ).trigger( new $.Event( event ) );
    360347                                        }
    361348                                });
    362                         });
    363                 }
    364         });
     349                        }
    365350
    366         // Word count
    367         if ( typeof window.jQuery !== 'undefined' ) {
    368                 editor.on( 'keyup', function( e ) {
    369                         var key = e.keyCode || e.charCode;
     351                        if ( editor.getParam( 'wp_paste_filters', true ) ) {
     352                                if ( ! tinymce.Env.webkit ) {
     353                                        // In WebKit handled by removeWebKitStyles()
     354                                        editor.on( 'PastePreProcess', function( event ) {
     355                                                // Remove all inline styles
     356                                                event.content = event.content.replace( /(<[^>]+) style="[^"]*"([^>]*>)/gi, '$1$2' );
    370357
    371                         if ( key === last ) {
    372                                 return;
    373                         }
     358                                                // Put back the internal styles
     359                                                event.content = event.content.replace(/(<[^>]+) data-mce-style=([^>]+>)/gi, '$1 style=$2' );
     360                                        });
     361                                }
    374362
    375                         if ( 13 === key || 8 === last || 46 === last ) {
    376                                 window.jQuery( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
     363                                editor.on( 'PastePostProcess', function( event ) {
     364                                        // Remove empty paragraphs
     365                                        each( dom.select( 'p', event.node ), function( node ) {
     366                                                if ( dom.isEmpty( node ) ) {
     367                                                        dom.remove( node );
     368                                                }
     369                                        });
     370                                });
    377371                        }
    378 
    379                         last = key;
    380372                });
    381         }
    382373
    383         editor.on( 'SaveContent', function( e ) {
    384                 // If editor is hidden, we just want the textarea's value to be saved
    385                 if ( ! editor.inline && editor.isHidden() ) {
    386                         e.content = e.element.value;
    387                         return;
    388                 }
     374                // Word count
     375                if ( $ ) {
     376                        editor.on( 'keyup', function( e ) {
     377                                var key = e.keyCode || e.charCode;
    389378
    390                 // Keep empty paragraphs :(
    391                 e.content = e.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p>&nbsp;</p>' );
     379                                if ( key === last ) {
     380                                        return;
     381                                }
    392382
    393                 if ( editor.getParam( 'wpautop', true ) && typeof window.switchEditors !== 'undefined' ) {
    394                         e.content = window.switchEditors.pre_wpautop( e.content );
     383                                if ( 13 === key || 8 === last || 46 === last ) {
     384                                        $( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
     385                                }
     386
     387                                last = key;
     388                        });
    395389                }
    396         });
    397390
    398         // Remove spaces from empty paragraphs.
    399         editor.on( 'BeforeSetContent', function( event ) {
    400                 var paragraph = tinymce.Env.webkit ? '<p><br /></p>' : '<p></p>';
     391                editor.on( 'SaveContent', function( event ) {
     392                        // If editor is hidden, we just want the textarea's value to be saved
     393                        if ( ! editor.inline && editor.isHidden() ) {
     394                                event.content = event.element.value;
     395                                return;
     396                        }
    401397
    402                 if ( event.content ) {
    403                         event.content = event.content.replace( /<p>(?:&nbsp;|\u00a0|\uFEFF|\s)+<\/p>/gi, paragraph );
    404                 }
    405         });
     398                        // Keep empty paragraphs :(
     399                        event.content = event.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p>&nbsp;</p>' );
    406400
    407         editor.on( 'preInit', function() {
    408                 // Don't replace <i> with <em> and <b> with <strong> and don't remove them when empty
    409                 editor.schema.addValidElements( '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],i,b' );
     401                        if ( hasWpautop ) {
     402                                event.content = wp.editor.removep( event.content );
     403                        }
     404                });
    410405
    411                 if ( tinymce.Env.iOS ) {
    412                         editor.settings.height = 300;
    413                 }
     406                editor.on( 'preInit', function() {
     407                        // Don't replace <i> with <em> and <b> with <strong> and don't remove them when empty
     408                        editor.schema.addValidElements( '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],i,b' );
    414409
    415                 each( {
    416                         c: 'JustifyCenter',
    417                         r: 'JustifyRight',
    418                         l: 'JustifyLeft',
    419                         j: 'JustifyFull',
    420                         q: 'mceBlockQuote',
    421                         u: 'InsertUnorderedList',
    422                         o: 'InsertOrderedList',
    423                         s: 'unlink',
    424                         m: 'WP_Medialib',
    425                         z: 'WP_Adv',
    426                         t: 'WP_More',
    427                         d: 'Strikethrough',
    428                         h: 'WP_Help',
    429                         p: 'WP_Page',
    430                         x: 'WP_Code'
    431                 }, function( command, key ) {
    432                         editor.shortcuts.add( 'access+' + key, '', command );
    433                 } );
     410                        if ( tinymce.Env.iOS ) {
     411                                editor.settings.height = 300;
     412                        }
    434413
    435                 editor.addShortcut( 'meta+s', '', function() {
    436                         if ( typeof wp !== 'undefined' && wp.autosave ) {
    437                                 wp.autosave.server.triggerSave();
     414                        // Start hidden when the Text editor is set to load first.
     415                        if ( tinymce.$( '#wp-' + editor.id + '-wrap' ).hasClass( 'html-active' ) ) {
     416                                editor.hide();
    438417                        }
     418
     419                        each( {
     420                                c: 'JustifyCenter',
     421                                r: 'JustifyRight',
     422                                l: 'JustifyLeft',
     423                                j: 'JustifyFull',
     424                                q: 'mceBlockQuote',
     425                                u: 'InsertUnorderedList',
     426                                o: 'InsertOrderedList',
     427                                s: 'unlink',
     428                                m: 'WP_Medialib',
     429                                z: 'WP_Adv',
     430                                t: 'WP_More',
     431                                d: 'Strikethrough',
     432                                h: 'WP_Help',
     433                                p: 'WP_Page',
     434                                x: 'WP_Code'
     435                        }, function( command, key ) {
     436                                editor.shortcuts.add( 'access+' + key, '', command );
     437                        } );
     438
     439                        editor.addShortcut( 'meta+s', '', function() {
     440                                if ( wp && wp.autosave ) {
     441                                        wp.autosave.server.triggerSave();
     442                                }
     443                        } );
    439444                } );
    440         } );
    441445
    442         /**
    443         * Experimental: create a floating toolbar.
    444         * This functionality will change in the next releases. Not recommended for use by plugins.
    445         */
    446         ( function() {
    447                 var Factory = tinymce.ui.Factory,
    448                         settings = editor.settings,
    449                         currentToolbar,
    450                         currentSelection;
     446                /**
     447                * Experimental: create a floating toolbar.
     448                * This functionality will change in the next releases. Not recommended for use by plugins.
     449                */
     450                ( function() {
     451                        var Factory = tinymce.ui.Factory,
     452                                settings = editor.settings,
     453                                currentToolbar,
     454                                currentSelection;
    451455
    452                 function create( buttons ) {
    453                         var toolbar,
    454                                 toolbarItems = [],
    455                                 buttonGroup;
     456                        function create( buttons ) {
     457                                var toolbar,
     458                                        toolbarItems = [],
     459                                        buttonGroup;
    456460
    457                         each( buttons, function( item ) {
    458                                 var itemName;
     461                                each( buttons, function( item ) {
     462                                        var itemName;
    459463
    460                                 function bindSelectorChanged() {
    461                                         var selection = editor.selection;
     464                                        function bindSelectorChanged() {
     465                                                var selection = editor.selection;
    462466
    463                                         if ( itemName === 'bullist' ) {
    464                                                 selection.selectorChanged( 'ul > li', function( state, args ) {
    465                                                         var i = args.parents.length,
    466                                                                 nodeName;
     467                                                if ( itemName === 'bullist' ) {
     468                                                        selection.selectorChanged( 'ul > li', function( state, args ) {
     469                                                                var i = args.parents.length,
     470                                                                        nodeName;
    467471
    468                                                         while ( i-- ) {
    469                                                                 nodeName = args.parents[ i ].nodeName;
     472                                                                while ( i-- ) {
     473                                                                        nodeName = args.parents[ i ].nodeName;
    470474
    471                                                                 if ( nodeName === 'OL' || nodeName == 'UL' ) {
    472                                                                         break;
     475                                                                        if ( nodeName === 'OL' || nodeName == 'UL' ) {
     476                                                                                break;
     477                                                                        }
    473478                                                                }
    474                                                         }
    475479
    476                                                         item.active( state && nodeName === 'UL' );
    477                                                 } );
    478                                         }
     480                                                                item.active( state && nodeName === 'UL' );
     481                                                        } );
     482                                                }
    479483
    480                                         if ( itemName === 'numlist' ) {
    481                                                 selection.selectorChanged( 'ol > li', function( state, args ) {
    482                                                         var i = args.parents.length,
    483                                                                 nodeName;
     484                                                if ( itemName === 'numlist' ) {
     485                                                        selection.selectorChanged( 'ol > li', function( state, args ) {
     486                                                                var i = args.parents.length,
     487                                                                        nodeName;
    484488
    485                                                         while ( i-- ) {
    486                                                                 nodeName = args.parents[ i ].nodeName;
     489                                                                while ( i-- ) {
     490                                                                        nodeName = args.parents[ i ].nodeName;
    487491
    488                                                                 if ( nodeName === 'OL' || nodeName === 'UL' ) {
    489                                                                         break;
     492                                                                        if ( nodeName === 'OL' || nodeName === 'UL' ) {
     493                                                                                break;
     494                                                                        }
    490495                                                                }
    491                                                         }
    492496
    493                                                         item.active( state && nodeName === 'OL' );
    494                                                 } );
    495                                         }
     497                                                                item.active( state && nodeName === 'OL' );
     498                                                        } );
     499                                                }
    496500
    497                                         if ( item.settings.stateSelector ) {
    498                                                 selection.selectorChanged( item.settings.stateSelector, function( state ) {
    499                                                         item.active( state );
    500                                                 }, true );
    501                                         }
     501                                                if ( item.settings.stateSelector ) {
     502                                                        selection.selectorChanged( item.settings.stateSelector, function( state ) {
     503                                                                item.active( state );
     504                                                        }, true );
     505                                                }
    502506
    503                                         if ( item.settings.disabledStateSelector ) {
    504                                                 selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
    505                                                         item.disabled( state );
    506                                                 } );
     507                                                if ( item.settings.disabledStateSelector ) {
     508                                                        selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
     509                                                                item.disabled( state );
     510                                                        } );
     511                                                }
    507512                                        }
    508                                 }
    509513
    510                                 if ( item === '|' ) {
    511                                         buttonGroup = null;
    512                                 } else {
    513                                         if ( Factory.has( item ) ) {
    514                                                 item = {
    515                                                         type: item
    516                                                 };
    517 
    518                                                 if ( settings.toolbar_items_size ) {
    519                                                         item.size = settings.toolbar_items_size;
    520                                                 }
    521 
    522                                                 toolbarItems.push( item );
    523 
     514                                        if ( item === '|' ) {
    524515                                                buttonGroup = null;
    525516                                        } else {
    526                                                 if ( ! buttonGroup ) {
    527                                                         buttonGroup = {
    528                                                                 type: 'buttongroup',
    529                                                                 items: []
     517                                                if ( Factory.has( item ) ) {
     518                                                        item = {
     519                                                                type: item
    530520                                                        };
    531521
    532                                                         toolbarItems.push( buttonGroup );
    533                                                 }
     522                                                        if ( settings.toolbar_items_size ) {
     523                                                                item.size = settings.toolbar_items_size;
     524                                                        }
    534525
    535                                                 if ( editor.buttons[ item ] ) {
    536                                                         itemName = item;
    537                                                         item = editor.buttons[ itemName ];
     526                                                        toolbarItems.push( item );
    538527
    539                                                         if ( typeof item === 'function' ) {
    540                                                                 item = item();
     528                                                        buttonGroup = null;
     529                                                } else {
     530                                                        if ( ! buttonGroup ) {
     531                                                                buttonGroup = {
     532                                                                        type: 'buttongroup',
     533                                                                        items: []
     534                                                                };
     535
     536                                                                toolbarItems.push( buttonGroup );
    541537                                                        }
    542538
    543                                                         item.type = item.type || 'button';
     539                                                        if ( editor.buttons[ item ] ) {
     540                                                                itemName = item;
     541                                                                item = editor.buttons[ itemName ];
    544542
    545                                                         if ( settings.toolbar_items_size ) {
    546                                                                 item.size = settings.toolbar_items_size;
    547                                                         }
     543                                                                if ( typeof item === 'function' ) {
     544                                                                        item = item();
     545                                                                }
    548546
    549                                                         item = Factory.create( item );
     547                                                                item.type = item.type || 'button';
    550548
    551                                                         buttonGroup.items.push( item );
     549                                                                if ( settings.toolbar_items_size ) {
     550                                                                        item.size = settings.toolbar_items_size;
     551                                                                }
    552552
    553                                                         if ( editor.initialized ) {
    554                                                                 bindSelectorChanged();
    555                                                         } else {
    556                                                                 editor.on( 'init', bindSelectorChanged );
     553                                                                item = Factory.create( item );
     554
     555                                                                buttonGroup.items.push( item );
     556
     557                                                                if ( editor.initialized ) {
     558                                                                        bindSelectorChanged();
     559                                                                } else {
     560                                                                        editor.on( 'init', bindSelectorChanged );
     561                                                                }
    557562                                                        }
    558563                                                }
    559564                                        }
    560                                 }
    561                         } );
     565                                } );
    562566
    563                         toolbar = Factory.create( {
    564                                 type: 'panel',
    565                                 layout: 'stack',
    566                                 classes: 'toolbar-grp inline-toolbar-grp',
    567                                 ariaRoot: true,
    568                                 ariaRemember: true,
    569                                 items: [ {
    570                                         type: 'toolbar',
    571                                         layout: 'flow',
    572                                         items: toolbarItems
    573                                 } ]
    574                         } );
     567                                toolbar = Factory.create( {
     568                                        type: 'panel',
     569                                        layout: 'stack',
     570                                        classes: 'toolbar-grp inline-toolbar-grp',
     571                                        ariaRoot: true,
     572                                        ariaRemember: true,
     573                                        items: [ {
     574                                                type: 'toolbar',
     575                                                layout: 'flow',
     576                                                items: toolbarItems
     577                                        } ]
     578                                } );
    575579
    576                         function hide() {
    577                                 toolbar.hide();
    578                         }
     580                                function hide() {
     581                                        toolbar.hide();
     582                                }
    579583
    580                         function reposition() {
    581                                 var top, left, minTop, className,
    582                                         windowPos, adminbar, mceToolbar, boundary,
    583                                         boundaryMiddle, boundaryVerticalMiddle, spaceTop,
    584                                         spaceBottom, windowWidth, toolbarWidth, toolbarHalf,
    585                                         iframe, iframePos, iframeWidth, iframeHeigth,
    586                                         toolbarNodeHeight, verticalSpaceNeeded,
    587                                         toolbarNode = this.getEl(),
    588                                         buffer = 5,
    589                                         margin = 8,
    590                                         adminbarHeight = 0;
     584                                function reposition() {
     585                                        var top, left, minTop, className,
     586                                                windowPos, adminbar, mceToolbar, boundary,
     587                                                boundaryMiddle, boundaryVerticalMiddle, spaceTop,
     588                                                spaceBottom, windowWidth, toolbarWidth, toolbarHalf,
     589                                                iframe, iframePos, iframeWidth, iframeHeigth,
     590                                                toolbarNodeHeight, verticalSpaceNeeded,
     591                                                toolbarNode = this.getEl(),
     592                                                buffer = 5,
     593                                                margin = 8,
     594                                                adminbarHeight = 0;
    591595
    592                                 if ( ! currentSelection ) {
    593                                         return;
    594                                 }
     596                                        if ( ! currentSelection ) {
     597                                                return;
     598                                        }
    595599
    596                                 windowPos = window.pageYOffset || document.documentElement.scrollTop;
    597                                 adminbar = tinymce.$( '#wpadminbar' )[0];
    598                                 mceToolbar = tinymce.$( '.mce-toolbar-grp', editor.getContainer() )[0];
    599                                 boundary = currentSelection.getBoundingClientRect();
    600                                 boundaryMiddle = ( boundary.left + boundary.right ) / 2;
    601                                 boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2;
    602                                 spaceTop = boundary.top;
    603                                 spaceBottom = iframeHeigth - boundary.bottom;
    604                                 windowWidth = window.innerWidth;
    605                                 toolbarWidth = toolbarNode.offsetWidth;
    606                                 toolbarHalf = toolbarWidth / 2;
    607                                 iframe = document.getElementById( editor.id + '_ifr' );
    608                                 iframePos = DOM.getPos( iframe );
    609                                 iframeWidth = iframe.offsetWidth;
    610                                 iframeHeigth = iframe.offsetHeight;
    611                                 toolbarNodeHeight = toolbarNode.offsetHeight;
    612                                 verticalSpaceNeeded = toolbarNodeHeight + margin + buffer;
     600                                        windowPos = window.pageYOffset || document.documentElement.scrollTop;
     601                                        adminbar = tinymce.$( '#wpadminbar' )[0];
     602                                        mceToolbar = tinymce.$( '.mce-toolbar-grp', editor.getContainer() )[0];
     603                                        boundary = currentSelection.getBoundingClientRect();
     604                                        boundaryMiddle = ( boundary.left + boundary.right ) / 2;
     605                                        boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2;
     606                                        spaceTop = boundary.top;
     607                                        spaceBottom = iframeHeigth - boundary.bottom;
     608                                        windowWidth = window.innerWidth;
     609                                        toolbarWidth = toolbarNode.offsetWidth;
     610                                        toolbarHalf = toolbarWidth / 2;
     611                                        iframe = document.getElementById( editor.id + '_ifr' );
     612                                        iframePos = DOM.getPos( iframe );
     613                                        iframeWidth = iframe.offsetWidth;
     614                                        iframeHeigth = iframe.offsetHeight;
     615                                        toolbarNodeHeight = toolbarNode.offsetHeight;
     616                                        verticalSpaceNeeded = toolbarNodeHeight + margin + buffer;
    613617
    614                                 if ( spaceTop >= verticalSpaceNeeded ) {
    615                                         className = ' mce-arrow-down';
    616                                         top = boundary.top + iframePos.y - toolbarNodeHeight - margin;
    617                                 } else if ( spaceBottom >= verticalSpaceNeeded ) {
    618                                         className = ' mce-arrow-up';
    619                                         top = boundary.bottom + iframePos.y;
    620                                 } else {
    621                                         top = buffer;
    622 
    623                                         if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) {
     618                                        if ( spaceTop >= verticalSpaceNeeded ) {
    624619                                                className = ' mce-arrow-down';
     620                                                top = boundary.top + iframePos.y - toolbarNodeHeight - margin;
     621                                        } else if ( spaceBottom >= verticalSpaceNeeded ) {
     622                                                className = ' mce-arrow-up';
     623                                                top = boundary.bottom + iframePos.y;
    625624                                        } else {
    626                                                 className = ' mce-arrow-up';
     625                                                top = buffer;
     626
     627                                                if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) {
     628                                                        className = ' mce-arrow-down';
     629                                                } else {
     630                                                        className = ' mce-arrow-up';
     631                                                }
    627632                                        }
    628                                 }
    629633
    630                                 // Make sure the image toolbar is below the main toolbar.
    631                                 if ( mceToolbar ) {
    632                                         minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight;
    633                                 } else {
    634                                         minTop = iframePos.y;
    635                                 }
     634                                        // Make sure the image toolbar is below the main toolbar.
     635                                        if ( mceToolbar ) {
     636                                                minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight;
     637                                        } else {
     638                                                minTop = iframePos.y;
     639                                        }
    636640
    637                                 // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window.
    638                                 if ( windowPos ) {
    639                                         if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) {
    640                                                 adminbarHeight = adminbar.clientHeight;
     641                                        // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window.
     642                                        if ( windowPos ) {
     643                                                if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) {
     644                                                        adminbarHeight = adminbar.clientHeight;
     645                                                }
     646
     647                                                if ( windowPos + adminbarHeight > minTop ) {
     648                                                        minTop = windowPos + adminbarHeight;
     649                                                }
    641650                                        }
    642651
    643                                         if ( windowPos + adminbarHeight > minTop ) {
    644                                                 minTop = windowPos + adminbarHeight;
     652                                        if ( top && minTop && ( minTop + buffer > top ) ) {
     653                                                top = minTop + buffer;
     654                                                className = '';
    645655                                        }
    646                                 }
    647656
    648                                 if ( top && minTop && ( minTop + buffer > top ) ) {
    649                                         top = minTop + buffer;
    650                                         className = '';
    651                                 }
     657                                        left = boundaryMiddle - toolbarHalf;
     658                                        left += iframePos.x;
    652659
    653                                 left = boundaryMiddle - toolbarHalf;
    654                                 left += iframePos.x;
     660                                        if ( boundary.left < 0 || boundary.right > iframeWidth ) {
     661                                                left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2;
     662                                        } else if ( toolbarWidth >= windowWidth ) {
     663                                                className += ' mce-arrow-full';
     664                                                left = 0;
     665                                        } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) ||
     666                                                ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) {
    655667
    656                                 if ( boundary.left < 0 || boundary.right > iframeWidth ) {
    657                                         left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2;
    658                                 } else if ( toolbarWidth >= windowWidth ) {
    659                                         className += ' mce-arrow-full';
    660                                         left = 0;
    661                                 } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) ||
    662                                         ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) {
     668                                                left = ( windowWidth - toolbarWidth ) / 2;
     669                                        } else if ( left < iframePos.x ) {
     670                                                className += ' mce-arrow-left';
     671                                                left = boundary.left + iframePos.x;
     672                                        } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) {
     673                                                className += ' mce-arrow-right';
     674                                                left = boundary.right - toolbarWidth + iframePos.x;
     675                                        }
    663676
    664                                         left = ( windowWidth - toolbarWidth ) / 2;
    665                                 } else if ( left < iframePos.x ) {
    666                                         className += ' mce-arrow-left';
    667                                         left = boundary.left + iframePos.x;
    668                                 } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) {
    669                                         className += ' mce-arrow-right';
    670                                         left = boundary.right - toolbarWidth + iframePos.x;
    671                                 }
     677                                        toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
     678                                        toolbarNode.className += className;
    672679
    673                                 toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
    674                                 toolbarNode.className += className;
     680                                        DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
    675681
    676                                 DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
     682                                        return this;
     683                                }
    677684
    678                                 return this;
    679                         }
     685                                toolbar.on( 'show', function() {
     686                                        currentToolbar = this;
     687                                        this.reposition();
     688                                } );
    680689
    681                         toolbar.on( 'show', function() {
    682                                 currentToolbar = this;
    683                                 this.reposition();
    684                         } );
     690                                toolbar.on( 'hide', function() {
     691                                        currentToolbar = false;
     692                                } );
    685693
    686                         toolbar.on( 'hide', function() {
    687                                 currentToolbar = false;
    688                         } );
     694                                toolbar.on( 'keydown', function( event ) {
     695                                        if ( event.keyCode === 27 ) {
     696                                                this.hide();
     697                                                editor.focus();
     698                                        }
     699                                } );
    689700
    690                         toolbar.on( 'keydown', function( event ) {
    691                                 if ( event.keyCode === 27 ) {
    692                                         this.hide();
    693                                         editor.focus();
    694                                 }
    695                         } );
     701                                toolbar.on( 'remove', function() {
     702                                        DOM.unbind( window, 'resize scroll', hide );
     703                                        editor.dom.unbind( editor.getWin(), 'resize scroll', hide );
     704                                        editor.off( 'blur hide', hide );
     705                                } );
    696706
    697                         toolbar.on( 'remove', function() {
    698                                 DOM.unbind( window, 'resize scroll', hide );
    699                                 editor.dom.unbind( editor.getWin(), 'resize scroll', hide );
    700                                 editor.off( 'blur hide', hide );
    701                         } );
     707                                editor.once( 'init', function() {
     708                                        DOM.bind( window, 'resize scroll', hide );
     709                                        editor.dom.bind( editor.getWin(), 'resize scroll', hide );
     710                                        editor.on( 'blur hide', hide );
     711                                } );
    702712
    703                         editor.once( 'init', function() {
    704                                 DOM.bind( window, 'resize scroll', hide );
    705                                 editor.dom.bind( editor.getWin(), 'resize scroll', hide );
    706                                 editor.on( 'blur hide', hide );
    707                         } );
     713                                toolbar.reposition = reposition;
     714                                toolbar.hide().renderTo( document.body );
    708715
    709                         toolbar.reposition = reposition;
    710                         toolbar.hide().renderTo( document.body );
     716                                return toolbar;
     717                        }
    711718
    712                         return toolbar;
    713                 }
     719                        editor.shortcuts.add( 'alt+119', '', function() {
     720                                var node;
    714721
    715                 editor.shortcuts.add( 'alt+119', '', function() {
    716                         var node;
     722                                if ( currentToolbar ) {
     723                                        node = currentToolbar.find( 'toolbar' )[0];
     724                                        node && node.focus( true );
     725                                }
     726                        } );
    717727
    718                         if ( currentToolbar ) {
    719                                 node = currentToolbar.find( 'toolbar' )[0];
    720                                 node && node.focus( true );
    721                         }
    722                 } );
     728                        editor.on( 'nodechange', function( event ) {
     729                                var collapsed = editor.selection.isCollapsed();
    723730
    724                 editor.on( 'nodechange', function( event ) {
    725                         var collapsed = editor.selection.isCollapsed();
     731                                var args = {
     732                                        element: event.element,
     733                                        parents: event.parents,
     734                                        collapsed: collapsed
     735                                };
    726736
    727                         var args = {
    728                                 element: event.element,
    729                                 parents: event.parents,
    730                                 collapsed: collapsed
    731                         };
     737                                editor.fire( 'wptoolbar', args );
    732738
    733                         editor.fire( 'wptoolbar', args );
     739                                currentSelection = args.selection || args.element;
    734740
    735                         currentSelection = args.selection || args.element;
     741                                currentToolbar && currentToolbar.hide();
     742                                args.toolbar && args.toolbar.show();
     743                        } );
    736744
    737                         currentToolbar && currentToolbar.hide();
    738                         args.toolbar && args.toolbar.show();
    739                 } );
     745                        editor.wp = editor.wp || {};
     746                        editor.wp._createToolbar = create;
     747                }());
    740748
    741                 editor.wp = editor.wp || {};
    742                 editor.wp._createToolbar = create;
    743         }());
     749                function noop() {}
    744750
    745         function noop() {}
     751                // Expose some functions (back-compat)
     752                return {
     753                        _showButtons: noop,
     754                        _hideButtons: noop,
     755                        _setEmbed: noop,
     756                        _getEmbed: noop
     757                };
     758        });
    746759
    747         // Expose some functions (back-compat)
    748         return {
    749                 _showButtons: noop,
    750                 _hideButtons: noop,
    751                 _setEmbed: noop,
    752                 _getEmbed: noop
    753         };
    754 });
     760}( window.tinymce, window.jQuery, window.wp ));