WordPress.org

Make WordPress Core

Ticket #32425: 32425.2.patch

File 32425.2.patch, 83.4 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$;
    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,
     13                                                target = mce$( event.target );
    1314
    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';
     15                                        if ( target.hasClass( 'wp-switch-editor' ) ) {
     16                                                id = target.attr( 'data-wp-editor-id' );
     17                                                mode = target.hasClass( 'switch-tmce' ) ? 'tmce' : 'html';
     18                                                switchEditor( id, mode );
     19                                        }
     20                                });
    3121                        }
    3222                }
    3323
    34                 function getToolbarHeight() {
    35                         var node = DOM.select( '.mce-toolbar-grp', ed.getContainer() )[0],
     24                function getToolbarHeight( editor ) {
     25                        var node = mce$( '.mce-toolbar-grp', editor.getContainer() )[0],
    3626                                height = node && node.clientHeight;
    3727
    3828                        if ( height && height > 10 && height < 200 ) {
     
    4232                        return 30;
    4333                }
    4434
    45                 if ( 'tmce' === mode || 'tinymce' === mode ) {
    46                         if ( ed && ! ed.isHidden() ) {
    47                                 return false;
    48                         }
     35                function switchEditor( id, mode ) {
     36                        id = id || 'content';
     37                        mode = mode || 'toggle';
    4938
    50                         if ( typeof( QTags ) !== 'undefined' ) {
    51                                 QTags.closeAllTags( id );
    52                         }
     39                        var editorHeight, toolbarHeight, iframe,
     40                                editor = tinymce.get( id ),
     41                                wrap = mce$( '#wp-' + id + '-wrap' ),
     42                                $textarea = mce$( '#' + id ),
     43                                textarea = $textarea[0];
    5344
    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 );
     45                        if ( 'toggle' === mode ) {
     46                                if ( editor && ! editor.isHidden() ) {
     47                                        mode = 'html';
     48                                } else {
     49                                        mode = 'tmce';
     50                                }
    5851                        }
    5952
    60                         if ( ed ) {
    61                                 ed.show();
     53                        if ( 'tmce' === mode || 'tinymce' === mode ) {
     54                                if ( editor && ! editor.isHidden() ) {
     55                                        return false;
     56                                }
    6257
    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                                         }
     58                                if ( typeof( window.QTags ) !== 'undefined' ) {
     59                                        window.QTags.closeAllTags( id );
    7260                                }
    73                         } else {
    74                                 tinymce.init( tinyMCEPreInit.mceInit[id] );
    75                         }
    7661
    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' );
     62                                editorHeight = parseInt( textarea.style.height, 10 ) || 0;
    8163
    82                 } else if ( 'html' === mode ) {
     64                                if ( editor ) {
     65                                        editor.show();
    8366
    84                         if ( ed && ed.isHidden() ) {
    85                                 return false;
    86                         }
     67                                        // No point resizing the iframe in iOS
     68                                        if ( ! tinymce.Env.iOS && editorHeight ) {
     69                                                toolbarHeight = getToolbarHeight( editor );
     70                                                editorHeight = editorHeight - toolbarHeight + 14;
    8771
    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 
    9772                                                // height cannot be under 50 or over 5000
    9873                                                if ( editorHeight > 50 && editorHeight < 5000 ) {
    99                                                         txtarea_el.style.height = editorHeight + 'px';
     74                                                        editor.theme.resizeTo( null, editorHeight );
    10075                                                }
    10176                                        }
     77                                } else {
     78                                        tinymce.init( window.tinyMCEPreInit.mceInit[id] );
    10279                                }
    10380
    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 );
     81                                wrap.removeClass( 'html-active' ).addClass( 'tmce-active' );
     82                                $textarea.attr( 'aria-hidden', true );
     83                                window.setUserSetting( 'editor', 'tinymce' );
     84
     85                        } else if ( 'html' === mode ) {
     86                                if ( editor && editor.isHidden() ) {
     87                                        return false;
    10988                                }
    11089
    111                                 DOM.setStyles( txtarea_el, {'display': '', 'visibility': ''} );
    112                         }
     90                                if ( editor ) {
     91                                        if ( ! tinymce.Env.iOS ) {
     92                                                iframe = editor.iframeElement;
     93                                                editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0;
    11394
    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         },
     95                                                if ( editorHeight ) {
     96                                                        toolbarHeight = getToolbarHeight( editor );
     97                                                        editorHeight = editorHeight + toolbarHeight - 14;
    12198
    122         _wp_Nop: function( content ) {
    123                 var blocklist1, blocklist2,
    124                         preserve_linebreaks = false,
    125                         preserve_br = false;
     99                                                        // height cannot be under 50 or over 5000
     100                                                        if ( editorHeight > 50 && editorHeight < 5000 ) {
     101                                                                textarea.style.height = editorHeight + 'px';
     102                                                        }
     103                                                }
     104                                        }
    126105
    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                 }
     106                                        editor.hide();
     107                                } else {
     108                                        // The TinyMCE instance doesn't exist, show the textarea
     109                                        $textarea.css({ 'display': '', 'visibility': '' });
     110                                }
    136111
    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;
     112                                wrap.removeClass( 'tmce-active' ).addClass( 'html-active' );
     113                                $textarea.attr( 'aria-hidden', false );
     114                                window.setUserSetting( 'editor', 'html' );
    274115                        }
    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' );
    282116                }
    283117
    284                 if ( preserve_br ) {
    285                         pee = pee.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' );
     118                if ( $ ) {
     119                        $( document ).ready( init );
     120                } else if ( document.addEventListener ) {
     121                        document.addEventListener( 'DOMContentLoaded', init, false );
     122                        window.addEventListener( 'load', init, false );
     123                } else if ( window.attachEvent ) {
     124                        window.attachEvent( 'onload', init );
     125                        document.attachEvent( 'onreadystatechange', function() {
     126                                if ( 'complete' === document.readyState ) {
     127                                        init();
     128                                }
     129                        } );
    286130                }
    287131
    288                 return pee;
    289         },
     132                return { go: switchEditor };
     133        }
    290134
    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 };
     135        window.switchEditors = new switchEditors();
     136}( 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, $wrap;
    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];
     1216                                        $wrap = tinymce.$( '#wp-' + id + '-wrap' );
    11931217
    1194                                         wrapper = tinymce.DOM.select( '#wp-' + edId + '-wrap' )[0];
     1218                                        if ( ( $wrap.hasClass( 'tmce-active' ) || ! tinyMCEPreInit.qtInit.hasOwnProperty( id ) ) && ! init.wp_skip_init ) {
     1219                                                tinymce.init( init );
    11951220
    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){}
     1221                                                if ( ! window.wpActiveEditor ) {
     1222                                                        window.wpActiveEditor = id;
     1223                                                }
    12061224                                        }
    12071225                                }
    12081226                        }
    12091227
    12101228                        if ( typeof quicktags !== 'undefined' ) {
    1211                                 for ( qtId in tinyMCEPreInit.qtInit ) {
    1212                                         try {
    1213                                                 quicktags( tinyMCEPreInit.qtInit[qtId] );
     1229                                for ( id in tinyMCEPreInit.qtInit ) {
     1230                                        quicktags( tinyMCEPreInit.qtInit[id] );
    12141231
    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 );
     1232                                        if ( ! window.wpActiveEditor ) {
     1233                                                window.wpActiveEditor = id;
    12261234                                        }
    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                                         }
    12331235                                }
    12341236                        }
    12351237                }());
  • 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
    3007  * be applied to an empty string.
     3006 * Generally the browsers treat everything inside a textarea as text, but
     3007 * it is still a good idea to HTML entity encode `<`, `>` and `&` in the content.
    30083008 *
    3009  * @since 2.0.0
     3009 * The filter 'format_for_editor' is applied here. If $text is empty the filter will
     3010 * be applied to an empty string.
    30103011 *
     3012 * @since 4.3.0
     3013 *
    30113014 * @param string $text The text to be formatted.
    3012  * @return string The formatted text after filter is applied.
     3015 * @return string The formatted text after filter is applied. <?
    30133016 */
    3014 function wp_richedit_pre( $text ) {
    3015         if ( empty( $text ) ) {
    3016                 /**
    3017                  * Filter text returned for the rich text editor.
    3018                  *
    3019                  * This filter is first evaluated, and the value returned, if an empty string
    3020                  * is passed to wp_richedit_pre(). If an empty string is passed, it results
    3021                  * in a break tag and line feed.
    3022                  *
    3023                  * If a non-empty string is passed, the filter is evaluated on the wp_richedit_pre()
    3024                  * return after being formatted.
    3025                  *
    3026                  * @since 2.0.0
    3027                  *
    3028                  * @param string $output Text for the rich text editor.
    3029                  */
    3030                 return apply_filters( 'richedit_pre', '' );
     3017function format_for_editor( $text ) {
     3018        // Back-compat: the text may have been encoded already. Check for any characters that need encoding.
     3019        if ( ! empty( $text ) && ( false !== strpos( $text, '<' ) || false !== strpos( $text, '>' ) ||
     3020                preg_match( '/&(?!#(?:\d+|x[a-f0-9]+);|[a-z1-4]{1,8};)/i', $text ) ) ) {
     3021
     3022                $text = htmlspecialchars( $text, ENT_NOQUOTES, get_option( 'blog_charset' ) );
    30313023        }
    30323024
    3033         $output = convert_chars($text);
    3034         $output = wpautop($output);
    3035         $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) );
    3036 
    3037         /** This filter is documented in wp-includes/formatting.php */
    3038         return apply_filters( 'richedit_pre', $output );
    3039 }
    3040 
    3041 /**
    3042  * Formats text for the HTML editor.
    3043  *
    3044  * Unless $output is empty it will pass through htmlspecialchars before the
    3045  * 'htmledit_pre' filter is applied.
    3046  *
    3047  * @since 2.5.0
    3048  *
    3049  * @param string $output The text to be formatted.
    3050  * @return string Formatted text after filter applied.
    3051  */
    3052 function wp_htmledit_pre( $output ) {
    3053         if ( !empty($output) )
    3054                 $output = htmlspecialchars($output, ENT_NOQUOTES, get_option( 'blog_charset' ) ); // convert only < > &
    3055 
    30563025        /**
    3057          * Filter the text before it is formatted for the HTML editor.
     3026         * Filter the text after it is formatted for the editor.
    30583027         *
    3059          * @since 2.5.0
     3028         * @since 4.3.0
    30603029         *
    3061          * @param string $output The HTML-formatted text.
     3030         * @param string $text The formatted text.
    30623031         */
    3063         return apply_filters( 'htmledit_pre', $output );
     3032        return apply_filters( 'format_for_editor', $text );
    30643033}
    30653034
    30663035/**
  • src/wp-includes/js/media-views.js

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