WordPress.org

Make WordPress Core

Ticket #32425: 32425.1.patch

File 32425.1.patch, 82.1 KB (added by azaozz, 6 years ago)
  • src/wp-admin/js/editor.js

     
    1 /* global tinymce, tinyMCEPreInit, QTags, setUserSetting */
    21
    3 window.switchEditors = {
     2( function( $ ) {
     3        function switchEditors() {
     4                var tinymce, mce$, exports;
    45
    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 );
     6                function init() {
     7                        if ( ! tinymce && window.tinymce ) {
     8                                tinymce = window.tinymce;
     9                                mce$ = tinymce.$;
    1010
    11                 this.go( id, mode );
    12         },
     11                                mce$( document ).on( 'click', function( event ) {
     12                                        var id, mode, target = mce$( event.target );
    1313
    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';
     14                                        if ( target.hasClass( 'wp-switch-editor' ) ) {
     15                                                id = target.attr( 'data-wp-editor-id' );
     16                                                mode = target.hasClass( 'switch-tmce' ) ? 'tmce' : 'html';
     17                                                switchEditor( id, mode );
     18                                        }
     19                                });
    3120                        }
    3221                }
    3322
    34                 function getToolbarHeight() {
    35                         var node = DOM.select( '.mce-toolbar-grp', ed.getContainer() )[0],
     23                function getToolbarHeight( editor ) {
     24                        var node = mce$( '.mce-toolbar-grp', editor.getContainer() )[0],
    3625                                height = node && node.clientHeight;
    3726
    3827                        if ( height && height > 10 && height < 200 ) {
     
    4231                        return 30;
    4332                }
    4433
    45                 if ( 'tmce' === mode || 'tinymce' === mode ) {
    46                         if ( ed && ! ed.isHidden() ) {
    47                                 return false;
    48                         }
     34                function switchEditor( id, mode ) {
     35                        id = id || 'content';
     36                        mode = mode || 'toggle';
    4937
    50                         if ( typeof( QTags ) !== 'undefined' ) {
    51                                 QTags.closeAllTags( id );
    52                         }
     38                        var editorHeight, toolbarHeight, iframe,
     39                                editor = tinymce.get( id ),
     40                                wrap = mce$( '#wp-' + id + '-wrap' ),
     41                                $textarea = mce$( '#' + id ),
     42                                textarea = $textarea[0];
    5343
    54                         editorHeight = txtarea_el ? parseInt( txtarea_el.style.height, 10 ) : 0;
    55 
    56                         if ( tinyMCEPreInit.mceInit[ id ] && tinyMCEPreInit.mceInit[ id ].wpautop ) {
    57                                 txtarea_el.value = t.wpautop( txtarea_el.value );
     44                        if ( 'toggle' === mode ) {
     45                                if ( editor && ! editor.isHidden() ) {
     46                                        mode = 'html';
     47                                } else {
     48                                        mode = 'tmce';
     49                                }
    5850                        }
    5951
    60                         if ( ed ) {
    61                                 ed.show();
     52                        if ( 'tmce' === mode || 'tinymce' === mode ) {
     53                                if ( editor && ! editor.isHidden() ) {
     54                                        return false;
     55                                }
    6256
    63                                 // No point resizing the iframe in iOS
    64                                 if ( ! tinymce.Env.iOS && editorHeight ) {
    65                                         toolbarHeight = getToolbarHeight();
    66                                         editorHeight = editorHeight - toolbarHeight + 14;
    67 
    68                                         // height cannot be under 50 or over 5000
    69                                         if ( editorHeight > 50 && editorHeight < 5000 ) {
    70                                                 ed.theme.resizeTo( null, editorHeight );
    71                                         }
     57                                if ( typeof( window.QTags ) !== 'undefined' ) {
     58                                        window.QTags.closeAllTags( id );
    7259                                }
    73                         } else {
    74                                 tinymce.init( tinyMCEPreInit.mceInit[id] );
    75                         }
    7660
    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' );
     61                                editorHeight = parseInt( textarea.style.height, 10 ) || 0;
    8162
    82                 } else if ( 'html' === mode ) {
     63                                if ( editor ) {
     64                                        editor.show();
    8365
    84                         if ( ed && ed.isHidden() ) {
    85                                 return false;
    86                         }
     66                                        // No point resizing the iframe in iOS
     67                                        if ( ! tinymce.Env.iOS && editorHeight ) {
     68                                                toolbarHeight = getToolbarHeight( editor );
     69                                                editorHeight = editorHeight - toolbarHeight + 14;
    8770
    88                         if ( ed ) {
    89                                 if ( ! tinymce.Env.iOS ) {
    90                                         iframe = DOM.get( id + '_ifr' );
    91                                         editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
    92 
    93                                         if ( editorHeight ) {
    94                                                 toolbarHeight = getToolbarHeight();
    95                                                 editorHeight = editorHeight + toolbarHeight - 14;
    96 
    9771                                                // height cannot be under 50 or over 5000
    9872                                                if ( editorHeight > 50 && editorHeight < 5000 ) {
    99                                                         txtarea_el.style.height = editorHeight + 'px';
     73                                                        editor.theme.resizeTo( null, editorHeight );
    10074                                                }
    10175                                        }
     76                                } else {
     77                                        tinymce.init( window.tinyMCEPreInit.mceInit[id] );
    10278                                }
    10379
    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 );
     80                                wrap.removeClass( 'html-active' ).addClass( 'tmce-active' );
     81                                $textarea.attr( 'aria-hidden', true );
     82                                window.setUserSetting( 'editor', 'tinymce' );
     83
     84                        } else if ( 'html' === mode ) {
     85                                if ( editor && editor.isHidden() ) {
     86                                        return false;
    10987                                }
    11088
    111                                 DOM.setStyles( txtarea_el, {'display': '', 'visibility': ''} );
    112                         }
     89                                if ( editor ) {
     90                                        if ( ! tinymce.Env.iOS ) {
     91                                                iframe = editor.iframeElement;
     92                                                editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
    11393
    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' );
    118                 }
    119                 return false;
    120         },
     94                                                if ( editorHeight ) {
     95                                                        toolbarHeight = getToolbarHeight( editor );
     96                                                        editorHeight = editorHeight + toolbarHeight - 14;
    12197
    122         _wp_Nop: function( content ) {
    123                 var blocklist1, blocklist2,
    124                         preserve_linebreaks = false,
    125                         preserve_br = false;
     98                                                        // height cannot be under 50 or over 5000
     99                                                        if ( editorHeight > 50 && editorHeight < 5000 ) {
     100                                                                textarea.style.height = editorHeight + 'px';
     101                                                        }
     102                                                }
     103                                        }
    126104
    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                 }
     105                                        editor.hide();
     106                                } else {
     107                                        // The TinyMCE instance doesn't exist, show the textarea
     108                                        $textarea.css({ 'display': '', 'visibility': '' });
     109                                }
    136110
    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                 }
    144 
    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>' );
    149 
    150                 // Mark </p> if it has any attributes.
    151                 content = content.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
    152 
    153                 // Separate <div> containing <p>
    154                 content = content.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
    155 
    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' );
    161 
    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' );
    167 
    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>' );
    172 
    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                 }
    177 
    178                 if ( content.indexOf( '<hr' ) !== -1 ) {
    179                         content = content.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
    180                 }
    181 
    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                 }
    187 
    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' );
    191 
    192                 // Trim whitespace
    193                 content = content.replace( /^\s+/, '' );
    194                 content = content.replace( /[\s\u00a0]+$/, '' );
    195 
    196                 // put back the line breaks in pre|script
    197                 if ( preserve_linebreaks ) {
    198                         content = content.replace( /<wp-line-break>/g, '\n' );
    199                 }
    200 
    201                 // and the <br> tags in captions
    202                 if ( preserve_br ) {
    203                         content = content.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
    204                 }
    205 
    206                 return content;
    207         },
    208 
    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';
    215 
    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                 }
    221 
    222                 pee = pee.replace( /<[^<>]+>/g, function( a ){
    223                         return a.replace( /[\r\n]+/g, ' ' );
    224                 });
    225 
    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>' );
    231                         });
    232                 }
    233 
    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]+/, ' ' );
    243                                 });
    244                                 // convert remaining line breaks to <br>
    245                                 return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
    246                         });
    247                 }
    248 
    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]' );
    270 
    271                 pee = pee.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
    272                         if ( c.match( /<p( [^>]*)?>/ ) ) {
    273                                 return a;
     111                                wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
     112                                $textarea.attr( 'aria-hidden', false );
     113                                window.setUserSetting( 'editor', 'html' );
    274114                        }
    275 
    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' );
    282115                }
    283116
    284                 if ( preserve_br ) {
    285                         pee = pee.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     117                if ( $ ) {
     118                        $( document ).ready( init );
     119                } else if ( document.addEventListener ) {
     120                        document.addEventListener( 'DOMContentLoaded', init, false );
     121                        window.addEventListener( 'load', init, false );
     122                } else if ( window.attachEvent ) {
     123                        window.attachEvent( 'onload', init );
     124                        document.attachEvent( 'onreadystatechange', function() {
     125                                if ( 'complete' === document.readyState ) {
     126                                        init();
     127                                }
     128                        } );
    286129                }
    287130
    288                 return pee;
    289         },
     131                return { go: switchEditor };
     132        }
    290133
    291         pre_wpautop: function( content ) {
    292                 var t = this, o = { o: t, data: content, unfiltered: content },
    293                         q = typeof( jQuery ) !== 'undefined';
    294 
    295                 if ( q ) {
    296                         jQuery( 'body' ).trigger( 'beforePreWpautop', [ o ] );
    297                 }
    298 
    299                 o.data = t._wp_Nop( o.data );
    300 
    301                 if ( q ) {
    302                         jQuery('body').trigger('afterPreWpautop', [ o ] );
    303                 }
    304 
    305                 return o.data;
    306         },
    307 
    308         wpautop: function( pee ) {
    309                 var t = this, o = { o: t, data: pee, unfiltered: pee },
    310                         q = typeof( jQuery ) !== 'undefined';
    311 
    312                 if ( q ) {
    313                         jQuery( 'body' ).trigger('beforeWpautop', [ o ] );
    314                 }
    315 
    316                 o.data = t._wp_Autop( o.data );
    317 
    318                 if ( q ) {
    319                         jQuery( 'body' ).trigger('afterWpautop', [ o ] );
    320                 }
    321 
    322                 return o.data;
    323         }
    324 };
     134        window.switchEditors = new switchEditors();
     135}( 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="' . esc_attr( trim( $set['editor_class'] ) ) . ' wp-editor-area"';
    149148                $tabindex = $set['tabindex'] ? ' tabindex="' . (int) $set['tabindex'] . '"' : '';
    150149                $switch_class = 'html-active';
    151150                $toolbar = $buttons = $autocomplete = '';
     151                $default_editor = null;
     152                $editor_id_attr = esc_attr( $editor_id );
    152153
    153154                if ( $set['drag_drop_upload'] ) {
    154155                        self::$drag_drop_upload = true;
    155156                }
    156157
    157                 if ( ! empty( $set['editor_height'] ) )
    158                         $height = ' style="height: ' . $set['editor_height'] . 'px"';
    159                 else
    160                         $height = ' rows="' . $set['textarea_rows'] . '"';
     158                if ( ! empty( $set['editor_height'] ) ) {
     159                        $height = ' style="height: ' . (int) $set['editor_height'] . 'px"';
     160                } else {
     161                        $height = ' rows="' . (int) $set['textarea_rows'] . '"';
     162                }
    161163
    162                 if ( !current_user_can( 'upload_files' ) )
     164                if ( ! current_user_can( 'upload_files' ) ) {
    163165                        $set['media_buttons'] = false;
     166                }
    164167
    165                 if ( ! self::$this_quicktags && self::$this_tinymce ) {
    166                         $switch_class = 'tmce-active';
     168                if ( self::$this_tinymce ) {
    167169                        $autocomplete = ' autocomplete="off"';
    168                 } elseif ( self::$this_quicktags && self::$this_tinymce ) {
    169170                        $default_editor = $set['default_editor'] ? $set['default_editor'] : wp_default_editor();
    170                         $autocomplete = ' autocomplete="off"';
    171171
    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';
     172                        if ( self::$this_quicktags ) {
     173                                // 'html' is used for the "Text" editor tab.
     174                                if ( 'html' !== $default_editor ) {
     175                                        $switch_class = 'tmce-active';
     176                                }
     177
     178                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-tmce" class="wp-switch-editor switch-tmce"' .
     179                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . __('Visual') . "</button>\n";
     180                                $buttons .= '<button type="button" id="' . $editor_id_attr . '-html" class="wp-switch-editor switch-html"' .
     181                                        ' data-wp-editor-id="' . $editor_id_attr . '">' . _x( 'Text', 'Name for the Text editor tab (formerly HTML)' ) . "</button>\n";
    176182                        } else {
    177                                 add_filter('the_editor_content', 'wp_richedit_pre');
    178183                                $switch_class = 'tmce-active';
    179184                        }
    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";
    183185                }
    184186
    185187                $wrap_class = 'wp-core-ui wp-editor-wrap ' . $switch_class;
     
    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.
     
    244260                 */
    245261                $content = apply_filters( 'the_editor_content', $content );
    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 ( null !== $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
     
    329355                                         * @param array  $plugins   An array of teenyMCE plugins.
    330356                                         * @param string $editor_id Unique editor identifier, e.g. 'content'.
    331357                                         */
    332                                         self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
     358                                        self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'wpfunctions', 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
    333359                                } else {
    334360
    335361                                        /**
     
    352378                                        $mce_external_plugins = apply_filters( 'mce_external_plugins', array() );
    353379
    354380                                        $plugins = array(
     381                                                'wpfunctions',
    355382                                                'charmap',
    356383                                                'colorpicker',
    357384                                                'hr',
     
    477504                                        'theme' => 'modern',
    478505                                        'skin' => 'lightgray',
    479506                                        'language' => self::$mce_locale,
    480                                         'formats' => "{
    481                                                 alignleft: [
    482                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}},
    483                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignleft'}
    484                                                 ],
    485                                                 aligncenter: [
    486                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}},
    487                                                         {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'}
    488                                                 ],
    489                                                 alignright: [
    490                                                         {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}},
    491                                                         {selector: 'img,table,dl.wp-caption', classes: 'alignright'}
    492                                                 ],
    493                                                 strikethrough: {inline: 'del'}
    494                                         }",
     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                                        '}',
    495522                                        'relative_urls' => false,
    496523                                        'remove_script_host' => false,
    497524                                        'convert_urls' => false,
     
    649676                                'body_class' => $body_class
    650677                        );
    651678
    652                         if ( $first_run )
    653                                 $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 );
    654681
    655682                        if ( is_array( $set['tinymce'] ) )
    656683                                $mceInit = array_merge( $mceInit, $set['tinymce'] );
     
    661688                         * before tinyMCE.init. Setting "valid_elements", "invalid_elements"
    662689                         * and "extended_valid_elements" can be done through this filter. Best
    663690                         * is to use the default cleanup by not specifying valid_elements,
    664                          * as TinyMCE contains full set of XHTML 1.0.
     691                         * as TinyMCE checks against the full set of HTML 5.0 elements and attributes.
    665692                         */
    666693                        if ( $set['teeny'] ) {
    667694
     
    11811208                ?>
    11821209
    11831210                ( function() {
    1184                         var init, edId, qtId, firstInit, wrapper;
     1211                        var init, id;
    11851212
    11861213                        if ( typeof tinymce !== 'undefined' ) {
    1187                                 for ( edId in tinyMCEPreInit.mceInit ) {
    1188                                         if ( firstInit ) {
    1189                                                 init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] );
    1190                                         } else {
    1191                                                 init = firstInit = tinyMCEPreInit.mceInit[edId];
    1192                                         }
     1214                                for ( id in tinyMCEPreInit.mceInit ) {
     1215                                        init = tinyMCEPreInit.mceInit[id];
    11931216
    1194                                         wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0];
     1217                                        if ( ! init.wp_skip_init ) {
     1218                                                tinymce.init( init );
    11951219
    1196                                         if ( ( tinymce.DOM.hasClass( wrapper, 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) &&
    1197                                                 ! init.wp_skip_init ) {
    1198 
    1199                                                 try {
    1200                                                         tinymce.init( init );
    1201 
    1202                                                         if ( ! window.wpActiveEditor ) {
    1203                                                                 window.wpActiveEditor = edId;
    1204                                                         }
    1205                                                 } catch(e){}
     1220                                                if ( ! window.wpActiveEditor ) {
     1221                                                        window.wpActiveEditor = id;
     1222                                                }
    12061223                                        }
    12071224                                }
    12081225                        }
    12091226
    12101227                        if ( typeof quicktags !== 'undefined' ) {
    1211                                 for ( qtId in tinyMCEPreInit.qtInit ) {
    1212                                         try {
    1213                                                 quicktags( tinyMCEPreInit.qtInit[qtId] );
     1228                                for ( id in tinyMCEPreInit.qtInit ) {
     1229                                        quicktags( tinyMCEPreInit.qtInit[id] );
    12141230
    1215                                                 if ( ! window.wpActiveEditor ) {
    1216                                                         window.wpActiveEditor = qtId;
    1217                                                 }
    1218                                         } catch(e){};
    1219                                 }
    1220                         }
    1221 
    1222                         if ( typeof jQuery !== 'undefined' ) {
    1223                                 jQuery('.wp-editor-wrap').on( 'click.wp-editor', function() {
    1224                                         if ( this.id ) {
    1225                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
     1231                                        if ( ! window.wpActiveEditor ) {
     1232                                                window.wpActiveEditor = id;
    12261233                                        }
    1227                                 });
    1228                         } else {
    1229                                 for ( qtId in tinyMCEPreInit.qtInit ) {
    1230                                         document.getElementById( 'wp-' + qtId + '-wrap' ).onclick = function() {
    1231                                                 window.wpActiveEditor = this.id.slice( 3, -5 );
    1232                                         }
    12331234                                }
    12341235                        }
    12351236                }());
  • 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

     
    346346add_action( 'before_delete_post', '_reset_front_page_settings_for_post' );
    347347add_action( 'wp_trash_post',      '_reset_front_page_settings_for_post' );
    348348
     349// Prepare the content for both Visual and Text editors
     350add_filter( 'the_editor_content', 'format_for_editor' );
     351
    349352// Post Formats
    350353add_filter( 'request', '_post_format_request' );
    351354add_filter( 'term_link', '_post_format_link', 10, 3 );
  • 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
     3006 * The filter 'format_for_editor' is applied here. If $text is empty the filter will
    30073007 * be applied to an empty string.
    30083008 *
    3009  * @since 2.0.0
     3009 * @since 4.3.0
    30103010 *
    30113011 * @param string $text The text to be formatted.
    30123012 * @return string The formatted text after filter is applied.
    30133013 */
    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', '' );
     3014function format_for_editor( $text ) {
     3015        if ( ! empty( $text ) ) {
     3016                $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > &
    30313017        }
    30323018
    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 
    30563019        /**
    3057          * Filter the text before it is formatted for the HTML editor.
     3020         * Filter the text after it is formatted for the editor.
    30583021         *
    3059          * @since 2.5.0
     3022         * @since 4.3.0
    30603023         *
    3061          * @param string $output The HTML-formatted text.
     3024         * @param string $text The formatted text.
    30623025         */
    3063         return apply_filters( 'htmledit_pre', $output );
     3026        return apply_filters( 'format_for_editor', $text );
    30643027}
    30653028
    30663029/**
  • 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

     
    88                each = tinymce.each,
    99                __ = editor.editorManager.i18n.translate,
    1010                wpAdvButton, style,
     11                hasWpautop = ( tinymce.wp && tinymce.wp.autop && editor.getParam( 'wpautop', true ) ),
     12                jq = window.jQuery,
    1113                last = 0;
    1214
    13         if ( typeof window.jQuery !== 'undefined' ) {
    14                 window.jQuery( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
     15        if ( jq ) {
     16                jq( document ).triggerHandler( 'tinymce-editor-setup', [ editor ] );
    1517        }
    1618
    1719        function toggleToolbars( state ) {
     
    8991    });
    9092
    9193        // Replace Read More/Next Page tags with images
    92         editor.on( 'BeforeSetContent', function( e ) {
    93                 var title;
     94        editor.on( 'BeforeSetContent', function( event ) {
     95                var title,
     96                        paragraph = tinymce.Env.webkit ? '<p><br /></p>' : '<p></p>';
    9497
    95                 if ( e.content ) {
    96                         if ( e.content.indexOf( '<!--more' ) !== -1 ) {
     98                if ( event.content ) {
     99                        if ( event.content.indexOf( '<!--more' ) !== -1 ) {
    97100                                title = __( 'Read more...' );
    98101
    99                                 e.content = e.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
     102                                event.content = event.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
    100103                                        return '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="more" data-wp-more-text="' + moretext + '" ' +
    101104                                                'class="wp-more-tag mce-wp-more" title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />';
    102105                                });
    103106                        }
    104107
    105                         if ( e.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
     108                        if ( event.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
    106109                                title = __( 'Page break' );
    107110
    108                                 e.content = e.content.replace( /<!--nextpage-->/g,
     111                                event.content = event.content.replace( /<!--nextpage-->/g,
    109112                                        '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="nextpage" class="wp-more-tag mce-wp-nextpage" ' +
    110113                                                'title="' + title + '" data-mce-resize="false" data-mce-placeholder="1" />' );
    111114                        }
     115
     116                        // Remove spaces from empty paragraphs.
     117                        event.content = event.content.replace( /<p>(?:&nbsp;|\u00a0|\uFEFF|\s)+<\/p>/gi, paragraph );
     118
     119                        if ( event.load && event.format !== 'raw' && hasWpautop ) {
     120                                event.content = tinymce.wp.autop( event.content, editor );
     121                        }
    112122                }
    113123        });
    114124
     
    289299                        doc = editor.getDoc(),
    290300                        dom = editor.dom;
    291301
    292                 if ( tinymce.Env.iOS ) {
     302                if ( env.iOS ) {
    293303                        dom.addClass( doc.documentElement, 'ios' );
    294304                }
    295305
     
    319329                });
    320330
    321331                // 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>');
     332                editor.on( 'BeforeSetContent', function( event ) {
     333                        if ( event.content ) {
     334                                event.content = event.content.replace( /<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)( [^>]*)?>/gi, '<$1$2>' )
     335                                        .replace( /<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre)>\s*<\/p>/gi, '</$1>' );
    327336                        }
    328337                });
    329338
    330                 if ( typeof window.jQuery !== 'undefined' ) {
    331                         window.jQuery( document ).triggerHandler( 'tinymce-editor-init', [editor] );
     339                if ( jq ) {
     340                        jq( document ).triggerHandler( 'tinymce-editor-init', [editor] );
    332341                }
    333342
    334343                if ( window.tinyMCEPreInit && window.tinyMCEPreInit.dragDropUpload ) {
    335344                        dom.bind( doc, 'dragstart dragend dragover drop', function( event ) {
    336                                 if ( typeof window.jQuery !== 'undefined' ) {
     345                                if ( jq ) {
    337346                                        // Trigger the jQuery handlers.
    338                                         window.jQuery( document ).trigger( new window.jQuery.Event( event ) );
     347                                        jq( document ).trigger( new jq.Event( event ) );
    339348                                }
    340349                        });
    341350                }
     
    364373        });
    365374
    366375        // Word count
    367         if ( typeof window.jQuery !== 'undefined' ) {
     376        if ( jq ) {
    368377                editor.on( 'keyup', function( e ) {
    369378                        var key = e.keyCode || e.charCode;
    370379
     
    373382                        }
    374383
    375384                        if ( 13 === key || 8 === last || 46 === last ) {
    376                                 window.jQuery( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
     385                                jq( document ).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
    377386                        }
    378387
    379388                        last = key;
     
    380389                });
    381390        }
    382391
    383         editor.on( 'SaveContent', function( e ) {
     392        editor.on( 'SaveContent', function( event ) {
    384393                // If editor is hidden, we just want the textarea's value to be saved
    385394                if ( ! editor.inline && editor.isHidden() ) {
    386                         e.content = e.element.value;
     395                        event.content = event.element.value;
    387396                        return;
    388397                }
    389398
    390399                // Keep empty paragraphs :(
    391                 e.content = e.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p>&nbsp;</p>' );
     400                event.content = event.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p>&nbsp;</p>' );
    392401
    393                 if ( editor.getParam( 'wpautop', true ) && typeof window.switchEditors !== 'undefined' ) {
    394                         e.content = window.switchEditors.pre_wpautop( e.content );
     402                if ( hasWpautop ) {
     403                        event.content = tinymce.wp.removep( event.content, editor );
    395404                }
    396405        });
    397406
    398         // Remove spaces from empty paragraphs.
    399         editor.on( 'BeforeSetContent', function( event ) {
    400                 var paragraph = tinymce.Env.webkit ? '<p><br /></p>' : '<p></p>';
    401 
    402                 if ( event.content ) {
    403                         event.content = event.content.replace( /<p>(?:&nbsp;|\u00a0|\uFEFF|\s)+<\/p>/gi, paragraph );
    404                 }
    405         });
    406 
    407407        editor.on( 'preInit', function() {
    408408                // Don't replace <i> with <em> and <b> with <strong> and don't remove them when empty
    409409                editor.schema.addValidElements( '@[id|accesskey|class|dir|lang|style|tabindex|title|contenteditable|draggable|dropzone|hidden|spellcheck|translate],i,b' );
     
    412412                        editor.settings.height = 300;
    413413                }
    414414
     415                // Start hidden when the Text editor is set to load first.
     416                if ( tinymce.$( '#wp-' + editor.id + '-wrap' ).hasClass( 'html-active' ) ) {
     417                        editor.hide();
     418                }
     419
    415420                each( {
    416421                        c: 'JustifyCenter',
    417422                        r: 'JustifyRight',
     
    439444                } );
    440445        } );
    441446
    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;
    451 
    452                 function create( buttons ) {
    453                         var toolbar,
    454                                 toolbarItems = [],
    455                                 buttonGroup;
    456 
    457                         each( buttons, function( item ) {
    458                                 var itemName;
    459 
    460                                 function bindSelectorChanged() {
    461                                         var selection = editor.selection;
    462 
    463                                         if ( itemName === 'bullist' ) {
    464                                                 selection.selectorChanged( 'ul > li', function( state, args ) {
    465                                                         var i = args.parents.length,
    466                                                                 nodeName;
    467 
    468                                                         while ( i-- ) {
    469                                                                 nodeName = args.parents[ i ].nodeName;
    470 
    471                                                                 if ( nodeName === 'OL' || nodeName == 'UL' ) {
    472                                                                         break;
    473                                                                 }
    474                                                         }
    475 
    476                                                         item.active( state && nodeName === 'UL' );
    477                                                 } );
    478                                         }
    479 
    480                                         if ( itemName === 'numlist' ) {
    481                                                 selection.selectorChanged( 'ol > li', function( state, args ) {
    482                                                         var i = args.parents.length,
    483                                                                 nodeName;
    484 
    485                                                         while ( i-- ) {
    486                                                                 nodeName = args.parents[ i ].nodeName;
    487 
    488                                                                 if ( nodeName === 'OL' || nodeName === 'UL' ) {
    489                                                                         break;
    490                                                                 }
    491                                                         }
    492 
    493                                                         item.active( state && nodeName === 'OL' );
    494                                                 } );
    495                                         }
    496 
    497                                         if ( item.settings.stateSelector ) {
    498                                                 selection.selectorChanged( item.settings.stateSelector, function( state ) {
    499                                                         item.active( state );
    500                                                 }, true );
    501                                         }
    502 
    503                                         if ( item.settings.disabledStateSelector ) {
    504                                                 selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
    505                                                         item.disabled( state );
    506                                                 } );
    507                                         }
    508                                 }
    509 
    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 
    524                                                 buttonGroup = null;
    525                                         } else {
    526                                                 if ( ! buttonGroup ) {
    527                                                         buttonGroup = {
    528                                                                 type: 'buttongroup',
    529                                                                 items: []
    530                                                         };
    531 
    532                                                         toolbarItems.push( buttonGroup );
    533                                                 }
    534 
    535                                                 if ( editor.buttons[ item ] ) {
    536                                                         itemName = item;
    537                                                         item = editor.buttons[ itemName ];
    538 
    539                                                         if ( typeof item === 'function' ) {
    540                                                                 item = item();
    541                                                         }
    542 
    543                                                         item.type = item.type || 'button';
    544 
    545                                                         if ( settings.toolbar_items_size ) {
    546                                                                 item.size = settings.toolbar_items_size;
    547                                                         }
    548 
    549                                                         item = Factory.create( item );
    550 
    551                                                         buttonGroup.items.push( item );
    552 
    553                                                         if ( editor.initialized ) {
    554                                                                 bindSelectorChanged();
    555                                                         } else {
    556                                                                 editor.on( 'init', bindSelectorChanged );
    557                                                         }
    558                                                 }
    559                                         }
    560                                 }
    561                         } );
    562 
    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                         } );
    575 
    576                         function hide() {
    577                                 toolbar.hide();
    578                         }
    579 
    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;
    591 
    592                                 if ( ! currentSelection ) {
    593                                         return;
    594                                 }
    595 
    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;
    613 
    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 ) {
    624                                                 className = ' mce-arrow-down';
    625                                         } else {
    626                                                 className = ' mce-arrow-up';
    627                                         }
    628                                 }
    629 
    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                                 }
    636 
    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                                         }
    642 
    643                                         if ( windowPos + adminbarHeight > minTop ) {
    644                                                 minTop = windowPos + adminbarHeight;
    645                                         }
    646                                 }
    647 
    648                                 if ( top && minTop && ( minTop + buffer > top ) ) {
    649                                         top = minTop + buffer;
    650                                         className = '';
    651                                 }
    652 
    653                                 left = boundaryMiddle - toolbarHalf;
    654                                 left += iframePos.x;
    655 
    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 ) ) {
    663 
    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                                 }
    672 
    673                                 toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
    674                                 toolbarNode.className += className;
    675 
    676                                 DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
    677 
    678                                 return this;
    679                         }
    680 
    681                         toolbar.on( 'show', function() {
    682                                 currentToolbar = this;
    683                                 this.reposition();
    684                         } );
    685 
    686                         toolbar.on( 'hide', function() {
    687                                 currentToolbar = false;
    688                         } );
    689 
    690                         toolbar.on( 'keydown', function( event ) {
    691                                 if ( event.keyCode === 27 ) {
    692                                         this.hide();
    693                                         editor.focus();
    694                                 }
    695                         } );
    696 
    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                         } );
    702 
    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                         } );
    708 
    709                         toolbar.reposition = reposition;
    710                         toolbar.hide().renderTo( document.body );
    711 
    712                         return toolbar;
    713                 }
    714 
    715                 editor.shortcuts.add( 'alt+119', '', function() {
    716                         var node;
    717 
    718                         if ( currentToolbar ) {
    719                                 node = currentToolbar.find( 'toolbar' )[0];
    720                                 node && node.focus( true );
    721                         }
    722                 } );
    723 
    724                 editor.on( 'nodechange', function( event ) {
    725                         var collapsed = editor.selection.isCollapsed();
    726 
    727                         var args = {
    728                                 element: event.element,
    729                                 parents: event.parents,
    730                                 collapsed: collapsed
    731                         };
    732 
    733                         editor.fire( 'wptoolbar', args );
    734 
    735                         currentSelection = args.selection || args.element;
    736 
    737                         currentToolbar && currentToolbar.hide();
    738                         args.toolbar && args.toolbar.show();
    739                 } );
    740 
    741                 editor.wp = editor.wp || {};
    742                 editor.wp._createToolbar = create;
    743         }());
    744 
    745447        function noop() {}
    746448
    747449        // Expose some functions (back-compat)
  • src/wp-includes/js/tinymce/plugins/wpfunctions/plugin.js

     
     1// Common WordPress functions
     2( function( tinymce, $ ) {
     3        var $document, unfiltered;
     4       
     5        if ( $ ) {
     6                $document = $( document );
     7        }
     8       
     9        function wpremovep( content, editor ) {
     10                var blocklist1, blocklist2,
     11                        preserve_linebreaks = false,
     12                        preserve_br = false,
     13                        obj = {
     14                                content: content
     15                        };
     16
     17                if ( $document ) {
     18                        $document.triggerHandler( 'wp-before-removep', [ obj, editor ] );
     19                }
     20               
     21                content = obj.content;
     22               
     23                // Protect pre|script tags
     24                if ( content.indexOf( '<pre' ) !== -1 || content.indexOf( '<script' ) !== -1 ) {
     25                        preserve_linebreaks = true;
     26                        content = content.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     27                                a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
     28                                a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
     29                                return a.replace( /\r?\n/g, '<wp-line-break>' );
     30                        });
     31                }
     32
     33                // keep <br> tags inside captions and remove line breaks
     34                if ( content.indexOf( '[caption' ) !== -1 ) {
     35                        preserve_br = true;
     36                        content = content.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     37                                return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' );
     38                        });
     39                }
     40
     41                // Pretty it up for the source editor
     42                blocklist1 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|div|h[1-6]|p|fieldset';
     43                content = content.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' );
     44                content = content.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' );
     45
     46                // Mark </p> if it has any attributes.
     47                content = content.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
     48
     49                // Separate <div> containing <p>
     50                content = content.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
     51
     52                // Remove <p> and <br />
     53                content = content.replace( /\s*<p>/gi, '' );
     54                content = content.replace( /\s*<\/p>\s*/gi, '\n\n' );
     55                content = content.replace( /\n[\s\u00a0]+\n/g, '\n\n' );
     56                content = content.replace( /\s*<br ?\/?>\s*/gi, '\n' );
     57
     58                // Fix some block element newline issues
     59                content = content.replace( /\s*<div/g, '\n<div' );
     60                content = content.replace( /<\/div>\s*/g, '</div>\n' );
     61                content = content.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' );
     62                content = content.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' );
     63
     64                blocklist2 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|h[1-6]|pre|fieldset';
     65                content = content.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' );
     66                content = content.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' );
     67                content = content.replace( /<li([^>]*)>/g, '\t<li$1>' );
     68
     69                if ( content.indexOf( '<option' ) !== -1 ) {
     70                        content = content.replace( /\s*<option/g, '\n<option' );
     71                        content = content.replace( /\s*<\/select>/g, '\n</select>' );
     72                }
     73
     74                if ( content.indexOf( '<hr' ) !== -1 ) {
     75                        content = content.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
     76                }
     77
     78                if ( content.indexOf( '<object' ) !== -1 ) {
     79                        content = content.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     80                                return a.replace( /[\r\n]+/g, '' );
     81                        });
     82                }
     83
     84                // Unmark special paragraph closing tags
     85                content = content.replace( /<\/p#>/g, '</p>\n' );
     86                content = content.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' );
     87
     88                // Trim whitespace
     89                content = content.replace( /^\s+/, '' );
     90                content = content.replace( /[\s\u00a0]+$/, '' );
     91
     92                // put back the line breaks in pre|script
     93                if ( preserve_linebreaks ) {
     94                        content = content.replace( /<wp-line-break>/g, '\n' );
     95                }
     96
     97                // and the <br> tags in captions
     98                if ( preserve_br ) {
     99                        content = content.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     100                }
     101
     102                obj.content = content;
     103
     104                if ( $document ) {
     105                        $document.triggerHandler( 'wp-after-removep', [ obj, editor ] );
     106                }
     107
     108                return obj.content;
     109        }
     110
     111        // Similar to `wpautop()` in formatting.php
     112        function wpautop( pee, editor ) {
     113                var preserve_linebreaks = false,
     114                        preserve_br = false,
     115                        obj = { content: pee },
     116                        blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
     117                                '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' +
     118                                '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary';
     119
     120                // Normalize line breaks
     121                pee = pee.replace( /\r\n|\r/g, '\n' );
     122
     123                if ( pee.indexOf( '\n' ) === -1 ) {
     124                        return pee;
     125                }
     126
     127                if ( $document ) {
     128                        $document.triggerHandler( 'wp-before-autop', [ obj, editor ] );
     129                }
     130
     131                pee = obj.content;
     132
     133                if ( pee.indexOf( '<object' ) !== -1 ) {
     134                        pee = pee.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     135                                return a.replace( /\n+/g, '' );
     136                        });
     137                }
     138
     139                pee = pee.replace( /<[^<>]+>/g, function( a ) {
     140                        return a.replace( /[\n\t ]+/g, ' ' );
     141                });
     142
     143                // Protect pre|script tags
     144                if ( pee.indexOf( '<pre' ) !== -1 || pee.indexOf( '<script' ) !== -1 ) {
     145                        preserve_linebreaks = true;
     146                        pee = pee.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     147                                return a.replace( /\n/g, '<wp-line-break>' );
     148                        });
     149                }
     150
     151                // keep <br> tags inside captions and convert line breaks
     152                if ( pee.indexOf( '[caption' ) !== -1 ) {
     153                        preserve_br = true;
     154                        pee = pee.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     155                                // keep existing <br>
     156                                a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' );
     157                                // no line breaks inside HTML tags
     158                                a = a.replace( /<[^<>]+>/g, function( b ) {
     159                                        return b.replace( /[\n\t ]+/, ' ' );
     160                                });
     161                                // convert remaining line breaks to <br>
     162                                return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
     163                        });
     164                }
     165
     166                pee = pee + '\n\n';
     167                pee = pee.replace( /<br \/>\s*<br \/>/gi, '\n\n' );
     168                pee = pee.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n$1' );
     169                pee = pee.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' );
     170                pee = pee.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // hr is self closing block element
     171                pee = pee.replace( /\s*<option/gi, '<option' ); // No <p> or <br> around <option>
     172                pee = pee.replace( /<\/option>\s*/gi, '</option>' );
     173                pee = pee.replace( /\n\s*\n+/g, '\n\n' );
     174                pee = pee.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' );
     175                pee = pee.replace( /<p>\s*?<\/p>/gi, '');
     176                pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     177                pee = pee.replace( /<p>(<li.+?)<\/p>/gi, '$1');
     178                pee = pee.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>');
     179                pee = pee.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>');
     180                pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' );
     181                pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     182                pee = pee.replace( /\s*\n/gi, '<br />\n');
     183                pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' );
     184                pee = pee.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' );
     185                pee = pee.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' );
     186
     187                pee = pee.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
     188                        if ( c.match( /<p( [^>]*)?>/ ) ) {
     189                                return a;
     190                        }
     191
     192                        return b + '<p>' + c + '</p>';
     193                });
     194
     195                // put back the line breaks in pre|script
     196                if ( preserve_linebreaks ) {
     197                        pee = pee.replace( /<wp-line-break>/g, '\n' );
     198                }
     199
     200                if ( preserve_br ) {
     201                        pee = pee.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     202                }
     203
     204                obj.content = pee;
     205
     206                if ( $document ) {
     207                        $document.triggerHandler( 'wp-after-autop', [ obj, editor ] );
     208                }
     209
     210                return obj.content;
     211        }
     212
     213        tinymce.wp = tinymce.wp || {};
     214        tinymce.wp.autop = wpautop;
     215        tinymce.wp.removep = wpremovep;
     216
     217        function compatEvent( eventName, obj ) {
     218                var compat, data = obj.content;
     219
     220                if ( eventName === 'beforePreWpautop' || eventName === 'beforeWpautop' ) {
     221                        unfiltered = data;
     222                }
     223
     224                compat = {
     225                        o: window.switchEditors,
     226                        data: data,
     227                        unfiltered: unfiltered
     228                };
     229
     230                $( document.body ).trigger( eventName, [ compat ] );
     231                obj.content = compat.data;
     232        }
     233
     234        // Back-compat for the old hooks
     235        if ( $ && window.switchEditors ) {
     236                $.extend( window.switchEditors, {
     237                        wpautop: wpautop,
     238                        pre_wpautop: wpremovep,
     239                        _wp_Autop: wpautop,
     240                        _wp_Nop: wpremovep
     241                } );
     242
     243                $document.on( 'wp-before-removep', function( event, obj ) {
     244                        compatEvent( 'beforePreWpautop', obj );
     245                }).on( 'wp-after-removep', function( event, obj ) {
     246                        compatEvent( 'afterPreWpautop', obj );
     247                }).on( 'wp-before-autop', function( event, obj ) {
     248                        compatEvent( 'beforeWpautop', obj );
     249                }).on( 'wp-after-autop', function( event, obj ) {
     250                        compatEvent( 'afterWpautop', obj );
     251                });
     252        }
     253
     254        tinymce.PluginManager.add( 'wpfunctions', function( editor ) {
     255                var Factory = tinymce.ui.Factory,
     256                        settings = editor.settings,
     257                        DOM = tinymce.DOM,
     258                        currentToolbar,
     259                        currentSelection;
     260
     261                /**
     262                 * Experimental: create a floating toolbar.
     263                 * This functionality will change in the next releases. Not recommended for use by plugins.
     264                 */
     265                function create( buttons ) {
     266                        var toolbar,
     267                                toolbarItems = [],
     268                                buttonGroup;
     269
     270                        tinymce.each( buttons, function( item ) {
     271                                var itemName;
     272
     273                                function bindSelectorChanged() {
     274                                        var selection = editor.selection;
     275
     276                                        if ( itemName === 'bullist' ) {
     277                                                selection.selectorChanged( 'ul > li', function( state, args ) {
     278                                                        var i = args.parents.length,
     279                                                                nodeName;
     280
     281                                                        while ( i-- ) {
     282                                                                nodeName = args.parents[ i ].nodeName;
     283
     284                                                                if ( nodeName === 'OL' || nodeName == 'UL' ) {
     285                                                                        break;
     286                                                                }
     287                                                        }
     288
     289                                                        item.active( state && nodeName === 'UL' );
     290                                                } );
     291                                        }
     292
     293                                        if ( itemName === 'numlist' ) {
     294                                                selection.selectorChanged( 'ol > li', function( state, args ) {
     295                                                        var i = args.parents.length,
     296                                                                nodeName;
     297
     298                                                        while ( i-- ) {
     299                                                                nodeName = args.parents[ i ].nodeName;
     300
     301                                                                if ( nodeName === 'OL' || nodeName === 'UL' ) {
     302                                                                        break;
     303                                                                }
     304                                                        }
     305
     306                                                        item.active( state && nodeName === 'OL' );
     307                                                } );
     308                                        }
     309
     310                                        if ( item.settings.stateSelector ) {
     311                                                selection.selectorChanged( item.settings.stateSelector, function( state ) {
     312                                                        item.active( state );
     313                                                }, true );
     314                                        }
     315
     316                                        if ( item.settings.disabledStateSelector ) {
     317                                                selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
     318                                                        item.disabled( state );
     319                                                } );
     320                                        }
     321                                }
     322
     323                                if ( item === '|' ) {
     324                                        buttonGroup = null;
     325                                } else {
     326                                        if ( Factory.has( item ) ) {
     327                                                item = {
     328                                                        type: item
     329                                                };
     330
     331                                                if ( settings.toolbar_items_size ) {
     332                                                        item.size = settings.toolbar_items_size;
     333                                                }
     334
     335                                                toolbarItems.push( item );
     336
     337                                                buttonGroup = null;
     338                                        } else {
     339                                                if ( ! buttonGroup ) {
     340                                                        buttonGroup = {
     341                                                                type: 'buttongroup',
     342                                                                items: []
     343                                                        };
     344
     345                                                        toolbarItems.push( buttonGroup );
     346                                                }
     347
     348                                                if ( editor.buttons[ item ] ) {
     349                                                        itemName = item;
     350                                                        item = editor.buttons[ itemName ];
     351
     352                                                        if ( typeof item === 'function' ) {
     353                                                                item = item();
     354                                                        }
     355
     356                                                        item.type = item.type || 'button';
     357
     358                                                        if ( settings.toolbar_items_size ) {
     359                                                                item.size = settings.toolbar_items_size;
     360                                                        }
     361
     362                                                        item = Factory.create( item );
     363
     364                                                        buttonGroup.items.push( item );
     365
     366                                                        if ( editor.initialized ) {
     367                                                                bindSelectorChanged();
     368                                                        } else {
     369                                                                editor.on( 'init', bindSelectorChanged );
     370                                                        }
     371                                                }
     372                                        }
     373                                }
     374                        } );
     375
     376                        toolbar = Factory.create( {
     377                                type: 'panel',
     378                                layout: 'stack',
     379                                classes: 'toolbar-grp inline-toolbar-grp',
     380                                ariaRoot: true,
     381                                ariaRemember: true,
     382                                items: [ {
     383                                        type: 'toolbar',
     384                                        layout: 'flow',
     385                                        items: toolbarItems
     386                                } ]
     387                        } );
     388
     389                        function hide() {
     390                                toolbar.hide();
     391                        }
     392
     393                        function reposition() {
     394                                var top, left, minTop, className,
     395                                        windowPos, adminbar, mceToolbar, boundary,
     396                                        boundaryMiddle, boundaryVerticalMiddle, spaceTop,
     397                                        spaceBottom, windowWidth, toolbarWidth, toolbarHalf,
     398                                        iframe, iframePos, iframeWidth, iframeHeigth,
     399                                        toolbarNodeHeight, verticalSpaceNeeded,
     400                                        toolbarNode = this.getEl(),
     401                                        buffer = 5,
     402                                        margin = 8,
     403                                        adminbarHeight = 0;
     404
     405                                if ( ! currentSelection ) {
     406                                        return;
     407                                }
     408
     409                                windowPos = window.pageYOffset || document.documentElement.scrollTop;
     410                                adminbar = tinymce.$( '#wpadminbar' )[0];
     411                                mceToolbar = tinymce.$( '.mce-toolbar-grp', editor.getContainer() )[0];
     412                                boundary = currentSelection.getBoundingClientRect();
     413                                boundaryMiddle = ( boundary.left + boundary.right ) / 2;
     414                                boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2;
     415                                spaceTop = boundary.top;
     416                                spaceBottom = iframeHeigth - boundary.bottom;
     417                                windowWidth = window.innerWidth;
     418                                toolbarWidth = toolbarNode.offsetWidth;
     419                                toolbarHalf = toolbarWidth / 2;
     420                                iframe = document.getElementById( editor.id + '_ifr' );
     421                                iframePos = DOM.getPos( iframe );
     422                                iframeWidth = iframe.offsetWidth;
     423                                iframeHeigth = iframe.offsetHeight;
     424                                toolbarNodeHeight = toolbarNode.offsetHeight;
     425                                verticalSpaceNeeded = toolbarNodeHeight + margin + buffer;
     426
     427                                if ( spaceTop >= verticalSpaceNeeded ) {
     428                                        className = ' mce-arrow-down';
     429                                        top = boundary.top + iframePos.y - toolbarNodeHeight - margin;
     430                                } else if ( spaceBottom >= verticalSpaceNeeded ) {
     431                                        className = ' mce-arrow-up';
     432                                        top = boundary.bottom + iframePos.y;
     433                                } else {
     434                                        top = buffer;
     435
     436                                        if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) {
     437                                                className = ' mce-arrow-down';
     438                                        } else {
     439                                                className = ' mce-arrow-up';
     440                                        }
     441                                }
     442
     443                                // Make sure the image toolbar is below the main toolbar.
     444                                if ( mceToolbar ) {
     445                                        minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight;
     446                                } else {
     447                                        minTop = iframePos.y;
     448                                }
     449
     450                                // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window.
     451                                if ( windowPos ) {
     452                                        if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) {
     453                                                adminbarHeight = adminbar.clientHeight;
     454                                        }
     455
     456                                        if ( windowPos + adminbarHeight > minTop ) {
     457                                                minTop = windowPos + adminbarHeight;
     458                                        }
     459                                }
     460
     461                                if ( top && minTop && ( minTop + buffer > top ) ) {
     462                                        top = minTop + buffer;
     463                                        className = '';
     464                                }
     465
     466                                left = boundaryMiddle - toolbarHalf;
     467                                left += iframePos.x;
     468
     469                                if ( boundary.left < 0 || boundary.right > iframeWidth ) {
     470                                        left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2;
     471                                } else if ( toolbarWidth >= windowWidth ) {
     472                                        className += ' mce-arrow-full';
     473                                        left = 0;
     474                                } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) ||
     475                                        ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) {
     476
     477                                        left = ( windowWidth - toolbarWidth ) / 2;
     478                                } else if ( left < iframePos.x ) {
     479                                        className += ' mce-arrow-left';
     480                                        left = boundary.left + iframePos.x;
     481                                } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) {
     482                                        className += ' mce-arrow-right';
     483                                        left = boundary.right - toolbarWidth + iframePos.x;
     484                                }
     485
     486                                toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
     487                                toolbarNode.className += className;
     488
     489                                DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
     490
     491                                return this;
     492                        }
     493
     494                        toolbar.on( 'show', function() {
     495                                currentToolbar = this;
     496                                this.reposition();
     497                        } );
     498
     499                        toolbar.on( 'hide', function() {
     500                                currentToolbar = false;
     501                        } );
     502
     503                        toolbar.on( 'keydown', function( event ) {
     504                                if ( event.keyCode === 27 ) {
     505                                        this.hide();
     506                                        editor.focus();
     507                                }
     508                        } );
     509
     510                        toolbar.on( 'remove', function() {
     511                                DOM.unbind( window, 'resize scroll', hide );
     512                                editor.dom.unbind( editor.getWin(), 'resize scroll', hide );
     513                                editor.off( 'blur hide', hide );
     514                        } );
     515
     516                        editor.once( 'init', function() {
     517                                DOM.bind( window, 'resize scroll', hide );
     518                                editor.dom.bind( editor.getWin(), 'resize scroll', hide );
     519                                editor.on( 'blur hide', hide );
     520                        } );
     521
     522                        toolbar.reposition = reposition;
     523                        toolbar.hide().renderTo( document.body );
     524
     525                        return toolbar;
     526                }
     527
     528                editor.shortcuts.add( 'alt+119', '', function() {
     529                        var node;
     530
     531                        if ( currentToolbar ) {
     532                                node = currentToolbar.find( 'toolbar' )[0];
     533                                node && node.focus( true );
     534                        }
     535                } );
     536
     537                editor.on( 'nodechange', function( event ) {
     538                        var collapsed = editor.selection.isCollapsed();
     539
     540                        var args = {
     541                                element: event.element,
     542                                parents: event.parents,
     543                                collapsed: collapsed
     544                        };
     545
     546                        editor.fire( 'wptoolbar', args );
     547
     548                        currentSelection = args.selection || args.element;
     549
     550                        currentToolbar && currentToolbar.hide();
     551                        args.toolbar && args.toolbar.show();
     552                } );
     553
     554                editor.wp = editor.wp || {};
     555                editor.wp._createToolbar = create;
     556        });
     557
     558}( window.tinymce, window.jQuery ));
  • src/wp-includes/js/tinymce/plugins/wpfunctions/plugin.js

    Property changes on: src/wp-includes/js/tinymce/plugins/wpfunctions/plugin.js
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1// Common WordPress functions
     2( function( tinymce, $ ) {
     3        var $document, unfiltered;
     4       
     5        if ( $ ) {
     6                $document = $( document );
     7        }
     8       
     9        function wpremovep( content, editor ) {
     10                var blocklist1, blocklist2,
     11                        preserve_linebreaks = false,
     12                        preserve_br = false,
     13                        obj = {
     14                                content: content
     15                        };
     16
     17                if ( $document ) {
     18                        $document.triggerHandler( 'wp-before-removep', [ obj, editor ] );
     19                }
     20               
     21                content = obj.content;
     22               
     23                // Protect pre|script tags
     24                if ( content.indexOf( '<pre' ) !== -1 || content.indexOf( '<script' ) !== -1 ) {
     25                        preserve_linebreaks = true;
     26                        content = content.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     27                                a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
     28                                a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
     29                                return a.replace( /\r?\n/g, '<wp-line-break>' );
     30                        });
     31                }
     32
     33                // keep <br> tags inside captions and remove line breaks
     34                if ( content.indexOf( '[caption' ) !== -1 ) {
     35                        preserve_br = true;
     36                        content = content.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     37                                return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' );
     38                        });
     39                }
     40
     41                // Pretty it up for the source editor
     42                blocklist1 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|div|h[1-6]|p|fieldset';
     43                content = content.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' );
     44                content = content.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' );
     45
     46                // Mark </p> if it has any attributes.
     47                content = content.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' );
     48
     49                // Separate <div> containing <p>
     50                content = content.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' );
     51
     52                // Remove <p> and <br />
     53                content = content.replace( /\s*<p>/gi, '' );
     54                content = content.replace( /\s*<\/p>\s*/gi, '\n\n' );
     55                content = content.replace( /\n[\s\u00a0]+\n/g, '\n\n' );
     56                content = content.replace( /\s*<br ?\/?>\s*/gi, '\n' );
     57
     58                // Fix some block element newline issues
     59                content = content.replace( /\s*<div/g, '\n<div' );
     60                content = content.replace( /<\/div>\s*/g, '</div>\n' );
     61                content = content.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' );
     62                content = content.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' );
     63
     64                blocklist2 = 'blockquote|ul|ol|li|table|thead|tbody|tfoot|tr|th|td|h[1-6]|pre|fieldset';
     65                content = content.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' );
     66                content = content.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' );
     67                content = content.replace( /<li([^>]*)>/g, '\t<li$1>' );
     68
     69                if ( content.indexOf( '<option' ) !== -1 ) {
     70                        content = content.replace( /\s*<option/g, '\n<option' );
     71                        content = content.replace( /\s*<\/select>/g, '\n</select>' );
     72                }
     73
     74                if ( content.indexOf( '<hr' ) !== -1 ) {
     75                        content = content.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' );
     76                }
     77
     78                if ( content.indexOf( '<object' ) !== -1 ) {
     79                        content = content.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     80                                return a.replace( /[\r\n]+/g, '' );
     81                        });
     82                }
     83
     84                // Unmark special paragraph closing tags
     85                content = content.replace( /<\/p#>/g, '</p>\n' );
     86                content = content.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' );
     87
     88                // Trim whitespace
     89                content = content.replace( /^\s+/, '' );
     90                content = content.replace( /[\s\u00a0]+$/, '' );
     91
     92                // put back the line breaks in pre|script
     93                if ( preserve_linebreaks ) {
     94                        content = content.replace( /<wp-line-break>/g, '\n' );
     95                }
     96
     97                // and the <br> tags in captions
     98                if ( preserve_br ) {
     99                        content = content.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     100                }
     101
     102                obj.content = content;
     103
     104                if ( $document ) {
     105                        $document.triggerHandler( 'wp-after-removep', [ obj, editor ] );
     106                }
     107
     108                return obj.content;
     109        }
     110
     111        // Similar to `wpautop()` in formatting.php
     112        function wpautop( pee, editor ) {
     113                var preserve_linebreaks = false,
     114                        preserve_br = false,
     115                        obj = { content: pee },
     116                        blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' +
     117                                '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' +
     118                                '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary';
     119
     120                // Normalize line breaks
     121                pee = pee.replace( /\r\n|\r/g, '\n' );
     122
     123                if ( pee.indexOf( '\n' ) === -1 ) {
     124                        return pee;
     125                }
     126
     127                if ( $document ) {
     128                        $document.triggerHandler( 'wp-before-autop', [ obj, editor ] );
     129                }
     130
     131                pee = obj.content;
     132
     133                if ( pee.indexOf( '<object' ) !== -1 ) {
     134                        pee = pee.replace( /<object[\s\S]+?<\/object>/g, function( a ) {
     135                                return a.replace( /\n+/g, '' );
     136                        });
     137                }
     138
     139                pee = pee.replace( /<[^<>]+>/g, function( a ) {
     140                        return a.replace( /[\n\t ]+/g, ' ' );
     141                });
     142
     143                // Protect pre|script tags
     144                if ( pee.indexOf( '<pre' ) !== -1 || pee.indexOf( '<script' ) !== -1 ) {
     145                        preserve_linebreaks = true;
     146                        pee = pee.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
     147                                return a.replace( /\n/g, '<wp-line-break>' );
     148                        });
     149                }
     150
     151                // keep <br> tags inside captions and convert line breaks
     152                if ( pee.indexOf( '[caption' ) !== -1 ) {
     153                        preserve_br = true;
     154                        pee = pee.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) {
     155                                // keep existing <br>
     156                                a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' );
     157                                // no line breaks inside HTML tags
     158                                a = a.replace( /<[^<>]+>/g, function( b ) {
     159                                        return b.replace( /[\n\t ]+/, ' ' );
     160                                });
     161                                // convert remaining line breaks to <br>
     162                                return a.replace( /\s*\n\s*/g, '<wp-temp-br />' );
     163                        });
     164                }
     165
     166                pee = pee + '\n\n';
     167                pee = pee.replace( /<br \/>\s*<br \/>/gi, '\n\n' );
     168                pee = pee.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n$1' );
     169                pee = pee.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' );
     170                pee = pee.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // hr is self closing block element
     171                pee = pee.replace( /\s*<option/gi, '<option' ); // No <p> or <br> around <option>
     172                pee = pee.replace( /<\/option>\s*/gi, '</option>' );
     173                pee = pee.replace( /\n\s*\n+/g, '\n\n' );
     174                pee = pee.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' );
     175                pee = pee.replace( /<p>\s*?<\/p>/gi, '');
     176                pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     177                pee = pee.replace( /<p>(<li.+?)<\/p>/gi, '$1');
     178                pee = pee.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>');
     179                pee = pee.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>');
     180                pee = pee.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' );
     181                pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' );
     182                pee = pee.replace( /\s*\n/gi, '<br />\n');
     183                pee = pee.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' );
     184                pee = pee.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' );
     185                pee = pee.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' );
     186
     187                pee = pee.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) {
     188                        if ( c.match( /<p( [^>]*)?>/ ) ) {
     189                                return a;
     190                        }
     191
     192                        return b + '<p>' + c + '</p>';
     193                });
     194
     195                // put back the line breaks in pre|script
     196                if ( preserve_linebreaks ) {
     197                        pee = pee.replace( /<wp-line-break>/g, '\n' );
     198                }
     199
     200                if ( preserve_br ) {
     201                        pee = pee.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     202                }
     203
     204                obj.content = pee;
     205
     206                if ( $document ) {
     207                        $document.triggerHandler( 'wp-after-autop', [ obj, editor ] );
     208                }
     209
     210                return obj.content;
     211        }
     212
     213        tinymce.wp = tinymce.wp || {};
     214        tinymce.wp.autop = wpautop;
     215        tinymce.wp.removep = wpremovep;
     216
     217        function compatEvent( eventName, obj ) {
     218                var compat, data = obj.content;
     219
     220                if ( eventName === 'beforePreWpautop' || eventName === 'beforeWpautop' ) {
     221                        unfiltered = data;
     222                }
     223
     224                compat = {
     225                        o: window.switchEditors,
     226                        data: data,
     227                        unfiltered: unfiltered
     228                };
     229
     230                $( document.body ).trigger( eventName, [ compat ] );
     231                obj.content = compat.data;
     232        }
     233
     234        // Back-compat for the old hooks
     235        if ( $ && window.switchEditors ) {
     236                $.extend( window.switchEditors, {
     237                        wpautop: wpautop,
     238                        pre_wpautop: wpremovep,
     239                        _wp_Autop: wpautop,
     240                        _wp_Nop: wpremovep
     241                } );
     242
     243                $document.on( 'wp-before-removep', function( event, obj ) {
     244                        compatEvent( 'beforePreWpautop', obj );
     245                }).on( 'wp-after-removep', function( event, obj ) {
     246                        compatEvent( 'afterPreWpautop', obj );
     247                }).on( 'wp-before-autop', function( event, obj ) {
     248                        compatEvent( 'beforeWpautop', obj );
     249                }).on( 'wp-after-autop', function( event, obj ) {
     250                        compatEvent( 'afterWpautop', obj );
     251                });
     252        }
     253
     254        tinymce.PluginManager.add( 'wpfunctions', function( editor ) {
     255                var Factory = tinymce.ui.Factory,
     256                        settings = editor.settings,
     257                        DOM = tinymce.DOM,
     258                        currentToolbar,
     259                        currentSelection;
     260
     261                /**
     262                 * Experimental: create a floating toolbar.
     263                 * This functionality will change in the next releases. Not recommended for use by plugins.
     264                 */
     265                function create( buttons ) {
     266                        var toolbar,
     267                                toolbarItems = [],
     268                                buttonGroup;
     269
     270                        tinymce.each( buttons, function( item ) {
     271                                var itemName;
     272
     273                                function bindSelectorChanged() {
     274                                        var selection = editor.selection;
     275
     276                                        if ( itemName === 'bullist' ) {
     277                                                selection.selectorChanged( 'ul > li', function( state, args ) {
     278                                                        var i = args.parents.length,
     279                                                                nodeName;
     280
     281                                                        while ( i-- ) {
     282                                                                nodeName = args.parents[ i ].nodeName;
     283
     284                                                                if ( nodeName === 'OL' || nodeName == 'UL' ) {
     285                                                                        break;
     286                                                                }
     287                                                        }
     288
     289                                                        item.active( state && nodeName === 'UL' );
     290                                                } );
     291                                        }
     292
     293                                        if ( itemName === 'numlist' ) {
     294                                                selection.selectorChanged( 'ol > li', function( state, args ) {
     295                                                        var i = args.parents.length,
     296                                                                nodeName;
     297
     298                                                        while ( i-- ) {
     299                                                                nodeName = args.parents[ i ].nodeName;
     300
     301                                                                if ( nodeName === 'OL' || nodeName === 'UL' ) {
     302                                                                        break;
     303                                                                }
     304                                                        }
     305
     306                                                        item.active( state && nodeName === 'OL' );
     307                                                } );
     308                                        }
     309
     310                                        if ( item.settings.stateSelector ) {
     311                                                selection.selectorChanged( item.settings.stateSelector, function( state ) {
     312                                                        item.active( state );
     313                                                }, true );
     314                                        }
     315
     316                                        if ( item.settings.disabledStateSelector ) {
     317                                                selection.selectorChanged( item.settings.disabledStateSelector, function( state ) {
     318                                                        item.disabled( state );
     319                                                } );
     320                                        }
     321                                }
     322
     323                                if ( item === '|' ) {
     324                                        buttonGroup = null;
     325                                } else {
     326                                        if ( Factory.has( item ) ) {
     327                                                item = {
     328                                                        type: item
     329                                                };
     330
     331                                                if ( settings.toolbar_items_size ) {
     332                                                        item.size = settings.toolbar_items_size;
     333                                                }
     334
     335                                                toolbarItems.push( item );
     336
     337                                                buttonGroup = null;
     338                                        } else {
     339                                                if ( ! buttonGroup ) {
     340                                                        buttonGroup = {
     341                                                                type: 'buttongroup',
     342                                                                items: []
     343                                                        };
     344
     345                                                        toolbarItems.push( buttonGroup );
     346                                                }
     347
     348                                                if ( editor.buttons[ item ] ) {
     349                                                        itemName = item;
     350                                                        item = editor.buttons[ itemName ];
     351
     352                                                        if ( typeof item === 'function' ) {
     353                                                                item = item();
     354                                                        }
     355
     356                                                        item.type = item.type || 'button';
     357
     358                                                        if ( settings.toolbar_items_size ) {
     359                                                                item.size = settings.toolbar_items_size;
     360                                                        }
     361
     362                                                        item = Factory.create( item );
     363
     364                                                        buttonGroup.items.push( item );
     365
     366                                                        if ( editor.initialized ) {
     367                                                                bindSelectorChanged();
     368                                                        } else {
     369                                                                editor.on( 'init', bindSelectorChanged );
     370                                                        }
     371                                                }
     372                                        }
     373                                }
     374                        } );
     375
     376                        toolbar = Factory.create( {
     377                                type: 'panel',
     378                                layout: 'stack',
     379                                classes: 'toolbar-grp inline-toolbar-grp',
     380                                ariaRoot: true,
     381                                ariaRemember: true,
     382                                items: [ {
     383                                        type: 'toolbar',
     384                                        layout: 'flow',
     385                                        items: toolbarItems
     386                                } ]
     387                        } );
     388
     389                        function hide() {
     390                                toolbar.hide();
     391                        }
     392
     393                        function reposition() {
     394                                var top, left, minTop, className,
     395                                        windowPos, adminbar, mceToolbar, boundary,
     396                                        boundaryMiddle, boundaryVerticalMiddle, spaceTop,
     397                                        spaceBottom, windowWidth, toolbarWidth, toolbarHalf,
     398                                        iframe, iframePos, iframeWidth, iframeHeigth,
     399                                        toolbarNodeHeight, verticalSpaceNeeded,
     400                                        toolbarNode = this.getEl(),
     401                                        buffer = 5,
     402                                        margin = 8,
     403                                        adminbarHeight = 0;
     404
     405                                if ( ! currentSelection ) {
     406                                        return;
     407                                }
     408
     409                                windowPos = window.pageYOffset || document.documentElement.scrollTop;
     410                                adminbar = tinymce.$( '#wpadminbar' )[0];
     411                                mceToolbar = tinymce.$( '.mce-toolbar-grp', editor.getContainer() )[0];
     412                                boundary = currentSelection.getBoundingClientRect();
     413                                boundaryMiddle = ( boundary.left + boundary.right ) / 2;
     414                                boundaryVerticalMiddle = ( boundary.top + boundary.bottom ) / 2;
     415                                spaceTop = boundary.top;
     416                                spaceBottom = iframeHeigth - boundary.bottom;
     417                                windowWidth = window.innerWidth;
     418                                toolbarWidth = toolbarNode.offsetWidth;
     419                                toolbarHalf = toolbarWidth / 2;
     420                                iframe = document.getElementById( editor.id + '_ifr' );
     421                                iframePos = DOM.getPos( iframe );
     422                                iframeWidth = iframe.offsetWidth;
     423                                iframeHeigth = iframe.offsetHeight;
     424                                toolbarNodeHeight = toolbarNode.offsetHeight;
     425                                verticalSpaceNeeded = toolbarNodeHeight + margin + buffer;
     426
     427                                if ( spaceTop >= verticalSpaceNeeded ) {
     428                                        className = ' mce-arrow-down';
     429                                        top = boundary.top + iframePos.y - toolbarNodeHeight - margin;
     430                                } else if ( spaceBottom >= verticalSpaceNeeded ) {
     431                                        className = ' mce-arrow-up';
     432                                        top = boundary.bottom + iframePos.y;
     433                                } else {
     434                                        top = buffer;
     435
     436                                        if ( boundaryVerticalMiddle >= verticalSpaceNeeded ) {
     437                                                className = ' mce-arrow-down';
     438                                        } else {
     439                                                className = ' mce-arrow-up';
     440                                        }
     441                                }
     442
     443                                // Make sure the image toolbar is below the main toolbar.
     444                                if ( mceToolbar ) {
     445                                        minTop = DOM.getPos( mceToolbar ).y + mceToolbar.clientHeight;
     446                                } else {
     447                                        minTop = iframePos.y;
     448                                }
     449
     450                                // Make sure the image toolbar is below the adminbar (if visible) or below the top of the window.
     451                                if ( windowPos ) {
     452                                        if ( adminbar && adminbar.getBoundingClientRect().top === 0 ) {
     453                                                adminbarHeight = adminbar.clientHeight;
     454                                        }
     455
     456                                        if ( windowPos + adminbarHeight > minTop ) {
     457                                                minTop = windowPos + adminbarHeight;
     458                                        }
     459                                }
     460
     461                                if ( top && minTop && ( minTop + buffer > top ) ) {
     462                                        top = minTop + buffer;
     463                                        className = '';
     464                                }
     465
     466                                left = boundaryMiddle - toolbarHalf;
     467                                left += iframePos.x;
     468
     469                                if ( boundary.left < 0 || boundary.right > iframeWidth ) {
     470                                        left = iframePos.x + ( iframeWidth - toolbarWidth ) / 2;
     471                                } else if ( toolbarWidth >= windowWidth ) {
     472                                        className += ' mce-arrow-full';
     473                                        left = 0;
     474                                } else if ( ( left < 0 && boundary.left + toolbarWidth > windowWidth ) ||
     475                                        ( left + toolbarWidth > windowWidth && boundary.right - toolbarWidth < 0 ) ) {
     476
     477                                        left = ( windowWidth - toolbarWidth ) / 2;
     478                                } else if ( left < iframePos.x ) {
     479                                        className += ' mce-arrow-left';
     480                                        left = boundary.left + iframePos.x;
     481                                } else if ( left + toolbarWidth > iframeWidth + iframePos.x ) {
     482                                        className += ' mce-arrow-right';
     483                                        left = boundary.right - toolbarWidth + iframePos.x;
     484                                }
     485
     486                                toolbarNode.className = toolbarNode.className.replace( / ?mce-arrow-[\w]+/g, '' );
     487                                toolbarNode.className += className;
     488
     489                                DOM.setStyles( toolbarNode, { 'left': left, 'top': top } );
     490
     491                                return this;
     492                        }
     493
     494                        toolbar.on( 'show', function() {
     495                                currentToolbar = this;
     496                                this.reposition();
     497                        } );
     498
     499                        toolbar.on( 'hide', function() {
     500                                currentToolbar = false;
     501                        } );
     502
     503                        toolbar.on( 'keydown', function( event ) {
     504                                if ( event.keyCode === 27 ) {
     505                                        this.hide();
     506                                        editor.focus();
     507                                }
     508                        } );
     509
     510                        toolbar.on( 'remove', function() {
     511                                DOM.unbind( window, 'resize scroll', hide );
     512                                editor.dom.unbind( editor.getWin(), 'resize scroll', hide );
     513                                editor.off( 'blur hide', hide );
     514                        } );
     515
     516                        editor.once( 'init', function() {
     517                                DOM.bind( window, 'resize scroll', hide );
     518                                editor.dom.bind( editor.getWin(), 'resize scroll', hide );
     519                                editor.on( 'blur hide', hide );
     520                        } );
     521
     522                        toolbar.reposition = reposition;
     523                        toolbar.hide().renderTo( document.body );
     524
     525                        return toolbar;
     526                }
     527
     528                editor.shortcuts.add( 'alt+119', '', function() {
     529                        var node;
     530
     531                        if ( currentToolbar ) {
     532                                node = currentToolbar.find( 'toolbar' )[0];
     533                                node && node.focus( true );
     534                        }
     535                } );
     536
     537                editor.on( 'nodechange', function( event ) {
     538                        var collapsed = editor.selection.isCollapsed();
     539
     540                        var args = {
     541                                element: event.element,
     542                                parents: event.parents,
     543                                collapsed: collapsed
     544                        };
     545
     546                        editor.fire( 'wptoolbar', args );
     547
     548                        currentSelection = args.selection || args.element;
     549
     550                        currentToolbar && currentToolbar.hide();
     551                        args.toolbar && args.toolbar.show();
     552                } );
     553
     554                editor.wp = editor.wp || {};
     555                editor.wp._createToolbar = create;
     556        });
     557
     558}( window.tinymce, window.jQuery ));