Make WordPress Core

Changeset 26876


Ignore:
Timestamp:
12/28/2013 11:52:04 PM (13 years ago)
Author:
azaozz
Message:

TinyMCE 4.0.12, first run.

  • Removes wp-tinymce-schema.js and mark-loaded.js, no longer needed.
  • Removes the inlinepopups and most of the wpdialogs plugins; wpdialog.js is moved to wp-includes/js.
  • Adds charmap, compat3x, image, link and textcolor plugins, previously contained in /themes/advanced.
  • Updates the wordpress, wpeditimage, wpfullscreen, wpgallery and wplink plugins.
  • Updates DFW, wp-admin/js/wp-fullscreen.js.

See #24067.

Location:
trunk
Files:
49 added
22 deleted
25 edited
1 copied
20 moved

Legend:

Unmodified
Added
Removed
  • trunk/Gruntfile.js

    r26771 r26876  
    1818                                src: []
    1919                        },
    20                         tinymce: {
    21                                 src: [
    22                                         '<%= concat.tinymce.dest %>',
    23                                         BUILD_DIR + 'wp-includes/js/tinymce/wp-tinymce-schema.min.js',
    24                                         BUILD_DIR + 'wp-includes/js/tinymce/mark_loaded.js'
    25                                 ]
    26                         },
     20                        tinymce: ['<%= concat.tinymce.dest %>'],
    2721                        qunit: ['tests/qunit/compiled.html']
    2822                },
    … …  
    192186                                        'wp-includes/js/plupload/handlers.js',
    193187                                        'wp-includes/js/plupload/wp-plupload.js',
    194                                         'wp-includes/js/tinymce/plugins/wordpress/editor_plugin_src.js',
    195                                         'wp-includes/js/tinymce/plugins/wp*/editor_plugin_src.js',
    196                                         'wp-includes/js/tinymce/mark_loaded_src.js',
     188                                        'wp-includes/js/tinymce/plugins/wordpress/plugin.js',
     189                                        'wp-includes/js/tinymce/plugins/wp*/plugin.js',
    197190                                        // Third party scripts
    198191                                        '!wp-admin/js/farbtastic.js',
    … …  
    263256                                        'wp-includes/js/plupload/handlers.js',
    264257                                        'wp-includes/js/plupload/wp-plupload.js',
    265                                         'wp-includes/js/tinymce/plugins/wp*/js/*.js',
    266                                         'wp-includes/js/tinymce/wp-tinymce-schema.js',
     258                                        'wp-includes/js/tinymce/plugins/wordpress/plugin.js',
     259                                        'wp-includes/js/tinymce/plugins/wp*/plugin.js',
     260
    267261                                        // Exceptions
    268262                                        '!wp-admin/js/custom-header.js', // Why? We should minify this.
    … …  
    272266                                        '!wp-includes/js/swfobject.js',
    273267                                        '!wp-includes/js/underscore.min.js',
    274                                         '!wp-includes/js/zxcvbn.min.js',
    275                                         // Hard-coded in editimage.html
    276                                         '!wp-includes/js/tinymce/plugins/wpeditimage/js/editimage.js'
    277                                 ]
    278                         },
    279                         tinymce: {
    280                                 expand: true,
    281                                 cwd: SOURCE_DIR,
    282                                 dest: BUILD_DIR,
    283                                 src: [
    284                                         'wp-includes/js/tinymce/plugins/wordpress/editor_plugin_src.js',
    285                                         'wp-includes/js/tinymce/plugins/wp*/editor_plugin_src.js',
    286                                         'wp-includes/js/tinymce/mark_loaded_src.js'
    287                                 ],
    288                                 // TinyMCE plugins use a nonstandard naming scheme: plugin files are named
    289                                 // `editor_plugin_src.js`, and are compressed into `editor_plugin.js`.
    290                                 rename: function(destBase, destPath) {
    291                                         destPath = destPath.replace(/_src.js$/, '.js');
    292                                         return path.join(destBase || '', destPath);
    293                                 }
     268                                        '!wp-includes/js/zxcvbn.min.js'
     269                                ]
    294270                        }
    295271                },
    … …  
    303279                                },
    304280                                src: [
    305                                         BUILD_DIR + 'wp-includes/js/tinymce/tiny_mce.js',
    306                                         BUILD_DIR + 'wp-includes/js/tinymce/wp-tinymce-schema.min.js',
    307                                         BUILD_DIR + 'wp-includes/js/tinymce/themes/advanced/editor_template.js',
    308                                         BUILD_DIR + 'wp-includes/js/tinymce/plugins/*/editor_plugin.js',
    309                                         BUILD_DIR + 'wp-includes/js/tinymce/mark_loaded.js'
     281                                        BUILD_DIR + 'wp-includes/js/tinymce/tinymce.min.js',
     282                                        BUILD_DIR + 'wp-includes/js/tinymce/themes/modern/theme.min.js',
     283                                        BUILD_DIR + 'wp-includes/js/tinymce/plugins/*/plugin.min.js'
    310284                                ],
    311285                                dest: BUILD_DIR + 'wp-includes/js/tinymce/wp-tinymce.js'
    … …  
    371345        // Build task.
    372346        grunt.registerTask('build', ['clean:all', 'copy:all', 'cssmin:core', 'colors', 'rtl', 'cssmin:rtl', 'cssmin:colors',
    373                 'uglify:core', 'uglify:tinymce', 'concat:tinymce', 'compress:tinymce', 'clean:tinymce']);
     347                'uglify:core', 'concat:tinymce', 'compress:tinymce', 'clean:tinymce']);
    374348
    375349        // Testing tasks.
    … …  
    384358        grunt.registerTask('qunit:compiled', 'Runs QUnit tests on compiled as well as uncompiled scripts.',
    385359                ['build', 'copy:qunit', 'qunit']);
     360
    386361        grunt.registerTask('test', 'Runs all QUnit and PHPUnit tasks.', ['qunit:compiled', 'phpunit']);
    387362
  • trunk/src/wp-admin/css/wp-admin.css

    r26847 r26876  
    43164316
    43174317.autosave-info {
    4318         padding: 2px 15px;
     4318        padding: 2px;
    43194319        text-align: right;
    43204320}
    … …  
    43244324}
    43254325
    4326 #content-resize-handle,
    4327 #post-body .wp_themeSkin .mceStatusbar a.mceResize {
     4326#content-resize-handle {
    43284327        background: transparent url('../images/resize.gif') no-repeat scroll right bottom;
    43294328        width: 12px;
    4330         height: 12px;
    4331         cursor: se-resize;
    4332 }
    4333 
    4334 .rtl #content-resize-handle,
    4335 .rtl #post-body .wp_themeSkin .mceStatusbar a.mceResize {
    4336         background: transparent url('../images/resize-rtl.gif') no-repeat scroll right bottom;
    4337 }
    4338 
    4339 #post-body .wp_themeSkin .mceStatusbar a.mceResize {
    4340         bottom: 3px;
    4341         position: absolute;
    4342         right: 3px;
    4343 }
    4344 
    4345 #post-body .postarea .wp_themeSkin .mceStatusbar a.mceResize {
    4346         bottom: -20px;
    4347 }
    4348 
    4349 #content-resize-handle {
    4350         position: absolute;
    4351         right: 3px;
    4352         bottom: -20px;
     4329        cursor: n-resize;
     4330}
     4331
     4332.rtl #content-resize-handle {
     4333        background: transparent url('../images/resize-rtl.gif') no-repeat scroll left bottom;
    43534334}
    43544335
    43554336.press-this #content-resize-handle {
    43564337        bottom: 2px;
    4357 }
    4358 
    4359 .tmce-active #content-resize-handle {
    4360         display: none;
    43614338}
    43624339
  • trunk/src/wp-admin/edit-form-advanced.php

    r26518 r26876  
    484484        'tabfocus_elements' => 'insert-media-button,save-post',
    485485        'editor_height' => 360,
     486        'tinymce' => array(
     487                'resize' => false,
     488        ),
    486489) ); ?>
    487490<table id="post-status-info" cellspacing="0"><tbody><tr>
    … …  
    500503        } ?>
    501504        </td>
     505        <td id="content-resize-handle" class="hide-if-no-js"><br /></td>
    502506</tr></tbody></table>
    503507
  • trunk/src/wp-admin/includes/ajax-actions.php

    r26868 r26876  
    17881788        $post_id = edit_post();
    17891789
    1790         if ( is_wp_error($post_id) ) {
    1791                 if ( $post_id->get_error_message() )
    1792                         $message = $post_id->get_error_message();
    1793                 else
    1794                         $message = __('Save failed');
    1795 
    1796                 echo json_encode( array( 'message' => $message, 'last_edited' => '' ) );
    1797                 wp_die();
    1798         } else {
    1799                 $message = __('Saved.');
     1790        if ( is_wp_error( $post_id ) ) {
     1791                wp_send_json_error();
    18001792        }
    18011793
    … …  
    18081800        }
    18091801
    1810         if ( $last_id = get_post_meta($post_id, '_edit_last', true) ) {
    1811                 $last_user = get_userdata($last_id);
     1802        if ( $last_id = get_post_meta( $post_id, '_edit_last', true ) ) {
     1803                $last_user = get_userdata( $last_id );
    18121804                $last_edited = sprintf( __('Last edited by %1$s on %2$s at %3$s'), esc_html( $last_user->display_name ), $last_date, $last_time );
    18131805        } else {
    … …  
    18151807        }
    18161808
    1817         echo json_encode( array( 'message' => $message, 'last_edited' => $last_edited ) );
    1818         wp_die();
     1809        wp_send_json_success( array( 'last_edited' => $last_edited ) );
    18191810}
    18201811
    … …  
    18401831         * @since 3.3.0
    18411832         *
    1842          * @param int $interval The interval in seconds the post lock duration should last, plus 5 seconds. Default 120.
     1833         * @param int $interval The interval in seconds the post lock duration should last, plus 5 seconds. Default 150.
    18431834         */
    1844         $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 120 ) + 5 ) . ':' . $active_lock[1];
     1835        $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1];
    18451836        update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) );
    18461837        wp_die( 1 );
  • trunk/src/wp-admin/js/editor.js

    r26224 r26876  
    1414        // mode can be 'html', 'tmce', or 'toggle'; 'html' is used for the 'Text' editor tab.
    1515        go: function( id, mode ) {
    16                 var t = this, ed, wrap_id, txtarea_el,
    17                         dom = tinymce.DOM;
     16                var t = this, ed, wrap_id, txtarea_el, editorHeight, toolbarHeight,
     17                        DOM = tinymce.DOM; //DOMUtils outside the editor iframe
    1818
    1919                id = id || 'content';
    … …  
    2222                ed = tinymce.get( id );
    2323                wrap_id = 'wp-' + id + '-wrap';
    24                 txtarea_el = dom.get( id );
     24                txtarea_el = DOM.get( id );
    2525
    2626                if ( 'toggle' === mode ) {
    … …  
    3232                }
    3333
     34                function getToolbarHeight() {
     35                        var height;
     36
     37                        try {
     38                                height = DOM.getSize( DOM.select( '.mce-toolbar-grp', ed.getContainer() )[0] );
     39                        } catch(e){}
     40
     41                        if ( height && height.h && height.h > 10 && height.h < 100 ) {
     42                                return height.h;
     43                        }
     44
     45                        return 0;
     46                }
     47
    3448                if ( 'tmce' === mode || 'tinymce' === mode ) {
    3549                        if ( ed && ! ed.isHidden() ) {
    … …  
    4155                        }
    4256
     57                        editorHeight = txtarea_el ? parseInt( txtarea_el.style.height, 10 ) : 0;
     58
    4359                        if ( tinyMCEPreInit.mceInit[ id ] && tinyMCEPreInit.mceInit[ id ].wpautop ) {
    4460                                txtarea_el.value = t.wpautop( txtarea_el.value );
    … …  
    4763                        if ( ed ) {
    4864                                ed.show();
     65
     66                                if ( editorHeight && ( toolbarHeight = getToolbarHeight() ) ) {
     67                                        editorHeight = editorHeight - toolbarHeight + 11;
     68
     69                                        // height cannot be under 50 or over 5000
     70                                        if ( editorHeight > 50 && editorHeight < 5000 ) {
     71                                                ed.theme.resizeTo( null, editorHeight );
     72                                        }
     73                                }
    4974                        } else {
    50                                 ed = new tinymce.Editor( id, tinyMCEPreInit.mceInit[ id ] );
    51                                 ed.render();
    52                         }
    53 
    54                         dom.removeClass( wrap_id, 'html-active' );
    55                         dom.addClass( wrap_id, 'tmce-active' );
     75                                tinymce.init( tinyMCEPreInit.mceInit[id] );
     76
     77                //              ed = tinymce.createEditor( id, tinyMCEPreInit.mceInit[id] );
     78                //              ed.render();
     79                        }
     80
     81                        DOM.removeClass( wrap_id, 'html-active' );
     82                        DOM.addClass( wrap_id, 'tmce-active' );
    5683                        setUserSetting( 'editor', 'tinymce' );
    5784
    … …  
    6390
    6491                        if ( ed ) {
     92                                editorHeight = DOM.get( id + '_ifr' );
     93                                editorHeight = editorHeight ? parseInt( editorHeight.style.height, 10 ) : 0;
     94
     95                                if ( editorHeight && ( toolbarHeight = getToolbarHeight() ) ) {
     96                                        editorHeight = editorHeight + toolbarHeight - 11;
     97
     98                                        // height cannot be under 50 or over 5000
     99                                        if ( editorHeight > 50 && editorHeight < 5000 ) {
     100                                                txtarea_el.style.height = editorHeight + 'px';
     101                                        }
     102                                }
     103
    65104                                ed.hide();
    66105                        } else {
    … …  
    70109                                }
    71110
    72                                 dom.setStyles( txtarea_el, {'display': '', 'visibility': ''} );
    73                         }
    74 
    75                         dom.removeClass( wrap_id, 'tmce-active' );
    76                         dom.addClass( wrap_id, 'html-active' );
     111                                DOM.setStyles( txtarea_el, {'display': '', 'visibility': ''} );
     112                        }
     113
     114                        DOM.removeClass( wrap_id, 'tmce-active' );
     115                        DOM.addClass( wrap_id, 'html-active' );
    77116                        setUserSetting( 'editor', 'html' );
    78117                }
    … …  
    89128                        preserve_linebreaks = true;
    90129                        content = content.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
    91                                 a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-temp-lb>' );
    92                                 return a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-temp-lb>' );
     130                                a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' );
     131                                a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' );
     132                                return a.replace( /\r?\n/g, '<wp-line-break>' );
    93133                        });
    94134                }
    … …  
    150190                // put back the line breaks in pre|script
    151191                if ( preserve_linebreaks ) {
    152                         content = content.replace( /<wp-temp-lb>/g, '\n' );
     192                        content = content.replace( /<wp-line-break>/g, '\n' );
    153193                }
    154194
    … …  
    182222                        preserve_linebreaks = true;
    183223                        pee = pee.replace( /<(pre|script)[^>]*>[\s\S]+?<\/\1>/g, function( a ) {
    184                                 return a.replace( /(\r\n|\n)/g, '<wp-temp-lb>' );
     224                                return a.replace( /(\r\n|\n)/g, '<wp-line-break>' );
    185225                        });
    186226                }
    … …  
    231271                // put back the line breaks in pre|script
    232272                if ( preserve_linebreaks ) {
    233                         pee = pee.replace( /<wp-temp-lb>/g, '\n' );
     273                        pee = pee.replace( /<wp-line-break>/g, '\n' );
    234274                }
    235275
  • trunk/src/wp-admin/js/media-upload.js

    r26323 r26876  
    77        var ed, mce = typeof(tinymce) != 'undefined', qt = typeof(QTags) != 'undefined';
    88
    9         if ( !wpActiveEditor ) {
     9        if ( ! wpActiveEditor ) {
    1010                if ( mce && tinymce.activeEditor ) {
    1111                        ed = tinymce.activeEditor;
    1212                        wpActiveEditor = ed.id;
    13                 } else if ( !qt ) {
     13                } else if ( ! qt ) {
    1414                        return false;
    1515                }
    1616        } else if ( mce ) {
    17                 if ( tinymce.activeEditor && (tinymce.activeEditor.id == 'mce_fullscreen' || tinymce.activeEditor.id == 'wp_mce_fullscreen') )
    18                         ed = tinymce.activeEditor;
    19                 else
    20                         ed = tinymce.get(wpActiveEditor);
     17                ed = tinymce.get( wpActiveEditor );
    2118        }
    2219
    23         if ( ed && !ed.isHidden() ) {
     20        if ( ed && ! ed.isHidden() ) {
    2421                // restore caret position on IE
    25                 if ( tinymce.isIE && ed.windowManager.insertimagebookmark )
    26                         ed.selection.moveToBookmark(ed.windowManager.insertimagebookmark);
     22        //      if ( tinymce.isIE && ed.windowManager.insertimagebookmark )
     23        //              ed.selection.moveToBookmark(ed.windowManager.insertimagebookmark);
    2724
    2825                if ( h.indexOf('[caption') !== -1 ) {
    … …  
    7976
    8077        $(window).resize(function(){ tb_position(); });
    81 
     78/*
    8279        // store caret position in IE
    8380        $(document).ready(function($){
    … …  
    9188                });
    9289        });
    93 
     90*/
    9491})(jQuery);
  • trunk/src/wp-admin/js/post.js

    r26851 r26876  
    813813        wptitlehint();
    814814
    815         // resizable textarea#content
    816         (function() {
    817                 var textarea = $('textarea#content'), offset = null, el;
     815        // Resize the visual and text editors
     816        ( function() {
     817                var editor, offset, mce,
     818                        $document = $( document ),
     819                        $textarea = $('textarea#content'),
     820                        $handle = $('#post-status-info');
     821               
    818822                // No point for touch devices
    819                 if ( !textarea.length || 'ontouchstart' in window )
     823                if ( ! $textarea.length || 'ontouchstart' in window ) {
    820824                        return;
    821 
    822                 function dragging(e) {
    823                         textarea.height( Math.max(50, offset + e.pageY) + 'px' );
    824                         return false;
    825825                }
    826826
     827                function dragging( event ) {
     828                        if ( mce ) {
     829                                editor.theme.resizeTo( null, offset + event.pageY );
     830                        } else {
     831                                $textarea.height( Math.max( 50, offset + event.pageY ) );
     832                        }
     833
     834                        event.preventDefault();
     835                }
     836
    827837                function endDrag() {
    828                         var height;
    829 
    830                         textarea.focus();
    831                         $(document).unbind('mousemove', dragging).unbind('mouseup', endDrag);
    832 
    833                         height = parseInt( textarea.css('height'), 10 );
     838                        var height, toolbarHeight;
     839
     840                        if ( mce ) {
     841                                editor.focus();
     842                                toolbarHeight = $( '#wp-content-editor-container .mce-toolbar-grp' ).height();
     843                                height = parseInt( $('#content_ifr').css('height'), 10 ) + toolbarHeight - 28;
     844                        } else {
     845                                $textarea.focus();
     846                                height = parseInt( $textarea.css('height'), 10 );
     847                        }
     848
     849                        $document.off( 'mousemove.wp-editor-resize mouseup.wp-editor-resize' );
    834850
    835851                        // sanity check
    836                         if ( height && height > 50 && height < 5000 )
     852                        if ( height && height > 50 && height < 5000 ) {
    837853                                setUserSetting( 'ed_size', height );
     854                        }
    838855                }
    839856
    840                 textarea.css('resize', 'none');
    841                 el = $('<div id="content-resize-handle"><br></div>');
    842                 $('#wp-content-wrap').append(el);
    843                 el.on('mousedown', function(e) {
    844                         offset = textarea.height() - e.pageY;
    845                         textarea.blur();
    846                         $(document).mousemove(dragging).mouseup(endDrag);
    847                         return false;
     857                $textarea.css( 'resize', 'none' );
     858
     859                $handle.on( 'mousedown.wp-editor-resize', function( event ) {
     860                        if ( typeof tinymce !== 'undefined' ) {
     861                                editor = tinymce.get('content');
     862                        }
     863
     864                        if ( editor && ! editor.isHidden() ) {
     865                                mce = true;
     866                                offset = $('#content_ifr').height() - event.pageY;
     867                        } else {
     868                                mce = false;
     869                                offset = $textarea.height() - event.pageY;
     870                                $textarea.blur();
     871                        }
     872
     873                        $document.on( 'mousemove.wp-editor-resize', dragging )
     874                                .on( 'mouseup.wp-editor-resize', endDrag );
     875
     876                        event.preventDefault();
    848877                });
    849878        })();
    850879
    851         if ( typeof(tinymce) != 'undefined' ) {
    852                 tinymce.onAddEditor.add(function(mce, ed){
    853                         // iOS expands the iframe to full height and the user cannot adjust it.
    854                         if ( ed.id != 'content' || tinymce.isIOS5 )
    855                                 return;
    856 
    857                         function getHeight() {
    858                                 var height, node = document.getElementById('content_ifr'),
    859                                         ifr_height = node ? parseInt( node.style.height, 10 ) : 0,
    860                                         tb_height = $('#content_tbl tr.mceFirst').height();
    861 
    862                                 if ( !ifr_height || !tb_height )
    863                                         return false;
    864 
    865                                 // total height including toolbar and statusbar
    866                                 height = ifr_height + tb_height + 21;
    867                                 // textarea height = total height - 33px toolbar
    868                                 height -= 33;
    869 
    870                                 return height;
    871                         }
    872 
    873                         // resize TinyMCE to match the textarea height when switching Text -> Visual
    874                         ed.onLoadContent.add( function() {
    875                                 var ifr_height, node = document.getElementById('content'),
    876                                         height = node ? parseInt( node.style.height, 10 ) : 0,
    877                                         tb_height = $('#content_tbl tr.mceFirst').height() || 33;
    878 
    879                                 // height cannot be under 50 or over 5000
    880                                 if ( !height || height < 50 || height > 5000 )
    881                                         height = 360; // default height for the main editor
    882 
    883                                 if ( getUserSetting( 'ed_size' ) > 5000  )
    884                                         setUserSetting( 'ed_size', 360 );
    885 
    886                                 // compensate for padding and toolbars
    887                                 ifr_height = ( height - tb_height ) + 12;
    888 
    889                                 // sanity check
    890                                 if ( ifr_height > 50 && ifr_height < 5000 ) {
    891                                         $('#content_tbl').css('height', '' );
    892                                         $('#content_ifr').css('height', ifr_height + 'px' );
    893                                 }
    894                         });
    895 
    896                         // resize the textarea to match TinyMCE's height when switching Visual -> Text
    897                         ed.onSaveContent.add( function() {
    898                                 var height = getHeight();
    899 
    900                                 if ( !height || height < 50 || height > 5000 )
    901                                         return;
    902 
    903                                 $('textarea#content').css( 'height', height + 'px' );
    904                         });
    905 
    906                         // save on resizing TinyMCE
    907                         ed.onPostRender.add(function() {
    908                                 $( '#content_resize' ).on( 'mousedown.wp-mce-resize', function() {
    909                                         $( document ).on( 'mouseup.wp-mce-resize', function() {
    910                                                 var height;
    911 
    912                                                 $(document).off('mouseup.wp-mce-resize');
    913 
    914                                                 height = getHeight();
    915                                                 // sanity check
    916                                                 if ( height && height > 50 && height < 5000 )
    917                                                         setUserSetting( 'ed_size', height );
    918                                         });
    919                                 });
    920                         });
    921                 });
    922 
     880        if ( typeof tinymce !== 'undefined' ) {
    923881                // When changing post formats, change the editor body class
    924882                $( '#post-formats-select input.post-format' ).on( 'change.set-editor-class', function() {
  • trunk/src/wp-admin/js/wp-fullscreen.js

    r26667 r26876  
    11/* global ajaxurl, deleteUserSetting, setUserSetting, switchEditors, tinymce, tinyMCEPreInit, wp_fullscreen_settings, wpActiveEditor:true, wpLink */
    2 /**
    3  * PubSub
    4  *
    5  * A lightweight publish/subscribe implementation.
    6  * Private use only!
    7  */
    8 var PubSub, fullscreen, wptitlehint;
    9 
    10 PubSub = function() {
    11         this.topics = {};
    12 };
    13 
    14 PubSub.prototype.subscribe = function( topic, callback ) {
    15         if ( ! this.topics[ topic ] )
    16                 this.topics[ topic ] = [];
    17 
    18         this.topics[ topic ].push( callback );
    19         return callback;
    20 };
    21 
    22 PubSub.prototype.unsubscribe = function( topic, callback ) {
    23         var i, l,
    24                 topics = this.topics[ topic ];
    25 
    26         if ( ! topics )
    27                 return callback || [];
    28 
    29         // Clear matching callbacks
    30         if ( callback ) {
    31                 for ( i = 0, l = topics.length; i < l; i++ ) {
    32                         if ( callback == topics[i] )
    33                                 topics.splice( i, 1 );
    34                 }
    35                 return callback;
    36 
    37         // Clear all callbacks
    38         } else {
    39                 this.topics[ topic ] = [];
    40                 return topics;
    41         }
    42 };
    43 
    44 PubSub.prototype.publish = function( topic, args ) {
    45         var i, l, broken,
    46                 topics = this.topics[ topic ];
    47 
    48         if ( ! topics )
    49                 return;
    50 
    51         args = args || [];
    52 
    53         for ( i = 0, l = topics.length; i < l; i++ ) {
    54                 broken = ( topics[i].apply( null, args ) === false || broken );
    55         }
    56         return ! broken;
    57 };
    58 
    592/**
    603 * Distraction Free Writing
    614 * (wp-fullscreen)
    625 *
    63  * Access the API globally using the fullscreen variable.
     6 * Access the API globally using the window.wp.editor.fullscreen variable.
    647 */
    65 
    66 (function($){
    67         var api, ps, bounder, s, timer, block, set_title_hint;
     8( function( $, window ) {
     9        var api, ps, s, toggleUI, uiTimer, PubSub,
     10                oldheight = 0,
     11                uiScrollTop = 0,
     12                transitionend = 'transitionend webkitTransitionEnd',
     13                $body = $( document.body ),
     14                $document = $( document );
     15
     16        /**
     17         * PubSub
     18         *
     19         * A lightweight publish/subscribe implementation.
     20         *
     21         * @access private
     22         */
     23        PubSub = function() {
     24                this.topics = {};
     25
     26                this.subscribe = function( topic, callback ) {
     27                        if ( ! this.topics[ topic ] )
     28                                this.topics[ topic ] = [];
     29
     30                        this.topics[ topic ].push( callback );
     31                        return callback;
     32                };
     33
     34                this.unsubscribe = function( topic, callback ) {
     35                        var i, l,
     36                                topics = this.topics[ topic ];
     37
     38                        if ( ! topics )
     39                                return callback || [];
     40
     41                        // Clear matching callbacks
     42                        if ( callback ) {
     43                                for ( i = 0, l = topics.length; i < l; i++ ) {
     44                                        if ( callback == topics[i] )
     45                                                topics.splice( i, 1 );
     46                                }
     47                                return callback;
     48
     49                        // Clear all callbacks
     50                        } else {
     51                                this.topics[ topic ] = [];
     52                                return topics;
     53                        }
     54                };
     55
     56                this.publish = function( topic, args ) {
     57                        var i, l, broken,
     58                                topics = this.topics[ topic ];
     59
     60                        if ( ! topics )
     61                                return;
     62
     63                        args = args || [];
     64
     65                        for ( i = 0, l = topics.length; i < l; i++ ) {
     66                                broken = ( topics[i].apply( null, args ) === false || broken );
     67                        }
     68                        return ! broken;
     69                };
     70        };
    6871
    6972        // Initialize the fullscreen/api object
    70         fullscreen = api = {};
     73        api = {};
    7174
    7275        // Create the PubSub (publish/subscribe) interface.
    7376        ps = api.pubsub = new PubSub();
    74         timer = 0;
    75         block = false;
    7677
    7778        s = api.settings = { // Settings
    78                 visible : false,
    79                 mode : 'tinymce',
    80                 editor_id : 'content',
    81                 title_id : '',
    82                 timer : 0,
    83                 toolbar_shown : false
    84         };
    85 
    86         /**
    87          * Bounder
    88          *
    89          * Creates a function that publishes start/stop topics.
    90          * Used to throttle events.
    91          */
    92         bounder = api.bounder = function( start, stop, delay, e ) {
    93                 var y, top;
    94 
    95                 delay = delay || 1250;
    96 
    97                 if ( e ) {
    98                         y = e.pageY || e.clientY || e.offsetY;
    99                         top = $(document).scrollTop();
    100 
    101                         if ( !e.isDefaultPrevented ) // test if e ic jQuery normalized
    102                                 y = 135 + y;
    103 
    104                         if ( y - top > 120 )
     79                visible: false,
     80                mode: 'tinymce',
     81                id: '',
     82                title_id: '',
     83                timer: 0,
     84                toolbar_shown: false
     85        };
     86
     87        function _hideUI() {
     88                $body.removeClass('wp-dfw-show-ui');
     89                uiShown = false;
     90        }
     91
     92        /**
     93         * toggleUI
     94         *
     95         * Toggle the CSS class to show/hide the toolbar, borders and statusbar.
     96         */
     97        toggleUI = api.toggleUI = function( hide ) {
     98                clearTimeout( uiTimer );
     99
     100                if ( ! $body.hasClass('wp-dfw-show-ui') ) {
     101                        $body.addClass('wp-dfw-show-ui');
     102                }
     103
     104                if ( hide === 'hide' ) {
     105                        uiTimer = setTimeout( _hideUI, 2000 );
     106                }
     107        };
     108
     109        function resetCssPosition( add ) {
     110                s.$dfwWrap.parents().each( function( i, parent ) {
     111                        var cssPosition, $parent = $(parent);
     112
     113                        if ( add ) {
     114                                if ( parent.style.position ) {
     115                                        $parent.data( 'wp-dfw-css-position', parent.style.position );
     116                                }
     117
     118                                $parent.css( 'position', 'static' );
     119                        } else {
     120                                cssPosition = $parent.data( 'wp-dfw-css-position' );
     121                                cssPosition = cssPosition || '';
     122                                $parent.css( 'position', cssPosition );
     123                        }
     124
     125                        if ( parent.nodeName === 'BODY' ) {
     126                                return false;
     127                        }
     128                });
     129        }
     130
     131        /**
     132         * on()
     133         *
     134         * Turns fullscreen on.
     135         *
     136         * @param string mode Optional. Switch to the given mode before opening.
     137         */
     138        api.on = function() {
     139                var id, $dfwWrap, editor, titleId;
     140
     141                if ( s.visible ) {
     142                        return;
     143                }
     144
     145                if ( ! s.$fullscreenFader ) {
     146                        api.ui.init();
     147                }
     148
     149                // Settings can be added or changed by defining "wp_fullscreen_settings" JS object.
     150                if ( typeof window.wp_fullscreen_settings === 'object' )
     151                        $.extend( s, window.wp_fullscreen_settings );
     152
     153                id = s.id || window.wpActiveEditor;
     154
     155                if ( ! id ) {
     156                        if ( s.hasTinymce ) {
     157                                id = tinymce.activeEditor.id;
     158                        } else {
    105159                                return;
    106                 }
    107 
    108                 if ( block )
     160                        }
     161                }
     162
     163                s.id = id;
     164                $dfwWrap = s.$dfwWrap = $( '#wp-' + id + '-wrap' );
     165
     166                if ( ! $dfwWrap.length ) {
    109167                        return;
    110 
    111                 block = true;
    112 
    113                 setTimeout( function() {
    114                         block = false;
    115                 }, 400 );
    116 
    117                 if ( s.timer )
    118                         clearTimeout( s.timer );
    119                 else
    120                         ps.publish( start );
    121 
    122                 function timed() {
    123                         ps.publish( stop );
    124                         s.timer = 0;
    125                 }
    126 
    127                 s.timer = setTimeout( timed, delay );
    128         };
    129 
    130         /**
    131          * on()
    132          *
    133          * Turns fullscreen on.
    134          *
    135          * @param string mode Optional. Switch to the given mode before opening.
    136          */
    137         api.on = function() {
    138                 if ( s.visible )
    139                         return;
    140 
    141                 // Settings can be added or changed by defining "wp_fullscreen_settings" JS object.
    142                 if ( typeof(wp_fullscreen_settings) == 'object' )
    143                         $.extend( s, wp_fullscreen_settings );
    144 
    145                 s.editor_id = wpActiveEditor || 'content';
    146 
    147                 if ( $('input#title').length && s.editor_id == 'content' )
    148                         s.title_id = 'title';
    149                 else if ( $('input#' + s.editor_id + '-title').length ) // the title input field should have [editor_id]-title HTML ID to be auto detected
    150                         s.title_id = s.editor_id + '-title';
    151                 else
    152                         $('#wp-fullscreen-title, #wp-fullscreen-title-prompt-text').hide();
    153 
    154                 s.mode = $('#' + s.editor_id).is(':hidden') ? 'tinymce' : 'html';
    155                 s.qt_canvas = $('#' + s.editor_id).get(0);
    156 
    157                 if ( ! s.element )
    158                         api.ui.init();
    159 
    160                 s.is_mce_on = s.has_tinymce && typeof( tinymce.get(s.editor_id) ) != 'undefined';
     168                }
     169
     170                s.$dfwTextarea = $( '#' + id );
     171                s.$editorContainer = $dfwWrap.find( '.wp-editor-container' );
     172                uiScrollTop = $document.scrollTop();
     173
     174                if ( s.hasTinymce ) {
     175                        s.editor = tinymce.get( id );
     176                }
     177
     178                if ( s.editor && ! s.editor.isHidden() ) {
     179                        s.origHeight = $( '#' + id + '_ifr' ).height();
     180                        s.mode = 'tinymce';
     181                } else {
     182                        s.origHeight = s.$dfwTextarea.height();
     183                        s.mode = 'html';
     184                }
     185
     186                // Try to find title field
     187                if ( typeof window.adminpage !== 'undefined' &&
     188                        ( window.adminpage === 'post-php' || window.adminpage === 'post-new-php' ) ) {
     189
     190                        titleId = 'title';
     191                } else {
     192                        titleId = id + '-title';
     193                }
     194
     195                s.$dfwTitle = $( '#' + titleId );
     196
     197                if ( ! s.$dfwTitle.length ) {
     198                        s.$dfwTitle = null;
     199                }
    161200
    162201                api.ui.fade( 'show', 'showing', 'shown' );
    … …  
    188227                var from = s.mode;
    189228
    190                 if ( ! to || ! s.visible || ! s.has_tinymce )
     229                if ( ! to || ! s.visible || ! s.hasTinymce || typeof switchEditors === 'undefined' ) {
    191230                        return from;
     231                }
    192232
    193233                // Don't switch if the mode is the same.
    … …  
    195235                        return from;
    196236
    197                 ps.publish( 'switchMode', [ from, to ] );
     237                if ( to === 'tinymce' && ! s.editor ) {
     238                        s.editor = tinymce.get( s.id );
     239
     240                        if ( ! s.editor &&  typeof tinyMCEPreInit !== 'undefined' &&
     241                                tinyMCEPreInit.mceInit && tinyMCEPreInit.mceInit[ s.id ] ) {
     242
     243                                // If the TinyMCE instance hasn't been created, set the "wp_fulscreen" flag on creating it
     244                                tinyMCEPreInit.mceInit[ s.id ].wp_fullscreen = true;
     245                        }
     246                }
     247
    198248                s.mode = to;
    199                 ps.publish( 'switchedMode', [ from, to ] );
     249                switchEditors.go( s.id, to );
     250                api.refreshButtons( true );
     251
     252                if ( to === 'html' ) {
     253                        setTimeout( api.resizeTextarea, 200 );
     254                }
    200255
    201256                return to;
    … …  
    207262
    208263        api.save = function() {
    209                 var hidden = $('#hiddenaction'), old = hidden.val(), spinner = $('#wp-fullscreen-save .spinner'),
    210                         message = $('#wp-fullscreen-save span');
    211 
    212                 spinner.show();
    213                 api.savecontent();
    214 
    215                 hidden.val('wp-fullscreen-save-post');
    216 
    217                 $.post( ajaxurl, $('form#post').serialize(), function(r){
    218                         spinner.hide();
    219                         message.show();
    220 
    221                         setTimeout( function(){
    222                                 message.fadeOut(1000);
    223                         }, 3000 );
    224 
    225                         if ( r.last_edited )
    226                                 $('#wp-fullscreen-save input').attr( 'title',  r.last_edited );
    227 
    228                 }, 'json');
    229 
    230                 hidden.val(old);
    231         };
    232 
    233         api.savecontent = function() {
    234                 var ed, content;
    235 
    236                 if ( s.title_id )
    237                         $('#' + s.title_id).val( $('#wp-fullscreen-title').val() );
    238 
    239                 if ( s.mode === 'tinymce' && (ed = tinymce.get('wp_mce_fullscreen')) ) {
    240                         content = ed.save();
    241                 } else {
    242                         content = $('#wp_mce_fullscreen').val();
    243                 }
    244 
    245                 $('#' + s.editor_id).val( content );
    246                 $(document).triggerHandler('wpcountwords', [ content ]);
    247         };
    248 
    249         set_title_hint = function( title ) {
    250                 if ( ! title.val().length )
    251                         title.siblings('label').css( 'visibility', '' );
    252                 else
    253                         title.siblings('label').css( 'visibility', 'hidden' );
    254         };
    255 
    256         api.dfw_width = function(n) {
    257                 var el = $('#wp-fullscreen-wrap'), w = el.width();
    258 
    259                 if ( !n ) { // reset to theme width
    260                         el.width( $('#wp-fullscreen-central-toolbar').width() );
     264                var $hidden = $('#hiddenaction'),
     265                        oldVal = $hidden.val(),
     266                        $spinner = $('#wp-fullscreen-save .spinner'),
     267                        $saveMessage = $('#wp-fullscreen-save .wp-fullscreen-saved-message')
     268                        $errorMessage = $('#wp-fullscreen-save .wp-fullscreen-error-message');
     269
     270                $spinner.show();
     271                $errorMessage.hide();
     272                $hidden.val('wp-fullscreen-save-post');
     273
     274                if ( s.editor && ! s.editor.isHidden() ) {
     275                        s.editor.save();
     276                }
     277
     278                $.ajax({
     279                        url: window.ajaxurl,
     280                        type: 'post',
     281                        data: $('form#post').serialize(),
     282                        dataType: 'json'
     283                }).done( function( response ) {
     284                        $spinner.hide();
     285
     286                        if ( response && response.success ) {
     287                                $saveMessage.show();
     288
     289                                setTimeout( function() {
     290                                        $saveMessage.fadeOut(300);
     291                                }, 3000 );
     292
     293                                if ( response.data && response.data.last_edited ) {
     294                                        $('#wp-fullscreen-save input').attr( 'title',  response.data.last_edited );
     295                                }
     296                        } else {
     297                                $errorMessage.show();
     298                        }
     299                }).fail( function() {
     300                        $spinner.hide();
     301                        $errorMessage.show();
     302                });
     303
     304                $hidden.val( oldVal );
     305        };
     306
     307        api.dfwWidth = function( pixels, total ) {
     308                var width;
     309
     310                if ( ! pixels ) {
     311                        // reset to theme width
     312                        width = $('#wp-fullscreen-body').data('theme-width') || 800;
     313                        s.$editorContainer.width( width );
     314
     315                        if ( s.$dfwTitle ) {
     316                                s.$dfwTitle.width( width - 16 );
     317                        }
     318
    261319                        deleteUserSetting('dfw_width');
    262320                        return;
    263321                }
    264322
    265                 w = n + w;
    266 
    267                 if ( w < 200 || w > 1200 ) // sanity check
     323                if ( total ) {
     324                        width = pixels;
     325                } else {
     326                        width = s.$editorContainer.width();
     327                        width += pixels;
     328                }
     329
     330                if ( width < 200 || width > 1200 ) {
     331                        // sanity check
    268332                        return;
    269 
    270                 el.width( w );
    271                 setUserSetting('dfw_width', w);
    272         };
    273 
    274         ps.subscribe( 'showToolbar', function() {
    275                 s.toolbars.removeClass('fade-1000').addClass('fade-300');
    276                 api.fade.In( s.toolbars, 300, function(){ ps.publish('toolbarShown'); }, true );
    277                 $('#wp-fullscreen-body').addClass('wp-fullscreen-focus');
    278                 s.toolbar_shown = true;
     333                }
     334
     335                s.$editorContainer.width( width );
     336
     337                if ( s.$dfwTitle ) {
     338                        s.$dfwTitle.width( width - 16 );
     339                }
     340
     341                setUserSetting( 'dfw_width', width );
     342        };
     343
     344        // This event occurs before the overlay blocks the UI.
     345        ps.subscribe( 'show', function() {
     346                var title = $('#last-edit').text();
     347
     348                if ( title ) {
     349                        $('#wp-fullscreen-save input').attr( 'title', title );
     350                }
    279351        });
    280352
    281         ps.subscribe( 'hideToolbar', function() {
    282                 s.toolbars.removeClass('fade-300').addClass('fade-1000');
    283                 api.fade.Out( s.toolbars, 1000, function(){ ps.publish('toolbarHidden'); }, true );
    284                 $('#wp-fullscreen-body').removeClass('wp-fullscreen-focus');
    285         });
    286 
    287         ps.subscribe( 'toolbarShown', function() {
    288                 s.toolbars.removeClass('fade-300');
    289         });
    290 
    291         ps.subscribe( 'toolbarHidden', function() {
    292                 s.toolbars.removeClass('fade-1000');
    293                 s.toolbar_shown = false;
    294         });
    295 
    296         ps.subscribe( 'show', function() { // This event occurs before the overlay blocks the UI.
    297                 var title;
    298 
    299                 if ( s.title_id ) {
    300                         title = $('#wp-fullscreen-title').val( $('#' + s.title_id).val() );
    301                         set_title_hint( title );
    302                 }
    303 
    304                 $('#wp-fullscreen-save input').attr( 'title',  $('#last-edit').text() );
    305 
    306                 s.textarea_obj.value = s.qt_canvas.value;
    307 
    308                 if ( s.has_tinymce && s.mode === 'tinymce' )
    309                         tinymce.execCommand('wpFullScreenInit');
    310 
    311                 s.orig_y = $(window).scrollTop();
    312         });
    313 
    314         ps.subscribe( 'showing', function() { // This event occurs while the DFW overlay blocks the UI.
    315                 $( document.body ).addClass( 'fullscreen-active' );
    316                 api.refresh_buttons();
    317 
    318                 $( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } );
    319                 bounder( 'showToolbar', 'hideToolbar', 2000 );
     353        // This event occurs while the overlay blocks the UI.
     354        ps.subscribe( 'showing', function() {
     355                $body.addClass( 'wp-fullscreen-active' );
     356
     357                s.$dfwWrap.addClass( 'wp-fullscreen-wrap' );
     358                s.$editorContainer.append( $('#wp-fullscreen-status') );
     359
     360                if ( s.$dfwTitle ) {
     361                        s.$dfwTitle.after( '<span id="wp-fullscreen-title-placeholder">' );
     362                        s.$dfwWrap.prepend( s.$dfwTitle.addClass('wp-fullscreen-title') );
     363                }
     364
     365                api.refreshButtons();
     366                resetCssPosition( true );
     367                $('#wpadminbar').hide();
     368
     369                // Show the UI for 2 sec. when opening
     370                toggleUI('hide');
    320371
    321372                api.bind_resize();
    322                 setTimeout( api.resize_textarea, 200 );
     373
     374                if ( s.editor ) {
     375                        s.editor.execCommand( 'wpFullScreenOn' );
     376                }
     377
     378                api.dfwWidth( $( '#wp-fullscreen-body' ).data('dfw-width') || 800, true );
    323379
    324380                // scroll to top so the user is not disoriented
    325381                scrollTo(0, 0);
    326 
    327                 // needed it for IE7 and compat mode
    328                 $('#wpadminbar').hide();
    329382        });
    330383
    331         ps.subscribe( 'shown', function() { // This event occurs after the DFW overlay is shown
    332                 var interim_init;
    333 
     384        // This event occurs after the overlay unblocks the UI
     385        ps.subscribe( 'shown', function() {
    334386                s.visible = true;
    335387
    336                 // init the standard TinyMCE instance if missing
    337                 if ( s.has_tinymce && ! s.is_mce_on ) {
    338 
    339                         interim_init = function(mce, ed) {
    340                                 var el = ed.getElement(), old_val = el.value, settings = tinyMCEPreInit.mceInit[s.editor_id];
    341 
    342                                 if ( settings && settings.wpautop && typeof(switchEditors) != 'undefined' )
    343                                         el.value = switchEditors.wpautop( el.value );
    344 
    345                                 ed.onInit.add(function(ed) {
    346                                         ed.hide();
    347                                         ed.getElement().value = old_val;
    348                                         tinymce.onAddEditor.remove(interim_init);
    349                                 });
    350                         };
    351 
    352                         tinymce.onAddEditor.add(interim_init);
    353                         tinymce.init(tinyMCEPreInit.mceInit[s.editor_id]);
    354 
    355                         s.is_mce_on = true;
    356                 }
    357 
    358                 wpActiveEditor = 'wp_mce_fullscreen';
     388                if ( s.editor && ! s.editor.isHidden() ) {
     389                        s.editor.execCommand( 'wpAutoResize' );
     390                } else {
     391                        api.resizeTextarea( 'force' );
     392                }
    359393        });
    360394
    361395        ps.subscribe( 'hide', function() { // This event occurs before the overlay blocks DFW.
    362                 var htmled_is_hidden = $('#' + s.editor_id).is(':hidden');
    363                 // Make sure the correct editor is displaying.
    364                 if ( s.has_tinymce && s.mode === 'tinymce' && !htmled_is_hidden ) {
    365                         switchEditors.go(s.editor_id, 'tmce');
    366                 } else if ( s.mode === 'html' && htmled_is_hidden ) {
    367                         switchEditors.go(s.editor_id, 'html');
    368                 }
    369 
    370                 // Save content must be after switchEditors or content will be overwritten. See #17229.
    371                 api.savecontent();
    372 
    373                 $( document ).unbind( '.fullscreen' );
    374                 $(s.textarea_obj).unbind('.grow');
    375 
    376                 if ( s.has_tinymce && s.mode === 'tinymce' )
    377                         tinymce.execCommand('wpFullScreenSave');
    378 
    379                 if ( s.title_id )
    380                         set_title_hint( $('#' + s.title_id) );
    381 
    382                 s.qt_canvas.value = s.textarea_obj.value;
     396                $document.unbind( '.fullscreen' );
     397                s.$dfwTextarea.unbind('.wp-dfw-resize');
    383398        });
    384399
    385400        ps.subscribe( 'hiding', function() { // This event occurs while the overlay blocks the DFW UI.
    386 
    387                 $( document.body ).removeClass( 'fullscreen-active' );
    388                 scrollTo(0, s.orig_y);
     401                $body.removeClass( 'wp-fullscreen-active' );
     402                $( '#wp-fullscreen-body' ).append( $('#wp-fullscreen-status') );
     403
     404                if ( s.$dfwTitle ) {
     405                        $( '#wp-fullscreen-title-placeholder' ).before( s.$dfwTitle.removeClass('wp-fullscreen-title') ).remove();
     406                }
     407
     408                s.$dfwWrap.removeClass( 'wp-fullscreen-wrap' )
     409                s.$editorContainer.css( 'width', '' );
     410                s.$dfwTextarea.add( '#' + s.id + '_ifr' ).height( s.origHeight );
     411
     412                if ( s.editor ) {
     413                        s.editor.execCommand( 'wpFullScreenOff' );
     414                }
     415
     416                resetCssPosition( false );
     417
     418                window.scrollTo( 0, uiScrollTop );
    389419                $('#wpadminbar').show();
    390420        });
    391421
    392         ps.subscribe( 'hidden', function() { // This event occurs after DFW is removed.
     422        // This event occurs after DFW is removed.
     423        ps.subscribe( 'hidden', function() {
    393424                s.visible = false;
    394                 $('#wp_mce_fullscreen, #wp-fullscreen-title').removeAttr('style');
    395 
    396                 if ( s.has_tinymce && s.is_mce_on )
    397                         tinymce.execCommand('wpFullScreenClose');
    398 
    399                 s.textarea_obj.value = '';
    400                 api.oldheight = 0;
    401                 wpActiveEditor = s.editor_id;
    402425        });
    403426
    404         ps.subscribe( 'switchMode', function( from, to ) {
    405                 var ed;
    406 
    407                 if ( !s.has_tinymce || !s.is_mce_on )
    408                         return;
    409 
    410                 ed = tinymce.get('wp_mce_fullscreen');
    411 
    412                 if ( from === 'html' && to === 'tinymce' ) {
    413 
    414                         if ( tinymce.get(s.editor_id).getParam('wpautop') && typeof(switchEditors) != 'undefined' )
    415                                 s.textarea_obj.value = switchEditors.wpautop( s.textarea_obj.value );
    416 
    417                         if ( 'undefined' == typeof(ed) )
    418                                 tinymce.execCommand('wpFullScreenInit');
    419                         else
    420                                 ed.show();
    421 
    422                 } else if ( from === 'tinymce' && to === 'html' ) {
    423                         if ( ed )
    424                                 ed.hide();
    425                 }
    426         });
    427 
    428         ps.subscribe( 'switchedMode', function( from, to ) {
    429                 api.refresh_buttons(true);
    430 
    431                 if ( to === 'html' )
    432                         setTimeout( api.resize_textarea, 200 );
    433         });
    434 
    435         /**
    436          * Buttons
    437          */
    438         api.b = function() {
    439                 if ( s.has_tinymce && 'tinymce' === s.mode )
    440                         tinymce.execCommand('Bold');
    441         };
    442 
    443         api.i = function() {
    444                 if ( s.has_tinymce && 'tinymce' === s.mode )
    445                         tinymce.execCommand('Italic');
    446         };
    447 
    448         api.ul = function() {
    449                 if ( s.has_tinymce && 'tinymce' === s.mode )
    450                         tinymce.execCommand('InsertUnorderedList');
    451         };
    452 
    453         api.ol = function() {
    454                 if ( s.has_tinymce && 'tinymce' === s.mode )
    455                         tinymce.execCommand('InsertOrderedList');
    456         };
    457 
    458         api.link = function() {
    459                 if ( s.has_tinymce && 'tinymce' === s.mode )
    460                         tinymce.execCommand('WP_Link');
    461                 else
    462                         wpLink.open();
    463         };
    464 
    465         api.unlink = function() {
    466                 if ( s.has_tinymce && 'tinymce' === s.mode )
    467                         tinymce.execCommand('unlink');
    468         };
    469 
    470         api.atd = function() {
    471                 if ( s.has_tinymce && 'tinymce' === s.mode )
    472                         tinymce.execCommand('mceWritingImprovementTool');
    473         };
    474 
    475         api.help = function() {
    476                 if ( s.has_tinymce && 'tinymce' === s.mode )
    477                         tinymce.execCommand('WP_Help');
    478         };
    479 
    480         api.blockquote = function() {
    481                 if ( s.has_tinymce && 'tinymce' === s.mode )
    482                         tinymce.execCommand('mceBlockQuote');
    483         };
    484 
    485         api.medialib = function() {
    486                 if ( typeof wp !== 'undefined' && wp.media && wp.media.editor )
    487                         wp.media.editor.open(s.editor_id);
    488         };
    489 
    490         api.refresh_buttons = function( fade ) {
    491                 fade = fade || false;
    492 
     427        api.refreshButtons = function( fade ) {
    493428                if ( s.mode === 'html' ) {
    494429                        $('#wp-fullscreen-mode-bar').removeClass('wp-tmce-mode').addClass('wp-html-mode');
    495430
    496                         if ( fade )
     431                        if ( fade ) {
    497432                                $('#wp-fullscreen-button-bar').fadeOut( 150, function(){
    498433                                        $(this).addClass('wp-html-mode').fadeIn( 150 );
    499434                                });
    500                         else
     435                        } else {
    501436                                $('#wp-fullscreen-button-bar').addClass('wp-html-mode');
    502 
     437                        }
    503438                } else if ( s.mode === 'tinymce' ) {
    504439                        $('#wp-fullscreen-mode-bar').removeClass('wp-html-mode').addClass('wp-tmce-mode');
    505440
    506                         if ( fade )
     441                        if ( fade ) {
    507442                                $('#wp-fullscreen-button-bar').fadeOut( 150, function(){
    508443                                        $(this).removeClass('wp-html-mode').fadeIn( 150 );
    509444                                });
    510                         else
     445                        } else {
    511446                                $('#wp-fullscreen-button-bar').removeClass('wp-html-mode');
     447                        }
    512448                }
    513449        };
    … …  
    520456        api.ui = {
    521457                init: function() {
    522                         var topbar = $('#fullscreen-topbar'), txtarea = $('#wp_mce_fullscreen'), last = 0;
    523 
    524                         s.toolbars = topbar.add( $('#wp-fullscreen-status') );
    525                         s.element = $('#fullscreen-fader');
    526                         s.textarea_obj = txtarea[0];
    527                         s.has_tinymce = typeof(tinymce) != 'undefined';
    528 
    529                         if ( !s.has_tinymce )
     458                        var toolbar, last = 0;
     459
     460                        s.toolbar = toolbar = $('#fullscreen-topbar');
     461                        s.$fullscreenFader = $('#fullscreen-fader');
     462                        s.hasTinymce = typeof tinymce !== 'undefined';
     463
     464                        if ( ! s.hasTinymce )
    530465                                $('#wp-fullscreen-mode-bar').hide();
    531466
    532                         if ( wptitlehint && $('#wp-fullscreen-title').length )
    533                                 wptitlehint('wp-fullscreen-title');
    534 
    535                         $(document).keyup(function(e){
     467                        $document.keyup( function(e) {
    536468                                var c = e.keyCode || e.charCode, a, data;
    537469
    538                                 if ( !fullscreen.settings.visible )
    539                                         return true;
    540 
    541                                 if ( navigator.platform && navigator.platform.indexOf('Mac') != -1 )
     470                                if ( ! s.visible ) {
     471                                        return;
     472                                }
     473
     474                                if ( navigator.platform && navigator.platform.indexOf('Mac') !== -1 ) {
    542475                                        a = e.ctrlKey; // Ctrl key for Mac
    543                                 else
     476                                } else {
    544477                                        a = e.altKey; // Alt key for Win & Linux
     478                                }
    545479
    546480                                if ( 27 == c ) { // Esc
    … …  
    556490                                        };
    557491
    558                                         if ( ! jQuery(document).triggerHandler( 'wp_CloseOnEscape', [data] ) )
     492                                        if ( ! $(document).triggerHandler( 'wp_CloseOnEscape', [data] ) ) {
    559493                                                fullscreen.off();
     494                                        }
    560495                                }
    561496
    562497                                if ( a && (61 == c || 107 == c || 187 == c) ) { // +
    563                                         api.dfw_width(25);
     498                                        api.dfwWidth( 25 );
    564499                                        e.preventDefault();
    565500                                }
    566501
    567502                                if ( a && (45 == c || 109 == c || 189 == c) ) { // -
    568                                         api.dfw_width(-25);
     503                                        api.dfwWidth( -25 );
    569504                                        e.preventDefault();
    570505                                }
    571506
    572507                                if ( a && 48 == c ) { // 0
    573                                         api.dfw_width(0);
     508                                        api.dfwWidth( 0 );
    574509                                        e.preventDefault();
    575510                                }
    576511                        });
    577512
    578                         // word count in Text mode
    579                         if ( typeof(wpWordCount) != 'undefined' ) {
    580 
    581                                 txtarea.keyup( function(e) {
    582                                         var k = e.keyCode || e.charCode;
    583 
    584                                         if ( k == last )
    585                                                 return true;
    586 
    587                                         if ( 13 == k || 8 == last || 46 == last )
    588                                                 $(document).triggerHandler('wpcountwords', [ txtarea.val() ]);
    589 
    590                                         last = k;
    591                                         return true;
    592                                 });
    593                         }
    594 
    595                         topbar.mouseenter(function(){
    596                                 s.toolbars.addClass('fullscreen-make-sticky');
    597                                 $( document ).unbind( '.fullscreen' );
    598                                 clearTimeout( s.timer );
    599                                 s.timer = 0;
    600                         }).mouseleave(function(){
    601                                 s.toolbars.removeClass('fullscreen-make-sticky');
    602 
    603                                 if ( s.visible )
    604                                         $( document ).bind( 'mousemove.fullscreen', function(e) { bounder( 'showToolbar', 'hideToolbar', 2000, e ); } );
     513                        toolbar.on( 'mouseenter', function() {
     514                                toggleUI('show');
     515                        }).on( 'mouseleave', function() {
     516                                toggleUI('hide');
     517                        });
     518
     519                        // Bind buttons
     520                        $('#wp-fullscreen-buttons').on( 'click.wp-fullscreen', function( event ) {
     521                                var command = event.target.id && event.target.id.substr(6);
     522
     523                                if ( s.editor && 'tinymce' === s.mode ) {
     524                                        switch( command ) {
     525                                                case 'bold':
     526                                                        s.editor.execCommand('Bold');
     527                                                        break;
     528                                                case 'italic':
     529                                                        s.editor.execCommand('Italic');
     530                                                        break;
     531                                                case 'bullist':
     532                                                        s.editor.execCommand('InsertUnorderedList');
     533                                                        break;
     534                                                case 'numlist':
     535                                                        s.editor.execCommand('InsertOrderedList');
     536                                                        break;
     537                                                case 'link':
     538                                                        s.editor.execCommand('WP_Link');
     539                                                        break;
     540                                                case 'unlink':
     541                                                        s.editor.execCommand('unlink');
     542                                                        break;
     543                                                case 'image':
     544                                                        s.editor.execCommand('mceImage');
     545                                                        break;
     546                                                case 'help':
     547                                                        s.editor.execCommand('WP_Help');
     548                                                        break;
     549                                                case 'blockquote':
     550                                                        s.editor.execCommand('mceBlockQuote');
     551                                                        break;
     552                                        }
     553                                } else if ( command === 'link' && window.wpLink ) {
     554                                        window.wpLink.open();
     555                                }
     556
     557                                if ( command === 'wp-media-library' && typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
     558                                        wp.media.editor.open( s.id );
     559                                }
    605560                        });
    606561                },
    607562
    608563                fade: function( before, during, after ) {
    609                         if ( ! s.element )
     564                        if ( ! s.$fullscreenFader ) {
    610565                                api.ui.init();
     566                        }
    611567
    612568                        // If any callback bound to before returns false, bail.
    613                         if ( before && ! ps.publish( before ) )
     569                        if ( before && ! ps.publish( before ) ) {
    614570                                return;
    615 
    616                         api.fade.In( s.element, 600, function() {
    617                                 if ( during )
     571                        }
     572
     573                        api.fade.In( s.$fullscreenFader, 200, function() {
     574                                if ( during ) {
    618575                                        ps.publish( during );
    619 
    620                                 api.fade.Out( s.element, 600, function() {
    621                                         if ( after )
     576                                }
     577
     578                                api.fade.Out( s.$fullscreenFader, 200, function() {
     579                                        if ( after ) {
    622580                                                ps.publish( after );
     581                                        }
    623582                                });
    624583                        });
    … …  
    627586
    628587        api.fade = {
    629                 transitionend: 'transitionend webkitTransitionEnd oTransitionEnd',
    630 
    631588                // Sensitivity to allow browsers to render the blank element before animating.
    632589                sensitivity: 100,
    … …  
    645602
    646603                                element.show();
    647                                 element.first().one( this.transitionend, function() {
     604                                element.first().one( transitionend, function() {
    648605                                        callback();
    649606                                });
     607
    650608                                setTimeout( function() { element.addClass( 'fade-trigger' ); }, this.sensitivity );
    651609                        } else {
    652                                 if ( stop )
     610                                if ( stop ) {
    653611                                        element.stop();
     612                                }
    654613
    655614                                element.css( 'opacity', 1 );
    656615                                element.first().fadeIn( speed, callback );
    657616
    658                                 if ( element.length > 1 )
     617                                if ( element.length > 1 ) {
    659618                                        element.not(':first').fadeIn( speed );
     619                                }
    660620                        }
    661621
    … …  
    669629                        stop = stop || false;
    670630
    671                         if ( ! element.is(':visible') )
     631                        if ( ! element.is(':visible') ) {
    672632                                return element;
     633                        }
    673634
    674635                        if ( api.fade.transitions ) {
    675                                 element.first().one( api.fade.transitionend, function() {
    676                                         if ( element.hasClass('fade-trigger') )
     636                                element.first().one( transitionend, function() {
     637                                        if ( element.hasClass('fade-trigger') ) {
    677638                                                return;
     639                                        }
    678640
    679641                                        element.hide();
    … …  
    682644                                setTimeout( function() { element.removeClass( 'fade-trigger' ); }, this.sensitivity );
    683645                        } else {
    684                                 if ( stop )
     646                                if ( stop ) {
    685647                                        element.stop();
     648                                }
    686649
    687650                                element.first().fadeOut( speed, callback );
    688651
    689                                 if ( element.length > 1 )
     652                                if ( element.length > 1 ) {
    690653                                        element.not(':first').fadeOut( speed );
     654                                }
    691655                        }
    692656
    … …  
    694658                },
    695659
    696                 transitions: (function() { // Check if the browser supports CSS 3.0 transitions
    697                         var s = document.documentElement.style;
    698 
    699                         return ( typeof ( s.WebkitTransition ) == 'string' ||
    700                                 typeof ( s.MozTransition ) == 'string' ||
    701                                 typeof ( s.OTransition ) == 'string' ||
    702                                 typeof ( s.transition ) == 'string' );
     660                // Check if the browser supports CSS 3.0 transitions
     661                transitions: ( function() {
     662                        var style = document.documentElement.style;
     663
     664                        return ( typeof style.WebkitTransition === 'string' ||
     665                                typeof style.MozTransition === 'string' ||
     666                                typeof style.OTransition === 'string' ||
     667                                typeof style.transition === 'string' );
    703668                })()
    704669        };
    … …  
    709674         * Automatically updates textarea height.
    710675         */
    711 
    712676        api.bind_resize = function() {
    713                 $(s.textarea_obj).bind('keypress.grow click.grow paste.grow', function(){
    714                         setTimeout( api.resize_textarea, 200 );
     677                s.$dfwTextarea.on( 'keydown.wp-dfw-resize click.wp-dfw-resize paste.wp-dfw-resize', function(e) {
     678                        api.resizeTextarea(e);
    715679                });
    716680        };
    717681
    718         api.oldheight = 0;
    719         api.resize_textarea = function() {
    720                 var txt = s.textarea_obj, newheight;
    721 
    722                 newheight = txt.scrollHeight > 300 ? txt.scrollHeight : 300;
    723 
    724                 if ( newheight != api.oldheight ) {
    725                         txt.style.height = newheight + 'px';
    726                         api.oldheight = newheight;
    727                 }
    728         };
    729 
    730 })(jQuery);
     682        api.resizeTextarea = function( event ) {
     683                var node = s.$dfwTextarea[0];
     684
     685                if ( node.scrollHeight > node.clientHeight ) {
     686                        node.style.height = node.scrollHeight + 50 + 'px';
     687                }
     688        };
     689
     690        // Export
     691        window.wp = window.wp || {};
     692        window.wp.editor = window.wp.editor || {};
     693        window.wp.editor.fullscreen = api;
     694
     695})( jQuery, window );
  • trunk/src/wp-includes/class-wp-editor.php

    r26315 r26876  
    190190                if ( self::$this_tinymce ) {
    191191
    192                         if ( empty(self::$first_init) ) {
    193                                 self::$baseurl = includes_url('js/tinymce');
    194                                 self::$mce_locale = $mce_locale = ( '' == get_locale() ) ? 'en' : strtolower( substr(get_locale(), 0, 2) ); // only ISO 639-1
     192                        if ( empty( self::$first_init ) ) {
     193                                self::$baseurl = includes_url( 'js/tinymce' );
     194                                $mce_locale = get_locale();
     195
     196                                if ( empty( $mce_locale ) || 'en' == substr( $mce_locale, 0, 2 ) ) {
     197                                        $mce_locale = 'en';
     198                                }
     199
     200                                self::$mce_locale = $mce_locale;
    195201                                $no_captions = (bool) apply_filters( 'disable_captions', '' );
    196                                 $plugins = array( 'inlinepopups', 'tabfocus', 'paste', 'media', 'fullscreen', 'wordpress', 'wpeditimage', 'wpgallery', 'wplink', 'wpdialogs' );
    197202                                $first_run = true;
    198203                                $ext_plugins = '';
    199204
    200205                                if ( $set['teeny'] ) {
    201                                         self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs' ), $editor_id );
     206                                        self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'fullscreen', 'link', 'image', 'wordpress', 'wplink' ), $editor_id );
    202207                                } else {
    203                                         /*
    204                                         The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
    205                                         It adds the plugin's name to TinyMCE's plugins init and the call to PluginManager to load the plugin.
    206                                         The url should be absolute and should include the js file name to be loaded. Example:
    207                                         array( 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js' )
    208                                         If the plugin uses a button, it should be added with one of the "$mce_buttons" filters.
    209                                         */
    210                                         $mce_external_plugins = apply_filters('mce_external_plugins', array());
    211 
    212                                         if ( ! empty($mce_external_plugins) ) {
    213 
    214                                                 /*
    215                                                 The following filter loads external language files for TinyMCE plugins.
    216                                                 It takes an associative array 'plugin_name' => 'path', where path is the
    217                                                 include path to the file. The language file should follow the same format as
    218                                                 /tinymce/langs/wp-langs.php and should define a variable $strings that
    219                                                 holds all translated strings.
    220                                                 When this filter is not used, the function will try to load {mce_locale}.js.
    221                                                 If that is not found, en.js will be tried next.
    222                                                 */
    223                                                 $mce_external_languages = apply_filters('mce_external_languages', array());
     208                                        /**
     209                                         * TinyMCE external plugins filter
     210                                         *
     211                                         * Takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
     212                                         * The url should be absolute and should include the js file name to be loaded.
     213                                         * Example: 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js'.
     214                                         * If the plugin adds a button, it should be added with one of the "$mce_buttons" filters.
     215                                         */
     216                                        $mce_external_plugins = apply_filters( 'mce_external_plugins', array() );
     217
     218                                        /**
     219                                         * TinyMCE default plugins filter
     220                                         *
     221                                         * Specifies which of the default plugins that are included in WordPress should be added to
     222                                         * the TinyMCE instance.
     223                                         */
     224                                        $plugins = array_unique( apply_filters( 'tiny_mce_plugins', array(
     225                                                'charmap',
     226                                                'link',
     227                                                'media',
     228                                                'paste',
     229                                                'tabfocus',
     230                                                'textcolor',
     231                                                'image',
     232                                                'fullscreen',
     233                                                'wordpress',
     234                                                'wpeditimage',
     235                                                'wpgallery',
     236                                                'wplink',
     237                                        ) ) );
     238
     239                                        if ( ! empty( $mce_external_plugins ) ) {
     240                                                /**
     241                                                 * This filter loads translations for external TinyMCE 3.x plugins.
     242                                                 *
     243                                                 * Takes an associative array 'plugin_name' => 'path', where path is the
     244                                                 * include path to the file. The language file should follow the same format as
     245                                                 * wp_mce_translation() and should define a variable $strings that
     246                                                 * holds all translated strings.
     247                                                 */
     248                                                $mce_external_languages = apply_filters( 'mce_external_languages', array() );
    224249
    225250                                                $loaded_langs = array();
    226251                                                $strings = '';
    227252
    228                                                 if ( ! empty($mce_external_languages) ) {
     253                                                if ( ! empty( $mce_external_languages ) ) {
    229254                                                        foreach ( $mce_external_languages as $name => $path ) {
    230                                                                 if ( @is_file($path) && @is_readable($path) ) {
    231                                                                         include_once($path);
     255                                                                if ( @is_file( $path ) && @is_readable( $path ) ) {
     256                                                                        include_once( $path );
    232257                                                                        $ext_plugins .= $strings . "\n";
    233258                                                                        $loaded_langs[] = $name;
    … …  
    237262
    238263                                                foreach ( $mce_external_plugins as $name => $url ) {
     264                                                        if ( in_array( $name, $plugins, true ) ) {
     265                                                                unset( $mce_external_plugins[ $name ] );
     266                                                                continue;
     267                                                        }
    239268
    240269                                                        $url = set_url_scheme( $url );
    241 
    242                                                         $plugins[] = '-' . $name;
    243 
    244                                                         $plugurl = dirname($url);
    245                                                         $strings = $str1 = $str2 = '';
    246                                                         if ( ! in_array($name, $loaded_langs) ) {
    247                                                                 $path = str_replace( content_url(), '', $plugurl );
    248                                                                 $path = WP_CONTENT_DIR . $path . '/langs/';
    249 
    250                                                                 if ( function_exists('realpath') )
    251                                                                         $path = trailingslashit( realpath($path) );
    252 
    253                                                                 if ( @is_file($path . $mce_locale . '.js') )
    254                                                                         $strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
    255 
    256                                                                 if ( @is_file($path . $mce_locale . '_dlg.js') )
    257                                                                         $strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
    258 
    259                                                                 if ( 'en' != $mce_locale && empty($strings) ) {
    260                                                                         if ( @is_file($path . 'en.js') ) {
    261                                                                                 $str1 = @file_get_contents($path . 'en.js');
    262                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
    263                                                                         }
    264 
    265                                                                         if ( @is_file($path . 'en_dlg.js') ) {
    266                                                                                 $str2 = @file_get_contents($path . 'en_dlg.js');
    267                                                                                 $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
    268                                                                         }
    269                                                                 }
    270 
    271                                                                 if ( ! empty($strings) )
    272                                                                         $ext_plugins .= "\n" . $strings . "\n";
     270                                                        $mce_external_plugins[ $name ] = $url;
     271                                                        $plugurl = dirname( $url );
     272
     273                                                        if ( in_array( $name, $loaded_langs ) ) {
     274                                                                $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
    273275                                                        }
    274 
    275                                                         $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
    276                                                         $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
    277276                                                }
    278277                                        }
    279 
    280                                         $plugins = array_unique( apply_filters('tiny_mce_plugins', $plugins) );
    281278                                }
    282279
    … …  
    287284                                self::$ext_plugins = $ext_plugins;
    288285
    289                                 if ( in_array( 'spellchecker', $plugins ) ) {
    290                                         /*
    291                                         translators: These languages show up in the spellchecker drop-down menu, in the order specified, and with the first
    292                                         language listed being the default language. They must be comma-separated and take the format of name=code, where name
    293                                         is the language name (which you may internationalize), and code is a valid ISO 639 language code. Please test the
    294                                         spellchecker with your values.
    295                                         */
    296                                         $mce_spellchecker_languages = __( 'English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv' );
    297 
    298                                         /*
    299                                         The following filter allows localization scripts to change the languages displayed in the spellchecker's drop-down menu.
    300                                         By default it uses Google's spellchecker API, but can be configured to use PSpell/ASpell if installed on the server.
    301                                         The + sign marks the default language. More: http://www.tinymce.com/wiki.php/Plugin:spellchecker.
    302                                         */
    303                                         $mce_spellchecker_languages = apply_filters( 'mce_spellchecker_languages', '+' . $mce_spellchecker_languages );
    304                                 }
    305 
    306286                                self::$first_init = array(
    307                                         'mode' => 'exact',
    308                                         'width' => '100%',
    309                                         'theme' => 'advanced',
    310                                         'skin' => 'wp_theme',
     287                                        'theme' => 'modern',
     288                                        'skin' => 'lightgray',
    311289                                        'language' => self::$mce_locale,
    312                                         'theme_advanced_toolbar_location' => 'top',
    313                                         'theme_advanced_toolbar_align' => 'left',
    314                                         'theme_advanced_statusbar_location' => 'bottom',
    315                                         'theme_advanced_resizing' => true,
    316                                         'theme_advanced_resize_horizontal' => false,
    317                                         'dialog_type' => 'modal',
     290                                        'resize' => 'vertical',
    318291                                        'formats' => "{
    319                                                 alignleft : [
    320                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
    321                                                         {selector : 'img,table', classes : 'alignleft'}
     292                                                alignleft: [
     293                                                        {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'left'}},
     294                                                        {selector: 'img,table,dl.wp-caption', classes: 'alignleft'}
    322295                                                ],
    323                                                 aligncenter : [
    324                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
    325                                                         {selector : 'img,table', classes : 'aligncenter'}
     296                                                aligncenter: [
     297                                                        {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'center'}},
     298                                                        {selector: 'img,table,dl.wp-caption', classes: 'aligncenter'}
    326299                                                ],
    327                                                 alignright : [
    328                                                         {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
    329                                                         {selector : 'img,table', classes : 'alignright'}
     300                                                alignright: [
     301                                                        {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles: {textAlign:'right'}},
     302                                                        {selector: 'img,table,dl.wp-caption', classes: 'alignright'}
    330303                                                ],
    331                                                 strikethrough : {inline : 'del'}
     304                                                strikethrough: {inline: 'del'}
    332305                                        }",
    333306                                        'relative_urls' => false,
    334307                                        'remove_script_host' => false,
    335308                                        'convert_urls' => false,
    336                                         'remove_linebreaks' => true,
    337                                         'gecko_spellcheck' => true,
     309                                        'browser_spellcheck' => true,
    338310                                        'fix_list_elements' => true,
     311                                        'entities' => '38,amp,60,lt,62,gt',
     312                                        'entity_encoding' => 'raw',
     313                                        'menubar' => false,
     314                                        'object_resizing' => false,
    339315                                        'keep_styles' => false,
    340                                         'entities' => '38,amp,60,lt,62,gt',
    341                                         'accessibility_focus' => true,
    342                                         'media_strict' => false,
    343316                                        'paste_remove_styles' => true,
    344                                         'paste_remove_spans' => true,
    345                                         'paste_strip_class_attributes' => 'all',
    346                                         'paste_text_use_dialog' => true,
    347                                         'webkit_fake_resize' => false,
    348                                         'preview_styles' => 'font-family font-weight text-decoration text-transform',
    349                                         'schema' => 'html5',
     317
     318                                        // limit the preview styles in the menu/toolbar
     319                        //              'preview_styles' => 'font-family font-weight font-style text-decoration text-transform color background-color border',
     320
    350321                                        'wpeditimage_disable_captions' => $no_captions,
    351                                         'wp_fullscreen_content_css' => self::$baseurl . '/plugins/wpfullscreen/css/wp-fullscreen.css',
    352                                         'plugins' => implode( ',', $plugins )
     322                                        'plugins' => implode( ',', $plugins ),
    353323                                );
    354324
    355                                 if ( in_array( 'spellchecker', $plugins ) ) {
     325                                if ( ! empty( $mce_external_plugins ) ) {
     326                                        self::$first_init['external_plugins'] = json_encode( $mce_external_plugins );
     327                                }
     328
     329                                if ( in_array( 'spellchecker', $plugins, true ) ) {
    356330                                        self::$first_init['spellchecker_rpc_url'] = self::$baseurl . '/plugins/spellchecker/rpc.php';
    357                                         self::$first_init['spellchecker_languages'] = $mce_spellchecker_languages;
     331                                        self::$first_init['spellchecker_language'] = self::$mce_locale;
    358332                                }
     333
     334                                // WordPress default stylesheet
     335                                $mce_css = array( self::$baseurl . '/skins/wordpress/wp-content.css' );
    359336
    360337                                // load editor_style.css if the current theme supports it
    … …  
    362339                                        $editor_styles = $GLOBALS['editor_styles'];
    363340
    364                                         $mce_css = array();
    365341                                        $editor_styles = array_unique( array_filter( $editor_styles ) );
    366342                                        $style_uri = get_stylesheet_directory_uri();
    … …  
    390366                                                        $mce_css[] = "$style_uri/$file";
    391367                                        }
    392 
    393                                         $mce_css = implode( ',', $mce_css );
    394                                 } else {
    395                                         $mce_css = '';
    396368                                }
    397369
    398                                 $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
     370                                $mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
    399371
    400372                                if ( ! empty($mce_css) )
    … …  
    403375
    404376                        if ( $set['teeny'] ) {
    405                                 $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'justifyleft', 'justifycenter', 'justifyright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
     377                                $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold', 'italic', 'underline', 'blockquote', 'strikethrough', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'undo', 'redo', 'link', 'unlink', 'fullscreen'), $editor_id );
    406378                                $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = array();
    407379                        } else {
    408                                 $mce_buttons = apply_filters('mce_buttons', array('bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'justifyleft', 'justifycenter', 'justifyright', 'link', 'unlink', 'wp_more', 'spellchecker', 'fullscreen', 'wp_adv' ), $editor_id);
    409                                 $mce_buttons_2 = apply_filters('mce_buttons_2', array( 'formatselect', 'underline', 'justifyfull', 'forecolor', 'pastetext', 'pasteword', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' ), $editor_id);
     380                                $mce_buttons = apply_filters('mce_buttons', array('bold', 'italic', 'strikethrough', 'bullist', 'numlist', 'blockquote', 'alignleft', 'aligncenter', 'alignright', 'image', 'link', 'unlink', 'wp_more', 'spellchecker', 'fullscreen', 'wp_adv' ), $editor_id);
     381                                $mce_buttons_2 = apply_filters('mce_buttons_2', array( 'formatselect', 'underline', 'alignjustify', 'forecolor', 'pastetext', 'removeformat', 'charmap', 'outdent', 'indent', 'undo', 'redo', 'wp_help' ), $editor_id);
    410382                                $mce_buttons_3 = apply_filters('mce_buttons_3', array(), $editor_id);
    411383                                $mce_buttons_4 = apply_filters('mce_buttons_4', array(), $editor_id);
    … …  
    443415
    444416                        $mceInit = array (
    445                                 'elements' => $editor_id,
     417                                'selector' => "#$editor_id",
    446418                                'wpautop' => (bool) $set['wpautop'],
    447                                 'remove_linebreaks' => (bool) $set['wpautop'],
    448                                 'apply_source_formatting' => (bool) !$set['wpautop'],
    449                                 'theme_advanced_buttons1' => implode($mce_buttons, ','),
    450                                 'theme_advanced_buttons2' => implode($mce_buttons_2, ','),
    451                                 'theme_advanced_buttons3' => implode($mce_buttons_3, ','),
    452                                 'theme_advanced_buttons4' => implode($mce_buttons_4, ','),
     419                                'indent' => ! $set['wpautop'],
     420                                'toolbar1' => implode($mce_buttons, ','),
     421                                'toolbar2' => implode($mce_buttons_2, ','),
     422                                'toolbar3' => implode($mce_buttons_3, ','),
     423                                'toolbar4' => implode($mce_buttons_4, ','),
    453424                                'tabfocus_elements' => $set['tabfocus_elements'],
    454425                                'body_class' => $body_class
    455426                        );
    456427
    457                         // The main editor doesn't use the TinyMCE resizing cookie.
    458                         $mceInit['theme_advanced_resizing_use_cookie'] = 'content' !== $editor_id || empty( $set['editor_height'] );
    459 
    460428                        if ( $first_run )
    461                                 $mceInit = array_merge(self::$first_init, $mceInit);
    462 
    463                         if ( is_array($set['tinymce']) )
    464                                 $mceInit = array_merge($mceInit, $set['tinymce']);
     429                                $mceInit = array_merge( self::$first_init, $mceInit );
     430
     431                        if ( is_array( $set['tinymce'] ) )
     432                                $mceInit = array_merge( $mceInit, $set['tinymce'] );
    465433
    466434                        // For people who really REALLY know what they're doing with TinyMCE
    … …  
    469437                        // Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
    470438                        if ( $set['teeny'] ) {
    471                                 $mceInit = apply_filters('teeny_mce_before_init', $mceInit, $editor_id);
     439                                $mceInit = apply_filters( 'teeny_mce_before_init', $mceInit, $editor_id );
    472440                        } else {
    473                                 $mceInit = apply_filters('tiny_mce_before_init', $mceInit, $editor_id);
    474                         }
    475 
    476                         if ( empty($mceInit['theme_advanced_buttons3']) && !empty($mceInit['theme_advanced_buttons4']) ) {
    477                                 $mceInit['theme_advanced_buttons3'] = $mceInit['theme_advanced_buttons4'];
    478                                 $mceInit['theme_advanced_buttons4'] = '';
     441                                $mceInit = apply_filters( 'tiny_mce_before_init', $mceInit, $editor_id );
     442                        }
     443
     444                        if ( empty( $mceInit['toolbar3'] ) && ! empty( $mceInit['toolbar4'] ) ) {
     445                                $mceInit['toolbar3'] = $mceInit['toolbar4'];
     446                                $mceInit['toolbar4'] = '';
    479447                        }
    480448
    … …  
    512480                if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
    513481                        wp_enqueue_script('wplink');
    514                         wp_enqueue_script('wpdialogs-popup');
     482                        wp_enqueue_script('wpdialogs');
    515483                        wp_enqueue_style('wp-jquery-ui-dialog');
    516484                }
    … …  
    520488
    521489                if ( self::$has_medialib ) {
    522                         add_thickbox();
     490        //              add_thickbox(); // ?
    523491                        wp_enqueue_script('media-upload');
    524492                }
     493        }
     494
     495        public static function wp_mce_translation() {
     496
     497                $mce_translation = array(
     498                        // Default TinyMCE strings
     499                        'Cut' => __('Cut'),
     500                        'Header 2' => __('Header 2'),
     501                        'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __('Your browser does not support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.'),
     502                        'Div' => __('Div'),
     503                        'Paste' => __('Paste'),
     504                        'Close' => __('Close'),
     505                        'Pre' => __('Pre'),
     506                        'Align right' => __('Align right'),
     507                        'New document' => __('New document'),
     508                        'Blockquote' => __('Blockquote'),
     509                        'Numbered list' => __('Numbered list'),
     510                        'Increase indent' => __('Increase indent'),
     511                        'Formats' => __('Formats'),
     512                        'Headers' => __('Headers'),
     513                        'Select all' => __('Select all'),
     514                        'Header 3' => __('Header 3'),
     515                        'Blocks' => __('Blocks'),
     516                        'Undo' => __('Undo'),
     517                        'Strikethrough' => __('Strikethrough'),
     518                        'Bullet list' => __('Bullet list'),
     519                        'Header 1' => __('Header 1'),
     520                        'Superscript' => __('Superscript'),
     521                        'Clear formatting' => __('Clear formatting'),
     522                        'Subscript' => __('Subscript'),
     523                        'Header 6' => __('Header 6'),
     524                        'Redo' => __('Redo'),
     525                        'Paragraph' => __('Paragraph'),
     526                        'Ok' => __('Ok'),
     527                        'Bold' => __('Bold'),
     528                        'Code' => __('Code'),
     529                        'Italic' => __('Italic'),
     530                        'Align center' => __('Align center'),
     531                        'Header 5' => __('Header 5'),
     532                        'Decrease indent' => __('Decrease indent'),
     533                        'Header 4' => __('Header 4'),
     534                        'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __('Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.'),
     535                        'Underline' => __('Underline'),
     536                        'Cancel' => __('Cancel'),
     537                        'Justify' => __('Justify'),
     538                        'Inline' => __('Inline'),
     539                        'Copy' => __('Copy'),
     540                        'Align left' => __('Align left'),
     541                        'Visual aids' => __('Visual aids'),
     542                        'Lower Greek' => __('Lower Greek'),
     543                        'Square' => __('Square'),
     544                        'Default' => __('Default'),
     545                        'Lower Alpha' => __('Lower Alpha'),
     546                        'Circle' => __('Circle'),
     547                        'Disc' => __('Disc'),
     548                        'Upper Alpha' => __('Upper Alpha'),
     549                        'Upper Roman' => __('Upper Roman'),
     550                        'Lower Roman' => __('Lower Roman'),
     551                        'Name' => __('Name'),
     552                        'Anchor' => __('Anchor'),
     553                        'You have unsaved changes are you sure you want to navigate away?' => __('You have unsaved changes are you sure you want to navigate away?'),
     554                        'Restore last draft' => __('Restore last draft'),
     555                        'Special character' => __('Special character'),
     556                        'Source code' => __('Source code'),
     557                        'Right to left' => __('Right to left'),
     558                        'Left to right' => __('Left to right'),
     559                        'Emoticons' => __('Emoticons'),
     560                        'Robots' => __('Robots'),
     561                        'Document properties' => __('Document properties'),
     562                        'Title' => __('Title'),
     563                        'Keywords' => __('Keywords'),
     564                        'Encoding' => __('Encoding'),
     565                        'Description' => __('Description'),
     566                        'Author' => __('Author'),
     567                        'Fullscreen' => __('Fullscreen'),
     568                        'Horizontal line' => __('Horizontal line'),
     569                        'Horizontal space' => __('Horizontal space'),
     570                        'Insert/edit image' => __('Insert/edit image'),
     571                        'General' => __('General'),
     572                        'Advanced' => __('Advanced'),
     573                        'Source' => __('Source'),
     574                        'Border' => __('Border'),
     575                        'Constrain proportions' => __('Constrain proportions'),
     576                        'Vertical space' => __('Vertical space'),
     577                        'Image description' => __('Image description'),
     578                        'Style' => __('Style'),
     579                        'Dimensions' => __('Dimensions'),
     580                        'Insert image' => __('Insert image'),
     581                        'Insert date/time' => __('Insert date/time'),
     582                        'Remove link' => __('Remove link'),
     583                        'Url' => __('Url'),
     584                        'Text to display' => __('Text to display'),
     585                        'Anchors' => __('Anchors'),
     586                        'Insert link' => __('Insert link'),
     587                        'New window' => __('New window'),
     588                        'None' => __('None'),
     589                        'Target' => __('Target'),
     590                        'Insert/edit link' => __('Insert/edit link'),
     591                        'Insert/edit video' => __('Insert/edit video'),
     592                        'Poster' => __('Poster'),
     593                        'Alternative source' => __('Alternative source'),
     594                        'Paste your embed code below:' => __('Paste your embed code below:'),
     595                        'Insert video' => __('Insert video'),
     596                        'Embed' => __('Embed'),
     597                        'Nonbreaking space' => __('Nonbreaking space'),
     598                        'Page break' => __('Page break'),
     599                        'Paste as text' => __('Paste as text'),
     600                        'Preview' => __('Preview'),
     601                        'Print' => __('Print'),
     602                        'Save' => __('Save'),
     603                        'Could not find the specified string.' => __('Could not find the specified string.'),
     604                        'Replace' => __('Replace'),
     605                        'Next' => __('Next'),
     606                        'Whole words' => __('Whole words'),
     607                        'Find and replace' => __('Find and replace'),
     608                        'Replace with' => __('Replace with'),
     609                        'Find' => __('Find'),
     610                        'Replace all' => __('Replace all'),
     611                        'Match case' => __('Match case'),
     612                        'Prev' => __('Prev'),
     613                        'Spellcheck' => __('Spellcheck'),
     614                        'Finish' => __('Finish'),
     615                        'Ignore all' => __('Ignore all'),
     616                        'Ignore' => __('Ignore'),
     617                        'Insert row before' => __('Insert row before'),
     618                        'Rows' => __('Rows'),
     619                        'Height' => __('Height'),
     620                        'Paste row after' => __('Paste row after'),
     621                        'Alignment' => __('Alignment'),
     622                        'Column group' => __('Column group'),
     623                        'Row' => __('Row'),
     624                        'Insert column before' => __('Insert column before'),
     625                        'Split cell' => __('Split cell'),
     626                        'Cell padding' => __('Cell padding'),
     627                        'Cell spacing' => __('Cell spacing'),
     628                        'Row type' => __('Row type'),
     629                        'Insert table' => __('Insert table'),
     630                        'Body' => __('Body'),
     631                        'Caption' => __('Caption'),
     632                        'Footer' => __('Footer'),
     633                        'Delete row' => __('Delete row'),
     634                        'Paste row before' => __('Paste row before'),
     635                        'Scope' => __('Scope'),
     636                        'Delete table' => __('Delete table'),
     637                        'Header cell' => __('Header cell'),
     638                        'Column' => __('Column'),
     639                        'Cell' => __('Cell'),
     640                        'Header' => __('Header'),
     641                        'Cell type' => __('Cell type'),
     642                        'Copy row' => __('Copy row'),
     643                        'Row properties' => __('Row properties'),
     644                        'Table properties' => __('Table properties'),
     645                        'Row group' => __('Row group'),
     646                        'Right' => __('Right'),
     647                        'Insert column after' => __('Insert column after'),
     648                        'Cols' => __('Cols'),
     649                        'Insert row after' => __('Insert row after'),
     650                        'Width' => __('Width'),
     651                        'Cell properties' => __('Cell properties'),
     652                        'Left' => __('Left'),
     653                        'Cut row' => __('Cut row'),
     654                        'Delete column' => __('Delete column'),
     655                        'Center' => __('Center'),
     656                        'Merge cells' => __('Merge cells'),
     657                        'Insert template' => __('Insert template'),
     658                        'Templates' => __('Templates'),
     659                        'Background color' => __('Background color'),
     660                        'Text color' => __('Text color'),
     661                        'Show blocks' => __('Show blocks'),
     662                        'Show invisible characters' => __('Show invisible characters'),
     663                        'Words: {0}' => __('Words: {0}'),
     664                        'Insert' => __('Insert'),
     665                        'File' => __('File'),
     666                        'Edit' => __('Edit'),
     667                        'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __('Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help'),
     668                        'Tools' => __('Tools'),
     669                        'View' => __('View'),
     670                        'Table' => __('Table'),
     671                        'Format' => __('Format'),
     672
     673                        // WordPress strings
     674                        'Help' => __('Help'),
     675                        'Toolbar Toggle' => __('Toolbar Toggle'),
     676                        'Insert Read More tag' => __('Insert Read More tag'),
     677                        'Distraction Free Writing' => __('Distraction Free Writing'),
     678                );
     679
     680                $baseurl = self::$baseurl;
     681                $mce_locale = self::$mce_locale;
     682
     683                $mce_translation = apply_filters( 'wp_mce_translation', $mce_translation, $mce_locale );
     684
     685                foreach ( $mce_translation as $key => $value ) {
     686                        if ( strpos( $value, '&' ) !== false )
     687                                $mce_translation[$key] = html_entity_decode( $value, ENT_QUOTES );
     688                }
     689
     690                return "tinymce.addI18n( '$mce_locale', " . json_encode( $mce_translation ) . ");\n" .
     691                        "tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
    525692        }
    526693
    … …  
    544711                        && false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
    545712
    546                 if ( $tmce_on && 'en' != self::$mce_locale )
    547                         include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
    548 
    549713                $mceInit = $qtInit = '';
    550714                if ( $tmce_on ) {
    … …  
    570734                $ref = array(
    571735                        'plugins' => implode( ',', self::$plugins ),
    572                         'theme' => 'advanced',
     736                        'theme' => 'modern',
    573737                        'language' => self::$mce_locale
    574738                );
    575739
    576                 $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
    577 
    578                 do_action('before_wp_tiny_mce', self::$mce_settings);
    579 ?>
    580 
    581         <script type="text/javascript">
     740                $suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '' : '.min';
     741
     742                do_action( 'before_wp_tiny_mce', self::$mce_settings );
     743                ?>
     744
     745                <script type="text/javascript">
    582746                tinyMCEPreInit = {
    583                         base : "<?php echo self::$baseurl; ?>",
    584                         suffix : "<?php echo $suffix; ?>",
    585                         query : "<?php echo $version; ?>",
    586                         mceInit : <?php echo $mceInit; ?>,
    587                         qtInit : <?php echo $qtInit; ?>,
    588                         ref : <?php echo self::_parse_init( $ref ); ?>,
    589                         load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
     747                        baseURL: "<?php echo self::$baseurl; ?>",
     748                        suffix: "<?php echo $suffix; ?>",
     749                        mceInit: <?php echo $mceInit; ?>,
     750                        qtInit: <?php echo $qtInit; ?>,
     751                        ref: <?php echo self::_parse_init( $ref ); ?>,
     752                        load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
    590753                };
    591         </script>
    592 <?php
     754                </script>
     755                <?php
    593756
    594757                $baseurl = self::$baseurl;
    … …  
    598761                                echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
    599762                        } else {
    600                                 echo "<script type='text/javascript' src='{$baseurl}/tiny_mce.js?$version'></script>\n";
    601                                 echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce-schema.js?$version'></script>\n";
    602                         }
    603 
    604                         if ( 'en' != self::$mce_locale && isset($lang) )
    605                                 echo "<script type='text/javascript'>\n$lang\n</script>\n";
    606                         else
     763                                echo "<script type='text/javascript' src='{$baseurl}/tinymce.js?$version'></script>\n";
     764                                echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
     765                        }
     766
     767                        if ( 'en' != self::$mce_locale )
     768                                echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
     769
     770                        if ( self::$ext_plugins ) {
     771                                // Load the old-format English strings to prevent unsightly labels in old style popups
    607772                                echo "<script type='text/javascript' src='{$baseurl}/langs/wp-langs-en.js?$version'></script>\n";
    608                 }
    609 
    610                 $mce = ( self::$has_tinymce && wp_default_editor() == 'tinymce' ) || ! self::$has_quicktags;
    611 ?>
    612 
    613         <script type="text/javascript">
    614                 var wpActiveEditor;
    615 
    616                 (function(){
    617                         var init, ed, qt, first_init, DOM, el, i, mce = <?php echo (int) $mce; ?>;
    618 
    619                         if ( typeof(tinymce) == 'object' ) {
    620                                 DOM = tinymce.DOM;
    621                                 // mark wp_theme/ui.css as loaded
    622                                 DOM.files[tinymce.baseURI.getURI() + '/themes/advanced/skins/wp_theme/ui.css'] = true;
    623 
    624                                 DOM.events.add( DOM.select('.wp-editor-wrap'), 'mousedown', function(e){
    625                                         if ( this.id )
    626                                                 wpActiveEditor = this.id.slice(3, -5);
    627                                 });
    628 
    629                                 for ( ed in tinyMCEPreInit.mceInit ) {
    630                                         if ( first_init ) {
    631                                                 init = tinyMCEPreInit.mceInit[ed] = tinymce.extend( {}, first_init, tinyMCEPreInit.mceInit[ed] );
     773                        }
     774                }
     775
     776                // Allow scripts to be added after tinymce.js has been loaded but before any editor instances are created.
     777                do_action( 'wp_tiny_mce_init', self::$mce_settings );
     778
     779                ?>
     780                <script type="text/javascript">
     781                <?php
     782
     783                if ( self::$ext_plugins )
     784                        echo self::$ext_plugins . "\n";
     785
     786                if ( ! is_admin() )
     787                        echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
     788
     789                ?>
     790
     791                ( function() {
     792                        var init, edId, qtId, firstInit,
     793                                loadMCE = typeof getUserSetting !== 'undefined' ? getUserSetting( 'editor' ) === 'tinymce' : true;
     794
     795                        if ( typeof quicktags !== 'undefined' ) {
     796                                for ( qtId in tinyMCEPreInit.qtInit ) {
     797                                        try { quicktags( tinyMCEPreInit.qtInit[qtId] ); } catch(e){};
     798                                }
     799                        }
     800
     801                        if ( typeof tinymce !== 'undefined' ) {
     802                                for ( edId in tinyMCEPreInit.mceInit ) {
     803                                        if ( firstInit ) {
     804                                                init = tinyMCEPreInit.mceInit[edId] = tinymce.extend( {}, firstInit, tinyMCEPreInit.mceInit[edId] );
    632805                                        } else {
    633                                                 init = first_init = tinyMCEPreInit.mceInit[ed];
     806                                                init = firstInit = tinyMCEPreInit.mceInit[edId];
    634807                                        }
    635808
    636                                         if ( mce )
    637                                                 try { tinymce.init(init); } catch(e){}
    638                                 }
    639                         } else {
    640                                 if ( tinyMCEPreInit.qtInit ) {
    641                                         for ( i in tinyMCEPreInit.qtInit ) {
    642                                                 el = tinyMCEPreInit.qtInit[i].id;
    643                                                 if ( el )
    644                                                         document.getElementById('wp-'+el+'-wrap').onmousedown = function(){ wpActiveEditor = this.id.slice(3, -5); }
     809                                        if ( ( loadMCE || ! tinyMCEPreInit.qtInit.hasOwnProperty( edId ) ) && ! init.wp_skip_init ) {
     810                                                try { tinymce.init( init ); } catch(e){}
    645811                                        }
    646812                                }
    647813                        }
    648 
    649                         if ( typeof(QTags) == 'function' ) {
    650                                 for ( qt in tinyMCEPreInit.qtInit ) {
    651                                         try { quicktags( tinyMCEPreInit.qtInit[qt] ); } catch(e){}
    652                                 }
    653                         }
    654                 })();
    655                 <?php
    656 
    657                 if ( self::$ext_plugins )
    658                         echo self::$ext_plugins . "\n";
    659 
    660                 if ( ! $compressed && $tmce_on ) {
    661                         ?>
    662                         (function(){var t=tinyMCEPreInit,sl=tinymce.ScriptLoader,ln=t.ref.language,th=t.ref.theme,pl=t.ref.plugins;sl.markDone(t.base+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'_dlg.js');sl.markDone(t.base+'/themes/advanced/skins/wp_theme/ui.css');tinymce.each(pl.split(','),function(n){if(n&&n.charAt(0)!='-'){sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'.js');sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'_dlg.js');}});})();
    663                         <?php
    664                 }
    665 
    666                 if ( !is_admin() )
    667                         echo 'var ajaxurl = "' . admin_url( 'admin-ajax.php', 'relative' ) . '";';
    668 
    669                 ?>
     814                }());
     815
    670816                </script>
    671817                <?php
    … …  
    677823                        self::wp_fullscreen_html();
    678824
    679                 do_action('after_wp_tiny_mce', self::$mce_settings);
     825                do_action( 'after_wp_tiny_mce', self::$mce_settings );
    680826        }
    681827
    … …  
    684830                $post = get_post();
    685831
    686                 $width = isset($content_width) && 800 > $content_width ? $content_width : 800;
     832                $width = isset( $content_width ) && 800 > $content_width ? $content_width : 800;
    687833                $width = $width + 22; // compensate for the padding and border
    688834                $dfw_width = get_user_setting( 'dfw_width', $width );
    689                 $save = isset($post->post_status) && $post->post_status == 'publish' ? __('Update') : __('Save');
    690         ?>
    691         <div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?>>
    692         <div id="fullscreen-topbar">
    693                 <div id="wp-fullscreen-toolbar">
    694                         <div id="wp-fullscreen-close"><a href="#" onclick="fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
     835                $save = isset( $post->post_status ) && $post->post_status == 'publish' ? __('Update') : __('Save');
     836
     837                ?>
     838                <div id="wp-fullscreen-body"<?php if ( is_rtl() ) echo ' class="rtl"'; ?> data-theme-width="<?php echo (int) $width; ?>" data-dfw-width="<?php echo (int) $dfw_width; ?>">
     839                <div id="fullscreen-topbar">
     840                        <div id="wp-fullscreen-toolbar">
     841                        <div id="wp-fullscreen-close"><a href="#" onclick="wp.editor.fullscreen.off();return false;"><?php _e('Exit fullscreen'); ?></a></div>
    695842                        <div id="wp-fullscreen-central-toolbar" style="width:<?php echo $width; ?>px;">
    696843
    697844                        <div id="wp-fullscreen-mode-bar"><div id="wp-fullscreen-modes">
    698                                 <a href="#" onclick="fullscreen.switchmode('tinymce');return false;"><?php _e( 'Visual' ); ?></a>
    699                                 <a href="#" onclick="fullscreen.switchmode('html');return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a>
     845                                <a href="#" onclick="wp.editor.fullscreen.switchmode('tinymce');return false;"><?php _e( 'Visual' ); ?></a>
     846                                <a href="#" onclick="wp.editor.fullscreen.switchmode('html');return false;"><?php _ex( 'Text', 'Name for the Text editor tab (formerly HTML)' ); ?></a>
    700847                        </div></div>
    701848
    702                         <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="wp_themeSkin">
    703         <?php
     849                        <div id="wp-fullscreen-button-bar"><div id="wp-fullscreen-buttons" class="mce-toolbar">
     850                <?php
    704851
    705852                $buttons = array(
    706853                        // format: title, onclick, show in both editors
    707                         'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'onclick' => 'fullscreen.b();', 'both' => false ),
    708                         'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'onclick' => 'fullscreen.i();', 'both' => false ),
    709                         '0' => 'separator',
    710                         'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'onclick' => 'fullscreen.ul();', 'both' => false ),
    711                         'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'onclick' => 'fullscreen.ol();', 'both' => false ),
    712                         '1' => 'separator',
    713                         'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'onclick' => 'fullscreen.blockquote();', 'both' => false ),
    714                         'image' => array( 'title' => __('Insert/edit image (Alt + Shift + M)'), 'onclick' => "fullscreen.medialib();", 'both' => true ),
    715                         '2' => 'separator',
    716                         'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'onclick' => 'fullscreen.link();', 'both' => true ),
    717                         'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'onclick' => 'fullscreen.unlink();', 'both' => false ),
    718                         '3' => 'separator',
    719                         'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'onclick' => 'fullscreen.help();', 'both' => false )
     854                        'bold' => array( 'title' => __('Bold (Ctrl + B)'), 'both' => false ),
     855                        'italic' => array( 'title' => __('Italic (Ctrl + I)'), 'both' => false ),
     856                        'bullist' => array( 'title' => __('Unordered list (Alt + Shift + U)'), 'both' => false ),
     857                        'numlist' => array( 'title' => __('Ordered list (Alt + Shift + O)'), 'both' => false ),
     858                        'blockquote' => array( 'title' => __('Blockquote (Alt + Shift + Q)'), 'both' => false ),
     859                        'wp-media-library' => array( 'title' => __('Media library (Alt + Shift + M)'), 'both' => true ),
     860                        'image' => array( 'title' => __('Insert/edit image'), 'both' => false ),
     861                        'link' => array( 'title' => __('Insert/edit link (Alt + Shift + A)'), 'both' => true ),
     862                        'unlink' => array( 'title' => __('Unlink (Alt + Shift + S)'), 'both' => false ),
     863                        'help' => array( 'title' => __('Help (Alt + Shift + H)'), 'both' => false ),
    720864                );
    721865
    … …  
    723867
    724868                foreach ( $buttons as $button => $args ) {
    725                         if ( 'separator' == $args ) { ?>
    726                                 <div><span aria-orientation="vertical" role="separator" class="mceSeparator"></span></div>
    727         <?php           continue;
    728                         } ?>
    729 
    730                         <div<?php if ( $args['both'] ) { ?> class="wp-fullscreen-both"<?php } ?>>
    731                         <a title="<?php echo $args['title']; ?>" onclick="<?php echo $args['onclick']; ?>return false;" class="mceButton mceButtonEnabled mce_<?php echo $button; ?>" href="#" id="wp_fs_<?php echo $button; ?>" role="button" aria-pressed="false">
    732                         <span class="mceIcon mce_<?php echo $button; ?>"></span>
    733                         </a>
     869                        if ( 'separator' == $args ) {
     870                                continue;
     871                        }
     872
     873                        $onclick = ! empty( $args['onclick'] ) ? ' onclick="' . $args['onclick'] . '"' : '';
     874                        ?>
     875
     876                        <div class="mce-widget mce-btn<?php if ( $args['both'] ) { ?> wp-fullscreen-both<?php } ?>">
     877                        <button type="button" role="presentation" title="<?php echo $args['title']; ?>"<?php echo $onclick; ?> id="wp_fs_<?php echo $button; ?>">
     878                                <i class="mce-ico mce-i-<?php echo $button; ?>"></i>
     879                        </button>
    734880                        </div>
    735         <?php
    736                 } ?>
    737 
    738                         </div></div>
    739 
    740                         <div id="wp-fullscreen-save">
    741                                 <input type="button" class="button-primary right" value="<?php echo $save; ?>" onclick="fullscreen.save();" />
    742                                 <span class="spinner"></span>
    743                                 <span class="fs-saved"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
    744                         </div>
    745 
    746                         </div>
     881                        <?php
     882                }
     883
     884                ?>
     885
     886                </div></div>
     887
     888                <div id="wp-fullscreen-save">
     889                        <input type="button" class="button-primary right" value="<?php echo $save; ?>" onclick="wp.editor.fullscreen.save();" />
     890                        <span class="spinner"></span>
     891                        <span class="wp-fullscreen-saved-message"><?php if ( $post->post_status == 'publish' ) _e('Updated.'); else _e('Saved.'); ?></span>
     892                        <span class="wp-fullscreen-error-message"><?php _e('Save failed.'); ?></span>
     893                </div>
     894
     895                </div>
    747896                </div>
    748897        </div>
    749 
    750         <div id="wp-fullscreen-wrap" style="width:<?php echo $dfw_width; ?>px;">
    751                 <?php if ( post_type_supports($post->post_type, 'title') ) { ?>
    752                 <label id="wp-fullscreen-title-prompt-text" for="wp-fullscreen-title"><?php echo apply_filters( 'enter_title_here', __( 'Enter title here' ), $post ); ?></label>
    753                 <input type="text" id="wp-fullscreen-title" value="" autocomplete="off" />
    754                 <?php } ?>
    755 
    756                 <div id="wp-fullscreen-container">
    757                         <textarea id="wp_mce_fullscreen"></textarea>
    758                 </div>
    759 
    760                 <div id="wp-fullscreen-status">
    761                         <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
    762                         <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
    763                 </div>
     898        <div id="wp-fullscreen-status">
     899                <div id="wp-fullscreen-count"><?php printf( __( 'Word count: %s' ), '<span class="word-count">0</span>' ); ?></div>
     900                <div id="wp-fullscreen-tagline"><?php _e('Just write.'); ?></div>
    764901        </div>
    765902        </div>
  • trunk/src/wp-includes/css/editor.css

    r26819 r26876  
    1 
    21/*------------------------------------------------------------------------------
    3 
    42 TinyMCE and Quicklinks toolbars
    53------------------------------------------------------------------------------*/
    64
    7 /* wp_theme/ui.css */
    8 .wp_themeSkin table,
    9 .wp_themeSkin tbody,
    10 .wp_themeSkin a,
    11 .wp_themeSkin img,
    12 .wp_themeSkin tr,
    13 .wp_themeSkin div,
    14 .wp_themeSkin td,
    15 .wp_themeSkin iframe,
    16 .wp_themeSkin span,
    17 .wp_themeSkin *,
    18 .wp_themeSkin .mceText {
     5.mce-panel {
    196        border: 0;
    20         margin: 0;
    21         padding: 0;
    22         white-space: nowrap;
    23         text-decoration: none;
    24         font-weight: normal;
    25         cursor: default;
    26         vertical-align: baseline;
    27         width: auto;
    28         border-collapse: separate;
    29 }
    30 
    31 .wp_themeSkin a:hover,
    32 .wp_themeSkin a:link,
    33 .wp_themeSkin a:visited,
    34 .wp_themeSkin a:active {
    35         text-decoration: none;
    36         font-weight: normal;
    37         cursor: default;
    38 }
    39 
    40 .wp_themeSkin table td {
    41         vertical-align: middle;
    42 }
    43 
    44 .wp_themeSkin *,
    45 .wp_themeSkin a:hover,
    46 .wp_themeSkin a:link,
    47 .wp_themeSkin a:visited,
    48 .wp_themeSkin a:active {
    49         color: #555;
    50 }
    51 
    52 /* These are part of TinyMCE, used in TinyMCE Advanced, but not WordPress. These are not updated for 3.8's design. */
    53 .wp_themeSkin span.mce_sup,
    54 .wp_themeSkin span.mce_sub,
    55 .wp_themeSkin span.mce_media,
    56 .wp_themeSkin span.mce_styleprops,
    57 .wp_themeSkin span.mce_search,
    58 .wp_themeSkin span.mce_emotions,
    59 .wp_themeSkin span.mce_print,
    60 .wp_themeSkin span.mce_attribs,
    61 .wp_themeSkin span.mce_hr,
    62 .wp_themeSkin span.mce_cut,
    63 .wp_themeSkin span.mce_copy,
    64 .wp_themeSkin span.mce_paste,
    65 .wp_themeSkin span.mce_cite,
    66 .wp_themeSkin span.mce_visualchars,
    67 .wp_themeSkin span.mce_advhr,
    68 .wp_themeSkin span.mce_insertdate,
    69 .wp_themeSkin span.mce_anchor,
    70 .wp_themeSkin span.mce_visualaid,
    71 .wp_themeSkin span.mce_cleanup,
    72 .wp_themeSkin span.mce_table,
    73 .wp_themeSkin span.mce_row_props,
    74 .wp_themeSkin span.mce_cell_props,
    75 .wp_themeSkin span.mce_row_before,
    76 .wp_themeSkin span.mce_row_after,
    77 .wp_themeSkin span.mce_delete_row,
    78 .wp_themeSkin span.mce_col_before,
    79 .wp_themeSkin span.mce_col_after,
    80 .wp_themeSkin span.mce_delete_col,
    81 .wp_themeSkin span.mce_split_cells,
    82 .wp_themeSkin span.mce_merge_cells,
    83 .wp_themeSkin span.mce_delete_table,
    84 .wp_themeSkin span.mce_ins,
    85 .wp_themeSkin span.mce_abbr,
    86 .wp_themeSkin span.mce_acronym,
    87 .wp_themeSkin span.mce_del,
    88 .wp_themeSkin span.mce_replace,
    89 .wp_themeSkin span.mce_code,
    90 .wp_themeSkin span.mce_nonbreaking,
    91 .wp_themeSkin span.mce_inserttime,
    92 .wp_themeSkin span.mce_insertlayer,
    93 .wp_themeSkin span.mce_moveforward,
    94 .wp_themeSkin span.mce_movebackward,
    95 .wp_themeSkin span.mce_absolute {
    96         -moz-transition: none;
    97         -webkit-transition: none;
    98         transition: none;
    99         background: url("../js/tinymce/themes/advanced/img/icons.gif") no-repeat 20px 20px;
    100 }
    101 
    102 /* No @font-face support */
    103 .no-font-face .wp_themeSkin span.mce_undo,
    104 .no-font-face .wp_themeSkin span.mce_redo,
    105 .no-font-face .wp_themeSkin span.mce_bullist,
    106 .no-font-face .wp_themeSkin span.mce_numlist,
    107 .no-font-face .wp_themeSkin span.mce_blockquote,
    108 .no-font-face .wp_themeSkin span.mce_charmap,
    109 .no-font-face .wp_themeSkin span.mce_bold,
    110 .no-font-face .wp_themeSkin span.mce_italic,
    111 .no-font-face .wp_themeSkin span.mce_underline,
    112 .no-font-face .wp_themeSkin span.mce_justifyleft,
    113 .no-font-face .wp_themeSkin span.mce_justifyright,
    114 .no-font-face .wp_themeSkin span.mce_justifycenter,
    115 .no-font-face .wp_themeSkin span.mce_justifyfull,
    116 .no-font-face .wp_themeSkin span.mce_indent,
    117 .no-font-face .wp_themeSkin span.mce_outdent,
    118 .no-font-face .wp_themeSkin span.mce_link,
    119 .no-font-face .wp_themeSkin span.mce_unlink,
    120 .no-font-face .wp_themeSkin span.mce_help,
    121 .no-font-face .wp_themeSkin span.mce_removeformat,
    122 .no-font-face .wp_themeSkin span.mce_fullscreen,
    123 .no-font-face .wp_themeSkin span.mce_wp_fullscreen,
    124 .no-font-face .wp_themeSkin span.mce_media,
    125 .no-font-face .wp_themeSkin span.mce_pastetext,
    126 .no-font-face .wp_themeSkin span.mce_pasteword,
    127 .no-font-face .wp_themeSkin span.mce_wp_help,
    128 .no-font-face .wp_themeSkin span.mce_wp_adv,
    129 .no-font-face .wp_themeSkin span.mce_wp_more,
    130 .no-font-face .wp_themeSkin span.mce_strikethrough,
    131 .no-font-face .wp_themeSkin span.mce_spellchecker,
    132 .no-font-face .wp_themeSkin span.mce_forecolor,
    133 .no-font-face .wp_themeSkin .mce_forecolorpicker,
    134 .no-font-face .wp_themeSkin .mceSplitButton .mce_spellchecker span.mce_spellchecker,
    135 .no-font-face .wp_themeSkin .mceSplitButton .mce_forecolor span.mce_forecolor,
    136 .no-font-face .wp_themeSkin .mceSplitButton span.mce_numlist,
    137 .no-font-face .wp_themeSkin .mceSplitButton span.mce_bullist {
    138         -moz-transition: none;
    139         -webkit-transition: none;
    140         transition: none;
    141         background-image: url('../images/wpicons.png?ver=20120720');
    142 }
    143 
    144 /* Theme */
    145 .no-font-face .wp_themeSkin span.mce_undo {background-position:-500px -20px}
    146 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_undo,
    147 .no-font-face .wp_themeSkin .mceButtonActive span.mce_undo {background-position:-500px 0}
    148 
    149 .no-font-face .wp_themeSkin span.mce_redo {background-position:-480px -20px}
    150 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_redo,
    151 .no-font-face .wp_themeSkin .mceButtonActive span.mce_redo {background-position:-480px 0}
    152 
    153 .no-font-face .wp_themeSkin span.mce_bullist {background-position:-40px -20px}
    154 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_bullist,
    155 .no-font-face .wp_themeSkin .mceButtonActive span.mce_bullist,
    156 .no-font-face .wp_themeSkin .mceSplitButton:hover span.mce_bullist {background-position:-40px 0}
    157 
    158 .no-font-face .wp_themeSkin span.mce_numlist {background-position:-60px -20px}
    159 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_numlist,
    160 .no-font-face .wp_themeSkin .mceButtonActive span.mce_numlist,
    161 .no-font-face .wp_themeSkin .mceSplitButton:hover span.mce_numlist {background-position:-60px 0}
    162 
    163 .no-font-face .wp_themeSkin span.mce_blockquote {background-position:-80px -20px}
    164 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_blockquote,
    165 .no-font-face .wp_themeSkin .mceButtonActive span.mce_blockquote {background-position:-80px 0}
    166 
    167 .no-font-face .wp_themeSkin span.mce_charmap {background-position:-420px -20px}
    168 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_charmap,
    169 .no-font-face .wp_themeSkin .mceButtonActive span.mce_charmap {background-position:-420px 0}
    170 
    171 .no-font-face .wp_themeSkin span.mce_bold {background-position:0 -20px}
    172 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_bold,
    173 .no-font-face .wp_themeSkin .mceButtonActive span.mce_bold {background-position:0 0}
    174 
    175 .no-font-face .wp_themeSkin span.mce_italic {background-position:-20px -20px}
    176 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_italic,
    177 .no-font-face .wp_themeSkin .mceButtonActive span.mce_italic {background-position:-20px 0}
    178 
    179 .no-font-face .wp_themeSkin span.mce_underline {background-position:-280px -20px}
    180 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_underline,
    181 .no-font-face .wp_themeSkin .mceButtonActive span.mce_underline {background-position:-280px 0}
    182 
    183 .no-font-face .wp_themeSkin span.mce_justifyleft {background-position:-100px -20px}
    184 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_justifyleft,
    185 .no-font-face .wp_themeSkin .mceButtonActive span.mce_justifyleft {background-position:-100px 0}
    186 
    187 .no-font-face .wp_themeSkin span.mce_justifyright {background-position:-140px -20px}
    188 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_justifyright,
    189 .no-font-face .wp_themeSkin .mceButtonActive span.mce_justifyright {background-position:-140px 0}
    190 
    191 .no-font-face .wp_themeSkin span.mce_justifycenter {background-position:-120px -20px}
    192 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_justifycenter,
    193 .no-font-face .wp_themeSkin .mceButtonActive span.mce_justifycenter {background-position:-120px 0}
    194 
    195 .no-font-face .wp_themeSkin span.mce_justifyfull {background-position:-300px -20px}
    196 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_justifyfull,
    197 .no-font-face .wp_themeSkin .mceButtonActive span.mce_justifyfull {background-position:-300px 0}
    198 
    199 .no-font-face .wp_themeSkin span.mce_indent {background-position:-460px -20px}
    200 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_indent,
    201 .no-font-face .wp_themeSkin .mceButtonActive span.mce_indent {background-position:-460px 0}
    202 
    203 .no-font-face .wp_themeSkin span.mce_outdent {background-position:-440px -20px}
    204 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_outdent,
    205 .no-font-face .wp_themeSkin .mceButtonActive span.mce_outdent {background-position:-440px 0}
    206 
    207 .no-font-face .wp_themeSkin span.mce_link {background-position:-160px -20px}
    208 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_link,
    209 .no-font-face .wp_themeSkin .mceButtonActive span.mce_link {background-position:-160px 0}
    210 
    211 .no-font-face .wp_themeSkin span.mce_unlink {background-position:-180px -20px}
    212 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_unlink,
    213 .no-font-face .wp_themeSkin .mceButtonActive span.mce_unlink {background-position:-180px 0}
    214 
    215 .no-font-face .wp_themeSkin span.mce_help {background-position:-520px -20px}
    216 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_help,
    217 .no-font-face .wp_themeSkin .mceButtonActive span.mce_help {background-position:-520px 0}
    218 
    219 .no-font-face .wp_themeSkin span.mce_removeformat {background-position:-380px -20px}
    220 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_removeformat,
    221 .no-font-face .wp_themeSkin .mceButtonActive span.mce_removeformat {background-position:-380px 0}
    222 
    223 .no-font-face .wp_themeSkin span.mce_strikethrough {background-position:-540px -20px;}
    224 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_strikethrough,
    225 .no-font-face .wp_themeSkin .mceButtonActive span.mce_strikethrough {background-position:-540px 0}
    226 
    227 .no-font-face .wp_themeSkin .mceSplitButton .mce_forecolor span.mce_forecolor {background-position:-320px -20px}
    228 .no-font-face .wp_themeSkin .mceSplitButtonEnabled:hover span.mce_forecolor,
    229 .no-font-face .wp_themeSkin .mceSplitButtonSelected span.mce_forecolor {background-position:-320px 0}
    230 
    231 .no-font-face .wp_themeSkin .mce_forecolorpicker {background-position:-320px -20px}
    232 
    233 /* Plugins in WP */
    234 .no-font-face .wp_themeSkin span.mce_fullscreen {background-position:-240px -20px}
    235 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_fullscreen,
    236 .no-font-face .wp_themeSkin .mceButtonActive span.mce_fullscreen {background-position:-240px 0}
    237 
    238 .no-font-face .wp_themeSkin span.mce_wp_fullscreen {background-position:-240px -20px}
    239 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_wp_fullscreen,
    240 .no-font-face .wp_themeSkin .mceButtonActive span.mce_wp_fullscreen {background-position:-240px 0}
    241 
    242 .no-font-face .wp_themeSkin span.mce_media {background-position:-400px -20px}
    243 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_media,
    244 .no-font-face .wp_themeSkin .mceButtonActive span.mce_media {background-position:-400px 0}
    245 
    246 .no-font-face .wp_themeSkin span.mce_pastetext {background-position:-340px -20px}
    247 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_pastetext,
    248 .no-font-face .wp_themeSkin .mceButtonActive span.mce_pastetext {background-position:-340px 0}
    249 
    250 .no-font-face .wp_themeSkin span.mce_pasteword {background-position:-360px -20px}
    251 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_pasteword,
    252 .no-font-face .wp_themeSkin .mceButtonActive span.mce_pasteword {background-position:-360px 0}
    253 
    254 .no-font-face .wp_themeSkin span.mce_spellchecker {background-position:-220px -20px}
    255 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_spellchecker,
    256 .no-font-face .wp_themeSkin .mceSplitButtonEnabled:hover span.mce_spellchecker,
    257 .no-font-face .wp_themeSkin .mceButtonActive span.mce_spellchecker,
    258 .no-font-face .wp_themeSkin .mceSplitButtonSelected span.mce_spellchecker {background-position:-220px 0}
    259 
    260 .no-font-face .wp_themeSkin span.mce_wp_help {background-position:-520px -20px}
    261 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_wp_help,
    262 .no-font-face .wp_themeSkin .mceButtonActive span.mce_wp_help {background-position:-520px 0}
    263 
    264 .no-font-face .wp_themeSkin span.mce_wp_adv {background-position:-260px -20px}
    265 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_wp_adv,
    266 .no-font-face .wp_themeSkin .mceButtonActive span.mce_wp_adv {background-position:-260px 0}
    267 
    268 .no-font-face .wp_themeSkin span.mce_wp_more {background-position:-200px -20px}
    269 .no-font-face .wp_themeSkin .mceButtonEnabled:hover span.mce_wp_more,
    270 .no-font-face .wp_themeSkin .mceButtonActive span.mce_wp_more {background-position:-200px 0}
    271 
    272 .no-font-face .mceIcon:before {
    273         display: none !important;
    274 }
    275 /* End no @font-face */
    276 
    277 /* Containers */
    278 .wp_themeSkin table {}
    279 
    280 .wp_themeSkin iframe {
    281         display: block;
    282 }
    283 
    284 .wp_themeSkin #mce_fullscreen_ifr {
    285         background-color: #fff;
    286 }
    287 
    288 .wp_themeSkin .mceToolbar {
    289         padding: 1px;
    290 }
    291 
    292 /* External */
    293 .wp_themeSkin .mceExternalToolbar {
    294         position: absolute;
    295         border-bottom: 0;
    296         display: none;
    297 }
    298 
    299 .wp_themeSkin .mceExternalToolbar td.mceToolbar {
    300         padding-right: 13px;
    301 }
    302 
    303 .wp_themeSkin .mceExternalClose {
    304         position: absolute;
    305         top: 3px;
    306         right: 3px;
    307         width: 7px;
    308         height: 7px;
    309         background: url("../js/tinymce/themes/advanced/img/icons.gif") -820px 0;
    310 }
    311 
    312 /* Layout */
    313 .wp_themeSkin table.mceToolbar,
    314 .wp_themeSkin tr.mceFirst .mceToolbar tr td,
    315 .wp_themeSkin tr.mceLast .mceToolbar tr td {
    316         border: 0;
    317         margin: 0;
    318         padding: 0;
    319 }
    320 
    321 .wp_themeSkin table.mceLayout {
    322         border: 0;
    323 }
    324 
    325 .wp_themeSkin .mceStatusbar {
    3267        background: #fff;
     8}
     9
     10.mce-toolbar-grp {
     11        border-bottom: 1px solid #dedede;
     12        background: #f5f5f5;
     13}
     14
     15.mce-toolbar {
     16        padding: 1px 3px;
     17}
     18
     19.mce-statusbar {
    32720        border-top: 1px solid #eee;
    328         color: #000;
    329         display: block;
    330         font-family: sans-serif;
    331         font-size: 12px;
    332         height: 20px;
    333         line-height: 16px;
    334         padding: 0 0 0 8px;
    335         overflow: visible;
    336 }
    337 
    338 .wp_themeSkin .mceStatusbar * {
    339         color: #555;
    340 }
    341 
    342 .wp_themeSkin .mceStatusbar div {
    343         float: left;
    344         padding: 2px;
    345 }
    346 
    347 .wp_themeSkin .mceStatusbar a.mceResize {
    348         display: block;
    349         float: right;
    350         background: url("../js/tinymce/themes/advanced/img/icons.gif") -800px 0;
    351         width: 20px;
    352         height: 20px;
    353         cursor: se-resize;
    354 }
    355 
    356 .wp_themeSkin .mceStatusbar a:hover {
    357         text-decoration: underline;
    358 }
    359 
    360 .wp_themeSkin table.mceToolbar {
    361         margin: 0 6px 2px;
    362 }
    363 
    364 .wp_themeSkin table.mceToolbar :active,
    365 .wp_themeSkin table.mceToolbar :focus,
    366 .wp_themeSkin table.mceToolbar:focus,
    367 .wp_themeSkin span.mceSeparator:focus {
    368         outline: none;
    369 }
    370 
    371 .wp_themeSkin #content_toolbar1 {
    372         margin-top: 2px;
    373 }
    374 
    375 .wp_themeSkin .mceToolbar .mceToolbarEndListBox span {
    376         display: none;
    377 }
    378 
    379 .wp_themeSkin span.mceIcon,
    380 .wp_themeSkin img.mceIcon {
    381         display: block;
    382         width: 20px;
    383         height: 20px;
    384 }
    385 
    386 a .mceIcon, .mceAction {
    387         text-align: center;
    388         font: normal 20px/1 'dashicons' !important;
    389         speak: none;
    390         -webkit-font-smoothing: antialiased;
    391         -moz-osx-font-smoothing: grayscale;
    392 }
    393 
    394 .mceAction {
    395         line-height:16px;
    396 }
    397 
    398 /* Button */
    399 .wp_themeSkin .mceButton {
    400         display: block;
    401         width: 20px;
    402         height: 20px;
    403         cursor: default;
    404         padding: 1px 2px;
    405         margin: 1px;
     21}
     22
     23.mce-path {
     24        padding: 0 8px 1px;
     25}
     26
     27.mce-path-item {
     28        line-height: 1;
     29}
     30
     31.mce-toolbar .mce-btn {
     32        border-color: transparent;
     33        background: transparent;
     34        box-shadow: none;
     35}
     36
     37#wp-fullscreen-buttons .mce-btn,
     38.mce-toolbar .mce-btn-group .mce-btn {
     39        border: 1px solid transparent;
     40        margin: 0 1px;
    40641        -webkit-border-radius: 2px;
    40742        border-radius: 2px;
    40843}
    40944
    410 .wp_themeSkin a.mceButtonEnabled:hover {
    411         background-image: inherit 0 -10px;
    412 }
    413 
    414 .wp_themeSkin .mceOldBoxModel a.mceButton span, .wp_themeSkin .mceOldBoxModel a.mceButton img {
    415         margin: 0 0 0 1px;
    416 }
    417 
    418 .wp_themeSkin .mceButtonDisabled .mceIcon {
    419         opacity: 0.2;
    420         filter: alpha(opacity=20);
    421 }
    422 
    423 /* Separator */
    424 .wp_themeSkin .mceSeparator {
    425         display: none;
    426 }
    427 
    428 /* ListBox */
    429 .wp_themeSkin .mceListBox,
    430 .wp_themeSkin .mceListBox a {
    431         display: block;
    432 }
    433 
    434 .wp_themeSkin .mceListBox .mceText {
    435         padding: 1px 4px 1px 5px;
    436         width: 70px;
    437         text-align: left;
    438         text-decoration: none;
    439         -webkit-border-bottom-left-radius: 2px;
    440         -webkit-border-top-left-radius: 2px;
    441         border-bottom-left-radius: 2px;
    442         border-top-left-radius: 2px;
    443         font-family: sans-serif;
    444         font-size: 12px;
    445         height: 20px;
    446         line-height: 20px;
    447         overflow: hidden;
    448 }
    449 
    450 .wp_themeSkin .mceListBox {
    451         margin: 1px;
    452         direction: ltr;
    453         background-color: #fff;
    454         border: 1px solid #ddd;
    455         -webkit-box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2);
    456         box-shadow:         inset 0 1px 1px -1px rgba(0, 0, 0, .2);
    457 }
    458 
    459 .wp_themeSkin .mceListBox .mceOpen {
    460         width: 12px;
    461         height: 20px;
    462         border-collapse: separate;
    463         padding: 1px;
    464         -webkit-border-bottom-left-radius: 0;
    465         -webkit-border-top-left-radius: 0;
    466         border-bottom-left-radius: 0;
    467         border-top-left-radius: 0;
    468 }
    469 
    470 .wp_themeSkin .mceListBox .mceFirst a {
    471         border-style: solid;
    472         border-width: 1px;
    473         border-bottom-right-radius: 2px;
    474         border-top-right-radius: 2px;
    475 }
    476 
    477 .wp_themeSkin .mceListBoxMenu .mce_formatPreview {
    478         line-height: normal;
    479 }
    480 
    481 .wp_themeSkin .mceListBox .mceOpen,
    482 .wp_themeSkin .mceListBoxHover .mceOpen,
    483 .wp_themeSkin .mceListBoxSelected .mceOpen,
    484 .wp_themeSkin table.mceListBoxEnabled .mceOpen {
    485         background-image: url("../images/down_arrow.gif");
    486         background-position: 3px 1px;
    487         background-repeat: no-repeat;
    488 }
    489 
    490 .wp_themeSkin .mceListBoxDisabled .mceText {
    491         color: gray;
    492 }
    493 
    494 .wp_themeSkin .mceListBoxMenu {
    495         overflow: auto;
    496         overflow-x: hidden;
    497 }
    498 
    499 .wp_themeSkin .mceOldBoxModel .mceListBox .mceText {
    500         height: 22px;
    501 }
    502 
    503 .wp_themeSkin select.mceListBox {
    504         font-family: sans-serif;
    505         font-size: 12px;
    506         border-color: #b2b2b2;
    507         background-color: #fff;
    508 }
    509 
    510 /* SplitButton */
    511 .wp_themeSkin .mceSplitButton a,
    512 .wp_themeSkin .mceSplitButton span {
    513         display: block;
    514         height: 20px;
    515 }
    516 
    517 .wp_themeSkin .mceSplitButton {
    518         display: block;
    519         direction: ltr;
    520 }
    521 
    522 .wp_themeSkin table.mceSplitButton td {
    523         padding: 2px;
    524         -webkit-border-radius: 2px;
    525         border-radius: 2px;
    526 }
    527 
    528 .wp_themeSkin table.mceSplitButton:hover td {
    529         background-image: inherit 0 -10px;
    530 }
    531 
    532 .wp_themeSkin .mceSplitButton a.mceAction {
    533         height: 20px;
    534         width: 20px;
    535         padding: 1px 2px;
    536         border-right: 0 none;
    537 }
    538 
    539 .wp_themeSkin .mceSplitButton span.mceAction {
    540         background-image: url("../js/tinymce/themes/advanced/img/icons.gif");
    541         background-repeat: no-repeat;
    542         background-color: transparent;
    543         width: 20px;
    544 }
    545 
    546 .wp_themeSkin .mceSplitButton span.mceAction.mce_bullist,
    547 .wp_themeSkin .mceSplitButton span.mceAction.mce_numlist {
    548         background-image: none;
    549 }
    550 
    551 .wp_themeSkin .mceSplitButton a.mceOpen {
    552         width: 11px;
    553         height: 20px;
    554         background-position: 0px 2px;
    555         background-repeat: no-repeat;
    556         padding: 1px 0;
    557 }
    558 
    559 .wp_themeSkin .mceSplitButton span.mceOpen {
    560         display: none;
    561 }
    562 
    563 .wp_themeSkin .mceSplitButtonDisabled .mceAction {
    564         opacity: 0.3;
    565         filter: alpha(opacity=30);
    566 }
    567 
    568 .wp_themeSkin .mceListBox a.mceText,
    569 .wp_themeSkin .mceSplitButton a.mceAction {
    570         -webkit-border-bottom-left-radius: 2px;
    571         -webkit-border-top-left-radius: 2px;
    572         border-bottom-left-radius: 2px;
    573         border-top-left-radius: 2px;
    574 }
    575 
    576 .wp_themeSkin .mceSplitButton a.mceOpen,
    577 .wp_themeSkin .mceListBox a.mceOpen {
    578         -webkit-border-bottom-right-radius: 2px;
    579         -webkit-border-top-right-radius: 2px;
    580         border-bottom-right-radius: 2px;
    581         border-top-right-radius: 2px;
    582 }
    583 
    584 /* ColorSplitButton */
    585 .wp_themeSkin div.mceColorSplitMenu table {
    586         background-color: #ebebeb;
    587         border-color: #bbb;
    588 }
    589 
    590 .wp_themeSkin .mceColorSplitMenu td {
    591         padding: 2px;
    592 }
    593 
    594 .wp_themeSkin .mceColorSplitMenu a {
    595         display: block;
    596         width: 9px;
    597         height: 9px;
    598         overflow: hidden;
    599         border-color: #B2B2B2;
    600 }
    601 
    602 .wp_themeSkin .mceColorSplitMenu td.mceMoreColors {
    603         padding: 1px 3px 1px 1px;
    604 }
    605 
    606 .wp_themeSkin .mceColorSplitMenu a.mceMoreColors {
    607         width: 100%;
    608         height: auto;
    609         text-align: center;
    610         font-family: "Open Sans", sans-serif;
    611         font-size: 11px;
    612         line-height: 20px;
    613         border-color: #fff;
    614 }
    615 
    616 .wp_themeSkin .mceColorSplitMenu a.mceMoreColors:hover {}
    617 .wp_themeSkin a.mceMoreColors:hover {}
    618 .wp_themeSkin .mceColorPreview {
    619         margin: -5px 0 0 2px;
    620         width: 16px;
    621         height: 4px;
    622         overflow: hidden;
    623 }
    624 
    625 /* Menu */
    626 .wp_themeSkin .mceMenu {
    627         position: absolute;
    628         left: 0;
    629         top: 0;
    630         z-index: 1000;
    631         border-color: #ddd;
    632         direction: ltr;
    633 }
    634 
    635 .wp_themeSkin .mceNoIcons span.mceIcon {
    636         width: 0;
    637 }
    638 
    639 .wp_themeSkin .mceNoIcons a .mceText {
    640         padding-left: 10px;
    641 }
    642 
    643 .wp_themeSkin .mceMenu table {
    644         background-color: #ebeaeb;
    645 }
    646 
    647 .wp_themeSkin .mceMenu a,
    648 .wp_themeSkin .mceMenu span,
    649 .wp_themeSkin .mceMenu {
    650         display: block;
    651 }
    652 
    653 .wp_themeSkin .mceMenu td {
    654         height: 20px;overflow: hidden;
    655 }
    656 
    657 .wp_themeSkin .mceMenu a {
    658         position: relative;
    659         padding: 3px 0 4px 0;
    660         text-decoration: none !important;
    661 }
    662 
    663 .wp_themeSkin .mceMenu .mceText {
    664         position: relative;
    665         display: block;
    666         font-family: "Open Sans", sans-serif;
    667         cursor: default;
    668         margin: 0;
    669         padding: 0 25px;
    670         color: #000;
    671 }
    672 
    673 .wp_themeSkin .mceMenu span.mceText, .wp_themeSkin .mceMenu .mcePreview {
    674         font-size: 12px;
    675 }
    676 
    677 .wp_themeSkin .mceMenu pre.mceText {
    678         font-family: Monospace;
    679 }
    680 
    681 .wp_themeSkin .mceMenu .mceIcon {
    682         position: absolute;
    683         top: 0;
    684         left: 0;
    685         width: 22px;
    686 }
    687 
    688 .wp_themeSkin .mceMenu .mceMenuItemEnabled a:hover,
    689 .wp_themeSkin .mceMenu .mceMenuItemActive {
    690         background-color: #f5f5f5;
    691 }
    692 
    693 .wp_themeSkin td.mceMenuItemSeparator {
    694         height: 1px;
    695         background-color: #aaa;
    696 }
    697 
    698 .wp_themeSkin .mceMenuItemTitle a {
    699         border-top: 0;
    700         border-right: 0;
    701         border-left: 0;
    702         border-bottom: 1px solid #aaa;
    703         text-decoration: none !important;
    704         background-color: #ccc;
    705 }
    706 
    707 .wp_themeSkin .mceMenuItemTitle span.mceText {
    708         font-weight: bold;
    709         padding-left: 4px;
    710         color: #000;
    711 }
    712 
    713 .wp_themeSkin .mceMenuItemSelected .mceIcon {
    714         background: url("../js/tinymce/themes/advanced/skins/default/img/menu_check.gif");
    715         color: #888;
    716 }
    717 
    718 .wp_themeSkin .mceNoIcons .mceMenuItemSelected a {
    719         background: url("../js/tinymce/themes/advanced/skins/default/img/menu_arrow.gif") no-repeat -6px center;
    720 }
    721 
    722 .wp_themeSkin .mceMenu span.mceMenuLine {
    723         display: none;
    724 }
    725 
    726 .wp_themeSkin .mceMenuItemSub a {
    727         background: url("../js/tinymce/themes/advanced/skins/default/img/menu_arrow.gif") no-repeat top right;
    728 }
    729 
    730 /* Progress,Resize */
    731 .wp_themeSkin .mceBlocker {
    732         position: absolute;
    733         left: 0;
    734         top: 0;
    735         z-index: 1000;
    736         opacity: 0.5;
    737         filter: alpha(opacity=50);
    738         background: #FFF;
    739 }
    740 
    741 .wp_themeSkin .mceProgress {
    742         position: absolute;
    743         left: 0;
    744         top: 0;
    745         z-index: 1001;
    746         background: url("../js/tinymce/themes/advanced/skins/default/img/progress.gif") no-repeat;
    747         width: 32px;
    748         height: 32px;
    749         margin: -16px 0 0 -16px;
    750 }
    751 
    752 .wp_themeSkin .mcePlaceHolder {
    753         border: 1px dotted gray;
    754 }
    755 
    756 /* Rtl */
    757 .mceRtl .mceListBox .mceText {
    758         text-align: right;
    759         padding: 0 4px 0 0;
    760 }
    761 
    762 .mceRtl .mceMenuItem .mceText {
    763         text-align: right;
    764 }
    765 
    766 /* Formats */
    767 .wp_themeSkin .mce_p span.mceText {}
    768 .wp_themeSkin .mce_address span.mceText {
    769         font-style: italic;
    770 }
    771 
    772 .wp_themeSkin .mce_pre span.mceText {
    773         font-family: monospace;
    774 }
    775 
    776 .wp_themeSkin .mce_h1 span.mceText {
    777         font-weight: bolder;
    778         font-size: 18px;
    779 }
    780 
    781 .wp_themeSkin .mce_h2 span.mceText {
    782         font-weight: bolder;
    783         font-size: 14px;
    784 }
    785 
    786 .wp_themeSkin .mce_h3 span.mceText {
    787         font-weight: bolder;
    788         font-size: 12px;
    789 }
    790 
    791 .wp_themeSkin .mce_h4 span.mceText {
    792         font-weight: bolder;
    793         font-size: 11px;
    794 }
    795 
    796 .wp_themeSkin .mce_h5 span.mceText {
    797         font-weight: bolder;
    798         font-size: 11px;
    799 }
    800 
    801 .wp_themeSkin .mce_h6 span.mceText {
    802         font-weight: bolder;
    803         font-size: 10px;
    804 }
    805 
    806 span.mce_bold:before {
    807         content: '\f200';
    808 }
    809 
    810 span.mce_italic:before {
    811         content: '\f201';
    812 }
    813 
    814 span.mce_bullist:before {
    815         content: '\f203';
    816 }
    817 
    818 span.mce_numlist:before {
    819         content: '\f204';
    820 }
    821 
    822 span.mce_blockquote:before {
    823         content: '\f205';
    824 }
    825 
    826 span.mce_justifyleft:before {
    827         content: '\f206';
    828 }
    829 
    830 span.mce_justifycenter:before {
    831         content: '\f207';
    832 }
    833 
    834 span.mce_justifyright:before {
    835         content: '\f208';
    836 }
    837 
    838 span.mce_link:before {
    839         content: '\f103';
    840 }
    841 
    842 span.mce_unlink:before {
    843         content: '\f225';
    844 }
    845 
    846 span.mce_wp_more:before {
    847         content: '\f209';
    848 }
    849 
    850 span.mce_strikethrough:before {
    851         content: '\f224';
    852 }
    853 
    854 span.mce_spellchecker {
    855         font-size: 20px;
    856         background: none !important;
    857         margin-top: 2px;
    858 }
    859 
    860 span.mce_spellchecker:before {
    861         content: '\f210';
    862 }
    863 
    864 span.mce_fullscreen:before,
    865 span.mce_wp_fullscreen:before {
    866         content: '\f211';
    867 }
    868 
    869 span.mce_wp_adv:before {
    870         content: '\f212';
    871 }
    872 span.mce_underline:before {
    873         content: '\f213';
    874 }
    875 
    876 span.mce_justifyfull:before {
    877         content: '\f214';
    878 }
    879 
    880 span.mce_forecolor {
    881         background: none !important;
    882 }
    883 
    884 span.mce_forecolor:before {
    885         content: '\f215';
    886 }
    887 
    888 span.mce_pastetext:before {
    889         content: '\f217';
    890 }
    891 
    892 span.mce_pasteword:before {
    893         content: '\f216';
    894 }
    895 
    896 span.mce_removeformat:before {
    897         content: '\f218';
    898 }
    899 
    900 span.mce_charmap:before {
    901         content: '\f220';
    902 }
    903 
    904 span.mce_outdent:before {
    905         content: '\f221';
    906 }
    907 
    908 span.mce_indent:before {
    909         content: '\f222';
    910 }
    911 
    912 span.mce_undo:before {
    913         content: '\f171';
    914 }
    915 
    916 span.mce_redo:before {
    917         content: '\f172';
    918 }
    919 
    920 span.mce_help:before,
    921 span.mce_wp_help:before {
    922         content: '\f223';
    923 }
    924 
    925 span.mce_image:before {
    926         content: '\f104';
    927 }
    928 
    929 span.mce_ltr:before {
    930         content: '\f320';
    931 }
    932 
    933 /* Default icons */
    934 .wp_themeSkin span.mce_cleanup {background-position:-380px -20px}
    935 .wp_themeSkin span.mce_anchor {background-position:-200px 0}
    936 .wp_themeSkin span.mce_sub {background-position:-600px 0}
    937 .wp_themeSkin span.mce_sup {background-position:-620px 0}
    938 .wp_themeSkin span.mce_newdocument {background-position:-520px 0}
    939 .wp_themeSkin span.mce_image {background-position:-380px 0}
    940 .wp_themeSkin span.mce_code {background-position:-260px 0}
    941 .wp_themeSkin span.mce_hr {background-position:-360px 0}
    942 .wp_themeSkin span.mce_visualaid {background-position:-660px 0}
    943 .wp_themeSkin span.mce_paste {background-position:-560px 0}
    944 .wp_themeSkin span.mce_copy {background-position:-700px 0}
    945 .wp_themeSkin span.mce_cut {background-position:-680px 0}
    946 .wp_themeSkin .mce_backcolor span.mceAction {background-position:-760px 0}
    947 .wp_themeSkin .mce_backcolorpicker {background-position:-760px 0}
    948 
    949 /* Plugins */
    950 .wp_themeSkin span.mce_advhr {background-position:-0px -20px}
    951 .wp_themeSkin span.mce_ltr {background-position:-20px -20px}
    952 .wp_themeSkin span.mce_rtl {background-position:-40px -20px}
    953 .wp_themeSkin span.mce_emotions {background-position:-60px -20px}
    954 .wp_themeSkin span.mce_fullpage {background-position:-80px -20px}
    955 .wp_themeSkin span.mce_iespell {background-position:-120px -20px}
    956 .wp_themeSkin span.mce_insertdate {background-position:-140px -20px}
    957 .wp_themeSkin span.mce_inserttime {background-position:-160px -20px}
    958 .wp_themeSkin span.mce_absolute {background-position:-180px -20px}
    959 .wp_themeSkin span.mce_backward {background-position:-200px -20px}
    960 .wp_themeSkin span.mce_forward {background-position:-220px -20px}
    961 .wp_themeSkin span.mce_insert_layer {background-position:-240px -20px}
    962 .wp_themeSkin span.mce_insertlayer {background-position:-260px -20px}
    963 .wp_themeSkin span.mce_movebackward {background-position:-280px -20px}
    964 .wp_themeSkin span.mce_moveforward {background-position:-300px -20px}
    965 .wp_themeSkin span.mce_media {background-position:-320px -20px}
    966 .wp_themeSkin span.mce_nonbreaking {background-position:-340px -20px}
    967 .wp_themeSkin span.mce_selectall {background-position:-400px -20px}
    968 .wp_themeSkin span.mce_preview {background-position:-420px -20px}
    969 .wp_themeSkin span.mce_print {background-position:-440px -20px}
    970 .wp_themeSkin span.mce_cancel {background-position:-460px -20px}
    971 .wp_themeSkin span.mce_save {background-position:-480px -20px}
    972 .wp_themeSkin span.mce_replace {background-position:-500px -20px}
    973 .wp_themeSkin span.mce_search {background-position:-520px -20px}
    974 .wp_themeSkin span.mce_styleprops {background-position:-560px -20px}
    975 .wp_themeSkin span.mce_table {background-position:-580px -20px}
    976 .wp_themeSkin span.mce_cell_props {background-position:-600px -20px}
    977 .wp_themeSkin span.mce_delete_table {background-position:-620px -20px}
    978 .wp_themeSkin span.mce_delete_col {background-position:-640px -20px}
    979 .wp_themeSkin span.mce_delete_row {background-position:-660px -20px}
    980 .wp_themeSkin span.mce_col_after {background-position:-680px -20px}
    981 .wp_themeSkin span.mce_col_before {background-position:-700px -20px}
    982 .wp_themeSkin span.mce_row_after {background-position:-720px -20px}
    983 .wp_themeSkin span.mce_row_before {background-position:-740px -20px}
    984 .wp_themeSkin span.mce_merge_cells {background-position:-760px -20px}
    985 .wp_themeSkin span.mce_table_props {background-position:-980px -20px}
    986 .wp_themeSkin span.mce_row_props {background-position:-780px -20px}
    987 .wp_themeSkin span.mce_split_cells {background-position:-800px -20px}
    988 .wp_themeSkin span.mce_template {background-position:-820px -20px}
    989 .wp_themeSkin span.mce_visualchars {background-position:-840px -20px}
    990 .wp_themeSkin span.mce_abbr {background-position:-860px -20px}
    991 .wp_themeSkin span.mce_acronym {background-position:-880px -20px}
    992 .wp_themeSkin span.mce_attribs {background-position:-900px -20px}
    993 .wp_themeSkin span.mce_cite {background-position:-920px -20px}
    994 .wp_themeSkin span.mce_del {background-position:-940px -20px}
    995 .wp_themeSkin span.mce_ins {background-position:-960px -20px}
    996 .wp_themeSkin span.mce_pagebreak {background-position:0 -40px}
    997 .wp_themeSkin span.mce_restoredraft {background-position:-20px -40px}
    998 .wp_themeSkin span.mce_visualblocks {background-position: -40px -40px}
    999 
    1000 /* border */
    1001 .wp_themeSkin .mceExternalToolbar,
    1002 .wp_themeSkin .mceButton,
    1003 .wp_themeSkin a.mceButtonEnabled:hover,
    1004 .wp_themeSkin a.mceButtonActive,
    1005 .wp_themeSkin a.mceButtonSelected,
    1006 .wp_themeSkin .mceListBox .mceText,
    1007 .wp_themeSkin .mceListBox .mceOpen,
    1008 .wp_themeSkin select.mceListBox,
    1009 .wp_themeSkin .mceSplitButton a.mceAction,
    1010 .wp_themeSkin .mceSplitButton a.mceOpen,
    1011 .wp_themeSkin .mceSplitButton a.mceOpen:hover,
    1012 .wp_themeSkin .mceSplitButtonSelected a.mceOpen,
    1013 .wp_themeSkin table.mceSplitButtonEnabled:hover a.mceAction,
    1014 .wp_themeSkin .mceSplitButton a.mceAction:hover,
    1015 .wp_themeSkin div.mceColorSplitMenu table,
    1016 .wp_themeSkin .mceColorSplitMenu a,
    1017 .wp_themeSkin .mceColorSplitMenu a.mceMoreColors,
    1018 .wp_themeSkin .mceColorSplitMenu a.mceMoreColors:hover,
    1019 .wp_themeSkin a.mceMoreColors:hover,
    1020 .wp_themeSkin .mceMenu {
    1021         border-style: solid;
    1022         border-width: 1px;
    1023 }
    1024 
    1025 .wp_themeSkin .mceListBox .mceText {
    1026         border-right: 0 none;
    1027 }
    1028 
    1029 .wp_themeSkin iframe {
    1030         background: transparent;
    1031 }
    1032 
    1033 .wp_themeSkin .mceButton {
    1034         border-color: transparent;
    1035 }
    1036 
    1037 .wp_themeSkin .mceListBox .mceText,
    1038 .wp_themeSkin .mceListBox .mceOpen {
    1039         border-color: transparent;
    1040 }
    1041 
    1042 .wp_themeSkin a.mceButtonEnabled:hover,
    1043 .wp_themeSkin table.mceSplitButton:hover {
     45#wp-fullscreen-buttons .mce-btn:hover,
     46.mce-toolbar .mce-btn-group .mce-btn:hover,
     47.mce-toolbar .mce-btn-group .mce-btn.mce-active {
     48        box-shadow: 0 0 transparent;
    104449        border-color: #bbb;
    104550        background: #eee;
    104651        background-image: -webkit-gradient(linear, left bottom, left top, from(#e5e5e5), to(#fff));
    104752        background-image: -webkit-linear-gradient(bottom, #e5e5e5, #fff);
    1048         background-image:    -moz-linear-gradient(bottom, #e5e5e5, #fff);
    1049         background-image:      -o-linear-gradient(bottom, #e5e5e5, #fff);
    105053        background-image: linear-gradient(to top, #e5e5e5, #fff);
    105154}
    105255
    1053 .wp_themeSkin a.mceButton:active,
    1054 .wp_themeSkin a.mceButtonEnabled:active,
    1055 .wp_themeSkin a.mceButtonSelected:active,
    1056 .wp_themeSkin a.mceButtonActive,
    1057 .wp_themeSkin a.mceButtonActive:active,
    1058 .wp_themeSkin a.mceButtonActive:hover,
    1059 .wp_themeSkin .mceSplitButtonSelected table,
    1060 .wp_themeSkin .mceSplitButtonSelected table:hover {
    1061         outline: none;
    1062         border-color: #999 #ccc #ccc #999;
    1063         background: #eee;
    1064         background-image: -webkit-gradient(linear, left bottom, left top, from(#f6f6f6), to(#e3e3e3));
    1065         background-image: -webkit-linear-gradient(bottom, #f6f6f6, #e3e3e3);
    1066         background-image:    -moz-linear-gradient(bottom, #f6f6f6, #e3e3e3);
    1067         background-image:      -o-linear-gradient(bottom, #f6f6f6, #e3e3e3);
    1068         background-image: linear-gradient(to top, #f6f6f6, #e3e3e3);
    1069 }
    1070 
    1071 .wp_themeSkin .mceSplitButtonSelected table a.mceOpen,
    1072 .wp_themeSkin .mceSplitButtonSelected table a.mceAction {
    1073         border-color: #999 #ccc #ccc #999;
    1074 }
    1075 
    1076 .wp_themeSkin .mceButtonDisabled {
     56.mce-toolbar .mce-btn-group .mce-disabled.mce-btn:hover {
    107757        border-color: transparent;
    1078 }
    1079 
    1080 .wp_themeSkin .mceListBox .mceOpen {
    1081         border-left: 0;
    1082 }
    1083 
    1084 .wp_themeSkin .mceListBoxEnabled:hover,
    1085 .wp_themeSkin .mceListBoxEnabled:active,
    1086 .wp_themeSkin .mceListBoxHover,
    1087 .wp_themeSkin .mceListBoxHover:active,
    1088 .wp_themeSkin .mceListBoxSelected {
    1089         -webkit-box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .3);
    1090         box-shadow:         inset 0 1px 1px -1px rgba(0, 0, 0, .3);
     58        background: transparent;
     59}
     60
     61.mce-toolbar .mce-btn-group .mce-first,
     62.mce-toolbar .mce-btn-group .mce-last {
     63        border-color: transparent;
     64}
     65
     66.mce-toolbar .mce-btn button {
     67        padding: 2px 3px;
     68        line-height: normal;
     69}
     70
     71.mce-toolbar .mce-btn i {
     72        text-shadow: 0;
     73}
     74
     75.mce-toolbar .mce-btn-group .mce-btn.mce-listbox {
     76        border-radius: 0;
     77        direction: ltr;
     78        background: #fff;
     79        border: 1px solid #ddd;
     80        -webkit-box-shadow: inset 0 1px 1px -1px rgba(0, 0, 0, .2);
     81        box-shadow:         inset 0 1px 1px -1px rgba(0, 0, 0, .2);
     82}
     83
     84.mce-toolbar .mce-btn-group .mce-btn.mce-listbox:hover {
     85        background-image: none;
    109186        border-color: #bbb;
    109287}
    109388
    1094 /* SplitButton */
    1095 .wp_themeSkin .mceSplitButton .mceLast span.mceOpen .mceIconOnly {
    1096         display: block;
    1097 }
    1098 
    1099 .wp_themeSkin .mceSplitButton a.mceAction,
    1100 .wp_themeSkin .mceSplitButton a.mceOpen {
    1101         border-color: transparent;
    1102 }
    1103 
    1104 .wp_themeSkin .mceSplitButton:hover a {
    1105         border-color: #bbb;
    1106 }
    1107 
    1108 .wp_themeSkin .mceSplitButtonEnabled a.mceOpen,
    1109 .wp_themeSkin .mceSplitButtonSelected a.mceOpen,
    1110 .wp_themeSkin .mceSplitButtonActive a.mceOpen,
    1111 .wp_themeSkin .mceSplitButtonEnabled:hover a.mceOpen {
    1112         background-image: url("../images/down_arrow.gif");
    1113         background-position: 1px 2px;
    1114         background-repeat: no-repeat;
    1115         border-left: 0;
    1116 }
    1117 
    1118 .wp_themeSkin .mceSplitButtonActive td {
    1119         -webkit-border-radius: 3px;
    1120         border-radius: 3px;
    1121 }
    1122 
    1123 .wp_themeSkin .mceColorSplitMenu a.mceMoreColors:hover {
    1124         border-color: #0A246A;
    1125         background-color: #B6BDD2;
    1126 }
    1127 
    1128 .wp_themeSkin a.mceMoreColors:hover {
    1129         border-color: #0A246A;
    1130 }
    1131 
    1132 .wp_themeSkin .mceMenuItemDisabled .mceText {
    1133         color: #888;
    1134 }
    1135 
    1136 #mceModalBlocker {
    1137         background: #000;
    1138         opacity: 0.7;
    1139         -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
    1140         filter: alpha(opacity=70);
    1141 }
    1142 
    1143 /* WP specific */
     89.mce-toolbar .mce-colorbutton .mce-open {
     90        border-right: 0;
     91}
     92
     93.mce-toolbar .mce-colorbutton .mce-preview {
     94        margin-left: -16px;
     95}
     96
     97/* Icons */
     98.mce-toolbar .mce-ico {
     99        color: #777;
     100        line-height: 20px;
     101        width: 20px;
     102        height: 20px;
     103        text-align: center;
     104}
     105
     106.mce-toolbar .mce-btn:hover .mce-ico {
     107        color: #333;
     108}
     109
     110.mce-i-bold,
     111.mce-i-italic,
     112.mce-i-bullist,
     113.mce-i-numlist,
     114.mce-i-blockquote,
     115.mce-i-alignleft,
     116.mce-i-aligncenter,
     117.mce-i-alignright,
     118.mce-i-link,
     119.mce-i-unlink,
     120.mce-i-wp_more,
     121.mce-i-strikethrough,
     122.mce-i-spellchecker,
     123.mce-i-fullscreen,
     124.mce-i-wp_fullscreen,
     125.mce-i-wp_adv,
     126.mce-i-underline,
     127.mce-i-alignjustify,
     128.mce-i-forecolor,
     129.mce-i-pastetext,
     130.mce-i-pasteword,
     131.mce-i-removeformat,
     132.mce-i-charmap,
     133.mce-i-outdent,
     134.mce-i-indent,
     135.mce-i-undo,
     136.mce-i-redo,
     137.mce-i-help,
     138.mce-i-wp_help,
     139.mce-i-wp-media-library,
     140.mce-i-ltr {
     141        font: normal 20px/1 'dashicons';
     142        padding: 0;
     143        vertical-align: top;
     144        speak: none;
     145        -webkit-font-smoothing: antialiased;
     146        -moz-osx-font-smoothing: grayscale;
     147}
     148
     149.mce-i-bold:before {
     150        content: '\f200';
     151}
     152
     153.mce-i-italic:before {
     154        content: '\f201';
     155}
     156
     157.mce-i-bullist:before {
     158        content: '\f203';
     159}
     160
     161.mce-i-numlist:before {
     162        content: '\f204';
     163}
     164
     165.mce-i-blockquote:before {
     166        content: '\f205';
     167}
     168
     169.mce-i-alignleft:before {
     170        content: '\f206';
     171}
     172
     173.mce-i-aligncenter:before {
     174        content: '\f207';
     175}
     176
     177.mce-i-alignright:before {
     178        content: '\f208';
     179}
     180
     181.mce-i-link:before {
     182        content: '\f103';
     183}
     184
     185.mce-i-unlink:before {
     186        content: '\f225';
     187}
     188
     189.mce-i-wp_more:before {
     190        content: '\f209';
     191}
     192
     193.mce-i-strikethrough:before {
     194        content: '\f224';
     195}
     196
     197.mce-i-spellchecker:before {
     198        content: '\f210';
     199}
     200
     201.mce-i-fullscreen:before,
     202.mce-i-wp_fullscreen:before {
     203        content: '\f211';
     204}
     205
     206.mce-i-wp_adv:before {
     207        content: '\f212';
     208}
     209.mce-i-underline:before {
     210        content: '\f213';
     211}
     212
     213.mce-i-alignjustify:before {
     214        content: '\f214';
     215}
     216
     217.mce-i-forecolor:before {
     218        content: '\f215';
     219}
     220
     221.mce-i-pastetext:before {
     222        content: '\f217';
     223}
     224
     225.mce-i-removeformat:before {
     226        content: '\f218';
     227}
     228
     229.mce-i-charmap:before {
     230        content: '\f220';
     231}
     232
     233.mce-i-outdent:before {
     234        content: '\f221';
     235}
     236
     237.mce-i-indent:before {
     238        content: '\f222';
     239}
     240
     241.mce-i-undo:before {
     242        content: '\f171';
     243}
     244
     245.mce-i-redo:before {
     246        content: '\f172';
     247}
     248
     249.mce-i-help:before,
     250.mce-i-wp_help:before {
     251        content: '\f223';
     252}
     253
     254.mce-i-wp-media-library:before {
     255        content: '\f104';
     256}
     257
     258.mce-i-ltr:before {
     259        content: '\f320';
     260}
     261
     262/* Editors */
    1144263.wp-editor-wrap {
    1145264        position: relative;
    … …  
    1286405}
    1287406
     407/* Quicktags */
    1288408.quicktags-toolbar {
    1289409        border-bottom-style: solid;
    … …  
    18801000
    18811001/* TinyMCE modal */
     1002/* TODO: restyle the TinyMCE 4.0 modals
    18821003.clearlooks2 .mceTop {
    18831004        border-bottom: 1px solid #ccc;
    … …  
    19471068        padding-left: 12px;
    19481069}
    1949 
     1070*/
    19501071/* Distraction Free Writing mode
    19511072 * =Overlay Styles
    … …  
    19621083}
    19631084
    1964 .fullscreen-active .fullscreen-overlay,
    1965 .fullscreen-active #wp-fullscreen-body {
     1085.wp-fullscreen-active .fullscreen-overlay,
     1086.wp-fullscreen-active #wp-fullscreen-body {
    19661087        display: block;
    19671088}
    … …  
    19711092}
    19721093
    1973 .fullscreen-active .fullscreen-fader {
     1094.wp-fullscreen-active .fullscreen-fader {
    19741095        display: none;
    19751096}
    … …  
    19771098/* =Overlay Body
    19781099-------------------------------------------------------------- */
     1100
    19791101#wp-fullscreen-body {
    1980         width: 100%;
    19811102        z-index: 150005;
    19821103        display: none;
     1104}
     1105
     1106.wp-fullscreen-wrap {
     1107        margin: 0;
     1108        padding: 0;
    19831109        position: absolute;
    1984         top: 0;
    19851110        left: 0;
    1986         font-size: 12px;
    1987 }
    1988 
    1989 #wp-fullscreen-wrap {
    1990         margin: 0 auto 50px;
    1991         position: relative;
    1992         padding-top: 60px;
    1993 }
    1994 
    1995 #wp-fullscreen-title {
    1996         font-size: 1.7em;
    1997         line-height: 100%;
    1998         outline: medium none;
    1999         padding: 6px 7px;
    2000         width: 100%;
    2001         margin-bottom: 30px;
    2002         -webkit-box-shadow: none;
    2003         box-shadow: none;
    2004 }
    2005 
    2006 #wp-fullscreen-container {
    2007         padding: 4px 10px 50px;
    2008 }
    2009 
    2010 #wp-fullscreen-title,
    2011 #wp-fullscreen-container {
     1111        right: 0;
     1112        bottom: 30px;
     1113        top: 60px;
     1114        z-index: 150010;
     1115       
     1116}
     1117
     1118.wp-fullscreen-wrap .wp-editor-container,
     1119#wp-fullscreen-central-toolbar {
     1120        max-width: 100%;
     1121}
     1122
     1123.wp-fullscreen-active .wp-editor-tools,
     1124.wp-fullscreen-active .quicktags-toolbar,
     1125.wp-fullscreen-active .mce-toolbar-grp,
     1126.wp-fullscreen-active .mce-statusbar {
     1127        display: none;
     1128}
     1129
     1130#wp-fullscreen-status {
     1131        margin: auto;
     1132        -webkit-transition: opacity 0.4s;
     1133        transition: opacity 0.4s;
     1134}
     1135
     1136.wp-fullscreen-active .wp-fullscreen-title,
     1137.wp-fullscreen-active .wp-fullscreen-title:focus,
     1138.wp-fullscreen-active .wp-editor-container {
    20121139        -webkit-border-radius: 0;
    20131140        border-radius: 0;
    20141141        border: 1px dashed transparent;
    20151142        background: transparent;
    2016         -moz-transition-property: border-color;
    2017         -moz-transition-duration: 0.6s;
    2018         -webkit-transition-property: border-color;
    2019         -webkit-transition-duration: 0.6s;
    2020         -o-transition-property: border-color;
    2021         -o-transition-duration: 0.6s;
    2022         transition-property: border-color;
    2023         transition-duration: 0.6s;
    2024 }
    2025 
    2026 #wp_mce_fullscreen {
    2027         width: 100%;
    2028         min-height: 300px;
    2029         border: 0;
    2030         background: transparent;
    2031         font-family: Consolas, Monaco, monospace;
    2032         line-height: 1.6em;
    2033         padding: 0;
    2034         overflow-y: hidden;
    2035         outline: none;
    2036         resize: none;
    20371143        -webkit-box-shadow: none;
    20381144        box-shadow: none;
     1145        -webkit-transition: border-color 0.4s;
     1146        transition: border-color 0.4s;
     1147}
     1148
     1149.wp-fullscreen-active .wp-editor-container {
     1150        margin: auto;
     1151}
     1152
     1153.wp-fullscreen-active .wp-fullscreen-title {
     1154        font-size: 1.7em;
     1155        line-height: 100%;
     1156        outline: medium none;
     1157        padding: 3px 7px;
     1158        margin: 10px auto 30px;
     1159        display: block;
    20391160}
    20401161
    20411162#wp-fullscreen-tagline {
    2042         color: #BBBBBB;
     1163        color: #bbb;
    20431164        font-size: 18px;
    20441165        float: right;
    … …  
    20511172        background: #f5f5f5;
    20521173        border-bottom: 1px solid #fff;
    2053         height: 40px;
     1174        height: 45px;
     1175        position: fixed;
    20541176        left: 0;
    2055         min-width: 800px;
    2056         position: fixed;
     1177        right: 0;
    20571178        top: 0;
    20581179        width: 100%;
    20591180        z-index: 150050;
     1181        -webkit-transition: opacity 0.4s;
     1182        transition: opacity 0.4s;
    20601183}
    20611184
    … …  
    20641187        clear: both;
    20651188        max-width: 1100px;
    2066         min-width: 820px;
    20671189        margin: 0 auto;
    20681190}
    … …  
    20751197}
    20761198
     1199#wp-fullscreen-button-bar {
     1200        margin-top: 2px;
     1201}
     1202
    20771203#wp-fullscreen-save {
    20781204        float: right;
    … …  
    20821208#wp-fullscreen-count,
    20831209#wp-fullscreen-close {
    2084         padding-top: 5px;
     1210        padding-top: 6px;
    20851211}
    20861212
    … …  
    20951221
    20961222#wp-fullscreen-mode-bar {
    2097         padding: 1px 14px 0 0;
     1223        padding: 3px 14px 0 0;
    20981224}
    20991225
    … …  
    21471273}
    21481274
    2149 #wp-fullscreen-buttons .active a {
    2150         background: inherit;
    2151 }
    2152 
    21531275#wp-fullscreen-buttons .hidden {
    21541276        display: none;
    … …  
    21591281}
    21601282
     1283#wp-fullscreen-buttons .mce-btn button {
     1284    margin: 0;
     1285    outline: 0 none;
     1286    border: 0 none;
     1287    white-space: nowrap;
     1288    width: auto;
     1289    background: none;
     1290        color: #333333;
     1291    cursor: pointer;
     1292    font-size: 18px;
     1293    line-height: 20px;
     1294    overflow: visible;
     1295    text-align: center;
     1296    -moz-box-sizing: border-box;
     1297    box-sizing: border-box;
     1298}
     1299
    21611300.wp-html-mode #wp-fullscreen-buttons div {
    21621301        display: none;
    … …  
    21651304.wp-html-mode #wp-fullscreen-buttons div.wp-fullscreen-both {
    21661305        display: block;
    2167 }
    2168 
    2169 #fullscreen-topbar.fullscreen-make-sticky {
    2170         display: block !important;
    21711306}
    21721307
    … …  
    21781313#wp-fullscreen-save span {
    21791314        padding-right: 4px;
     1315        line-height: 26px;
    21801316        display: none;
    21811317}
    … …  
    21831319/* =Thickbox Adjustments
    21841320-------------------------------------------------------------- */
    2185 .fullscreen-active #TB_overlay {
     1321.wp-fullscreen-active #TB_overlay {
    21861322        z-index: 150100;
    21871323}
    21881324
    2189 .fullscreen-active #TB_window {
     1325.wp-fullscreen-active #TB_window {
    21901326        z-index: 150102;
    21911327}
    … …  
    21931329/* =TinyMCE Adjustments
    21941330-------------------------------------------------------------- */
    2195 #wp_mce_fullscreen_ifr {
    2196         background: transparent;
    2197 }
    2198 
    2199 #wp_mce_fullscreen_parent #wp_mce_fullscreen_tbl tr.mceFirst {
    2200         display : none;
    2201 }
    2202 
    2203 #wp-fullscreen-container .wp_themeSkin table td {
    2204         vertical-align: top;
     1331.wp-fullscreen-active #mce-modal-block {
     1332        z-index: 150100 !important;
     1333}
     1334
     1335.wp-fullscreen-active .mce-window {
     1336        z-index: 150102 !important;
    22051337}
    22061338
    … …  
    22101342}
    22111343
    2212 .wp-fullscreen-focus #wp-fullscreen-title,
    2213 .wp-fullscreen-focus #wp-fullscreen-container {
    2214         border-color: #ccc;
    2215 }
    2216 
    22171344/* =CSS 3 transitions
    22181345-------------------------------------------------------------- */
     1346
     1347.wp-fullscreen-active #wp-fullscreen-status,
     1348.wp-fullscreen-active #fullscreen-topbar {
     1349        -webkit-transition-duration: 0.8s;
     1350        transition-duration: 0.8s;
     1351        opacity: 0;
     1352        filter: alpha(opacity=0);
     1353}
     1354
     1355.wp-fullscreen-active.wp-dfw-show-ui #wp-fullscreen-status,
     1356.wp-fullscreen-active.wp-dfw-show-ui #fullscreen-topbar {
     1357        -webkit-transition-duration: 0.4s;
     1358        transition-duration: 0.4s;
     1359        opacity: 1;
     1360        filter: alpha(opacity=100);
     1361}
     1362
     1363.wp-fullscreen-active .wp-fullscreen-title,
     1364.wp-fullscreen-active .wp-editor-container {
     1365        -webkit-transition-duration: 0.8s;
     1366        transition-duration: 0.8s;
     1367        border-color: transparent;
     1368}
     1369
     1370.wp-fullscreen-active.wp-dfw-show-ui .wp-fullscreen-title,
     1371.wp-fullscreen-active.wp-dfw-show-ui .wp-editor-container {
     1372        -webkit-transition-duration: 0.4s;
     1373        transition-duration: 0.4s;
     1374        border-color: #ccc;
     1375}
    22191376
    22201377.fade-1000,
    … …  
    22231380.fade-300 {
    22241381        opacity: 0;
    2225         -moz-transition-property: opacity;
    22261382        -webkit-transition-property: opacity;
    2227         -o-transition-property: opacity;
    22281383        transition-property: opacity;
    22291384}
    22301385
    22311386.fade-1000 {
    2232         -moz-transition-duration: 1s;
    22331387        -webkit-transition-duration: 1s;
    2234         -o-transition-duration: 1s;
    22351388        transition-duration: 1s;
    22361389}
    22371390
    22381391.fade-600 {
    2239         -moz-transition-duration: 0.6s;
    22401392        -webkit-transition-duration: 0.6s;
    2241         -o-transition-duration: 0.6s;
    22421393        transition-duration: 0.6s;
    22431394}
    22441395
    22451396.fade-400 {
    2246         -moz-transition-duration: 0.4s;
    22471397        -webkit-transition-duration: 0.4s;
    2248         -o-transition-duration: 0.4s;
    22491398        transition-duration: 0.4s;
    22501399}
    22511400
    22521401.fade-300 {
    2253         -moz-transition-duration: 0.3s;
    22541402        -webkit-transition-duration: 0.3s;
    2255         -o-transition-duration: 0.3s;
    22561403        transition-duration: 0.3s;
    22571404}
    … …  
    22631410/* =Localization
    22641411-------------------------------------------------------------- */
    2265 .rtl .wp_themeSkin .mceColorSplitMenu a.mceMoreColors,
    2266 .rtl .wp_themeSkin .mceMenu .mceText,
    22671412.rtl .wp-switch-editor,
    2268 .rtl .quicktags-toolbar input,
    2269 .rtl .clearlooks2 .mceTop span,
    2270 .rtl .wp_themeSkin .mceColorSplitMenu a.mceMoreColors {
     1413.rtl .quicktags-toolbar input {
    22711414        font-family: Tahoma, sans-serif;
    22721415}
    22731416
    2274 html:lang(he-il) .rtl .wp_themeSkin .mceColorSplitMenu a.mceMoreColors,
    2275 html:lang(he-il) .rtl .wp_themeSkin .mceMenu .mceText,
    22761417html:lang(he-il) .rtl .wp-switch-editor,
    2277 html:lang(he-il) .rtl .quicktags-toolbar input,
    2278 html:lang(he-il) .rtl .clearlooks2 .mceTop span,
    2279 html:lang(he-il) .rtl .wp_themeSkin .mceColorSplitMenu a.mceMoreColors  {
     1418html:lang(he-il) .rtl .quicktags-toolbar input  {
    22801419        font-family: Arial, sans-serif;
    22811420}
    … …  
    22921431        }
    22931432
    2294         .wp_themeSkin .mceListBox .mceOpen,
    2295         .wp_themeSkin .mceListBoxHover .mceOpen,
    2296         .wp_themeSkin .mceListBoxSelected .mceOpen,
    2297         .wp_themeSkin table.mceListBoxEnabled .mceOpen {
    2298                 background-image: url('../images/down_arrow-2x.gif');
    2299                 background-size: 10px 20px;
    2300         }
    2301 
    2302         .wp_themeSkin .mceSplitButtonEnabled a.mceOpen,
    2303         .wp_themeSkin .mceSplitButtonSelected a.mceOpen,
    2304         .wp_themeSkin .mceSplitButtonActive a.mceOpen,
    2305         .wp_themeSkin .mceSplitButtonEnabled:hover a.mceOpen {
    2306                 background-image: url('../images/down_arrow-2x.gif');
    2307                 background-size: 10px 20px;
    2308         }
    2309 
    23101433        #wp-link .toggle-arrow {
    23111434                background: transparent url('../images/toggle-arrow-2x.png') top left no-repeat;
    … …  
    23131436        }
    23141437}
     1438
     1439/* TODO: DFW responsive */
     1440
     1441
  • trunk/src/wp-includes/js/autosave.js

    r26202 r26876  
    4444
    4545        window.onbeforeunload = function(){
    46                 var editor = typeof(tinymce) != 'undefined' ? tinymce.activeEditor : false, compareString;
     46                var editor = typeof(tinymce) != 'undefined' ? tinymce.activeEditor : false;
    4747
    4848                if ( editor && ! editor.isHidden() ) {
    … …  
    5050                                return autosaveL10n.saveAlert;
    5151                } else {
    52                         if ( fullscreen && fullscreen.settings.visible ) {
    53                                 compareString = wp.autosave.getCompareString({
    54                                         post_title: $('#wp-fullscreen-title').val() || '',
    55                                         content: $('#wp_mce_fullscreen').val() || '',
    56                                         excerpt: $('#excerpt').val() || ''
    57                                 });
    58                         } else {
    59                                 compareString = wp.autosave.getCompareString();
    60                         }
    61 
    62                         if ( compareString != autosaveLast )
     52                        if ( wp.autosave.getCompareString() != autosaveLast )
    6353                                return autosaveL10n.saveAlert;
    6454                }
    … …  
    116106
    117107        // This code is meant to allow tabbing from Title to Post content.
    118         $('#title').on('keydown.editor-focus', function(e) {
    119                 var ed;
    120 
    121                 if ( e.which != 9 )
    122                         return;
    123 
    124                 if ( !e.ctrlKey && !e.altKey && !e.shiftKey ) {
    125                         if ( typeof(tinymce) != 'undefined' )
    126                                 ed = tinymce.get('content');
    127 
    128                         if ( ed && !ed.isHidden() ) {
    129                                 $(this).one('keyup', function(){
    130                                         $('#content_tbl td.mceToolbar > a').focus();
     108        $('#title').on( 'keydown.editor-focus', function( event ) {
     109                var editor;
     110
     111                if ( event.which === 9 && ! event.ctrlKey && ! event.altKey && ! event.shiftKey ) {
     112                        if ( typeof tinymce !== 'undefined' ) {
     113                                editor = tinymce.get('content');
     114                        }
     115
     116                        if ( editor && ! editor.isHidden() ) {
     117                                $(this).one( 'keyup', function() {
     118                                        editor.focus();
    131119                                });
    132120                        } else {
    … …  
    134122                        }
    135123
    136                         e.preventDefault();
     124                        event.preventDefault();
    137125                }
    138126        });
    … …  
    346334                        return data;
    347335                } else {
    348                         if ( 'mce_fullscreen' == ed.id )
    349                                 tinymce.get('content').setContent(ed.getContent({format : 'raw'}), {format : 'raw'});
    350 
    351336                        tinymce.triggerSave();
    352337                }
    353338        }
    354339
    355         if ( typeof fullscreen != 'undefined' && fullscreen.settings.visible ) {
    356                 data.post_title = $('#wp-fullscreen-title').val() || '';
    357                 data.content = $('#wp_mce_fullscreen').val() || '';
    358         } else {
    359                 data.post_title = $('#title').val() || '';
    360                 data.content = $('#content').val() || '';
    361         }
     340        data.post_title = $('#title').val() || '';
     341        data.content = $('#content').val() || '';
    362342
    363343        /*
  • trunk/src/wp-includes/js/media-editor.js

    r26200 r26876  
    467467                                }
    468468                        } else if ( mce ) {
    469                                 if ( tinymce.activeEditor && (tinymce.activeEditor.id == 'mce_fullscreen' || tinymce.activeEditor.id == 'wp_mce_fullscreen') )
    470                                         ed = tinymce.activeEditor;
    471                                 else
    472                                         ed = tinymce.get(wpActiveEditor);
    473                         }
    474 
    475                         if ( ed && !ed.isHidden() ) {
     469                                ed = tinymce.get( wpActiveEditor );
     470                        }
     471
     472                        if ( ed && ! ed.isHidden() ) {
    476473                                // restore caret position on IE
    477                                 if ( tinymce.isIE && ed.windowManager.insertimagebookmark )
    478                                         ed.selection.moveToBookmark(ed.windowManager.insertimagebookmark);
     474        //                      if ( tinymce.isIE && ed.windowManager.insertimagebookmark )
     475        //                              ed.selection.moveToBookmark(ed.windowManager.insertimagebookmark);
    479476
    480477                                if ( h.indexOf('[caption') !== -1 ) {
    … …  
    665662
    666663                        id = this.id( id );
    667 
     664/*
    668665                        // Save a bookmark of the caret position in IE.
    669666                        if ( typeof tinymce !== 'undefined' ) {
    … …  
    675672                                }
    676673                        }
    677 
     674*/
    678675                        workflow = this.get( id );
    679676
  • trunk/src/wp-includes/js/quicktags.js

    r26553 r26876  
    186186                tb.id = toolbar_id;
    187187                tb.className = 'quicktags-toolbar';
     188                tb.onclick = function() {
     189                        window.wpActiveEditor = id;
     190                };
    188191
    189192                canvas.parentNode.insertBefore(tb, canvas);
    … …  
    564567                var URL, t = this;
    565568
    566                 if ( typeof(wpLink) !== 'undefined' ) {
    567                         wpLink.open();
     569                if ( typeof wpLink !== 'undefined' ) {
     570                        wpLink.open( ed.id );
    568571                        return;
    569572                }
    … …  
    606609        qt.FullscreenButton.prototype = new qt.Button();
    607610        qt.FullscreenButton.prototype.callback = function(e, c) {
    608                 if ( !c.id || typeof(fullscreen) === 'undefined' ) {
     611                if ( ! c.id || typeof wp === 'undefined' || ! wp.editor || ! wp.editor.fullscreen ) {
    609612                        return;
    610613                }
    611614
    612                 fullscreen.on();
     615                wp.editor.fullscreen.on();
    613616        };
    614617
  • trunk/src/wp-includes/js/tinymce/langs/wp-langs-en.js

    r22996 r26876  
    1 tinyMCE.addI18n({en:{
    2 common:{
    3 edit_confirm:"Do you want to use the WYSIWYG mode for this textarea?",
    4 apply:"Apply",
    5 insert:"Insert",
    6 update:"Update",
    7 cancel:"Cancel",
    8 close:"Close",
    9 browse:"Browse",
    10 class_name:"Class",
    11 not_set:"-- Not set --",
    12 clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.",
    13 clipboard_no_support:"Currently not supported by your browser, use keyboard shortcuts instead.",
    14 popup_blocked:"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.",
    15 invalid_data:"ERROR: Invalid values entered, these are marked in red.",
    16 invalid_data_number:"{#field} must be a number",
    17 invalid_data_min:"{#field} must be a number greater than {#min}",
    18 invalid_data_size:"{#field} must be a number or percentage",
    19 more_colors:"More colors"
    20 },
    21 colors:{
    22 "000000":"Black",
    23 "993300":"Burnt orange",
    24 "333300":"Dark olive",
    25 "003300":"Dark green",
    26 "003366":"Dark azure",
    27 "000080":"Navy Blue",
    28 "333399":"Indigo",
    29 "333333":"Very dark gray",
    30 "800000":"Maroon",
    31 "FF6600":"Orange",
    32 "808000":"Olive",
    33 "008000":"Green",
    34 "008080":"Teal",
    35 "0000FF":"Blue",
    36 "666699":"Grayish blue",
    37 "808080":"Gray",
    38 "FF0000":"Red",
    39 "FF9900":"Amber",
    40 "99CC00":"Yellow green",
    41 "339966":"Sea green",
    42 "33CCCC":"Turquoise",
    43 "3366FF":"Royal blue",
    44 "800080":"Purple",
    45 "999999":"Medium gray",
    46 "FF00FF":"Magenta",
    47 "FFCC00":"Gold",
    48 "FFFF00":"Yellow",
    49 "00FF00":"Lime",
    50 "00FFFF":"Aqua",
    51 "00CCFF":"Sky blue",
    52 "993366":"Brown",
    53 "C0C0C0":"Silver",
    54 "FF99CC":"Pink",
    55 "FFCC99":"Peach",
    56 "FFFF99":"Light yellow",
    57 "CCFFCC":"Pale green",
    58 "CCFFFF":"Pale cyan",
    59 "99CCFF":"Light sky blue",
    60 "CC99FF":"Plum",
    61 "FFFFFF":"White"
    62 },
    63 contextmenu:{
    64 align:"Alignment",
    65 left:"Left",
    66 center:"Center",
    67 right:"Right",
    68 full:"Full"
    69 },
    70 insertdatetime:{
    71 date_fmt:"%Y-%m-%d",
    72 time_fmt:"%H:%M:%S",
    73 insertdate_desc:"Insert date",
    74 inserttime_desc:"Insert time",
    75 months_long:"January,February,March,April,May,June,July,August,September,October,November,December",
    76 months_short:"Jan_January_abbreviation,Feb_February_abbreviation,Mar_March_abbreviation,Apr_April_abbreviation,May_May_abbreviation,Jun_June_abbreviation,Jul_July_abbreviation,Aug_August_abbreviation,Sep_September_abbreviation,Oct_October_abbreviation,Nov_November_abbreviation,Dec_December_abbreviation",
    77 day_long:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
    78 day_short:"Sun,Mon,Tue,Wed,Thu,Fri,Sat"
    79 },
    80 print:{
    81 print_desc:"Print"
    82 },
    83 preview:{
    84 preview_desc:"Preview"
    85 },
    86 directionality:{
    87 ltr_desc:"Direction left to right",
    88 rtl_desc:"Direction right to left"
    89 },
    90 layer:{
    91 insertlayer_desc:"Insert new layer",
    92 forward_desc:"Move forward",
    93 backward_desc:"Move backward",
    94 absolute_desc:"Toggle absolute positioning",
    95 content:"New layer..."
    96 },
    97 save:{
    98 save_desc:"Save",
    99 cancel_desc:"Cancel all changes"
    100 },
    101 nonbreaking:{
    102 nonbreaking_desc:"Insert non-breaking space character"
    103 },
    104 iespell:{
    105 iespell_desc:"Run spell checking",
    106 download:"ieSpell not detected. Do you want to install it now?"
    107 },
    108 advhr:{
    109 advhr_desc:"Horizontal rule"
    110 },
    111 emotions:{
    112 emotions_desc:"Emotions"
    113 },
    114 searchreplace:{
    115 search_desc:"Find",
    116 replace_desc:"Find/Replace"
    117 },
    118 advimage:{
    119 image_desc:"Insert/edit image"
    120 },
    121 advlink:{
    122 link_desc:"Insert/edit link"
    123 },
    124 xhtmlxtras:{
    125 cite_desc:"Citation",
    126 abbr_desc:"Abbreviation",
    127 acronym_desc:"Acronym",
    128 del_desc:"Deletion",
    129 ins_desc:"Insertion",
    130 attribs_desc:"Insert/Edit Attributes"
    131 },
    132 style:{
    133 desc:"Edit CSS Style"
    134 },
    135 paste:{
    136 paste_text_desc:"Paste as Plain Text",
    137 paste_word_desc:"Paste from Word",
    138 selectall_desc:"Select All",
    139 plaintext_mode_sticky:"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.",
    140 plaintext_mode:"Paste is now in plain text mode. Click again to toggle back to regular paste mode."
    141 },
    142 paste_dlg:{
    143 text_title:"Use CTRL + V on your keyboard to paste the text into the window.",
    144 text_linebreaks:"Keep linebreaks",
    145 word_title:"Use CTRL + V on your keyboard to paste the text into the window."
    146 },
    147 table:{
    148 desc:"Inserts a new table",
    149 row_before_desc:"Insert row before",
    150 row_after_desc:"Insert row after",
    151 delete_row_desc:"Delete row",
    152 col_before_desc:"Insert column before",
    153 col_after_desc:"Insert column after",
    154 delete_col_desc:"Remove column",
    155 split_cells_desc:"Split merged table cells",
    156 merge_cells_desc:"Merge table cells",
    157 row_desc:"Table row properties",
    158 cell_desc:"Table cell properties",
    159 props_desc:"Table properties",
    160 paste_row_before_desc:"Paste table row before",
    161 paste_row_after_desc:"Paste table row after",
    162 cut_row_desc:"Cut table row",
    163 copy_row_desc:"Copy table row",
    164 del:"Delete table",
    165 row:"Row",
    166 col:"Column",
    167 cell:"Cell"
    168 },
    169 autosave:{
    170 unload_msg:"The changes you made will be lost if you navigate away from this page."
    171 },
    172 fullscreen:{
    173 desc:"Toggle fullscreen mode (Alt + Shift + G)"
    174 },
    175 media:{
    176 desc:"Insert / edit embedded media",
    177 edit:"Edit embedded media"
    178 },
    179 fullpage:{
    180 desc:"Document properties"
    181 },
    182 template:{
    183 desc:"Insert predefined template content"
    184 },
    185 visualchars:{
    186 desc:"Visual control characters on/off."
    187 },
    188 spellchecker:{
    189 desc:"Toggle spellchecker (Alt + Shift + N)",
    190 menu:"Spellchecker settings",
    191 ignore_word:"Ignore word",
    192 ignore_words:"Ignore all",
    193 langs:"Languages",
    194 wait:"Please wait...",
    195 sug:"Suggestions",
    196 no_sug:"No suggestions",
    197 no_mpell:"No misspellings found.",
    198 learn_word:"Learn word"
    199 },
    200 pagebreak:{
    201 desc:"Insert Page Break"
    202 },
    203 advlist:{
    204 types:"Types",
    205 def:"Default",
    206 lower_alpha:"Lower alpha",
    207 lower_greek:"Lower greek",
    208 lower_roman:"Lower roman",
    209 upper_alpha:"Upper alpha",
    210 upper_roman:"Upper roman",
    211 circle:"Circle",
    212 disc:"Disc",
    213 square:"Square"
    214 },
    215 aria:{
    216 rich_text_area:"Rich Text Area"
    217 },
    218 wordcount:{
    219 words:"Words: "
    220 }
    221 }});
    222 
    223 tinyMCE.addI18n("en.advanced",{
    224 style_select:"Styles",
    225 font_size:"Font size",
    226 fontdefault:"Font family",
    227 block:"Format",
    228 paragraph:"Paragraph",
    229 div:"Div",
    230 address:"Address",
    231 pre:"Preformatted",
    232 h1:"Heading 1",
    233 h2:"Heading 2",
    234 h3:"Heading 3",
    235 h4:"Heading 4",
    236 h5:"Heading 5",
    237 h6:"Heading 6",
    238 blockquote:"Blockquote",
    239 code:"Code",
    240 samp:"Code sample",
    241 dt:"Definition term ",
    242 dd:"Definition description",
    243 bold_desc:"Bold (Ctrl + B)",
    244 italic_desc:"Italic (Ctrl + I)",
    245 underline_desc:"Underline",
    246 striketrough_desc:"Strikethrough (Alt + Shift + D)",
    247 justifyleft_desc:"Align Left (Alt + Shift + L)",
    248 justifycenter_desc:"Align Center (Alt + Shift + C)",
    249 justifyright_desc:"Align Right (Alt + Shift + R)",
    250 justifyfull_desc:"Align Full (Alt + Shift + J)",
    251 bullist_desc:"Unordered list (Alt + Shift + U)",
    252 numlist_desc:"Ordered list (Alt + Shift + O)",
    253 outdent_desc:"Outdent",
    254 indent_desc:"Indent",
    255 undo_desc:"Undo (Ctrl + Z)",
    256 redo_desc:"Redo (Ctrl + Y)",
    257 link_desc:"Insert/edit link (Alt + Shift + A)",
    258 unlink_desc:"Unlink (Alt + Shift + S)",
    259 image_desc:"Insert/edit image (Alt + Shift + M)",
    260 cleanup_desc:"Cleanup messy code",
    261 code_desc:"Edit HTML Source",
    262 sub_desc:"Subscript",
    263 sup_desc:"Superscript",
    264 hr_desc:"Insert horizontal ruler",
    265 removeformat_desc:"Remove formatting",
    266 forecolor_desc:"Select text color",
    267 backcolor_desc:"Select background color",
    268 charmap_desc:"Insert custom character",
    269 visualaid_desc:"Toggle guidelines/invisible elements",
    270 anchor_desc:"Insert/edit anchor",
    271 cut_desc:"Cut",
    272 copy_desc:"Copy",
    273 paste_desc:"Paste",
    274 image_props_desc:"Image properties",
    275 newdocument_desc:"New document",
    276 help_desc:"Help",
    277 blockquote_desc:"Blockquote (Alt + Shift + Q)",
    278 clipboard_msg:"Copy/Cut/Paste is not available in Mozilla and Firefox.",
    279 path:"Path",
    280 newdocument:"Are you sure you want to clear all contents?",
    281 toolbar_focus:"Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X",
    282 more_colors:"More colors",
    283 shortcuts_desc:"Accessibility Help",
    284 help_shortcut:" Press ALT F10 for toolbar. Press ALT 0 for help.",
    285 rich_text_area:"Rich Text Area",
    286 toolbar:"Toolbar"
    287 });
    288 
    289 tinyMCE.addI18n("en.advanced_dlg",{
    290 about_title:"About TinyMCE",
    291 about_general:"About",
    292 about_help:"Help",
    293 about_license:"License",
    294 about_plugins:"Plugins",
    295 about_plugin:"Plugin",
    296 about_author:"Author",
    297 about_version:"Version",
    298 about_loaded:"Loaded plugins",
    299 anchor_title:"Insert/edit anchor",
    300 anchor_name:"Anchor name",
    301 code_title:"HTML Source Editor",
    302 code_wordwrap:"Word wrap",
    303 colorpicker_title:"Select a color",
    304 colorpicker_picker_tab:"Picker",
    305 colorpicker_picker_title:"Color picker",
    306 colorpicker_palette_tab:"Palette",
    307 colorpicker_palette_title:"Palette colors",
    308 colorpicker_named_tab:"Named",
    309 colorpicker_named_title:"Named colors",
    310 colorpicker_color:"Color:",
    311 colorpicker_name:"Name:",
    312 charmap_title:"Select custom character",
    313 charmap_usage:"Use left and right arrows to navigate.",
    314 image_title:"Insert/edit image",
    315 image_src:"Image URL",
    316 image_alt:"Image description",
    317 image_list:"Image list",
    318 image_border:"Border",
    319 image_dimensions:"Dimensions",
    320 image_vspace:"Vertical space",
    321 image_hspace:"Horizontal space",
    322 image_align:"Alignment",
    323 image_align_baseline:"Baseline",
    324 image_align_top:"Top",
    325 image_align_middle:"Middle",
    326 image_align_bottom:"Bottom",
    327 image_align_texttop:"Text top",
    328 image_align_textbottom:"Text bottom",
    329 image_align_left:"Left",
    330 image_align_right:"Right",
    331 link_title:"Insert/edit link",
    332 link_url:"Link URL",
    333 link_target:"Target",
    334 link_target_same:"Open link in the same window",
    335 link_target_blank:"Open link in a new window",
    336 link_titlefield:"Title",
    337 link_is_email:"The URL you entered seems to be an email address, do you want to add the required mailto: prefix?",
    338 link_is_external:"The URL you entered seems to external link, do you want to add the required http:// prefix?",
    339 link_list:"Link list",
    340 accessibility_help:"Accessibility Help",
    341 accessibility_usage_title:"General Usage"
    342 });
    343 
    344 tinyMCE.addI18n("en.media_dlg",{
    345 title:"Insert / edit embedded media",
    346 general:"General",
    347 advanced:"Advanced",
    348 file:"File/URL",
    349 list:"List",
    350 size:"Dimensions",
    351 preview:"Preview",
    352 constrain_proportions:"Constrain proportions",
    353 type:"Type",
    354 id:"Id",
    355 name:"Name",
    356 class_name:"Class",
    357 vspace:"V-Space",
    358 hspace:"H-Space",
    359 play:"Auto play",
    360 loop:"Loop",
    361 menu:"Show menu",
    362 quality:"Quality",
    363 scale:"Scale",
    364 align:"Align",
    365 salign:"SAlign",
    366 wmode:"WMode",
    367 bgcolor:"Background",
    368 base:"Base",
    369 flashvars:"Flashvars",
    370 liveconnect:"SWLiveConnect",
    371 autohref:"AutoHREF",
    372 cache:"Cache",
    373 hidden:"Hidden",
    374 controller:"Controller",
    375 kioskmode:"Kiosk mode",
    376 playeveryframe:"Play every frame",
    377 targetcache:"Target cache",
    378 correction:"No correction",
    379 enablejavascript:"Enable JavaScript",
    380 starttime:"Start time",
    381 endtime:"End time",
    382 href:"href",
    383 qtsrcchokespeed:"Choke speed",
    384 target:"Target",
    385 volume:"Volume",
    386 autostart:"Auto start",
    387 enabled:"Enabled",
    388 fullscreen:"Fullscreen",
    389 invokeurls:"Invoke URLs",
    390 mute:"Mute",
    391 stretchtofit:"Stretch to fit",
    392 windowlessvideo:"Windowless video",
    393 balance:"Balance",
    394 baseurl:"Base URL",
    395 captioningid:"Captioning id",
    396 currentmarker:"Current marker",
    397 currentposition:"Current position",
    398 defaultframe:"Default frame",
    399 playcount:"Play count",
    400 rate:"Rate",
    401 uimode:"UI Mode",
    402 flash_options:"Flash options",
    403 qt_options:"QuickTime options",
    404 wmp_options:"Windows media player options",
    405 rmp_options:"Real media player options",
    406 shockwave_options:"Shockwave options",
    407 autogotourl:"Auto goto URL",
    408 center:"Center",
    409 imagestatus:"Image status",
    410 maintainaspect:"Maintain aspect",
    411 nojava:"No java",
    412 prefetch:"Prefetch",
    413 shuffle:"Shuffle",
    414 console:"Console",
    415 numloop:"Num loops",
    416 controls:"Controls",
    417 scriptcallbacks:"Script callbacks",
    418 swstretchstyle:"Stretch style",
    419 swstretchhalign:"Stretch H-Align",
    420 swstretchvalign:"Stretch V-Align",
    421 sound:"Sound",
    422 progress:"Progress",
    423 qtsrc:"QT Src",
    424 qt_stream_warn:"Streamed rtsp resources should be added to the QT Src field under the advanced tab.",
    425 align_top:"Top",
    426 align_right:"Right",
    427 align_bottom:"Bottom",
    428 align_left:"Left",
    429 align_center:"Center",
    430 align_top_left:"Top left",
    431 align_top_right:"Top right",
    432 align_bottom_left:"Bottom left",
    433 align_bottom_right:"Bottom right",
    434 flv_options:"Flash video options",
    435 flv_scalemode:"Scale mode",
    436 flv_buffer:"Buffer",
    437 flv_startimage:"Start image",
    438 flv_starttime:"Start time",
    439 flv_defaultvolume:"Default volume",
    440 flv_hiddengui:"Hidden GUI",
    441 flv_autostart:"Auto start",
    442 flv_loop:"Loop",
    443 flv_showscalemodes:"Show scale modes",
    444 flv_smoothvideo:"Smooth video",
    445 flv_jscallback:"JS Callback",
    446 html5_video_options:"HTML5 Video Options",
    447 altsource1:"Alternative source 1",
    448 altsource2:"Alternative source 2",
    449 preload:"Preload",
    450 poster:"Poster",
    451 source:"Source"
    452 });
    453 
    454 tinyMCE.addI18n("en.wordpress",{
    455 wp_adv_desc:"Show/Hide Kitchen Sink (Alt + Shift + Z)",
    456 wp_more_desc:"Insert More Tag (Alt + Shift + T)",
    457 wp_page_desc:"Insert Page break (Alt + Shift + P)",
    458 wp_help_desc:"Help (Alt + Shift + H)",
    459 wp_more_alt:"More...",
    460 wp_page_alt:"Next page...",
    461 add_media:"Add Media",
    462 add_image:"Add an Image",
    463 add_video:"Add Video",
    464 add_audio:"Add Audio",
    465 editgallery:"Edit Gallery",
    466 delgallery:"Delete Gallery",
    467 wp_fullscreen_desc:"Distraction Free Writing mode (Alt + Shift + W)"
    468 });
    469 
    470 tinyMCE.addI18n("en.wpeditimage",{
    471 edit_img:"Edit Image",
    472 del_img:"Delete Image",
    473 adv_settings:"Advanced Settings",
    474 none:"None",
    475 size:"Size",
    476 thumbnail:"Thumbnail",
    477 medium:"Medium",
    478 full_size:"Full Size",
    479 current_link:"Current Link",
    480 link_to_img:"Link to Image",
    481 link_help:"Enter a link URL or click above for presets.",
    482 adv_img_settings:"Advanced Image Settings",
    483 source:"Source",
    484 width:"Width",
    485 height:"Height",
    486 orig_size:"Original Size",
    487 css:"CSS Class",
    488 adv_link_settings:"Advanced Link Settings",
    489 link_rel:"Link Rel",
    490 height:"Height",
    491 orig_size:"Original Size",
    492 css:"CSS Class",
    493 s60:"60%",
    494 s70:"70%",
    495 s80:"80%",
    496 s90:"90%",
    497 s100:"100%",
    498 s110:"110%",
    499 s120:"120%",
    500 s130:"130%",
    501 img_title:"Title",
    502 caption:"Caption",
    503 alt:"Alternative Text"
    504 });
     1/**
     2 * TinyMCE 3.x language strings
     3 *
     4 * Loaded only when external plugins are added to TinyMCE.
     5 */
     6( function() {
     7        var main = {}, lang = 'en';
     8
     9        if ( typeof tinyMCEPreInit !== 'undefined' && tinyMCEPreInit.ref.language !== 'en' ) {
     10                lang = tinyMCEPreInit.ref.language;
     11        }
     12
     13        main[lang] = {
     14                common: {
     15                        edit_confirm: "Do you want to use the WYSIWYG mode for this textarea?",
     16                        apply: "Apply",
     17                        insert: "Insert",
     18                        update: "Update",
     19                        cancel: "Cancel",
     20                        close: "Close",
     21                        browse: "Browse",
     22                        class_name: "Class",
     23                        not_set: "-- Not set --",
     24                        clipboard_msg: "Copy/Cut/Paste is not available in Mozilla and Firefox.",
     25                        clipboard_no_support: "Currently not supported by your browser, use keyboard shortcuts instead.",
     26                        popup_blocked: "Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.",
     27                        invalid_data: "ERROR: Invalid values entered, these are marked in red.",
     28                        invalid_data_number: "{#field} must be a number",
     29                        invalid_data_min: "{#field} must be a number greater than {#min}",
     30                        invalid_data_size: "{#field} must be a number or percentage",
     31                        more_colors: "More colors"
     32                },
     33                colors: {
     34                        "000000": "Black",
     35                        "993300": "Burnt orange",
     36                        "333300": "Dark olive",
     37                        "003300": "Dark green",
     38                        "003366": "Dark azure",
     39                        "000080": "Navy Blue",
     40                        "333399": "Indigo",
     41                        "333333": "Very dark gray",
     42                        "800000": "Maroon",
     43                        "FF6600": "Orange",
     44                        "808000": "Olive",
     45                        "008000": "Green",
     46                        "008080": "Teal",
     47                        "0000FF": "Blue",
     48                        "666699": "Grayish blue",
     49                        "808080": "Gray",
     50                        "FF0000": "Red",
     51                        "FF9900": "Amber",
     52                        "99CC00": "Yellow green",
     53                        "339966": "Sea green",
     54                        "33CCCC": "Turquoise",
     55                        "3366FF": "Royal blue",
     56                        "800080": "Purple",
     57                        "999999": "Medium gray",
     58                        "FF00FF": "Magenta",
     59                        "FFCC00": "Gold",
     60                        "FFFF00": "Yellow",
     61                        "00FF00": "Lime",
     62                        "00FFFF": "Aqua",
     63                        "00CCFF": "Sky blue",
     64                        "993366": "Brown",
     65                        "C0C0C0": "Silver",
     66                        "FF99CC": "Pink",
     67                        "FFCC99": "Peach",
     68                        "FFFF99": "Light yellow",
     69                        "CCFFCC": "Pale green",
     70                        "CCFFFF": "Pale cyan",
     71                        "99CCFF": "Light sky blue",
     72                        "CC99FF": "Plum",
     73                        "FFFFFF": "White"
     74                },
     75                contextmenu: {
     76                        align: "Alignment",
     77                        left: "Left",
     78                        center: "Center",
     79                        right: "Right",
     80                        full: "Full"
     81                },
     82                insertdatetime: {
     83                        date_fmt: "%Y-%m-%d",
     84                        time_fmt: "%H:%M:%S",
     85                        insertdate_desc: "Insert date",
     86                        inserttime_desc: "Insert time",
     87                        months_long: "January,February,March,April,May,June,July,August,September,October,November,December",
     88                        months_short: "Jan_January_abbreviation,Feb_February_abbreviation,Mar_March_abbreviation,Apr_April_abbreviation,May_May_abbreviation,Jun_June_abbreviation,Jul_July_abbreviation,Aug_August_abbreviation,Sep_September_abbreviation,Oct_October_abbreviation,Nov_November_abbreviation,Dec_December_abbreviation",
     89                        day_long: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
     90                        day_short: "Sun,Mon,Tue,Wed,Thu,Fri,Sat"
     91                },
     92                print: {
     93                        print_desc: "Print"
     94                },
     95                preview: {
     96                        preview_desc: "Preview"
     97                },
     98                directionality: {
     99                        ltr_desc: "Direction left to right",
     100                        rtl_desc: "Direction right to left"
     101                },
     102                layer: {
     103                        insertlayer_desc: "Insert new layer",
     104                        forward_desc: "Move forward",
     105                        backward_desc: "Move backward",
     106                        absolute_desc: "Toggle absolute positioning",
     107                        content: "New layer..."
     108                },
     109                save: {
     110                        save_desc: "Save",
     111                        cancel_desc: "Cancel all changes"
     112                },
     113                nonbreaking: {
     114                        nonbreaking_desc: "Insert non-breaking space character"
     115                },
     116                iespell: {
     117                        iespell_desc: "Run spell checking",
     118                        download: "ieSpell not detected. Do you want to install it now?"
     119                },
     120                advhr: {
     121                        advhr_desc: "Horizontal rule"
     122                },
     123                emotions: {
     124                        emotions_desc: "Emotions"
     125                },
     126                searchreplace: {
     127                        search_desc: "Find",
     128                        replace_desc: "Find/Replace"
     129                },
     130                advimage: {
     131                        image_desc: "Insert/edit image"
     132                },
     133                advlink: {
     134                        link_desc: "Insert/edit link"
     135                },
     136                xhtmlxtras: {
     137                        cite_desc: "Citation",
     138                        abbr_desc: "Abbreviation",
     139                        acronym_desc: "Acronym",
     140                        del_desc: "Deletion",
     141                        ins_desc: "Insertion",
     142                        attribs_desc: "Insert/Edit Attributes"
     143                },
     144                style: {
     145                        desc: "Edit CSS Style"
     146                },
     147                paste: {
     148                        paste_text_desc: "Paste as Plain Text",
     149                        paste_word_desc: "Paste from Word",
     150                        selectall_desc: "Select All",
     151                        plaintext_mode_sticky: "Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.",
     152                        plaintext_mode: "Paste is now in plain text mode. Click again to toggle back to regular paste mode."
     153                },
     154                paste_dlg: {
     155                        text_title: "Use CTRL + V on your keyboard to paste the text into the window.",
     156                        text_linebreaks: "Keep linebreaks",
     157                        word_title: "Use CTRL + V on your keyboard to paste the text into the window."
     158                },
     159                table: {
     160                        desc: "Inserts a new table",
     161                        row_before_desc: "Insert row before",
     162                        row_after_desc: "Insert row after",
     163                        delete_row_desc: "Delete row",
     164                        col_before_desc: "Insert column before",
     165                        col_after_desc: "Insert column after",
     166                        delete_col_desc: "Remove column",
     167                        split_cells_desc: "Split merged table cells",
     168                        merge_cells_desc: "Merge table cells",
     169                        row_desc: "Table row properties",
     170                        cell_desc: "Table cell properties",
     171                        props_desc: "Table properties",
     172                        paste_row_before_desc: "Paste table row before",
     173                        paste_row_after_desc: "Paste table row after",
     174                        cut_row_desc: "Cut table row",
     175                        copy_row_desc: "Copy table row",
     176                        del: "Delete table",
     177                        row: "Row",
     178                        col: "Column",
     179                        cell: "Cell"
     180                },
     181                autosave: {
     182                        unload_msg: "The changes you made will be lost if you navigate away from this page."
     183                },
     184                fullscreen: {
     185                        desc: "Toggle fullscreen mode (Alt + Shift + G)"
     186                },
     187                media: {
     188                        desc: "Insert / edit embedded media",
     189                        edit: "Edit embedded media"
     190                },
     191                fullpage: {
     192                        desc: "Document properties"
     193                },
     194                template: {
     195                        desc: "Insert predefined template content"
     196                },
     197                visualchars: {
     198                        desc: "Visual control characters on/off."
     199                },
     200                spellchecker: {
     201                        desc: "Toggle spellchecker (Alt + Shift + N)",
     202                        menu: "Spellchecker settings",
     203                        ignore_word: "Ignore word",
     204                        ignore_words: "Ignore all",
     205                        langs: "Languages",
     206                        wait: "Please wait...",
     207                        sug: "Suggestions",
     208                        no_sug: "No suggestions",
     209                        no_mpell: "No misspellings found.",
     210                        learn_word: "Learn word"
     211                },
     212                pagebreak: {
     213                        desc: "Insert Page Break"
     214                },
     215                advlist:{
     216                        types: "Types",
     217                        def: "Default",
     218                        lower_alpha: "Lower alpha",
     219                        lower_greek: "Lower greek",
     220                        lower_roman: "Lower roman",
     221                        upper_alpha: "Upper alpha",
     222                        upper_roman: "Upper roman",
     223                        circle: "Circle",
     224                        disc: "Disc",
     225                        square: "Square"
     226                },
     227                aria: {
     228                        rich_text_area: "Rich Text Area"
     229                },
     230                wordcount:{
     231                        words: "Words: "
     232                }
     233        };
     234
     235        tinyMCE.addI18n( main );
     236
     237        tinyMCE.addI18n( lang + ".advanced", {
     238                style_select: "Styles",
     239                font_size: "Font size",
     240                fontdefault: "Font family",
     241                block: "Format",
     242                paragraph: "Paragraph",
     243                div: "Div",
     244                address: "Address",
     245                pre: "Preformatted",
     246                h1: "Heading 1",
     247                h2: "Heading 2",
     248                h3: "Heading 3",
     249                h4: "Heading 4",
     250                h5: "Heading 5",
     251                h6: "Heading 6",
     252                blockquote: "Blockquote",
     253                code: "Code",
     254                samp: "Code sample",
     255                dt: "Definition term ",
     256                dd: "Definition description",
     257                bold_desc: "Bold (Ctrl + B)",
     258                italic_desc: "Italic (Ctrl + I)",
     259                underline_desc: "Underline",
     260                striketrough_desc: "Strikethrough (Alt + Shift + D)",
     261                justifyleft_desc: "Align Left (Alt + Shift + L)",
     262                justifycenter_desc: "Align Center (Alt + Shift + C)",
     263                justifyright_desc: "Align Right (Alt + Shift + R)",
     264                justifyfull_desc: "Align Full (Alt + Shift + J)",
     265                bullist_desc: "Unordered list (Alt + Shift + U)",
     266                numlist_desc: "Ordered list (Alt + Shift + O)",
     267                outdent_desc: "Outdent",
     268                indent_desc: "Indent",
     269                undo_desc: "Undo (Ctrl + Z)",
     270                redo_desc: "Redo (Ctrl + Y)",
     271                link_desc: "Insert/edit link (Alt + Shift + A)",
     272                unlink_desc: "Unlink (Alt + Shift + S)",
     273                image_desc: "Insert/edit image (Alt + Shift + M)",
     274                cleanup_desc: "Cleanup messy code",
     275                code_desc: "Edit HTML Source",
     276                sub_desc: "Subscript",
     277                sup_desc: "Superscript",
     278                hr_desc: "Insert horizontal ruler",
     279                removeformat_desc: "Remove formatting",
     280                forecolor_desc: "Select text color",
     281                backcolor_desc: "Select background color",
     282                charmap_desc: "Insert custom character",
     283                visualaid_desc: "Toggle guidelines/invisible elements",
     284                anchor_desc: "Insert/edit anchor",
     285                cut_desc: "Cut",
     286                copy_desc: "Copy",
     287                paste_desc: "Paste",
     288                image_props_desc: "Image properties",
     289                newdocument_desc: "New document",
     290                help_desc: "Help",
     291                blockquote_desc: "Blockquote (Alt + Shift + Q)",
     292                clipboard_msg: "Copy/Cut/Paste is not available in Mozilla and Firefox.",
     293                path: "Path",
     294                newdocument: "Are you sure you want to clear all contents?",
     295                toolbar_focus: "Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X",
     296                more_colors: "More colors",
     297                shortcuts_desc: "Accessibility Help",
     298                help_shortcut: " Press ALT F10 for toolbar. Press ALT 0 for help.",
     299                rich_text_area: "Rich Text Area",
     300                toolbar: "Toolbar"
     301        });
     302
     303        tinyMCE.addI18n( lang + ".advanced_dlg", {
     304                about_title: "About TinyMCE",
     305                about_general: "About",
     306                about_help: "Help",
     307                about_license: "License",
     308                about_plugins: "Plugins",
     309                about_plugin: "Plugin",
     310                about_author: "Author",
     311                about_version: "Version",
     312                about_loaded: "Loaded plugins",
     313                anchor_title: "Insert/edit anchor",
     314                anchor_name: "Anchor name",
     315                code_title: "HTML Source Editor",
     316                code_wordwrap: "Word wrap",
     317                colorpicker_title: "Select a color",
     318                colorpicker_picker_tab: "Picker",
     319                colorpicker_picker_title: "Color picker",
     320                colorpicker_palette_tab: "Palette",
     321                colorpicker_palette_title: "Palette colors",
     322                colorpicker_named_tab: "Named",
     323                colorpicker_named_title: "Named colors",
     324                colorpicker_color: "Color: ",
     325                colorpicker_name: "Name: ",
     326                charmap_title: "Select custom character",
     327                charmap_usage: "Use left and right arrows to navigate.",
     328                image_title: "Insert/edit image",
     329                image_src: "Image URL",
     330                image_alt: "Image description",
     331                image_list: "Image list",
     332                image_border: "Border",
     333                image_dimensions: "Dimensions",
     334                image_vspace: "Vertical space",
     335                image_hspace: "Horizontal space",
     336                image_align: "Alignment",
     337                image_align_baseline: "Baseline",
     338                image_align_top: "Top",
     339                image_align_middle: "Middle",
     340                image_align_bottom: "Bottom",
     341                image_align_texttop: "Text top",
     342                image_align_textbottom: "Text bottom",
     343                image_align_left: "Left",
     344                image_align_right: "Right",
     345                link_title: "Insert/edit link",
     346                link_url: "Link URL",
     347                link_target: "Target",
     348                link_target_same: "Open link in the same window",
     349                link_target_blank: "Open link in a new window",
     350                link_titlefield: "Title",
     351                link_is_email: "The URL you entered seems to be an email address, do you want to add the required mailto: prefix?",
     352                link_is_external: "The URL you entered seems to be an external link, do you want to add the required http:// prefix?",
     353                link_list: "Link list",
     354                accessibility_help: "Accessibility Help",
     355                accessibility_usage_title: "General Usage"
     356        });
     357
     358        tinyMCE.addI18n( lang + ".media_dlg", {
     359                title: "Insert / edit embedded media",
     360                general: "General",
     361                advanced: "Advanced",
     362                file: "File/URL",
     363                list: "List",
     364                size: "Dimensions",
     365                preview: "Preview",
     366                constrain_proportions: "Constrain proportions",
     367                type: "Type",
     368                id: "Id",
     369                name: "Name",
     370                class_name: "Class",
     371                vspace: "V-Space",
     372                hspace: "H-Space",
     373                play: "Auto play",
     374                loop: "Loop",
     375                menu: "Show menu",
     376                quality: "Quality",
     377                scale: "Scale",
     378                align: "Align",
     379                salign: "SAlign",
     380                wmode: "WMode",
     381                bgcolor: "Background",
     382                base: "Base",
     383                flashvars: "Flashvars",
     384                liveconnect: "SWLiveConnect",
     385                autohref: "AutoHREF",
     386                cache: "Cache",
     387                hidden: "Hidden",
     388                controller: "Controller",
     389                kioskmode: "Kiosk mode",
     390                playeveryframe: "Play every frame",
     391                targetcache: "Target cache",
     392                correction: "No correction",
     393                enablejavascript: "Enable JavaScript",
     394                starttime: "Start time",
     395                endtime: "End time",
     396                href: "href",
     397                qtsrcchokespeed: "Choke speed",
     398                target: "Target",
     399                volume: "Volume",
     400                autostart: "Auto start",
     401                enabled: "Enabled",
     402                fullscreen: "Fullscreen",
     403                invokeurls: "Invoke URLs",
     404                mute: "Mute",
     405                stretchtofit: "Stretch to fit",
     406                windowlessvideo: "Windowless video",
     407                balance: "Balance",
     408                baseurl: "Base URL",
     409                captioningid: "Captioning id",
     410                currentmarker: "Current marker",
     411                currentposition: "Current position",
     412                defaultframe: "Default frame",
     413                playcount: "Play count",
     414                rate: "Rate",
     415                uimode: "UI Mode",
     416                flash_options: "Flash options",
     417                qt_options: "QuickTime options",
     418                wmp_options: "Windows media player options",
     419                rmp_options: "Real media player options",
     420                shockwave_options: "Shockwave options",
     421                autogotourl: "Auto goto URL",
     422                center: "Center",
     423                imagestatus: "Image status",
     424                maintainaspect: "Maintain aspect",
     425                nojava: "No java",
     426                prefetch: "Prefetch",
     427                shuffle: "Shuffle",
     428                console: "Console",
     429                numloop: "Num loops",
     430                controls: "Controls",
     431                scriptcallbacks: "Script callbacks",
     432                swstretchstyle: "Stretch style",
     433                swstretchhalign: "Stretch H-Align",
     434                swstretchvalign: "Stretch V-Align",
     435                sound: "Sound",
     436                progress: "Progress",
     437                qtsrc: "QT Src",
     438                qt_stream_warn: "Streamed rtsp resources should be added to the QT Src field under the advanced tab.",
     439                align_top: "Top",
     440                align_right: "Right",
     441                align_bottom: "Bottom",
     442                align_left: "Left",
     443                align_center: "Center",
     444                align_top_left: "Top left",
     445                align_top_right: "Top right",
     446                align_bottom_left: "Bottom left",
     447                align_bottom_right: "Bottom right",
     448                flv_options: "Flash video options",
     449                flv_scalemode: "Scale mode",
     450                flv_buffer: "Buffer",
     451                flv_startimage: "Start image",
     452                flv_starttime: "Start time",
     453                flv_defaultvolume: "Default volume",
     454                flv_hiddengui: "Hidden GUI",
     455                flv_autostart: "Auto start",
     456                flv_loop: "Loop",
     457                flv_showscalemodes: "Show scale modes",
     458                flv_smoothvideo: "Smooth video",
     459                flv_jscallback: "JS Callback",
     460                html5_video_options: "HTML5 Video Options",
     461                altsource1: "Alternative source 1",
     462                altsource2: "Alternative source 2",
     463                preload: "Preload",
     464                poster: "Poster",
     465                source: "Source"
     466        });
     467
     468        tinyMCE.addI18n( lang + ".wordpress", {
     469                wp_adv_desc: "Show/Hide Kitchen Sink (Alt + Shift + Z)",
     470                wp_more_desc: "Insert More Tag (Alt + Shift + T)",
     471                wp_page_desc: "Insert Page break (Alt + Shift + P)",
     472                wp_help_desc: "Help (Alt + Shift + H)",
     473                wp_more_alt: "More...",
     474                wp_page_alt: "Next page...",
     475                add_media: "Add Media",
     476                add_image: "Add an Image",
     477                add_video: "Add Video",
     478                add_audio: "Add Audio",
     479                editgallery: "Edit Gallery",
     480                delgallery: "Delete Gallery",
     481                wp_fullscreen_desc: "Distraction Free Writing mode (Alt + Shift + W)"
     482        });
     483
     484        tinyMCE.addI18n( lang + ".wpeditimage", {
     485                edit_img: "Edit Image",
     486                del_img: "Delete Image",
     487                adv_settings: "Advanced Settings",
     488                none: "None",
     489                size: "Size",
     490                thumbnail: "Thumbnail",
     491                medium: "Medium",
     492                full_size: "Full Size",
     493                current_link: "Current Link",
     494                link_to_img: "Link to Image",
     495                link_help: "Enter a link URL or click above for presets.",
     496                adv_img_settings: "Advanced Image Settings",
     497                source: "Source",
     498                width: "Width",
     499                height: "Height",
     500                orig_size: "Original Size",
     501                css: "CSS Class",
     502                adv_link_settings: "Advanced Link Settings",
     503                link_rel: "Link Rel",
     504                height: "Height",
     505                orig_size: "Original Size",
     506                css: "CSS Class",
     507                s60: "60%",
     508                s70: "70%",
     509                s80: "80%",
     510                s90: "90%",
     511                s100: "100%",
     512                s110: "110%",
     513                s120: "120%",
     514                s130: "130%",
     515                img_title: "Title",
     516                caption: "Caption",
     517                alt: "Alternative Text"
     518        });
     519}());
  • trunk/src/wp-includes/js/tinymce/plugins/directionality/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
     2 * plugin.js
    33 *
    4  * Copyright 2009, Moxiecode Systems AB
     4 * Copyright, Moxiecode Systems AB
    55 * Released under LGPL License.
    66 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     7 * License: http://www.tinymce.com/license
     8 * Contributing: http://www.tinymce.com/contributing
    99 */
    1010
    11 (function() {
    12         tinymce.create('tinymce.plugins.Directionality', {
    13                 init : function(ed, url) {
    14                         var t = this;
     11/*global tinymce:true */
    1512
    16                         t.editor = ed;
     13tinymce.PluginManager.add('directionality', function(editor) {
     14        function setDir(dir) {
     15                var dom = editor.dom, curDir, blocks = editor.selection.getSelectedBlocks();
    1716
    18                         function setDir(dir) {
    19                                 var dom = ed.dom, curDir, blocks = ed.selection.getSelectedBlocks();
     17                if (blocks.length) {
     18                        curDir = dom.getAttrib(blocks[0], "dir");
    2019
    21                                 if (blocks.length) {
    22                                         curDir = dom.getAttrib(blocks[0], "dir");
    23 
    24                                         tinymce.each(blocks, function(block) {
    25                                                 // Add dir to block if the parent block doesn't already have that dir
    26                                                 if (!dom.getParent(block.parentNode, "*[dir='" + dir + "']", dom.getRoot())) {
    27                                                         if (curDir != dir) {
    28                                                                 dom.setAttrib(block, "dir", dir);
    29                                                         } else {
    30                                                                 dom.setAttrib(block, "dir", null);
    31                                                         }
    32                                                 }
    33                                         });
    34 
    35                                         ed.nodeChanged();
     20                        tinymce.each(blocks, function(block) {
     21                                // Add dir to block if the parent block doesn't already have that dir
     22                                if (!dom.getParent(block.parentNode, "*[dir='" + dir + "']", dom.getRoot())) {
     23                                        if (curDir != dir) {
     24                                                dom.setAttrib(block, "dir", dir);
     25                                        } else {
     26                                                dom.setAttrib(block, "dir", null);
     27                                        }
    3628                                }
    37                         }
    38 
    39                         ed.addCommand('mceDirectionLTR', function() {
    40                                 setDir("ltr");
    4129                        });
    4230
    43                         ed.addCommand('mceDirectionRTL', function() {
    44                                 setDir("rtl");
    45                         });
     31                        editor.nodeChanged();
     32                }
     33        }
    4634
    47                         ed.addButton('ltr', {title : 'directionality.ltr_desc', cmd : 'mceDirectionLTR'});
    48                         ed.addButton('rtl', {title : 'directionality.rtl_desc', cmd : 'mceDirectionRTL'});
     35        function generateSelector(dir) {
     36                var selector = [];
    4937
    50                         ed.onNodeChange.add(t._nodeChange, t);
    51                 },
     38                tinymce.each('h1 h2 h3 h4 h5 h6 div p'.split(' '), function(name) {
     39                        selector.push(name + '[dir=' + dir + ']');
     40                });
    5241
    53                 getInfo : function() {
    54                         return {
    55                                 longname : 'Directionality',
    56                                 author : 'Moxiecode Systems AB',
    57                                 authorurl : 'http://tinymce.moxiecode.com',
    58                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality',
    59                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
    60                         };
    61                 },
     42                return selector.join(',');
     43        }
    6244
    63                 // Private methods
    64 
    65                 _nodeChange : function(ed, cm, n) {
    66                         var dom = ed.dom, dir;
    67 
    68                         n = dom.getParent(n, dom.isBlock);
    69                         if (!n) {
    70                                 cm.setDisabled('ltr', 1);
    71                                 cm.setDisabled('rtl', 1);
    72                                 return;
    73                         }
    74 
    75                         dir = dom.getAttrib(n, 'dir');
    76                         cm.setActive('ltr', dir == "ltr");
    77                         cm.setDisabled('ltr', 0);
    78                         cm.setActive('rtl', dir == "rtl");
    79                         cm.setDisabled('rtl', 0);
    80                 }
     45        editor.addCommand('mceDirectionLTR', function() {
     46                setDir("ltr");
    8147        });
    8248
    83         // Register plugin
    84         tinymce.PluginManager.add('directionality', tinymce.plugins.Directionality);
    85 })();
     49        editor.addCommand('mceDirectionRTL', function() {
     50                setDir("rtl");
     51        });
     52
     53        editor.addButton('ltr', {
     54                title: 'Left to right',
     55                cmd: 'mceDirectionLTR',
     56                stateSelector: generateSelector('ltr')
     57        });
     58
     59        editor.addButton('rtl', {
     60                title: 'Right to left',
     61                cmd: 'mceDirectionRTL',
     62                stateSelector: generateSelector('rtl')
     63        });
     64});
  • trunk/src/wp-includes/js/tinymce/plugins/directionality/plugin.min.js

    r26875 r26876  
    1 (function(){tinymce.create("tinymce.plugins.Directionality",{init:function(b,c){var d=this;d.editor=b;function a(e){var h=b.dom,g,f=b.selection.getSelectedBlocks();if(f.length){g=h.getAttrib(f[0],"dir");tinymce.each(f,function(i){if(!h.getParent(i.parentNode,"*[dir='"+e+"']",h.getRoot())){if(g!=e){h.setAttrib(i,"dir",e)}else{h.setAttrib(i,"dir",null)}}});b.nodeChanged()}}b.addCommand("mceDirectionLTR",function(){a("ltr")});b.addCommand("mceDirectionRTL",function(){a("rtl")});b.addButton("ltr",{title:"directionality.ltr_desc",cmd:"mceDirectionLTR"});b.addButton("rtl",{title:"directionality.rtl_desc",cmd:"mceDirectionRTL"});b.onNodeChange.add(d._nodeChange,d)},getInfo:function(){return{longname:"Directionality",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,e){var d=b.dom,c;e=d.getParent(e,d.isBlock);if(!e){a.setDisabled("ltr",1);a.setDisabled("rtl",1);return}c=d.getAttrib(e,"dir");a.setActive("ltr",c=="ltr");a.setDisabled("ltr",0);a.setActive("rtl",c=="rtl");a.setDisabled("rtl",0)}});tinymce.PluginManager.add("directionality",tinymce.plugins.Directionality)})();
     1tinymce.PluginManager.add("directionality",function(e){function t(t){var n,i=e.dom,a=e.selection.getSelectedBlocks();a.length&&(n=i.getAttrib(a[0],"dir"),tinymce.each(a,function(e){i.getParent(e.parentNode,"*[dir='"+t+"']",i.getRoot())||(n!=t?i.setAttrib(e,"dir",t):i.setAttrib(e,"dir",null))}),e.nodeChanged())}function n(e){var t=[];return tinymce.each("h1 h2 h3 h4 h5 h6 div p".split(" "),function(n){t.push(n+"[dir="+e+"]")}),t.join(",")}e.addCommand("mceDirectionLTR",function(){t("ltr")}),e.addCommand("mceDirectionRTL",function(){t("rtl")}),e.addButton("ltr",{title:"Left to right",cmd:"mceDirectionLTR",stateSelector:n("ltr")}),e.addButton("rtl",{title:"Right to left",cmd:"mceDirectionRTL",stateSelector:n("rtl")})});
  • trunk/src/wp-includes/js/tinymce/plugins/fullscreen/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
     2 * plugin.js
    33 *
    4  * Copyright 2009, Moxiecode Systems AB
     4 * Copyright, Moxiecode Systems AB
    55 * Released under LGPL License.
    66 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     7 * License: http://www.tinymce.com/license
     8 * Contributing: http://www.tinymce.com/contributing
    99 */
    1010
    11 (function() {
    12         var DOM = tinymce.DOM;
     11/*global tinymce:true */
    1312
    14         // State Transfer function
    15         var transferState = function(oldEditor, newEditor, bookmark) {
    16                 var transferColorButtonState = function(swapme) {
    17                         var c = oldEditor.controlManager.get(swapme);
    18                         var newC = newEditor.controlManager.get(swapme);
     13tinymce.PluginManager.add('fullscreen', function(editor) {
     14        var fullscreenState = false, DOM = tinymce.DOM, iframeWidth, iframeHeight, resizeHandler;
     15        var containerWidth, containerHeight;
    1916
    20                         if (c && newC) {
    21                                 newC.displayColor(c.value);
     17        if (editor.settings.inline) {
     18                return;
     19        }
     20
     21        function getWindowSize() {
     22                var w, h, win = window, doc = document;
     23                var body = doc.body;
     24
     25                // Old IE
     26                if (body.offsetWidth) {
     27                        w = body.offsetWidth;
     28                        h = body.offsetHeight;
     29                }
     30
     31                // Modern browsers
     32                if (win.innerWidth && win.innerHeight) {
     33                        w = win.innerWidth;
     34                        h = win.innerHeight;
     35                }
     36
     37                return {w: w, h: h};
     38        }
     39
     40        function toggleFullscreen() {
     41                var body = document.body, documentElement = document.documentElement, editorContainerStyle;
     42                var editorContainer, iframe, iframeStyle;
     43
     44                function resize() {
     45                        DOM.setStyle(iframe, 'height', getWindowSize().h - (editorContainer.clientHeight - iframe.clientHeight));
     46                }
     47
     48                fullscreenState = !fullscreenState;
     49
     50                editorContainer = editor.getContainer();
     51                editorContainerStyle = editorContainer.style;
     52                iframe = editor.getContentAreaContainer().firstChild;
     53                iframeStyle = iframe.style;
     54
     55                if (fullscreenState) {
     56                        iframeWidth = iframeStyle.width;
     57                        iframeHeight = iframeStyle.height;
     58                        iframeStyle.width = iframeStyle.height = '100%';
     59                        containerWidth = editorContainerStyle.width;
     60                        containerHeight = editorContainerStyle.height;
     61                        editorContainerStyle.width = editorContainerStyle.height = '';
     62
     63                        DOM.addClass(body, 'mce-fullscreen');
     64                        DOM.addClass(documentElement, 'mce-fullscreen');
     65                        DOM.addClass(editorContainer, 'mce-fullscreen');
     66
     67                        DOM.bind(window, 'resize', resize);
     68                        resize();
     69                        resizeHandler = resize;
     70                } else {
     71                        iframeStyle.width = iframeWidth;
     72                        iframeStyle.height = iframeHeight;
     73
     74                        if (containerWidth) {
     75                                editorContainerStyle.width = containerWidth;
    2276                        }
    2377
    24                 };
     78                        if (containerHeight) {
     79                                editorContainerStyle.height = containerHeight;
     80                        }
    2581
    26                 transferColorButtonState('forecolor');
    27                 transferColorButtonState('backcolor');
    28                 newEditor.setContent(oldEditor.getContent({format : 'raw'}), {format : 'raw'});
    29                 newEditor.selection.moveToBookmark(bookmark);
     82                        DOM.removeClass(body, 'mce-fullscreen');
     83                        DOM.removeClass(documentElement, 'mce-fullscreen');
     84                        DOM.removeClass(editorContainer, 'mce-fullscreen');
     85                        DOM.unbind(window, 'resize', resizeHandler);
     86                }
    3087
    31                 if (oldEditor.plugins.spellchecker && newEditor.plugins.spellchecker) {
    32                         newEditor.plugins.spellchecker.setLanguage(oldEditor.plugins.spellchecker.selectedLang);
    33                 }
    34         };
     88                editor.fire('FullscreenStateChanged', {state: fullscreenState});
     89        }
    3590
    36         tinymce.create('tinymce.plugins.FullScreenPlugin', {
    37                 init : function(ed, url) {
    38                         var t = this, s = {}, de = DOM.doc.documentElement, vp, fullscreen_overflow, fullscreen_html_overflow, fullscreen_scrollx, fullscreen_scrolly, posCss, bookmark;
     91        editor.on('init', function() {
     92                editor.addShortcut('Ctrl+Alt+F', '', toggleFullscreen);
     93        });
    3994
    40                         // Register commands
    41                         ed.addCommand('mceFullScreen', function() {
    42                                 var win, oed;
    43 
    44                                 if (ed.getParam('fullscreen_is_enabled')) {
    45                                         if (ed.getParam('fullscreen_new_window'))
    46                                                 closeFullscreen(); // Call to close in fullscreen.htm
    47                                         else {
    48                                                 DOM.win.setTimeout(function() {
    49                                                         var fullscreenEditor = ed;
    50 
    51                                                         // find the editor that opened this one, execute restore function there
    52                                                         var originalEditor = tinyMCE.get(fullscreenEditor.getParam('fullscreen_editor_id'));
    53                                                         originalEditor.plugins.fullscreen.saveState(fullscreenEditor);
    54 
    55                                                         tinyMCE.remove(fullscreenEditor);
    56                                                 }, 10);
    57                                         }
    58 
    59                                         return;
    60                                 }
    61 
    62                                 if (ed.getParam('fullscreen_new_window')) {
    63                                         t.fullscreenSettings = {
    64                                                 bookmark: ed.selection.getBookmark()
    65                                         };
    66                                         win = DOM.win.open(url + "/fullscreen.htm", "mceFullScreenPopup", "fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width=" + screen.availWidth + ",height=" + screen.availHeight);
    67                                         try {
    68                                                 win.resizeTo(screen.availWidth, screen.availHeight);
    69                                         } catch (e) {
    70                                                 // Ignore
    71                                         }
    72                                 } else {
    73                                         fullscreen_overflow = DOM.getStyle(DOM.doc.body, 'overflow', 1) || 'auto';
    74                                         fullscreen_html_overflow = DOM.getStyle(de, 'overflow', 1);
    75                                         vp = DOM.getViewPort();
    76                                         fullscreen_scrollx = vp.x;
    77                                         fullscreen_scrolly = vp.y;
    78 
    79                                         // Fixes an Opera bug where the scrollbars doesn't reappear
    80                                         if (tinymce.isOpera && fullscreen_overflow == 'visible')
    81                                                 fullscreen_overflow = 'auto';
    82 
    83                                         // Fixes an IE bug where horizontal scrollbars would appear
    84                                         if (tinymce.isIE && fullscreen_overflow == 'scroll')
    85                                                 fullscreen_overflow = 'auto';
    86 
    87                                         // Fixes an IE bug where the scrollbars doesn't reappear
    88                                         if (tinymce.isIE && (fullscreen_html_overflow == 'visible' || fullscreen_html_overflow == 'scroll'))
    89                                                 fullscreen_html_overflow = 'auto';
    90 
    91                                         if (fullscreen_overflow == '0px')
    92                                                 fullscreen_overflow = '';
    93 
    94                                         DOM.setStyle(DOM.doc.body, 'overflow', 'hidden');
    95                                         de.style.overflow = 'hidden'; //Fix for IE6/7
    96                                         vp = DOM.getViewPort();
    97                                         DOM.win.scrollTo(0, 0);
    98 
    99                                         if (tinymce.isIE)
    100                                                 vp.h -= 1;
    101 
    102                                         // Use fixed position if it exists
    103                                         if (tinymce.isIE6 || document.compatMode == 'BackCompat')
    104                                                 posCss = 'absolute;top:' + vp.y;
    105                                         else
    106                                                 posCss = 'fixed;top:0';
    107 
    108                                         n = DOM.add(DOM.doc.body, 'div', {
    109                                                 id : 'mce_fullscreen_container',
    110                                                 style : 'position:' + posCss + ';left:0;width:' + vp.w + 'px;height:' + vp.h + 'px;z-index:200000;'});
    111                                         DOM.add(n, 'div', {id : 'mce_fullscreen'});
    112 
    113                                         tinymce.each(ed.settings, function(v, n) {
    114                                                 s[n] = v;
    115                                         });
    116 
    117                                         s.id = 'mce_fullscreen';
    118                                         s.width = n.clientWidth;
    119                                         s.height = n.clientHeight - 15;
    120                                         s.fullscreen_is_enabled = true;
    121                                         s.fullscreen_editor_id = ed.id;
    122                                         s.theme_advanced_resizing = false;
    123                                         s.save_onsavecallback = function() {
    124                                                 ed.setContent(tinyMCE.get(s.id).getContent());
    125                                                 ed.execCommand('mceSave');
    126                                         };
    127 
    128                                         tinymce.each(ed.getParam('fullscreen_settings'), function(v, k) {
    129                                                 s[k] = v;
    130                                         });
    131 
    132                                         t.fullscreenSettings = {
    133                                                 bookmark: ed.selection.getBookmark(),
    134                                                 fullscreen_overflow: fullscreen_overflow,
    135                                                 fullscreen_html_overflow: fullscreen_html_overflow,
    136                                                 fullscreen_scrollx: fullscreen_scrollx,
    137                                                 fullscreen_scrolly: fullscreen_scrolly
    138                                         };
    139 
    140                                         if (s.theme_advanced_toolbar_location === 'external')
    141                                                 s.theme_advanced_toolbar_location = 'top';
    142 
    143                                         tinyMCE.oldSettings = tinyMCE.settings; // Store old settings, the Editor constructor overwrites them
    144                                         t.fullscreenEditor = new tinymce.Editor('mce_fullscreen', s);
    145                                         t.fullscreenEditor.onInit.add(function() {
    146                                                 t.loadState(t.fullscreenEditor);
    147                                         });
    148 
    149                                         t.fullscreenEditor.render();
    150 
    151                                         t.fullscreenElement = new tinymce.dom.Element('mce_fullscreen_container');
    152                                         t.fullscreenElement.update();
    153                                         //document.body.overflow = 'hidden';
    154 
    155                                         t.resizeFunc = tinymce.dom.Event.add(DOM.win, 'resize', function() {
    156                                                 var vp = tinymce.DOM.getViewPort(), fed = t.fullscreenEditor, outerSize, innerSize;
    157 
    158                                                 // Get outer/inner size to get a delta size that can be used to calc the new iframe size
    159                                                 outerSize = fed.dom.getSize(fed.getContainer().getElementsByTagName('table')[0]);
    160                                                 innerSize = fed.dom.getSize(fed.getContainer().getElementsByTagName('iframe')[0]);
    161 
    162                                                 fed.theme.resizeTo(vp.w - outerSize.w + innerSize.w, vp.h - outerSize.h + innerSize.h);
    163                                         });
    164                                 }
    165                         });
    166 
    167                         // Register buttons
    168                         ed.addButton('fullscreen', {title : 'fullscreen.desc', cmd : 'mceFullScreen'});
    169 
    170                         ed.onNodeChange.add(function(ed, cm) {
    171                                 cm.setActive('fullscreen', ed.getParam('fullscreen_is_enabled'));
    172                         });
    173 
    174                         // fullscreenEditor is a param here because in window mode we don't create it
    175                         t.loadState = function(fullscreenEditor) {
    176                                 if (!(fullscreenEditor && t.fullscreenSettings)) {
    177                                         throw "No fullscreen editor to load to";
    178                                 }
    179 
    180                                 transferState(ed, fullscreenEditor, t.fullscreenSettings.bookmark);
    181                                 fullscreenEditor.focus();
    182 
    183                         };
    184 
    185                         // fullscreenEditor is a param here because in window mode we don't create it
    186                         t.saveState = function(fullscreenEditor) {
    187                                 if (!(fullscreenEditor && t.fullscreenSettings)) {
    188                                         throw "No fullscreen editor to restore from";
    189                                 }
    190                                 var settings = t.fullscreenSettings;
    191 
    192                                 transferState(fullscreenEditor, ed, fullscreenEditor.selection.getBookmark());
    193 
    194                                 // cleanup only required if window mode isn't used
    195                                 if (!ed.getParam('fullscreen_new_window')) {
    196                                         tinymce.dom.Event.remove(DOM.win, 'resize', t.resizeFunc);
    197                                         delete t.resizeFunc;
    198 
    199                                         DOM.remove('mce_fullscreen_container');
    200 
    201                                         DOM.doc.documentElement.style.overflow = settings.fullscreen_html_overflow;
    202                                         DOM.setStyle(DOM.doc.body, 'overflow', settings.fullscreen_overflow);
    203                                         DOM.win.scrollTo(settings.fullscreen_scrollx, settings.fullscreen_scrolly);
    204                                 }
    205                                 tinyMCE.settings = tinyMCE.oldSettings; // Restore old settings
    206 
    207                                 // clear variables
    208                                 delete tinyMCE.oldSettings;
    209                                 delete t.fullscreenEditor;
    210                                 delete t.fullscreenElement;
    211                                 delete t.fullscreenSettings;
    212 
    213                                 // allow the fullscreen editor to be removed before restoring focus and selection
    214                                 DOM.win.setTimeout(function() {
    215                                         ed.selection.moveToBookmark(bookmark);
    216                                         ed.focus();
    217                                 }, 10);
    218                         };
    219                 },
    220 
    221                 getInfo : function() {
    222                         return {
    223                                 longname : 'Fullscreen',
    224                                 author : 'Moxiecode Systems AB',
    225                                 authorurl : 'http://tinymce.moxiecode.com',
    226                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen',
    227                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
    228                         };
     95        editor.on('remove', function() {
     96                if (resizeHandler) {
     97                        DOM.unbind(window, 'resize', resizeHandler);
    22998                }
    23099        });
    231100
    232         // Register plugin
    233         tinymce.PluginManager.add('fullscreen', tinymce.plugins.FullScreenPlugin);
    234 })();
     101        editor.addCommand('mceFullScreen', toggleFullscreen);
     102
     103        editor.addMenuItem('fullscreen', {
     104                text: 'Fullscreen',
     105                shortcut: 'Ctrl+Alt+F',
     106                selectable: true,
     107                onClick: toggleFullscreen,
     108                onPostRender: function() {
     109                        var self = this;
     110
     111                        editor.on('FullscreenStateChanged', function(e) {
     112                                self.active(e.state);
     113                        });
     114                },
     115                context: 'view'
     116        });
     117
     118        editor.addButton('fullscreen', {
     119                tooltip: 'Fullscreen',
     120                shortcut: 'Ctrl+Alt+F',
     121                onClick: toggleFullscreen,
     122                onPostRender: function() {
     123                        var self = this;
     124
     125                        editor.on('FullscreenStateChanged', function(e) {
     126                                self.active(e.state);
     127                        });
     128                }
     129        });
     130
     131        return {
     132                isFullscreen: function() {
     133                        return fullscreenState;
     134                }
     135        };
     136});
  • trunk/src/wp-includes/js/tinymce/plugins/fullscreen/plugin.min.js

    r26862 r26876  
    1 (function(){var b=tinymce.DOM;var a=function(d,f,e){var c=function(g){var i=d.controlManager.get(g);var h=f.controlManager.get(g);if(i&&h){h.displayColor(i.value)}};c("forecolor");c("backcolor");f.setContent(d.getContent({format:"raw"}),{format:"raw"});f.selection.moveToBookmark(e);if(d.plugins.spellchecker&&f.plugins.spellchecker){f.plugins.spellchecker.setLanguage(d.plugins.spellchecker.selectedLang)}};tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(i,c){var l=this,m={},k=b.doc.documentElement,d,o,h,g,f,e,j;i.addCommand("mceFullScreen",function(){var q,r;if(i.getParam("fullscreen_is_enabled")){if(i.getParam("fullscreen_new_window")){closeFullscreen()}else{b.win.setTimeout(function(){var t=i;var s=tinyMCE.get(t.getParam("fullscreen_editor_id"));s.plugins.fullscreen.saveState(t);tinyMCE.remove(t)},10)}return}if(i.getParam("fullscreen_new_window")){l.fullscreenSettings={bookmark:i.selection.getBookmark()};q=b.win.open(c+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{q.resizeTo(screen.availWidth,screen.availHeight)}catch(p){}}else{o=b.getStyle(b.doc.body,"overflow",1)||"auto";h=b.getStyle(k,"overflow",1);d=b.getViewPort();g=d.x;f=d.y;if(tinymce.isOpera&&o=="visible"){o="auto"}if(tinymce.isIE&&o=="scroll"){o="auto"}if(tinymce.isIE&&(h=="visible"||h=="scroll")){h="auto"}if(o=="0px"){o=""}b.setStyle(b.doc.body,"overflow","hidden");k.style.overflow="hidden";d=b.getViewPort();b.win.scrollTo(0,0);if(tinymce.isIE){d.h-=1}if(tinymce.isIE6||document.compatMode=="BackCompat"){e="absolute;top:"+d.y}else{e="fixed;top:0"}n=b.add(b.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+e+";left:0;width:"+d.w+"px;height:"+d.h+"px;z-index:200000;"});b.add(n,"div",{id:"mce_fullscreen"});tinymce.each(i.settings,function(s,t){m[t]=s});m.id="mce_fullscreen";m.width=n.clientWidth;m.height=n.clientHeight-15;m.fullscreen_is_enabled=true;m.fullscreen_editor_id=i.id;m.theme_advanced_resizing=false;m.save_onsavecallback=function(){i.setContent(tinyMCE.get(m.id).getContent());i.execCommand("mceSave")};tinymce.each(i.getParam("fullscreen_settings"),function(t,s){m[s]=t});l.fullscreenSettings={bookmark:i.selection.getBookmark(),fullscreen_overflow:o,fullscreen_html_overflow:h,fullscreen_scrollx:g,fullscreen_scrolly:f};if(m.theme_advanced_toolbar_location==="external"){m.theme_advanced_toolbar_location="top"}tinyMCE.oldSettings=tinyMCE.settings;l.fullscreenEditor=new tinymce.Editor("mce_fullscreen",m);l.fullscreenEditor.onInit.add(function(){l.loadState(l.fullscreenEditor)});l.fullscreenEditor.render();l.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");l.fullscreenElement.update();l.resizeFunc=tinymce.dom.Event.add(b.win,"resize",function(){var v=tinymce.DOM.getViewPort(),t=l.fullscreenEditor,s,u;s=t.dom.getSize(t.getContainer().getElementsByTagName("table")[0]);u=t.dom.getSize(t.getContainer().getElementsByTagName("iframe")[0]);t.theme.resizeTo(v.w-s.w+u.w,v.h-s.h+u.h)})}});i.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});i.onNodeChange.add(function(q,p){p.setActive("fullscreen",q.getParam("fullscreen_is_enabled"))});l.loadState=function(p){if(!(p&&l.fullscreenSettings)){throw"No fullscreen editor to load to"}a(i,p,l.fullscreenSettings.bookmark);p.focus()};l.saveState=function(q){if(!(q&&l.fullscreenSettings)){throw"No fullscreen editor to restore from"}var p=l.fullscreenSettings;a(q,i,q.selection.getBookmark());if(!i.getParam("fullscreen_new_window")){tinymce.dom.Event.remove(b.win,"resize",l.resizeFunc);delete l.resizeFunc;b.remove("mce_fullscreen_container");b.doc.documentElement.style.overflow=p.fullscreen_html_overflow;b.setStyle(b.doc.body,"overflow",p.fullscreen_overflow);b.win.scrollTo(p.fullscreen_scrollx,p.fullscreen_scrolly)}tinyMCE.settings=tinyMCE.oldSettings;delete tinyMCE.oldSettings;delete l.fullscreenEditor;delete l.fullscreenElement;delete l.fullscreenSettings;b.win.setTimeout(function(){i.selection.moveToBookmark(j);i.focus()},10)}},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})();
     1tinymce.PluginManager.add("fullscreen",function(e){function t(){var e,t,n=window,i=document,l=i.body;return l.offsetWidth&&(e=l.offsetWidth,t=l.offsetHeight),n.innerWidth&&n.innerHeight&&(e=n.innerWidth,t=n.innerHeight),{w:e,h:t}}function n(){function n(){d.setStyle(a,"height",t().h-(h.clientHeight-a.clientHeight))}var u,h,a,f,m=document.body,g=document.documentElement;s=!s,h=e.getContainer(),u=h.style,a=e.getContentAreaContainer().firstChild,f=a.style,s?(i=f.width,l=f.height,f.width=f.height="100%",c=u.width,o=u.height,u.width=u.height="",d.addClass(m,"mce-fullscreen"),d.addClass(g,"mce-fullscreen"),d.addClass(h,"mce-fullscreen"),d.bind(window,"resize",n),n(),r=n):(f.width=i,f.height=l,c&&(u.width=c),o&&(u.height=o),d.removeClass(m,"mce-fullscreen"),d.removeClass(g,"mce-fullscreen"),d.removeClass(h,"mce-fullscreen"),d.unbind(window,"resize",r)),e.fire("FullscreenStateChanged",{state:s})}var i,l,r,c,o,s=!1,d=tinymce.DOM;return e.settings.inline?void 0:(e.on("init",function(){e.addShortcut("Ctrl+Alt+F","",n)}),e.on("remove",function(){r&&d.unbind(window,"resize",r)}),e.addCommand("mceFullScreen",n),e.addMenuItem("fullscreen",{text:"Fullscreen",shortcut:"Ctrl+Alt+F",selectable:!0,onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})},context:"view"}),e.addButton("fullscreen",{tooltip:"Fullscreen",shortcut:"Ctrl+Alt+F",onClick:n,onPostRender:function(){var t=this;e.on("FullscreenStateChanged",function(e){t.active(e.state)})}}),{isFullscreen:function(){return s}})});
  • trunk/src/wp-includes/js/tinymce/plugins/media/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
     2 * plugin.js
    33 *
    4  * Copyright 2009, Moxiecode Systems AB
     4 * Copyright, Moxiecode Systems AB
    55 * Released under LGPL License.
    66 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     7 * License: http://www.tinymce.com/license
     8 * Contributing: http://www.tinymce.com/contributing
    99 */
    1010
    11 (function() {
    12         var rootAttributes = tinymce.explode('id,name,width,height,style,align,class,hspace,vspace,bgcolor,type'), excludedAttrs = tinymce.makeMap(rootAttributes.join(',')), Node = tinymce.html.Node,
    13                 mediaTypes, scriptRegExp, JSON = tinymce.util.JSON, mimeTypes;
    14 
    15         // Media types supported by this plugin
    16         mediaTypes = [
    17                 // Type, clsid:s, mime types, codebase
    18                 ["Flash", "d27cdb6e-ae6d-11cf-96b8-444553540000", "application/x-shockwave-flash", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
    19                 ["ShockWave", "166b1bca-3f9c-11cf-8075-444553540000", "application/x-director", "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],
    20                 ["WindowsMedia", "6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a", "application/x-mplayer2", "http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],
    21                 ["QuickTime", "02bf25d5-8c17-4b23-bc80-d3488abddc6b", "video/quicktime", "http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],
    22                 ["RealMedia", "cfcdaa03-8be4-11cf-b84b-0020afbbccfa", "audio/x-pn-realaudio-plugin", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
    23                 ["Java", "8ad9c840-044e-11d1-b3e9-00805f499d93", "application/x-java-applet", "http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],
    24                 ["Silverlight", "dfeaf541-f3e1-4c24-acac-99c30715084a", "application/x-silverlight-2"],
    25                 ["Iframe"],
    26                 ["Video"],
    27                 ["EmbeddedAudio"],
    28                 ["Audio"]
     11/*jshint maxlen:255 */
     12/*global tinymce:true */
     13
     14tinymce.PluginManager.add('media', function(editor, url) {
     15        var urlPatterns = [
     16                {regex: /youtu\.be\/([a-z1-9.-_]+)/, type: 'iframe', w: 425, h: 350, url: 'http://www.youtube.com/embed/$1'},
     17                {regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 425, h: 350, url: 'http://www.youtube.com/embed/$2'},
     18                {regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: 'http://player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc'},
     19                {regex: /maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/, type: 'iframe', w: 425, h: 350, url: 'http://maps.google.com/maps/ms?msid=$2&output=embed"'}
    2920        ];
    3021
    31         function normalizeSize(size) {
    32                 return typeof(size) == "string" ? size.replace(/[^0-9%]/g, '') : size;
    33         }
    34 
    35         function toArray(obj) {
    36                 var undef, out, i;
    37 
    38                 if (obj && !obj.splice) {
    39                         out = [];
    40 
    41                         for (i = 0; true; i++) {
    42                                 if (obj[i])
    43                                         out[i] = obj[i];
    44                                 else
     22        function guessMime(url) {
     23                if (url.indexOf('.mp3') != -1) {
     24                        return 'audio/mpeg';
     25                }
     26
     27                if (url.indexOf('.wav') != -1) {
     28                        return 'audio/wav';
     29                }
     30
     31                if (url.indexOf('.mp4') != -1) {
     32                        return 'video/mp4';
     33                }
     34
     35                if (url.indexOf('.webm') != -1) {
     36                        return 'video/webm';
     37                }
     38
     39                if (url.indexOf('.ogg') != -1) {
     40                        return 'video/ogg';
     41                }
     42
     43                if (url.indexOf('.swf') != -1) {
     44                        return 'application/x-shockwave-flash';
     45                }
     46
     47                return '';
     48        }
     49
     50        function getVideoScriptMatch(src) {
     51                var prefixes = editor.settings.media_scripts;
     52
     53                if (prefixes) {
     54                        for (var i = 0; i < prefixes.length; i++) {
     55                                if (src.indexOf(prefixes[i].filter) !== -1) {
     56                                        return prefixes[i];
     57                                }
     58                        }
     59                }
     60        }
     61
     62        function showDialog() {
     63                var win, width, height, data;
     64
     65                function recalcSize(e) {
     66                        var widthCtrl, heightCtrl, newWidth, newHeight;
     67
     68                        widthCtrl = win.find('#width')[0];
     69                        heightCtrl = win.find('#height')[0];
     70
     71                        newWidth = widthCtrl.value();
     72                        newHeight = heightCtrl.value();
     73
     74                        if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
     75                                if (e.control == widthCtrl) {
     76                                        newHeight = Math.round((newWidth / width) * newHeight);
     77                                        heightCtrl.value(newHeight);
     78                                } else {
     79                                        newWidth = Math.round((newHeight / height) * newWidth);
     80                                        widthCtrl.value(newWidth);
     81                                }
     82                        }
     83
     84                        width = newWidth;
     85                        height = newHeight;
     86                }
     87
     88                data = getData(editor.selection.getNode());
     89                width = data.width;
     90                height = data.height;
     91
     92                win = editor.windowManager.open({
     93                        title: 'Insert/edit video',
     94                        data: data,
     95                        bodyType: 'tabpanel',
     96                        body: [
     97                                {
     98                                        title: 'General',
     99                                        type: "form",
     100                                        onShowTab: function() {
     101                                                data = htmlToData(this.next().find('#embed').value());
     102                                                this.fromJSON(data);
     103                                        },
     104                                        items: [
     105                                                {name: 'source1', type: 'filepicker', filetype: 'media', size: 40, autofocus: true, label: 'Source'},
     106                                                {name: 'source2', type: 'filepicker', filetype: 'media', size: 40, label: 'Alternative source'},
     107                                                {name: 'poster', type: 'filepicker', filetype: 'image', size: 40, label: 'Poster'},
     108                                                {
     109                                                        type: 'container',
     110                                                        label: 'Dimensions',
     111                                                        layout: 'flex',
     112                                                        align: 'center',
     113                                                        spacing: 5,
     114                                                        items: [
     115                                                                {name: 'width', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
     116                                                                {type: 'label', text: 'x'},
     117                                                                {name: 'height', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
     118                                                                {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
     119                                                        ]
     120                                                }
     121                                        ]
     122                                },
     123
     124                                {
     125                                        title: 'Embed',
     126                                        type: "panel",
     127                                        layout: 'flex',
     128                                        direction: 'column',
     129                                        align: 'stretch',
     130                                        padding: 10,
     131                                        spacing: 10,
     132                                        onShowTab: function() {
     133                                                this.find('#embed').value(dataToHtml(this.parent().toJSON()));
     134                                        },
     135                                        items: [
     136                                                {
     137                                                        type: 'label',
     138                                                        text: 'Paste your embed code below:'
     139                                                },
     140                                                {
     141                                                        type: 'textbox',
     142                                                        flex: 1,
     143                                                        name: 'embed',
     144                                                        value: getSource(),
     145                                                        multiline: true,
     146                                                        label: 'Source'
     147                                                }
     148                                        ]
     149                                }
     150                        ],
     151                        onSubmit: function() {
     152                                editor.insertContent(dataToHtml(this.toJSON()));
     153                        }
     154                });
     155        }
     156
     157        function getSource() {
     158                var elm = editor.selection.getNode();
     159
     160                if (elm.getAttribute('data-mce-object')) {
     161                        return editor.selection.getContent();
     162                }
     163        }
     164
     165        function dataToHtml(data) {
     166                var html = '';
     167
     168                if (!data.source1) {
     169                        tinymce.extend(data, htmlToData(data.embed));
     170                        if (!data.source1) {
     171                                return '';
     172                        }
     173                }
     174
     175                data.source1 = editor.convertURL(data.source1, "source");
     176                data.source2 = editor.convertURL(data.source2, "source");
     177                data.source1mime = guessMime(data.source1);
     178                data.source2mime = guessMime(data.source2);
     179                data.poster = editor.convertURL(data.poster, "poster");
     180                data.flashPlayerUrl = editor.convertURL(url + '/moxieplayer.swf', "movie");
     181
     182                if (data.embed) {
     183                        html = updateHtml(data.embed, data, true);
     184                } else {
     185                        tinymce.each(urlPatterns, function(pattern) {
     186                                var match, i, url;
     187
     188                                if ((match = pattern.regex.exec(data.source1))) {
     189                                        url = pattern.url;
     190
     191                                        for (i = 0; match[i]; i++) {
     192                                                /*jshint loopfunc:true*/
     193                                                url = url.replace('$' + i, function() {
     194                                                        return match[i];
     195                                                });
     196                                        }
     197
     198                                        data.source1 = url;
     199                                        data.type = pattern.type;
     200                                        data.width = pattern.w;
     201                                        data.height = pattern.h;
     202                                }
     203                        });
     204
     205                        var videoScript = getVideoScriptMatch(data.source1);
     206                        if (videoScript) {
     207                                data.type = 'script';
     208                                data.width = videoScript.width;
     209                                data.height = videoScript.height;
     210                        }
     211
     212                        data.width = data.width || 300;
     213                        data.height = data.height || 150;
     214
     215                        tinymce.each(data, function(value, key) {
     216                                data[key] = editor.dom.encode(value);
     217                        });
     218
     219                        if (data.type == "iframe") {
     220                                html += '<iframe src="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '"></iframe>';
     221                        } else if (data.source1mime == "application/x-shockwave-flash") {
     222                                html += '<object data="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '" type="application/x-shockwave-flash">';
     223
     224                                if (data.poster) {
     225                                        html += '<img src="' + data.poster + '" width="' + data.width + '" height="' + data.height + '" />';
     226                                }
     227
     228                                html += '</object>';
     229                        } else if (data.source1mime.indexOf('audio') != -1) {
     230                                if (editor.settings.audio_template_callback) {
     231                                        html = editor.settings.audio_template_callback(data);
     232                                } else {
     233                                        html += (
     234                                                '<audio controls="controls" src="' + data.source1 + '">' +
     235                                                        (data.source2 ? '\n<source src="' + data.source2 + '"' + (data.source2mime ? ' type="' + data.source2mime + '"' : '') + ' />\n' : '') +
     236                                                '</audio>'
     237                                        );
     238                                }
     239                        } else if (data.type == "script") {
     240                                html += '<script src="' + data.source1 + '"></script>';
     241                        } else {
     242                                if (editor.settings.video_template_callback) {
     243                                        html = editor.settings.video_template_callback(data);
     244                                } else {
     245                                        html = (
     246                                                '<video width="' + data.width + '" height="' + data.height + '"' + (data.poster ? ' poster="' + data.poster + '"' : '') + ' controls="controls">\n' +
     247                                                        '<source src="' + data.source1 + '"' + (data.source1mime ? ' type="' + data.source1mime + '"' : '') + ' />\n' +
     248                                                        (data.source2 ? '<source src="' + data.source2 + '"' + (data.source2mime ? ' type="' + data.source2mime + '"' : '') + ' />\n' : '') +
     249                                                '</video>'
     250                                        );
     251                                }
     252                        }
     253                }
     254
     255                return html;
     256        }
     257
     258        function htmlToData(html) {
     259                var data = {};
     260
     261                new tinymce.html.SaxParser({
     262                        validate: false,
     263                        allow_conditional_comments: true,
     264                        special: 'script,noscript',
     265                        start: function(name, attrs) {
     266                                if (!data.source1 && name == "param") {
     267                                        data.source1 = attrs.map.movie;
     268                                }
     269
     270                                if (name == "iframe" || name == "object" || name == "embed" || name == "video" || name == "audio") {
     271                                        if (!data.type) {
     272                                                data.type = name;
     273                                        }
     274
     275                                        data = tinymce.extend(attrs.map, data);
     276                                }
     277
     278                                if (name == "script") {
     279                                        var videoScript = getVideoScriptMatch(attrs.map.src);
     280                                        if (!videoScript) {
     281                                                return;
     282                                        }
     283
     284                                        data = {
     285                                                type: "script",
     286                                                source1: attrs.map.src,
     287                                                width: videoScript.width,
     288                                                height: videoScript.height
     289                                        };
     290                                }
     291
     292                                if (name == "source") {
     293                                        if (!data.source1) {
     294                                                data.source1 = attrs.map.src;
     295                                        } else if (!data.source2) {
     296                                                data.source2 = attrs.map.src;
     297                                        }
     298                                }
     299
     300                                if (name == "img" && !data.poster) {
     301                                        data.poster = attrs.map.src;
     302                                }
     303                        }
     304                }).parse(html);
     305
     306                data.source1 = data.source1 || data.src || data.data;
     307                data.source2 = data.source2 || '';
     308                data.poster = data.poster || '';
     309
     310                return data;
     311        }
     312
     313        function getData(element) {
     314                if (element.getAttribute('data-mce-object')) {
     315                        return htmlToData(editor.serializer.serialize(element, {selection: true}));
     316                }
     317
     318                return {};
     319        }
     320
     321        function updateHtml(html, data, updateAll) {
     322                var writer = new tinymce.html.Writer();
     323                var sourceCount = 0, hasImage;
     324
     325                function setAttributes(attrs, updatedAttrs) {
     326                        var name, i, value, attr;
     327
     328                        for (name in updatedAttrs) {
     329                                value = "" + updatedAttrs[name];
     330
     331                                if (attrs.map[name]) {
     332                                        i = attrs.length;
     333                                        while (i--) {
     334                                                attr = attrs[i];
     335
     336                                                if (attr.name == name) {
     337                                                        if (value) {
     338                                                                attrs.map[name] = value;
     339                                                                attr.value = value;
     340                                                        } else {
     341                                                                delete attrs.map[name];
     342                                                                attrs.splice(i, 1);
     343                                                        }
     344                                                }
     345                                        }
     346                                } else if (value) {
     347                                        attrs.push({
     348                                                name: name,
     349                                                value: value
     350                                        });
     351
     352                                        attrs.map[name] = value;
     353                                }
     354                        }
     355                }
     356
     357                new tinymce.html.SaxParser({
     358                        validate: false,
     359                        allow_conditional_comments: true,
     360                        special: 'script,noscript',
     361
     362                        comment: function(text) {
     363                                writer.comment(text);
     364                        },
     365
     366                        cdata: function(text) {
     367                                writer.cdata(text);
     368                        },
     369
     370                        text: function(text, raw) {
     371                                writer.text(text, raw);
     372                        },
     373
     374                        start: function(name, attrs, empty) {
     375                                switch (name) {
     376                                        case "video":
     377                                        case "object":
     378                                        case "embed":
     379                                        case "img":
     380                                        case "iframe":
     381                                                setAttributes(attrs, {
     382                                                        width: data.width,
     383                                                        height: data.height
     384                                                });
    45385                                        break;
    46                         }
    47 
    48                         return out;
    49                 }
    50 
    51                 return obj;
    52         };
    53 
    54         tinymce.create('tinymce.plugins.MediaPlugin', {
    55                 init : function(ed, url) {
    56                         var self = this, lookup = {}, i, y, item, name;
    57 
    58                         function isMediaImg(node) {
    59                                 return node && node.nodeName === 'IMG' && ed.dom.hasClass(node, 'mceItemMedia');
    60                         };
    61 
    62                         self.editor = ed;
    63                         self.url = url;
    64 
    65                         // Parse media types into a lookup table
    66                         scriptRegExp = '';
    67                         for (i = 0; i < mediaTypes.length; i++) {
    68                                 name = mediaTypes[i][0];
    69 
    70                                 item = {
    71                                         name : name,
    72                                         clsids : tinymce.explode(mediaTypes[i][1] || ''),
    73                                         mimes : tinymce.explode(mediaTypes[i][2] || ''),
    74                                         codebase : mediaTypes[i][3]
    75                                 };
    76 
    77                                 for (y = 0; y < item.clsids.length; y++)
    78                                         lookup['clsid:' + item.clsids[y]] = item;
    79 
    80                                 for (y = 0; y < item.mimes.length; y++)
    81                                         lookup[item.mimes[y]] = item;
    82 
    83                                 lookup['mceItem' + name] = item;
    84                                 lookup[name.toLowerCase()] = item;
    85 
    86                                 scriptRegExp += (scriptRegExp ? '|' : '') + name;
    87                         }
    88 
    89                         // Handle the media_types setting
    90                         tinymce.each(ed.getParam("media_types",
    91                                 "video=mp4,m4v,ogv,webm;" +
    92                                 "silverlight=xap;" +
    93                                 "flash=swf,flv;" +
    94                                 "shockwave=dcr;" +
    95                                 "quicktime=mov,qt,mpg,mpeg;" +
    96                                 "shockwave=dcr;" +
    97                                 "windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;" +
    98                                 "realmedia=rm,ra,ram;" +
    99                                 "java=jar;" +
    100                                 "audio=mp3,ogg"
    101                         ).split(';'), function(item) {
    102                                 var i, extensions, type;
    103 
    104                                 item = item.split(/=/);
    105                                 extensions = tinymce.explode(item[1].toLowerCase());
    106                                 for (i = 0; i < extensions.length; i++) {
    107                                         type = lookup[item[0].toLowerCase()];
    108 
    109                                         if (type)
    110                                                 lookup[extensions[i]] = type;
    111                                 }
    112                         });
    113 
    114                         scriptRegExp = new RegExp('write(' + scriptRegExp + ')\\(([^)]+)\\)');
    115                         self.lookup = lookup;
    116 
    117                         ed.onPreInit.add(function() {
    118                                 // Allow video elements
    119                                 ed.schema.addValidElements('object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]');
    120 
    121                                 // Convert video elements to image placeholder
    122                                 ed.parser.addNodeFilter('object,embed,video,audio,script,iframe', function(nodes) {
    123                                         var i = nodes.length;
    124 
    125                                         while (i--)
    126                                                 self.objectToImg(nodes[i]);
     386                                }
     387
     388                                if (updateAll) {
     389                                        switch (name) {
     390                                                case "video":
     391                                                        setAttributes(attrs, {
     392                                                                poster: data.poster,
     393                                                                src: ""
     394                                                        });
     395
     396                                                        if (data.source2) {
     397                                                                setAttributes(attrs, {
     398                                                                        src: ""
     399                                                                });
     400                                                        }
     401                                                break;
     402
     403                                                case "iframe":
     404                                                        setAttributes(attrs, {
     405                                                                src: data.source1
     406                                                        });
     407                                                break;
     408
     409                                                case "source":
     410                                                        sourceCount++;
     411
     412                                                        if (sourceCount <= 2) {
     413                                                                setAttributes(attrs, {
     414                                                                        src: data["source" + sourceCount],
     415                                                                        type: data["source" + sourceCount + "mime"]
     416                                                                });
     417
     418                                                                if (!data["source" + sourceCount]) {
     419                                                                        return;
     420                                                                }
     421                                                        }
     422                                                break;
     423
     424                                                case "img":
     425                                                        if (!data.poster) {
     426                                                                return;
     427                                                        }
     428
     429                                                        hasImage = true;
     430                                                        break;
     431                                        }
     432                                }
     433
     434                                writer.start(name, attrs, empty);
     435                        },
     436
     437                        end: function(name) {
     438                                if (name == "video" && updateAll) {
     439                                        for (var index = 1; index <= 2; index++) {
     440                                                if (data["source" + index]) {
     441                                                        var attrs = [];
     442                                                        attrs.map = {};
     443
     444                                                        if (sourceCount < index) {
     445                                                                setAttributes(attrs, {
     446                                                                        src: data["source" + index],
     447                                                                        type: data["source" + index + "mime"]
     448                                                                });
     449
     450                                                                writer.start("source", attrs, true);
     451                                                        }
     452                                                }
     453                                        }
     454                                }
     455
     456                                if (data.poster && name == "object" && updateAll && !hasImage) {
     457                                        var imgAttrs = [];
     458                                        imgAttrs.map = {};
     459
     460                                        setAttributes(imgAttrs, {
     461                                                src: data.poster,
     462                                                width: data.width,
     463                                                height: data.height
     464                                        });
     465
     466                                        writer.start("img", imgAttrs, true);
     467                                }
     468
     469                                writer.end(name);
     470                        }
     471                }, new tinymce.html.Schema({})).parse(html);
     472
     473                return writer.getContent();
     474        }
     475
     476        editor.on('ResolveName', function(e) {
     477                var name;
     478
     479                if (e.target.nodeType == 1 && (name = e.target.getAttribute("data-mce-object"))) {
     480                        e.name = name;
     481                }
     482        });
     483
     484        editor.on('preInit', function() {
     485                // Make sure that any messy HTML is retained inside these
     486                var specialElements = editor.schema.getSpecialElements();
     487                tinymce.each('video audio iframe object'.split(' '), function(name) {
     488                        specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
     489                });
     490
     491                // Allow elements
     492                editor.schema.addValidElements('object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]');
     493
     494                // Set allowFullscreen attribs as boolean
     495                var boolAttrs = editor.schema.getBoolAttrs();
     496                tinymce.each('webkitallowfullscreen mozallowfullscreen allowfullscreen'.split(' '), function(name) {
     497                        boolAttrs[name] = {};
     498                });
     499
     500                // Converts iframe, video etc into placeholder images
     501                editor.parser.addNodeFilter('iframe,video,audio,object,embed,script', function(nodes, name) {
     502                        var i = nodes.length, ai, node, placeHolder, attrName, attrValue, attribs, innerHtml;
     503                        var videoScript;
     504
     505                        while (i--) {
     506                                node = nodes[i];
     507
     508                                if (node.name == 'script') {
     509                                        videoScript = getVideoScriptMatch(node.attr('src'));
     510                                        if (!videoScript) {
     511                                                continue;
     512                                        }
     513                                }
     514
     515                                placeHolder = new tinymce.html.Node('img', 1);
     516                                placeHolder.shortEnded = true;
     517
     518                                if (videoScript) {
     519                                        if (videoScript.width) {
     520                                                node.attr('width', videoScript.width.toString());
     521                                        }
     522
     523                                        if (videoScript.height) {
     524                                                node.attr('height', videoScript.height.toString());
     525                                        }
     526                                }
     527
     528                                // Prefix all attributes except width, height and style since we
     529                                // will add these to the placeholder
     530                                attribs = node.attributes;
     531                                ai = attribs.length;
     532                                while (ai--) {
     533                                        attrName = attribs[ai].name;
     534                                        attrValue = attribs[ai].value;
     535
     536                                        if (attrName !== "width" && attrName !== "height" && attrName !== "style") {
     537                                                if (attrName == "data" || attrName == "src") {
     538                                                        attrValue = editor.convertURL(attrValue, attrName);
     539                                                }
     540
     541                                                placeHolder.attr('data-mce-p-' + attrName, attrValue);
     542                                        }
     543                                }
     544
     545                                // Place the inner HTML contents inside an escaped attribute
     546                                // This enables us to copy/paste the fake object
     547                                innerHtml = node.firstChild && node.firstChild.value;
     548                                if (innerHtml) {
     549                                        placeHolder.attr("data-mce-html", escape(innerHtml));
     550                                        placeHolder.firstChild = null;
     551                                }
     552
     553                                placeHolder.attr({
     554                                        width: node.attr('width') || "300",
     555                                        height: node.attr('height') || (name == "audio" ? "30" : "150"),
     556                                        style: node.attr('style'),
     557                                        src: tinymce.Env.transparentSrc,
     558                                        "data-mce-object": name,
     559                                        "class": "mce-object mce-object-" + name
    127560                                });
    128561
    129                                 // Convert image placeholders to video elements
    130                                 ed.serializer.addNodeFilter('img', function(nodes, name, args) {
    131                                         var i = nodes.length, node;
    132 
    133                                         while (i--) {
    134                                                 node = nodes[i];
    135                                                 if ((node.attr('class') || '').indexOf('mceItemMedia') !== -1)
    136                                                         self.imgToObject(node, args);
    137                                         }
     562                                node.replace(placeHolder);
     563                        }
     564                });
     565
     566                // Replaces placeholder images with real elements for video, object, iframe etc
     567                editor.serializer.addAttributeFilter('data-mce-object', function(nodes, name) {
     568                        var i = nodes.length, node, realElm, ai, attribs, innerHtml, innerNode, realElmName;
     569
     570                        while (i--) {
     571                                node = nodes[i];
     572                                realElmName = node.attr(name);
     573                                realElm = new tinymce.html.Node(realElmName, 1);
     574
     575                                // Add width/height to everything but audio
     576                                if (realElmName != "audio" && realElmName != "script") {
     577                                        realElm.attr({
     578                                                width: node.attr('width'),
     579                                                height: node.attr('height')
     580                                        });
     581                                }
     582
     583                                realElm.attr({
     584                                        style: node.attr('style')
    138585                                });
    139                         });
    140 
    141                         ed.onInit.add(function() {
    142                                 // Display "media" instead of "img" in element path
    143                                 if (ed.theme && ed.theme.onResolveName) {
    144                                         ed.theme.onResolveName.add(function(theme, path_object) {
    145                                                 if (path_object.name === 'img' && ed.dom.hasClass(path_object.node, 'mceItemMedia'))
    146                                                         path_object.name = 'media';
    147                                         });
    148                                 }
    149 
    150                                 // Add contect menu if it's loaded
    151                                 if (ed && ed.plugins.contextmenu) {
    152                                         ed.plugins.contextmenu.onContextMenu.add(function(plugin, menu, element) {
    153                                                 if (element.nodeName === 'IMG' && element.className.indexOf('mceItemMedia') !== -1)
    154                                                         menu.add({title : 'media.edit', icon : 'media', cmd : 'mceMedia'});
    155                                         });
    156                                 }
    157                         });
    158 
    159                         // Register commands
    160                         ed.addCommand('mceMedia', function() {
    161                                 var data, img;
    162 
    163                                 img = ed.selection.getNode();
    164                                 if (isMediaImg(img)) {
    165                                         data = ed.dom.getAttrib(img, 'data-mce-json');
    166                                         if (data) {
    167                                                 data = JSON.parse(data);
    168 
    169                                                 // Add some extra properties to the data object
    170                                                 tinymce.each(rootAttributes, function(name) {
    171                                                         var value = ed.dom.getAttrib(img, name);
    172 
    173                                                         if (value)
    174                                                                 data[name] = value;
    175                                                 });
    176 
    177                                                 data.type = self.getType(img.className).name.toLowerCase();
    178                                         }
    179                                 }
    180 
    181                                 if (!data) {
    182                                         data = {
    183                                                 type : 'flash',
    184                                                 video: {sources:[]},
    185                                                 params: {}
    186                                         };
    187                                 }
    188 
    189                                 ed.windowManager.open({
    190                                         file : url + '/media.htm',
    191                                         width : 430 + parseInt(ed.getLang('media.delta_width', 0)),
    192                                         height : 500 + parseInt(ed.getLang('media.delta_height', 0)),
    193                                         inline : 1
    194                                 }, {
    195                                         plugin_url : url,
    196                                         data : data
    197                                 });
    198                         });
    199 
    200                         // Register buttons
    201                         ed.addButton('media', {title : 'media.desc', cmd : 'mceMedia'});
    202 
    203                         // Update media selection status
    204                         ed.onNodeChange.add(function(ed, cm, node) {
    205                                 cm.setActive('media', isMediaImg(node));
    206                         });
    207                 },
    208 
    209                 convertUrl : function(url, force_absolute) {
    210                         var self = this, editor = self.editor, settings = editor.settings,
    211                                 urlConverter = settings.url_converter,
    212                                 urlConverterScope = settings.url_converter_scope || self;
    213 
    214                         if (!url)
    215                                 return url;
    216 
    217                         if (force_absolute)
    218                                 return editor.documentBaseURI.toAbsolute(url);
    219 
    220                         return urlConverter.call(urlConverterScope, url, 'src', 'object');
    221                 },
    222 
    223                 getInfo : function() {
    224                         return {
    225                                 longname : 'Media',
    226                                 author : 'Moxiecode Systems AB',
    227                                 authorurl : 'http://tinymce.moxiecode.com',
    228                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media',
    229                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
    230                         };
    231                 },
    232 
    233                 /**
    234                  * Converts the JSON data object to an img node.
    235                  */
    236                 dataToImg : function(data, force_absolute) {
    237                         var self = this, editor = self.editor, baseUri = editor.documentBaseURI, sources, attrs, img, i;
    238 
    239                         data.params.src = self.convertUrl(data.params.src, force_absolute);
    240 
    241                         attrs = data.video.attrs;
    242                         if (attrs)
    243                                 attrs.src = self.convertUrl(attrs.src, force_absolute);
    244 
    245                         if (attrs)
    246                                 attrs.poster = self.convertUrl(attrs.poster, force_absolute);
    247 
    248                         sources = toArray(data.video.sources);
    249                         if (sources) {
    250                                 for (i = 0; i < sources.length; i++)
    251                                         sources[i].src = self.convertUrl(sources[i].src, force_absolute);
    252                         }
    253 
    254                         img = self.editor.dom.create('img', {
    255                                 id : data.id,
    256                                 style : data.style,
    257                                 align : data.align,
    258                                 hspace : data.hspace,
    259                                 vspace : data.vspace,
    260                                 src : self.editor.theme.url + '/img/trans.gif',
    261                                 'class' : 'mceItemMedia mceItem' + self.getType(data.type).name,
    262                                 'data-mce-json' : JSON.serialize(data, "'")
    263                         });
    264 
    265                         img.width = data.width = normalizeSize(data.width || (data.type == 'audio' ? "300" : "320"));
    266                         img.height = data.height = normalizeSize(data.height || (data.type == 'audio' ? "32" : "240"));
    267 
    268                         return img;
    269                 },
    270 
    271                 /**
    272                  * Converts the JSON data object to a HTML string.
    273                  */
    274                 dataToHtml : function(data, force_absolute) {
    275                         return this.editor.serializer.serialize(this.dataToImg(data, force_absolute), {forced_root_block : '', force_absolute : force_absolute});
    276                 },
    277 
    278                 /**
    279                  * Converts the JSON data object to a HTML string.
    280                  */
    281                 htmlToData : function(html) {
    282                         var fragment, img, data;
    283 
    284                         data = {
    285                                 type : 'flash',
    286                                 video: {sources:[]},
    287                                 params: {}
    288                         };
    289 
    290                         fragment = this.editor.parser.parse(html);
    291                         img = fragment.getAll('img')[0];
    292 
    293                         if (img) {
    294                                 data = JSON.parse(img.attr('data-mce-json'));
    295                                 data.type = this.getType(img.attr('class')).name.toLowerCase();
    296 
    297                                 // Add some extra properties to the data object
    298                                 tinymce.each(rootAttributes, function(name) {
    299                                         var value = img.attr(name);
    300 
    301                                         if (value)
    302                                                 data[name] = value;
    303                                 });
    304                         }
    305 
    306                         return data;
    307                 },
    308 
    309                 /**
    310                  * Get type item by extension, class, clsid or mime type.
    311                  *
    312                  * @method getType
    313                  * @param {String} value Value to get type item by.
    314                  * @return {Object} Type item object or undefined.
    315                  */
    316                 getType : function(value) {
    317                         var i, values, typeItem;
    318 
    319                         // Find type by checking the classes
    320                         values = tinymce.explode(value, ' ');
    321                         for (i = 0; i < values.length; i++) {
    322                                 typeItem = this.lookup[values[i]];
    323 
    324                                 if (typeItem)
    325                                         return typeItem;
    326                         }
    327                 },
    328 
    329                 /**
    330                  * Converts a tinymce.html.Node image element to video/object/embed.
    331                  */
    332                 imgToObject : function(node, args) {
    333                         var self = this, editor = self.editor, video, object, embed, iframe, name, value, data,
    334                                 source, sources, params, param, typeItem, i, item, mp4Source, replacement,
    335                                 posterSrc, style, audio;
    336 
    337                         // Adds the flash player
    338                         function addPlayer(video_src, poster_src) {
    339                                 var baseUri, flashVars, flashVarsOutput, params, flashPlayer;
    340 
    341                                 flashPlayer = editor.getParam('flash_video_player_url', self.convertUrl(self.url + '/moxieplayer.swf'));
    342                                 if (flashPlayer) {
    343                                         baseUri = editor.documentBaseURI;
    344                                         data.params.src = flashPlayer;
    345 
    346                                         // Convert the movie url to absolute urls
    347                                         if (editor.getParam('flash_video_player_absvideourl', true)) {
    348                                                 video_src = baseUri.toAbsolute(video_src || '', true);
    349                                                 poster_src = baseUri.toAbsolute(poster_src || '', true);
    350                                         }
    351 
    352                                         // Generate flash vars
    353                                         flashVarsOutput = '';
    354                                         flashVars = editor.getParam('flash_video_player_flashvars', {url : '$url', poster : '$poster'});
    355                                         tinymce.each(flashVars, function(value, name) {
    356                                                 // Replace $url and $poster variables in flashvars value
    357                                                 value = value.replace(/\$url/, video_src || '');
    358                                                 value = value.replace(/\$poster/, poster_src || '');
    359 
    360                                                 if (value.length > 0)
    361                                                         flashVarsOutput += (flashVarsOutput ? '&' : '') + name + '=' + escape(value);
    362                                         });
    363 
    364                                         if (flashVarsOutput.length)
    365                                                 data.params.flashvars = flashVarsOutput;
    366 
    367                                         params = editor.getParam('flash_video_player_params', {
    368                                                 allowfullscreen: true,
    369                                                 allowscriptaccess: true
    370                                         });
    371 
    372                                         tinymce.each(params, function(value, name) {
    373                                                 data.params[name] = "" + value;
    374                                         });
    375                                 }
    376                         };
    377 
    378                         data = node.attr('data-mce-json');
    379                         if (!data)
    380                                 return;
    381 
    382                         data = JSON.parse(data);
    383                         typeItem = this.getType(node.attr('class'));
    384 
    385                         style = node.attr('data-mce-style');
    386                         if (!style) {
    387                                 style = node.attr('style');
    388 
    389                                 if (style)
    390                                         style = editor.dom.serializeStyle(editor.dom.parseStyle(style, 'img'));
    391                         }
    392 
    393                         // Use node width/height to override the data width/height when the placeholder is resized
    394                         data.width = node.attr('width') || data.width;
    395                         data.height = node.attr('height') || data.height;
    396 
    397                         // Handle iframe
    398                         if (typeItem.name === 'Iframe') {
    399                                 replacement = new Node('iframe', 1);
    400 
    401                                 tinymce.each(rootAttributes, function(name) {
    402                                         var value = node.attr(name);
    403 
    404                                         if (name == 'class' && value)
    405                                                 value = value.replace(/mceItem.+ ?/g, '');
    406 
    407                                         if (value && value.length > 0)
    408                                                 replacement.attr(name, value);
    409                                 });
    410 
    411                                 for (name in data.params)
    412                                         replacement.attr(name, data.params[name]);
    413 
    414                                 replacement.attr({
    415                                         style: style,
    416                                         src: data.params.src
    417                                 });
    418 
    419                                 node.replace(replacement);
    420 
    421                                 return;
    422                         }
    423 
    424                         // Handle scripts
    425                         if (this.editor.settings.media_use_script) {
    426                                 replacement = new Node('script', 1).attr('type', 'text/javascript');
    427 
    428                                 value = new Node('#text', 3);
    429                                 value.value = 'write' + typeItem.name + '(' + JSON.serialize(tinymce.extend(data.params, {
    430                                         width: node.attr('width'),
    431                                         height: node.attr('height')
    432                                 })) + ');';
    433 
    434                                 replacement.append(value);
    435                                 node.replace(replacement);
    436 
    437                                 return;
    438                         }
    439 
    440                         // Add HTML5 video element
    441                         if (typeItem.name === 'Video' && data.video.sources && data.video.sources[0]) {
    442                                 // Create new object element
    443                                 video = new Node('video', 1).attr(tinymce.extend({
    444                                         id : node.attr('id'),
    445                                         width: normalizeSize(node.attr('width')),
    446                                         height: normalizeSize(node.attr('height')),
    447                                         style : style
    448                                 }, data.video.attrs));
    449 
    450                                 // Get poster source and use that for flash fallback
    451                                 if (data.video.attrs)
    452                                         posterSrc = data.video.attrs.poster;
    453 
    454                                 sources = data.video.sources = toArray(data.video.sources);
    455                                 for (i = 0; i < sources.length; i++) {
    456                                         if (/\.mp4$/.test(sources[i].src))
    457                                                 mp4Source = sources[i].src;
    458                                 }
    459 
    460                                 if (!sources[0].type) {
    461                                         video.attr('src', sources[0].src);
    462                                         sources.splice(0, 1);
    463                                 }
    464 
    465                                 for (i = 0; i < sources.length; i++) {
    466                                         source = new Node('source', 1).attr(sources[i]);
    467                                         source.shortEnded = true;
    468                                         video.append(source);
    469                                 }
    470 
    471                                 // Create flash fallback for video if we have a mp4 source
    472                                 if (mp4Source) {
    473                                         addPlayer(mp4Source, posterSrc);
    474                                         typeItem = self.getType('flash');
    475                                 } else
    476                                         data.params.src = '';
    477                         }
    478 
    479                         // Add HTML5 audio element
    480                         if (typeItem.name === 'Audio' && data.video.sources && data.video.sources[0]) {
    481                                 // Create new object element
    482                                 audio = new Node('audio', 1).attr(tinymce.extend({
    483                                         id : node.attr('id'),
    484                                         width: normalizeSize(node.attr('width')),
    485                                         height: normalizeSize(node.attr('height')),
    486                                         style : style
    487                                 }, data.video.attrs));
    488 
    489                                 // Get poster source and use that for flash fallback
    490                                 if (data.video.attrs)
    491                                         posterSrc = data.video.attrs.poster;
    492 
    493                                 sources = data.video.sources = toArray(data.video.sources);
    494                                 if (!sources[0].type) {
    495                                         audio.attr('src', sources[0].src);
    496                                         sources.splice(0, 1);
    497                                 }
    498 
    499                                 for (i = 0; i < sources.length; i++) {
    500                                         source = new Node('source', 1).attr(sources[i]);
    501                                         source.shortEnded = true;
    502                                         audio.append(source);
    503                                 }
    504 
    505                                 data.params.src = '';
    506                         }
    507 
    508                         if (typeItem.name === 'EmbeddedAudio') {
    509                                 embed = new Node('embed', 1);
    510                                 embed.shortEnded = true;
    511                                 embed.attr({
    512                                         id: node.attr('id'),
    513                                         width: normalizeSize(node.attr('width')),
    514                                         height: normalizeSize(node.attr('height')),
    515                                         style : style,
    516                                         type: node.attr('type')
    517                                 });
    518 
    519                                 for (name in data.params)
    520                                         embed.attr(name, data.params[name]);
    521 
    522                                 tinymce.each(rootAttributes, function(name) {
    523                                         if (data[name] && name != 'type')
    524                                                 embed.attr(name, data[name]);
    525                                 });
    526 
    527                                 data.params.src = '';
    528                         }
    529 
    530                         // Do we have a params src then we can generate object
    531                         if (data.params.src) {
    532                                 // Is flv movie add player for it
    533                                 if (/\.flv$/i.test(data.params.src))
    534                                         addPlayer(data.params.src, '');
    535 
    536                                 if (args && args.force_absolute)
    537                                         data.params.src = editor.documentBaseURI.toAbsolute(data.params.src);
    538 
    539                                 // Create new object element
    540                                 object = new Node('object', 1).attr({
    541                                         id : node.attr('id'),
    542                                         width: normalizeSize(node.attr('width')),
    543                                         height: normalizeSize(node.attr('height')),
    544                                         style : style
    545                                 });
    546 
    547                                 tinymce.each(rootAttributes, function(name) {
    548                                         var value = data[name];
    549 
    550                                         if (name == 'class' && value)
    551                                                 value = value.replace(/mceItem.+ ?/g, '');
    552 
    553                                         if (value && name != 'type')
    554                                                 object.attr(name, value);
    555                                 });
    556 
    557                                 // Add params
    558                                 for (name in data.params) {
    559                                         param = new Node('param', 1);
    560                                         param.shortEnded = true;
    561                                         value = data.params[name];
    562 
    563                                         // Windows media needs to use url instead of src for the media URL
    564                                         if (name === 'src' && typeItem.name === 'WindowsMedia')
    565                                                 name = 'url';
    566 
    567                                         param.attr({name: name, value: value});
    568                                         object.append(param);
    569                                 }
    570 
    571                                 // Setup add type and classid if strict is disabled
    572                                 if (this.editor.getParam('media_strict', true)) {
    573                                         object.attr({
    574                                                 data: data.params.src,
    575                                                 type: typeItem.mimes[0]
    576                                         });
    577                                 } else {
    578                                         if ( typeItem.clsids )
    579                                                 object.attr('clsid', typeItem.clsids[0]);
    580                                         object.attr('codebase', typeItem.codebase);
    581 
    582                                         embed = new Node('embed', 1);
    583                                         embed.shortEnded = true;
    584                                         embed.attr({
    585                                                 id: node.attr('id'),
    586                                                 width: normalizeSize(node.attr('width')),
    587                                                 height: normalizeSize(node.attr('height')),
    588                                                 style : style,
    589                                                 type: typeItem.mimes[0]
    590                                         });
    591 
    592                                         for (name in data.params)
    593                                                 embed.attr(name, data.params[name]);
    594 
    595                                         tinymce.each(rootAttributes, function(name) {
    596                                                 if (data[name] && name != 'type')
    597                                                         embed.attr(name, data[name]);
    598                                         });
    599 
    600                                         object.append(embed);
    601                                 }
    602 
    603                                 // Insert raw HTML
    604                                 if (data.object_html) {
    605                                         value = new Node('#text', 3);
    606                                         value.raw = true;
    607                                         value.value = data.object_html;
    608                                         object.append(value);
    609                                 }
    610 
    611                                 // Append object to video element if it exists
    612                                 if (video)
    613                                         video.append(object);
    614                         }
    615 
    616                         if (video) {
    617                                 // Insert raw HTML
    618                                 if (data.video_html) {
    619                                         value = new Node('#text', 3);
    620                                         value.raw = true;
    621                                         value.value = data.video_html;
    622                                         video.append(value);
    623                                 }
    624                         }
    625 
    626                         if (audio) {
    627                                 // Insert raw HTML
    628                                 if (data.video_html) {
    629                                         value = new Node('#text', 3);
    630                                         value.raw = true;
    631                                         value.value = data.video_html;
    632                                         audio.append(value);
    633                                 }
    634                         }
    635 
    636                         var n = video || audio || object || embed;
    637                         if (n)
    638                                 node.replace(n);
    639                         else
    640                                 node.remove();
    641                 },
    642 
    643                 /**
    644                  * Converts a tinymce.html.Node video/object/embed to an img element.
    645                  *
    646                  * The video/object/embed will be converted into an image placeholder with a JSON data attribute like this:
    647                  * <img class="mceItemMedia mceItemFlash" width="100" height="100" data-mce-json="{..}" />
    648                  *
    649                  * The JSON structure will be like this:
    650                  * {'params':{'flashvars':'something','quality':'high','src':'someurl'}, 'video':{'sources':[{src: 'someurl', type: 'video/mp4'}]}}
    651                  */
    652                 objectToImg : function(node) {
    653                         var object, embed, video, iframe, img, name, id, width, height, style, i, html,
    654                                 param, params, source, sources, data, type, lookup = this.lookup,
    655                                 matches, attrs, urlConverter = this.editor.settings.url_converter,
    656                                 urlConverterScope = this.editor.settings.url_converter_scope,
    657                                 hspace, vspace, align, bgcolor;
    658 
    659                         function getInnerHTML(node) {
    660                                 return new tinymce.html.Serializer({
    661                                         inner: true,
    662                                         validate: false
    663                                 }).serialize(node);
    664                         };
    665 
    666                         function lookupAttribute(o, attr) {
    667                                 return lookup[(o.attr(attr) || '').toLowerCase()];
    668                         }
    669 
    670                         function lookupExtension(src) {
    671                                 var ext = src.replace(/^.*\.([^.]+)$/, '$1');
    672                                 return lookup[ext.toLowerCase() || ''];
    673                         }
    674 
    675                         // If node isn't in document
    676                         if (!node.parent)
    677                                 return;
    678 
    679                         // Handle media scripts
    680                         if (node.name === 'script') {
    681                                 if (node.firstChild)
    682                                         matches = scriptRegExp.exec(node.firstChild.value);
    683 
    684                                 if (!matches)
    685                                         return;
    686 
    687                                 type = matches[1];
    688                                 data = {video : {}, params : JSON.parse(matches[2])};
    689                                 width = data.params.width;
    690                                 height = data.params.height;
    691                         }
    692 
    693                         // Setup data objects
    694                         data = data || {
    695                                 video : {},
    696                                 params : {}
    697                         };
    698 
    699                         // Setup new image object
    700                         img = new Node('img', 1);
    701                         img.attr({
    702                                 src : this.editor.theme.url + '/img/trans.gif'
    703                         });
    704 
    705                         // Video element
    706                         name = node.name;
    707                         if (name === 'video' || name == 'audio') {
    708                                 video = node;
    709                                 object = node.getAll('object')[0];
    710                                 embed = node.getAll('embed')[0];
    711                                 width = video.attr('width');
    712                                 height = video.attr('height');
    713                                 id = video.attr('id');
    714                                 data.video = {attrs : {}, sources : []};
    715 
    716                                 // Get all video attributes
    717                                 attrs = data.video.attrs;
    718                                 for (name in video.attributes.map)
    719                                         attrs[name] = video.attributes.map[name];
    720 
    721                                 source = node.attr('src');
    722                                 if (source)
    723                                         data.video.sources.push({src : urlConverter.call(urlConverterScope, source, 'src', node.name)});
    724 
    725                                 // Get all sources
    726                                 sources = video.getAll("source");
    727                                 for (i = 0; i < sources.length; i++) {
    728                                         source = sources[i].remove();
    729 
    730                                         data.video.sources.push({
    731                                                 src: urlConverter.call(urlConverterScope, source.attr('src'), 'src', 'source'),
    732                                                 type: source.attr('type'),
    733                                                 media: source.attr('media')
    734                                         });
    735                                 }
    736 
    737                                 // Convert the poster URL
    738                                 if (attrs.poster)
    739                                         attrs.poster = urlConverter.call(urlConverterScope, attrs.poster, 'poster', node.name);
    740                         }
    741 
    742                         // Object element
    743                         if (node.name === 'object') {
    744                                 object = node;
    745                                 embed = node.getAll('embed')[0];
    746                         }
    747 
    748                         // Embed element
    749                         if (node.name === 'embed')
    750                                 embed = node;
    751 
    752                         // Iframe element
    753                         if (node.name === 'iframe') {
    754                                 iframe = node;
    755                                 type = 'Iframe';
    756                         }
    757 
    758                         if (object) {
    759                                 // Get width/height
    760                                 width = width || object.attr('width');
    761                                 height = height || object.attr('height');
    762                                 style = style || object.attr('style');
    763                                 id = id || object.attr('id');
    764                                 hspace = hspace || object.attr('hspace');
    765                                 vspace = vspace || object.attr('vspace');
    766                                 align = align || object.attr('align');
    767                                 bgcolor = bgcolor || object.attr('bgcolor');
    768                                 data.name = object.attr('name');
    769 
    770                                 // Get all object params
    771                                 params = object.getAll("param");
    772                                 for (i = 0; i < params.length; i++) {
    773                                         param = params[i];
    774                                         name = param.remove().attr('name');
    775 
    776                                         if (!excludedAttrs[name])
    777                                                 data.params[name] = param.attr('value');
    778                                 }
    779 
    780                                 data.params.src = data.params.src || object.attr('data');
    781                         }
    782 
    783                         if (embed) {
    784                                 // Get width/height
    785                                 width = width || embed.attr('width');
    786                                 height = height || embed.attr('height');
    787                                 style = style || embed.attr('style');
    788                                 id = id || embed.attr('id');
    789                                 hspace = hspace || embed.attr('hspace');
    790                                 vspace = vspace || embed.attr('vspace');
    791                                 align = align || embed.attr('align');
    792                                 bgcolor = bgcolor || embed.attr('bgcolor');
    793 
    794                                 // Get all embed attributes
    795                                 for (name in embed.attributes.map) {
    796                                         if (!excludedAttrs[name] && !data.params[name])
    797                                                 data.params[name] = embed.attributes.map[name];
    798                                 }
    799                         }
    800 
    801                         if (iframe) {
    802                                 // Get width/height
    803                                 width = normalizeSize(iframe.attr('width'));
    804                                 height = normalizeSize(iframe.attr('height'));
    805                                 style = style || iframe.attr('style');
    806                                 id = iframe.attr('id');
    807                                 hspace = iframe.attr('hspace');
    808                                 vspace = iframe.attr('vspace');
    809                                 align = iframe.attr('align');
    810                                 bgcolor = iframe.attr('bgcolor');
    811 
    812                                 tinymce.each(rootAttributes, function(name) {
    813                                         img.attr(name, iframe.attr(name));
    814                                 });
    815 
    816                                 // Get all iframe attributes
    817                                 for (name in iframe.attributes.map) {
    818                                         if (!excludedAttrs[name] && !data.params[name])
    819                                                 data.params[name] = iframe.attributes.map[name];
    820                                 }
    821                         }
    822 
    823                         // Use src not movie
    824                         if (data.params.movie) {
    825                                 data.params.src = data.params.src || data.params.movie;
    826                                 delete data.params.movie;
    827                         }
    828 
    829                         // Convert the URL to relative/absolute depending on configuration
    830                         if (data.params.src)
    831                                 data.params.src = urlConverter.call(urlConverterScope, data.params.src, 'src', 'object');
    832 
    833                         if (video) {
    834                                 if (node.name === 'video')
    835                                         type = lookup.video.name;
    836                                 else if (node.name === 'audio')
    837                                         type = lookup.audio.name;
    838                         }
    839 
    840                         if (object && !type)
    841                                 type = (lookupAttribute(object, 'clsid') || lookupAttribute(object, 'classid') || lookupAttribute(object, 'type') || {}).name;
    842 
    843                         if (embed && !type)
    844                                 type = (lookupAttribute(embed, 'type') || lookupExtension(data.params.src) || {}).name;
    845 
    846                         // for embedded audio we preserve the original specified type
    847                         if (embed && type == 'EmbeddedAudio') {
    848                                 data.params.type = embed.attr('type');
    849                         }
    850 
    851                         // Replace the video/object/embed element with a placeholder image containing the data
    852                         node.replace(img);
    853 
    854                         // Remove embed
    855                         if (embed)
    856                                 embed.remove();
    857 
    858                         // Serialize the inner HTML of the object element
    859                         if (object) {
    860                                 html = getInnerHTML(object.remove());
    861 
    862                                 if (html)
    863                                         data.object_html = html;
    864                         }
    865 
    866                         // Serialize the inner HTML of the video element
    867                         if (video) {
    868                                 html = getInnerHTML(video.remove());
    869 
    870                                 if (html)
    871                                         data.video_html = html;
    872                         }
    873 
    874                         data.hspace = hspace;
    875                         data.vspace = vspace;
    876                         data.align = align;
    877                         data.bgcolor = bgcolor;
    878 
    879                         // Set width/height of placeholder
    880                         img.attr({
    881                                 id : id,
    882                                 'class' : 'mceItemMedia mceItem' + (type || 'Flash'),
    883                                 style : style,
    884                                 width : width || (node.name == 'audio' ? "300" : "320"),
    885                                 height : height || (node.name == 'audio' ? "32" : "240"),
    886                                 hspace : hspace,
    887                                 vspace : vspace,
    888                                 align : align,
    889                                 bgcolor : bgcolor,
    890                                 "data-mce-json" : JSON.serialize(data, "'")
    891                         });
    892                 }
     586
     587                                // Unprefix all placeholder attributes
     588                                attribs = node.attributes;
     589                                ai = attribs.length;
     590                                while (ai--) {
     591                                        var attrName = attribs[ai].name;
     592
     593                                        if (attrName.indexOf('data-mce-p-') === 0) {
     594                                                realElm.attr(attrName.substr(11), attribs[ai].value);
     595                                        }
     596                                }
     597
     598                                if (realElmName == "script") {
     599                                        realElm.attr('type', 'text/javascript');
     600                                }
     601
     602                                // Inject innerhtml
     603                                innerHtml = node.attr('data-mce-html');
     604                                if (innerHtml) {
     605                                        innerNode = new tinymce.html.Node('#text', 3);
     606                                        innerNode.raw = true;
     607                                        innerNode.value = unescape(innerHtml);
     608                                        realElm.append(innerNode);
     609                                }
     610
     611                                node.replace(realElm);
     612                        }
     613                });
    893614        });
    894615
    895         // Register plugin
    896         tinymce.PluginManager.add('media', tinymce.plugins.MediaPlugin);
    897 })();
     616        editor.on('ObjectSelected', function(e) {
     617                var objectType = e.target.getAttribute('data-mce-object');
     618
     619                if (objectType == "audio" || objectType == "script") {
     620                        e.preventDefault();
     621                }
     622        });
     623
     624        editor.on('objectResized', function(e) {
     625                var target = e.target, html;
     626
     627                if (target.getAttribute('data-mce-object')) {
     628                        html = target.getAttribute('data-mce-html');
     629                        if (html) {
     630                                html = unescape(html);
     631                                target.setAttribute('data-mce-html', escape(
     632                                        updateHtml(html, {
     633                                                width: e.width,
     634                                                height: e.height
     635                                        })
     636                                ));
     637                        }
     638                }
     639        });
     640
     641        editor.addButton('media', {
     642                tooltip: 'Insert/edit video',
     643                onclick: showDialog,
     644                stateSelector: 'img[data-mce-object=video]'
     645        });
     646
     647        editor.addMenuItem('media', {
     648                icon: 'media',
     649                text: 'Insert video',
     650                onclick: showDialog,
     651                context: 'insert',
     652                prependToContext: true
     653        });
     654});
  • trunk/src/wp-includes/js/tinymce/plugins/media/plugin.min.js

    r26862 r26876  
    1 (function(){var b=tinymce.explode("id,name,width,height,style,align,class,hspace,vspace,bgcolor,type"),a=tinymce.makeMap(b.join(",")),f=tinymce.html.Node,d,i,h=tinymce.util.JSON,g;d=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["EmbeddedAudio"],["Audio"]];function e(j){return typeof(j)=="string"?j.replace(/[^0-9%]/g,""):j}function c(m){var l,j,k;if(m&&!m.splice){j=[];for(k=0;true;k++){if(m[k]){j[k]=m[k]}else{break}}return j}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(n,j){var r=this,l={},m,p,q,k;function o(s){return s&&s.nodeName==="IMG"&&n.dom.hasClass(s,"mceItemMedia")}r.editor=n;r.url=j;i="";for(m=0;m<d.length;m++){k=d[m][0];q={name:k,clsids:tinymce.explode(d[m][1]||""),mimes:tinymce.explode(d[m][2]||""),codebase:d[m][3]};for(p=0;p<q.clsids.length;p++){l["clsid:"+q.clsids[p]]=q}for(p=0;p<q.mimes.length;p++){l[q.mimes[p]]=q}l["mceItem"+k]=q;l[k.toLowerCase()]=q;i+=(i?"|":"")+k}tinymce.each(n.getParam("media_types","video=mp4,m4v,ogv,webm;silverlight=xap;flash=swf,flv;shockwave=dcr;quicktime=mov,qt,mpg,mpeg;shockwave=dcr;windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;realmedia=rm,ra,ram;java=jar;audio=mp3,ogg").split(";"),function(v){var s,u,t;v=v.split(/=/);u=tinymce.explode(v[1].toLowerCase());for(s=0;s<u.length;s++){t=l[v[0].toLowerCase()];if(t){l[u[s]]=t}}});i=new RegExp("write("+i+")\\(([^)]+)\\)");r.lookup=l;n.onPreInit.add(function(){n.schema.addValidElements("object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]");n.parser.addNodeFilter("object,embed,video,audio,script,iframe",function(s){var t=s.length;while(t--){r.objectToImg(s[t])}});n.serializer.addNodeFilter("img",function(s,u,t){var v=s.length,w;while(v--){w=s[v];if((w.attr("class")||"").indexOf("mceItemMedia")!==-1){r.imgToObject(w,t)}}})});n.onInit.add(function(){if(n.theme&&n.theme.onResolveName){n.theme.onResolveName.add(function(s,t){if(t.name==="img"&&n.dom.hasClass(t.node,"mceItemMedia")){t.name="media"}})}if(n&&n.plugins.contextmenu){n.plugins.contextmenu.onContextMenu.add(function(t,u,s){if(s.nodeName==="IMG"&&s.className.indexOf("mceItemMedia")!==-1){u.add({title:"media.edit",icon:"media",cmd:"mceMedia"})}})}});n.addCommand("mceMedia",function(){var t,s;s=n.selection.getNode();if(o(s)){t=n.dom.getAttrib(s,"data-mce-json");if(t){t=h.parse(t);tinymce.each(b,function(u){var v=n.dom.getAttrib(s,u);if(v){t[u]=v}});t.type=r.getType(s.className).name.toLowerCase()}}if(!t){t={type:"flash",video:{sources:[]},params:{}}}n.windowManager.open({file:j+"/media.htm",width:430+parseInt(n.getLang("media.delta_width",0)),height:500+parseInt(n.getLang("media.delta_height",0)),inline:1},{plugin_url:j,data:t})});n.addButton("media",{title:"media.desc",cmd:"mceMedia"});n.onNodeChange.add(function(t,s,u){s.setActive("media",o(u))})},convertUrl:function(l,o){var k=this,n=k.editor,m=n.settings,p=m.url_converter,j=m.url_converter_scope||k;if(!l){return l}if(o){return n.documentBaseURI.toAbsolute(l)}return p.call(j,l,"src","object")},getInfo:function(){return{longname:"Media",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media",version:tinymce.majorVersion+"."+tinymce.minorVersion}},dataToImg:function(m,k){var r=this,o=r.editor,p=o.documentBaseURI,j,q,n,l;m.params.src=r.convertUrl(m.params.src,k);q=m.video.attrs;if(q){q.src=r.convertUrl(q.src,k)}if(q){q.poster=r.convertUrl(q.poster,k)}j=c(m.video.sources);if(j){for(l=0;l<j.length;l++){j[l].src=r.convertUrl(j[l].src,k)}}n=r.editor.dom.create("img",{id:m.id,style:m.style,align:m.align,hspace:m.hspace,vspace:m.vspace,src:r.editor.theme.url+"/img/trans.gif","class":"mceItemMedia mceItem"+r.getType(m.type).name,"data-mce-json":h.serialize(m,"'")});n.width=m.width=e(m.width||(m.type=="audio"?"300":"320"));n.height=m.height=e(m.height||(m.type=="audio"?"32":"240"));return n},dataToHtml:function(j,k){return this.editor.serializer.serialize(this.dataToImg(j,k),{forced_root_block:"",force_absolute:k})},htmlToData:function(l){var k,j,m;m={type:"flash",video:{sources:[]},params:{}};k=this.editor.parser.parse(l);j=k.getAll("img")[0];if(j){m=h.parse(j.attr("data-mce-json"));m.type=this.getType(j.attr("class")).name.toLowerCase();tinymce.each(b,function(n){var o=j.attr(n);if(o){m[n]=o}})}return m},getType:function(m){var k,j,l;j=tinymce.explode(m," ");for(k=0;k<j.length;k++){l=this.lookup[j[k]];if(l){return l}}},imgToObject:function(z,o){var u=this,p=u.editor,C,H,j,t,I,y,G,w,k,E,s,q,A,D,m,x,l,B,F;function r(n,J){var N,M,O,L,K;K=p.getParam("flash_video_player_url",u.convertUrl(u.url+"/moxieplayer.swf"));if(K){N=p.documentBaseURI;G.params.src=K;if(p.getParam("flash_video_player_absvideourl",true)){n=N.toAbsolute(n||"",true);J=N.toAbsolute(J||"",true)}O="";M=p.getParam("flash_video_player_flashvars",{url:"$url",poster:"$poster"});tinymce.each(M,function(Q,P){Q=Q.replace(/\$url/,n||"");Q=Q.replace(/\$poster/,J||"");if(Q.length>0){O+=(O?"&":"")+P+"="+escape(Q)}});if(O.length){G.params.flashvars=O}L=p.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(L,function(Q,P){G.params[P]=""+Q})}}G=z.attr("data-mce-json");if(!G){return}G=h.parse(G);q=this.getType(z.attr("class"));B=z.attr("data-mce-style");if(!B){B=z.attr("style");if(B){B=p.dom.serializeStyle(p.dom.parseStyle(B,"img"))}}G.width=z.attr("width")||G.width;G.height=z.attr("height")||G.height;if(q.name==="Iframe"){x=new f("iframe",1);tinymce.each(b,function(n){var J=z.attr(n);if(n=="class"&&J){J=J.replace(/mceItem.+ ?/g,"")}if(J&&J.length>0){x.attr(n,J)}});for(I in G.params){x.attr(I,G.params[I])}x.attr({style:B,src:G.params.src});z.replace(x);return}if(this.editor.settings.media_use_script){x=new f("script",1).attr("type","text/javascript");y=new f("#text",3);y.value="write"+q.name+"("+h.serialize(tinymce.extend(G.params,{width:z.attr("width"),height:z.attr("height")}))+");";x.append(y);z.replace(x);return}if(q.name==="Video"&&G.video.sources[0]){C=new f("video",1).attr(tinymce.extend({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);for(A=0;A<k.length;A++){if(/\.mp4$/.test(k[A].src)){m=k[A].src}}if(!k[0].type){C.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new f("source",1).attr(k[A]);w.shortEnded=true;C.append(w)}if(m){r(m,l);q=u.getType("flash")}else{G.params.src=""}}if(q.name==="Audio"&&G.video.sources[0]){F=new f("audio",1).attr(tinymce.extend({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);if(!k[0].type){F.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new f("source",1).attr(k[A]);w.shortEnded=true;F.append(w)}G.params.src=""}if(q.name==="EmbeddedAudio"){j=new f("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B,type:z.attr("type")});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(b,function(n){if(G[n]&&n!="type"){j.attr(n,G[n])}});G.params.src=""}if(G.params.src){if(/\.flv$/i.test(G.params.src)){r(G.params.src,"")}if(o&&o.force_absolute){G.params.src=p.documentBaseURI.toAbsolute(G.params.src)}H=new f("object",1).attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B});tinymce.each(b,function(n){var J=G[n];if(n=="class"&&J){J=J.replace(/mceItem.+ ?/g,"")}if(J&&n!="type"){H.attr(n,J)}});for(I in G.params){s=new f("param",1);s.shortEnded=true;y=G.params[I];if(I==="src"&&q.name==="WindowsMedia"){I="url"}s.attr({name:I,value:y});H.append(s)}if(this.editor.getParam("media_strict",true)){H.attr({data:G.params.src,type:q.mimes[0]})}else{H.attr({classid:"clsid:"+q.clsids[0],codebase:q.codebase});j=new f("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B,type:q.mimes[0]});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(b,function(n){if(G[n]&&n!="type"){j.attr(n,G[n])}});H.append(j)}if(G.object_html){y=new f("#text",3);y.raw=true;y.value=G.object_html;H.append(y)}if(C){C.append(H)}}if(C){if(G.video_html){y=new f("#text",3);y.raw=true;y.value=G.video_html;C.append(y)}}if(F){if(G.video_html){y=new f("#text",3);y.raw=true;y.value=G.video_html;F.append(y)}}var v=C||F||H||j;if(v){z.replace(v)}else{z.remove()}},objectToImg:function(C){var L,k,F,s,M,N,y,A,x,G,E,t,q,I,B,l,K,o,H=this.lookup,m,z,v=this.editor.settings.url_converter,n=this.editor.settings.url_converter_scope,w,r,D,j;function u(O){return new tinymce.html.Serializer({inner:true,validate:false}).serialize(O)}function J(P,O){return H[(P.attr(O)||"").toLowerCase()]}function p(P){var O=P.replace(/^.*\.([^.]+)$/,"$1");return H[O.toLowerCase()||""]}if(!C.parent){return}if(C.name==="script"){if(C.firstChild){m=i.exec(C.firstChild.value)}if(!m){return}o=m[1];K={video:{},params:h.parse(m[2])};A=K.params.width;x=K.params.height}K=K||{video:{},params:{}};M=new f("img",1);M.attr({src:this.editor.theme.url+"/img/trans.gif"});N=C.name;if(N==="video"||N=="audio"){F=C;L=C.getAll("object")[0];k=C.getAll("embed")[0];A=F.attr("width");x=F.attr("height");y=F.attr("id");K.video={attrs:{},sources:[]};z=K.video.attrs;for(N in F.attributes.map){z[N]=F.attributes.map[N]}B=C.attr("src");if(B){K.video.sources.push({src:v.call(n,B,"src",C.name)})}l=F.getAll("source");for(E=0;E<l.length;E++){B=l[E].remove();K.video.sources.push({src:v.call(n,B.attr("src"),"src","source"),type:B.attr("type"),media:B.attr("media")})}if(z.poster){z.poster=v.call(n,z.poster,"poster",C.name)}}if(C.name==="object"){L=C;k=C.getAll("embed")[0]}if(C.name==="embed"){k=C}if(C.name==="iframe"){s=C;o="Iframe"}if(L){A=A||L.attr("width");x=x||L.attr("height");G=G||L.attr("style");y=y||L.attr("id");w=w||L.attr("hspace");r=r||L.attr("vspace");D=D||L.attr("align");j=j||L.attr("bgcolor");K.name=L.attr("name");I=L.getAll("param");for(E=0;E<I.length;E++){q=I[E];N=q.remove().attr("name");if(!a[N]){K.params[N]=q.attr("value")}}K.params.src=K.params.src||L.attr("data")}if(k){A=A||k.attr("width");x=x||k.attr("height");G=G||k.attr("style");y=y||k.attr("id");w=w||k.attr("hspace");r=r||k.attr("vspace");D=D||k.attr("align");j=j||k.attr("bgcolor");for(N in k.attributes.map){if(!a[N]&&!K.params[N]){K.params[N]=k.attributes.map[N]}}}if(s){A=e(s.attr("width"));x=e(s.attr("height"));G=G||s.attr("style");y=s.attr("id");w=s.attr("hspace");r=s.attr("vspace");D=s.attr("align");j=s.attr("bgcolor");tinymce.each(b,function(O){M.attr(O,s.attr(O))});for(N in s.attributes.map){if(!a[N]&&!K.params[N]){K.params[N]=s.attributes.map[N]}}}if(K.params.movie){K.params.src=K.params.src||K.params.movie;delete K.params.movie}if(K.params.src){K.params.src=v.call(n,K.params.src,"src","object")}if(F){if(C.name==="video"){o=H.video.name}else{if(C.name==="audio"){o=H.audio.name}}}if(L&&!o){o=(J(L,"clsid")||J(L,"classid")||J(L,"type")||{}).name}if(k&&!o){o=(J(k,"type")||p(K.params.src)||{}).name}if(k&&o=="EmbeddedAudio"){K.params.type=k.attr("type")}C.replace(M);if(k){k.remove()}if(L){t=u(L.remove());if(t){K.object_html=t}}if(F){t=u(F.remove());if(t){K.video_html=t}}K.hspace=w;K.vspace=r;K.align=D;K.bgcolor=j;M.attr({id:y,"class":"mceItemMedia mceItem"+(o||"Flash"),style:G,width:A||(C.name=="audio"?"300":"320"),height:x||(C.name=="audio"?"32":"240"),hspace:w,vspace:r,align:D,bgcolor:j,"data-mce-json":h.serialize(K,"'")})}});tinymce.PluginManager.add("media",tinymce.plugins.MediaPlugin)})();
     1tinymce.PluginManager.add("media",function(e,t){function i(e){return-1!=e.indexOf(".mp3")?"audio/mpeg":-1!=e.indexOf(".wav")?"audio/wav":-1!=e.indexOf(".mp4")?"video/mp4":-1!=e.indexOf(".webm")?"video/webm":-1!=e.indexOf(".ogg")?"video/ogg":-1!=e.indexOf(".swf")?"application/x-shockwave-flash":""}function r(t){var i=e.settings.media_scripts;if(i)for(var r=0;r<i.length;r++)if(-1!==t.indexOf(i[r].filter))return i[r]}function a(){function t(e){var t,o,c,n;t=i.find("#width")[0],o=i.find("#height")[0],c=t.value(),n=o.value(),i.find("#constrain")[0].checked()&&r&&a&&c&&n&&(e.control==t?(n=Math.round(c/r*n),o.value(n)):(c=Math.round(n/a*c),t.value(c))),r=c,a=n}var i,r,a,m;m=s(e.selection.getNode()),r=m.width,a=m.height,i=e.windowManager.open({title:"Insert/edit video",data:m,bodyType:"tabpanel",body:[{title:"General",type:"form",onShowTab:function(){m=n(this.next().find("#embed").value()),this.fromJSON(m)},items:[{name:"source1",type:"filepicker",filetype:"media",size:40,autofocus:!0,label:"Source"},{name:"source2",type:"filepicker",filetype:"media",size:40,label:"Alternative source"},{name:"poster",type:"filepicker",filetype:"image",size:40,label:"Poster"},{type:"container",label:"Dimensions",layout:"flex",align:"center",spacing:5,items:[{name:"width",type:"textbox",maxLength:3,size:3,onchange:t},{type:"label",text:"x"},{name:"height",type:"textbox",maxLength:3,size:3,onchange:t},{name:"constrain",type:"checkbox",checked:!0,text:"Constrain proportions"}]}]},{title:"Embed",type:"panel",layout:"flex",direction:"column",align:"stretch",padding:10,spacing:10,onShowTab:function(){this.find("#embed").value(c(this.parent().toJSON()))},items:[{type:"label",text:"Paste your embed code below:"},{type:"textbox",flex:1,name:"embed",value:o(),multiline:!0,label:"Source"}]}],onSubmit:function(){e.insertContent(c(this.toJSON()))}})}function o(){var t=e.selection.getNode();return t.getAttribute("data-mce-object")?e.selection.getContent():void 0}function c(a){var o="";if(!a.source1&&(tinymce.extend(a,n(a.embed)),!a.source1))return"";if(a.source1=e.convertURL(a.source1,"source"),a.source2=e.convertURL(a.source2,"source"),a.source1mime=i(a.source1),a.source2mime=i(a.source2),a.poster=e.convertURL(a.poster,"poster"),a.flashPlayerUrl=e.convertURL(t+"/moxieplayer.swf","movie"),a.embed)o=m(a.embed,a,!0);else{tinymce.each(d,function(e){var t,i,r;if(t=e.regex.exec(a.source1)){for(r=e.url,i=0;t[i];i++)r=r.replace("$"+i,function(){return t[i]});a.source1=r,a.type=e.type,a.width=e.w,a.height=e.h}});var c=r(a.source1);c&&(a.type="script",a.width=c.width,a.height=c.height),a.width=a.width||300,a.height=a.height||150,tinymce.each(a,function(t,i){a[i]=e.dom.encode(t)}),"iframe"==a.type?o+='<iframe src="'+a.source1+'" width="'+a.width+'" height="'+a.height+'"></iframe>':"application/x-shockwave-flash"==a.source1mime?(o+='<object data="'+a.source1+'" width="'+a.width+'" height="'+a.height+'" type="application/x-shockwave-flash">',a.poster&&(o+='<img src="'+a.poster+'" width="'+a.width+'" height="'+a.height+'" />'),o+="</object>"):-1!=a.source1mime.indexOf("audio")?e.settings.audio_template_callback?o=e.settings.audio_template_callback(a):o+='<audio controls="controls" src="'+a.source1+'">'+(a.source2?'\n<source src="'+a.source2+'"'+(a.source2mime?' type="'+a.source2mime+'"':"")+" />\n":"")+"</audio>":"script"==a.type?o+='<script src="'+a.source1+'"></script>':o=e.settings.video_template_callback?e.settings.video_template_callback(a):'<video width="'+a.width+'" height="'+a.height+'"'+(a.poster?' poster="'+a.poster+'"':"")+' controls="controls">\n<source src="'+a.source1+'"'+(a.source1mime?' type="'+a.source1mime+'"':"")+" />\n"+(a.source2?'<source src="'+a.source2+'"'+(a.source2mime?' type="'+a.source2mime+'"':"")+" />\n":"")+"</video>"}return o}function n(e){var t={};return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",start:function(e,i){if(t.source1||"param"!=e||(t.source1=i.map.movie),("iframe"==e||"object"==e||"embed"==e||"video"==e||"audio"==e)&&(t.type||(t.type=e),t=tinymce.extend(i.map,t)),"script"==e){var a=r(i.map.src);if(!a)return;t={type:"script",source1:i.map.src,width:a.width,height:a.height}}"source"==e&&(t.source1?t.source2||(t.source2=i.map.src):t.source1=i.map.src),"img"!=e||t.poster||(t.poster=i.map.src)}}).parse(e),t.source1=t.source1||t.src||t.data,t.source2=t.source2||"",t.poster=t.poster||"",t}function s(t){return t.getAttribute("data-mce-object")?n(e.serializer.serialize(t,{selection:!0})):{}}function m(e,t,i){function r(e,t){var i,r,a,o;for(i in t)if(a=""+t[i],e.map[i])for(r=e.length;r--;)o=e[r],o.name==i&&(a?(e.map[i]=a,o.value=a):(delete e.map[i],e.splice(r,1)));else a&&(e.push({name:i,value:a}),e.map[i]=a)}var a,o=new tinymce.html.Writer,c=0;return new tinymce.html.SaxParser({validate:!1,allow_conditional_comments:!0,special:"script,noscript",comment:function(e){o.comment(e)},cdata:function(e){o.cdata(e)},text:function(e,t){o.text(e,t)},start:function(e,n,s){switch(e){case"video":case"object":case"embed":case"img":case"iframe":r(n,{width:t.width,height:t.height})}if(i)switch(e){case"video":r(n,{poster:t.poster,src:""}),t.source2&&r(n,{src:""});break;case"iframe":r(n,{src:t.source1});break;case"source":if(c++,2>=c&&(r(n,{src:t["source"+c],type:t["source"+c+"mime"]}),!t["source"+c]))return;break;case"img":if(!t.poster)return;a=!0}o.start(e,n,s)},end:function(e){if("video"==e&&i)for(var n=1;2>=n;n++)if(t["source"+n]){var s=[];s.map={},n>c&&(r(s,{src:t["source"+n],type:t["source"+n+"mime"]}),o.start("source",s,!0))}if(t.poster&&"object"==e&&i&&!a){var m=[];m.map={},r(m,{src:t.poster,width:t.width,height:t.height}),o.start("img",m,!0)}o.end(e)}},new tinymce.html.Schema({})).parse(e),o.getContent()}var d=[{regex:/youtu\.be\/([a-z1-9.-_]+)/,type:"iframe",w:425,h:350,url:"http://www.youtube.com/embed/$1"},{regex:/youtube\.com(.+)v=([^&]+)/,type:"iframe",w:425,h:350,url:"http://www.youtube.com/embed/$2"},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"http://player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc"},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'http://maps.google.com/maps/ms?msid=$2&output=embed"'}];e.on("ResolveName",function(e){var t;1==e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}),e.on("preInit",function(){var t=e.schema.getSpecialElements();tinymce.each("video audio iframe object".split(" "),function(e){t[e]=new RegExp("</"+e+"[^>]*>","gi")}),e.schema.addValidElements("object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]");var i=e.schema.getBoolAttrs();tinymce.each("webkitallowfullscreen mozallowfullscreen allowfullscreen".split(" "),function(e){i[e]={}}),e.parser.addNodeFilter("iframe,video,audio,object,embed,script",function(t,i){for(var a,o,c,n,s,m,d,u,l=t.length;l--;)if(o=t[l],"script"!=o.name||(u=r(o.attr("src")))){for(c=new tinymce.html.Node("img",1),c.shortEnded=!0,u&&(u.width&&o.attr("width",u.width.toString()),u.height&&o.attr("height",u.height.toString())),m=o.attributes,a=m.length;a--;)n=m[a].name,s=m[a].value,"width"!==n&&"height"!==n&&"style"!==n&&(("data"==n||"src"==n)&&(s=e.convertURL(s,n)),c.attr("data-mce-p-"+n,s));d=o.firstChild&&o.firstChild.value,d&&(c.attr("data-mce-html",escape(d)),c.firstChild=null),c.attr({width:o.attr("width")||"300",height:o.attr("height")||("audio"==i?"30":"150"),style:o.attr("style"),src:tinymce.Env.transparentSrc,"data-mce-object":i,"class":"mce-object mce-object-"+i}),o.replace(c)}}),e.serializer.addAttributeFilter("data-mce-object",function(e,t){for(var i,r,a,o,c,n,s,m=e.length;m--;){for(i=e[m],s=i.attr(t),r=new tinymce.html.Node(s,1),"audio"!=s&&"script"!=s&&r.attr({width:i.attr("width"),height:i.attr("height")}),r.attr({style:i.attr("style")}),o=i.attributes,a=o.length;a--;){var d=o[a].name;0===d.indexOf("data-mce-p-")&&r.attr(d.substr(11),o[a].value)}"script"==s&&r.attr("type","text/javascript"),c=i.attr("data-mce-html"),c&&(n=new tinymce.html.Node("#text",3),n.raw=!0,n.value=unescape(c),r.append(n)),i.replace(r)}})}),e.on("ObjectSelected",function(e){var t=e.target.getAttribute("data-mce-object");("audio"==t||"script"==t)&&e.preventDefault()}),e.on("objectResized",function(e){var t,i=e.target;i.getAttribute("data-mce-object")&&(t=i.getAttribute("data-mce-html"),t&&(t=unescape(t),i.setAttribute("data-mce-html",escape(m(t,{width:e.width,height:e.height})))))}),e.addButton("media",{tooltip:"Insert/edit video",onclick:a,stateSelector:"img[data-mce-object=video]"}),e.addMenuItem("media",{icon:"media",text:"Insert video",onclick:a,context:"insert",prependToContext:!0})});
  • trunk/src/wp-includes/js/tinymce/plugins/paste/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
    3  *
    4  * Copyright 2009, Moxiecode Systems AB
     2 * Compiled inline version. (Library mode)
     3 */
     4
     5/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
     6/*globals $code */
     7
     8(function(exports, undefined) {
     9        "use strict";
     10
     11        var modules = {};
     12
     13        function require(ids, callback) {
     14                var module, defs = [];
     15
     16                for (var i = 0; i < ids.length; ++i) {
     17                        module = modules[ids[i]] || resolve(ids[i]);
     18                        if (!module) {
     19                                throw 'module definition dependecy not found: ' + ids[i];
     20                        }
     21
     22                        defs.push(module);
     23                }
     24
     25                callback.apply(null, defs);
     26        }
     27
     28        function define(id, dependencies, definition) {
     29                if (typeof id !== 'string') {
     30                        throw 'invalid module definition, module id must be defined and be a string';
     31                }
     32
     33                if (dependencies === undefined) {
     34                        throw 'invalid module definition, dependencies must be specified';
     35                }
     36
     37                if (definition === undefined) {
     38                        throw 'invalid module definition, definition function must be specified';
     39                }
     40
     41                require(dependencies, function() {
     42                        modules[id] = definition.apply(null, arguments);
     43                });
     44        }
     45
     46        function defined(id) {
     47                return !!modules[id];
     48        }
     49
     50        function resolve(id) {
     51                var target = exports;
     52                var fragments = id.split(/[.\/]/);
     53
     54                for (var fi = 0; fi < fragments.length; ++fi) {
     55                        if (!target[fragments[fi]]) {
     56                                return;
     57                        }
     58
     59                        target = target[fragments[fi]];
     60                }
     61
     62                return target;
     63        }
     64
     65        function expose(ids) {
     66                for (var i = 0; i < ids.length; i++) {
     67                        var target = exports;
     68                        var id = ids[i];
     69                        var fragments = id.split(/[.\/]/);
     70
     71                        for (var fi = 0; fi < fragments.length - 1; ++fi) {
     72                                if (target[fragments[fi]] === undefined) {
     73                                        target[fragments[fi]] = {};
     74                                }
     75
     76                                target = target[fragments[fi]];
     77                        }
     78
     79                        target[fragments[fragments.length - 1]] = modules[id];
     80                }
     81        }
     82
     83// Included from: js/tinymce/plugins/paste/classes/Utils.js
     84
     85/**
     86 * Utils.js
     87 *
     88 * Copyright, Moxiecode Systems AB
    589 * Released under LGPL License.
    690 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     91 * License: http://www.tinymce.com/license
     92 * Contributing: http://www.tinymce.com/contributing
    993 */
    1094
    11 (function() {
    12         var each = tinymce.each,
    13                 defs = {
    14                         paste_auto_cleanup_on_paste : true,
    15                         paste_enable_default_filters : true,
    16                         paste_block_drop : false,
    17                         paste_retain_style_properties : "none",
    18                         paste_strip_class_attributes : "mso",
    19                         paste_remove_spans : false,
    20                         paste_remove_styles : false,
    21                         paste_remove_styles_if_webkit : true,
    22                         paste_convert_middot_lists : true,
    23                         paste_convert_headers_to_strong : false,
    24                         paste_dialog_width : "450",
    25                         paste_dialog_height : "400",
    26                         paste_max_consecutive_linebreaks: 2,
    27                         paste_text_use_dialog : false,
    28                         paste_text_sticky : false,
    29                         paste_text_sticky_default : false,
    30                         paste_text_notifyalways : false,
    31                         paste_text_linebreaktype : "combined",
    32                         paste_text_replacements : [
    33                                 [/\u2026/g, "..."],
    34                                 [/[\x93\x94\u201c\u201d]/g, '"'],
    35                                 [/[\x60\x91\x92\u2018\u2019]/g, "'"]
    36                         ]
    37                 };
    38 
    39         function getParam(ed, name) {
    40                 return ed.getParam(name, defs[name]);
     95/**
     96 * This class contails various utility functions for the paste plugin.
     97 *
     98 * @class tinymce.pasteplugin.Clipboard
     99 * @private
     100 */
     101define("tinymce/pasteplugin/Utils", [
     102        "tinymce/util/Tools",
     103        "tinymce/html/DomParser",
     104        "tinymce/html/Schema"
     105], function(Tools, DomParser, Schema) {
     106        function filter(content, items) {
     107                Tools.each(items, function(v) {
     108                        if (v.constructor == RegExp) {
     109                                content = content.replace(v, '');
     110                        } else {
     111                                content = content.replace(v[0], v[1]);
     112                        }
     113                });
     114
     115                return content;
    41116        }
    42117
    43         tinymce.create('tinymce.plugins.PastePlugin', {
    44                 init : function(ed, url) {
    45                         var t = this;
    46 
    47                         t.editor = ed;
    48                         t.url = url;
    49 
    50                         // Setup plugin events
    51                         t.onPreProcess = new tinymce.util.Dispatcher(t);
    52                         t.onPostProcess = new tinymce.util.Dispatcher(t);
    53 
    54                         // Register default handlers
    55                         t.onPreProcess.add(t._preProcess);
    56                         t.onPostProcess.add(t._postProcess);
    57 
    58                         // Register optional preprocess handler
    59                         t.onPreProcess.add(function(pl, o) {
    60                                 ed.execCallback('paste_preprocess', pl, o);
     118        /**
     119         * Gets the innerText of the specified element. It will handle edge cases
     120         * and works better than textContent on Gecko.
     121         *
     122         * @param {String} html HTML string to get text from.
     123         * @return {String} String of text with line feeds.
     124         */
     125        function innerText(html) {
     126                var schema = new Schema(), domParser = new DomParser({}, schema), text = '';
     127                var shortEndedElements = schema.getShortEndedElements();
     128                var ignoreElements = Tools.makeMap('script noscript style textarea video audio iframe object', ' ');
     129                var blockElements = schema.getBlockElements();
     130
     131                function walk(node) {
     132                        var name = node.name, currentNode = node;
     133
     134                        if (name === 'br') {
     135                                text += '\n';
     136                                return;
     137                        }
     138
     139                        // img/input/hr
     140                        if (shortEndedElements[name]) {
     141                                text += ' ';
     142                        }
     143
     144                        // Ingore script, video contents
     145                        if (ignoreElements[name]) {
     146                                text += ' ';
     147                                return;
     148                        }
     149
     150                        if (node.type == 3) {
     151                                text += node.value;
     152                        }
     153
     154                        // Walk all children
     155                        if (!node.shortEnded) {
     156                                if ((node = node.firstChild)) {
     157                                        do {
     158                                                walk(node);
     159                                        } while ((node = node.next));
     160                                }
     161                        }
     162
     163                        // Add \n or \n\n for blocks or P
     164                        if (blockElements[name] && currentNode.next) {
     165                                text += '\n';
     166
     167                                if (name == 'p') {
     168                                        text += '\n';
     169                                }
     170                        }
     171                }
     172
     173                walk(domParser.parse(html));
     174
     175                return text;
     176        }
     177
     178        return {
     179                filter: filter,
     180                innerText: innerText
     181        };
     182});
     183
     184// Included from: js/tinymce/plugins/paste/classes/Clipboard.js
     185
     186/**
     187 * Clipboard.js
     188 *
     189 * Copyright, Moxiecode Systems AB
     190 * Released under LGPL License.
     191 *
     192 * License: http://www.tinymce.com/license
     193 * Contributing: http://www.tinymce.com/contributing
     194 */
     195
     196/**
     197 * This class contains logic for getting HTML contents out of the clipboard.
     198 *
     199 * We need to make a lot of ugly hacks to get the contents out of the clipboard since
     200 * the W3C Clipboard API is broken in all browsers: Gecko/WebKit/Blink. We might rewrite
     201 * this the way those API:s stabilize.
     202 *
     203 * Current implementation steps:
     204 *  1. On keydown with paste keys Ctrl+V or Shift+Insert create
     205 *     a paste bin element and move focus to that element.
     206 *  2. Wait for the browser to fire a "paste" event and get the contents out of the paste bin.
     207 *  3. Check if the paste was successful if true, process the HTML.
     208 *  (4). If the paste was unsuccessful use IE execCommand, Clipboard API, document.dataTransfer old WebKit API etc.
     209 *
     210 * @class tinymce.pasteplugin.Clipboard
     211 * @private
     212 */
     213define("tinymce/pasteplugin/Clipboard", [
     214        "tinymce/Env",
     215        "tinymce/util/VK",
     216        "tinymce/pasteplugin/Utils"
     217], function(Env, VK, Utils) {
     218        return function(editor) {
     219                var self = this, pasteBinElm, lastRng, keyboardPasteTimeStamp = 0;
     220                var pasteBinDefaultContent = '%MCEPASTEBIN%', keyboardPastePlainTextState;
     221
     222                /**
     223                 * Pastes the specified HTML. This means that the HTML is filtered and then
     224                 * inserted at the current selection in the editor. It will also fire paste events
     225                 * for custom user filtering.
     226                 *
     227                 * @param {String} html HTML code to paste into the current selection.
     228                 */
     229                function pasteHtml(html) {
     230                        var args, dom = editor.dom;
     231
     232                        args = editor.fire('BeforePastePreProcess', {content: html}); // Internal event used by Quirks
     233                        args = editor.fire('PastePreProcess', args);
     234                        html = args.content;
     235
     236                        if (!args.isDefaultPrevented()) {
     237                                // User has bound PastePostProcess events then we need to pass it through a DOM node
     238                                // This is not ideal but we don't want to let the browser mess up the HTML for example
     239                                // some browsers add &nbsp; to P tags etc
     240                                if (editor.hasEventListeners('PastePostProcess') && !args.isDefaultPrevented()) {
     241                                        // We need to attach the element to the DOM so Sizzle selectors work on the contents
     242                                        var tempBody = dom.add(editor.getBody(), 'div', {style: 'display:none'}, html);
     243                                        args = editor.fire('PastePostProcess', {node: tempBody});
     244                                        dom.remove(tempBody);
     245                                        html = args.node.innerHTML;
     246                                }
     247
     248                                if (!args.isDefaultPrevented()) {
     249                                        editor.insertContent(html);
     250                                }
     251                        }
     252                }
     253
     254                /**
     255                 * Pastes the specified text. This means that the plain text is processed
     256                 * and converted into BR and P elements. It will fire paste events for custom filtering.
     257                 *
     258                 * @param {String} text Text to paste as the current selection location.
     259                 */
     260                function pasteText(text) {
     261                        text = editor.dom.encode(text).replace(/\r\n/g, '\n');
     262
     263                        var startBlock = editor.dom.getParent(editor.selection.getStart(), editor.dom.isBlock);
     264
     265                        // Create start block html for example <p attr="value">
     266                        var forcedRootBlockName = editor.settings.forced_root_block;
     267                        var forcedRootBlockStartHtml;
     268                        if (forcedRootBlockName) {
     269                                forcedRootBlockStartHtml = editor.dom.createHTML(forcedRootBlockName, editor.settings.forced_root_block_attrs);
     270                                forcedRootBlockStartHtml = forcedRootBlockStartHtml.substr(0, forcedRootBlockStartHtml.length - 3) + '>';
     271                        }
     272
     273                        if ((startBlock && /^(PRE|DIV)$/.test(startBlock.nodeName)) || !forcedRootBlockName) {
     274                                text = Utils.filter(text, [
     275                                        [/\n/g, "<br>"]
     276                                ]);
     277                        } else {
     278                                text = Utils.filter(text, [
     279                                        [/\n\n/g, "</p>" + forcedRootBlockStartHtml],
     280                                        [/^(.*<\/p>)(<p>)$/, forcedRootBlockStartHtml + '$1'],
     281                                        [/\n/g, "<br />"]
     282                                ]);
     283
     284                                if (text.indexOf('<p>') != -1) {
     285                                        text = forcedRootBlockStartHtml + text;
     286                                }
     287                        }
     288
     289                        pasteHtml(text);
     290                }
     291
     292                /**
     293                 * Creates a paste bin element and moves the selection into that element. It will also move the element offscreen
     294                 * so that resize handles doesn't get produced on IE or Drag handles or Firefox.
     295                 */
     296                function createPasteBin() {
     297                        var dom = editor.dom, body = editor.getBody(), viewport = editor.dom.getViewPort(editor.getWin());
     298                        var height = editor.inline ? body.clientHeight : viewport.h;
     299
     300                        removePasteBin();
     301
     302                        // Create a pastebin
     303                        pasteBinElm = dom.add(editor.getBody(), 'div', {
     304                                id: "mcepastebin",
     305                                contentEditable: true,
     306                                "data-mce-bogus": "1",
     307                                style: 'position: fixed; top: 20px;' +
     308                                        'width: 10px; height: ' + (height - 40) + 'px; overflow: hidden; opacity: 0'
     309                        }, pasteBinDefaultContent);
     310
     311                        // Move paste bin out of sight since the controlSelection rect gets displayed otherwise
     312                        dom.setStyle(pasteBinElm, 'left', dom.getStyle(body, 'direction', true) == 'rtl' ? 0xFFFF : -0xFFFF);
     313
     314                        // Prevent focus events from bubbeling fixed FocusManager issues
     315                        dom.bind(pasteBinElm, 'beforedeactivate focusin focusout', function(e) {
     316                                e.stopPropagation();
    61317                        });
    62318
    63                         // Register optional postprocess
    64                         t.onPostProcess.add(function(pl, o) {
    65                                 ed.execCallback('paste_postprocess', pl, o);
     319                        lastRng = editor.selection.getRng();
     320                        pasteBinElm.focus();
     321                        editor.selection.select(pasteBinElm, true);
     322                }
     323
     324                /**
     325                 * Removes the paste bin if it exists.
     326                 */
     327                function removePasteBin() {
     328                        if (pasteBinElm) {
     329                                editor.dom.unbind(pasteBinElm);
     330                                editor.dom.remove(pasteBinElm);
     331
     332                                if (lastRng) {
     333                                        editor.selection.setRng(lastRng);
     334                                }
     335                        }
     336
     337                        keyboardPastePlainTextState = false;
     338                        pasteBinElm = lastRng = null;
     339                }
     340
     341                /**
     342                 * Returns the contents of the paste bin as a HTML string.
     343                 *
     344                 * @return {String} Get the contents of the paste bin.
     345                 */
     346                function getPasteBinHtml() {
     347                        return pasteBinElm ? pasteBinElm.innerHTML : pasteBinDefaultContent;
     348                }
     349
     350                /**
     351                 * Gets various content types out of a datatransfer object.
     352                 *
     353                 * @param {DataTransfer} dataTransfer Event fired on paste.
     354                 * @return {Object} Object with mime types and data for those mime types.
     355                 */
     356                function getDataTransferItems(dataTransfer) {
     357                        var data = {};
     358
     359                        if (dataTransfer && dataTransfer.types) {
     360                                data['text/plain'] = dataTransfer.getData('Text');
     361
     362                                for (var i = 0; i < dataTransfer.types.length; i++) {
     363                                        var contentType = dataTransfer.types[i];
     364                                        data[contentType] = dataTransfer.getData(contentType);
     365                                }
     366                        }
     367
     368                        return data;
     369                }
     370
     371                /**
     372                 * Gets various content types out of the Clipboard API. It will also get the
     373                 * plain text using older IE and WebKit API:s.
     374                 *
     375                 * @param {ClipboardEvent} clipboardEvent Event fired on paste.
     376                 * @return {Object} Object with mime types and data for those mime types.
     377                 */
     378                function getClipboardContent(clipboardEvent) {
     379                        return getDataTransferItems(clipboardEvent.clipboardData || editor.getDoc().dataTransfer);
     380                }
     381
     382                function getCaretRangeFromEvent(e) {
     383                        var doc = editor.getDoc(), rng;
     384
     385                        if (doc.caretPositionFromPoint) {
     386                                var point = doc.caretPositionFromPoint(e.pageX, e.pageY);
     387                                rng = doc.createRange();
     388                                rng.setStart(point.offsetNode, point.offset);
     389                                rng.collapse(true);
     390                        } else if (doc.caretRangeFromPoint) {
     391                                rng = doc.caretRangeFromPoint(e.pageX, e.pageY);
     392                        }
     393
     394                        return rng;
     395                }
     396
     397                editor.on('keydown', function(e) {
     398                        if (e.isDefaultPrevented()) {
     399                                return;
     400                        }
     401
     402                        // Ctrl+V or Shift+Insert
     403                        if ((VK.metaKeyPressed(e) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45)) {
     404                                keyboardPastePlainTextState = e.shiftKey && e.keyCode == 86;
     405
     406                                // Prevent undoManager keydown handler from making an undo level with the pastebin in it
     407                                e.stopImmediatePropagation();
     408
     409                                keyboardPasteTimeStamp = new Date().getTime();
     410
     411                                // IE doesn't support Ctrl+Shift+V and it doesn't even produce a paste event
     412                                // so lets fake a paste event and let IE use the execCommand/dataTransfer methods
     413                                if (Env.ie && keyboardPastePlainTextState) {
     414                                        e.preventDefault();
     415                                        editor.fire('paste', {ieFake: true});
     416                                        return;
     417                                }
     418
     419                                createPasteBin();
     420                        }
     421                });
     422
     423                editor.on('paste', function(e) {
     424                        var clipboardContent = getClipboardContent(e);
     425                        var isKeyBoardPaste = new Date().getTime() - keyboardPasteTimeStamp < 1000;
     426                        var plainTextMode = self.pasteFormat == "text" || keyboardPastePlainTextState;
     427
     428                        // Not a keyboard paste prevent default paste and try to grab the clipboard contents using different APIs
     429                        if (!isKeyBoardPaste) {
     430                                e.preventDefault();
     431                        }
     432
     433                        // Try IE only method if paste isn't a keyboard paste
     434                        if (Env.ie && (!isKeyBoardPaste || e.ieFake)) {
     435                                createPasteBin();
     436
     437                                editor.dom.bind(pasteBinElm, 'paste', function(e) {
     438                                        e.stopPropagation();
     439                                });
     440
     441                                editor.getDoc().execCommand('Paste', false, null);
     442                                clipboardContent["text/html"] = getPasteBinHtml();
     443                        }
     444
     445                        setTimeout(function() {
     446                                var html = getPasteBinHtml();
     447
     448                                // WebKit has a nice bug where it clones the paste bin if you paste from for example notepad
     449                                if (pasteBinElm && pasteBinElm.firstChild && pasteBinElm.firstChild.id === 'mcepastebin') {
     450                                        plainTextMode = true;
     451                                }
     452
     453                                removePasteBin();
     454
     455                                if (html == pasteBinDefaultContent || !isKeyBoardPaste) {
     456                                        html = clipboardContent['text/html'] || clipboardContent['text/plain'] || pasteBinDefaultContent;
     457
     458                                        if (html == pasteBinDefaultContent) {
     459                                                if (!isKeyBoardPaste) {
     460                                                        editor.windowManager.alert('Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents.');
     461                                                }
     462
     463                                                return;
     464                                        }
     465                                }
     466
     467                                if (plainTextMode) {
     468                                        pasteText(clipboardContent['text/plain'] || Utils.innerText(html));
     469                                } else {
     470                                        pasteHtml(html);
     471                                }
     472                        }, 0);
     473                });
     474
     475                editor.on('dragstart', function(e) {
     476                        if (e.dataTransfer.types) {
     477                                e.dataTransfer.setData('mce-internal', editor.selection.getContent());
     478                        }
     479                });
     480
     481                editor.on('drop', function(e) {
     482                        var rng = getCaretRangeFromEvent(e);
     483
     484                        if (rng) {
     485                                var dropContent = getDataTransferItems(e.dataTransfer);
     486                                var content = dropContent['mce-internal'] || dropContent['text/html'] || dropContent['text/plain'];
     487
     488                                if (content) {
     489                                        e.preventDefault();
     490
     491                                        editor.undoManager.transact(function() {
     492                                                if (dropContent['mce-internal']) {
     493                                                        editor.execCommand('Delete');
     494                                                }
     495
     496                                                editor.selection.setRng(rng);
     497
     498                                                if (!dropContent['text/html']) {
     499                                                        pasteText(content);
     500                                                } else {
     501                                                        pasteHtml(content);
     502                                                }
     503                                        });
     504                                }
     505                        }
     506                });
     507
     508                self.pasteHtml = pasteHtml;
     509                self.pasteText = pasteText;
     510
     511                // Remove all data images from paste for example from Gecko
     512                // except internal images like video elements
     513                editor.on('preInit', function() {
     514                        editor.parser.addNodeFilter('img', function(nodes) {
     515                                if (!editor.settings.paste_data_images) {
     516                                        var i = nodes.length;
     517
     518                                        while (i--) {
     519                                                var src = nodes[i].attributes.map.src;
     520                                                if (src && src.indexOf('data:image') === 0) {
     521                                                        if (!nodes[i].attr('data-mce-object') && src !== Env.transparentSrc) {
     522                                                                nodes[i].remove();
     523                                                        }
     524                                                }
     525                                        }
     526                                }
    66527                        });
    67 
    68                         ed.onKeyDown.addToTop(function(ed, e) {
    69                                 // Block ctrl+v from adding an undo level since the default logic in tinymce.Editor will add that
    70                                 if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
    71                                         return false; // Stop other listeners
    72                         });
    73 
    74                         // Initialize plain text flag
    75                         ed.pasteAsPlainText = getParam(ed, 'paste_text_sticky_default');
    76 
    77                         // This function executes the process handlers and inserts the contents
    78                         // force_rich overrides plain text mode set by user, important for pasting with execCommand
    79                         function process(o, force_rich) {
    80                                 var dom = ed.dom, rng;
    81 
    82                                 // Execute pre process handlers
    83                                 t.onPreProcess.dispatch(t, o);
    84 
    85                                 // Create DOM structure
    86                                 o.node = dom.create('div', 0, o.content);
    87 
    88                                 // If pasting inside the same element and the contents is only one block
    89                                 // remove the block and keep the text since Firefox will copy parts of pre and h1-h6 as a pre element
    90                                 if (tinymce.isGecko) {
    91                                         rng = ed.selection.getRng(true);
    92                                         if (rng.startContainer == rng.endContainer && rng.startContainer.nodeType == 3) {
    93                                                 // Is only one block node and it doesn't contain word stuff
    94                                                 if (o.node.childNodes.length === 1 && /^(p|h[1-6]|pre)$/i.test(o.node.firstChild.nodeName) && o.content.indexOf('__MCE_ITEM__') === -1)
    95                                                         dom.remove(o.node.firstChild, true);
     528                });
     529
     530                // Fix for #6504 we need to remove the paste bin on IE if the user paste in a file
     531                editor.on('PreProcess', function() {
     532                        editor.dom.remove(editor.dom.get('mcepastebin'));
     533                });
     534        };
     535});
     536
     537// Included from: js/tinymce/plugins/paste/classes/WordFilter.js
     538
     539/**
     540 * WordFilter.js
     541 *
     542 * Copyright, Moxiecode Systems AB
     543 * Released under LGPL License.
     544 *
     545 * License: http://www.tinymce.com/license
     546 * Contributing: http://www.tinymce.com/contributing
     547 */
     548
     549/**
     550 * This class parses word HTML into proper TinyMCE markup.
     551 *
     552 * @class tinymce.pasteplugin.Quirks
     553 * @private
     554 */
     555define("tinymce/pasteplugin/WordFilter", [
     556        "tinymce/util/Tools",
     557        "tinymce/html/DomParser",
     558        "tinymce/html/Schema",
     559        "tinymce/html/Serializer",
     560        "tinymce/html/Node",
     561        "tinymce/pasteplugin/Utils"
     562], function(Tools, DomParser, Schema, Serializer, Node, Utils) {
     563        function isWordContent(content) {
     564                return (/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i).test(content);
     565        }
     566
     567        function WordFilter(editor) {
     568                var settings = editor.settings;
     569
     570                editor.on('BeforePastePreProcess', function(e) {
     571                        var content = e.content, retainStyleProperties, validStyles;
     572
     573                        retainStyleProperties = settings.paste_retain_style_properties;
     574                        if (retainStyleProperties) {
     575                                validStyles = Tools.makeMap(retainStyleProperties);
     576                        }
     577
     578                        /**
     579                         * Converts fake bullet and numbered lists to real semantic OL/UL.
     580                         *
     581                         * @param {tinymce.html.Node} node Root node to convert children of.
     582                         */
     583                        function convertFakeListsToProperLists(node) {
     584                                var currentListNode, prevListNode, lastLevel = 1;
     585
     586                                function convertParagraphToLi(paragraphNode, listStartTextNode, listName, start) {
     587                                        var level = paragraphNode._listLevel || lastLevel;
     588
     589                                        // Handle list nesting
     590                                        if (level != lastLevel) {
     591                                                if (level < lastLevel) {
     592                                                        // Move to parent list
     593                                                        if (currentListNode) {
     594                                                                currentListNode = currentListNode.parent.parent;
     595                                                        }
     596                                                } else {
     597                                                        // Create new list
     598                                                        prevListNode = currentListNode;
     599                                                        currentListNode = null;
     600                                                }
    96601                                        }
    97                                 }
    98 
    99                                 // Execute post process handlers
    100                                 t.onPostProcess.dispatch(t, o);
    101 
    102                                 // Serialize content
    103                                 o.content = ed.serializer.serialize(o.node, {getInner : 1, forced_root_block : ''});
    104 
    105                                 // Plain text option active?
    106                                 if ((!force_rich) && (ed.pasteAsPlainText)) {
    107                                         t._insertPlainText(o.content);
    108 
    109                                         if (!getParam(ed, "paste_text_sticky")) {
    110                                                 ed.pasteAsPlainText = false;
    111                                                 ed.controlManager.setActive("pastetext", false);
     602
     603                                        if (!currentListNode || currentListNode.name != listName) {
     604                                                prevListNode = prevListNode || currentListNode;
     605                                                currentListNode = new Node(listName, 1);
     606
     607                                                if (start > 1) {
     608                                                        currentListNode.attr('start', '' + start);
     609                                                }
     610
     611                                                paragraphNode.wrap(currentListNode);
     612                                        } else {
     613                                                currentListNode.append(paragraphNode);
    112614                                        }
    113                                 } else {
    114                                         t._insert(o.content);
    115                                 }
    116                         }
    117 
    118                         // Add command for external usage
    119                         ed.addCommand('mceInsertClipboardContent', function(u, o) {
    120                                 process(o, true);
    121                         });
    122 
    123                         if (!getParam(ed, "paste_text_use_dialog")) {
    124                                 ed.addCommand('mcePasteText', function(u, v) {
    125                                         var cookie = tinymce.util.Cookie;
    126 
    127                                         ed.pasteAsPlainText = !ed.pasteAsPlainText;
    128                                         ed.controlManager.setActive('pastetext', ed.pasteAsPlainText);
    129 
    130                                         if ((ed.pasteAsPlainText) && (!cookie.get("tinymcePasteText"))) {
    131                                                 if (getParam(ed, "paste_text_sticky")) {
    132                                                         ed.windowManager.alert(ed.translate('paste.plaintext_mode_sticky'));
    133                                                 } else {
    134                                                         ed.windowManager.alert(ed.translate('paste.plaintext_mode'));
    135                                                 }
    136 
    137                                                 if (!getParam(ed, "paste_text_notifyalways")) {
    138                                                         cookie.set("tinymcePasteText", "1", new Date(new Date().getFullYear() + 1, 12, 31))
     615
     616                                        paragraphNode.name = 'li';
     617                                        listStartTextNode.value = '';
     618
     619                                        var nextNode = listStartTextNode.next;
     620                                        if (nextNode && nextNode.type == 3) {
     621                                                nextNode.value = nextNode.value.replace(/^\u00a0+/, '');
     622                                        }
     623
     624                                        // Append list to previous list if it exists
     625                                        if (level > lastLevel && prevListNode) {
     626                                                prevListNode.lastChild.append(currentListNode);
     627                                        }
     628
     629                                        lastLevel = level;
     630                                }
     631
     632                                var paragraphs = node.getAll('p');
     633
     634                                for (var i = 0; i < paragraphs.length; i++) {
     635                                        node = paragraphs[i];
     636
     637                                        if (node.name == 'p' && node.firstChild) {
     638                                                // Find first text node in paragraph
     639                                                var nodeText = '';
     640                                                var listStartTextNode = node.firstChild;
     641
     642                                                while (listStartTextNode) {
     643                                                        nodeText = listStartTextNode.value;
     644                                                        if (nodeText) {
     645                                                                break;
     646                                                        }
     647
     648                                                        listStartTextNode = listStartTextNode.firstChild;
     649                                                }
     650
     651                                                // Detect unordered lists look for bullets
     652                                                if (/^\s*[\u2022\u00b7\u00a7\u00d8\u25CF]\s*$/.test(nodeText)) {
     653                                                        convertParagraphToLi(node, listStartTextNode, 'ul');
     654                                                        continue;
     655                                                }
     656
     657                                                // Detect ordered lists 1., a. or ixv.
     658                                                if (/^\s*\w+\.$/.test(nodeText)) {
     659                                                        // Parse OL start number
     660                                                        var matches = /([0-9])\./.exec(nodeText);
     661                                                        var start = 1;
     662                                                        if (matches) {
     663                                                                start = parseInt(matches[1], 10);
     664                                                        }
     665
     666                                                        convertParagraphToLi(node, listStartTextNode, 'ol', start);
     667                                                        continue;
     668                                                }
     669
     670                                                currentListNode = null;
     671                                        }
     672                                }
     673                        }
     674
     675                        function filterStyles(node, styleValue) {
     676                                // Parse out list indent level for lists
     677                                if (node.name === 'p') {
     678                                        var matches = /mso-list:\w+ \w+([0-9]+)/.exec(styleValue);
     679
     680                                        if (matches) {
     681                                                node._listLevel = parseInt(matches[1], 10);
     682                                        }
     683                                }
     684
     685                                if (editor.getParam("paste_retain_style_properties", "none")) {
     686                                        var outputStyle = "";
     687
     688                                        Tools.each(editor.dom.parseStyle(styleValue), function(value, name) {
     689                                                // Convert various MS styles to W3C styles
     690                                                switch (name) {
     691                                                        case "horiz-align":
     692                                                                name = "text-align";
     693                                                                return;
     694
     695                                                        case "vert-align":
     696                                                                name = "vertical-align";
     697                                                                return;
     698
     699                                                        case "font-color":
     700                                                        case "mso-foreground":
     701                                                                name = "color";
     702                                                                return;
     703
     704                                                        case "mso-background":
     705                                                        case "mso-highlight":
     706                                                                name = "background";
     707                                                                break;
     708                                                }
     709
     710                                                // Output only valid styles
     711                                                if (retainStyleProperties == "all" || (validStyles && validStyles[name])) {
     712                                                        outputStyle += name + ':' + value + ';';
     713                                                }
     714                                        });
     715
     716                                        if (outputStyle) {
     717                                                return outputStyle;
     718                                        }
     719                                }
     720
     721                                return null;
     722                        }
     723
     724                        if (settings.paste_enable_default_filters === false) {
     725                                return;
     726                        }
     727
     728                        // Detect is the contents is Word junk HTML
     729                        if (isWordContent(e.content)) {
     730                                e.wordContent = true; // Mark it for other processors
     731
     732                                // Remove basic Word junk
     733                                content = Utils.filter(content, [
     734                                        // Word comments like conditional comments etc
     735                                        /<!--[\s\S]+?-->/gi,
     736
     737                                        // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content,
     738                                        // MS Office namespaced tags, and a few other tags
     739                                        /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,
     740
     741                                        // Convert <s> into <strike> for line-though
     742                                        [/<(\/?)s>/gi, "<$1strike>"],
     743
     744                                        // Replace nsbp entites to char since it's easier to handle
     745                                        [/&nbsp;/gi, "\u00a0"],
     746
     747                                        // Convert <span style="mso-spacerun:yes">___</span> to string of alternating
     748                                        // breaking/non-breaking spaces of same length
     749                                        [/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,
     750                                                function(str, spaces) {
     751                                                        return (spaces.length > 0) ?
     752                                                                spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : "";
     753                                                }
     754                                        ]
     755                                ]);
     756
     757                                var validElements = settings.paste_word_valid_elements;
     758                                if (!validElements) {
     759                                        validElements = '@[style],-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,' +
     760                                                '-table,-tr,-td[colspan|rowspan],-th,-thead,-tfoot,-tbody,-a[href|name],sub,sup,strike,br';
     761                                }
     762
     763                                // Setup strict schema
     764                                var schema = new Schema({
     765                                        valid_elements: validElements
     766                                });
     767
     768                                // Parse HTML into DOM structure
     769                                var domParser = new DomParser({}, schema);
     770
     771                                domParser.addAttributeFilter('style', function(nodes) {
     772                                        var i = nodes.length, node;
     773
     774                                        while (i--) {
     775                                                node = nodes[i];
     776                                                node.attr('style', filterStyles(node, node.attr('style')));
     777
     778                                                // Remove pointess spans
     779                                                if (node.name == 'span' && !node.attributes.length) {
     780                                                        node.unwrap();
    139781                                                }
    140782                                        }
    141783                                });
    142                         }
    143 
    144                         ed.addButton('pastetext', {title: 'paste.paste_text_desc', cmd: 'mcePasteText'});
    145                         ed.addButton('selectall', {title: 'paste.selectall_desc', cmd: 'selectall'});
    146 
    147                         // This function grabs the contents from the clipboard by adding a
    148                         // hidden div and placing the caret inside it and after the browser paste
    149                         // is done it grabs that contents and processes that
    150                         function grabContent(e) {
    151                                 var n, or, rng, oldRng, sel = ed.selection, dom = ed.dom, body = ed.getBody(), posY, textContent;
    152 
    153                                 // Check if browser supports direct plaintext access
    154                                 if (e.clipboardData || dom.doc.dataTransfer) {
    155                                         textContent = (e.clipboardData || dom.doc.dataTransfer).getData('Text');
    156 
    157                                         if (ed.pasteAsPlainText) {
    158                                                 e.preventDefault();
    159                                                 process({content : dom.encode(textContent).replace(/\r?\n/g, '<br />')});
    160                                                 return;
     784
     785                                domParser.addNodeFilter('a', function(nodes) {
     786                                        var i = nodes.length, node, href, name;
     787
     788                                        while (i--) {
     789                                                node = nodes[i];
     790                                                href = node.attr('href');
     791                                                name = node.attr('name');
     792
     793                                                if (href && href.indexOf('file://') === 0) {
     794                                                        href = href.split('#')[1];
     795                                                        if (href) {
     796                                                                href = '#' + href;
     797                                                        }
     798                                                }
     799
     800                                                if (!href && !name) {
     801                                                        node.unwrap();
     802                                                } else {
     803                                                        node.attr({
     804                                                                href: href,
     805                                                                name: name
     806                                                        });
     807                                                }
    161808                                        }
    162                                 }
    163 
    164                                 if (dom.get('_mcePaste'))
    165                                         return;
    166 
    167                                 // Create container to paste into
    168                                 n = dom.add(body, 'div', {id : '_mcePaste', 'class' : 'mcePaste', 'data-mce-bogus' : '1'}, '\uFEFF\uFEFF');
    169 
    170                                 // If contentEditable mode we need to find out the position of the closest element
    171                                 if (body != ed.getDoc().body)
    172                                         posY = dom.getPos(ed.selection.getStart(), body).y;
    173                                 else
    174                                         posY = body.scrollTop + dom.getViewPort(ed.getWin()).y;
    175 
    176                                 // Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles
    177                                 // If also needs to be in view on IE or the paste would fail
    178                                 dom.setStyles(n, {
    179                                         position : 'absolute',
    180                                         left : tinymce.isGecko ? -40 : 0, // Need to move it out of site on Gecko since it will othewise display a ghost resize rect for the div
    181                                         top : posY - 25,
    182                                         width : 1,
    183                                         height : 1,
    184                                         overflow : 'hidden'
    185809                                });
    186 
    187                                 if (tinymce.isIE) {
    188                                         // Store away the old range
    189                                         oldRng = sel.getRng();
    190 
    191                                         // Select the container
    192                                         rng = dom.doc.body.createTextRange();
    193                                         rng.moveToElementText(n);
    194                                         rng.execCommand('Paste');
    195 
    196                                         // Remove container
    197                                         dom.remove(n);
    198 
    199                                         // Check if the contents was changed, if it wasn't then clipboard extraction failed probably due
    200                                         // to IE security settings so we pass the junk though better than nothing right
    201                                         if (n.innerHTML === '\uFEFF\uFEFF') {
    202                                                 ed.execCommand('mcePasteWord');
    203                                                 e.preventDefault();
    204                                                 return;
    205                                         }
    206 
    207                                         // Restore the old range and clear the contents before pasting
    208                                         sel.setRng(oldRng);
    209                                         sel.setContent('');
    210 
    211                                         // For some odd reason we need to detach the the mceInsertContent call from the paste event
    212                                         // It's like IE has a reference to the parent element that you paste in and the selection gets messed up
    213                                         // when it tries to restore the selection
    214                                         setTimeout(function() {
    215                                                 // Process contents
    216                                                 process({content : n.innerHTML});
    217                                         }, 0);
    218 
    219                                         // Block the real paste event
    220                                         return tinymce.dom.Event.cancel(e);
    221                                 } else {
    222                                         function block(e) {
    223                                                 e.preventDefault();
    224                                         };
    225 
    226                                         // Block mousedown and click to prevent selection change
    227                                         dom.bind(ed.getDoc(), 'mousedown', block);
    228                                         dom.bind(ed.getDoc(), 'keydown', block);
    229 
    230                                         or = ed.selection.getRng();
    231 
    232                                         // Move select contents inside DIV
    233                                         n = n.firstChild;
    234                                         rng = ed.getDoc().createRange();
    235                                         rng.setStart(n, 0);
    236                                         rng.setEnd(n, 2);
    237                                         sel.setRng(rng);
    238 
    239                                         // Wait a while and grab the pasted contents
    240                                         window.setTimeout(function() {
    241                                                 var h = '', nl;
    242 
    243                                                 // Paste divs duplicated in paste divs seems to happen when you paste plain text so lets first look for that broken behavior in WebKit
    244                                                 if (!dom.select('div.mcePaste > div.mcePaste').length) {
    245                                                         nl = dom.select('div.mcePaste');
    246 
    247                                                         // WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string
    248                                                         each(nl, function(n) {
    249                                                                 var child = n.firstChild;
    250 
    251                                                                 // WebKit inserts a DIV container with lots of odd styles
    252                                                                 if (child && child.nodeName == 'DIV' && child.style.marginTop && child.style.backgroundColor) {
    253                                                                         dom.remove(child, 1);
    254                                                                 }
    255 
    256                                                                 // Remove apply style spans
    257                                                                 each(dom.select('span.Apple-style-span', n), function(n) {
    258                                                                         dom.remove(n, 1);
    259                                                                 });
    260 
    261                                                                 // Remove bogus br elements
    262                                                                 each(dom.select('br[data-mce-bogus]', n), function(n) {
    263                                                                         dom.remove(n);
    264                                                                 });
    265 
    266                                                                 // WebKit will make a copy of the DIV for each line of plain text pasted and insert them into the DIV
    267                                                                 if (n.parentNode.className != 'mcePaste')
    268                                                                         h += n.innerHTML;
    269                                                         });
    270                                                 } else {
    271                                                         // Found WebKit weirdness so force the content into paragraphs this seems to happen when you paste plain text from Nodepad etc
    272                                                         // So this logic will replace double enter with paragraphs and single enter with br so it kind of looks the same
    273                                                         h = '<p>' + dom.encode(textContent).replace(/\r?\n\r?\n/g, '</p><p>').replace(/\r?\n/g, '<br />') + '</p>';
    274                                                 }
    275 
    276                                                 // Remove the nodes
    277                                                 each(dom.select('div.mcePaste'), function(n) {
    278                                                         dom.remove(n);
    279                                                 });
    280 
    281                                                 // Restore the old selection
    282                                                 if (or)
    283                                                         sel.setRng(or);
    284 
    285                                                 process({content : h});
    286 
    287                                                 // Unblock events ones we got the contents
    288                                                 dom.unbind(ed.getDoc(), 'mousedown', block);
    289                                                 dom.unbind(ed.getDoc(), 'keydown', block);
    290                                         }, 0);
    291                                 }
    292                         }
    293 
    294                         // Check if we should use the new auto process method                   
    295                         if (getParam(ed, "paste_auto_cleanup_on_paste")) {
    296                                 // Is it's Opera or older FF use key handler
    297                                 if (tinymce.isOpera || /Firefox\/2/.test(navigator.userAgent)) {
    298                                         ed.onKeyDown.addToTop(function(ed, e) {
    299                                                 if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
    300                                                         grabContent(e);
    301                                         });
    302                                 } else {
    303                                         // Grab contents on paste event on Gecko and WebKit
    304                                         ed.onPaste.addToTop(function(ed, e) {
    305                                                 return grabContent(e);
    306                                         });
    307                                 }
    308                         }
    309 
    310                         ed.onInit.add(function() {
    311                                 ed.controlManager.setActive("pastetext", ed.pasteAsPlainText);
    312 
    313                                 // Block all drag/drop events
    314                                 if (getParam(ed, "paste_block_drop")) {
    315                                         ed.dom.bind(ed.getBody(), ['dragend', 'dragover', 'draggesture', 'dragdrop', 'drop', 'drag'], function(e) {
    316                                                 e.preventDefault();
    317                                                 e.stopPropagation();
    318 
    319                                                 return false;
    320                                         });
    321                                 }
     810                                // Parse into DOM structure
     811                                var rootNode = domParser.parse(content);
     812
     813                                // Process DOM
     814                                convertFakeListsToProperLists(rootNode);
     815
     816                                // Serialize DOM back to HTML
     817                                e.content = new Serializer({}, schema).serialize(rootNode);
     818                        }
     819                });
     820        }
     821
     822        WordFilter.isWordContent = isWordContent;
     823
     824        return WordFilter;
     825});
     826
     827// Included from: js/tinymce/plugins/paste/classes/Quirks.js
     828
     829/**
     830 * Quirks.js
     831 *
     832 * Copyright, Moxiecode Systems AB
     833 * Released under LGPL License.
     834 *
     835 * License: http://www.tinymce.com/license
     836 * Contributing: http://www.tinymce.com/contributing
     837 */
     838
     839/**
     840 * This class contains various fixes for browsers. These issues can not be feature
     841 * detected since we have no direct control over the clipboard. However we might be able
     842 * to remove some of these fixes once the browsers gets updated/fixed.
     843 *
     844 * @class tinymce.pasteplugin.Quirks
     845 * @private
     846 */
     847define("tinymce/pasteplugin/Quirks", [
     848        "tinymce/Env",
     849        "tinymce/util/Tools",
     850        "tinymce/pasteplugin/WordFilter",
     851        "tinymce/pasteplugin/Utils"
     852], function(Env, Tools, WordFilter, Utils) {
     853        "use strict";
     854
     855        return function(editor) {
     856                function addPreProcessFilter(filterFunc) {
     857                        editor.on('BeforePastePreProcess', function(e) {
     858                                e.content = filterFunc(e.content);
    322859                        });
    323 
    324                         // Add legacy support
    325                         t._legacySupport();
    326                 },
    327 
    328                 getInfo : function() {
    329                         return {
    330                                 longname : 'Paste text/word',
    331                                 author : 'Moxiecode Systems AB',
    332                                 authorurl : 'http://tinymce.moxiecode.com',
    333                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste',
    334                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
    335                         };
    336                 },
    337 
    338                 _preProcess : function(pl, o) {
    339                         var ed = this.editor,
    340                                 h = o.content,
    341                                 grep = tinymce.grep,
    342                                 explode = tinymce.explode,
    343                                 trim = tinymce.trim,
    344                                 len, stripClass;
    345 
    346                         //console.log('Before preprocess:' + o.content);
    347 
    348                         function process(items) {
    349                                 each(items, function(v) {
    350                                         // Remove or replace
    351                                         if (v.constructor == RegExp)
    352                                                 h = h.replace(v, '');
    353                                         else
    354                                                 h = h.replace(v[0], v[1]);
    355                                 });
    356                         }
    357                        
    358                         if (ed.settings.paste_enable_default_filters == false) {
    359                                 return;
    360                         }
    361 
    362                         // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
    363                         if (tinymce.isIE && document.documentMode >= 9 && /<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(o.content)) {
    364                                 // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
    365                                 process([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g, '$1']]);
    366 
    367                                 // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
    368                                 process([
    369                                         [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
    370                                         [/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s
    371                                         [/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR
    372                                 ]);
    373                         }
    374 
    375                         // Detect Word content and process it more aggressive
    376                         if (/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(h) || o.wordContent) {
    377                                 o.wordContent = true;                   // Mark the pasted contents as word specific content
    378                                 //console.log('Word contents detected.');
    379 
    380                                 // Process away some basic content
    381                                 process([
    382                                         /^\s*(&nbsp;)+/gi,                              // &nbsp; entities at the start of contents
    383                                         /(&nbsp;|<br[^>]*>)+\s*$/gi             // &nbsp; entities at the end of contents
    384                                 ]);
    385 
    386                                 if (getParam(ed, "paste_convert_headers_to_strong")) {
    387                                         h = h.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>");
    388                                 }
    389 
    390                                 if (getParam(ed, "paste_convert_middot_lists")) {
    391                                         process([
    392                                                 [/<!--\[if !supportLists\]-->/gi, '$&__MCE_ITEM__'],                                    // Convert supportLists to a list item marker
    393                                                 [/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi, '$1__MCE_ITEM__'],             // Convert mso-list and symbol spans to item markers
    394                                                 [/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi, '$1__MCE_ITEM__']                             // Convert mso-list and symbol paragraphs to item markers (FF)
    395                                         ]);
    396                                 }
    397 
    398                                 process([
    399                                         // Word comments like conditional comments etc
    400                                         /<!--[\s\S]+?-->/gi,
    401 
    402                                         // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, MS Office namespaced tags, and a few other tags
    403                                         /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,
    404 
    405                                         // Convert <s> into <strike> for line-though
    406                                         [/<(\/?)s>/gi, "<$1strike>"],
    407 
    408                                         // Replace nsbp entites to char since it's easier to handle
    409                                         [/&nbsp;/gi, "\u00a0"]
    410                                 ]);
    411 
    412                                 // Remove bad attributes, with or without quotes, ensuring that attribute text is really inside a tag.
    413                                 // If JavaScript had a RegExp look-behind, we could have integrated this with the last process() array and got rid of the loop. But alas, it does not, so we cannot.
    414                                 do {
    415                                         len = h.length;
    416                                         h = h.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi, "$1");
    417                                 } while (len != h.length);
    418 
    419                                 // Remove all spans if no styles is to be retained
    420                                 if (getParam(ed, "paste_retain_style_properties").replace(/^none$/i, "").length == 0) {
    421                                         h = h.replace(/<\/?span[^>]*>/gi, "");
    422                                 } else {
    423                                         // We're keeping styles, so at least clean them up.
    424                                         // CSS Reference: http://msdn.microsoft.com/en-us/library/aa155477.aspx
    425 
    426                                         process([
    427                                                 // Convert <span style="mso-spacerun:yes">___</span> to string of alternating breaking/non-breaking spaces of same length
    428                                                 [/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,
    429                                                         function(str, spaces) {
    430                                                                 return (spaces.length > 0)? spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : "";
    431                                                         }
    432                                                 ],
    433 
    434                                                 // Examine all styles: delete junk, transform some, and keep the rest
    435                                                 [/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,
    436                                                         function(str, tag, style) {
    437                                                                 var n = [],
    438                                                                         i = 0,
    439                                                                         s = explode(trim(style).replace(/&quot;/gi, "'"), ";");
    440 
    441                                                                 // Examine each style definition within the tag's style attribute
    442                                                                 each(s, function(v) {
    443                                                                         var name, value,
    444                                                                                 parts = explode(v, ":");
    445 
    446                                                                         function ensureUnits(v) {
    447                                                                                 return v + ((v !== "0") && (/\d$/.test(v)))? "px" : "";
    448                                                                         }
    449 
    450                                                                         if (parts.length == 2) {
    451                                                                                 name = parts[0].toLowerCase();
    452                                                                                 value = parts[1].toLowerCase();
    453 
    454                                                                                 // Translate certain MS Office styles into their CSS equivalents
    455                                                                                 switch (name) {
    456                                                                                         case "mso-padding-alt":
    457                                                                                         case "mso-padding-top-alt":
    458                                                                                         case "mso-padding-right-alt":
    459                                                                                         case "mso-padding-bottom-alt":
    460                                                                                         case "mso-padding-left-alt":
    461                                                                                         case "mso-margin-alt":
    462                                                                                         case "mso-margin-top-alt":
    463                                                                                         case "mso-margin-right-alt":
    464                                                                                         case "mso-margin-bottom-alt":
    465                                                                                         case "mso-margin-left-alt":
    466                                                                                         case "mso-table-layout-alt":
    467                                                                                         case "mso-height":
    468                                                                                         case "mso-width":
    469                                                                                         case "mso-vertical-align-alt":
    470                                                                                                 n[i++] = name.replace(/^mso-|-alt$/g, "") + ":" + ensureUnits(value);
    471                                                                                                 return;
    472 
    473                                                                                         case "horiz-align":
    474                                                                                                 n[i++] = "text-align:" + value;
    475                                                                                                 return;
    476 
    477                                                                                         case "vert-align":
    478                                                                                                 n[i++] = "vertical-align:" + value;
    479                                                                                                 return;
    480 
    481                                                                                         case "font-color":
    482                                                                                         case "mso-foreground":
    483                                                                                                 n[i++] = "color:" + value;
    484                                                                                                 return;
    485 
    486                                                                                         case "mso-background":
    487                                                                                         case "mso-highlight":
    488                                                                                                 n[i++] = "background:" + value;
    489                                                                                                 return;
    490 
    491                                                                                         case "mso-default-height":
    492                                                                                                 n[i++] = "min-height:" + ensureUnits(value);
    493                                                                                                 return;
    494 
    495                                                                                         case "mso-default-width":
    496                                                                                                 n[i++] = "min-width:" + ensureUnits(value);
    497                                                                                                 return;
    498 
    499                                                                                         case "mso-padding-between-alt":
    500                                                                                                 n[i++] = "border-collapse:separate;border-spacing:" + ensureUnits(value);
    501                                                                                                 return;
    502 
    503                                                                                         case "text-line-through":
    504                                                                                                 if ((value == "single") || (value == "double")) {
    505                                                                                                         n[i++] = "text-decoration:line-through";
    506                                                                                                 }
    507                                                                                                 return;
    508 
    509                                                                                         case "mso-zero-height":
    510                                                                                                 if (value == "yes") {
    511                                                                                                         n[i++] = "display:none";
    512                                                                                                 }
    513                                                                                                 return;
    514                                                                                 }
    515 
    516                                                                                 // Eliminate all MS Office style definitions that have no CSS equivalent by examining the first characters in the name
    517                                                                                 if (/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(name)) {
    518                                                                                         return;
    519                                                                                 }
    520 
    521                                                                                 // If it reached this point, it must be a valid CSS style
    522                                                                                 n[i++] = name + ":" + parts[1];         // Lower-case name, but keep value case
    523                                                                         }
    524                                                                 });
    525 
    526                                                                 // If style attribute contained any valid styles the re-write it; otherwise delete style attribute.
    527                                                                 if (i > 0) {
    528                                                                         return tag + ' style="' + n.join(';') + '"';
    529                                                                 } else {
    530                                                                         return tag;
    531                                                                 }
    532                                                         }
    533                                                 ]
    534                                         ]);
    535                                 }
    536                         }
    537 
    538                         // Replace headers with <strong>
    539                         if (getParam(ed, "paste_convert_headers_to_strong")) {
    540                                 process([
    541                                         [/<h[1-6][^>]*>/gi, "<p><strong>"],
    542                                         [/<\/h[1-6][^>]*>/gi, "</strong></p>"]
    543                                 ]);
    544                         }
    545 
    546                         process([
    547                                 // Copy paste from Java like Open Office will produce this junk on FF
    548                                 [/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi, '']
     860                }
     861
     862                /**
     863                 * Removes WebKit fragment comments and converted-space spans.
     864                 *
     865                 * This:
     866                 *   <!--StartFragment-->a<span class="Apple-converted-space">&nbsp;</span>b<!--EndFragment-->
     867                 *
     868                 * Becomes:
     869                 *   a&nbsp;b
     870                 */
     871                function removeWebKitFragments(html) {
     872                        html = Utils.filter(html, [
     873                                /^[\s\S]*<!--StartFragment-->|<!--EndFragment-->[\s\S]*$/g,        // WebKit fragment
     874                                [/<span class="Apple-converted-space">\u00a0<\/span>/g, '\u00a0'], // WebKit &nbsp;
     875                                /<br>$/                                                                                                                    // Traling BR elements
    549876                        ]);
    550877
    551                         // Class attribute options are: leave all as-is ("none"), remove all ("all"), or remove only those starting with mso ("mso").
    552                         // Note:-  paste_strip_class_attributes: "none", verify_css_classes: true is also a good variation.
    553                         stripClass = getParam(ed, "paste_strip_class_attributes");
    554 
    555                         if (stripClass !== "none") {
    556                                 function removeClasses(match, g1) {
    557                                                 if (stripClass === "all")
    558                                                         return '';
    559 
    560                                                 var cls = grep(explode(g1.replace(/^(["'])(.*)\1$/, "$2"), " "),
    561                                                         function(v) {
    562                                                                 return (/^(?!mso)/i.test(v));
    563                                                         }
    564                                                 );
    565 
    566                                                 return cls.length ? ' class="' + cls.join(" ") + '"' : '';
    567                                 };
    568 
    569                                 h = h.replace(/ class="([^"]+)"/gi, removeClasses);
    570                                 h = h.replace(/ class=([\-\w]+)/gi, removeClasses);
    571                         }
    572 
    573                         // Remove spans option
    574                         if (getParam(ed, "paste_remove_spans")) {
    575                                 h = h.replace(/<\/?span[^>]*>/gi, "");
    576                         }
    577 
    578                         //console.log('After preprocess:' + h);
    579 
    580                         o.content = h;
    581                 },
     878                        return html;
     879                }
    582880
    583881                /**
    584                  * Various post process items.
     882                 * Removes BR elements after block elements. IE9 has a nasty bug where it puts a BR element after each
     883                 * block element when pasting from word. This removes those elements.
     884                 *
     885                 * This:
     886                 *  <p>a</p><br><p>b</p>
     887                 *
     888                 * Becomes:
     889                 *  <p>a</p><p>b</p>
    585890                 */
    586                 _postProcess : function(pl, o) {
    587                         var t = this, ed = t.editor, dom = ed.dom, styleProps;
    588 
    589                         if (ed.settings.paste_enable_default_filters == false) {
    590                                 return;
    591                         }
    592                        
    593                         if (o.wordContent) {
    594                                 // Remove named anchors or TOC links
    595                                 each(dom.select('a', o.node), function(a) {
    596                                         if (!a.href || a.href.indexOf('#_Toc') != -1)
    597                                                 dom.remove(a, 1);
    598                                 });
    599 
    600                                 if (getParam(ed, "paste_convert_middot_lists")) {
    601                                         t._convertLists(pl, o);
    602                                 }
    603 
    604                                 // Process styles
    605                                 styleProps = getParam(ed, "paste_retain_style_properties"); // retained properties
    606 
    607                                 // Process only if a string was specified and not equal to "all" or "*"
    608                                 if ((tinymce.is(styleProps, "string")) && (styleProps !== "all") && (styleProps !== "*")) {
    609                                         styleProps = tinymce.explode(styleProps.replace(/^none$/i, ""));
    610 
    611                                         // Retains some style properties
    612                                         each(dom.select('*', o.node), function(el) {
    613                                                 var newStyle = {}, npc = 0, i, sp, sv;
    614 
    615                                                 // Store a subset of the existing styles
    616                                                 if (styleProps) {
    617                                                         for (i = 0; i < styleProps.length; i++) {
    618                                                                 sp = styleProps[i];
    619                                                                 sv = dom.getStyle(el, sp);
    620 
    621                                                                 if (sv) {
    622                                                                         newStyle[sp] = sv;
    623                                                                         npc++;
    624                                                                 }
    625                                                         }
    626                                                 }
    627 
    628                                                 // Remove all of the existing styles
    629                                                 dom.setAttrib(el, 'style', '');
    630 
    631                                                 if (styleProps && npc > 0)
    632                                                         dom.setStyles(el, newStyle); // Add back the stored subset of styles
    633                                                 else // Remove empty span tags that do not have class attributes
    634                                                         if (el.nodeName == 'SPAN' && !el.className)
    635                                                                 dom.remove(el, true);
    636                                         });
    637                                 }
    638                         }
    639 
    640                         // Remove all style information or only specifically on WebKit to avoid the style bug on that browser
    641                         if (getParam(ed, "paste_remove_styles") || (getParam(ed, "paste_remove_styles_if_webkit") && tinymce.isWebKit)) {
    642                                 each(dom.select('*[style]', o.node), function(el) {
    643                                         el.removeAttribute('style');
    644                                         el.removeAttribute('data-mce-style');
    645                                 });
     891                function removeExplorerBrElementsAfterBlocks(html) {
     892                        // Only filter word specific content
     893                        if (!WordFilter.isWordContent(html)) {
     894                                return html;
     895                        }
     896
     897                        // Produce block regexp based on the block elements in schema
     898                        var blockElements = [];
     899
     900                        Tools.each(editor.schema.getBlockElements(), function(block, blockName) {
     901                                blockElements.push(blockName);
     902                        });
     903
     904                        var explorerBlocksRegExp = new RegExp(
     905                                '(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?(' + blockElements.join('|') + ')[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*',
     906                                'g'
     907                        );
     908
     909                        // Remove BR:s from: <BLOCK>X</BLOCK><BR>
     910                        html = Utils.filter(html, [
     911                                [explorerBlocksRegExp, '$1']
     912                        ]);
     913
     914                        // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
     915                        html = Utils.filter(html, [
     916                                [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
     917                                [/<br>/g, ' '],            // Replace single br elements with space since they are word wrap BR:s
     918                                [/<BR><BR>/g, '<br>']      // Replace back the double brs but into a single BR
     919                        ]);
     920
     921                        return html;
     922                }
     923
     924                /**
     925                 * WebKit has a nasty bug where the all runtime styles gets added to style attributes when copy/pasting contents.
     926                 * This fix solves that by simply removing the whole style attribute.
     927                 *
     928                 * Todo: This can be made smarter. Keeping styles that override existing ones etc.
     929                 *
     930                 * @param {String} content Content that needs to be processed.
     931                 * @return {String} Processed contents.
     932                 */
     933                function removeWebKitStyles(content) {
     934                        if (editor.settings.paste_remove_styles || editor.settings.paste_remove_styles_if_webkit !== false) {
     935                                content = content.replace(/ style=\"[^\"]+\"/g, '');
     936                        }
     937
     938                        return content;
     939                }
     940
     941                // Sniff browsers and apply fixes since we can't feature detect
     942                if (Env.webkit) {
     943                        addPreProcessFilter(removeWebKitStyles);
     944                        addPreProcessFilter(removeWebKitFragments);
     945                }
     946
     947                if (Env.ie) {
     948                        addPreProcessFilter(removeExplorerBrElementsAfterBlocks);
     949                }
     950        };
     951});
     952
     953// Included from: js/tinymce/plugins/paste/classes/Plugin.js
     954
     955/**
     956 * Plugin.js
     957 *
     958 * Copyright, Moxiecode Systems AB
     959 * Released under LGPL License.
     960 *
     961 * License: http://www.tinymce.com/license
     962 * Contributing: http://www.tinymce.com/contributing
     963 */
     964
     965/**
     966 * This class contains the tinymce plugin logic for the paste plugin.
     967 *
     968 * @class tinymce.pasteplugin.Plugin
     969 * @private
     970 */
     971define("tinymce/pasteplugin/Plugin", [
     972        "tinymce/PluginManager",
     973        "tinymce/pasteplugin/Clipboard",
     974        "tinymce/pasteplugin/WordFilter",
     975        "tinymce/pasteplugin/Quirks"
     976], function(PluginManager, Clipboard, WordFilter, Quirks) {
     977        var userIsInformed;
     978
     979        PluginManager.add('paste', function(editor) {
     980                var self = this, clipboard, settings = editor.settings;
     981
     982                function togglePlainTextPaste() {
     983                        if (clipboard.pasteFormat == "text") {
     984                                this.active(false);
     985                                clipboard.pasteFormat = "html";
    646986                        } else {
    647                                 if (tinymce.isWebKit) {
    648                                         // We need to compress the styles on WebKit since if you paste <img border="0" /> it will become <img border="0" style="... lots of junk ..." />
    649                                         // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles
    650                                         each(dom.select('*', o.node), function(el) {
    651                                                 el.removeAttribute('data-mce-style');
    652                                         });
    653                                 }
    654                         }
    655                 },
    656 
    657                 /**
    658                  * Converts the most common bullet and number formats in Office into a real semantic UL/LI list.
    659                  */
    660                 _convertLists : function(pl, o) {
    661                         var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html;
    662 
    663                         // Convert middot lists into real semantic lists
    664                         each(dom.select('p', o.node), function(p) {
    665                                 var sib, val = '', type, html, idx, parents;
    666 
    667                                 // Get text node value at beginning of paragraph
    668                                 for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling)
    669                                         val += sib.nodeValue;
    670 
    671                                 val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/&nbsp;/g, '\u00a0');
    672 
    673                                 // Detect unordered lists look for bullets
    674                                 if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(val))
    675                                         type = 'ul';
    676 
    677                                 // Detect ordered lists 1., a. or ixv.
    678                                 if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(val))
    679                                         type = 'ol';
    680 
    681                                 // Check if node value matches the list pattern: o&nbsp;&nbsp;
    682                                 if (type) {
    683                                         margin = parseFloat(p.style.marginLeft || 0);
    684 
    685                                         if (margin > lastMargin)
    686                                                 levels.push(margin);
    687 
    688                                         if (!listElm || type != lastType) {
    689                                                 listElm = dom.create(type);
    690                                                 dom.insertAfter(listElm, p);
    691                                         } else {
    692                                                 // Nested list element
    693                                                 if (margin > lastMargin) {
    694                                                         listElm = li.appendChild(dom.create(type));
    695                                                 } else if (margin < lastMargin) {
    696                                                         // Find parent level based on margin value
    697                                                         idx = tinymce.inArray(levels, margin);
    698                                                         parents = dom.getParents(listElm.parentNode, type);
    699                                                         listElm = parents[parents.length - 1 - idx] || listElm;
    700                                                 }
    701                                         }
    702 
    703                                         // Remove middot or number spans if they exists
    704                                         each(dom.select('span', p), function(span) {
    705                                                 var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, '');
    706 
    707                                                 // Remove span with the middot or the number
    708                                                 if (type == 'ul' && /^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(html))
    709                                                         dom.remove(span);
    710                                                 else if (/^__MCE_ITEM__[\s\S]*\w+\.(&nbsp;|\u00a0)*\s*/.test(html))
    711                                                         dom.remove(span);
    712                                         });
    713 
    714                                         html = p.innerHTML;
    715 
    716                                         // Remove middot/list items
    717                                         if (type == 'ul')
    718                                                 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*(&nbsp;|\u00a0)+\s*/, '');
    719                                         else
    720                                                 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.(&nbsp;|\u00a0)+\s*/, '');
    721 
    722                                         // Create li and add paragraph data into the new li
    723                                         li = listElm.appendChild(dom.create('li', 0, html));
    724                                         dom.remove(p);
    725 
    726                                         lastMargin = margin;
    727                                         lastType = type;
    728                                 } else
    729                                         listElm = lastMargin = 0; // End list element
     987                                clipboard.pasteFormat = "text";
     988                                this.active(true);
     989
     990                                if (!userIsInformed) {
     991                                        editor.windowManager.alert(
     992                                                'Paste is now in plain text mode. Contents will now ' +
     993                                                'be pasted as plain text until you toggle this option off.'
     994                                        );
     995
     996                                        userIsInformed = true;
     997                                }
     998                        }
     999                }
     1000
     1001                self.clipboard = clipboard = new Clipboard(editor);
     1002                self.quirks = new Quirks(editor);
     1003                self.wordFilter = new WordFilter(editor);
     1004
     1005                if (editor.settings.paste_as_text) {
     1006                        self.clipboard.pasteFormat = "text";
     1007                }
     1008
     1009                if (settings.paste_preprocess) {
     1010                        editor.on('PastePreProcess', function(e) {
     1011                                settings.paste_preprocess.call(self, self, e);
    7301012                        });
    731 
    732                         // Remove any left over makers
    733                         html = o.node.innerHTML;
    734                         if (html.indexOf('__MCE_ITEM__') != -1)
    735                                 o.node.innerHTML = html.replace(/__MCE_ITEM__/g, '');
    736                 },
    737 
    738                 /**
    739                  * Inserts the specified contents at the caret position.
    740                  */
    741                 _insert : function(h, skip_undo) {
    742                         var ed = this.editor, r = ed.selection.getRng();
    743 
    744                         // First delete the contents seems to work better on WebKit when the selection spans multiple list items or multiple table cells.
    745                         if (!ed.selection.isCollapsed() && r.startContainer != r.endContainer)
    746                                 ed.getDoc().execCommand('Delete', false, null);
    747 
    748                         ed.execCommand('mceInsertContent', false, h, {skip_undo : skip_undo});
    749                 },
    750 
    751                 /**
    752                  * Instead of the old plain text method which tried to re-create a paste operation, the
    753                  * new approach adds a plain text mode toggle switch that changes the behavior of paste.
    754                  * This function is passed the same input that the regular paste plugin produces.
    755                  * It performs additional scrubbing and produces (and inserts) the plain text.
    756                  * This approach leverages all of the great existing functionality in the paste
    757                  * plugin, and requires minimal changes to add the new functionality.
    758                  * Speednet - June 2009
    759                  */
    760                 _insertPlainText : function(content) {
    761                         var ed = this.editor,
    762                                 linebr = getParam(ed, "paste_text_linebreaktype"),
    763                                 rl = getParam(ed, "paste_text_replacements"),
    764                                 is = tinymce.is;
    765 
    766                         function process(items) {
    767                                 each(items, function(v) {
    768                                         if (v.constructor == RegExp)
    769                                                 content = content.replace(v, "");
    770                                         else
    771                                                 content = content.replace(v[0], v[1]);
    772                                 });
    773                         };
    774 
    775                         if ((typeof(content) === "string") && (content.length > 0)) {
    776                                 // If HTML content with line-breaking tags, then remove all cr/lf chars because only tags will break a line
    777                                 if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(content)) {
    778                                         process([
    779                                                 /[\n\r]+/g
    780                                         ]);
    781                                 } else {
    782                                         // Otherwise just get rid of carriage returns (only need linefeeds)
    783                                         process([
    784                                                 /\r+/g
    785                                         ]);
    786                                 }
    787 
    788                                 process([
    789                                         [/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi, "\n\n"],               // Block tags get a blank line after them
    790                                         [/<br[^>]*>|<\/tr>/gi, "\n"],                           // Single linebreak for <br /> tags and table rows
    791                                         [/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"],           // Table cells get tabs betweem them
    792                                         /<[a-z!\/?][^>]*>/gi,                                           // Delete all remaining tags
    793                                         [/&nbsp;/gi, " "],                                                      // Convert non-break spaces to regular spaces (remember, *plain text*)
    794                                         [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"] // Cool little RegExp deletes whitespace around linebreak chars.
    795                                 ]);
    796 
    797                                 var maxLinebreaks = Number(getParam(ed, "paste_max_consecutive_linebreaks"));
    798                                 if (maxLinebreaks > -1) {
    799                                         var maxLinebreaksRegex = new RegExp("\n{" + (maxLinebreaks + 1) + ",}", "g");
    800                                         var linebreakReplacement = "";
    801 
    802                                         while (linebreakReplacement.length < maxLinebreaks) {
    803                                                 linebreakReplacement += "\n";
    804                                         }
    805 
    806                                         process([
    807                                                 [maxLinebreaksRegex, linebreakReplacement] // Limit max consecutive linebreaks
    808                                         ]);
    809                                 }
    810 
    811                                 content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content));
    812 
    813                                 // Perform default or custom replacements
    814                                 if (is(rl, "array")) {
    815                                         process(rl);
    816                                 } else if (is(rl, "string")) {
    817                                         process(new RegExp(rl, "gi"));
    818                                 }
    819 
    820                                 // Treat paragraphs as specified in the config
    821                                 if (linebr == "none") {
    822                                         // Convert all line breaks to space
    823                                         process([
    824                                                 [/\n+/g, " "]
    825                                         ]);
    826                                 } else if (linebr == "br") {
    827                                         // Convert all line breaks to <br />
    828                                         process([
    829                                                 [/\n/g, "<br />"]
    830                                         ]);
    831                                 } else if (linebr == "p") {
    832                                         // Convert all line breaks to <p>...</p>
    833                                         process([
    834                                                 [/\n+/g, "</p><p>"],
    835                                                 [/^(.*<\/p>)(<p>)$/, '<p>$1']
    836                                         ]);
    837                                 } else {
    838                                         // defaults to "combined"
    839                                         // Convert single line breaks to <br /> and double line breaks to <p>...</p>
    840                                         process([
    841                                                 [/\n\n/g, "</p><p>"],
    842                                                 [/^(.*<\/p>)(<p>)$/, '<p>$1'],
    843                                                 [/\n/g, "<br />"]
    844                                         ]);
    845                                 }
    846 
    847                                 ed.execCommand('mceInsertContent', false, content);
    848                         }
    849                 },
    850 
    851                 /**
    852                  * This method will open the old style paste dialogs. Some users might want the old behavior but still use the new cleanup engine.
    853                  */
    854                 _legacySupport : function() {
    855                         var t = this, ed = t.editor;
    856 
    857                         // Register command(s) for backwards compatibility
    858                         ed.addCommand("mcePasteWord", function() {
    859                                 ed.windowManager.open({
    860                                         file: t.url + "/pasteword.htm",
    861                                         width: parseInt(getParam(ed, "paste_dialog_width")),
    862                                         height: parseInt(getParam(ed, "paste_dialog_height")),
    863                                         inline: 1
    864                                 });
     1013                }
     1014
     1015                if (settings.paste_postprocess) {
     1016                        editor.on('PastePostProcess', function(e) {
     1017                                settings.paste_postprocess.call(self, self, e);
    8651018                        });
    866 
    867                         if (getParam(ed, "paste_text_use_dialog")) {
    868                                 ed.addCommand("mcePasteText", function() {
    869                                         ed.windowManager.open({
    870                                                 file : t.url + "/pastetext.htm",
    871                                                 width: parseInt(getParam(ed, "paste_dialog_width")),
    872                                                 height: parseInt(getParam(ed, "paste_dialog_height")),
    873                                                 inline : 1
    874                                         });
    875                                 });
    876                         }
    877 
    878                         // Register button for backwards compatibility
    879                         ed.addButton("pasteword", {title : "paste.paste_word_desc", cmd : "mcePasteWord"});
    880                 }
     1019                }
     1020
     1021                editor.addCommand('mceInsertClipboardContent', function(ui, value) {
     1022                        if (value.content) {
     1023                                self.clipboard.pasteHtml(value.content);
     1024                        }
     1025
     1026                        if (value.text) {
     1027                                self.clipboard.pasteText(value.text);
     1028                        }
     1029                });
     1030
     1031                // Block all drag/drop events
     1032                if (editor.paste_block_drop) {
     1033                        editor.on('dragend dragover draggesture dragdrop drop drag', function(e) {
     1034                                e.preventDefault();
     1035                                e.stopPropagation();
     1036                        });
     1037                }
     1038
     1039                // Prevent users from dropping data images on Gecko
     1040                if (!editor.settings.paste_data_images) {
     1041                        editor.on('drop', function(e) {
     1042                                var dataTransfer = e.dataTransfer;
     1043
     1044                                if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
     1045                                        e.preventDefault();
     1046                                }
     1047                        });
     1048                }
     1049
     1050                editor.addButton('pastetext', {
     1051                        icon: 'pastetext',
     1052                        tooltip: 'Paste as text',
     1053                        onclick: togglePlainTextPaste,
     1054                        active: self.clipboard.pasteFormat == "text"
     1055                });
     1056
     1057                editor.addMenuItem('pastetext', {
     1058                        text: 'Paste as text',
     1059                        selectable: true,
     1060                        active: clipboard.pasteFormat,
     1061                        onclick: togglePlainTextPaste
     1062                });
    8811063        });
    882 
    883         // Register plugin
    884         tinymce.PluginManager.add("paste", tinymce.plugins.PastePlugin);
    885 })();
     1064});
     1065
     1066expose(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/Clipboard","tinymce/pasteplugin/WordFilter","tinymce/pasteplugin/Quirks","tinymce/pasteplugin/Plugin"]);
     1067})(this);
  • trunk/src/wp-includes/js/tinymce/plugins/paste/plugin.min.js

    r26862 r26876  
    1 (function(){var c=tinymce.each,a={paste_auto_cleanup_on_paste:true,paste_enable_default_filters:true,paste_block_drop:false,paste_retain_style_properties:"none",paste_strip_class_attributes:"mso",paste_remove_spans:false,paste_remove_styles:false,paste_remove_styles_if_webkit:true,paste_convert_middot_lists:true,paste_convert_headers_to_strong:false,paste_dialog_width:"450",paste_dialog_height:"400",paste_max_consecutive_linebreaks:2,paste_text_use_dialog:false,paste_text_sticky:false,paste_text_sticky_default:false,paste_text_notifyalways:false,paste_text_linebreaktype:"combined",paste_text_replacements:[[/\u2026/g,"..."],[/[\x93\x94\u201c\u201d]/g,'"'],[/[\x60\x91\x92\u2018\u2019]/g,"'"]]};function b(d,e){return d.getParam(e,a[e])}tinymce.create("tinymce.plugins.PastePlugin",{init:function(d,e){var f=this;f.editor=d;f.url=e;f.onPreProcess=new tinymce.util.Dispatcher(f);f.onPostProcess=new tinymce.util.Dispatcher(f);f.onPreProcess.add(f._preProcess);f.onPostProcess.add(f._postProcess);f.onPreProcess.add(function(i,j){d.execCallback("paste_preprocess",i,j)});f.onPostProcess.add(function(i,j){d.execCallback("paste_postprocess",i,j)});d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){return false}});d.pasteAsPlainText=b(d,"paste_text_sticky_default");function h(l,j){var k=d.dom,i;f.onPreProcess.dispatch(f,l);l.node=k.create("div",0,l.content);if(tinymce.isGecko){i=d.selection.getRng(true);if(i.startContainer==i.endContainer&&i.startContainer.nodeType==3){if(l.node.childNodes.length===1&&/^(p|h[1-6]|pre)$/i.test(l.node.firstChild.nodeName)&&l.content.indexOf("__MCE_ITEM__")===-1){k.remove(l.node.firstChild,true)}}}f.onPostProcess.dispatch(f,l);l.content=d.serializer.serialize(l.node,{getInner:1,forced_root_block:""});if((!j)&&(d.pasteAsPlainText)){f._insertPlainText(l.content);if(!b(d,"paste_text_sticky")){d.pasteAsPlainText=false;d.controlManager.setActive("pastetext",false)}}else{f._insert(l.content)}}d.addCommand("mceInsertClipboardContent",function(i,j){h(j,true)});if(!b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(j,i){var k=tinymce.util.Cookie;d.pasteAsPlainText=!d.pasteAsPlainText;d.controlManager.setActive("pastetext",d.pasteAsPlainText);if((d.pasteAsPlainText)&&(!k.get("tinymcePasteText"))){if(b(d,"paste_text_sticky")){d.windowManager.alert(d.translate("paste.plaintext_mode_sticky"))}else{d.windowManager.alert(d.translate("paste.plaintext_mode"))}if(!b(d,"paste_text_notifyalways")){k.set("tinymcePasteText","1",new Date(new Date().getFullYear()+1,12,31))}}})}d.addButton("pastetext",{title:"paste.paste_text_desc",cmd:"mcePasteText"});d.addButton("selectall",{title:"paste.selectall_desc",cmd:"selectall"});function g(s){var l,p,j,t,k=d.selection,o=d.dom,q=d.getBody(),i,r;if(s.clipboardData||o.doc.dataTransfer){r=(s.clipboardData||o.doc.dataTransfer).getData("Text");if(d.pasteAsPlainText){s.preventDefault();h({content:o.encode(r).replace(/\r?\n/g,"<br />")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="<p>"+o.encode(r).replace(/\r?\n\r?\n/g,"</p><p>").replace(/\r?\n/g,"<br />")+"</p>"}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9&&/<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(e.content)){d([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g,"$1"]]);d([[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*(&nbsp;)+/gi,/(&nbsp;|<br[^>]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"<p><strong>$1</strong></p>")}if(b(k,"paste_convert_middot_lists")){d([[/<!--\[if !supportLists\]-->/gi,"$&__MCE_ITEM__"],[/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/&quot;/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/<h[1-6][^>]*>/gi,"<p><strong>"],[/<\/h[1-6][^>]*>/gi,"</strong></p>"]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l<d.length;l++){o=d[l];j=h.getStyle(m,o);if(j){n[o]=j;k++}}}h.setAttrib(m,"style","");if(d&&k>0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/&nbsp;/g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f<d){n=tinymce.inArray(m,f);o=i.getParents(h.parentNode,s);h=o[o.length-1-n]||h}}}c(i.select("span",t),function(v){var p=v.innerHTML.replace(/<\/?\w+[^>]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.(&nbsp;|\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*(&nbsp;|\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.(&nbsp;|\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(j){var h=this.editor,f=b(h,"paste_text_linebreaktype"),k=b(h,"paste_text_replacements"),g=tinymce.is;function e(m){c(m,function(n){if(n.constructor==RegExp){j=j.replace(n,"")}else{j=j.replace(n[0],n[1])}})}if((typeof(j)==="string")&&(j.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(j)){e([/[\n\r]+/g])}else{e([/\r+/g])}e([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/<br[^>]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*<t[dh][^>]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/&nbsp;/gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"]]);var d=Number(b(h,"paste_max_consecutive_linebreaks"));if(d>-1){var l=new RegExp("\n{"+(d+1)+",}","g");var i="";while(i.length<d){i+="\n"}e([[l,i]])}j=h.dom.decode(tinymce.html.Entities.encodeRaw(j));if(g(k,"array")){e(k)}else{if(g(k,"string")){e(new RegExp(k,"gi"))}}if(f=="none"){e([[/\n+/g," "]])}else{if(f=="br"){e([[/\n/g,"<br />"]])}else{if(f=="p"){e([[/\n+/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"]])}else{e([[/\n\n/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"],[/\n/g,"<br />"]])}}}h.execCommand("mceInsertContent",false,j)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})();
     1!function(e,t){"use strict";function n(e,t){for(var n,r=[],i=0;i<e.length;++i){if(n=s[e[i]]||o(e[i]),!n)throw"module definition dependecy not found: "+e[i];r.push(n)}t.apply(null,r)}function r(e,r,i){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(r===t)throw"invalid module definition, dependencies must be specified";if(i===t)throw"invalid module definition, definition function must be specified";n(r,function(){s[e]=i.apply(null,arguments)})}function i(e){return!!s[e]}function o(t){for(var n=e,r=t.split(/[.\/]/),i=0;i<r.length;++i){if(!n[r[i]])return;n=n[r[i]]}return n}function a(n){for(var r=0;r<n.length;r++){for(var i=e,o=n[r],a=o.split(/[.\/]/),l=0;l<a.length-1;++l)i[a[l]]===t&&(i[a[l]]={}),i=i[a[l]];i[a[a.length-1]]=s[o]}}var s={},l="tinymce/pasteplugin/Utils",c="tinymce/util/Tools",d="tinymce/html/DomParser",u="tinymce/html/Schema",f="tinymce/pasteplugin/Clipboard",p="tinymce/Env",m="tinymce/util/VK",h="tinymce/pasteplugin/WordFilter",g="tinymce/html/Serializer",v="tinymce/html/Node",y="tinymce/pasteplugin/Quirks",b="tinymce/pasteplugin/Plugin",C="tinymce/PluginManager";r(l,[c,d,u],function(e,t,n){function r(t,n){return e.each(n,function(e){t=e.constructor==RegExp?t.replace(e,""):t.replace(e[0],e[1])}),t}function i(r){function i(e){var t=e.name,n=e;if("br"===t)return s+="\n",void 0;if(l[t]&&(s+=" "),c[t])return s+=" ",void 0;if(3==e.type&&(s+=e.value),!e.shortEnded&&(e=e.firstChild))do i(e);while(e=e.next);d[t]&&n.next&&(s+="\n","p"==t&&(s+="\n"))}var o=new n,a=new t({},o),s="",l=o.getShortEndedElements(),c=e.makeMap("script noscript style textarea video audio iframe object"," "),d=o.getBlockElements();return i(a.parse(r)),s}return{filter:r,innerText:i}}),r(f,[p,m,l],function(e,t,n){return function(r){function i(e){var t,n=r.dom;if(t=r.fire("BeforePastePreProcess",{content:e}),t=r.fire("PastePreProcess",t),e=t.content,!t.isDefaultPrevented()){if(r.hasEventListeners("PastePostProcess")&&!t.isDefaultPrevented()){var i=n.add(r.getBody(),"div",{style:"display:none"},e);t=r.fire("PastePostProcess",{node:i}),n.remove(i),e=t.node.innerHTML}t.isDefaultPrevented()||r.insertContent(e)}}function o(e){e=r.dom.encode(e).replace(/\r\n/g,"\n");var t=r.dom.getParent(r.selection.getStart(),r.dom.isBlock),o=r.settings.forced_root_block,a;o&&(a=r.dom.createHTML(o,r.settings.forced_root_block_attrs),a=a.substr(0,a.length-3)+">"),t&&/^(PRE|DIV)$/.test(t.nodeName)||!o?e=n.filter(e,[[/\n/g,"<br>"]]):(e=n.filter(e,[[/\n\n/g,"</p>"+a],[/^(.*<\/p>)(<p>)$/,a+"$1"],[/\n/g,"<br />"]]),-1!=e.indexOf("<p>")&&(e=a+e)),i(e)}function a(){var e=r.dom,t=r.getBody(),n=r.dom.getViewPort(r.getWin()),i=r.inline?t.clientHeight:n.h;s(),p=e.add(r.getBody(),"div",{id:"mcepastebin",contentEditable:!0,"data-mce-bogus":"1",style:"position: fixed; top: 20px;width: 10px; height: "+(i-40)+"px; overflow: hidden; opacity: 0"},g),e.setStyle(p,"left","rtl"==e.getStyle(t,"direction",!0)?65535:-65535),e.bind(p,"beforedeactivate focusin focusout",function(e){e.stopPropagation()}),m=r.selection.getRng(),p.focus(),r.selection.select(p,!0)}function s(){p&&(r.dom.unbind(p),r.dom.remove(p),m&&r.selection.setRng(m)),v=!1,p=m=null}function l(){return p?p.innerHTML:g}function c(e){var t={};if(e&&e.types){t["text/plain"]=e.getData("Text");for(var n=0;n<e.types.length;n++){var r=e.types[n];t[r]=e.getData(r)}}return t}function d(e){return c(e.clipboardData||r.getDoc().dataTransfer)}function u(e){var t=r.getDoc(),n;if(t.caretPositionFromPoint){var i=t.caretPositionFromPoint(e.pageX,e.pageY);n=t.createRange(),n.setStart(i.offsetNode,i.offset),n.collapse(!0)}else t.caretRangeFromPoint&&(n=t.caretRangeFromPoint(e.pageX,e.pageY));return n}var f=this,p,m,h=0,g="%MCEPASTEBIN%",v;r.on("keydown",function(n){if(!n.isDefaultPrevented()&&(t.metaKeyPressed(n)&&86==n.keyCode||n.shiftKey&&45==n.keyCode)){if(v=n.shiftKey&&86==n.keyCode,n.stopImmediatePropagation(),h=(new Date).getTime(),e.ie&&v)return n.preventDefault(),r.fire("paste",{ieFake:!0}),void 0;a()}}),r.on("paste",function(t){var c=d(t),u=(new Date).getTime()-h<1e3,m="text"==f.pasteFormat||v;u||t.preventDefault(),!e.ie||u&&!t.ieFake||(a(),r.dom.bind(p,"paste",function(e){e.stopPropagation()}),r.getDoc().execCommand("Paste",!1,null),c["text/html"]=l()),setTimeout(function(){var e=l();return p&&p.firstChild&&"mcepastebin"===p.firstChild.id&&(m=!0),s(),e!=g&&u||(e=c["text/html"]||c["text/plain"]||g,e!=g)?(m?o(c["text/plain"]||n.innerText(e)):i(e),void 0):(u||r.windowManager.alert("Please use Ctrl+V/Cmd+V keyboard shortcuts to paste contents."),void 0)},0)}),r.on("dragstart",function(e){e.dataTransfer.types&&e.dataTransfer.setData("mce-internal",r.selection.getContent())}),r.on("drop",function(e){var t=u(e);if(t){var n=c(e.dataTransfer),a=n["mce-internal"]||n["text/html"]||n["text/plain"];a&&(e.preventDefault(),r.undoManager.transact(function(){n["mce-internal"]&&r.execCommand("Delete"),r.selection.setRng(t),n["text/html"]?i(a):o(a)}))}}),f.pasteHtml=i,f.pasteText=o,r.on("preInit",function(){r.parser.addNodeFilter("img",function(t){if(!r.settings.paste_data_images)for(var n=t.length;n--;){var i=t[n].attributes.map.src;i&&0===i.indexOf("data:image")&&(t[n].attr("data-mce-object")||i===e.transparentSrc||t[n].remove())}})}),r.on("PreProcess",function(){r.dom.remove(r.dom.get("mcepastebin"))})}}),r(h,[c,d,u,g,v,l],function(e,t,n,r,i,o){function a(e){return/<font face="Times New Roman"|class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test(e)}function s(s){var l=s.settings;s.on("BeforePastePreProcess",function(c){function d(e){function t(e,t,a,s){var l=e._listLevel||o;l!=o&&(o>l?n&&(n=n.parent.parent):(r=n,n=null)),n&&n.name==a?n.append(e):(r=r||n,n=new i(a,1),s>1&&n.attr("start",""+s),e.wrap(n)),e.name="li",t.value="";var c=t.next;c&&3==c.type&&(c.value=c.value.replace(/^\u00a0+/,"")),l>o&&r&&r.lastChild.append(n),o=l}for(var n,r,o=1,a=e.getAll("p"),s=0;s<a.length;s++)if(e=a[s],"p"==e.name&&e.firstChild){for(var l="",c=e.firstChild;c&&!(l=c.value);)c=c.firstChild;if(/^\s*[\u2022\u00b7\u00a7\u00d8\u25CF]\s*$/.test(l)){t(e,c,"ul");continue}if(/^\s*\w+\.$/.test(l)){var d=/([0-9])\./.exec(l),u=1;d&&(u=parseInt(d[1],10)),t(e,c,"ol",u);continue}n=null}}function u(t,n){if("p"===t.name){var r=/mso-list:\w+ \w+([0-9]+)/.exec(n);r&&(t._listLevel=parseInt(r[1],10))}if(s.getParam("paste_retain_style_properties","none")){var i="";if(e.each(s.dom.parseStyle(n),function(e,t){switch(t){case"horiz-align":return t="text-align",void 0;case"vert-align":return t="vertical-align",void 0;case"font-color":case"mso-foreground":return t="color",void 0;case"mso-background":case"mso-highlight":t="background"}("all"==p||m&&m[t])&&(i+=t+":"+e+";")}),i)return i}return null}var f=c.content,p,m;if(p=l.paste_retain_style_properties,p&&(m=e.makeMap(p)),l.paste_enable_default_filters!==!1&&a(c.content)){c.wordContent=!0,f=o.filter(f,[/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\xa0"],[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(e,t){return t.length>0?t.replace(/./," ").slice(Math.floor(t.length/2)).split("").join("\xa0"):""}]]);var h=l.paste_word_valid_elements;h||(h="@[style],-strong/b,-em/i,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-table,-tr,-td[colspan|rowspan],-th,-thead,-tfoot,-tbody,-a[href|name],sub,sup,strike,br");var g=new n({valid_elements:h}),v=new t({},g);v.addAttributeFilter("style",function(e){for(var t=e.length,n;t--;)n=e[t],n.attr("style",u(n,n.attr("style"))),"span"!=n.name||n.attributes.length||n.unwrap()}),v.addNodeFilter("a",function(e){for(var t=e.length,n,r,i;t--;)n=e[t],r=n.attr("href"),i=n.attr("name"),r&&0===r.indexOf("file://")&&(r=r.split("#")[1],r&&(r="#"+r)),r||i?n.attr({href:r,name:i}):n.unwrap()});var y=v.parse(f);d(y),c.content=new r({},g).serialize(y)}})}return s.isWordContent=a,s}),r(y,[p,c,h,l],function(e,t,n,r){return function(i){function o(e){i.on("BeforePastePreProcess",function(t){t.content=e(t.content)})}function a(e){return e=r.filter(e,[/^[\s\S]*<!--StartFragment-->|<!--EndFragment-->[\s\S]*$/g,[/<span class="Apple-converted-space">\u00a0<\/span>/g,"\xa0"],/<br>$/])}function s(e){if(!n.isWordContent(e))return e;var o=[];t.each(i.schema.getBlockElements(),function(e,t){o.push(t)});var a=new RegExp("(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*(<\\/?("+o.join("|")+")[^>]*>)(?:<br>&nbsp;[\\s\\r\\n]+|<br>)*","g");return e=r.filter(e,[[a,"$1"]]),e=r.filter(e,[[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}function l(e){return(i.settings.paste_remove_styles||i.settings.paste_remove_styles_if_webkit!==!1)&&(e=e.replace(/ style=\"[^\"]+\"/g,"")),e}e.webkit&&(o(l),o(a)),e.ie&&o(s)}}),r(b,[C,f,h,y],function(e,t,n,r){var i;e.add("paste",function(e){function o(){"text"==s.pasteFormat?(this.active(!1),s.pasteFormat="html"):(s.pasteFormat="text",this.active(!0),i||(e.windowManager.alert("Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off."),i=!0))}var a=this,s,l=e.settings;a.clipboard=s=new t(e),a.quirks=new r(e),a.wordFilter=new n(e),e.settings.paste_as_text&&(a.clipboard.pasteFormat="text"),l.paste_preprocess&&e.on("PastePreProcess",function(e){l.paste_preprocess.call(a,a,e)}),l.paste_postprocess&&e.on("PastePostProcess",function(e){l.paste_postprocess.call(a,a,e)}),e.addCommand("mceInsertClipboardContent",function(e,t){t.content&&a.clipboard.pasteHtml(t.content),t.text&&a.clipboard.pasteText(t.text)}),e.paste_block_drop&&e.on("dragend dragover draggesture dragdrop drop drag",function(e){e.preventDefault(),e.stopPropagation()}),e.settings.paste_data_images||e.on("drop",function(e){var t=e.dataTransfer;t&&t.files&&t.files.length>0&&e.preventDefault()}),e.addButton("pastetext",{icon:"pastetext",tooltip:"Paste as text",onclick:o,active:"text"==a.clipboard.pasteFormat}),e.addMenuItem("pastetext",{text:"Paste as text",selectable:!0,active:s.pasteFormat,onclick:o})})}),a([l,f,h,y,b])}(this);
  • trunk/src/wp-includes/js/tinymce/plugins/spellchecker/config.php

    r16405 r26876  
    66 */
    77        // General settings
    8         $config['general.engine'] = 'GoogleSpell';
     8//      $config['general.engine'] = 'GoogleSpell';
    99        //$config['general.engine'] = 'PSpell';
    1010        //$config['general.engine'] = 'PSpellShell';
  • trunk/src/wp-includes/js/tinymce/plugins/spellchecker/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
     2 * Compiled inline version. (Library mode)
     3 */
     4
     5/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
     6/*globals $code */
     7
     8(function(exports, undefined) {
     9        "use strict";
     10
     11        var modules = {};
     12
     13        function require(ids, callback) {
     14                var module, defs = [];
     15
     16                for (var i = 0; i < ids.length; ++i) {
     17                        module = modules[ids[i]] || resolve(ids[i]);
     18                        if (!module) {
     19                                throw 'module definition dependecy not found: ' + ids[i];
     20                        }
     21
     22                        defs.push(module);
     23                }
     24
     25                callback.apply(null, defs);
     26        }
     27
     28        function define(id, dependencies, definition) {
     29                if (typeof id !== 'string') {
     30                        throw 'invalid module definition, module id must be defined and be a string';
     31                }
     32
     33                if (dependencies === undefined) {
     34                        throw 'invalid module definition, dependencies must be specified';
     35                }
     36
     37                if (definition === undefined) {
     38                        throw 'invalid module definition, definition function must be specified';
     39                }
     40
     41                require(dependencies, function() {
     42                        modules[id] = definition.apply(null, arguments);
     43                });
     44        }
     45
     46        function defined(id) {
     47                return !!modules[id];
     48        }
     49
     50        function resolve(id) {
     51                var target = exports;
     52                var fragments = id.split(/[.\/]/);
     53
     54                for (var fi = 0; fi < fragments.length; ++fi) {
     55                        if (!target[fragments[fi]]) {
     56                                return;
     57                        }
     58
     59                        target = target[fragments[fi]];
     60                }
     61
     62                return target;
     63        }
     64
     65        function expose(ids) {
     66                for (var i = 0; i < ids.length; i++) {
     67                        var target = exports;
     68                        var id = ids[i];
     69                        var fragments = id.split(/[.\/]/);
     70
     71                        for (var fi = 0; fi < fragments.length - 1; ++fi) {
     72                                if (target[fragments[fi]] === undefined) {
     73                                        target[fragments[fi]] = {};
     74                                }
     75
     76                                target = target[fragments[fi]];
     77                        }
     78
     79                        target[fragments[fragments.length - 1]] = modules[id];
     80                }
     81        }
     82
     83// Included from: js/tinymce/plugins/spellchecker/classes/DomTextMatcher.js
     84
     85/**
     86 * DomTextMatcher.js
    387 *
    4  * Copyright 2009, Moxiecode Systems AB
     88 * Copyright, Moxiecode Systems AB
    589 * Released under LGPL License.
    690 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     91 * License: http://www.tinymce.com/license
     92 * Contributing: http://www.tinymce.com/contributing
    993 */
    1094
    11 (function() {
    12         var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;
    13 
    14         tinymce.create('tinymce.plugins.SpellcheckerPlugin', {
    15                 getInfo : function() {
    16                         return {
    17                                 longname : 'Spellchecker',
    18                                 author : 'Moxiecode Systems AB',
    19                                 authorurl : 'http://tinymce.moxiecode.com',
    20                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',
    21                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
     95/**
     96 * This class logic for filtering text and matching words.
     97 *
     98 * @class tinymce.spellcheckerplugin.TextFilter
     99 * @private
     100 */
     101define("tinymce/spellcheckerplugin/DomTextMatcher", [], function() {
     102        // Based on work developed by: James Padolsey http://james.padolsey.com
     103        // released under UNLICENSE that is compatible with LGPL
     104        // TODO: Handle contentEditable edgecase:
     105        // <p>text<span contentEditable="false">text<span contentEditable="true">text</span>text</span>text</p>
     106        return function(regex, node, schema) {
     107                var m, matches = [], text, count = 0, doc;
     108                var blockElementsMap, hiddenTextElementsMap, shortEndedElementsMap;
     109
     110                doc = node.ownerDocument;
     111                blockElementsMap = schema.getBlockElements(); // H1-H6, P, TD etc
     112                hiddenTextElementsMap = schema.getWhiteSpaceElements(); // TEXTAREA, PRE, STYLE, SCRIPT
     113                shortEndedElementsMap = schema.getShortEndedElements(); // BR, IMG, INPUT
     114
     115                function getMatchIndexes(m) {
     116                        if (!m[0]) {
     117                                throw 'findAndReplaceDOMText cannot handle zero-length matches';
     118                        }
     119
     120                        var index = m.index;
     121
     122                        return [index, index + m[0].length, [m[0]]];
     123                }
     124
     125                function getText(node) {
     126                        var txt;
     127
     128                        if (node.nodeType === 3) {
     129                                return node.data;
     130                        }
     131
     132                        if (hiddenTextElementsMap[node.nodeName] && !blockElementsMap[node.nodeName]) {
     133                                return '';
     134                        }
     135
     136                        txt = '';
     137
     138                        if (blockElementsMap[node.nodeName] || shortEndedElementsMap[node.nodeName]) {
     139                                txt += '\n';
     140                        }
     141
     142                        if ((node = node.firstChild)) {
     143                                do {
     144                                        txt += getText(node);
     145                                } while ((node = node.nextSibling));
     146                        }
     147
     148                        return txt;
     149                }
     150
     151                function stepThroughMatches(node, matches, replaceFn) {
     152                        var startNode, endNode, startNodeIndex,
     153                                endNodeIndex, innerNodes = [], atIndex = 0, curNode = node,
     154                                matchLocation = matches.shift(), matchIndex = 0;
     155
     156                        out: while (true) {
     157                                if (blockElementsMap[curNode.nodeName] || shortEndedElementsMap[curNode.nodeName]) {
     158                                        atIndex++;
     159                                }
     160
     161                                if (curNode.nodeType === 3) {
     162                                        if (!endNode && curNode.length + atIndex >= matchLocation[1]) {
     163                                                // We've found the ending
     164                                                endNode = curNode;
     165                                                endNodeIndex = matchLocation[1] - atIndex;
     166                                        } else if (startNode) {
     167                                                // Intersecting node
     168                                                innerNodes.push(curNode);
     169                                        }
     170
     171                                        if (!startNode && curNode.length + atIndex > matchLocation[0]) {
     172                                                // We've found the match start
     173                                                startNode = curNode;
     174                                                startNodeIndex = matchLocation[0] - atIndex;
     175                                        }
     176
     177                                        atIndex += curNode.length;
     178                                }
     179
     180                                if (startNode && endNode) {
     181                                        curNode = replaceFn({
     182                                                startNode: startNode,
     183                                                startNodeIndex: startNodeIndex,
     184                                                endNode: endNode,
     185                                                endNodeIndex: endNodeIndex,
     186                                                innerNodes: innerNodes,
     187                                                match: matchLocation[2],
     188                                                matchIndex: matchIndex
     189                                        });
     190
     191                                        // replaceFn has to return the node that replaced the endNode
     192                                        // and then we step back so we can continue from the end of the
     193                                        // match:
     194                                        atIndex -= (endNode.length - endNodeIndex);
     195                                        startNode = null;
     196                                        endNode = null;
     197                                        innerNodes = [];
     198                                        matchLocation = matches.shift();
     199                                        matchIndex++;
     200
     201                                        if (!matchLocation) {
     202                                                break; // no more matches
     203                                        }
     204                                } else if ((!hiddenTextElementsMap[curNode.nodeName] || blockElementsMap[curNode.nodeName]) && curNode.firstChild) {
     205                                        // Move down
     206                                        curNode = curNode.firstChild;
     207                                        continue;
     208                                } else if (curNode.nextSibling) {
     209                                        // Move forward:
     210                                        curNode = curNode.nextSibling;
     211                                        continue;
     212                                }
     213
     214                                // Move forward or up:
     215                                while (true) {
     216                                        if (curNode.nextSibling) {
     217                                                curNode = curNode.nextSibling;
     218                                                break;
     219                                        } else if (curNode.parentNode !== node) {
     220                                                curNode = curNode.parentNode;
     221                                        } else {
     222                                                break out;
     223                                        }
     224                                }
     225                        }
     226                }
     227
     228                /**
     229                * Generates the actual replaceFn which splits up text nodes
     230                * and inserts the replacement element.
     231                */
     232                function genReplacer(nodeName) {
     233                        var makeReplacementNode;
     234
     235                        if (typeof nodeName != 'function') {
     236                                var stencilNode = nodeName.nodeType ? nodeName : doc.createElement(nodeName);
     237
     238                                makeReplacementNode = function(fill, matchIndex) {
     239                                        var clone = stencilNode.cloneNode(false);
     240
     241                                        clone.setAttribute('data-mce-index', matchIndex);
     242
     243                                        if (fill) {
     244                                                clone.appendChild(doc.createTextNode(fill));
     245                                        }
     246
     247                                        return clone;
     248                                };
     249                        } else {
     250                                makeReplacementNode = nodeName;
     251                        }
     252
     253                        return function replace(range) {
     254                                var before, after, parentNode, startNode = range.startNode,
     255                                        endNode = range.endNode, matchIndex = range.matchIndex;
     256
     257                                if (startNode === endNode) {
     258                                        var node = startNode;
     259
     260                                        parentNode = node.parentNode;
     261                                        if (range.startNodeIndex > 0) {
     262                                                // Add `before` text node (before the match)
     263                                                before = doc.createTextNode(node.data.substring(0, range.startNodeIndex));
     264                                                parentNode.insertBefore(before, node);
     265                                        }
     266
     267                                        // Create the replacement node:
     268                                        var el = makeReplacementNode(range.match[0], matchIndex);
     269                                        parentNode.insertBefore(el, node);
     270                                        if (range.endNodeIndex < node.length) {
     271                                                // Add `after` text node (after the match)
     272                                                after = doc.createTextNode(node.data.substring(range.endNodeIndex));
     273                                                parentNode.insertBefore(after, node);
     274                                        }
     275
     276                                        node.parentNode.removeChild(node);
     277
     278                                        return el;
     279                                } else {
     280                                        // Replace startNode -> [innerNodes...] -> endNode (in that order)
     281                                        before = doc.createTextNode(startNode.data.substring(0, range.startNodeIndex));
     282                                        after = doc.createTextNode(endNode.data.substring(range.endNodeIndex));
     283                                        var elA = makeReplacementNode(startNode.data.substring(range.startNodeIndex), matchIndex);
     284                                        var innerEls = [];
     285
     286                                        for (var i = 0, l = range.innerNodes.length; i < l; ++i) {
     287                                                var innerNode = range.innerNodes[i];
     288                                                var innerEl = makeReplacementNode(innerNode.data, matchIndex);
     289                                                innerNode.parentNode.replaceChild(innerEl, innerNode);
     290                                                innerEls.push(innerEl);
     291                                        }
     292
     293                                        var elB = makeReplacementNode(endNode.data.substring(0, range.endNodeIndex), matchIndex);
     294
     295                                        parentNode = startNode.parentNode;
     296                                        parentNode.insertBefore(before, startNode);
     297                                        parentNode.insertBefore(elA, startNode);
     298                                        parentNode.removeChild(startNode);
     299
     300                                        parentNode = endNode.parentNode;
     301                                        parentNode.insertBefore(elB, endNode);
     302                                        parentNode.insertBefore(after, endNode);
     303                                        parentNode.removeChild(endNode);
     304
     305                                        return elB;
     306                                }
    22307                        };
    23                 },
    24 
    25                 init : function(ed, url) {
    26                         var t = this, cm;
    27 
    28                         t.url = url;
    29                         t.editor = ed;
    30                         t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}");
    31 
    32                         if (t.rpcUrl == '{backend}') {
    33                                 // Sniff if the browser supports native spellchecking (Don't know of a better way)
    34                                 if (tinymce.isIE)
     308                }
     309
     310                text = getText(node);
     311                if (text && regex.global) {
     312                        while ((m = regex.exec(text))) {
     313                                matches.push(getMatchIndexes(m));
     314                        }
     315                }
     316
     317                function filter(callback) {
     318                        var filteredMatches = [];
     319
     320                        each(function(match, i) {
     321                                if (callback(match, i)) {
     322                                        filteredMatches.push(match);
     323                                }
     324                        });
     325
     326                        matches = filteredMatches;
     327
     328                        /*jshint validthis:true*/
     329                        return this;
     330                }
     331
     332                function each(callback) {
     333                        for (var i = 0, l = matches.length; i < l; i++) {
     334                                if (callback(matches[i], i) === false) {
     335                                        break;
     336                                }
     337                        }
     338
     339                        /*jshint validthis:true*/
     340                        return this;
     341                }
     342
     343                function mark(replacementNode) {
     344                        if (matches.length) {
     345                                count = matches.length;
     346                                stepThroughMatches(node, matches, genReplacer(replacementNode));
     347                        }
     348
     349                        /*jshint validthis:true*/
     350                        return this;
     351                }
     352
     353                return {
     354                        text: text,
     355                        count: count,
     356                        matches: matches,
     357                        each: each,
     358                        filter: filter,
     359                        mark: mark
     360                };
     361        };
     362});
     363
     364// Included from: js/tinymce/plugins/spellchecker/classes/Plugin.js
     365
     366/**
     367 * Plugin.js
     368 *
     369 * Copyright, Moxiecode Systems AB
     370 * Released under LGPL License.
     371 *
     372 * License: http://www.tinymce.com/license
     373 * Contributing: http://www.tinymce.com/contributing
     374 */
     375
     376/*jshint camelcase:false */
     377
     378/**
     379 * This class contains all core logic for the spellchecker plugin.
     380 *
     381 * @class tinymce.spellcheckerplugin.Plugin
     382 * @private
     383 */
     384define("tinymce/spellcheckerplugin/Plugin", [
     385        "tinymce/spellcheckerplugin/DomTextMatcher",
     386        "tinymce/PluginManager",
     387        "tinymce/util/Tools",
     388        "tinymce/ui/Menu",
     389        "tinymce/dom/DOMUtils",
     390        "tinymce/util/JSONRequest",
     391        "tinymce/util/URI"
     392], function(DomTextMatcher, PluginManager, Tools, Menu, DOMUtils, JSONRequest, URI) {
     393        PluginManager.add('spellchecker', function(editor, url) {
     394                var lastSuggestions, started, suggestionsMenu, settings = editor.settings;
     395
     396                function isEmpty(obj) {
     397                        /*jshint unused:false*/
     398                        for (var name in obj) {
     399                                return false;
     400                        }
     401
     402                        return true;
     403                }
     404
     405                function showSuggestions(target, word) {
     406                        var items = [], suggestions = lastSuggestions[word];
     407
     408                        Tools.each(suggestions, function(suggestion) {
     409                                items.push({
     410                                        text: suggestion,
     411                                        onclick: function() {
     412                                                editor.insertContent(suggestion);
     413                                                checkIfFinished();
     414                                        }
     415                                });
     416                        });
     417
     418                        items.push.apply(items, [
     419                                {text: '-'},
     420
     421                                {text: 'Ignore', onclick: function() {
     422                                        ignoreWord(target, word);
     423                                }},
     424
     425                                {text: 'Ignore all', onclick: function() {
     426                                        ignoreWord(target, word, true);
     427                                }},
     428
     429                                {text: 'Finish', onclick: finish}
     430                        ]);
     431
     432                        // Render menu
     433                        suggestionsMenu = new Menu({
     434                                items: items,
     435                                context: 'contextmenu',
     436                                onautohide: function(e) {
     437                                        if (e.target.className.indexOf('spellchecker') != -1) {
     438                                                e.preventDefault();
     439                                        }
     440                                },
     441                                onhide: function() {
     442                                        suggestionsMenu.remove();
     443                                        suggestionsMenu = null;
     444                                }
     445                        });
     446
     447                        suggestionsMenu.renderTo(document.body);
     448
     449                        // Position menu
     450                        var pos = DOMUtils.DOM.getPos(editor.getContentAreaContainer());
     451                        var targetPos = editor.dom.getPos(target);
     452
     453                        pos.x += targetPos.x;
     454                        pos.y += targetPos.y;
     455
     456                        suggestionsMenu.moveTo(pos.x, pos.y + target.offsetHeight);
     457                }
     458
     459                function spellcheck() {
     460                        var textFilter, words = [], uniqueWords = {};
     461
     462                        if (started) {
     463                                finish();
     464                                return;
     465                        }
     466
     467                        started = true;
     468
     469                        function doneCallback(suggestions) {
     470                                editor.setProgressState(false);
     471
     472                                if (isEmpty(suggestions)) {
     473                                        editor.windowManager.alert('No misspellings found');
     474                                        started = false;
    35475                                        return;
    36 
    37                                 t.hasSupport = true;
    38 
    39                                 // Disable the context menu when spellchecking is active
    40                                 ed.onContextMenu.addToTop(function(ed, e) {
    41                                         if (t.active)
    42                                                 return false;
     476                                }
     477
     478                                lastSuggestions = suggestions;
     479
     480                                textFilter.filter(function(match) {
     481                                        return !!suggestions[match[2][0]];
     482                                }).mark(editor.dom.create('span', {
     483                                        "class": 'mce-spellchecker-word',
     484                                        "data-mce-bogus": 1
     485                                }));
     486
     487                                textFilter = null;
     488                                editor.fire('SpellcheckStart');
     489                        }
     490
     491                        // Regexp for finding word specific characters this will split words by
     492                        // spaces, quotes, copy right characters etc. It's escaped with unicode characters
     493                        // to make it easier to output scripts on servers using different encodings
     494                        // so if you add any characters outside the 128 byte range make sure to escape it
     495                        var nonWordSeparatorCharacters = editor.getParam('spellchecker_wordchar_pattern') || new RegExp("[^" +
     496                                "\\s!\"#$%&()*+,-./:;<=>?@[\\]^_{|}`" +
     497                                "\u00a7\u00a9\u00ab\u00ae\u00b1\u00b6\u00b7\u00b8\u00bb" +
     498                                "\u00bc\u00bd\u00be\u00bf\u00d7\u00f7\u00a4\u201d\u201c\u201e" +
     499                        "]+", "g");
     500
     501                        // Find all words and make an unique words array
     502                        textFilter = new DomTextMatcher(nonWordSeparatorCharacters, editor.getBody(), editor.schema).each(function(match) {
     503                                var word = match[2][0];
     504
     505                                // TODO: Fix so it remembers correctly spelled words
     506                                if (!uniqueWords[word]) {
     507                                        // Ignore numbers and single character words
     508                                        if (/^\d+$/.test(word) || word.length == 1) {
     509                                                return;
     510                                        }
     511
     512                                        words.push(word);
     513                                        uniqueWords[word] = true;
     514                                }
     515                        });
     516
     517                        function defaultSpellcheckCallback(method, words, doneCallback) {
     518                                JSONRequest.sendRPC({
     519                                        url: new URI(url).toAbsolute(settings.spellchecker_rpc_url),
     520                                        method: method,
     521                                        params: {
     522                                                lang: settings.spellchecker_language || "en",
     523                                                words: words
     524                                        },
     525                                        success: function(result) {
     526                                                doneCallback(result);
     527                                        },
     528                                        error: function(error, xhr) {
     529                                                if (error == "JSON Parse error.") {
     530                                                        error = "Non JSON response:" + xhr.responseText;
     531                                                } else {
     532                                                        error = "Error: " + error;
     533                                                }
     534
     535                                                editor.windowManager.alert(error);
     536                                                editor.setProgressState(false);
     537                                                textFilter = null;
     538                                                started = false;
     539                                        }
    43540                                });
    44541                        }
    45542
    46                         // Register commands
    47                         ed.addCommand('mceSpellCheck', function() {
    48                                 if (t.rpcUrl == '{backend}') {
    49                                         // Enable/disable native spellchecker
    50                                         t.editor.getBody().spellcheck = t.active = !t.active;
    51                                         return;
    52                                 }
    53 
    54                                 if (!t.active) {
    55                                         ed.setProgressState(1);
    56                                         t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {
    57                                                 if (r.length > 0) {
    58                                                         t.active = 1;
    59                                                         t._markWords(r);
    60                                                         ed.setProgressState(0);
    61                                                         ed.nodeChanged();
    62                                                 } else {
    63                                                         ed.setProgressState(0);
    64 
    65                                                         if (ed.getParam('spellchecker_report_no_misspellings', true))
    66                                                                 ed.windowManager.alert('spellchecker.no_mpell');
     543                        editor.setProgressState(true);
     544
     545                        var spellCheckCallback = settings.spellchecker_callback || defaultSpellcheckCallback;
     546                        spellCheckCallback("spellcheck", words, doneCallback);
     547                }
     548
     549                function checkIfFinished() {
     550                        if (!editor.dom.select('span.mce-spellchecker-word').length) {
     551                                finish();
     552                        }
     553                }
     554
     555                function unwrap(node) {
     556                        var parentNode = node.parentNode;
     557                        parentNode.insertBefore(node.firstChild, node);
     558                        node.parentNode.removeChild(node);
     559                }
     560
     561                function ignoreWord(target, word, all) {
     562                        if (all) {
     563                                Tools.each(editor.dom.select('span.mce-spellchecker-word'), function(item) {
     564                                        var text = item.innerText || item.textContent;
     565
     566                                        if (text == word) {
     567                                                unwrap(item);
     568                                        }
     569                                });
     570                        } else {
     571                                unwrap(target);
     572                        }
     573
     574                        checkIfFinished();
     575                }
     576
     577                function finish() {
     578                        var i, nodes, node;
     579
     580                        started = false;
     581                        node = editor.getBody();
     582                        nodes = node.getElementsByTagName('span');
     583                        i = nodes.length;
     584                        while (i--) {
     585                                node = nodes[i];
     586                                if (node.getAttribute('data-mce-index')) {
     587                                        unwrap(node);
     588                                }
     589                        }
     590
     591                        editor.fire('SpellcheckEnd');
     592                }
     593
     594                function selectMatch(index) {
     595                        var nodes, i, spanElm, spanIndex = -1, startContainer, endContainer;
     596
     597                        index = "" + index;
     598                        nodes = editor.getBody().getElementsByTagName("span");
     599                        for (i = 0; i < nodes.length; i++) {
     600                                spanElm = nodes[i];
     601                                if (spanElm.className == "mce-spellchecker-word") {
     602                                        spanIndex = spanElm.getAttribute('data-mce-index');
     603                                        if (spanIndex === index) {
     604                                                spanIndex = index;
     605
     606                                                if (!startContainer) {
     607                                                        startContainer = spanElm.firstChild;
    67608                                                }
    68                                         });
    69                                 } else
    70                                         t._done();
    71                         });
    72 
    73                         if (ed.settings.content_css !== false)
    74                                 ed.contentCSS.push(url + '/css/content.css');
    75 
    76                         ed.onClick.add(t._showMenu, t);
    77                         ed.onContextMenu.add(t._showMenu, t);
    78                         ed.onBeforeGetContent.add(function() {
    79                                 if (t.active)
    80                                         t._removeWords();
    81                         });
    82 
    83                         ed.onNodeChange.add(function(ed, cm) {
    84                                 cm.setActive('spellchecker', t.active);
    85                         });
    86 
    87                         ed.onSetContent.add(function() {
    88                                 t._done();
    89                         });
    90 
    91                         ed.onBeforeGetContent.add(function() {
    92                                 t._done();
    93                         });
    94 
    95                         ed.onBeforeExecCommand.add(function(ed, cmd) {
    96                                 if (cmd == 'mceFullScreen')
    97                                         t._done();
    98                         });
    99 
    100                         // Find selected language
    101                         t.languages = {};
    102                         each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) {
    103                                 if (k.indexOf('+') === 0) {
    104                                         k = k.substring(1);
    105                                         t.selectedLang = v;
    106                                 }
    107 
    108                                 t.languages[k] = v;
    109                         });
    110                 },
    111 
    112                 createControl : function(n, cm) {
    113                         var t = this, c, ed = t.editor;
    114 
    115                         if (n == 'spellchecker') {
    116                                 // Use basic button if we use the native spellchecker
    117                                 if (t.rpcUrl == '{backend}') {
    118                                         // Create simple toggle button if we have native support
    119                                         if (t.hasSupport)
    120                                                 c = cm.createButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
    121 
    122                                         return c;
    123                                 }
    124 
    125                                 c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
    126 
    127                                 c.onRenderMenu.add(function(c, m) {
    128                                         m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
    129                                         t.menuItems = {};
    130                                         each(t.languages, function(v, k) {
    131                                                 var o = {icon : 1}, mi;
    132 
    133                                                 o.onclick = function() {
    134                                                         if (v == t.selectedLang) {
    135                                                                 return;
    136                                                         }
    137                                                         t._updateMenu(mi);
    138                                                         t.selectedLang = v;
    139                                                 };
    140 
    141                                                 o.title = k;
    142                                                 mi = m.add(o);
    143                                                 mi.setSelected(v == t.selectedLang);
    144                                                 t.menuItems[v] = mi;
    145                                                 if (v == t.selectedLang)
    146                                                         t.selectedItem = mi;
    147                                         });
     609
     610                                                endContainer = spanElm.firstChild;
     611                                        }
     612
     613                                        if (spanIndex !== index && endContainer) {
     614                                                break;
     615                                        }
     616                                }
     617                        }
     618
     619                        var rng = editor.dom.createRng();
     620                        rng.setStart(startContainer, 0);
     621                        rng.setEnd(endContainer, endContainer.length);
     622                        editor.selection.setRng(rng);
     623
     624                        return rng;
     625                }
     626
     627                editor.on('click', function(e) {
     628                        if (e.target.className == "mce-spellchecker-word") {
     629                                e.preventDefault();
     630
     631                                var rng = selectMatch(e.target.getAttribute('data-mce-index'));
     632                                showSuggestions(e.target, rng.toString());
     633                        }
     634                });
     635
     636                editor.addMenuItem('spellchecker', {
     637                        text: 'Spellcheck',
     638                        context: 'tools',
     639                        onclick: spellcheck,
     640                        selectable: true,
     641                        onPostRender: function() {
     642                                var self = this;
     643
     644                                editor.on('SpellcheckStart SpellcheckEnd', function() {
     645                                        self.active(started);
    148646                                });
    149 
    150 
    151 
    152                                 return c;
    153                         }
    154                 },
    155 
    156                 setLanguage: function(lang) {
    157                         var t = this;
    158 
    159                         if (lang == t.selectedLang) {
    160                                 // allowed
    161                                 return;
    162                         }
    163 
    164                         if (tinymce.grep(t.languages, function(v) { return v === lang; }).length === 0) {
    165                                 throw "Unknown language: " + lang;
    166                         }
    167 
    168                         t.selectedLang = lang;
    169 
    170                         // if the menu has been shown, update it as well
    171                         if (t.menuItems) {
    172                                 t._updateMenu(t.menuItems[lang]);
    173                         }
    174 
    175                         if (t.active) {
    176                                 // clear error in the old language.
    177                                 t._done();
    178 
    179                                 // Don't immediately block the UI to check spelling in the new language, this is an API not a user action.
    180                         }
    181                 },
    182 
    183                 // Internal functions
    184 
    185                 _updateMenu: function(mi) {
    186                         mi.setSelected(1);
    187                         this.selectedItem.setSelected(0);
    188                         this.selectedItem = mi;
    189                 },
    190 
    191                 _walk : function(n, f) {
    192                         var d = this.editor.getDoc(), w;
    193 
    194                         if (d.createTreeWalker) {
    195                                 w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
    196 
    197                                 while ((n = w.nextNode()) != null)
    198                                         f.call(this, n);
    199                         } else
    200                                 tinymce.walk(n, f, 'childNodes');
    201                 },
    202 
    203                 _getSeparators : function() {
    204                         var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
    205 
    206                         // Build word separator regexp
    207                         for (i=0; i<str.length; i++)
    208                                 re += '\\' + str.charAt(i);
    209 
    210                         return re;
    211                 },
    212 
    213                 _getWords : function() {
    214                         var ed = this.editor, wl = [], tx = '', lo = {}, rawWords = [];
    215 
    216                         // Get area text
    217                         this._walk(ed.getBody(), function(n) {
    218                                 if (n.nodeType == 3)
    219                                         tx += n.nodeValue + ' ';
    220                         });
    221 
    222                         // split the text up into individual words
    223                         if (ed.getParam('spellchecker_word_pattern')) {
    224                                 // look for words that match the pattern
    225                                 rawWords = tx.match('(' + ed.getParam('spellchecker_word_pattern') + ')', 'gi');
    226                         } else {
    227                                 // Split words by separator
    228                                 tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
    229                                 tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));
    230                                 rawWords = tx.split(' ');
    231                         }
    232 
    233                         // Build word array and remove duplicates
    234                         each(rawWords, function(v) {
    235                                 if (!lo[v]) {
    236                                         wl.push(v);
    237                                         lo[v] = 1;
    238                                 }
    239                         });
    240 
    241                         return wl;
    242                 },
    243 
    244                 _removeWords : function(w) {
    245                         var ed = this.editor, dom = ed.dom, se = ed.selection, r = se.getRng(true);
    246 
    247                         each(dom.select('span').reverse(), function(n) {
    248                                 if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
    249                                         if (!w || dom.decode(n.innerHTML) == w)
    250                                                 dom.remove(n, 1);
    251                                 }
    252                         });
    253 
    254                         se.setRng(r);
    255                 },
    256 
    257                 _markWords : function(wl) {
    258                         var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, r = se.getRng(true), nl = [],
    259                                 w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g');
    260 
    261                         // Collect all text nodes
    262                         this._walk(ed.getBody(), function(n) {
    263                                 if (n.nodeType == 3) {
    264                                         nl.push(n);
    265                                 }
    266                         });
    267 
    268                         // Wrap incorrect words in spans
    269                         each(nl, function(n) {
    270                                 var node, elem, txt, pos, v = n.nodeValue;
    271 
    272                                 rx.lastIndex = 0;
    273                                 if (rx.test(v)) {
    274                                         // Encode the content
    275                                         v = dom.encode(v);
    276                                         // Create container element
    277                                         elem = dom.create('span', {'class' : 'mceItemHidden'});
    278 
    279                                         // Following code fixes IE issues by creating text nodes
    280                                         // using DOM methods instead of innerHTML.
    281                                         // Bug #3124: <PRE> elements content is broken after spellchecking.
    282                                         // Bug #1408: Preceding whitespace characters are removed
    283                                         // @TODO: I'm not sure that both are still issues on IE9.
    284                                         if (tinymce.isIE) {
    285                                                 // Enclose mispelled words with temporal tag
    286                                                 v = v.replace(rx, '$1<mcespell>$2</mcespell>');
    287                                                 // Loop over the content finding mispelled words
    288                                                 while ((pos = v.indexOf('<mcespell>')) != -1) {
    289                                                         // Add text node for the content before the word
    290                                                         txt = v.substring(0, pos);
    291                                                         if (txt.length) {
    292                                                                 node = doc.createTextNode(dom.decode(txt));
    293                                                                 elem.appendChild(node);
    294                                                         }
    295                                                         v = v.substring(pos+10);
    296                                                         pos = v.indexOf('</mcespell>');
    297                                                         txt = v.substring(0, pos);
    298                                                         v = v.substring(pos+11);
    299                                                         // Add span element for the word
    300                                                         elem.appendChild(dom.create('span', {'class' : 'mceItemHiddenSpellWord'}, txt));
    301                                                 }
    302                                                 // Add text node for the rest of the content
    303                                                 if (v.length) {
    304                                                         node = doc.createTextNode(dom.decode(v));
    305                                                         elem.appendChild(node);
    306                                                 }
    307                                         } else {
    308                                                 // Other browsers preserve whitespace characters on innerHTML usage
    309                                                 elem.innerHTML = v.replace(rx, '$1<span class="mceItemHiddenSpellWord">$2</span>');
    310                                         }
    311 
    312                                         // Finally, replace the node with the container
    313                                         dom.replace(elem, n);
    314                                 }
    315                         });
    316 
    317                         se.setRng(r);
    318                 },
    319 
    320                 _showMenu : function(ed, e) {
    321                         var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target;
    322 
    323                         e = 0; // Fixes IE memory leak
    324 
    325                         if (!m) {
    326                                 m = ed.controlManager.createDropMenu('spellcheckermenu', {'class' : 'mceNoIcons'});
    327                                 t._menu = m;
    328                         }
    329 
    330                         if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) {
    331                                 m.removeAll();
    332                                 m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
    333 
    334                                 t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) {
    335                                         var ignoreRpc;
    336 
    337                                         m.removeAll();
    338 
    339                                         if (r.length > 0) {
    340                                                 m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
    341                                                 each(r, function(v) {
    342                                                         m.add({title : v, onclick : function() {
    343                                                                 dom.replace(ed.getDoc().createTextNode(v), wordSpan);
    344                                                                 t._checkDone();
    345                                                         }});
    346                                                 });
    347 
    348                                                 m.addSeparator();
    349                                         } else
    350                                                 m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
    351 
    352                                         if (ed.getParam('show_ignore_words', true)) {
    353                                                 ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", '');
    354                                                 m.add({
    355                                                         title : 'spellchecker.ignore_word',
    356                                                         onclick : function() {
    357                                                                 var word = wordSpan.innerHTML;
    358 
    359                                                                 dom.remove(wordSpan, 1);
    360                                                                 t._checkDone();
    361 
    362                                                                 // tell the server if we need to
    363                                                                 if (ignoreRpc) {
    364                                                                         ed.setProgressState(1);
    365                                                                         t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) {
    366                                                                                 ed.setProgressState(0);
    367                                                                         });
    368                                                                 }
    369                                                         }
    370                                                 });
    371 
    372                                                 m.add({
    373                                                         title : 'spellchecker.ignore_words',
    374                                                         onclick : function() {
    375                                                                 var word = wordSpan.innerHTML;
    376 
    377                                                                 t._removeWords(dom.decode(word));
    378                                                                 t._checkDone();
    379 
    380                                                                 // tell the server if we need to
    381                                                                 if (ignoreRpc) {
    382                                                                         ed.setProgressState(1);
    383                                                                         t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) {
    384                                                                                 ed.setProgressState(0);
    385                                                                         });
    386                                                                 }
    387                                                         }
    388                                                 });
    389                                         }
    390 
    391                                         if (t.editor.getParam("spellchecker_enable_learn_rpc")) {
    392                                                 m.add({
    393                                                         title : 'spellchecker.learn_word',
    394                                                         onclick : function() {
    395                                                                 var word = wordSpan.innerHTML;
    396 
    397                                                                 dom.remove(wordSpan, 1);
    398                                                                 t._checkDone();
    399 
    400                                                                 ed.setProgressState(1);
    401                                                                 t._sendRPC('learnWord', [t.selectedLang, word], function(r) {
    402                                                                         ed.setProgressState(0);
    403                                                                 });
    404                                                         }
    405                                                 });
    406                                         }
    407 
    408                                         m.update();
     647                        }
     648                });
     649
     650                editor.addButton('spellchecker', {
     651                        tooltip: 'Spellcheck',
     652                        onclick: spellcheck,
     653                        onPostRender: function() {
     654                                var self = this;
     655
     656                                editor.on('SpellcheckStart SpellcheckEnd', function() {
     657                                        self.active(started);
    409658                                });
    410 
    411                                 p1 = DOM.getPos(ed.getContentAreaContainer());
    412                                 m.settings.offset_x = p1.x;
    413                                 m.settings.offset_y = p1.y;
    414 
    415                                 ed.selection.select(wordSpan);
    416                                 p1 = dom.getPos(wordSpan);
    417                                 m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y);
    418 
    419                                 return tinymce.dom.Event.cancel(e);
    420                         } else
    421                                 m.hideMenu();
    422                 },
    423 
    424                 _checkDone : function() {
    425                         var t = this, ed = t.editor, dom = ed.dom, o;
    426 
    427                         each(dom.select('span'), function(n) {
    428                                 if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
    429                                         o = true;
    430                                         return false;
    431                                 }
    432                         });
    433 
    434                         if (!o)
    435                                 t._done();
    436                 },
    437 
    438                 _done : function() {
    439                         var t = this, la = t.active;
    440 
    441                         if (t.active) {
    442                                 t.active = 0;
    443                                 t._removeWords();
    444 
    445                                 if (t._menu)
    446                                         t._menu.hideMenu();
    447 
    448                                 if (la)
    449                                         t.editor.nodeChanged();
    450                         }
    451                 },
    452 
    453                 _sendRPC : function(m, p, cb) {
    454                         var t = this;
    455 
    456                         JSONRequest.sendRPC({
    457                                 url : t.rpcUrl,
    458                                 method : m,
    459                                 params : p,
    460                                 success : cb,
    461                                 error : function(e, x) {
    462                                         t.editor.setProgressState(0);
    463                                         t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
    464                                 }
    465                         });
    466                 }
     659                        }
     660                });
     661
     662                editor.on('remove', function() {
     663                        if (suggestionsMenu) {
     664                                suggestionsMenu.remove();
     665                                suggestionsMenu = null;
     666                        }
     667                });
    467668        });
    468 
    469         // Register plugin
    470         tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
    471 })();
     669});
     670
     671expose(["tinymce/spellcheckerplugin/DomTextMatcher","tinymce/spellcheckerplugin/Plugin"]);
     672})(this);
  • trunk/src/wp-includes/js/tinymce/plugins/spellchecker/plugin.min.js

    r26862 r26876  
    1 (function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);f.menuItems={};c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}f._updateMenu(l);f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);f.menuItems[n]=l;if(n==f.selectedLang){f.selectedItem=l}})});return g}},setLanguage:function(e){var d=this;if(e==d.selectedLang){return}if(tinymce.grep(d.languages,function(f){return f===e}).length===0){throw"Unknown language: "+e}d.selectedLang=e;if(d.menuItems){d._updateMenu(d.menuItems[e])}if(d.active){d._done()}},_updateMenu:function(d){d.setSelected(1);this.selectedItem.setSelected(0);this.selectedItem=d},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d<f.length;d++){e+="\\"+f.charAt(d)}return e},_getWords:function(){var e=this.editor,g=[],d="",f={},h=[];this._walk(e.getBody(),function(i){if(i.nodeType==3){d+=i.nodeValue+" "}});if(e.getParam("spellchecker_word_pattern")){h=d.match("("+e.getParam("spellchecker_word_pattern")+")","gi")}else{d=d.replace(new RegExp("([0-9]|["+this._getSeparators()+"])","g")," ");d=tinymce.trim(d.replace(/(\s+)/g," "));h=d.split(" ")}c(h,function(i){if(!f[i]){g.push(i);f[i]=1}});return g},_removeWords:function(d){var e=this.editor,h=e.dom,g=e.selection,f=g.getRng(true);c(h.select("span").reverse(),function(i){if(i&&(h.hasClass(i,"mceItemHiddenSpellWord")||h.hasClass(i,"mceItemHidden"))){if(!d||h.decode(i.innerHTML)==d){h.remove(i,1)}}});g.setRng(f)},_markWords:function(l){var h=this.editor,g=h.dom,j=h.getDoc(),i=h.selection,d=i.getRng(true),e=[],k=l.join("|"),m=this._getSeparators(),f=new RegExp("(^|["+m+"])("+k+")(?=["+m+"]|$)","g");this._walk(h.getBody(),function(o){if(o.nodeType==3){e.push(o)}});c(e,function(t){var r,q,o,s,p=t.nodeValue;f.lastIndex=0;if(f.test(p)){p=g.encode(p);q=g.create("span",{"class":"mceItemHidden"});if(tinymce.isIE){p=p.replace(f,"$1<mcespell>$2</mcespell>");while((s=p.indexOf("<mcespell>"))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(g.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("</mcespell>");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(g.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(g.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(f,'$1<span class="mceItemHiddenSpellWord">$2</span>')}g.replace(q,t)}});i.setRng(d)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}if(h.getParam("show_ignore_words",true)){e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}})}if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})();
     1!function(e,t){"use strict";function n(e,t){for(var n,r=[],o=0;o<e.length;++o){if(n=c[e[o]]||i(e[o]),!n)throw"module definition dependecy not found: "+e[o];r.push(n)}t.apply(null,r)}function r(e,r,o){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(r===t)throw"invalid module definition, dependencies must be specified";if(o===t)throw"invalid module definition, definition function must be specified";n(r,function(){c[e]=o.apply(null,arguments)})}function o(e){return!!c[e]}function i(t){for(var n=e,r=t.split(/[.\/]/),o=0;o<r.length;++o){if(!n[r[o]])return;n=n[r[o]]}return n}function a(n){for(var r=0;r<n.length;r++){for(var o=e,i=n[r],a=i.split(/[.\/]/),l=0;l<a.length-1;++l)o[a[l]]===t&&(o[a[l]]={}),o=o[a[l]];o[a[a.length-1]]=c[i]}}var c={},l="tinymce/spellcheckerplugin/DomTextMatcher",d="tinymce/spellcheckerplugin/Plugin",s="tinymce/PluginManager",u="tinymce/util/Tools",f="tinymce/ui/Menu",h="tinymce/dom/DOMUtils",g="tinymce/util/JSONRequest",p="tinymce/util/URI";r(l,[],function(){return function(e,t,n){function r(e){if(!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";var t=e.index;return[t,t+e[0].length,[e[0]]]}function o(e){var t;if(3===e.nodeType)return e.data;if(m[e.nodeName]&&!p[e.nodeName])return"";if(t="",(p[e.nodeName]||v[e.nodeName])&&(t+="\n"),e=e.firstChild)do t+=o(e);while(e=e.nextSibling);return t}function i(e,t,n){var r,o,i,a,c=[],l=0,d=e,s=t.shift(),u=0;e:for(;;){if((p[d.nodeName]||v[d.nodeName])&&l++,3===d.nodeType&&(!o&&d.length+l>=s[1]?(o=d,a=s[1]-l):r&&c.push(d),!r&&d.length+l>s[0]&&(r=d,i=s[0]-l),l+=d.length),r&&o){if(d=n({startNode:r,startNodeIndex:i,endNode:o,endNodeIndex:a,innerNodes:c,match:s[2],matchIndex:u}),l-=o.length-a,r=null,o=null,c=[],s=t.shift(),u++,!s)break}else{if((!m[d.nodeName]||p[d.nodeName])&&d.firstChild){d=d.firstChild;continue}if(d.nextSibling){d=d.nextSibling;continue}}for(;;){if(d.nextSibling){d=d.nextSibling;break}if(d.parentNode===e)break e;d=d.parentNode}}}function a(e){var t;if("function"!=typeof e){var n=e.nodeType?e:g.createElement(e);t=function(e,t){var r=n.cloneNode(!1);return r.setAttribute("data-mce-index",t),e&&r.appendChild(g.createTextNode(e)),r}}else t=e;return function r(e){var n,r,o,i=e.startNode,a=e.endNode,c=e.matchIndex;if(i===a){var l=i;o=l.parentNode,e.startNodeIndex>0&&(n=g.createTextNode(l.data.substring(0,e.startNodeIndex)),o.insertBefore(n,l));var d=t(e.match[0],c);return o.insertBefore(d,l),e.endNodeIndex<l.length&&(r=g.createTextNode(l.data.substring(e.endNodeIndex)),o.insertBefore(r,l)),l.parentNode.removeChild(l),d}n=g.createTextNode(i.data.substring(0,e.startNodeIndex)),r=g.createTextNode(a.data.substring(e.endNodeIndex));for(var s=t(i.data.substring(e.startNodeIndex),c),u=[],f=0,h=e.innerNodes.length;h>f;++f){var p=e.innerNodes[f],m=t(p.data,c);p.parentNode.replaceChild(m,p),u.push(m)}var v=t(a.data.substring(0,e.endNodeIndex),c);return o=i.parentNode,o.insertBefore(n,i),o.insertBefore(s,i),o.removeChild(i),o=a.parentNode,o.insertBefore(v,a),o.insertBefore(r,a),o.removeChild(a),v}}function c(e){var t=[];return l(function(n,r){e(n,r)&&t.push(n)}),u=t,this}function l(e){for(var t=0,n=u.length;n>t&&e(u[t],t)!==!1;t++);return this}function d(e){return u.length&&(h=u.length,i(t,u,a(e))),this}var s,u=[],f,h=0,g,p,m,v;if(g=t.ownerDocument,p=n.getBlockElements(),m=n.getWhiteSpaceElements(),v=n.getShortEndedElements(),f=o(t),f&&e.global)for(;s=e.exec(f);)u.push(r(s));return{text:f,count:h,matches:u,each:l,filter:c,mark:d}}}),r(d,[l,s,u,f,h,g,p],function(e,t,n,r,o,i,a){t.add("spellchecker",function(t,c){function l(e){for(var t in e)return!1;return!0}function d(e,i){var a=[],c=m[i];n.each(c,function(e){a.push({text:e,onclick:function(){t.insertContent(e),u()}})}),a.push.apply(a,[{text:"-"},{text:"Ignore",onclick:function(){h(e,i)}},{text:"Ignore all",onclick:function(){h(e,i,!0)}},{text:"Finish",onclick:g}]),N=new r({items:a,context:"contextmenu",onautohide:function(e){-1!=e.target.className.indexOf("spellchecker")&&e.preventDefault()},onhide:function(){N.remove(),N=null}}),N.renderTo(document.body);var l=o.DOM.getPos(t.getContentAreaContainer()),d=t.dom.getPos(e);l.x+=d.x,l.y+=d.y,N.moveTo(l.x,l.y+e.offsetHeight)}function s(){function n(e){return t.setProgressState(!1),l(e)?(t.windowManager.alert("No misspellings found"),v=!1,void 0):(m=e,o.filter(function(t){return!!e[t[2][0]]}).mark(t.dom.create("span",{"class":"mce-spellchecker-word","data-mce-bogus":1})),o=null,t.fire("SpellcheckStart"),void 0)}function r(e,n,r){i.sendRPC({url:new a(c).toAbsolute(x.spellchecker_rpc_url),method:e,params:{lang:x.spellchecker_language||"en",words:n},success:function(e){r(e)},error:function(e,n){e="JSON Parse error."==e?"Non JSON response:"+n.responseText:"Error: "+e,t.windowManager.alert(e),t.setProgressState(!1),o=null,v=!1}})}var o,d=[],s={};if(v)return g(),void 0;v=!0;var u=t.getParam("spellchecker_wordchar_pattern")||new RegExp('[^\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`\xa7\xa9\xab\xae\xb1\xb6\xb7\xb8\xbb\xbc\xbd\xbe\xbf\xd7\xf7\xa4\u201d\u201c\u201e]+',"g");o=new e(u,t.getBody(),t.schema).each(function(e){var t=e[2][0];if(!s[t]){if(/^\d+$/.test(t)||1==t.length)return;d.push(t),s[t]=!0}}),t.setProgressState(!0);var f=x.spellchecker_callback||r;f("spellcheck",d,n)}function u(){t.dom.select("span.mce-spellchecker-word").length||g()}function f(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function h(e,r,o){o?n.each(t.dom.select("span.mce-spellchecker-word"),function(e){var t=e.innerText||e.textContent;t==r&&f(e)}):f(e),u()}function g(){var e,n,r;for(v=!1,r=t.getBody(),n=r.getElementsByTagName("span"),e=n.length;e--;)r=n[e],r.getAttribute("data-mce-index")&&f(r);t.fire("SpellcheckEnd")}function p(e){var n,r,o,i=-1,a,c;for(e=""+e,n=t.getBody().getElementsByTagName("span"),r=0;r<n.length&&(o=n[r],"mce-spellchecker-word"!=o.className||(i=o.getAttribute("data-mce-index"),i===e&&(i=e,a||(a=o.firstChild),c=o.firstChild),i===e||!c));r++);var l=t.dom.createRng();return l.setStart(a,0),l.setEnd(c,c.length),t.selection.setRng(l),l}var m,v,N,x=t.settings;t.on("click",function(e){if("mce-spellchecker-word"==e.target.className){e.preventDefault();var t=p(e.target.getAttribute("data-mce-index"));d(e.target,t.toString())}}),t.addMenuItem("spellchecker",{text:"Spellcheck",context:"tools",onclick:s,selectable:!0,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(v)})}}),t.addButton("spellchecker",{tooltip:"Spellcheck",onclick:s,onPostRender:function(){var e=this;t.on("SpellcheckStart SpellcheckEnd",function(){e.active(v)})}}),t.on("remove",function(){N&&(N.remove(),N=null)})})}),a([l,d])}(this);
  • trunk/src/wp-includes/js/tinymce/plugins/spellchecker/rpc.php

    r16405 r26876  
    9797        $spellchecker = new $config['general.engine']($config);
    9898        $result = call_user_func_array(array($spellchecker, $input['method']), $input['params']);
    99 } else
    100         die('{"result":null,"id":null,"error":{"errstr":"You must choose an spellchecker engine in the config.php file.","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}');
    101 
     99} else {
     100//      die('{"result":null,"id":null,"error":{"errstr":"You must choose an spellchecker engine in the config.php file.","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}');
     101        die('{"error":"You must choose spellchecker engine in the config.php file."}');
     102}
    102103// Request and response id should always be the same
    103104$output = array(
  • trunk/src/wp-includes/js/tinymce/plugins/tabfocus/plugin.js

    r26862 r26876  
    11/**
    2  * editor_plugin_src.js
     2 * plugin.js
    33 *
    4  * Copyright 2009, Moxiecode Systems AB
     4 * Copyright, Moxiecode Systems AB
    55 * Released under LGPL License.
    66 *
    7  * License: http://tinymce.moxiecode.com/license
    8  * Contributing: http://tinymce.moxiecode.com/contributing
     7 * License: http://www.tinymce.com/license
     8 * Contributing: http://www.tinymce.com/contributing
    99 */
    1010
    11 (function() {
    12         var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, explode = tinymce.explode;
     11/*global tinymce:true */
    1312
    14         tinymce.create('tinymce.plugins.TabFocusPlugin', {
    15                 init : function(ed, url) {
    16                         function tabCancel(ed, e) {
    17                                 if (e.keyCode === 9)
    18                                         return Event.cancel(e);
     13tinymce.PluginManager.add('tabfocus', function(editor) {
     14        var DOM = tinymce.DOM, each = tinymce.each, explode = tinymce.explode;
     15
     16        function tabCancel(e) {
     17                if (e.keyCode === 9) {
     18                        e.preventDefault();
     19                }
     20        }
     21
     22        function tabHandler(e) {
     23                var x, el, v, i;
     24
     25                function find(direction) {
     26                        el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
     27
     28                        function canSelectRecursive(e) {
     29                                return e.nodeName==="BODY" || (e.type != 'hidden' &&
     30                                        e.style.display != "none" &&
     31                                        e.style.visibility != "hidden" && canSelectRecursive(e.parentNode));
    1932                        }
    2033
    21                         function tabHandler(ed, e) {
    22                                 var x, i, f, el, v;
     34                        function canSelectInOldIe(el) {
     35                                return el.tabIndex || el.nodeName == "INPUT" || el.nodeName == "TEXTAREA";
     36                        }
    2337
    24                                 function find(d) {
    25                                         el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
     38                        function canSelect(el) {
     39                                return ((!canSelectInOldIe(el))) && el.getAttribute("tabindex") != '-1' && canSelectRecursive(el);
     40                        }
    2641
    27                                         function canSelectRecursive(e) {
    28                                                 return e.nodeName==="BODY" || (e.type != 'hidden' &&
    29                                                         !(e.style.display == "none") &&
    30                                                         !(e.style.visibility == "hidden") && canSelectRecursive(e.parentNode));
     42                        each(el, function(e, i) {
     43                                if (e.id == editor.id) {
     44                                        x = i;
     45                                        return false;
     46                                }
     47                        });
     48                        if (direction > 0) {
     49                                for (i = x + 1; i < el.length; i++) {
     50                                        if (canSelect(el[i])) {
     51                                                return el[i];
    3152                                        }
    32                                         function canSelectInOldIe(el) {
    33                                                 return el.attributes["tabIndex"].specified || el.nodeName == "INPUT" || el.nodeName == "TEXTAREA";
    34                                         }
    35                                         function isOldIe() {
    36                                                 return tinymce.isIE6 || tinymce.isIE7;
    37                                         }
    38                                         function canSelect(el) {
    39                                                 return ((!isOldIe() || canSelectInOldIe(el))) && el.getAttribute("tabindex") != '-1' && canSelectRecursive(el);
    40                                         }
    41 
    42                                         each(el, function(e, i) {
    43                                                 if (e.id == ed.id) {
    44                                                         x = i;
    45                                                         return false;
    46                                                 }
    47                                         });
    48                                         if (d > 0) {
    49                                                 for (i = x + 1; i < el.length; i++) {
    50                                                         if (canSelect(el[i]))
    51                                                                 return el[i];
    52                                                 }
    53                                         } else {
    54                                                 for (i = x - 1; i >= 0; i--) {
    55                                                         if (canSelect(el[i]))
    56                                                                 return el[i];
    57                                                 }
    58                                         }
    59 
    60                                         return null;
    6153                                }
    62 
    63                                 if (e.keyCode === 9) {
    64                                         v = explode(ed.getParam('tab_focus', ed.getParam('tabfocus_elements', ':prev,:next')));
    65 
    66                                         if (v.length == 1) {
    67                                                 v[1] = v[0];
    68                                                 v[0] = ':prev';
    69                                         }
    70 
    71                                         // Find element to focus
    72                                         if (e.shiftKey) {
    73                                                 if (v[0] == ':prev')
    74                                                         el = find(-1);
    75                                                 else
    76                                                         el = DOM.get(v[0]);
    77                                         } else {
    78                                                 if (v[1] == ':next')
    79                                                         el = find(1);
    80                                                 else
    81                                                         el = DOM.get(v[1]);
    82                                         }
    83 
    84                                         if (el) {
    85                                                 if (el.id && (ed = tinymce.get(el.id || el.name)))
    86                                                         ed.focus();
    87                                                 else
    88                                                         window.setTimeout(function() {
    89                                                                 if (!tinymce.isWebKit)
    90                                                                         window.focus();
    91                                                                 el.focus();
    92                                                         }, 10);
    93 
    94                                                 return Event.cancel(e);
     54                        } else {
     55                                for (i = x - 1; i >= 0; i--) {
     56                                        if (canSelect(el[i])) {
     57                                                return el[i];
    9558                                        }
    9659                                }
    9760                        }
    9861
    99                         ed.onKeyUp.add(tabCancel);
     62                        return null;
     63                }
    10064
    101                         if (tinymce.isGecko) {
    102                                 ed.onKeyPress.add(tabHandler);
    103                                 ed.onKeyDown.add(tabCancel);
    104                         } else
    105                                 ed.onKeyDown.add(tabHandler);
     65                if (e.keyCode === 9) {
     66                        v = explode(editor.getParam('tab_focus', editor.getParam('tabfocus_elements', ':prev,:next')));
    10667
    107                 },
     68                        if (v.length == 1) {
     69                                v[1] = v[0];
     70                                v[0] = ':prev';
     71                        }
    10872
    109                 getInfo : function() {
    110                         return {
    111                                 longname : 'Tabfocus',
    112                                 author : 'Moxiecode Systems AB',
    113                                 authorurl : 'http://tinymce.moxiecode.com',
    114                                 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus',
    115                                 version : tinymce.majorVersion + "." + tinymce.minorVersion
    116                         };
     73                        // Find element to focus
     74                        if (e.shiftKey) {
     75                                if (v[0] == ':prev') {
     76                                        el = find(-1);
     77                                } else {
     78                                        el = DOM.get(v[0]);
     79                                }
     80                        } else {
     81                                if (v[1] == ':next') {
     82                                        el = find(1);
     83                                } else {
     84                                        el = DOM.get(v[1]);
     85                                }
     86                        }
     87
     88                        if (el) {
     89                                var focusEditor = tinymce.get(el.id || el.name);
     90
     91                                if (el.id && focusEditor) {
     92                                        focusEditor.focus();
     93                                } else {
     94                                        window.setTimeout(function() {
     95                                                if (!tinymce.Env.webkit) {
     96                                                        window.focus();
     97                                                }
     98
     99                                                el.focus();
     100                                        }, 10);
     101                                }
     102
     103                                e.preventDefault();
     104                        }
     105                }
     106        }
     107
     108        editor.on('init', function() {
     109                if (editor.inline) {
     110                        // Remove default tabIndex in inline mode
     111                        tinymce.DOM.setAttrib(editor.getBody(), 'tabIndex', null);
    117112                }
    118113        });
    119114
    120         // Register plugin
    121         tinymce.PluginManager.add('tabfocus', tinymce.plugins.TabFocusPlugin);
    122 })();
     115        editor.on('keyup', tabCancel);
     116
     117        if (tinymce.Env.gecko) {
     118                editor.on('keypress keydown', tabHandler);
     119        } else {
     120                editor.on('keydown', tabHandler);
     121        }
     122});
  • trunk/src/wp-includes/js/tinymce/plugins/tabfocus/plugin.min.js

    r26862 r26876  
    1 (function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]:not(iframe)");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})();
     1tinymce.PluginManager.add("tabfocus",function(e){function n(e){9===e.keyCode&&e.preventDefault()}function t(n){function t(n){function t(e){return"BODY"===e.nodeName||"hidden"!=e.type&&"none"!=e.style.display&&"hidden"!=e.style.visibility&&t(e.parentNode)}function r(e){return e.tabIndex||"INPUT"==e.nodeName||"TEXTAREA"==e.nodeName}function a(e){return!r(e)&&"-1"!=e.getAttribute("tabindex")&&t(e)}if(d=i.select(":input:enabled,*[tabindex]:not(iframe)"),o(d,function(n,t){return n.id==e.id?(u=t,!1):void 0}),n>0){for(c=u+1;c<d.length;c++)if(a(d[c]))return d[c]}else for(c=u-1;c>=0;c--)if(a(d[c]))return d[c];return null}var u,d,a,c;if(9===n.keyCode&&(a=r(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==a.length&&(a[1]=a[0],a[0]=":prev"),d=n.shiftKey?":prev"==a[0]?t(-1):i.get(a[0]):":next"==a[1]?t(1):i.get(a[1]))){var f=tinymce.get(d.id||d.name);d.id&&f?f.focus():window.setTimeout(function(){tinymce.Env.webkit||window.focus(),d.focus()},10),n.preventDefault()}}var i=tinymce.DOM,o=tinymce.each,r=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null)}),e.on("keyup",n),tinymce.Env.gecko?e.on("keypress keydown",t):e.on("keydown",t)});
  • trunk/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js

    r26862 r26876  
    1 /* global tinymce, getUserSetting, setUserSetting, switchEditors, autosave */
    2 /**
    3  * WordPress plugin.
    4  */
    5 
    6 (function() {
    7         var DOM = tinymce.DOM;
    8 
    9         tinymce.create('tinymce.plugins.WordPress', {
    10                 init : function(ed, url) {
    11                         var t = this, tbId = ed.getParam('wordpress_adv_toolbar', 'toolbar2'), last = 0, moreHTML, nextpageHTML, closeOnClick, mod_key, style;
    12                         moreHTML = '<img src="' + url + '/img/trans.gif" class="mce-wp-more mceItemNoResize" title="'+ed.getLang('wordpress.wp_more_alt')+'" />';
    13                         nextpageHTML = '<img src="' + url + '/img/trans.gif" class="mce-wp-nextpage mceItemNoResize" title="'+ed.getLang('wordpress.wp_page_alt')+'" />';
    14 
    15                         if ( getUserSetting('hidetb', '0') == '1' )
    16                                 ed.settings.wordpress_adv_hidden = 0;
    17 
    18                         // Hides the specified toolbar and resizes the iframe
    19                         ed.onPostRender.add(function() {
    20                                 var adv_toolbar = ed.controlManager.get(tbId);
    21                                 if ( ed.getParam('wordpress_adv_hidden', 1) && adv_toolbar ) {
    22                                         DOM.hide(adv_toolbar.id);
    23                                         t._resizeIframe(ed, tbId, 28);
     1/* global tinymce, autosave, getUserSetting, setUserSetting, switchEditors */
     2tinymce.PluginManager.add( 'wordpress', function( editor ) {
     3        var DOM = tinymce.DOM, wpAdvButton, modKey, style,
     4                last = 0;
     5
     6        function toggleToolbars( state ) {
     7                var iframe,
     8                        pixels = 0,
     9                        initial = state === 'hide',
     10                        toolbars = editor.theme.panel && editor.theme.panel.find('.toolbar');
     11
     12                if ( ! toolbars || toolbars.length < 2 || ( state === 'hide' && ! toolbars[1].visible() ) ) {
     13                        return;
     14                }
     15
     16                if ( ! state && toolbars[1].visible() ) {
     17                        state = 'hide';
     18                }
     19
     20                tinymce.each( toolbars, function( toolbar, i ) {
     21                        if ( i > 0 ) {
     22                                if ( state === 'hide' ) {
     23                                        toolbar.hide();
     24                                        pixels += 30;
     25                                } else {
     26                                        toolbar.show();
     27                                        pixels -= 30;
    2428                                }
    25                         });
    26 
    27                         // Register commands
    28                         ed.addCommand('WP_More', function() {
    29                                 ed.execCommand('mceInsertContent', 0, moreHTML);
    30                         });
    31 
    32                         ed.addCommand('WP_Page', function() {
    33                                 ed.execCommand('mceInsertContent', 0, nextpageHTML);
    34                         });
    35 
    36                         ed.addCommand('WP_Help', function() {
    37                                 ed.windowManager.open({
    38                                         url : tinymce.baseURL + '/wp-mce-help.php',
    39                                         width : 450,
    40                                         height : 420,
    41                                         inline : 1
     29                        }
     30                });
     31
     32                if ( pixels && ! initial ) {
     33                        iframe = editor.getContentAreaContainer().firstChild;
     34                        DOM.setStyle( iframe, 'height', iframe.clientHeight + pixels ); // Resize iframe
     35
     36                        if ( state === 'hide' ) {
     37                                setUserSetting('hidetb', '1');
     38                                wpAdvButton && wpAdvButton.active( false );
     39                        } else {
     40                                setUserSetting('hidetb', '0');
     41                                wpAdvButton && wpAdvButton.active( true );
     42                        }
     43                }
     44        }
     45
     46        // Add the kitchen sink button :)
     47        editor.addButton( 'wp_adv', {
     48                tooltip: 'Toolbar Toggle',
     49                cmd: 'WP_Adv',
     50                onPostRender: function() {
     51                        wpAdvButton = this;
     52                }
     53        });
     54
     55        // Hide the toolbars after loading
     56        editor.on( 'PostRender', function() {
     57                if ( getUserSetting('hidetb', '1') === '1' ) {
     58                        toggleToolbars( 'hide' );
     59                }
     60        });
     61
     62        editor.addCommand( 'WP_Adv', function() {
     63                toggleToolbars();
     64        });
     65
     66        editor.on( 'focus', function() {
     67        window.wpActiveEditor = editor.id;
     68    });
     69
     70        // Replace Read More/Next Page tags with images
     71        editor.on( 'BeforeSetContent', function( e ) {
     72                if ( e.content ) {
     73                        if ( e.content.indexOf( '<!--more' ) !== -1 ) {
     74                                e.content = e.content.replace( /<!--more(.*?)-->/g, function( match, moretext ) {
     75                                        return '<img src="' + tinymce.Env.transparentSrc + '" data-wp-more="' + moretext + '" ' +
     76                                                'class="wp-more-tag mce-wp-more" title="Read More..." data-mce-resize="false" data-mce-placeholder="1" />';
    4277                                });
    43                         });
    44 
    45                         ed.addCommand('WP_Adv', function() {
    46                                 var cm = ed.controlManager, id = cm.get(tbId).id;
    47 
    48                                 if ( 'undefined' == id )
    49                                         return;
    50 
    51                                 if ( DOM.isHidden(id) ) {
    52                                         cm.setActive('wp_adv', 1);
    53                                         DOM.show(id);
    54                                         t._resizeIframe(ed, tbId, -28);
    55                                         ed.settings.wordpress_adv_hidden = 0;
    56                                         setUserSetting('hidetb', '1');
    57                                 } else {
    58                                         cm.setActive('wp_adv', 0);
    59                                         DOM.hide(id);
    60                                         t._resizeIframe(ed, tbId, 28);
    61                                         ed.settings.wordpress_adv_hidden = 1;
    62                                         setUserSetting('hidetb', '0');
    63                                 }
    64                         });
    65 
    66                         ed.addCommand('WP_Medialib', function() {
    67                                 if ( typeof wp !== 'undefined' && wp.media && wp.media.editor )
    68                                         wp.media.editor.open( ed.id );
    69                         });
    70 
    71                         // Register buttons
    72                         ed.addButton('wp_more', {
    73                                 title : 'wordpress.wp_more_desc',
    74                                 cmd : 'WP_More'
    75                         });
    76 
    77                         ed.addButton('wp_page', {
    78                                 title : 'wordpress.wp_page_desc',
    79                                 image : url + '/img/page.gif',
    80                                 cmd : 'WP_Page'
    81                         });
    82 
    83                         ed.addButton('wp_help', {
    84                                 title : 'wordpress.wp_help_desc',
    85                                 cmd : 'WP_Help'
    86                         });
    87 
    88                         ed.addButton('wp_adv', {
    89                                 title : 'wordpress.wp_adv_desc',
    90                                 cmd : 'WP_Adv'
    91                         });
    92 
    93                         // Add Media button
    94                         ed.addButton('add_media', {
    95                                 title : 'wordpress.add_media',
    96                                 image : url + '/img/image.gif',
    97                                 cmd : 'WP_Medialib'
    98                         });
    99 
    100                         // Add Media buttons to fullscreen and handle align buttons for image captions
    101                         ed.onBeforeExecCommand.add(function(ed, cmd, ui, val, o) {
    102                                 var DOM = tinymce.DOM, n, DL, DIV, cls, a, align;
    103                                 if ( 'mceFullScreen' == cmd ) {
    104                                         if ( 'mce_fullscreen' != ed.id && DOM.select('a.thickbox').length )
    105                                                 ed.settings.theme_advanced_buttons1 += ',|,add_media';
    106                                 }
    107 
    108                                 if ( 'JustifyLeft' == cmd || 'JustifyRight' == cmd || 'JustifyCenter' == cmd ) {
    109                                         n = ed.selection.getNode();
    110 
    111                                         if ( n.nodeName == 'IMG' ) {
    112                                                 align = cmd.substr(7).toLowerCase();
    113                                                 a = 'align' + align;
    114                                                 DL = ed.dom.getParent(n, 'dl.wp-caption');
    115                                                 DIV = ed.dom.getParent(n, 'div.mceTemp');
    116 
    117                                                 if ( DL && DIV ) {
    118                                                         cls = ed.dom.hasClass(DL, a) ? 'alignnone' : a;
    119                                                         DL.className = DL.className.replace(/align[^ '"]+\s?/g, '');
    120                                                         ed.dom.addClass(DL, cls);
    121 
    122                                                         if (cls == 'aligncenter')
    123                                                                 ed.dom.addClass(DIV, 'mceIEcenter');
    124                                                         else
    125                                                                 ed.dom.removeClass(DIV, 'mceIEcenter');
    126 
    127                                                         o.terminate = true;
    128                                                         ed.execCommand('mceRepaint');
    129                                                 } else {
    130                                                         if ( ed.dom.hasClass(n, a) )
    131                                                                 ed.dom.addClass(n, 'alignnone');
    132                                                         else
    133                                                                 ed.dom.removeClass(n, 'alignnone');
     78                        }
     79
     80                        if ( e.content.indexOf( '<!--nextpage-->' ) !== -1 ) {
     81                                e.content = e.content.replace( /<!--nextpage-->/g,
     82                                        '<img src="' + tinymce.Env.transparentSrc + '" class="wp-more-tag mce-wp-nextpage" ' +
     83                                                'title="Page break" data-mce-resize="false" data-mce-placeholder="1" />' );
     84                        }
     85                }
     86        });
     87
     88        // Replace images with tags
     89        editor.on( 'PostProcess', function( e ) {
     90                if ( e.get ) {
     91                        e.content = e.content.replace(/<img[^>]+>/g, function( image ) {
     92                                var match, moretext = '';
     93
     94                                if ( image.indexOf('wp-more-tag') !== -1 ) {
     95                                        if ( image.indexOf('mce-wp-more') !== -1 ) {
     96                                                if ( match = image.match( /data-wp-more="([^"]+)"/ ) ) {
     97                                                        moretext = match[1];
    13498                                                }
     99
     100                                                image = '<!--more' + moretext + '-->';
     101                                        } else if ( image.indexOf('mce-wp-nextpage') !== -1 ) {
     102                                                image = '<!--nextpage-->';
    135103                                        }
    136104                                }
    137105
    138                                 if ( tinymce.isWebKit && ( 'InsertUnorderedList' == cmd || 'InsertOrderedList' == cmd ) ) {
    139                                         if ( !style )
    140                                                 style = ed.dom.create('style', {'type': 'text/css'}, '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}');
    141 
    142                                         ed.getDoc().head.appendChild( style );
     106                                return image;
     107                        });
     108                }
     109        });
     110
     111        // Display the tag name instead of img in element path
     112        editor.on( 'ResolveName', function( e ) {
     113                var dom = editor.dom,
     114                        target = e.target;
     115
     116                if ( target.nodeName === 'IMG' && dom.hasClass( target, 'wp-more-tag' ) ) {
     117                        if ( dom.hasClass( target, 'mce-wp-more' ) ) {
     118                                e.name = 'more';
     119                        } else if ( dom.hasClass( target, 'mce-wp-nextpage' ) ) {
     120                                e.name = 'nextpage';
     121                        }
     122                }
     123        });
     124
     125        // Register commands
     126        editor.addCommand( 'WP_More', function( tag ) {
     127                var parent, html, title,
     128                        classname = 'wp-more-tag',
     129                        dom = editor.dom,
     130                        node = editor.selection.getNode();
     131
     132                tag = tag || 'more';
     133                classname += ' mce-wp-' + tag;
     134                title = tag === 'more' ? 'More...' : 'Next Page';
     135                html = '<img src="' + tinymce.Env.transparentSrc + '" title="' + title + '" class="' + classname + '" data-mce-resize="false" data-mce-placeholder="1" />';
     136
     137                if ( node.nodeName === 'BODY' ) {
     138                        editor.insertContent( '<p>' + html + '</p>' );
     139                        return;
     140                }
     141
     142                // Get the top level parent node
     143                parent = dom.getParent( node, function( found ) {
     144                        if ( found.parentNode && found.parentNode.nodeName === 'BODY' ) {
     145                                return true;
     146                        }
     147
     148                        return false;
     149                }, editor.getBody() );
     150
     151                if ( parent ) {
     152                        dom.insertAfter( dom.create( 'p', null, html ), parent );
     153                }
     154        });
     155
     156        editor.addCommand( 'WP_Page', function() {
     157                editor.execCommand( 'WP_More', 'nextpage' );
     158        });
     159
     160        editor.addCommand( 'WP_Help', function() {
     161                editor.windowManager.open({
     162                        url: tinymce.baseURL + '/wp-mce-help.php',
     163                        width: 450,
     164                        height: 420,
     165                        inline: 1
     166                });
     167        });
     168
     169        editor.addCommand( 'WP_Medialib', function() {
     170                if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
     171                        wp.media.editor.open( editor.id );
     172                }
     173        });
     174
     175        // Register buttons
     176        editor.addButton( 'wp_more', {
     177                tooltip: 'Insert Read More tag',
     178                onclick: function() {
     179                        editor.execCommand( 'WP_More', 'more' );
     180                }
     181        });
     182
     183        editor.addButton( 'wp_page', {
     184                tooltip: 'Page break',
     185                onclick: function() {
     186                        editor.execCommand( 'WP_More', 'nextpage' );
     187                }
     188        });
     189
     190        editor.addButton( 'wp_help', {
     191                tooltip: 'Help',
     192                cmd: 'WP_Help'
     193        });
     194
     195        // Menubar
     196        // Insert->Add Media
     197        if ( typeof wp !== 'undefined' && wp.media && wp.media.editor ) {
     198                editor.addMenuItem( 'add_media', {
     199                        text: 'Add Media',
     200                        context: 'insert',
     201                        cmd: 'WP_Medialib'
     202                });
     203        }
     204
     205        // Insert "Read More..."
     206        editor.addMenuItem( 'wp_more', {
     207                text: 'Insert Read More tag',
     208                context: 'insert',
     209                onclick: function() {
     210                        editor.execCommand( 'WP_More', 'more' );
     211                }
     212        });
     213
     214        // Insert "Next Page"
     215        editor.addMenuItem( 'wp_page', {
     216                text: 'Page break',
     217                context: 'insert',
     218                onclick: function() {
     219                        editor.execCommand( 'WP_More', 'nextpage' );
     220                }
     221        });
     222
     223        editor.on( 'BeforeExecCommand', function(e) {
     224                if ( tinymce.Env.webkit && ( e.command === 'InsertUnorderedList' || e.command === 'InsertOrderedList' ) ) {
     225                        if ( ! style ) {
     226                                style = editor.dom.create( 'style', {'type': 'text/css'},
     227                                        '#tinymce,#tinymce span,#tinymce li,#tinymce li>span,#tinymce p,#tinymce p>span{font:medium sans-serif;color:#000;line-height:normal;}');
     228                        }
     229
     230                        editor.getDoc().head.appendChild( style );
     231                }
     232        });
     233
     234        editor.on( 'ExecCommand', function( e ) {
     235                if ( tinymce.Env.webkit && style &&
     236                        ( 'InsertUnorderedList' === e.command || 'InsertOrderedList' === e.command ) ) {
     237
     238                        editor.dom.remove( style );
     239                }
     240        });
     241
     242        editor.on( 'init', function() {
     243                var env = tinymce.Env,
     244                        bodyClass = ['mceContentBody'], // back-compat for themes that use this in editor-style.css...
     245                        body = editor.getBody();
     246
     247                if ( editor.getParam( 'directionality' ) === 'rtl' ) {
     248                        bodyClass.push('rtl');
     249                }
     250
     251                if ( env.ie ) {
     252                        if ( parseInt( env.ie, 10 ) === 9 ) {
     253                                bodyClass.push('ie9');
     254                        } else if ( parseInt( env.ie, 10 ) === 8 ) {
     255                                bodyClass.push('ie8');
     256                        } else if ( env.ie < 8 ) {
     257                                bodyClass.push('ie7');
     258                        }
     259                }
     260
     261                bodyClass.push('wp-editor');
     262
     263                tinymce.each( bodyClass, function( cls ) {
     264                        if ( cls ) {
     265                                editor.dom.addClass( body, cls );
     266                        }
     267                });
     268
     269                // Remove invalid parent paragraphs when inserting HTML
     270                // TODO: still needed?
     271                editor.on( 'BeforeSetContent', function( e ) {
     272                        if ( e.content ) {
     273                                e.content = e.content.replace(/<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)( [^>]*)?>/gi, '<$1$2>');
     274                                e.content = e.content.replace(/<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)>\s*<\/p>/gi, '</$1>');
     275                        }
     276                });
     277        });
     278
     279        // Word count
     280        if ( typeof jQuery !== 'undefined' ) {
     281                editor.on( 'keyup', function( e ) {
     282                        var key = e.keyCode || e.charCode;
     283
     284                        if ( key === last ) {
     285                                return;
     286                        }
     287
     288                        if ( 13 === key || 8 === last || 46 === last ) {
     289                                jQuery(document).triggerHandler( 'wpcountwords', [ editor.getContent({ format : 'raw' }) ] );
     290                        }
     291
     292                        last = key;
     293                });
     294        }
     295
     296        editor.on( 'SaveContent', function( e ) {
     297                // If editor is hidden, we just want the textarea's value to be saved
     298                if ( editor.isHidden() ) {
     299                        e.content = e.element.value;
     300                        return;
     301                }
     302
     303                // Keep empty paragraphs :(
     304                e.content = e.content.replace( /<p>(<br ?\/?>|\u00a0|\uFEFF)?<\/p>/g, '<p>&nbsp;</p>' );
     305
     306                if ( editor.getParam( 'wpautop', true ) && typeof switchEditors !== 'undefined' ) {
     307                        e.content = switchEditors.pre_wpautop( e.content );
     308                }
     309        });
     310
     311        // Add custom shortcuts
     312        modKey = 'alt+shift';
     313
     314        editor.addShortcut( modKey + '+c', '', 'JustifyCenter' );
     315        editor.addShortcut( modKey + '+r', '', 'JustifyRight' );
     316        editor.addShortcut( modKey + '+l', '', 'JustifyLeft' );
     317        editor.addShortcut( modKey + '+j', '', 'JustifyFull' );
     318        editor.addShortcut( modKey + '+q', '', 'mceBlockQuote' );
     319        editor.addShortcut( modKey + '+u', '', 'InsertUnorderedList' );
     320        editor.addShortcut( modKey + '+o', '', 'InsertOrderedList' );
     321        editor.addShortcut( modKey + '+n', '', 'mceSpellCheck' );
     322        editor.addShortcut( modKey + '+a', '', 'WP_Link' );
     323        editor.addShortcut( modKey + '+s', '', 'unlink' );
     324        editor.addShortcut( modKey + '+m', '', 'WP_Medialib' );
     325        editor.addShortcut( modKey + '+z', '', 'WP_Adv' );
     326        editor.addShortcut( modKey + '+t', '', 'WP_More' );
     327        editor.addShortcut( modKey + '+d', '', 'Strikethrough' );
     328        editor.addShortcut( modKey + '+h', '', 'WP_Help' );
     329        editor.addShortcut( modKey + '+p', '', 'WP_Page' );
     330        editor.addShortcut( 'ctrl+s', '', function() {
     331                if ( typeof autosave === 'function' ) {
     332                        autosave();
     333                }
     334        });
     335
     336        // popup buttons for the gallery, etc.
     337        editor.on( 'init', function() {
     338                editor.dom.bind( editor.getWin(), 'scroll', function() {
     339                        _hideButtons();
     340                });
     341
     342                editor.dom.bind( editor.getBody(), 'dragstart', function() {
     343                        _hideButtons();
     344                });
     345        });
     346
     347        editor.on( 'BeforeExecCommand', function() {
     348                _hideButtons();
     349        });
     350
     351        editor.on( 'SaveContent', function() {
     352                _hideButtons();
     353        });
     354
     355        editor.on( 'MouseDown', function( e ) {
     356                if ( e.target.nodeName !== 'IMG' ) {
     357                        _hideButtons();
     358                }
     359        });
     360
     361        editor.on( 'keydown', function( e ) {
     362                if ( e.which === tinymce.util.VK.DELETE || e.which === tinymce.util.VK.BACKSPACE ) {
     363                        _hideButtons();
     364                }
     365        });
     366
     367        // Internal functions
     368        function _setEmbed( c ) {
     369                return c.replace( /\[embed\]([\s\S]+?)\[\/embed\][\s\u00a0]*/g, function( a, b ) {
     370                        return '<img width="300" height="200" src="' + tinymce.Env.transparentSrc + '" class="wp-oembed" ' +
     371                                'alt="'+ b +'" title="'+ b +'" data-mce-resize="false" data-mce-placeholder="1" />';
     372                });
     373        }
     374
     375        function _getEmbed( c ) {
     376                return c.replace( /<img[^>]+>/g, function( a ) {
     377                        if ( a.indexOf('class="wp-oembed') !== -1 ) {
     378                                var u = a.match( /alt="([^\"]+)"/ );
     379
     380                                if ( u[1] ) {
     381                                        a = '[embed]' + u[1] + '[/embed]';
    143382                                }
    144                         });
    145 
    146                         ed.onExecCommand.add( function( ed, cmd ) {
    147                                 if ( tinymce.isWebKit && style && ( 'InsertUnorderedList' == cmd || 'InsertOrderedList' == cmd ) )
    148                                         ed.dom.remove( style );
    149                         });
    150 
    151                         ed.onInit.add(function(ed) {
    152                                 var bodyClass = ed.getParam('body_class', ''), body = ed.getBody();
    153 
    154                                 // add body classes
    155                                 if ( bodyClass )
    156                                         bodyClass = bodyClass.split(' ');
    157                                 else
    158                                         bodyClass = [];
    159 
    160                                 if ( ed.getParam('directionality', '') == 'rtl' )
    161                                         bodyClass.push('rtl');
    162 
    163                                 if ( tinymce.isIE9 )
    164                                         bodyClass.push('ie9');
    165                                 else if ( tinymce.isIE8 )
    166                                         bodyClass.push('ie8');
    167                                 else if ( tinymce.isIE7 )
    168                                         bodyClass.push('ie7');
    169 
    170                                 if ( ed.id != 'wp_mce_fullscreen' && ed.id != 'mce_fullscreen' )
    171                                         bodyClass.push('wp-editor');
    172                                 else if ( ed.id == 'mce_fullscreen' )
    173                                         bodyClass.push('mce-fullscreen');
    174 
    175                                 tinymce.each( bodyClass, function(cls){
    176                                         if ( cls )
    177                                                 ed.dom.addClass(body, cls);
    178                                 });
    179 
    180                                 // make sure these run last
    181                                 ed.onNodeChange.add( function(ed, cm, e) {
    182                                         var DL;
    183 
    184                                         if ( e.nodeName == 'IMG' ) {
    185                                                 DL = ed.dom.getParent(e, 'dl.wp-caption');
    186                                         } else if ( e.nodeName == 'DIV' && ed.dom.hasClass(e, 'mceTemp') ) {
    187                                                 DL = e.firstChild;
    188 
    189                                                 if ( ! ed.dom.hasClass(DL, 'wp-caption') )
    190                                                         DL = false;
    191                                         }
    192 
    193                                         if ( DL ) {
    194                                                 if ( ed.dom.hasClass(DL, 'alignleft') )
    195                                                         cm.setActive('justifyleft', 1);
    196                                                 else if ( ed.dom.hasClass(DL, 'alignright') )
    197                                                         cm.setActive('justifyright', 1);
    198                                                 else if ( ed.dom.hasClass(DL, 'aligncenter') )
    199                                                         cm.setActive('justifycenter', 1);
    200                                         }
    201                                 });
    202 
    203                                 // remove invalid parent paragraphs when pasting HTML and/or switching to the HTML editor and back
    204                                 ed.onBeforeSetContent.add(function(ed, o) {
    205                                         if ( o.content ) {
    206                                                 o.content = o.content.replace(/<p>\s*<(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)( [^>]*)?>/gi, '<$1$2>');
    207                                                 o.content = o.content.replace(/<\/(p|div|ul|ol|dl|table|blockquote|h[1-6]|fieldset|pre|address)>\s*<\/p>/gi, '</$1>');
    208                                         }
    209                                 });
    210                         });
    211 
    212                         // Word count
    213                         if ( 'undefined' != typeof(jQuery) ) {
    214                                 ed.onKeyUp.add(function(ed, e) {
    215                                         var k = e.keyCode || e.charCode;
    216 
    217                                         if ( k == last )
    218                                                 return;
    219 
    220                                         if ( 13 == k || 8 == last || 46 == last )
    221                                                 jQuery(document).triggerHandler('wpcountwords', [ ed.getContent({format : 'raw'}) ]);
    222 
    223                                         last = k;
    224                                 });
    225                         }
    226 
    227                         // keep empty paragraphs :(
    228                         ed.onSaveContent.addToTop(function(ed, o) {
    229                                 o.content = o.content.replace(/<p>(<br ?\/?>|\u00a0|\uFEFF)?<\/p>/g, '<p>&nbsp;</p>');
    230                         });
    231 
    232                         // Fix bug in iOS Safari where it's impossible to type after a touchstart event on the parent document.
    233                         // Happens after zooming in or out while the keyboard is open. See #25131.
    234                         if ( tinymce.isIOS5 ) {
    235                                 ed.onKeyDown.add( function() {
    236                                         if ( document.activeElement == document.body ) {
    237                                                 ed.getWin().focus();
    238                                         }
    239                                 });
    240                         }
    241 
    242                         ed.onSaveContent.add(function(ed, o) {
    243                                 // If editor is hidden, we just want the textarea's value to be saved
    244                                 if ( ed.isHidden() )
    245                                         o.content = o.element.value;
    246                                 else if ( ed.getParam('wpautop', true) && typeof(switchEditors) == 'object' )
    247                                         o.content = switchEditors.pre_wpautop(o.content);
    248                         });
    249 
    250                         /* disable for now
    251                         ed.onBeforeSetContent.add(function(ed, o) {
    252                                 o.content = t._setEmbed(o.content);
    253                         });
    254 
    255                         ed.onPostProcess.add(function(ed, o) {
    256                                 if ( o.get )
    257                                         o.content = t._getEmbed(o.content);
    258                         });
    259                         */
    260 
    261                         // Add listeners to handle more break
    262                         t._handleMoreBreak(ed, url);
    263 
    264                         // Add custom shortcuts
    265                         mod_key = 'alt+shift';
    266 
    267                 //      if ( tinymce.isGecko ) // disable for mow, too many shortcuts conflicts
    268                 //              mod_key = 'ctrl+alt';
    269 
    270                         ed.addShortcut(mod_key + '+c', 'justifycenter_desc', 'JustifyCenter');
    271                         ed.addShortcut(mod_key + '+r', 'justifyright_desc', 'JustifyRight');
    272                         ed.addShortcut(mod_key + '+l', 'justifyleft_desc', 'JustifyLeft');
    273                         ed.addShortcut(mod_key + '+j', 'justifyfull_desc', 'JustifyFull');
    274                         ed.addShortcut(mod_key + '+q', 'blockquote_desc', 'mceBlockQuote');
    275                         ed.addShortcut(mod_key + '+u', 'bullist_desc', 'InsertUnorderedList');
    276                         ed.addShortcut(mod_key + '+o', 'numlist_desc', 'InsertOrderedList');
    277                         ed.addShortcut(mod_key + '+n', 'spellchecker.desc', 'mceSpellCheck');
    278                         ed.addShortcut(mod_key + '+a', 'link_desc', 'WP_Link');
    279                         ed.addShortcut(mod_key + '+s', 'unlink_desc', 'unlink');
    280                         ed.addShortcut(mod_key + '+m', 'image_desc', 'WP_Medialib');
    281                         ed.addShortcut(mod_key + '+z', 'wordpress.wp_adv_desc', 'WP_Adv');
    282                         ed.addShortcut(mod_key + '+t', 'wordpress.wp_more_desc', 'WP_More');
    283                         ed.addShortcut(mod_key + '+d', 'striketrough_desc', 'Strikethrough');
    284                         ed.addShortcut(mod_key + '+h', 'help_desc', 'WP_Help');
    285                         ed.addShortcut(mod_key + '+p', 'wordpress.wp_page_desc', 'WP_Page');
    286                         ed.addShortcut('ctrl+s', 'save_desc', function(){if('function'==typeof autosave)autosave();});
    287 
    288                         if ( /\bwpfullscreen\b/.test(ed.settings.plugins) )
    289                                 ed.addShortcut(mod_key + '+w', 'wordpress.wp_fullscreen_desc', 'wpFullScreen');
    290                         else if ( /\bfullscreen\b/.test(ed.settings.plugins) )
    291                                 ed.addShortcut(mod_key + '+g', 'fullscreen.desc', 'mceFullScreen');
    292 
    293                         // popup buttons for images and the gallery
    294                         ed.onInit.add(function(ed) {
    295                                 tinymce.dom.Event.add(ed.getWin(), 'scroll', function() {
    296                                         ed.plugins.wordpress._hideButtons();
    297                                 });
    298                                 tinymce.dom.Event.add(ed.getBody(), 'dragstart', function() {
    299                                         ed.plugins.wordpress._hideButtons();
    300                                 });
    301                         });
    302 
    303                         ed.onBeforeExecCommand.add( function( ed ) {
    304                                 ed.plugins.wordpress._hideButtons();
    305                         });
    306 
    307                         ed.onSaveContent.add( function( ed ) {
    308                                 ed.plugins.wordpress._hideButtons();
    309                         });
    310 
    311                         ed.onMouseDown.add(function(ed, e) {
    312                                 if ( e.target.nodeName != 'IMG' )
    313                                         ed.plugins.wordpress._hideButtons();
    314                         });
    315 
    316                         ed.onKeyDown.add(function(ed, e){
    317                                 if ( e.which == tinymce.VK.DELETE || e.which == tinymce.VK.BACKSPACE )
    318                                         ed.plugins.wordpress._hideButtons();
    319                         });
    320 
    321                         closeOnClick = function(e){
    322                                 var id;
    323 
    324                                 if ( e.target.id == 'mceModalBlocker' || e.target.className == 'ui-widget-overlay' ) {
    325                                         for ( id in ed.windowManager.windows ) {
    326                                                 ed.windowManager.close(null, id);
    327                                         }
    328                                 }
    329                         };
    330 
    331                         // close popups when clicking on the background
    332                         tinymce.dom.Event.remove(document.body, 'click', closeOnClick);
    333                         tinymce.dom.Event.add(document.body, 'click', closeOnClick);
    334                 },
    335 
    336                 getInfo : function() {
    337                         return {
    338                                 longname : 'WordPress Plugin',
    339                                 author : 'WordPress', // add Moxiecode?
    340                                 authorurl : 'http://wordpress.org',
    341                                 infourl : 'http://wordpress.org',
    342                                 version : '3.0'
    343                         };
    344                 },
    345 
    346                 // Internal functions
    347                 _setEmbed : function(c) {
    348                         return c.replace(/\[embed\]([\s\S]+?)\[\/embed\][\s\u00a0]*/g, function(a,b){
    349                                 return '<img width="300" height="200" src="' + tinymce.baseURL + '/plugins/wordpress/img/trans.gif" class="wp-oembed mceItemNoResize" alt="'+b+'" title="'+b+'" />';
    350                         });
    351                 },
    352 
    353                 _getEmbed : function(c) {
    354                         return c.replace(/<img[^>]+>/g, function(a) {
    355                                 if ( a.indexOf('class="wp-oembed') != -1 ) {
    356                                         var u = a.match(/alt="([^\"]+)"/);
    357                                         if ( u[1] )
    358                                                 a = '[embed]' + u[1] + '[/embed]';
    359                                 }
    360                                 return a;
    361                         });
    362                 },
    363 
    364                 _showButtons : function(n, id) {
    365                         var ed = tinymce.activeEditor, p1, p2, vp, DOM = tinymce.DOM, X, Y;
    366 
    367                         vp = ed.dom.getViewPort(ed.getWin());
    368                         p1 = DOM.getPos(ed.getContentAreaContainer());
    369                         p2 = ed.dom.getPos(n);
    370 
    371                         X = Math.max(p2.x - vp.x, 0) + p1.x;
    372                         Y = Math.max(p2.y - vp.y, 0) + p1.y;
    373 
    374                         DOM.setStyles(id, {
    375                                 'top' : Y+5+'px',
    376                                 'left' : X+5+'px',
    377                                 'display' : 'block'
    378                         });
    379                 },
    380 
    381                 _hideButtons : function() {
    382                         var DOM = tinymce.DOM;
    383                         DOM.hide( DOM.select('#wp_editbtns, #wp_gallerybtns') );
    384                 },
    385 
    386                 // Resizes the iframe by a relative height value
    387                 _resizeIframe : function(ed, tb_id, dy) {
    388                         var ifr = ed.getContentAreaContainer().firstChild;
    389 
    390                         DOM.setStyle(ifr, 'height', ifr.clientHeight + dy); // Resize iframe
    391                         ed.theme.deltaHeight += dy; // For resize cookie
    392                 },
    393 
    394                 _handleMoreBreak : function(ed, url) {
    395                         var moreHTML, nextpageHTML;
    396 
    397                         moreHTML = '<img src="' + url + '/img/trans.gif" alt="$1" class="mce-wp-more mceItemNoResize" title="'+ed.getLang('wordpress.wp_more_alt')+'" />';
    398                         nextpageHTML = '<img src="' + url + '/img/trans.gif" class="mce-wp-nextpage mceItemNoResize" title="'+ed.getLang('wordpress.wp_page_alt')+'" />';
    399 
    400                         // Display morebreak instead if img in element path
    401                         ed.onPostRender.add(function() {
    402                                 if (ed.theme.onResolveName) {
    403                                         ed.theme.onResolveName.add(function(th, o) {
    404                                                 if (o.node.nodeName == 'IMG') {
    405                                                         if ( ed.dom.hasClass(o.node, 'mce-wp-more') )
    406                                                                 o.name = 'wpmore';
    407                                                         if ( ed.dom.hasClass(o.node, 'mce-wp-nextpage') )
    408                                                                 o.name = 'wppage';
    409                                                 }
    410 
    411                                         });
    412                                 }
    413                         });
    414 
    415                         // Replace morebreak with images
    416                         ed.onBeforeSetContent.add(function(ed, o) {
    417                                 if ( o.content ) {
    418                                         o.content = o.content.replace(/<!--more(.*?)-->/g, moreHTML);
    419                                         o.content = o.content.replace(/<!--nextpage-->/g, nextpageHTML);
    420                                 }
    421                         });
    422 
    423                         // Replace images with morebreak
    424                         ed.onPostProcess.add(function(ed, o) {
    425                                 if (o.get)
    426                                         o.content = o.content.replace(/<img[^>]+>/g, function(im) {
    427                                                 if (im.indexOf('class="mce-wp-more') !== -1) {
    428                                                         var m, moretext = (m = im.match(/alt="(.*?)"/)) ? m[1] : '';
    429                                                         im = '<!--more'+moretext+'-->';
    430                                                 }
    431                                                 if (im.indexOf('class="mce-wp-nextpage') !== -1)
    432                                                         im = '<!--nextpage-->';
    433 
    434                                                 return im;
    435                                         });
    436                         });
    437 
    438                         // Set active buttons if user selected pagebreak or more break
    439                         ed.onNodeChange.add(function(ed, cm, n) {
    440                                 cm.setActive('wp_page', n.nodeName === 'IMG' && ed.dom.hasClass(n, 'mce-wp-nextpage'));
    441                                 cm.setActive('wp_more', n.nodeName === 'IMG' && ed.dom.hasClass(n, 'mce-wp-more'));
    442                         });
    443                 }
    444         });
    445 
    446         // Register plugin
    447         tinymce.PluginManager.add('wordpress', tinymce.plugins.WordPress);
    448 })();
     383                        }
     384
     385                        return a;
     386                });
     387        }
     388
     389        function _showButtons( n, id ) {
     390                var p1, p2, vp, X, Y;
     391
     392                vp = editor.dom.getViewPort( editor.getWin() );
     393                p1 = DOM.getPos( editor.getContentAreaContainer() );
     394                p2 = editor.dom.getPos( n );
     395
     396                X = Math.max( p2.x - vp.x, 0 ) + p1.x;
     397                Y = Math.max( p2.y - vp.y, 0 ) + p1.y;
     398
     399                DOM.setStyles( id, {
     400                        'top' : Y + 5 + 'px',
     401                        'left' : X + 5 + 'px',
     402                        'display': 'block'
     403                });
     404        }
     405
     406        function _hideButtons() {
     407                DOM.hide( DOM.select( '#wp_editbtns, #wp_gallerybtns' ) );
     408        }
     409
     410        // Expose some functions (back-compat)
     411        return {
     412                _showButtons: _showButtons,
     413                _hideButtons: _hideButtons,
     414                _setEmbed: _setEmbed,
     415                _getEmbed: _getEmbed
     416        };
     417});
  • trunk/src/wp-includes/js/tinymce/plugins/wpdialogs/plugin.js

    r26862 r26876  
    11/* global tinymce */
    2 (function() {
    3         tinymce.create('tinymce.plugins.WPDialogs', {
    4                 init : function( ed ) {
    5                         tinymce.create('tinymce.WPWindowManager:tinymce.InlineWindowManager', {
    6                                 WPWindowManager : function(ed) {
    7                                         this.parent(ed);
    8                                 },
    92
    10                                 open : function(f, p) {
    11                                         var t = this, element;
     3tinymce.WPWindowManager = function( editor ) {
     4        var element;
    125
    13                                         if ( ! f.wpDialog )
    14                                                 return this.parent( f, p );
    15                                         else if ( ! f.id )
    16                                                 return;
     6        this.parent = editor.windowManager;
     7        this.editor = editor;
    178
    18                                         element = jQuery('#' + f.id);
    19                                         if ( ! element.length )
    20                                                 return;
     9        tinymce.extend( this, this.parent )
    2110
    22                                         t.features = f;
    23                                         t.params = p;
    24                                         t.onOpen.dispatch(t, f, p);
    25                                         t.element = t.windows[ f.id ] = element;
     11        this.open = function( args, params ) {
     12                var self = this, element;
    2613
    27                                         // Store selection
    28                                         t.bookmark = t.editor.selection.getBookmark(1);
     14                if ( ! args.wpDialog )
     15                        return this.parent.open( args, params );
     16                else if ( ! args.id )
     17                        return;
    2918
    30                                         // Create the dialog if necessary
    31                                         if ( ! element.data('wpdialog') ) {
    32                                                 element.wpdialog({
    33                                                         title: f.title,
    34                                                         width: f.width,
    35                                                         height: f.height,
    36                                                         modal: true,
    37                                                         dialogClass: 'wp-dialog',
    38                                                         zIndex: 300000
    39                                                 });
    40                                         }
     19                self.element = element = jQuery('#' + args.id);
     20                if ( ! element.length )
     21                        return;
    4122
    42                                         element.wpdialog('open');
    43                                 },
    44                                 close : function() {
    45                                         if ( ! this.features.wpDialog )
    46                                                 return this.parent.apply( this, arguments );
     23                self.features = args;
     24                self.params = params;
     25                self.onOpen.dispatch( self, args, params );
     26                self.windows.push( element );
    4727
    48                                         this.element.wpdialog('close');
    49                                 }
     28                // Store selection
     29        //      self.bookmark = self.editor.selection.getBookmark(1);
     30
     31                // Create the dialog if necessary
     32                if ( ! element.data('wpdialog') ) {
     33                        element.wpdialog({
     34                                title: args.title,
     35                                width: args.width,
     36                                height: args.height,
     37                                modal: true,
     38                                dialogClass: 'wp-dialog',
     39                                zIndex: 300000
    5040                        });
     41                }
    5142
    52                         // Replace window manager
    53                         ed.onBeforeRenderUI.add(function() {
    54                                 ed.windowManager = new tinymce.WPWindowManager(ed);
    55                         });
    56                 },
     43                element.wpdialog('open');
     44        };
    5745
    58                 getInfo : function() {
    59                         return {
    60                                 longname : 'WPDialogs',
    61                                 author : 'WordPress',
    62                                 authorurl : 'http://wordpress.org',
    63                                 infourl : 'http://wordpress.org',
    64                                 version : '0.1'
    65                         };
    66                 }
     46        this.close = function() {
     47                if ( ! this.features.wpDialog )
     48                        return this.parent.close.apply( this, arguments );
     49
     50                this.element.wpdialog('close');
     51        };
     52}
     53
     54tinymce.PluginManager.add( 'wpdialogs', function( editor ) {
     55        // Replace window manager
     56        editor.on( 'init', function() {
     57                editor.windowManager = new tinymce.WPWindowManager( editor );
    6758        });
    68 
    69         // Register plugin
    70         tinymce.PluginManager.add('wpdialogs', tinymce.plugins.WPDialogs);
    71 })();
     59});
  • trunk/src/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js

    r26862 r26876  
    11/* global tinymce */
    2 (function() {
    3         var mouse = {};
    4 
    5         tinymce.create('tinymce.plugins.wpEditImage', {
    6                 url: '',
    7                 editor: {},
    8 
    9                 init: function(ed, url) {
    10                         var t = this;
    11 
    12                         t.url = url;
    13                         t.editor = ed;
    14                         t._createButtons();
    15 
    16                         ed.addCommand('WP_EditImage', t._editImage);
    17 
    18                         ed.onInit.add(function(ed) {
    19                                 ed.dom.events.add(ed.getBody(), 'mousedown', function(e) {
    20                                         var parent;
    21 
    22                                         if ( e.target.nodeName == 'IMG' && ( parent = ed.dom.getParent(e.target, 'div.mceTemp') ) ) {
    23                                                 if ( tinymce.isGecko )
    24                                                         ed.selection.select(parent);
    25                                                 else if ( tinymce.isWebKit )
    26                                                         ed.dom.events.prevent(e);
    27                                         }
     2tinymce.PluginManager.add( 'wpeditimage', function( editor ) {
     3        function parseShortcode( content ) {
     4                return content.replace( /(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function( a, b, c ) {
     5                        var id, cls, w, cap, img, width,
     6                                trim = tinymce.trim;
     7
     8                        id = b.match( /id=['"]([^'"]*)['"] ?/ );
     9                        if ( id ) {
     10                                b = b.replace( id[0], '' );
     11                        }
     12
     13                        cls = b.match( /align=['"]([^'"]*)['"] ?/ );
     14                        if ( cls ) {
     15                                b = b.replace( cls[0], '' );
     16                        }
     17
     18                        w = b.match( /width=['"]([0-9]*)['"] ?/ );
     19                        if ( w ) {
     20                                b = b.replace( w[0], '' );
     21                        }
     22
     23                        c = trim( c );
     24                        img = c.match( /((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)([\s\S]*)/i );
     25
     26                        if ( img && img[2] ) {
     27                                cap = trim( img[2] );
     28                                img = trim( img[1] );
     29                        } else {
     30                                // old captions shortcode style
     31                                cap = trim( b ).replace( /caption=['"]/, '' ).replace( /['"]$/, '' );
     32                                img = c;
     33                        }
     34
     35                        id = ( id && id[1] ) ? id[1] : '';
     36                        cls = ( cls && cls[1] ) ? cls[1] : 'alignnone';
     37                        w = ( w && w[1] ) ? w[1] : '';
     38
     39                        if ( ! w || ! cap ) {
     40                                return c;
     41                        }
     42
     43                        width = parseInt( w, 10 ) + 10;
     44
     45                        return '<div class="mceTemp" draggable="true"><dl id="'+ id +'" class="wp-caption '+ cls +'" style="width: '+ width +'px">' +
     46                                '<dt class="wp-caption-dt">'+ img +'</dt><dd class="wp-caption-dd">'+ cap +'</dd></dl></div>';
     47                });
     48        }
     49
     50        function getShortcode( content ) {
     51                return content.replace( /<div (?:id="attachment_|class="mceTemp)[^>]*>([\s\S]+?)<\/div>/g, function( a, b ) {
     52                        var ret = b.replace( /<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>/gi, function( a, b, c, cap ) {
     53                                var id, cls, w;
     54
     55                                w = c.match( /width="([0-9]*)"/ );
     56                                w = ( w && w[1] ) ? w[1] : '';
     57
     58                                if ( ! w || ! cap ) {
     59                                        return c;
     60                                }
     61
     62                                id = b.match( /id="([^"]*)"/ );
     63                                id = ( id && id[1] ) ? id[1] : '';
     64
     65                                cls = b.match( /class="([^"]*)"/ );
     66                                cls = ( cls && cls[1] ) ? cls[1] : '';
     67                                cls = cls.match( /align[a-z]+/ ) || 'alignnone';
     68
     69                                cap = cap.replace( /\r\n|\r/g, '\n' ).replace( /<[a-zA-Z0-9]+( [^<>]+)?>/g, function( a ) {
     70                                        // no line breaks inside HTML tags
     71                                        return a.replace( /[\r\n\t]+/, ' ' );
    2872                                });
    2973
    30                                 // when pressing Return inside a caption move the caret to a new parapraph under it
    31                                 ed.dom.events.add(ed.getBody(), 'keydown', function(e) {
    32                                         var n, DL, DIV, P;
    33 
    34                                         if ( e.keyCode == 13 ) {
    35                                                 n = ed.selection.getNode();
    36                                                 DL = ed.dom.getParent(n, 'dl.wp-caption');
    37 
    38                                                 if ( DL )
    39                                                         DIV = ed.dom.getParent(DL, 'div.mceTemp');
    40 
    41                                                 if ( DIV ) {
    42                                                         ed.dom.events.cancel(e);
    43                                                         P = ed.dom.create('p', {}, '\uFEFF');
    44                                                         ed.dom.insertAfter( P, DIV );
    45                                                         ed.selection.setCursorLocation(P, 0);
    46                                                         return false;
    47                                                 }
    48                                         }
    49                                 });
    50 
    51                                 // iOS6 doesn't show the buttons properly on click, show them on 'touchstart'
    52                                 if ( 'ontouchstart' in window ) {
    53                                         ed.dom.events.add(ed.getBody(), 'touchstart', function(e){
    54                                                 t._showButtons(e);
    55                                         });
    56                                 }
     74                                // convert remaining line breaks to <br>
     75                                cap = cap.replace( /\s*\n\s*/g, '<br />' );
     76
     77                                return '[caption id="'+ id +'" align="'+ cls +'" width="'+ w +'"]'+ c +' '+ cap +'[/caption]';
    5778                        });
    5879
    59                         // resize the caption <dl> when the image is soft-resized by the user
    60                         ed.onMouseUp.add(function(ed, e) {
    61                                 if ( tinymce.isWebKit || tinymce.isOpera )
    62                                         return;
    63 
    64                                 if ( mouse.x && (e.clientX != mouse.x || e.clientY != mouse.y) ) {
    65                                         var n = ed.selection.getNode();
    66 
    67                                         if ( 'IMG' == n.nodeName ) {
    68                                                 window.setTimeout(function(){
    69                                                         var DL = ed.dom.getParent(n, 'dl.wp-caption'), width;
    70 
    71                                                         if ( n.width != mouse.img_w || n.height != mouse.img_h )
    72                                                                 n.className = n.className.replace(/size-[^ "']+/, '');
    73 
    74                                                         if ( DL ) {
    75                                                                 width = ed.dom.getAttrib(n, 'width') || n.width;
    76                                                                 width = parseInt(width, 10);
    77                                                                 ed.dom.setStyle(DL, 'width', 10 + width);
    78                                                                 ed.execCommand('mceRepaint');
     80                        if ( ret.indexOf('[caption') !== 0 ) {
     81                                // the caption html seems brocken, try to find the image that may be wrapped in a link
     82                                // and may be followed by <p> with the caption text.
     83                                ret = b.replace( /[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2' );
     84                        }
     85
     86                        return ret;
     87                });
     88        }
     89
     90        editor.on( 'init', function() {
     91                var dom = editor.dom;
     92
     93                // Add caption field to the default image dialog
     94                editor.on( 'wpLoadImageForm', function( e ) {
     95                        if ( editor.getParam( 'wpeditimage_disable_captions' ) ) {
     96                                return;
     97                        }
     98
     99                        var captionField = {
     100                                type: 'textbox',
     101                                flex: 1,
     102                                name: 'caption',
     103                                minHeight: 60,
     104                                multiline: true,
     105                                scroll: true,
     106                                label: 'Image caption'
     107                        };
     108
     109                        e.data.splice( e.data.length - 1, 0, captionField );
     110                });
     111
     112                // Fix caption parent width for images added from URL
     113                editor.on( 'wpNewImageRefresh', function( e ) {
     114                        var parent, captionWidth;
     115
     116                        if ( parent = dom.getParent( e.node, 'dl.wp-caption' ) ) {
     117                                if ( ! parent.style.width ) {
     118                                        captionWidth = parseInt( e.node.clientWidth, 10 ) + 10;
     119                                        captionWidth = captionWidth ? captionWidth + 'px' : '50%';
     120                                        dom.setStyle( parent, 'width', captionWidth );
     121                                }
     122                        }
     123                });
     124
     125                editor.on( 'wpImageFormSubmit', function( e ) {
     126                        var data = e.imgData.data,
     127                                imgNode = e.imgData.node,
     128                                caption = e.imgData.caption,
     129                                captionId = '',
     130                                captionAlign = '',
     131                                captionWidth = '',
     132                                wrap, parent, html, P, imgId;
     133
     134                        // Temp image id so we can find the node later
     135                        data.id = '__wp-temp-img-id';
     136                        // Cancel the original callback
     137                        e.imgData.cancel = true;
     138
     139                        if ( ! data.style ) {
     140                                data.style = null;
     141                        }
     142
     143                        if ( ! data.src ) {
     144                                // Delete the image and the caption
     145                                if ( imgNode ) {
     146                                        if ( wrap = dom.getParent( imgNode, 'div.mceTemp' ) ) {
     147                                                dom.remove( wrap );
     148                                        } else if ( imgNode.parentNode.nodeName === 'A' ) {
     149                                                dom.remove( imgNode.parentNode );
     150                                        } else {
     151                                                dom.remove( imgNode );
     152                                        }
     153
     154                                        editor.nodeChanged();
     155                                }
     156                                return;
     157                        }
     158
     159                        if ( ! imgNode ) {
     160                                // New image inserted
     161                                html = dom.createHTML( 'img', data );
     162
     163                                if ( caption ) {
     164                                        node = editor.selection.getNode();
     165
     166                                        if ( data.width ) {
     167                                                captionWidth = parseInt( data.width, 10 ) + 10;
     168                                                captionWidth = ' style="width: '+ captionWidth +'px"';
     169                                        }
     170
     171                                        html = '<dl class="wp-caption alignnone"' + captionWidth + '>' +
     172                                                '<dt class="wp-caption-dt">'+ html +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>';
     173
     174                                        if ( node.nodeName === 'P' ) {
     175                                                parent = node;
     176                                        } else {
     177                                                parent = dom.getParent( node, 'p' );
     178                                        }
     179
     180                                        if ( parent && parent.nodeName === 'P' ) {
     181                                                wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
     182                                                dom.insertAfter( wrap, parent );
     183                                                editor.selection.select( wrap );
     184                                                editor.nodeChanged();
     185
     186                                                if ( dom.isEmpty( parent ) ) {
     187                                                        dom.remove( parent );
     188                                                }
     189                                        } else {
     190                                                editor.selection.setContent( '<div class="mceTemp">' + html + '</div>' );
     191                                        }
     192                                } else {
     193                                        editor.selection.setContent( html );
     194                                }
     195                        } else {
     196                                // Edit existing image
     197
     198                                // Store the original image id if any
     199                                imgId = imgNode.id || null;
     200                                // Update the image node
     201                                dom.setAttribs( imgNode, data );
     202                                wrap = dom.getParent( imgNode, 'dl.wp-caption' );
     203
     204                                if ( caption ) {
     205                                        if ( wrap ) {
     206                                                if ( parent = dom.select( 'dd.wp-caption-dd', wrap )[0] ) {
     207                                                        parent.innerHTML = caption;
     208                                                }
     209                                        } else {
     210                                                if ( imgNode.className ) {
     211                                                        captionId = imgNode.className.match( /wp-image-([0-9]+)/ );
     212                                                        captionAlign = imgNode.className.match( /align(left|right|center|none)/ );
     213                                                }
     214
     215                                                if ( captionAlign ) {
     216                                                        captionAlign = captionAlign[0];
     217                                                        imgNode.className = imgNode.className.replace( /align(left|right|center|none)/g, '' );
     218                                                } else {
     219                                                        captionAlign = 'alignnone';
     220                                                }
     221
     222                                                captionAlign = ' class="wp-caption ' + captionAlign + '"';
     223
     224                                                if ( captionId ) {
     225                                                        captionId = ' id="attachment_' + captionId[1] + '"';
     226                                                }
     227
     228                                                captionWidth = data.width || imgNode.clientWidth;
     229
     230                                                if ( captionWidth ) {
     231                                                        captionWidth = parseInt( captionWidth, 10 ) + 10;
     232                                                        captionWidth = ' style="width: '+ captionWidth +'px"';
     233                                                }
     234
     235                                                if ( imgNode.parentNode && imgNode.parentNode.nodeName === 'A' ) {
     236                                                        html = dom.getOuterHTML( imgNode.parentNode );
     237                                                        node = imgNode.parentNode;
     238                                                } else {
     239                                                        html = dom.getOuterHTML( imgNode );
     240                                                        node = imgNode;
     241                                                }
     242
     243                                                html = '<dl ' + captionId + captionAlign + captionWidth + '>' +
     244                                                        '<dt class="wp-caption-dt">'+ html +'</dt><dd class="wp-caption-dd">'+ caption +'</dd></dl>';
     245
     246                                                if ( parent = dom.getParent( imgNode, 'p' ) ) {
     247                                                        wrap = dom.create( 'div', { 'class': 'mceTemp' }, html );
     248                                                        dom.insertAfter( wrap, parent );
     249                                                        editor.selection.select( wrap );
     250                                                        editor.nodeChanged();
     251
     252                                                        // Delete the old image node
     253                                                        dom.remove( node );
     254
     255                                                        if ( dom.isEmpty( parent ) ) {
     256                                                                dom.remove( parent );
    79257                                                        }
    80                                                 }, 100);
    81                                         }
    82                                 }
    83                                 mouse = {};
    84                         });
    85 
    86                         // show editimage buttons
    87                         ed.onMouseDown.add(function(ed, e){
    88                                 t._showButtons(e);
    89                         });
    90 
    91                         ed.onBeforeSetContent.add(function(ed, o) {
    92                                 o.content = ed.wpSetImgCaption(o.content);
    93                         });
    94 
    95                         ed.onPostProcess.add(function(ed, o) {
    96                                 if (o.get)
    97                                         o.content = ed.wpGetImgCaption(o.content);
    98                         });
    99 
    100                         ed.wpSetImgCaption = function(content) {
    101                                 return t._do_shcode(content);
    102                         };
    103 
    104                         ed.wpGetImgCaption = function(content) {
    105                                 return t._get_shcode(content);
    106                         };
    107 
     258                                                } else {
     259                                                        editor.selection.setContent( '<div class="mceTemp">' + html + '</div>' );
     260                                                }
     261                                        }
     262                                } else {
     263                                        if ( wrap ) {
     264                                                // Remove the caption wrapper and place the image in new paragraph
     265                                                if ( imgNode.parentNode.nodeName === 'A' ) {
     266                                                        html = dom.getOuterHTML( imgNode.parentNode );
     267                                                } else {
     268                                                        html = dom.getOuterHTML( imgNode );
     269                                                }
     270
     271                                                parent = dom.create( 'p', {}, html );
     272                                                dom.insertAfter( parent, wrap.parentNode );
     273                                                editor.selection.select( parent );
     274                                                editor.nodeChanged();
     275                                                dom.remove( wrap.parentNode );
     276                                        }
     277                                }
     278                        }
     279
     280                        imgNode = dom.get('__wp-temp-img-id');
     281                        dom.setAttrib( imgNode, 'id', imgId );
     282                        e.imgData.node = imgNode;
     283                });
     284
     285                editor.on( 'wpLoadImageData', function( e ) {
     286                        var parent,
     287                                data = e.imgData.data
     288                                imgNode = e.imgData.node;
     289
     290                        if ( parent = dom.getParent( imgNode, 'dl.wp-caption' ) ) {
     291                                parent = dom.select( 'dd.wp-caption-dd', parent )[0];
     292                                data.caption = parent ? parent.innerHTML : '';
     293                        }
     294                });
     295
     296                // Prevent dragging images out of the caption elements
     297                dom.bind( editor.getDoc(), 'dragstart', function( event ) {
     298                        var node = editor.selection.getNode();
     299
     300                        if ( node.nodeName === 'IMG' && dom.getParent( node, '.wp-caption' ) ) {
     301                                event.preventDefault();
     302                        }
     303                });
     304        });
     305
     306        editor.on( 'BeforeExecCommand', function( e ) {
     307                var node, p, DL, align,
     308                        cmd = e.command,
     309                        dom = editor.dom;
     310
     311                if ( cmd === 'mceInsertContent' ) {
    108312                        // When inserting content, if the caret is inside a caption create new paragraph under
    109313                        // and move the caret there
    110                         ed.onBeforeExecCommand.add( function( ed, cmd ) {
    111                                 var node, p;
    112 
    113                                 if ( cmd == 'mceInsertContent' ) {
    114                                         node = ed.dom.getParent(ed.selection.getNode(), 'div.mceTemp');
    115 
    116                                         if ( !node )
    117                                                 return;
    118 
    119                                         p = ed.dom.create('p');
    120                                         ed.dom.insertAfter( p, node );
    121                                         ed.selection.setCursorLocation(p, 0);
    122                                 }
    123                         });
    124                 },
    125 
    126                 _do_shcode : function(content) {
    127                         return content.replace(/(?:<p>)?\[(?:wp_)?caption([^\]]+)\]([\s\S]+?)\[\/(?:wp_)?caption\](?:<\/p>)?/g, function(a,b,c){
    128                                 var id, cls, w, cap, div_cls, img, trim = tinymce.trim;
    129 
    130                                 id = b.match(/id=['"]([^'"]*)['"] ?/);
    131                                 if ( id )
    132                                         b = b.replace(id[0], '');
    133 
    134                                 cls = b.match(/align=['"]([^'"]*)['"] ?/);
    135                                 if ( cls )
    136                                         b = b.replace(cls[0], '');
    137 
    138                                 w = b.match(/width=['"]([0-9]*)['"] ?/);
    139                                 if ( w )
    140                                         b = b.replace(w[0], '');
    141 
    142                                 c = trim(c);
    143                                 img = c.match(/((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)([\s\S]*)/i);
    144 
    145                                 if ( img && img[2] ) {
    146                                         cap = trim( img[2] );
    147                                         img = trim( img[1] );
     314                        if ( node = dom.getParent( editor.selection.getNode(), 'div.mceTemp' ) ) {
     315                                p = dom.create( 'p' );
     316                                dom.insertAfter( p, node );
     317                                editor.selection.setCursorLocation( p, 0 );
     318                                editor.nodeChanged();
     319
     320                                if ( tinymce.Env.ie > 8 ) {
     321                                        setTimeout( function() {
     322                                                editor.selection.setCursorLocation( p, 0 );
     323                                                editor.selection.setContent( e.value );
     324                                        }, 500 );
     325
     326                                        return false;
     327                                }
     328                        }
     329                } else if ( cmd === 'JustifyLeft' || cmd === 'JustifyRight' || cmd === 'JustifyCenter' ) {
     330                        // When inside an image caption, set the align* class on dt.wp-caption
     331                        node = editor.selection.getNode();
     332                        align = cmd.substr(7).toLowerCase();
     333                        align = 'align' + align;
     334
     335                        if ( dom.is( node, 'dl.wp-caption' ) ) {
     336                                DL = node;
     337                        } else {
     338                                DL = dom.getParent( node, 'dl.wp-caption' );
     339                        }
     340
     341                        if ( DL ) {
     342                                if ( dom.hasClass( DL, align ) ) {
     343                                        dom.removeClass( DL, align );
     344                                        dom.addClass( DL, 'alignnone' );
    148345                                } else {
    149                                         // old captions shortcode style
    150                                         cap = trim(b).replace(/caption=['"]/, '').replace(/['"]$/, '');
    151                                         img = c;
    152                                 }
    153 
    154                                 id = ( id && id[1] ) ? id[1] : '';
    155                                 cls = ( cls && cls[1] ) ? cls[1] : 'alignnone';
    156                                 w = ( w && w[1] ) ? w[1] : '';
    157 
    158                                 if ( !w || !cap )
    159                                         return c;
    160 
    161                                 div_cls = 'mceTemp';
    162                                 if ( cls == 'aligncenter' )
    163                                         div_cls += ' mceIEcenter';
    164 
    165                                 w = parseInt( w, 10 ) + 10;
    166                                 return '<div class="'+div_cls+'"><dl id="'+id+'" class="wp-caption '+cls+'" style="width: '+w+
    167                                 'px"><dt class="wp-caption-dt">'+img+'</dt><dd class="wp-caption-dd">'+cap+'</dd></dl></div>';
    168                         });
    169                 },
    170 
    171                 _get_shcode : function(content) {
    172                         return content.replace(/<div (?:id="attachment_|class="mceTemp)[^>]*>([\s\S]+?)<\/div>/g, function(a, b){
    173                                 var ret = b.replace(/<dl ([^>]+)>\s*<dt [^>]+>([\s\S]+?)<\/dt>\s*<dd [^>]+>([\s\S]*?)<\/dd>\s*<\/dl>/gi, function(a,b,c,cap){
    174                                         var id, cls, w;
    175 
    176                                         w = c.match(/width="([0-9]*)"/);
    177                                         w = ( w && w[1] ) ? w[1] : '';
    178 
    179                                         if ( !w || !cap )
    180                                                 return c;
    181 
    182                                         id = b.match(/id="([^"]*)"/);
    183                                         id = ( id && id[1] ) ? id[1] : '';
    184 
    185                                         cls = b.match(/class="([^"]*)"/);
    186                                         cls = ( cls && cls[1] ) ? cls[1] : '';
    187                                         cls = cls.match(/align[a-z]+/) || 'alignnone';
    188 
    189                                         cap = cap.replace(/\r\n|\r/g, '\n').replace(/<[a-zA-Z0-9]+( [^<>]+)?>/g, function(a){
    190                                                 // no line breaks inside HTML tags
    191                                                 return a.replace(/[\r\n\t]+/, ' ');
    192                                         });
    193 
    194                                         // convert remaining line breaks to <br>
    195                                         cap = cap.replace(/\s*\n\s*/g, '<br />');
    196 
    197                                         return '[caption id="'+id+'" align="'+cls+'" width="'+w+'"]'+c+' '+cap+'[/caption]';
    198                                 });
    199 
    200                                 if ( ret.indexOf('[caption') !== 0 ) {
    201                                         // the caption html seems brocken, try to find the image that may be wrapped in a link
    202                                         // and may be followed by <p> with the caption text.
    203                                         ret = b.replace(/[\s\S]*?((?:<a [^>]+>)?<img [^>]+>(?:<\/a>)?)(<p>[\s\S]*<\/p>)?[\s\S]*/gi, '<p>$1</p>$2');
    204                                 }
    205 
    206                                 return ret;
    207                         });
    208                 },
    209 
    210                 _createButtons : function() {
    211                         var t = this, ed = tinymce.activeEditor, DOM = tinymce.DOM, editButton, dellButton, isRetina;
    212 
    213                         if ( DOM.get('wp_editbtns') )
    214                                 return;
    215 
    216                         isRetina = ( window.devicePixelRatio && window.devicePixelRatio > 1 ) || // WebKit, Opera
    217                                 ( window.matchMedia && window.matchMedia('(min-resolution:130dpi)').matches ); // Firefox, IE10, Opera
    218 
    219                         DOM.add(document.body, 'div', {
    220                                 id : 'wp_editbtns',
    221                                 style : 'display:none;'
    222                         });
    223 
    224                         editButton = DOM.add('wp_editbtns', 'img', {
    225                                 src : isRetina ? t.url+'/img/image-2x.png' : t.url+'/img/image.png',
    226                                 id : 'wp_editimgbtn',
    227                                 width : '24',
    228                                 height : '24',
    229                                 title : ed.getLang('wpeditimage.edit_img')
    230                         });
    231 
    232                         tinymce.dom.Event.add(editButton, 'mousedown', function() {
    233                                 t._editImage();
    234                                 ed.plugins.wordpress._hideButtons();
    235                         });
    236 
    237                         dellButton = DOM.add('wp_editbtns', 'img', {
    238                                 src : isRetina ? t.url+'/img/delete-2x.png' : t.url+'/img/delete.png',
    239                                 id : 'wp_delimgbtn',
    240                                 width : '24',
    241                                 height : '24',
    242                                 title : ed.getLang('wpeditimage.del_img')
    243                         });
    244 
    245                         tinymce.dom.Event.add(dellButton, 'mousedown', function() {
    246                                 var ed = tinymce.activeEditor, el = ed.selection.getNode(), parent;
    247 
    248                                 if ( el.nodeName == 'IMG' && ed.dom.getAttrib(el, 'class').indexOf('mceItem') == -1 ) {
    249                                         if ( (parent = ed.dom.getParent(el, 'div')) && ed.dom.hasClass(parent, 'mceTemp') ) {
    250                                                 ed.dom.remove(parent);
    251                                         } else {
    252                                                 if ( el.parentNode.nodeName == 'A' && el.parentNode.childNodes.length == 1 )
    253                                                         el = el.parentNode;
    254 
    255                                                 if ( el.parentNode.nodeName == 'P' && el.parentNode.childNodes.length == 1 )
    256                                                         el = el.parentNode;
    257 
    258                                                 ed.dom.remove(el);
    259                                         }
    260 
    261                                         ed.execCommand('mceRepaint');
    262                                         return false;
    263                                 }
    264                                 ed.plugins.wordpress._hideButtons();
    265                         });
    266                 },
    267                
    268                 _editImage : function() {
    269                         var ed = tinymce.activeEditor, url = this.url, el = ed.selection.getNode(), vp, H, W, cls = el.className;
    270 
    271                         if ( cls.indexOf('mceItem') != -1 || cls.indexOf('wpGallery') != -1 || el.nodeName != 'IMG' )
    272                                 return;
    273 
    274                         vp = tinymce.DOM.getViewPort();
    275                         H = 680 < (vp.h - 70) ? 680 : vp.h - 70;
    276                         W = 650 < vp.w ? 650 : vp.w;
    277 
    278                         ed.windowManager.open({
    279                                 file: url + '/editimage.html',
    280                                 width: W+'px',
    281                                 height: H+'px',
    282                                 inline: true
    283                         });
    284                 },
    285 
    286                 _showButtons : function(e) {
    287                         var ed = this.editor, target = e.target;
    288 
    289                         if ( target.nodeName != 'IMG' ) {
    290                                 if ( target.firstChild && target.firstChild.nodeName == 'IMG' && target.childNodes.length == 1 ) {
    291                                         target = target.firstChild;
    292                                 } else {
    293                                         ed.plugins.wordpress._hideButtons();
    294                                         return;
    295                                 }
    296                         }
    297 
    298                         if ( ed.dom.getAttrib(target, 'class').indexOf('mceItem') == -1 ) {
    299                                 mouse = {
    300                                         x: e.clientX,
    301                                         y: e.clientY,
    302                                         img_w: target.clientWidth,
    303                                         img_h: target.clientHeight
    304                                 };
    305 
    306                                 if ( e.type == 'touchstart' ) {
    307                                         ed.selection.select(target);
    308                                         ed.dom.events.cancel(e);
    309                                 }
    310 
    311                                 ed.plugins.wordpress._hideButtons();
    312                                 ed.plugins.wordpress._showButtons(target, 'wp_editbtns');
    313                         }
    314                 },
    315 
    316                 getInfo : function() {
    317                         return {
    318                                 longname : 'Edit Image',
    319                                 author : 'WordPress',
    320                                 authorurl : 'http://wordpress.org',
    321                                 infourl : '',
    322                                 version : '1.0'
    323                         };
     346                                        DL.className = DL.className.replace( /align[^ ]+/g, '' );
     347                                        dom.addClass( DL, align );
     348                                }
     349
     350                                return false;
     351                        }
    324352                }
    325353        });
    326354
    327         tinymce.PluginManager.add('wpeditimage', tinymce.plugins.wpEditImage);
    328 })();
     355        editor.on( 'keydown', function( e ) {
     356                var node, wrap, P, spacer,
     357                        selection = editor.selection,
     358                        dom = editor.dom;
     359
     360                if ( e.keyCode === tinymce.util.VK.ENTER ) {
     361                        // When pressing Enter inside a caption move the caret to a new parapraph under it
     362                        wrap = dom.getParent( editor.selection.getNode(), 'div.mceTemp' );
     363
     364                        if ( wrap ) {
     365                                dom.events.cancel(e); // Doesn't cancel all :(
     366
     367                                // Remove any extra dt and dd cleated on pressing Enter...
     368                                tinymce.each( dom.select( 'dt, dd', wrap ), function( element ) {
     369                                        if ( dom.isEmpty( element ) ) {
     370                                                dom.remove( element );
     371                                        }
     372                                });
     373
     374                                spacer = tinymce.Env.ie ? '' : '<br data-mce-bogus="1" />';
     375                                P = dom.create( 'p', null, spacer );
     376                                dom.insertAfter( P, wrap );
     377                                selection.setCursorLocation( P, 0 );
     378                                editor.nodeChanged();
     379                        }
     380                } else if ( e.keyCode === tinymce.util.VK.DELETE || e.keyCode === tinymce.util.VK.BACKSPACE ) {
     381                        node = selection.getNode();
     382
     383                        if ( node.nodeName === 'DIV' && dom.hasClass( node, 'mceTemp' ) ) {
     384                                wrap = node;
     385                        } else if ( node.nodeName === 'IMG' || node.nodeName === 'DT' || node.nodeName === 'A' ) {
     386                                wrap = dom.getParent( node, 'div.mceTemp' );
     387                        }
     388
     389                        if ( wrap ) {
     390                                dom.events.cancel(e);
     391
     392                                if ( wrap.nextSibling ) {
     393                                        selection.select( wrap.nextSibling );
     394                                } else if ( wrap.previousSibling ) {
     395                                        selection.select( wrap.previousSibling );
     396                                } else {
     397                                        selection.select( wrap.parentNode );
     398                                }
     399
     400                                selection.collapse( true );
     401                                editor.nodeChanged();
     402                                dom.remove( wrap );
     403                                wrap = null;
     404                                return false;
     405                        }
     406                }
     407        });
     408
     409        editor.wpSetImgCaption = function( content ) {
     410                return parseShortcode( content );
     411        };
     412
     413        editor.wpGetImgCaption = function( content ) {
     414                return getShortcode( content );
     415        };
     416
     417        editor.on( 'BeforeSetContent', function( e ) {
     418                e.content = editor.wpSetImgCaption( e.content );
     419        });
     420
     421        editor.on( 'PostProcess', function( e ) {
     422                if ( e.get ) {
     423                        e.content = editor.wpGetImgCaption( e.content );
     424                }
     425        });
     426
     427        return {
     428                _do_shcode: parseShortcode,
     429                _get_shcode: getShortcode
     430        };
     431});
  • trunk/src/wp-includes/js/tinymce/plugins/wpfullscreen/plugin.js

    r26862 r26876  
    1 /* global tinymce:false, switchEditors, fullscreen */
     1/* global tinymce */
    22/**
    3  * WP Fullscreen TinyMCE plugin
    4  *
    5  * Contains code from Moxiecode Systems AB released under LGPL http://tinymce.moxiecode.com/license
     3 * WP Fullscreen (Distraction Free Writing) TinyMCE plugin
    64 */
     5tinymce.PluginManager.add( 'wpfullscreen', function( editor ) {
     6        var settings = editor.settings,
     7                oldSize = 0;
    78
    8 (function() {
    9         tinymce.create('tinymce.plugins.wpFullscreenPlugin', {
    10                 resize_timeout: false,
     9        function resize( e ) {
     10                var deltaSize, myHeight,
     11                        d = editor.getDoc(),
     12                        body = d.body,
     13                        de = d.documentElement,
     14                        DOM = tinymce.DOM
     15                        resizeHeight = 250;
    1116
    12                 init : function( ed ) {
    13                         var t = this, s = {}, DOM = tinymce.DOM;
     17                if ( ( e && e.type == 'setcontent' && e.initial ) || editor.settings.inline ) {
     18                        return;
     19                }
    1420
    15                         // Register commands
    16                         ed.addCommand('wpFullScreenClose', function() {
    17                                 // this removes the editor, content has to be saved first with tinymce.execCommand('wpFullScreenSave');
    18                                 if ( ed.getParam('wp_fullscreen_is_enabled') ) {
    19                                         DOM.win.setTimeout(function() {
    20                                                 tinymce.remove(ed);
    21                                                 DOM.remove('wp_mce_fullscreen_parent');
    22                                                 tinymce.settings = tinymce.oldSettings; // Restore old settings
    23                                         }, 10);
    24                                 }
    25                         });
     21                // Get height differently depending on the browser used
     22                myHeight = tinymce.Env.ie ? body.scrollHeight : ( tinymce.Env.webkit && body.clientHeight === 0 ? 0 : body.offsetHeight );
    2623
    27                         ed.addCommand('wpFullScreenSave', function() {
    28                                 var ed = tinymce.get('wp_mce_fullscreen'), edd;
     24                // Don't make it smaller than 250px
     25                if ( myHeight > 250 ) {
     26                        resizeHeight = myHeight;
     27                }
    2928
    30                                 ed.focus();
    31                                 edd = tinymce.get( ed.getParam('wp_fullscreen_editor_id') );
     29                body.scrollTop = 0;
    3230
    33                                 edd.setContent( ed.getContent({format : 'raw'}), {format : 'raw'} );
    34                         });
     31                // Resize content element
     32                if ( resizeHeight !== oldSize ) {
     33                        deltaSize = resizeHeight - oldSize;
     34                        DOM.setStyle( DOM.get( editor.id + '_ifr' ), 'height', resizeHeight + 'px' );
     35                        oldSize = resizeHeight;
    3536
    36                         ed.addCommand('wpFullScreenInit', function() {
    37                                 var d, b, fsed;
     37                        // WebKit doesn't decrease the size of the body element until the iframe gets resized
     38                        // So we need to continue to resize the iframe down until the size gets fixed
     39                        if ( tinymce.isWebKit && deltaSize < 0 ) {
     40                                resize( e );
     41                        }
     42                }
     43        }
    3844
    39                                 ed = tinymce.activeEditor;
    40                                 d = ed.getDoc();
    41                                 b = d.body;
     45        // Register the command
     46        editor.addCommand( 'wpAutoResize', resize );
    4247
    43                                 tinymce.oldSettings = tinymce.settings; // Store old settings
     48        function fullscreenOn() {
     49                settings.wp_fullscreen = true;
     50                editor.dom.addClass( editor.getDoc().documentElement, 'wp-fullscreen' );
     51                // Add listeners for auto-resizing
     52                editor.on( 'change setcontent paste keyup', resize );
     53        }
    4454
    45                                 tinymce.each(ed.settings, function(v, n) {
    46                                         s[n] = v;
    47                                 });
     55        function fullscreenOff() {
     56                settings.wp_fullscreen = false;
     57                editor.dom.removeClass( editor.getDoc().documentElement, 'wp-fullscreen' );
     58                // Remove listeners for auto-resizing
     59                editor.off( 'change setcontent paste keyup', resize );
     60                oldSize = 0;
     61        }
    4862
    49                                 s.id = 'wp_mce_fullscreen';
    50                                 s.wp_fullscreen_is_enabled = true;
    51                                 s.wp_fullscreen_editor_id = ed.id;
    52                                 s.theme_advanced_resizing = false;
    53                                 s.theme_advanced_statusbar_location = 'none';
    54                                 s.content_css = s.content_css ? s.content_css + ',' + s.wp_fullscreen_content_css : s.wp_fullscreen_content_css;
    55                                 s.height = tinymce.isIE ? b.scrollHeight : b.offsetHeight;
     63        // For use from outside the editor.
     64        editor.addCommand( 'wpFullScreenOn', fullscreenOn );
     65        editor.addCommand( 'wpFullScreenOff', fullscreenOff );
    5666
    57                                 tinymce.each(ed.getParam('wp_fullscreen_settings'), function(v, k) {
    58                                         s[k] = v;
    59                                 });
     67        function toggleFullscreen() {
     68                // Toggle DFW mode. For use from inside the editor.
     69                if ( typeof wp === 'undefined' || ! wp.editor || ! wp.editor.fullscreen ) {
     70                        return;
     71                }
    6072
    61                                 fsed = new tinymce.Editor('wp_mce_fullscreen', s);
    62                                 fsed.onInit.add(function(edd) {
    63                                         var DOM = tinymce.DOM, buttons = DOM.select('a.mceButton', DOM.get('wp-fullscreen-buttons'));
     73                if ( editor.getParam('wp_fullscreen') ) {
     74                        wp.editor.fullscreen.off();
     75                } else {
     76                        wp.editor.fullscreen.on();
     77                }
     78        }
    6479
    65                                         if ( !ed.isHidden() )
    66                                                 edd.setContent( ed.getContent() );
    67                                         else
    68                                                 edd.setContent( switchEditors.wpautop( edd.getElement().value ) );
     80        editor.addCommand( 'wpFullScreen', toggleFullscreen );
    6981
    70                                         setTimeout(function(){ // add last
    71                                                 edd.onNodeChange.add( function() {
    72                                                         tinymce.each(buttons, function(c) {
    73                                                                 var btn, cls;
     82        editor.on( 'init', function() {
     83                // Set the editor when initializing from whitin DFW
     84                if ( editor.getParam('wp_fullscreen') ) {
     85                        fullscreenOn();
     86                }
    7487
    75                                                                 if ( btn = DOM.get( 'wp_mce_fullscreen_' + c.id.substr(6) ) ) {
    76                                                                         cls = btn.className;
    77 
    78                                                                         if ( cls )
    79                                                                                 c.className = cls;
    80                                                                 }
    81                                                         });
    82                                                 });
    83                                         }, 1000);
    84 
    85                                         edd.dom.addClass(edd.getBody(), 'wp-fullscreen-editor');
    86                                         edd.focus();
    87                                 });
    88 
    89                                 fsed.render();
    90 
    91                                 if ( 'undefined' != fullscreen ) {
    92                                         fsed.dom.bind( fsed.dom.doc, 'mousemove', function(e){
    93                                                 fullscreen.bounder( 'showToolbar', 'hideToolbar', 2000, e );
    94                                         });
    95                                 }
    96                         });
    97 
    98                         ed.addCommand('wpFullScreen', function() {
    99                                 if ( typeof(fullscreen) == 'undefined' )
    100                                         return;
    101 
    102                                 if ( 'wp_mce_fullscreen' == ed.id )
    103                                         fullscreen.off();
    104                                 else
    105                                         fullscreen.on();
    106                         });
    107 
    108                         // Register buttons
    109                         ed.addButton('wp_fullscreen', {
    110                                 title : 'wordpress.wp_fullscreen_desc',
    111                                 cmd : 'wpFullScreen'
    112                         });
    113 
    114                         // END fullscreen
    115 //----------------------------------------------------------------
    116                         // START autoresize
    117 
    118                         if ( ed.getParam('fullscreen_is_enabled') || !ed.getParam('wp_fullscreen_is_enabled') )
    119                                 return;
    120 
    121                         /**
    122                          * This method gets executed each time the editor needs to resize.
    123                          */
    124                         function resize(editor, e) {
    125                                 var DOM = tinymce.DOM, body = ed.getBody(), ifr = DOM.get(ed.id + '_ifr'), height, y = ed.dom.win.scrollY;
    126 
    127                                 if ( t.resize_timeout )
    128                                         return;
    129 
    130                                 // sometimes several events are fired few ms apart, trottle down resizing a little
    131                                 t.resize_timeout = true;
    132                                 setTimeout(function(){
    133                                         t.resize_timeout = false;
    134                                 }, 500);
    135 
    136                                 height = body.scrollHeight > 300 ? body.scrollHeight : 300;
    137 
    138                                 if ( height != ifr.scrollHeight ) {
    139                                         DOM.setStyle(ifr, 'height', height + 'px');
    140                                         ed.getWin().scrollTo(0, 0); // iframe window object, make sure there's no scrolling
    141                                 }
    142 
    143                                 // WebKit scrolls to top on paste...
    144                                 if ( e && e.type == 'paste' && tinymce.isWebKit ) {
    145                                         setTimeout(function(){
    146                                                 ed.dom.win.scrollTo(0, y);
    147                                         }, 40);
    148                                 }
    149                         }
    150 
    151                         // Add appropriate listeners for resizing content area
    152                         ed.onInit.add( function( ed ) {
    153                                 ed.onChange.add(resize);
    154                                 ed.onSetContent.add(resize);
    155                                 ed.onPaste.add(resize);
    156                                 ed.onKeyUp.add(resize);
    157                                 ed.onPostRender.add(resize);
    158 
    159                                 ed.getBody().style.overflowY = 'hidden';
    160                         });
    161 
    162                         if ( ed.getParam('autoresize_on_init', true) ) {
    163                                 ed.onLoadContent.add( function() {
    164                                         // Because the content area resizes when its content CSS loads,
    165                                         // and we can't easily add a listener to its onload event,
    166                                         // we'll just trigger a resize after a short loading period
    167                                         setTimeout(function() {
    168                                                 resize();
    169                                         }, 1200);
    170                                 });
    171                         }
    172 
    173                         // Register the command so that it can be invoked by using tinymce.activeEditor.execCommand('mceExample');
    174                         ed.addCommand('wpAutoResize', resize);
    175                 },
    176 
    177                 getInfo : function() {
    178                         return {
    179                                 longname : 'WP Fullscreen',
    180                                 author : 'WordPress',
    181                                 authorurl : 'http://wordpress.org',
    182                                 infourl : '',
    183                                 version : '1.0'
    184                         };
    185                 }
     88                editor.addShortcut( 'alt+shift+w', '', 'wpFullScreen' );
    18689        });
    18790
    188         // Register plugin
    189         tinymce.PluginManager.add('wpfullscreen', tinymce.plugins.wpFullscreenPlugin);
    190 })();
     91        // Register buttons
     92        editor.addButton( 'wp_fullscreen', {
     93                tooltip: 'Distraction Free Writing',
     94                shortcut: 'Alt+Shift+W',
     95                onclick: toggleFullscreen
     96        });
     97
     98        editor.addMenuItem( 'wp_fullscreen', {
     99                text: 'Distraction Free Writing',
     100                shortcut: 'Alt+Shift+W',
     101                context: 'view',
     102                onclick: toggleFullscreen
     103        });
     104});
  • trunk/src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js

    r26862 r26876  
    11/* global tinymce */
    2 (function() {
    3         tinymce.create('tinymce.plugins.wpGallery', {
     2tinymce.PluginManager.add('wpgallery', function( editor, url ) {
    43
    5                 init : function(ed, url) {
    6                         var t = this;
     4        function parseGallery( content ) {
     5                return content.replace( /\[gallery([^\]]*)\]/g, function( match, attr ) {
     6                        var data = tinymce.DOM.encode( attr );
    77
    8                         t.url = url;
    9                         t.editor = ed;
    10                         t._createButtons();
     8                        return '<img src="' + tinymce.Env.transparentSrc + '" class="wp-gallery mceItem" ' +
     9                                'title="gallery' + data + '" data-mce-resize="false" data-mce-placeholder="1" />';
     10                });
     11        }
    1112
    12                         // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('...');
    13                         ed.addCommand('WP_Gallery', function() {
    14                                 if ( tinymce.isIE )
    15                                         ed.selection.moveToBookmark( ed.wpGalleryBookmark );
     13        function getGallery( content ) {
     14                function getAttr( str, name ) {
     15                        name = new RegExp( name + '=\"([^\"]+)\"', 'g' ).exec( str );
     16                        return name ? tinymce.DOM.decode( name[1] ) : '';
     17                }
    1618
    17                                 var el = ed.selection.getNode(),
    18                                         gallery = wp.media.gallery,
    19                                         frame;
     19                return content.replace( /(?:<p[^>]*>)*(<img[^>]+>)(?:<\/p>)*/g, function( match, image ) {
     20                        var cls = getAttr( image, 'class' );
    2021
    21                                 // Check if the `wp.media.gallery` API exists.
    22                                 if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.gallery )
    23                                         return;
     22                        if ( cls.indexOf('wp-gallery') !== -1 ) {
     23                                return '<p>['+ tinymce.trim( getAttr( image, 'title' ) ) +']</p>';
     24                        }
    2425
    25                                 // Make sure we've selected a gallery node.
    26                                 if ( el.nodeName != 'IMG' || ed.dom.getAttrib(el, 'class').indexOf('wp-gallery') == -1 )
    27                                         return;
     26                        return match;
     27                });
     28        }
    2829
    29                                 frame = gallery.edit( '[' + ed.dom.getAttrib( el, 'title' ) + ']' );
     30        // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('...');
     31        editor.addCommand( 'WP_Gallery', function() {
     32                var gallery, frame, node;
    3033
    31                                 frame.state('gallery-edit').on( 'update', function( selection ) {
    32                                         var shortcode = gallery.shortcode( selection ).string().slice( 1, -1 );
    33                                         ed.dom.setAttrib( el, 'title', shortcode );
    34                                 });
     34                // Check if the `wp.media.gallery` API exists.
     35                if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.gallery ) {
     36                        return;
     37                }
     38
     39                node = editor.selection.getNode();
     40                gallery = wp.media.gallery;
     41
     42                // Make sure we've selected a gallery node.
     43                if ( node.nodeName === 'IMG' && editor.dom.hasClass( node, 'wp-gallery' ) ) {
     44                        frame = gallery.edit( '[' + editor.dom.getAttrib( node, 'title' ) + ']' );
     45
     46                        frame.state('gallery-edit').on( 'update', function( selection ) {
     47                                var shortcode = gallery.shortcode( selection ).string().slice( 1, -1 );
     48                                editor.dom.setAttrib( node, 'title', shortcode );
    3549                        });
     50                }
     51        });
     52/*
     53        editor.on( 'init', function( e ) {
     54        //      _createButtons()
    3655
    37                         ed.onInit.add(function(ed) {
    38                                 // iOS6 doesn't show the buttons properly on click, show them on 'touchstart'
    39                                 if ( 'ontouchstart' in window ) {
    40                                         ed.dom.events.add(ed.getBody(), 'touchstart', function(e){
    41                                                 var target = e.target;
     56                // iOS6 doesn't show the buttons properly on click, show them on 'touchstart'
     57                if ( 'ontouchstart' in window ) {
     58                        editor.dom.events.bind( editor.getBody(), 'touchstart', function( e ) {
     59                                var target = e.target;
    4260
    43                                                 if ( target.nodeName == 'IMG' && ed.dom.hasClass(target, 'wp-gallery') ) {
    44                                                         ed.selection.select(target);
    45                                                         ed.dom.events.cancel(e);
    46                                                         ed.plugins.wordpress._hideButtons();
    47                                                         ed.plugins.wordpress._showButtons(target, 'wp_gallerybtns');
    48                                                 }
    49                                         });
     61                                if ( target.nodeName == 'IMG' && editor.dom.hasClass( target, 'wp-gallery' ) ) {
     62                                        editor.selection.select( target );
     63                                        editor.dom.events.cancel( e );
     64                                        editor.plugins.wordpress._hideButtons();
     65                                        editor.plugins.wordpress._showButtons( target, 'wp_gallerybtns' );
    5066                                }
    5167                        });
    52 
    53                         ed.onMouseDown.add(function(ed, e) {
    54                                 if ( e.target.nodeName == 'IMG' && ed.dom.hasClass(e.target, 'wp-gallery') ) {
    55                                         ed.plugins.wordpress._hideButtons();
    56                                         ed.plugins.wordpress._showButtons(e.target, 'wp_gallerybtns');
     68                }
     69        });
     70*/
     71        editor.on( 'mouseup', function( e ) {
     72                if ( e.target.nodeName == 'IMG' && editor.dom.hasClass( e.target, 'wp-gallery' ) ) {
     73                        // Don't trigger on right-click
     74                        if ( e.button !== 2 ) {
     75                                if ( editor.dom.hasClass( e.target, 'wp-gallery-selected' ) ) {
     76                                        editor.execCommand('WP_Gallery');
     77                                        editor.dom.removeClass( e.target, 'wp-gallery-selected' );
     78                                } else {
     79                                        editor.dom.addClass( e.target, 'wp-gallery-selected' );
    5780                                }
    58                         });
    59 
    60                         ed.onBeforeSetContent.add(function(ed, o) {
    61                                 o.content = t._do_gallery(o.content);
    62                         });
    63 
    64                         ed.onPostProcess.add(function(ed, o) {
    65                                 if (o.get)
    66                                         o.content = t._get_gallery(o.content);
    67                         });
    68                 },
    69 
    70                 _do_gallery : function(co) {
    71                         return co.replace(/\[gallery([^\]]*)\]/g, function(a,b){
    72                                 return '<img src="'+tinymce.baseURL+'/plugins/wpgallery/img/t.gif" class="wp-gallery mceItem" title="gallery'+tinymce.DOM.encode(b)+'" />';
    73                         });
    74                 },
    75 
    76                 _get_gallery : function(co) {
    77 
    78                         function getAttr(s, n) {
    79                                 n = new RegExp(n + '=\"([^\"]+)\"', 'g').exec(s);
    80                                 return n ? tinymce.DOM.decode(n[1]) : '';
    8181                        }
    82 
    83                         return co.replace(/(?:<p[^>]*>)*(<img[^>]+>)(?:<\/p>)*/g, function(a,im) {
    84                                 var cls = getAttr(im, 'class');
    85 
    86                                 if ( cls.indexOf('wp-gallery') != -1 )
    87                                         return '<p>['+tinymce.trim(getAttr(im, 'title'))+']</p>';
    88 
    89                                 return a;
    90                         });
    91                 },
    92 
    93                 _createButtons : function() {
    94                         var t = this, ed = tinymce.activeEditor, DOM = tinymce.DOM, editButton, dellButton, isRetina;
    95 
    96                         if ( DOM.get('wp_gallerybtns') )
    97                                 return;
    98 
    99                         isRetina = ( window.devicePixelRatio && window.devicePixelRatio > 1 ) || // WebKit, Opera
    100                                 ( window.matchMedia && window.matchMedia('(min-resolution:130dpi)').matches ); // Firefox, IE10, Opera
    101 
    102                         DOM.add(document.body, 'div', {
    103                                 id : 'wp_gallerybtns',
    104                                 style : 'display:none;'
    105                         });
    106 
    107                         editButton = DOM.add('wp_gallerybtns', 'img', {
    108                                 src : isRetina ? t.url+'/img/edit-2x.png' : t.url+'/img/edit.png',
    109                                 id : 'wp_editgallery',
    110                                 width : '24',
    111                                 height : '24',
    112                                 title : ed.getLang('wordpress.editgallery')
    113                         });
    114 
    115                         tinymce.dom.Event.add( editButton, 'mousedown', function() {
    116                                 var ed = tinymce.activeEditor;
    117                                 ed.wpGalleryBookmark = ed.selection.getBookmark('simple');
    118                                 ed.execCommand('WP_Gallery');
    119                                 ed.plugins.wordpress._hideButtons();
    120                         });
    121 
    122                         dellButton = DOM.add('wp_gallerybtns', 'img', {
    123                                 src : isRetina ? t.url+'/img/delete-2x.png' : t.url+'/img/delete.png',
    124                                 id : 'wp_delgallery',
    125                                 width : '24',
    126                                 height : '24',
    127                                 title : ed.getLang('wordpress.delgallery')
    128                         });
    129 
    130                         tinymce.dom.Event.add(dellButton, 'mousedown', function(e) {
    131                                 var ed = tinymce.activeEditor, el = ed.selection.getNode();
    132 
    133                                 if ( el.nodeName == 'IMG' && ed.dom.hasClass(el, 'wp-gallery') ) {
    134                                         ed.dom.remove(el);
    135 
    136                                         ed.execCommand('mceRepaint');
    137                                         ed.dom.events.cancel(e);
    138                                 }
    139 
    140                                 ed.plugins.wordpress._hideButtons();
    141                         });
    142                 },
    143 
    144                 getInfo : function() {
    145                         return {
    146                                 longname : 'Gallery Settings',
    147                                 author : 'WordPress',
    148                                 authorurl : 'http://wordpress.org',
    149                                 infourl : '',
    150                                 version : '1.0'
    151                         };
     82                } else {
     83                        editor.dom.removeClass( editor.dom.select( 'img.wp-gallery-selected' ), 'wp-gallery-selected' );
    15284                }
    15385        });
    15486
    155         tinymce.PluginManager.add('wpgallery', tinymce.plugins.wpGallery);
    156 })();
     87        // Display 'gallery' instead of img in element path
     88        editor.on( 'ResolveName', function( e ) {
     89                var dom = editor.dom,
     90                        target = e.target;
     91
     92                if ( target.nodeName === 'IMG' && dom.hasClass( target, 'wp-gallery' ) ) {
     93                        e.name = 'gallery';
     94                }
     95        });
     96
     97        editor.on( 'BeforeSetContent', function( e ) {
     98                e.content = parseGallery( e.content );
     99        });
     100
     101        editor.on( 'PostProcess', function( e ) {
     102                if ( e.get ) {
     103                        e.content = getGallery( e.content );
     104                }
     105        });
     106
     107        return {
     108                _do_gallery: parseGallery,
     109                _get_gallery: getGallery
     110        };
     111});
  • trunk/src/wp-includes/js/tinymce/plugins/wplink/plugin.js

    r26862 r26876  
    11/* global tinymce */
    2 
    3 (function() {
    4         tinymce.create('tinymce.plugins.wpLink', {
    5                 /**
    6                  * Initializes the plugin, this will be executed after the plugin has been created.
    7                  * This call is done before the editor instance has finished its initialization so use the onInit event
    8                  * of the editor instance to intercept that event.
    9                  *
    10                  * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in.
    11                  * @param {string} url Absolute URL to where the plugin is located.
    12                  */
    13                 init : function(ed, url) {
    14                         var disabled = true;
    15 
    16                         // Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');
    17                         ed.addCommand('WP_Link', function() {
    18                                 if ( disabled )
    19                                         return;
    20                                 ed.windowManager.open({
    21                                         id : 'wp-link',
    22                                         width : 480,
    23                                         height : 'auto',
    24                                         wpDialog : true,
    25                                         title : ed.getLang('advlink.link_desc')
    26                                 }, {
    27                                         plugin_url : url // Plugin absolute URL
    28                                 });
    29                         });
    30 
    31                         // Register example button
    32                         ed.addButton('link', {
    33                                 title : 'advanced.link_desc',
    34                                 cmd : 'WP_Link'
    35                         });
    36 
    37                         ed.onNodeChange.add(function(ed, cm, n, co) {
    38                                 disabled = co && n.nodeName != 'A';
    39                         });
    40                 },
    41                 /**
    42                  * Returns information about the plugin as a name/value array.
    43                  * The current keys are longname, author, authorurl, infourl and version.
    44                  *
    45                  * @return {Object} Name/value array containing information about the plugin.
    46                  */
    47                 getInfo : function() {
    48                         return {
    49                                 longname : 'WordPress Link Dialog',
    50                                 author : 'WordPress',
    51                                 authorurl : 'http://wordpress.org',
    52                                 infourl : '',
    53                                 version : '1.0'
    54                         };
     2tinymce.PluginManager.add( 'wplink', function( editor ) {
     3        // Register a command so that it can be invoked by using tinyMCE.activeEditor.execCommand( 'WP_Link' );
     4        editor.addCommand( 'WP_Link', function() {
     5                if ( typeof window.wpLink !== 'undefined' ) {
     6                        window.wpLink.open( editor.id );
    557                }
    568        });
    579
    58         // Register plugin
    59         tinymce.PluginManager.add('wplink', tinymce.plugins.wpLink);
    60 })();
     10        editor.addButton( 'link', {
     11                icon: 'link',
     12                tooltip: 'Insert/edit link',
     13                shortcut: 'Alt+Shift+A',
     14                cmd: 'WP_Link',
     15                stateSelector: 'a[href]'
     16        });
    6117
     18        editor.addMenuItem( 'link', {
     19                icon: 'link',
     20                text: 'Insert link',
     21                shortcut: 'Alt+Shift+A',
     22                cmd: 'WP_Link',
     23                stateSelector: 'a[href]',
     24                context: 'insert',
     25                prependToContext: true
     26        });
     27});
  • trunk/src/wp-includes/js/tinymce/plugins/wpview/plugin.js

    r26862 r26876  
    1717                                return;
    1818
    19                         editor.onPreInit.add( function( editor ) {
     19                        editor.on( 'PreInit', function() {
    2020                                // Add elements so we can set `contenteditable` to false.
    2121                                editor.schema.addValidElements('div[*],span[*]');
    … …  
    2626                        // view wrappers. Since the editor's DOM is outdated at this point,
    2727                        // we'll wait to render the views.
    28                         editor.onBeforeSetContent.add( function( editor, o ) {
    29                                 if ( ! o.content )
     28                        editor.on( 'BeforeSetContent', function( e ) {
     29                                if ( ! e.content )
    3030                                        return;
    3131
    32                                 o.content = wp.mce.view.toViews( o.content );
     32                                e.content = wp.mce.view.toViews( e.content );
    3333                        });
    3434
    3535                        // When the editor's content has been updated and the DOM has been
    3636                        // processed, render the views in the document.
    37                         editor.onSetContent.add( function( editor ) {
     37                        editor.on( 'SetContent', function() {
    3838                                wp.mce.view.render( editor.getDoc() );
    3939                        });
    4040
    41                         editor.onInit.add( function( editor ) {
    42 
     41                        editor.on( 'init', function() {
     42                                var selection = editor.selection;
    4343                                // When a view is selected, ensure content that is being pasted
    4444                                // or inserted is added to a text node (instead of the view).
    45                                 editor.selection.onBeforeSetContent.add( function( selection ) {
    46                                         var view = wpView.getParentView( selection.getNode() ),
    47                                                 walker, target;
     45                                editor.on( 'BeforeSetContent', function( e ) {
     46                                        var walker, target,
     47                                                view = wpView.getParentView( selection.getNode() );
    4848
    4949                                        // If the selection is not within a view, bail.
    … …  
    7272                                //
    7373                                // Runs on paste and on inserting nodes/html.
    74                                 editor.selection.onSetContent.add( function( selection, o ) {
    75                                         if ( ! o.context )
     74                                editor.on( 'SetContent', function( e ) {
     75                                        if ( ! e.context )
    7676                                                return;
    7777
    … …  
    8888                        // When the editor's contents are being accessed as a string,
    8989                        // transform any views back to their text representations.
    90                         editor.onPostProcess.add( function( editor, o ) {
    91                                 if ( ( ! o.get && ! o.save ) || ! o.content )
     90                        editor.on( 'PostProcess', function( e ) {
     91                                if ( ( ! e.get && ! e.save ) || ! e.content )
    9292                                        return;
    9393
    94                                 o.content = wp.mce.view.toText( o.content );
     94                                e.content = wp.mce.view.toText( e.content );
    9595                        });
    9696
    9797                        // Triggers when the selection is changed.
    9898                        // Add the event handler to the top of the stack.
    99                         editor.onNodeChange.addToTop( function( editor, controlManager, node ) {
     99                        editor.on( 'NodeChange', function( e ) {
    100100                                var view = wpView.getParentView( node );
    101101
    … …  
    113113                        });
    114114
    115                         editor.onKeyDown.addToTop( function( editor, event ) {
     115                        editor.on( 'keydown', function( event ) {
    116116                                var keyCode = event.keyCode,
    117117                                        view, instance;
    … …  
    123123                                // If the caret is not within the selected view, deselect the
    124124                                // view and bail.
    125                                 view = wpView.getParentView( editor.selection.getNode() );
     125                                view = wpView.getParentView( selection.getNode() );
    126126                                if ( view !== selected ) {
    127127                                        wpView.deselect();
    … …  
    172172                                wp.mce.view.deselect( selected );
    173173                        selected = null;
    174                 },
    175 
    176                 getInfo : function() {
    177                         return {
    178                                 longname  : 'WordPress Views',
    179                                 author    : 'WordPress',
    180                                 authorurl : 'http://wordpress.org',
    181                                 infourl   : 'http://wordpress.org',
    182                                 version   : '1.0'
    183                         };
    184174                }
    185175        });
  • trunk/src/wp-includes/js/tinymce/tiny_mce_popup.js

    r25761 r26876  
    1 
    2 // Uncomment and change this document.domain value if you are loading the script cross subdomains
    3 // document.domain = 'moxiecode.com';
    4 
    5 var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document,{ownEvents:true,proxy:tinyMCEPopup._eventProxy});b.dom.bind(window,"ready",b._onDOMLoaded,b);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var a=this;setTimeout(function(){var b=a.dom.getViewPort(window);a.editor.windowManager.resizeBy(a.getWindowArg("mce_width")-b.w,a.getWindowArg("mce_height")-b.h,a.id||window)},10)},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false&&b.editor.settings.language_load!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(a){var a=(a&&a.target)||window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}if(!b.editor.getParam("browser_preferred_colors",false)||!b.isWindow){b.dom.addClass(document.body,"forceColors")}document.body.style.display="";if(tinymce.isIE&&!tinymce.isIE11){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}else{if(tinymce.isIE11){document.addEventListener("mouseup",tinyMCEPopup._restoreSelection,false)}}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){b.dom.bind(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){var b=a.target||a.srcElement;if(b.onchange){b.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_eventProxy:function(a){return function(b){tinyMCEPopup.dom.events.callNativeHandler(a,b)}}};tinyMCEPopup.init();
     1/**
     2 * Popup.js
     3 *
     4 * Copyright, Moxiecode Systems AB
     5 * Released under LGPL License.
     6 *
     7 * License: http://www.tinymce.com/license
     8 * Contributing: http://www.tinymce.com/contributing
     9 */
     10
     11var tinymce, tinyMCE;
     12
     13/**
     14 * TinyMCE popup/dialog helper class. This gives you easy access to the
     15 * parent editor instance and a bunch of other things. It's higly recommended
     16 * that you load this script into your dialogs.
     17 *
     18 * @static
     19 * @class tinyMCEPopup
     20 */
     21var tinyMCEPopup = {
     22        /**
     23         * Initializes the popup this will be called automatically.
     24         *
     25         * @method init
     26         */
     27        init: function() {
     28                var self = this, parentWin, settings, uiWindow;
     29
     30                // Find window & API
     31                parentWin = self.getWin();
     32                tinymce = tinyMCE = parentWin.tinymce;
     33                self.editor = tinymce.EditorManager.activeEditor;
     34                self.params = self.editor.windowManager.getParams();
     35
     36                uiWindow = self.editor.windowManager.windows[self.editor.windowManager.windows.length - 1];
     37                self.features = uiWindow.features;
     38                self.uiWindow = uiWindow;
     39
     40                settings = self.editor.settings;
     41
     42                // Setup popup CSS path(s)
     43                if (settings.popup_css !== false) {
     44                        if (settings.popup_css) {
     45                                settings.popup_css = self.editor.documentBaseURI.toAbsolute(settings.popup_css);
     46                        } else {
     47                                settings.popup_css = self.editor.baseURI.toAbsolute("plugins/compat3x/css/dialog.css");
     48                        }
     49                }
     50
     51                if (settings.popup_css_add) {
     52                        settings.popup_css += ',' + self.editor.documentBaseURI.toAbsolute(settings.popup_css_add);
     53                }
     54
     55                // Setup local DOM
     56                self.dom = self.editor.windowManager.createInstance('tinymce.dom.DOMUtils', document, {
     57                        ownEvents: true,
     58                        proxy: tinyMCEPopup._eventProxy
     59                });
     60
     61                self.dom.bind(window, 'ready', self._onDOMLoaded, self);
     62
     63                // Enables you to skip loading the default css
     64                if (self.features.popup_css !== false) {
     65                        self.dom.loadCSS(self.features.popup_css || self.editor.settings.popup_css);
     66                }
     67
     68                // Setup on init listeners
     69                self.listeners = [];
     70
     71                /**
     72                 * Fires when the popup is initialized.
     73                 *
     74                 * @event onInit
     75                 * @param {tinymce.Editor} editor Editor instance.
     76                 * @example
     77                 * // Alerts the selected contents when the dialog is loaded
     78                 * tinyMCEPopup.onInit.add(function(ed) {
     79                 *     alert(ed.selection.getContent());
     80                 * });
     81                 *
     82                 * // Executes the init method on page load in some object using the SomeObject scope
     83                 * tinyMCEPopup.onInit.add(SomeObject.init, SomeObject);
     84                 */
     85                self.onInit = {
     86                        add: function(func, scope) {
     87                                self.listeners.push({func : func, scope : scope});
     88                        }
     89                };
     90
     91                self.isWindow = !self.getWindowArg('mce_inline');
     92                self.id = self.getWindowArg('mce_window_id');
     93        },
     94
     95        /**
     96         * Returns the reference to the parent window that opened the dialog.
     97         *
     98         * @method getWin
     99         * @return {Window} Reference to the parent window that opened the dialog.
     100         */
     101        getWin: function() {
     102                // Added frameElement check to fix bug: #2817583
     103                return (!window.frameElement && window.dialogArguments) || opener || parent || top;
     104        },
     105
     106        /**
     107         * Returns a window argument/parameter by name.
     108         *
     109         * @method getWindowArg
     110         * @param {String} name Name of the window argument to retrive.
     111         * @param {String} defaultValue Optional default value to return.
     112         * @return {String} Argument value or default value if it wasn't found.
     113         */
     114        getWindowArg : function(name, defaultValue) {
     115                var value = this.params[name];
     116
     117                return tinymce.is(value) ? value : defaultValue;
     118        },
     119
     120        /**
     121         * Returns a editor parameter/config option value.
     122         *
     123         * @method getParam
     124         * @param {String} name Name of the editor config option to retrive.
     125         * @param {String} defaultValue Optional default value to return.
     126         * @return {String} Parameter value or default value if it wasn't found.
     127         */
     128        getParam : function(name, defaultValue) {
     129                return this.editor.getParam(name, defaultValue);
     130        },
     131
     132        /**
     133         * Returns a language item by key.
     134         *
     135         * @method getLang
     136         * @param {String} name Language item like mydialog.something.
     137         * @param {String} defaultValue Optional default value to return.
     138         * @return {String} Language value for the item like "my string" or the default value if it wasn't found.
     139         */
     140        getLang : function(name, defaultValue) {
     141                return this.editor.getLang(name, defaultValue);
     142        },
     143
     144        /**
     145         * Executed a command on editor that opened the dialog/popup.
     146         *
     147         * @method execCommand
     148         * @param {String} cmd Command to execute.
     149         * @param {Boolean} ui Optional boolean value if the UI for the command should be presented or not.
     150         * @param {Object} val Optional value to pass with the comman like an URL.
     151         * @param {Object} a Optional arguments object.
     152         */
     153        execCommand : function(cmd, ui, val, args) {
     154                args = args || {};
     155                args.skip_focus = 1;
     156
     157                this.restoreSelection();
     158                return this.editor.execCommand(cmd, ui, val, args);
     159        },
     160
     161        /**
     162         * Resizes the dialog to the inner size of the window. This is needed since various browsers
     163         * have different border sizes on windows.
     164         *
     165         * @method resizeToInnerSize
     166         */
     167        resizeToInnerSize : function() {
     168                /*var self = this;
     169
     170                // Detach it to workaround a Chrome specific bug
     171                // https://sourceforge.net/tracker/?func=detail&atid=635682&aid=2926339&group_id=103281
     172                setTimeout(function() {
     173                        var vp = self.dom.getViewPort(window);
     174
     175                        self.editor.windowManager.resizeBy(
     176                                self.getWindowArg('mce_width') - vp.w,
     177                                self.getWindowArg('mce_height') - vp.h,
     178                                self.id || window
     179                        );
     180                }, 10);*/
     181        },
     182
     183        /**
     184         * Will executed the specified string when the page has been loaded. This function
     185         * was added for compatibility with the 2.x branch.
     186         *
     187         * @method executeOnLoad
     188         * @param {String} evil String to evalutate on init.
     189         */
     190        executeOnLoad : function(evil) {
     191                this.onInit.add(function() {
     192                        eval(evil);
     193                });
     194        },
     195
     196        /**
     197         * Stores the current editor selection for later restoration. This can be useful since some browsers
     198         * looses it's selection if a control element is selected/focused inside the dialogs.
     199         *
     200         * @method storeSelection
     201         */
     202        storeSelection : function() {
     203                this.editor.windowManager.bookmark = tinyMCEPopup.editor.selection.getBookmark(1);
     204        },
     205
     206        /**
     207         * Restores any stored selection. This can be useful since some browsers
     208         * looses it's selection if a control element is selected/focused inside the dialogs.
     209         *
     210         * @method restoreSelection
     211         */
     212        restoreSelection : function() {
     213                var self = tinyMCEPopup;
     214
     215                if (!self.isWindow && tinymce.isIE) {
     216                        self.editor.selection.moveToBookmark(self.editor.windowManager.bookmark);
     217                }
     218        },
     219
     220        /**
     221         * Loads a specific dialog language pack. If you pass in plugin_url as a argument
     222         * when you open the window it will load the <plugin url>/langs/<code>_dlg.js lang pack file.
     223         *
     224         * @method requireLangPack
     225         */
     226        requireLangPack : function() {
     227                var self = this, url = self.getWindowArg('plugin_url') || self.getWindowArg('theme_url'), settings = self.editor.settings, lang;
     228
     229                if (settings.language !== false) {
     230                        lang = settings.language || "en";
     231                }
     232
     233                if (url && lang && self.features.translate_i18n !== false && settings.language_load !== false) {
     234                        url += '/langs/' + lang + '_dlg.js';
     235
     236                        if (!tinymce.ScriptLoader.isDone(url)) {
     237                                document.write('<script type="text/javascript" src="' + url + '"></script>');
     238                                tinymce.ScriptLoader.markDone(url);
     239                        }
     240                }
     241        },
     242
     243        /**
     244         * Executes a color picker on the specified element id. When the user
     245         * then selects a color it will be set as the value of the specified element.
     246         *
     247         * @method pickColor
     248         * @param {DOMEvent} e DOM event object.
     249         * @param {string} element_id Element id to be filled with the color value from the picker.
     250         */
     251        pickColor : function(e, element_id) {
     252                this.execCommand('mceColorPicker', true, {
     253                        color : document.getElementById(element_id).value,
     254                        func : function(c) {
     255                                document.getElementById(element_id).value = c;
     256
     257                                try {
     258                                        document.getElementById(element_id).onchange();
     259                                } catch (ex) {
     260                                        // Try fire event, ignore errors
     261                                }
     262                        }
     263                });
     264        },
     265
     266        /**
     267         * Opens a filebrowser/imagebrowser this will set the output value from
     268         * the browser as a value on the specified element.
     269         *
     270         * @method openBrowser
     271         * @param {string} element_id Id of the element to set value in.
     272         * @param {string} type Type of browser to open image/file/flash.
     273         * @param {string} option Option name to get the file_broswer_callback function name from.
     274         */
     275        openBrowser : function(element_id, type) {
     276                tinyMCEPopup.restoreSelection();
     277                this.editor.execCallback('file_browser_callback', element_id, document.getElementById(element_id).value, type, window);
     278        },
     279
     280        /**
     281         * Creates a confirm dialog. Please don't use the blocking behavior of this
     282         * native version use the callback method instead then it can be extended.
     283         *
     284         * @method confirm
     285         * @param {String} t Title for the new confirm dialog.
     286         * @param {function} cb Callback function to be executed after the user has selected ok or cancel.
     287         * @param {Object} s Optional scope to execute the callback in.
     288         */
     289        confirm : function(t, cb, s) {
     290                this.editor.windowManager.confirm(t, cb, s, window);
     291        },
     292
     293        /**
     294         * Creates a alert dialog. Please don't use the blocking behavior of this
     295         * native version use the callback method instead then it can be extended.
     296         *
     297         * @method alert
     298         * @param {String} t Title for the new alert dialog.
     299         * @param {function} cb Callback function to be executed after the user has selected ok.
     300         * @param {Object} s Optional scope to execute the callback in.
     301         */
     302        alert : function(tx, cb, s) {
     303                this.editor.windowManager.alert(tx, cb, s, window);
     304        },
     305
     306        /**
     307         * Closes the current window.
     308         *
     309         * @method close
     310         */
     311        close : function() {
     312                var t = this;
     313
     314                // To avoid domain relaxing issue in Opera
     315                function close() {
     316                        t.editor.windowManager.close(window);
     317                        tinymce = tinyMCE = t.editor = t.params = t.dom = t.dom.doc = null; // Cleanup
     318                }
     319
     320                if (tinymce.isOpera) {
     321                        t.getWin().setTimeout(close, 0);
     322                } else {
     323                        close();
     324                }
     325        },
     326
     327        // Internal functions   
     328
     329        _restoreSelection : function() {
     330                var e = window.event.srcElement;
     331
     332                if (e.nodeName == 'INPUT' && (e.type == 'submit' || e.type == 'button'))
     333                        tinyMCEPopup.restoreSelection();
     334        },
     335
     336/*      _restoreSelection : function() {
     337                var e = window.event.srcElement;
     338
     339                // If user focus a non text input or textarea
     340                if ((e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA') || e.type != 'text')
     341                        tinyMCEPopup.restoreSelection();
     342        },*/
     343
     344        _onDOMLoaded : function() {
     345                var t = tinyMCEPopup, ti = document.title, h, nv;
     346
     347                // Translate page
     348                if (t.features.translate_i18n !== false) {
     349                        var map = {
     350                                "update": "Ok",
     351                                "insert": "Ok",
     352                                "cancel": "Cancel",
     353                                "not_set": "--",
     354                                "class_name": "Class name",
     355                                "browse": "Browse"
     356                        };
     357
     358                        var langCode = tinymce.settings.language || 'en';
     359                        for (var key in map) {
     360                                tinymce.i18n.data[langCode + "." + key] = tinymce.i18n.translate(map[key]);
     361                        }
     362
     363                        h = document.body.innerHTML;
     364
     365                        // Replace a=x with a="x" in IE
     366                        if (tinymce.isIE) {
     367                                h = h.replace(/ (value|title|alt)=([^"][^\s>]+)/gi, ' $1="$2"');
     368                        }
     369
     370                        document.dir = t.editor.getParam('directionality','');
     371
     372                        if ((nv = t.editor.translate(h)) && nv != h) {
     373                                document.body.innerHTML = nv;
     374                        }
     375
     376                        if ((nv = t.editor.translate(ti)) && nv != ti) {
     377                                document.title = ti = nv;
     378                        }
     379                }
     380
     381                if (!t.editor.getParam('browser_preferred_colors', false) || !t.isWindow) {
     382                        t.dom.addClass(document.body, 'forceColors');
     383                }
     384
     385                document.body.style.display = '';
     386
     387                // Restore selection in IE when focus is placed on a non textarea or input element of the type text
     388                if (tinymce.isIE) {
     389                        document.attachEvent('onmouseup', tinyMCEPopup._restoreSelection);
     390
     391                        // Add base target element for it since it would fail with modal dialogs
     392                        t.dom.add(t.dom.select('head')[0], 'base', {target : '_self'});
     393                }
     394
     395                t.restoreSelection();
     396                t.resizeToInnerSize();
     397
     398                // Set inline title
     399                if (!t.isWindow) {
     400                        t.editor.windowManager.setTitle(window, ti);
     401                } else {
     402                        window.focus();
     403                }
     404
     405                if (!tinymce.isIE && !t.isWindow) {
     406                        t.dom.bind(document, 'focus', function() {
     407                                t.editor.windowManager.focus(t.id);
     408                        });
     409                }
     410
     411                // Patch for accessibility
     412                tinymce.each(t.dom.select('select'), function(e) {
     413                        e.onkeydown = tinyMCEPopup._accessHandler;
     414                });
     415
     416                // Call onInit
     417                // Init must be called before focus so the selection won't get lost by the focus call
     418                tinymce.each(t.listeners, function(o) {
     419                        o.func.call(o.scope, t.editor);
     420                });
     421
     422                // Move focus to window
     423                if (t.getWindowArg('mce_auto_focus', true)) {
     424                        window.focus();
     425
     426                        // Focus element with mceFocus class
     427                        tinymce.each(document.forms, function(f) {
     428                                tinymce.each(f.elements, function(e) {
     429                                        if (t.dom.hasClass(e, 'mceFocus') && !e.disabled) {
     430                                                e.focus();
     431                                                return false; // Break loop
     432                                        }
     433                                });
     434                        });
     435                }
     436
     437                document.onkeyup = tinyMCEPopup._closeWinKeyHandler;
     438
     439                t.uiWindow.getEl('head').firstChild.firstChild.data = document.title;
     440        },
     441
     442        _accessHandler : function(e) {
     443                e = e || window.event;
     444
     445                if (e.keyCode == 13 || e.keyCode == 32) {
     446                        var elm = e.target || e.srcElement;
     447
     448                        if (elm.onchange) {
     449                                elm.onchange();
     450                        }
     451
     452                        return tinymce.dom.Event.cancel(e);
     453                }
     454        },
     455
     456        _closeWinKeyHandler : function(e) {
     457                e = e || window.event;
     458
     459                if (e.keyCode == 27) {
     460                        tinyMCEPopup.close();
     461                }
     462        },
     463
     464        _eventProxy: function(id) {
     465                return function(evt) {
     466                        tinyMCEPopup.dom.events.callNativeHandler(id, evt);
     467                };
     468        }
     469};
     470
     471tinyMCEPopup.init();
     472
     473tinymce.util.Dispatcher = function(scope) {
     474        this.scope = scope || this;
     475        this.listeners = [];
     476
     477        this.add = function(callback, scope) {
     478                this.listeners.push({cb : callback, scope : scope || this.scope});
     479
     480                return callback;
     481        };
     482
     483        this.addToTop = function(callback, scope) {
     484                var self = this, listener = {cb : callback, scope : scope || self.scope};
     485
     486                // Create new listeners if addToTop is executed in a dispatch loop
     487                if (self.inDispatch) {
     488                        self.listeners = [listener].concat(self.listeners);
     489                } else {
     490                        self.listeners.unshift(listener);
     491                }
     492
     493                return callback;
     494        };
     495
     496        this.remove = function(callback) {
     497                var listeners = this.listeners, output = null;
     498
     499                tinymce.each(listeners, function(listener, i) {
     500                        if (callback == listener.cb) {
     501                                output = listener;
     502                                listeners.splice(i, 1);
     503                                return false;
     504                        }
     505                });
     506
     507                return output;
     508        };
     509
     510        this.dispatch = function() {
     511                var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener;
     512
     513                self.inDispatch = true;
     514               
     515                // Needs to be a real loop since the listener count might change while looping
     516                // And this is also more efficient
     517                for (i = 0; i < listeners.length; i++) {
     518                        listener = listeners[i];
     519                        returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]);
     520
     521                        if (returnValue === false) {
     522                                break;
     523                        }
     524                }
     525
     526                self.inDispatch = false;
     527
     528                return returnValue;
     529        };
     530};
  • trunk/src/wp-includes/js/tinymce/tinymce.js

    r26862 r26876  
    1 (function(e){var a=/^\s*|\s*$/g,b,d="B".replace(/A(.)|B/,"$1")==="$1";var c={majorVersion:"3",minorVersion:"5.9",releaseDate:"2013-10-18",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isIE11=g.indexOf("Trident/")!=-1&&(g.indexOf("rv:")!=-1||o.appName.indexOf("Netscape")!=-1);s.isOpera=e.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName)||s.isIE11;s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&!s.isIE11&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(e.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m<f.length;m++){r=f[m].href;if(r){if(/^https?:\/\/[^\/]+$/.test(r)){r+="/"}k=r?r.match(/.*\//)[0]:""}}function h(i){if(i.src&&/tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(i.src)){if(/_(src|dev)\.js/g.test(i.src)){s.suffix="_src"}if((j=i.src.indexOf("?"))!=-1){s.query=i.src.substring(j+1)}s.baseURL=i.src.substring(0,i.src.lastIndexOf("/"));if(k&&s.baseURL.indexOf("://")==-1&&s.baseURL.indexOf("/")!==0){s.baseURL=k+s.baseURL}return s.baseURL}return null}f=q.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}l=q.getElementsByTagName("head")[0];if(l){f=l.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}}return},is:function(g,f){if(!f){return g!==b}if(f=="array"&&c.isArray(g)){return true}return typeof(g)==f},isArray:Array.isArray||function(f){return Object.prototype.toString.call(f)==="[object Array]"},makeMap:function(f,j,h){var g;f=f||[];j=j||",";if(typeof(f)=="string"){f=f.split(j)}h=h||{};g=f.length;while(g--){h[f[g]]={}}return h},each:function(i,f,h){var j,g;if(!i){return 0}h=h||i;if(i.length!==b){for(j=0,g=i.length;j<g;j++){if(f.call(h,i[j],j,i)===false){return 0}}}else{for(j in i){if(i.hasOwnProperty(j)){if(f.call(h,i[j],j,i)===false){return 0}}}}return 1},map:function(g,h){var i=[];c.each(g,function(f){i.push(h(f))});return i},grep:function(g,h){var i=[];c.each(g,function(f){if(!h||h(f)){i.push(f)}});return i},inArray:function(g,h){var j,f;if(g){for(j=0,f=g.length;j<f;j++){if(g[j]===h){return j}}}return -1},extend:function(n,k){var j,f,h,g=arguments,m;for(j=1,f=g.length;j<f;j++){k=g[j];for(h in k){if(k.hasOwnProperty(h)){m=k[h];if(m!==b){n[h]=m}}}}return n},trim:function(f){return(f?""+f:"").replace(a,"")},create:function(o,f,j){var n=this,g,i,k,l,h,m=0;o=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(o);k=o[3].match(/(^|\.)(\w+)$/i)[2];i=n.createNS(o[3].replace(/\.\w+$/,""),j);if(i[k]){return}if(o[2]=="static"){i[k]=f;if(this.onCreate){this.onCreate(o[2],o[3],i[k])}return}if(!f[k]){f[k]=function(){};m=1}i[k]=f[k];n.extend(i[k].prototype,f);if(o[5]){g=n.resolve(o[5]).prototype;l=o[5].match(/\.(\w+)$/i)[1];h=i[k];if(m){i[k]=function(){return g[l].apply(this,arguments)}}else{i[k]=function(){this.parent=g[l];return h.apply(this,arguments)}}i[k].prototype[k]=i[k];n.each(g,function(p,q){i[k].prototype[q]=g[q]});n.each(f,function(p,q){if(g[q]){i[k].prototype[q]=function(){this.parent=g[q];return p.apply(this,arguments)}}else{if(q!=k){i[k].prototype[q]=p}}})}n.each(f["static"],function(p,q){i[k][q]=p});if(this.onCreate){this.onCreate(o[2],o[3],i[k].prototype)}},walk:function(i,h,j,g){g=g||this;if(i){if(j){i=i[j]}c.each(i,function(k,f){if(h.call(g,k,f,j)===false){return false}c.walk(k,h,j,g)})}},createNS:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0;g<j.length;g++){f=j[g];if(!h[f]){h[f]={}}h=h[f]}return h},resolve:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0,f=j.length;g<f;g++){h=h[j[g]];if(!h){break}}return h},addUnload:function(j,i){var h=this,g;g=function(){var f=h.unloads,l,m;if(f){for(m in f){l=f[m];if(l&&l.func){l.func.call(l.scope,1)}}if(e.detachEvent){e.detachEvent("onbeforeunload",k);e.detachEvent("onunload",g)}else{if(e.removeEventListener){e.removeEventListener("unload",g,false)}}h.unloads=l=f=w=g=0;if(e.CollectGarbage){CollectGarbage()}}};function k(){var l=document;function f(){l.detachEvent("onstop",f);if(g){g()}l=0}if(l.readyState=="interactive"){if(l){l.attachEvent("onstop",f)}e.setTimeout(function(){if(l){l.detachEvent("onstop",f)}},0)}}j={func:j,scope:i||this};if(!h.unloads){if(e.attachEvent){e.attachEvent("onunload",g);e.attachEvent("onbeforeunload",k)}else{if(e.addEventListener){e.addEventListener("unload",g,false)}}h.unloads=[j]}else{h.unloads.push(j)}return j},removeUnload:function(i){var g=this.unloads,h=null;c.each(g,function(j,f){if(j&&j.func==i){g.splice(f,1);h=i;return false}});return h},explode:function(f,g){if(!f||c.is(f,"array")){return f}return c.map(f.split(g||","),c.trim)},_addVer:function(g){var f;if(!this.query){return g}f=(g.indexOf("?")==-1?"?":"&")+this.query;if(g.indexOf("#")==-1){return g+f}return g.replace("#",f+"#")},_replace:function(h,f,g){if(d){return g.replace(h,function(){var l=f,j=arguments,k;for(k=0;k<j.length-2;k++){if(j[k]===b){l=l.replace(new RegExp("\\$"+k,"g"),"")}else{l=l.replace(new RegExp("\\$"+k,"g"),j[k])}}return l})}return g.replace(h,f)}};c._init();e.tinymce=e.tinyMCE=c})(window);tinymce.create("tinymce.util.Dispatcher",{scope:null,listeners:null,inDispatch:false,Dispatcher:function(a){this.scope=a||this;this.listeners=[]},add:function(b,a){this.listeners.push({cb:b,scope:a||this.scope});return b},addToTop:function(d,b){var a=this,c={cb:d,scope:b||a.scope};if(a.inDispatch){a.listeners=[c].concat(a.listeners)}else{a.listeners.unshift(c)}return d},remove:function(c){var b=this.listeners,a=null;tinymce.each(b,function(e,d){if(c==e.cb){a=e;b.splice(d,1);return false}});return a},dispatch:function(){var a=this,e,b=arguments,c,d=a.listeners,f;a.inDispatch=true;for(c=0;c<d.length;c++){f=d[c];e=f.cb.apply(f.scope,b.length>0?b:[f.scope]);if(e===false){break}}a.inDispatch=false;return e}});(function(){var a=tinymce.each;tinymce.create("tinymce.util.URI",{URI:function(e,g){var f=this,i,d,c,h;e=tinymce.trim(e);g=f.settings=g||{};if(/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e)){f.source=e;return}if(e.indexOf("/")===0&&e.indexOf("//")!==0){e=(g.base_uri?g.base_uri.protocol||"http":"http")+"://mce_host"+e}if(!/^[\w\-]*:?\/\//.test(e)){h=g.base_uri?g.base_uri.path:new tinymce.util.URI(location.href).directory;e=((g.base_uri&&g.base_uri.protocol)||"http")+"://mce_host"+f.toAbsPath(h,e)}e=e.replace(/@@/g,"(mce_at)");e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e);a(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(b,j){var k=e[j];if(k){k=k.replace(/\(mce_at\)/g,"@@")}f[b]=k});c=g.base_uri;if(c){if(!f.protocol){f.protocol=c.protocol}if(!f.userInfo){f.userInfo=c.userInfo}if(!f.port&&f.host==="mce_host"){f.port=c.port}if(!f.host||f.host==="mce_host"){f.host=c.host}f.source=""}},setPath:function(c){var b=this;c=/^(.*?)\/?(\w+)?$/.exec(c);b.path=c[0];b.directory=c[1];b.file=c[2];b.source="";b.getURI()},toRelative:function(b){var d=this,f;if(b==="./"){return b}b=new tinymce.util.URI(b,{base_uri:d});if((b.host!="mce_host"&&d.host!=b.host&&b.host)||d.port!=b.port||d.protocol!=b.protocol){return b.getURI()}var c=d.getURI(),e=b.getURI();if(c==e||(c.charAt(c.length-1)=="/"&&c.substr(0,c.length-1)==e)){return c}f=d.toRelPath(d.path,b.path);if(b.query){f+="?"+b.query}if(b.anchor){f+="#"+b.anchor}return f},toAbsolute:function(b,c){b=new tinymce.util.URI(b,{base_uri:this});return b.getURI(this.host==b.host&&this.protocol==b.protocol?c:0)},toRelPath:function(g,h){var c,f=0,d="",e,b;g=g.substring(0,g.lastIndexOf("/"));g=g.split("/");c=h.split("/");if(g.length>=c.length){for(e=0,b=g.length;e<b;e++){if(e>=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length<c.length){for(e=0,b=c.length;e<b;e++){if(e>=g.length||g[e]!=c[e]){f=e+1;break}}}if(f===1){return h}for(e=0,b=g.length-(f-1);e<b;e++){d+="../"}for(e=f-1,b=c.length;e<b;e++){if(e!=f-1){d+="/"+c[e]}else{d+=c[e]}}return d},toAbsPath:function(e,f){var c,b=0,h=[],d,g;d=/\/$/.test(f)?"/":"";e=e.split("/");f=f.split("/");a(e,function(i){if(i){h.push(i)}});e=h;for(c=f.length-1,h=[];c>=0;c--){if(f[c].length===0||f[c]==="."){continue}if(f[c]===".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!==0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(c,e,d){var b=new Date();b.setTime(b.getTime()-1000);this.set(c,"",b,e,d)}})})();(function(){function serialize(o,quote){var i,v,t,name;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&Object.prototype.toString.call(o)==="[object Array]"){for(i=0,v="[";i<o.length;i++){v+=(i>0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(name in o){if(o.hasOwnProperty(name)){v+=typeof o[name]!="function"?(v.length>1?","+quote:quote)+name+quote+":"+serialize(o[name],quote):""}}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(b){return b.shiftKey||b.ctrlKey||b.altKey},metaKeyPressed:function(b){return a.isMac?b.metaKey:b.ctrlKey&&!b.altKey}}})(tinymce);tinymce.util.Quirks=function(a){var j=tinymce.VK,f=j.BACKSPACE,k=j.DELETE,e=a.dom,m=a.selection,I=a.settings,x=a.parser,p=a.serializer,F=tinymce.each;function B(O,N){try{a.getDoc().execCommand(O,false,N)}catch(M){}}function o(){var M=a.getDoc().documentMode;return M?M:6}function A(M){return M.isDefaultPrevented()}function K(){function M(S){var O,Q,N,T,P,R,U;function V(){if(P.nodeType==3){if(S&&R==P.length){return true}if(!S&&R===0){return true}}}O=m.getRng();var W=[O.startContainer,O.startOffset,O.endContainer,O.endOffset];if(!O.collapsed){S=true}P=O[(S?"start":"end")+"Container"];R=O[(S?"start":"end")+"Offset"];if(P.nodeType==3){Q=e.getParent(O.startContainer,e.isBlock);if(S){Q=e.getNext(Q,e.isBlock)}if(Q&&(V()||!O.collapsed)){N=e.create("em",{id:"__mceDel"});F(tinymce.grep(Q.childNodes),function(X){N.appendChild(X)});Q.appendChild(N)}}O=e.createRng();O.setStart(W[0],W[1]);O.setEnd(W[2],W[3]);m.setRng(O);a.getDoc().execCommand(S?"ForwardDelete":"Delete",false,null);if(N){T=m.getBookmark();while(U=e.get("__mceDel")){e.remove(U,true)}m.moveToBookmark(T)}}a.onKeyDown.add(function(N,P){var O;O=P.keyCode==k;if(!A(P)&&(O||P.keyCode==f)&&!j.modifierPressed(P)){P.preventDefault();M(O)}});a.addCommand("Delete",function(){M()})}function r(){function M(P){var O=e.create("body");var Q=P.cloneContents();O.appendChild(Q);return m.serializer.serialize(O,{format:"html"})}function N(O){var Q=M(O);var R=e.createRng();R.selectNode(a.getBody());var P=M(R);return Q===P}a.onKeyDown.add(function(P,R){var Q=R.keyCode,O;if(!A(R)&&(Q==k||Q==f)){O=P.selection.isCollapsed();if(O&&!e.isEmpty(P.getBody())){return}if(tinymce.isIE&&!O){return}if(!O&&!N(P.selection.getRng())){return}P.setContent("");P.selection.setCursorLocation(P.getBody(),0);P.nodeChanged()}})}function J(){a.onKeyDown.add(function(M,N){if(!A(N)&&N.keyCode==65&&j.metaKeyPressed(N)){N.preventDefault();M.execCommand("SelectAll")}})}function L(){if(!a.settings.content_editable){e.bind(a.getDoc(),"focusin",function(M){m.setRng(m.getRng())});e.bind(a.getDoc(),"mousedown",function(M){if(M.target==a.getDoc().documentElement){a.getWin().focus();m.setRng(m.getRng())}})}}function C(){a.onKeyDown.add(function(M,P){if(!A(P)&&P.keyCode===f){if(m.isCollapsed()&&m.getRng(true).startOffset===0){var O=m.getNode();var N=O.previousSibling;if(N&&N.nodeName&&N.nodeName.toLowerCase()==="hr"){e.remove(N);tinymce.dom.Event.cancel(P)}}}})}function z(){if(!Range.prototype.getClientRects){a.onMouseDown.add(function(N,O){if(!A(O)&&O.target.nodeName==="HTML"){var M=N.getBody();M.blur();setTimeout(function(){M.focus()},0)}})}}function h(){a.onClick.add(function(M,N){N=N.target;if(/^(IMG|HR)$/.test(N.nodeName)){m.getSel().setBaseAndExtent(N,0,N,1)}if(N.nodeName=="A"&&e.hasClass(N,"mceItemAnchor")){m.select(N)}M.nodeChanged()})}function c(){function N(){var P=e.getAttribs(m.getStart().cloneNode(false));return function(){var Q=m.getStart();if(Q!==a.getBody()){e.setAttrib(Q,"style",null);F(P,function(R){Q.setAttributeNode(R.cloneNode(true))})}}}function M(){return !m.isCollapsed()&&e.getParent(m.getStart(),e.isBlock)!=e.getParent(m.getEnd(),e.isBlock)}function O(P,Q){Q.preventDefault();return false}a.onKeyPress.add(function(P,R){var Q;if(!A(R)&&(R.keyCode==8||R.keyCode==46)&&M()){Q=N();P.getDoc().execCommand("delete",false,null);Q();R.preventDefault();return false}});e.bind(a.getDoc(),"cut",function(Q){var P;if(!A(Q)&&M()){P=N();a.onKeyUp.addToTop(O);setTimeout(function(){P();a.onKeyUp.remove(O)},0)}})}function b(){var N,M;e.bind(a.getDoc(),"selectionchange",function(){if(M){clearTimeout(M);M=0}M=window.setTimeout(function(){var O=m.getRng();if(!N||!tinymce.dom.RangeUtils.compareRanges(O,N)){a.nodeChanged();N=O}},50)})}function y(){document.body.setAttribute("role","application")}function u(){a.onKeyDown.add(function(M,O){if(!A(O)&&O.keyCode===f){if(m.isCollapsed()&&m.getRng(true).startOffset===0){var N=m.getNode().previousSibling;if(N&&N.nodeName&&N.nodeName.toLowerCase()==="table"){return tinymce.dom.Event.cancel(O)}}}})}function D(){if(o()>7){return}B("RespectVisibilityInDesign",true);a.contentStyles.push(".mceHideBrInPre pre br {display: none}");e.addClass(a.getBody(),"mceHideBrInPre");x.addNodeFilter("pre",function(M,O){var P=M.length,R,N,S,Q;while(P--){R=M[P].getAll("br");N=R.length;while(N--){S=R[N];Q=S.prev;if(Q&&Q.type===3&&Q.value.charAt(Q.value-1)!="\n"){Q.value+="\n"}else{S.parent.insert(new tinymce.html.Node("#text",3),S,true).value="\n"}}}});p.addNodeFilter("pre",function(M,O){var P=M.length,R,N,S,Q;while(P--){R=M[P].getAll("br");N=R.length;while(N--){S=R[N];Q=S.prev;if(Q&&Q.type==3){Q.value=Q.value.replace(/\r?\n$/,"")}}}})}function g(){e.bind(a.getBody(),"mouseup",function(O){var N,M=m.getNode();if(M.nodeName=="IMG"){if(N=e.getStyle(M,"width")){e.setAttrib(M,"width",N.replace(/[^0-9%]+/g,""));e.setStyle(M,"width","")}if(N=e.getStyle(M,"height")){e.setAttrib(M,"height",N.replace(/[^0-9%]+/g,""));e.setStyle(M,"height","")}}})}function d(){a.onKeyDown.add(function(S,T){var R,M,N,P,Q,U,O;R=T.keyCode==k;if(!A(T)&&(R||T.keyCode==f)&&!j.modifierPressed(T)){M=m.getRng();N=M.startContainer;P=M.startOffset;O=M.collapsed;if(N.nodeType==3&&N.nodeValue.length>0&&((P===0&&!O)||(O&&P===(R?0:1)))){U=N.previousSibling;if(U&&U.nodeName=="IMG"){return}nonEmptyElements=S.schema.getNonEmptyElements();T.preventDefault();Q=e.create("br",{id:"__tmp"});N.parentNode.insertBefore(Q,N);S.getDoc().execCommand(R?"ForwardDelete":"Delete",false,null);N=m.getRng().startContainer;U=N.previousSibling;if(U&&U.nodeType==1&&!e.isBlock(U)&&e.isEmpty(U)&&!nonEmptyElements[U.nodeName.toLowerCase()]){e.remove(U)}e.remove("__tmp")}}})}function H(){a.onKeyDown.add(function(Q,R){var O,N,S,M,P;if(A(R)||R.keyCode!=j.BACKSPACE){return}O=m.getRng();N=O.startContainer;S=O.startOffset;M=e.getRoot();P=N;if(!O.collapsed||S!==0){return}while(P&&P.parentNode&&P.parentNode.firstChild==P&&P.parentNode!=M){P=P.parentNode}if(P.tagName==="BLOCKQUOTE"){Q.formatter.toggle("blockquote",null,P);O=e.createRng();O.setStart(N,0);O.setEnd(N,0);m.setRng(O)}})}function G(){function M(){a._refreshContentEditable();B("StyleWithCSS",false);B("enableInlineTableEditing",false);if(!I.object_resizing){B("enableObjectResizing",false)}}if(!I.readonly){a.onBeforeExecCommand.add(M);a.onMouseDown.add(M)}}function t(){function M(N,O){F(e.select("a"),function(R){var P=R.parentNode,Q=e.getRoot();if(P.lastChild===R){while(P&&!e.isBlock(P)){if(P.parentNode.lastChild!==P||P===Q){return}P=P.parentNode}e.add(P,"br",{"data-mce-bogus":1})}})}a.onExecCommand.add(function(N,O){if(O==="CreateLink"){M(N)}});a.onSetContent.add(m.onSetContent.add(M))}function n(){if(I.forced_root_block){a.onInit.add(function(){B("DefaultParagraphSeparator",I.forced_root_block)})}}function q(){function M(O,N){if(!O||!N.initial){a.execCommand("mceRepaint")}}a.onUndo.add(M);a.onRedo.add(M);a.onSetContent.add(M)}function i(){a.onKeyDown.add(function(N,O){var M;if(!A(O)&&O.keyCode==f){M=N.getDoc().selection.createRange();if(M&&M.item){O.preventDefault();N.undoManager.beforeChange();e.remove(M.item(0));N.undoManager.add()}}})}function s(){var M;if(o()>=10){M="";F("p div h1 h2 h3 h4 h5 h6".split(" "),function(N,O){M+=(O>0?",":"")+N+":empty"});a.contentStyles.push(M+"{padding-right: 1px !important}")}}function v(){var O,N,ae,M,Z,ac,aa,ad,P,Q,ab,X,W,Y=document,U=a.getDoc();if(!I.object_resizing||I.webkit_fake_resize===false){return}B("enableObjectResizing",false);ab={n:[0.5,0,0,-1],e:[1,0.5,1,0],s:[0.5,1,0,1],w:[0,0.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};function S(ai){var ah,ag;ah=ai.screenX-ac;ag=ai.screenY-aa;X=ah*Z[2]+ad;W=ag*Z[3]+P;X=X<5?5:X;W=W<5?5:W;if(j.modifierPressed(ai)||(ae.nodeName=="IMG"&&Z[2]*Z[3]!==0)){X=Math.round(W/Q);W=Math.round(X*Q)}e.setStyles(M,{width:X,height:W});if(Z[2]<0&&M.clientWidth<=X){e.setStyle(M,"left",O+(ad-X))}if(Z[3]<0&&M.clientHeight<=W){e.setStyle(M,"top",N+(P-W))}}function af(){function ag(ah,ai){if(ai){if(ae.style[ah]||!a.schema.isValid(ae.nodeName.toLowerCase(),ah)){e.setStyle(ae,ah,ai)}else{e.setAttrib(ae,ah,ai)}}}ag("width",X);ag("height",W);e.unbind(U,"mousemove",S);e.unbind(U,"mouseup",af);if(Y!=U){e.unbind(Y,"mousemove",S);e.unbind(Y,"mouseup",af)}e.remove(M);R(ae)}function R(aj){var ah,ai,ag;T();ah=e.getPos(aj);O=ah.x;N=ah.y;ai=aj.offsetWidth;ag=aj.offsetHeight;if(ae!=aj){ae=aj;X=W=0}F(ab,function(am,ak){var al;al=e.get("mceResizeHandle"+ak);if(!al){al=e.add(U.documentElement,"div",{id:"mceResizeHandle"+ak,"class":"mceResizeHandle",style:"cursor:"+ak+"-resize; margin:0; padding:0"});e.bind(al,"mousedown",function(an){an.preventDefault();af();ac=an.screenX;aa=an.screenY;ad=ae.clientWidth;P=ae.clientHeight;Q=P/ad;Z=am;M=ae.cloneNode(true);e.addClass(M,"mceClonedResizable");e.setStyles(M,{left:O,top:N,margin:0});U.documentElement.appendChild(M);e.bind(U,"mousemove",S);e.bind(U,"mouseup",af);if(Y!=U){e.bind(Y,"mousemove",S);e.bind(Y,"mouseup",af)}})}else{e.show(al)}e.setStyles(al,{left:(ai*am[0]+O)-(al.offsetWidth/2),top:(ag*am[1]+N)-(al.offsetHeight/2)})});if(!tinymce.isOpera&&ae.nodeName=="IMG"){ae.setAttribute("data-mce-selected","1")}}function T(){if(ae){ae.removeAttribute("data-mce-selected")}for(var ag in ab){e.hide("mceResizeHandle"+ag)}}a.contentStyles.push(".mceResizeHandle {position: absolute;border: 1px solid black;background: #FFF;width: 5px;height: 5px;z-index: 10000}.mceResizeHandle:hover {background: #000}img[data-mce-selected] {outline: 1px solid black}img.mceClonedResizable, table.mceClonedResizable {position: absolute;outline: 1px dashed black;opacity: .5;z-index: 10000}");function V(){var ag=e.getParent(m.getNode(),"table,img");F(e.select("img[data-mce-selected]"),function(ah){ah.removeAttribute("data-mce-selected")});if(ag){R(ag)}else{T()}}a.onNodeChange.add(V);e.bind(U,"selectionchange",V);a.serializer.addAttributeFilter("data-mce-selected",function(ag,ah){var ai=ag.length;while(ai--){ag[ai].attr(ah,null)}})}function E(){if(o()<9){x.addNodeFilter("noscript",function(M){var N=M.length,O,P;while(N--){O=M[N];P=O.firstChild;if(P){O.attr("data-mce-innertext",P.value)}}});p.addNodeFilter("noscript",function(M){var N=M.length,O,Q,P;while(N--){O=M[N];Q=M[N].firstChild;if(Q){Q.value=tinymce.html.Entities.decode(Q.value)}else{P=O.attributes.map["data-mce-innertext"];if(P){O.attr("data-mce-innertext",null);Q=new tinymce.html.Node("#text",3);Q.value=P;Q.raw=true;O.append(Q)}}}})}}function l(){a.contentStyles.push("body {min-height: 100px}");a.onClick.add(function(M,N){if(N.target.nodeName=="HTML"){a.execCommand("SelectAll");a.selection.collapse(true);a.nodeChanged()}})}u();H();r();if(tinymce.isWebKit){d();K();L();h();n();if(tinymce.isIDevice){b()}else{v();J()}}if(tinymce.isIE&&!tinymce.isIE11){C();y();D();g();i();s();E()}if(tinymce.isIE11){l()}if(tinymce.isGecko&&!tinymce.isIE11){C();z();c();G();t();q()}if(tinymce.isOpera){v()}};(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={128:"\u20AC",130:"\u201A",131:"\u0192",132:"\u201E",133:"\u2026",134:"\u2020",135:"\u2021",136:"\u02C6",137:"\u2030",138:"\u0160",139:"\u2039",140:"\u0152",142:"\u017D",145:"\u2018",146:"\u2019",147:"\u201C",148:"\u201D",149:"\u2022",150:"\u2013",151:"\u2014",152:"\u02DC",153:"\u2122",154:"\u0161",155:"\u203A",156:"\u0153",158:"\u017E",159:"\u0178"};g={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;"};d={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n<m.length;n+=2){o=String.fromCharCode(parseInt(m[n],p));if(!g[o]){l="&"+m[n+1]+";";q[o]=l;q[l]=o}}return q}}a=e("50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,t9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro",32);j.html=j.html||{};j.html.Entities={encodeRaw:function(m,l){return m.replace(l?k:b,function(n){return g[n]||n})},encodeAllRaw:function(l){return(""+l).replace(f,function(m){return g[m]||m})},encodeNumeric:function(m,l){return m.replace(l?k:b,function(n){if(n.length>1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g<j.length;g++){a[j[g]]="\uFEFF"+g;a["\uFEFF"+g]=j[g]}function c(n,q,p,i){function o(r){r=parseInt(r).toString(16);return r.length>1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(s){var z={},q,n,x,r,v=d.url_converter,y=d.url_converter_scope||this;function p(D,G){var F,C,B,E;if(z["border-image"]==="none"){delete z["border-image"]}F=z[D+"-top"+G];if(!F){return}C=z[D+"-right"+G];if(F!=C){return}B=z[D+"-bottom"+G];if(C!=B){return}E=z[D+"-left"+G];if(B!=E){return}z[D+G]=E;delete z[D+"-top"+G];delete z[D+"-right"+G];delete z[D+"-bottom"+G];delete z[D+"-left"+G]}function u(C){var D=z[C],B;if(!D||D.indexOf(" ")<0){return}D=D.split(" ");B=D.length;while(B--){if(D[B]!==D[0]){return false}}z[C]=D[0];return true}function A(D,C,B,E){if(!u(C)){return}if(!u(B)){return}if(!u(E)){return}z[D]=z[C]+" "+z[B]+" "+z[E];delete z[C];delete z[B];delete z[E]}function t(B){r=true;return a[B]}function i(C,B){if(r){C=C.replace(/\uFEFF[0-9]/g,function(D){return a[D]})}if(!B){C=C.replace(/\\([\'\";:])/g,"$1")}return C}function o(C,B,F,E,G,D){G=G||D;if(G){G=i(G);return"'"+G.replace(/\'/g,"\\'")+"'"}B=i(B||F||E);if(v){B=v.call(y,B,"style")}return"url('"+B.replace(/\'/g,"\\'")+"')"}if(s){s=s.replace(/\\[\"\';:\uFEFF]/g,t).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(B){return B.replace(/[;:]/g,t)});while(q=b.exec(s)){n=q[1].replace(l,"").toLowerCase();x=q[2].replace(l,"");if(n&&x.length>0){if(n==="font-weight"&&x==="700"){x="bold"}else{if(n==="color"||n==="background-color"){x=x.toLowerCase()}}x=x.replace(k,c);x=x.replace(h,o);z[n]=r?i(x,true):x}b.lastIndex=q.index+q[0].length}p("border","");p("border","-width");p("border","-color");p("border","-style");p("padding","");p("margin","");A("border","border-width","border-style","border-color");if(z.border==="medium none"){delete z.border}}return z},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u<s;u++){t=x[u];v=p[t];if(v!==e&&v.length>0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(f){var a={},e=f.makeMap,g=f.each;function d(j,i){return j.split(i||",")}function h(m,l){var j,k={};function i(n){return n.replace(/[A-Z]+/g,function(o){return i(m[o])})}for(j in m){if(m.hasOwnProperty(j)){m[j]=i(m[j])}}i(l).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(q,o,n,p){n=d(n,"|");k[o]={attributes:e(n),attributesOrder:n,children:e(p,"|",{"#comment":{}})}});return k}function b(){var i=a.html5;if(!i){i=a.html5=h({A:"id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr",C:"#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video"},"html[A|manifest][body|head]head[A][base|command|link|meta|noscript|script|style|title]title[A][#]base[A|href|target][]link[A|href|rel|media|type|sizes][]meta[A|http-equiv|name|content|charset][]style[A|type|media|scoped][#]script[A|charset|type|src|defer|async][#]noscript[A][C]body[A][C]section[A][C]nav[A][C]article[A][C]aside[A][C]h1[A][B]h2[A][B]h3[A][B]h4[A][B]h5[A][B]h6[A][B]hgroup[A][h1|h2|h3|h4|h5|h6]header[A][C]footer[A][C]address[A][C]p[A][B]br[A][]pre[A][B]dialog[A][dd|dt]blockquote[A|cite][C]ol[A|start|reversed][li]ul[A][li]li[A|value][C]dl[A][dd|dt]dt[A][B]dd[A][C]a[A|href|target|ping|rel|media|type][B]em[A][B]strong[A][B]small[A][B]cite[A][B]q[A|cite][B]dfn[A][B]abbr[A][B]code[A][B]var[A][B]samp[A][B]kbd[A][B]sub[A][B]sup[A][B]i[A][B]b[A][B]mark[A][B]progress[A|value|max][B]meter[A|value|min|max|low|high|optimum][B]time[A|datetime][B]ruby[A][B|rt|rp]rt[A][B]rp[A][B]bdo[A][B]span[A][B]ins[A|cite|datetime][B]del[A|cite|datetime][B]figure[A][C|legend|figcaption]figcaption[A][C]img[A|alt|src|height|width|usemap|ismap][]iframe[A|name|src|height|width|sandbox|seamless][]embed[A|src|height|width|type][]object[A|data|type|height|width|usemap|name|form|classid][param]param[A|name|value][]details[A|open][C|legend]command[A|type|label|icon|disabled|checked|radiogroup][]menu[A|type|label][C|li]legend[A][C|B]div[A][C]source[A|src|type|media][]audio[A|src|autobuffer|autoplay|loop|controls][source]video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]hr[A][]form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]fieldset[A|disabled|form|name][C|legend]label[A|form|for][B]input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]datalist[A][B|option]optgroup[A|disabled|label][option]option[A|disabled|selected|label|value][]textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]keygen[A|autofocus|challenge|disabled|form|keytype|name][]output[A|for|form|name][B]canvas[A|width|height][]map[A|name][B|C]area[A|shape|coords|href|alt|target|media|rel|ping|type][]mathml[A][]svg[A][]table[A|border][caption|colgroup|thead|tfoot|tbody|tr]caption[A][C]colgroup[A|span][col]col[A|span][]thead[A][tr]tfoot[A][tr]tbody[A][tr]tr[A][th|td]th[A|headers|rowspan|colspan|scope][B]td[A|headers|rowspan|colspan][C]wbr[A][]")}return i}function c(){var i=a.html4;if(!i){i=a.html4=h({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]")}return i}f.html.Schema=function(A){var u=this,s={},k={},j=[],D,y;var o,q,z,r,v,n,p={};function m(F,E,H){var G=A[F];if(!G){G=a[F];if(!G){G=e(E," ",e(E.toUpperCase()," "));G=f.extend(G,H);a[F]=G}}else{G=e(G,",",e(G.toUpperCase()," "))}return G}A=A||{};y=A.schema=="html5"?b():c();if(A.verify_html===false){A.valid_elements="*[*]"}if(A.valid_styles){D={};g(A.valid_styles,function(F,E){D[E]=f.explode(F)})}o=m("whitespace_elements","pre script noscript style textarea");q=m("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr");z=m("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr");r=m("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls");n=m("non_empty_elements","td th iframe video audio object script",z);textBlockElementsMap=m("text_block_elements","h1 h2 h3 h4 h5 h6 p div address pre form blockquote center dir fieldset header footer article section hgroup aside nav figure");v=m("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup",textBlockElementsMap);function i(E){return new RegExp("^"+E.replace(/([?+*])/g,".$1")+"$")}function C(L){var K,G,Z,V,aa,F,I,U,X,Q,Y,ac,O,J,W,E,S,H,ab,ad,P,T,N=/^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,R=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,M=/[*?+]/;if(L){L=d(L);if(s["@"]){S=s["@"].attributes;H=s["@"].attributesOrder}for(K=0,G=L.length;K<G;K++){F=N.exec(L[K]);if(F){W=F[1];Q=F[2];E=F[3];X=F[4];O={};J=[];I={attributes:O,attributesOrder:J};if(W==="#"){I.paddEmpty=true}if(W==="-"){I.removeEmpty=true}if(S){for(ad in S){O[ad]=S[ad]}J.push.apply(J,H)}if(X){X=d(X,"|");for(Z=0,V=X.length;Z<V;Z++){F=R.exec(X[Z]);if(F){U={};ac=F[1];Y=F[2].replace(/::/g,":");W=F[3];T=F[4];if(ac==="!"){I.attributesRequired=I.attributesRequired||[];I.attributesRequired.push(Y);U.required=true}if(ac==="-"){delete O[Y];J.splice(f.inArray(J,Y),1);continue}if(W){if(W==="="){I.attributesDefault=I.attributesDefault||[];I.attributesDefault.push({name:Y,value:T});U.defaultValue=T}if(W===":"){I.attributesForced=I.attributesForced||[];I.attributesForced.push({name:Y,value:T});U.forcedValue=T}if(W==="<"){U.validValues=e(T,"?")}}if(M.test(Y)){I.attributePatterns=I.attributePatterns||[];U.pattern=i(Y);I.attributePatterns.push(U)}else{if(!O[Y]){J.push(Y)}O[Y]=U}}}}if(!S&&Q=="@"){S=O;H=J}if(E){I.outputName=Q;s[E]=I}if(M.test(Q)){I.pattern=i(Q);j.push(I)}else{s[Q]=I}}}}}function t(E){s={};j=[];C(E);g(y,function(G,F){k[F]=G.children})}function l(F){var E=/^(~)?(.+)$/;if(F){g(d(F),function(J){var H=E.exec(J),I=H[1]==="~",K=I?"span":"div",G=H[2];k[G]=k[K];p[G]=K;if(!I){v[G.toUpperCase()]={};v[G]={}}if(!s[G]){s[G]=s[K]}g(k,function(L,M){if(L[K]){L[G]=L[K]}})})}}function x(F){var E=/^([+\-]?)(\w+)\[([^\]]+)\]$/;if(F){g(d(F),function(J){var I=E.exec(J),G,H;if(I){H=I[1];if(H){G=k[I[2]]}else{G=k[I[2]]={"#comment":{}}}G=k[I[2]];g(d(I[3],"|"),function(K){if(H==="-"){delete G[K]}else{G[K]={}}})}})}}function B(E){var G=s[E],F;if(G){return G}F=j.length;while(F--){G=j[F];if(G.pattern.test(E)){return G}}}if(!A.valid_elements){g(y,function(F,E){s[E]={attributes:F.attributes,attributesOrder:F.attributesOrder};k[E]=F.children});if(A.schema!="html5"){g(d("strong/b,em/i"),function(E){E=d(E,"/");s[E[1]].outputName=E[0]})}s.img.attributesDefault=[{name:"alt",value:""}];g(d("ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i"),function(E){if(s[E]){s[E].removeEmpty=true}});g(d("p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption"),function(E){s[E].paddEmpty=true})}else{t(A.valid_elements)}l(A.custom_elements);x(A.valid_children);C(A.extended_valid_elements);x("+ol[ul|ol],+ul[ul|ol]");if(A.invalid_elements){f.each(f.explode(A.invalid_elements),function(E){if(s[E]){delete s[E]}})}if(!B("span")){C("span[!data-mce-type|*]")}u.children=k;u.styles=D;u.getBoolAttrs=function(){return r};u.getBlockElements=function(){return v};u.getTextBlockElements=function(){return textBlockElementsMap};u.getShortEndedElements=function(){return z};u.getSelfClosingElements=function(){return q};u.getNonEmptyElements=function(){return n};u.getWhiteSpaceElements=function(){return o};u.isValidChild=function(E,G){var F=k[E];return !!(F&&F[G])};u.isValid=function(F,E){var H,G,I=B(F);if(I){if(E){if(I.attributes[E]){return true}H=I.attributePatterns;if(H){G=H.length;while(G--){if(H[G].pattern.test(F)){return true}}}}else{return true}}return false};u.getElementRule=B;u.getCustomElements=function(){return p};u.addValidElements=C;u.setValidElements=t;u.addCustomElements=l;u.addValidChildren=x;u.elements=s}})(tinymce);(function(a){a.html.SaxParser=function(c,e){var b=this,d=function(){};c=c||{};b.schema=e=e||new a.html.Schema();if(c.fix_self_closing!==false){c.fix_self_closing=true}a.each("comment cdata text start end pi doctype".split(" "),function(f){if(f){b[f]=c[f]||d}});b.parse=function(E){var n=this,g,G=0,I,B,A=[],N,Q,C,r,z,s,M,H,O,v,m,k,t,R,o,P,F,S,L,f,J,l,D,K,h,x=0,j=a.html.Entities.decode,y,q;function u(T){var V,U;V=A.length;while(V--){if(A[V].name===T){break}}if(V>=0){for(U=A.length-1;U>=V;U--){T=A[U];if(T.valid){n.end(T.name)}}A.length=V}}function p(U,T,Y,X,W){var Z,V;T=T.toLowerCase();Y=T in H?T:j(Y||X||W||"");if(v&&!z&&T.indexOf("data-")!==0){Z=P[T];if(!Z&&F){V=F.length;while(V--){Z=F[V];if(Z.pattern.test(T)){break}}if(V===-1){Z=null}}if(!Z){return}if(Z.validValues&&!(Y in Z.validValues)){return}}N.map[T]=Y;N.push({name:T,value:Y})}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g");D=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;K={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};M=e.getShortEndedElements();J=c.self_closing_elements||e.getSelfClosingElements();H=e.getBoolAttrs();v=c.validate;s=c.remove_internals;y=c.fix_self_closing;q=a.isIE;o=/^:/;while(g=l.exec(E)){if(G<g.index){n.text(j(E.substr(G,g.index-G)))}if(I=g[6]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}u(I)}else{if(I=g[7]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}O=I in M;if(y&&J[I]&&A.length>0&&A[A.length-1].name===I){u(I)}if(!v||(m=e.getElementRule(I))){k=true;if(v){P=m.attributes;F=m.attributePatterns}if(R=g[8]){z=R.indexOf("data-mce-type")!==-1;if(z&&s){k=false}N=[];N.map={};R.replace(D,p)}else{N=[];N.map={}}if(v&&!z){S=m.attributesRequired;L=m.attributesDefault;f=m.attributesForced;if(f){Q=f.length;while(Q--){t=f[Q];r=t.name;h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}if(L){Q=L.length;while(Q--){t=L[Q];r=t.name;if(!(r in N.map)){h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}}if(S){Q=S.length;while(Q--){if(S[Q] in N.map){break}}if(Q===-1){k=false}}if(N.map["data-mce-bogus"]){k=false}}if(k){n.start(I,N,O)}}else{k=false}if(B=K[I]){B.lastIndex=G=g.index+g[0].length;if(g=B.exec(E)){if(k){C=E.substr(G,g.index-G)}G=g.index+g[0].length}else{C=E.substr(G);G=E.length}if(k&&C.length>0){n.text(C,true)}if(k){n.end(I)}l.lastIndex=G;continue}if(!O){if(!R||R.indexOf("/")!=R.length-1){A.push({name:I,valid:k})}else{if(k){n.end(I)}}}}else{if(I=g[1]){n.comment(I)}else{if(I=g[2]){n.cdata(I)}else{if(I=g[3]){n.doctype(I)}else{if(I=g[4]){n.pi(I,g[5])}}}}}}G=g.index+g[0].length}if(G<E.length){n.text(j(E.substr(G)))}for(Q=A.length-1;Q>=0;Q--){I=A[Q];if(I.valid){n.end(I.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h<f;h++){j=m[h];if(j.name!=="id"){k[k.length]={name:j.name,value:j.value};k.map[j.name]=j.value}}n.attributes=k}n.value=g.value;n.shortEnded=g.shortEnded;return n},wrap:function(g){var f=this;f.parent.insert(g,f);g.append(f);return f},unwrap:function(){var f=this,h,g;for(h=f.firstChild;h;){g=h.next;f.insert(h,f,true);h=g}f.remove()},remove:function(){var f=this,h=f.parent,g=f.next,i=f.prev;if(h){if(h.firstChild===f){h.firstChild=g;if(g){g.prev=null}}else{i.next=g}if(h.lastChild===f){h.lastChild=i;if(i){i.next=null}}else{g.prev=i}f.parent=f.next=f.prev=null}return f},append:function(h){var f=this,g;if(h.parent){h.remove()}g=f.lastChild;if(g){g.next=h;h.prev=g;f.lastChild=h}else{f.lastChild=f.firstChild=h}h.parent=f;return h},insert:function(h,f,i){var g;if(h.parent){h.remove()}g=f.parent||this;if(i){if(f===g.firstChild){g.firstChild=h}else{f.prev.next=h}h.prev=f.prev;h.next=f;f.prev=h}else{if(f===g.lastChild){g.lastChild=h}else{f.next.prev=h}h.next=f.next;h.prev=f;f.next=h}h.parent=g;return h},getAll:function(g){var f=this,h,i=[];for(h=f.firstChild;h;h=a(h,f)){if(h.name===g){i.push(h)}}return i},empty:function(){var g=this,f,h,j;if(g.firstChild){f=[];for(j=g.firstChild;j;j=a(j,g)){f.push(j)}h=f.length;while(h--){j=f[h];j.parent=j.firstChild=j.lastChild=j.next=j.prev=null}}g.firstChild=g.lastChild=null;return g},isEmpty:function(k){var f=this,j=f.firstChild,h,g;if(j){do{if(j.type===1){if(j.attributes.map["data-mce-bogus"]){continue}if(k[j.name]){return false}h=j.attributes.length;while(h--){g=j.attributes[h].name;if(g==="name"||g.indexOf("data-mce-")===0){return false}}}if(j.type===8){return false}if((j.type===3&&!c.test(j.value))){return false}}while(j=a(j,f))}return true},walk:function(f){return a(this,null,f)}});d.extend(b,{create:function(g,f){var i,h;i=new b(g,e[g]||1);if(f){for(h in f){i.attr(h,f[h])}}return i}});d.html.Node=b})(tinymce);(function(b){var a=b.html.Node;b.html.DomParser=function(g,h){var f=this,e={},d=[],i={},c={};g=g||{};g.validate="validate" in g?g.validate:true;g.root_name=g.root_name||"body";f.schema=h=h||new b.html.Schema();function j(n){var p,q,y,x,A,o,r,l,u,v,k,t,m,z,s;t=b.makeMap("tr,td,th,tbody,thead,tfoot,table");k=h.getNonEmptyElements();m=h.getTextBlockElements();for(p=0;p<n.length;p++){q=n[p];if(!q.parent||q.fixed){continue}if(m[q.name]&&q.parent.name=="li"){z=q.next;while(z){if(m[z.name]){z.name="li";z.fixed=true;q.parent.insert(z,q.parent)}else{break}z=z.next}q.unwrap(q);continue}x=[q];for(y=q.parent;y&&!h.isValidChild(y.name,q.name)&&!t[y.name];y=y.parent){x.push(y)}if(y&&x.length>1){x.reverse();A=o=f.filterNode(x[0].clone());for(u=0;u<x.length-1;u++){if(h.isValidChild(o.name,x[u].name)){r=f.filterNode(x[u].clone());o.append(r)}else{r=o}for(l=x[u].firstChild;l&&l!=x[u+1];){s=l.next;r.append(l);l=s}o=r}if(!A.isEmpty(k)){y.insert(A,x[0],true);y.insert(q,A)}else{y.insert(q,x[0],true)}y=x[0];if(y.isEmpty(k)||y.firstChild===y.lastChild&&y.firstChild.name==="br"){y.empty().remove()}}else{if(q.parent){if(q.name==="li"){z=q.prev;if(z&&(z.name==="ul"||z.name==="ul")){z.append(q);continue}z=q.next;if(z&&(z.name==="ul"||z.name==="ul")){z.insert(q,z.firstChild,true);continue}q.wrap(f.filterNode(new a("ul",1)));continue}if(h.isValidChild(q.parent.name,"div")&&h.isValidChild("div",q.name)){q.wrap(f.filterNode(new a("div",1)))}else{if(q.name==="style"||q.name==="script"){q.empty().remove()}else{q.unwrap()}}}}}}f.filterNode=function(m){var l,k,n;if(k in e){n=i[k];if(n){n.push(m)}else{i[k]=[m]}}l=d.length;while(l--){k=d[l].name;if(k in m.attributes.map){n=c[k];if(n){n.push(m)}else{c[k]=[m]}}}return m};f.addNodeFilter=function(k,l){b.each(b.explode(k),function(m){var n=e[m];if(!n){e[m]=n=[]}n.push(l)})};f.addAttributeFilter=function(k,l){b.each(b.explode(k),function(m){var n;for(n=0;n<d.length;n++){if(d[n].name===m){d[n].callbacks.push(l);return}}d.push({name:m,callbacks:[l]})})};f.parse=function(v,m){var n,J,B,A,D,C,x,r,F,N,z,o,E,M=[],L,t,k,y,s,p,u,q;m=m||{};i={};c={};o=b.extend(b.makeMap("script,style,head,html,body,title,meta,param"),h.getBlockElements());u=h.getNonEmptyElements();p=h.children;z=g.validate;q="forced_root_block" in m?m.forced_root_block:g.forced_root_block;s=h.getWhiteSpaceElements();E=/^[ \t\r\n]+/;t=/[ \t\r\n]+$/;k=/[ \t\r\n]+/g;y=/^[ \t\r\n]+$/;function G(){var O=J.firstChild,l,P;while(O){l=O.next;if(O.type==3||(O.type==1&&O.name!=="p"&&!o[O.name]&&!O.attr("data-mce-type"))){if(!P){P=K(q,1);J.insert(P,O);P.append(O)}else{P.append(O)}}else{P=null}O=l}}function K(l,O){var P=new a(l,O),Q;if(l in e){Q=i[l];if(Q){Q.push(P)}else{i[l]=[P]}}return P}function I(P){var Q,l,O;for(Q=P.prev;Q&&Q.type===3;){l=Q.value.replace(t,"");if(l.length>0){Q.value=l;Q=Q.prev}else{O=Q.prev;Q.remove();Q=O}}}function H(O){var P,l={};for(P in O){if(P!=="li"&&P!="p"){l[P]=O[P]}}return l}n=new b.html.SaxParser({validate:z,self_closing_elements:H(h.getSelfClosingElements()),cdata:function(l){B.append(K("#cdata",4)).value=l},text:function(P,l){var O;if(!L){P=P.replace(k," ");if(B.lastChild&&o[B.lastChild.name]){P=P.replace(E,"")}}if(P.length!==0){O=K("#text",3);O.raw=!!l;B.append(O).value=P}},comment:function(l){B.append(K("#comment",8)).value=l},pi:function(l,O){B.append(K(l,7)).value=O;I(B)},doctype:function(O){var l;l=B.append(K("#doctype",10));l.value=O;I(B)},start:function(l,W,P){var U,R,Q,O,S,X,V,T;Q=z?h.getElementRule(l):{};if(Q){U=K(Q.outputName||l,1);U.attributes=W;U.shortEnded=P;B.append(U);T=p[B.name];if(T&&p[U.name]&&!T[U.name]){M.push(U)}R=d.length;while(R--){S=d[R].name;if(S in W.map){F=c[S];if(F){F.push(U)}else{c[S]=[U]}}}if(o[l]){I(U)}if(!P){B=U}if(!L&&s[l]){L=true}}},end:function(l){var S,P,R,O,Q;P=z?h.getElementRule(l):{};if(P){if(o[l]){if(!L){S=B.firstChild;if(S&&S.type===3){R=S.value.replace(E,"");if(R.length>0){S.value=R;S=S.next}else{O=S.next;S.remove();S=O}while(S&&S.type===3){R=S.value;O=S.next;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}S=B.lastChild;if(S&&S.type===3){R=S.value.replace(t,"");if(R.length>0){S.value=R;S=S.prev}else{O=S.prev;S.remove();S=O}while(S&&S.type===3){R=S.value;O=S.prev;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}}}if(L&&s[l]){L=false}if(P.removeEmpty||P.paddEmpty){if(B.isEmpty(u)){if(P.paddEmpty){B.empty().append(new a("#text","3")).value="\u00a0"}else{if(!B.attributes.map.name&&!B.attributes.map.id){Q=B.parent;B.empty().remove();B=Q;return}}}}B=B.parent}}},h);J=B=new a(m.context||g.root_name,11);n.parse(v);if(z&&M.length){if(!m.context){j(M)}else{m.invalid=true}}if(q&&J.name=="body"){G()}if(!m.invalid){for(N in i){F=e[N];A=i[N];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(D=0,C=F.length;D<C;D++){F[D](A,N,m)}}for(D=0,C=d.length;D<C;D++){F=d[D];if(F.name in c){A=c[F.name];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(x=0,r=F.callbacks.length;x<r;x++){F.callbacks[x](A,F.name,m)}}}}return J};if(g.remove_trailing_brs){f.addNodeFilter("br",function(n,m){var r,q=n.length,o,v=b.extend({},h.getBlockElements()),k=h.getNonEmptyElements(),t,s,p,u;v.body=1;for(r=0;r<q;r++){o=n[r];t=o.parent;if(v[o.parent.name]&&o===t.lastChild){p=o.prev;while(p){u=p.name;if(u!=="span"||p.attr("data-mce-type")!=="bookmark"){if(u!=="br"){break}if(u==="br"){o=null;break}}p=p.prev}if(o){o.remove();if(t.isEmpty(k)){elementRule=h.getElementRule(t.name);if(elementRule){if(elementRule.removeEmpty){t.remove()}else{if(elementRule.paddEmpty){t.empty().append(new b.html.Node("#text",3)).value="\u00a0"}}}}}}else{s=o;while(t.firstChild===s&&t.lastChild===s){s=t;if(v[t.name]){break}t=t.parent}if(s===t){textNode=new b.html.Node("#text",3);textNode.value="\u00a0";o.replace(textNode)}}}})}if(!g.allow_html_in_named_anchor){f.addAttributeFilter("id,name",function(k,l){var n=k.length,p,m,o,q;while(n--){q=k[n];if(q.name==="a"&&q.firstChild&&!q.attr("href")){o=q.parent;p=q.lastChild;do{m=p.prev;o.insert(p,q);p=m}while(p)}}})}}})(tinymce);tinymce.html.Writer=function(e){var c=[],a,b,d,f,g;e=e||{};a=e.indent;b=tinymce.makeMap(e.indent_before||"");d=tinymce.makeMap(e.indent_after||"");f=tinymce.html.Entities.getEncodeFunc(e.entity_encoding||"raw",e.entities);g=e.element_format=="html";return{start:function(m,k,p){var n,j,h,o;if(a&&b[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n<j;n++){h=k[n];c.push(" ",h.name,'="',f(h.value,true),'"')}}if(!p||g){c[c.length]=">"}else{c[c.length]=" />"}if(p&&a&&d[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("</",h,">");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("<![CDATA[",h,"]]>")},comment:function(h){c.push("<!--",h,"-->")},pi:function(h,i){if(i){c.push("<?",h," ",i,"?>")}else{c.push("<?",h,"?>")}if(a){c.push("\n")}},doctype:function(h){c.push("<!DOCTYPE",h,">",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n<m;n++){r=q.attributesOrder[n];if(r in s.map){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}for(n=0,m=s.length;n<m;n++){r=s[n].name;if(!(r in u.map)){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}s=u}e.start(k.name,s,o);if(!o){if((k=k.firstChild)){do{f(k)}while(k=k.next)}e.end(j)}}else{t(k)}}if(h.type==1&&!c.inner){f(h)}else{g[11](h)}return e.getContent()}}})(tinymce);tinymce.dom={};(function(b,h){var g=!!document.addEventListener;function c(k,j,l,i){if(k.addEventListener){k.addEventListener(j,l,i||false)}else{if(k.attachEvent){k.attachEvent("on"+j,l)}}}function e(k,j,l,i){if(k.removeEventListener){k.removeEventListener(j,l,i||false)}else{if(k.detachEvent){k.detachEvent("on"+j,l)}}}function a(n,l){var i,k=l||{};function j(){return false}function m(){return true}for(i in n){if(i!=="layerX"&&i!=="layerY"){k[i]=n[i]}}if(!k.target){k.target=k.srcElement||document}k.preventDefault=function(){k.isDefaultPrevented=m;if(n){if(n.preventDefault){n.preventDefault()}else{n.returnValue=false}}};k.stopPropagation=function(){k.isPropagationStopped=m;if(n){if(n.stopPropagation){n.stopPropagation()}else{n.cancelBubble=true}}};k.stopImmediatePropagation=function(){k.isImmediatePropagationStopped=m;k.stopPropagation()};if(!k.isDefaultPrevented){k.isDefaultPrevented=j;k.isPropagationStopped=j;k.isImmediatePropagationStopped=j}return k}function d(m,n,l){var k=m.document,j={type:"ready"};function i(){if(!l.domLoaded){l.domLoaded=true;n(j)}}if(k.readyState=="complete"){i();return}if(g){c(m,"DOMContentLoaded",i)}else{c(k,"readystatechange",function(){if(k.readyState==="complete"){e(k,"readystatechange",arguments.callee);i()}});if(k.documentElement.doScroll&&m===m.top){(function(){try{k.documentElement.doScroll("left")}catch(o){setTimeout(arguments.callee,0);return}i()})()}}c(m,"load",i)}function f(k){var q=this,p={},i,o,n,m,l;m="onmouseenter" in document.documentElement;n="onfocusin" in document.documentElement;l={mouseenter:"mouseover",mouseleave:"mouseout"};i=1;q.domLoaded=false;q.events=p;function j(t,x){var s,u,r,v;s=p[x][t.type];if(s){for(u=0,r=s.length;u<r;u++){v=s[u];if(v&&v.func.call(v.scope,t)===false){t.preventDefault()}if(t.isImmediatePropagationStopped()){return}}}}q.bind=function(x,A,D,E){var s,t,u,r,B,z,C,v=window;function y(F){j(a(F||v.event),s)}if(!x||x.nodeType===3||x.nodeType===8){return}if(!x[h]){s=i++;x[h]=s;p[s]={}}else{s=x[h];if(!p[s]){p[s]={}}}E=E||x;A=A.split(" ");u=A.length;while(u--){r=A[u];z=y;B=C=false;if(r==="DOMContentLoaded"){r="ready"}if((q.domLoaded||x.readyState=="complete")&&r==="ready"){q.domLoaded=true;D.call(E,a({type:r}));continue}if(!m){B=l[r];if(B){z=function(F){var H,G;H=F.currentTarget;G=F.relatedTarget;if(G&&H.contains){G=H.contains(G)}else{while(G&&G!==H){G=G.parentNode}}if(!G){F=a(F||v.event);F.type=F.type==="mouseout"?"mouseleave":"mouseenter";F.target=H;j(F,s)}}}}if(!n&&(r==="focusin"||r==="focusout")){C=true;B=r==="focusin"?"focus":"blur";z=function(F){F=a(F||v.event);F.type=F.type==="focus"?"focusin":"focusout";j(F,s)}}t=p[s][r];if(!t){p[s][r]=t=[{func:D,scope:E}];t.fakeName=B;t.capture=C;t.nativeHandler=z;if(!g){t.proxyHandler=k(s)}if(r==="ready"){d(x,z,q)}else{c(x,B||r,g?z:t.proxyHandler,C)}}else{t.push({func:D,scope:E})}}x=t=0;return D};q.unbind=function(x,z,A){var s,u,v,B,r,t;if(!x||x.nodeType===3||x.nodeType===8){return q}s=x[h];if(s){t=p[s];if(z){z=z.split(" ");v=z.length;while(v--){r=z[v];u=t[r];if(u){if(A){B=u.length;while(B--){if(u[B].func===A){u.splice(B,1)}}}if(!A||u.length===0){delete t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}}}}else{for(r in t){u=t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}t={}}for(r in t){return q}delete p[s];try{delete x[h]}catch(y){x[h]=null}}return q};q.fire=function(u,s,r){var v,t;if(!u||u.nodeType===3||u.nodeType===8){return q}t=a(null,r);t.type=s;do{v=u[h];if(v){j(t,v)}u=u.parentNode||u.ownerDocument||u.defaultView||u.parentWindow}while(u&&!t.isPropagationStopped());return q};q.clean=function(u){var s,r,t=q.unbind;if(!u||u.nodeType===3||u.nodeType===8){return q}if(u[h]){t(u)}if(!u.getElementsByTagName){u=u.document}if(u&&u.getElementsByTagName){t(u);r=u.getElementsByTagName("*");s=r.length;while(s--){u=r[s];if(u[h]){t(u)}}}return q};q.callNativeHandler=function(s,r){if(p){p[s][r.type].nativeHandler(r)}};q.destory=function(){p={}};q.add=function(v,s,u,t){if(typeof(v)==="string"){v=document.getElementById(v)}if(v&&v instanceof Array){var r=v.length;while(r--){q.add(v[r],s,u,t)}return}if(s==="init"){s="ready"}return q.bind(v,s instanceof Array?s.join(" "):s,u,t)};q.remove=function(v,s,u,t){if(!v){return q}if(typeof(v)==="string"){v=document.getElementById(v)}if(v instanceof Array){var r=v.length;while(r--){q.remove(v[r],s,u,t)}return q}return q.unbind(v,s instanceof Array?s.join(" "):s,u)};q.clear=function(r){if(typeof(r)==="string"){r=document.getElementById(r)}return q.clean(r)};q.cancel=function(r){if(r){q.prevent(r);q.stop(r)}return false};q.prevent=function(r){if(!r.preventDefault){r=a(r)}r.preventDefault();return false};q.stop=function(r){if(!r.stopPropagation){r=a(r)}r.stopPropagation();return false}}b.EventUtils=f;b.Event=new f(function(i){return function(j){tinymce.dom.Event.callNativeHandler(i,j)}});b.Event.bind(window,"ready",function(){});b=0})(tinymce.dom,"data-mce-expando");tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(e){var g=e.each,d=e.is,f=e.isWebKit,b=e.isIE,h=e.html.Entities,c=/^([a-z0-9],?)+$/i,a=/^[ \t\r\n]*$/;e.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(o,l){var k=this,i,j,n;k.doc=o;k.win=window;k.files={};k.cssFlicker=false;k.counter=0;k.stdMode=!e.isIE||o.documentMode>=8;k.boxModel=!e.isIE||o.compatMode=="CSS1Compat"||k.stdMode;k.hasOuterHTML="outerHTML" in o.createElement("a");k.settings=l=e.extend({keep_values:false,hex_colors:1},l);k.schema=l.schema;k.styles=new e.html.Styles({url_converter:l.url_converter,url_converter_scope:l.url_converter_scope},l.schema);if(e.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(m){k.cssFlicker=true}}k.fixDoc(o);k.events=l.ownEvents?new e.dom.EventUtils(l.proxy):e.dom.Event;e.addUnload(k.destroy,k);n=l.schema?l.schema.getBlockElements():{};k.isBlock=function(q){if(!q){return false}var p=q.nodeType;if(p){return !!(p===1&&n[q.nodeName])}return !!n[q]}},fixDoc:function(k){var j=this.settings,i;if(b&&!e.isIE11&&j.schema){("abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video").replace(/\w+/g,function(l){k.createElement(l)});for(i in j.schema.getCustomElements()){k.createElement(i)}}},clone:function(k,i){var j=this,m,l;if(!b||e.isIE11||k.nodeType!==1||i){return k.cloneNode(i)}l=j.doc;if(!i){m=l.createElement(k.nodeName);g(j.getAttribs(k),function(n){j.setAttrib(m,n.nodeName,j.getAttrib(k,n.nodeName))});return m}return m.firstChild},getRoot:function(){var i=this,j=i.settings;return(j&&i.get(j.root_element))||i.doc.body},getViewPort:function(j){var k,i;j=!j?this.win:j;k=j.document;i=this.boxModel?k.documentElement:k.body;return{x:j.pageXOffset||i.scrollLeft,y:j.pageYOffset||i.scrollTop,w:j.innerWidth||i.clientWidth,h:j.innerHeight||i.clientHeight}},getRect:function(l){var k,i=this,j;l=i.get(l);k=i.getPos(l);j=i.getSize(l);return{x:k.x,y:k.y,w:j.w,h:j.h}},getSize:function(l){var j=this,i,k;l=j.get(l);i=j.getStyle(l,"width");k=j.getStyle(l,"height");if(i.indexOf("px")===-1){i=0}if(k.indexOf("px")===-1){k=0}return{w:parseInt(i,10)||l.offsetWidth||l.clientWidth,h:parseInt(k,10)||l.offsetHeight||l.clientHeight}},getParent:function(k,j,i){return this.getParents(k,j,i,false)},getParents:function(s,m,k,q){var j=this,i,l=j.settings,p=[];s=j.get(s);q=q===undefined;if(l.strict_root){k=k||j.getRoot()}if(d(m,"string")){i=m;if(m==="*"){m=function(o){return o.nodeType==1}}else{m=function(o){return j.is(o,i)}}}while(s){if(s==k||!s.nodeType||s.nodeType===9){break}if(!m||m(s)){if(q){p.push(s)}else{return s}}s=s.parentNode}return q?p:null},get:function(i){var j;if(i&&this.doc&&typeof(i)=="string"){j=i;i=this.doc.getElementById(i);if(i&&i.id!==j){return this.doc.getElementsByName(j)[1]}}return i},getNext:function(j,i){return this._findSib(j,i,"nextSibling")},getPrev:function(j,i){return this._findSib(j,i,"previousSibling")},select:function(k,j){var i=this;return e.dom.Sizzle(k,i.get(j)||i.get(i.settings.root_element)||i.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(c.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return e.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(l,o,i,k,m){var j=this;return this.run(l,function(r){var q,n;q=d(o,"string")?j.doc.createElement(o):o;j.setAttribs(q,i);if(k){if(k.nodeType){q.appendChild(k)}else{j.setHTML(q,k)}}return !m?r.appendChild(q):q})},create:function(k,i,j){return this.add(this.doc.createElement(k),k,i,j,1)},createHTML:function(q,i,m){var p="",l=this,j;p+="<"+q;for(j in i){if(i.hasOwnProperty(j)){p+=" "+j+'="'+l.encode(i[j])+'"'}}if(typeof(m)!="undefined"){return p+">"+m+"</"+q+">"}return p+" />"},remove:function(i,j){return this.run(i,function(l){var m,k=l.parentNode;if(!k){return null}if(j){while(m=l.firstChild){if(!e.isIE||m.nodeType!==3||m.nodeValue){k.insertBefore(m,l)}else{l.removeChild(m)}}}return k.removeChild(l)})},setStyle:function(l,i,j){var k=this;return k.run(l,function(o){var n,m;n=o.style;i=i.replace(/-(\D)/g,function(q,p){return p.toUpperCase()});if(k.pixelStyles.test(i)&&(e.is(j,"number")||/^[\-0-9\.]+$/.test(j))){j+="px"}switch(i){case"opacity":if(b&&!e.isIE11){n.filter=j===""?"":"alpha(opacity="+(j*100)+")";if(!l.currentStyle||!l.currentStyle.hasLayout){n.display="inline-block"}}n[i]=n["-moz-opacity"]=n["-khtml-opacity"]=j||"";break;case"float":(b&&!e.isIE11)?n.styleFloat=j:n.cssFloat=j;break;default:n[i]=j||""}if(k.settings.update_styles){k.setAttrib(o,"data-mce-style")}})},getStyle:function(l,i,k){l=this.get(l);if(!l){return}if(this.doc.defaultView&&k){i=i.replace(/[A-Z]/g,function(m){return"-"+m});try{return this.doc.defaultView.getComputedStyle(l,null).getPropertyValue(i)}catch(j){return null}}i=i.replace(/-(\D)/g,function(n,m){return m.toUpperCase()});if(i=="float"){i=b?"styleFloat":"cssFloat"}if(l.currentStyle&&k){return l.currentStyle[i]}return l.style?l.style[i]:undefined},setStyles:function(l,m){var j=this,k=j.settings,i;i=k.update_styles;k.update_styles=0;g(m,function(o,p){j.setStyle(l,p,o)});k.update_styles=i;if(k.update_styles){j.setAttrib(l,k.cssText)}},removeAllAttribs:function(i){return this.run(i,function(l){var k,j=l.attributes;for(k=j.length-1;k>=0;k--){l.removeAttributeNode(j.item(k))}})},setAttrib:function(k,l,i){var j=this;if(!k||!l){return}if(j.settings.strict){l=l.toLowerCase()}return this.run(k,function(p){var o=j.settings;var m=p.getAttribute(l);if(i!==null){switch(l){case"style":if(!d(i,"string")){g(i,function(q,r){j.setStyle(p,r,q)});return}if(o.keep_values){if(i&&!j._isRes(i)){p.setAttribute("data-mce-style",i,2)}else{p.removeAttribute("data-mce-style",2)}}p.style.cssText=i;break;case"class":p.className=i||"";break;case"src":case"href":if(o.keep_values){if(o.url_converter){i=o.url_converter.call(o.url_converter_scope||j,i,l,p)}j.setAttrib(p,"data-mce-"+l,i,2)}break;case"shape":p.setAttribute("data-mce-style",i);break}}if(d(i)&&i!==null&&i.length!==0){p.setAttribute(l,""+i,2)}else{p.removeAttribute(l,2)}if(tinyMCE.activeEditor&&m!=i){var n=tinyMCE.activeEditor;n.onSetAttrib.dispatch(n,p,l,i)}})},setAttribs:function(j,k){var i=this;return this.run(j,function(l){g(k,function(m,o){i.setAttrib(l,o,m)})})},getAttrib:function(m,o,k){var i,j=this,l;m=j.get(m);if(!m||m.nodeType!==1){return k===l?false:k}if(!d(k)){k=""}if(/^(src|href|style|coords|shape)$/.test(o)){i=m.getAttribute("data-mce-"+o);if(i){return i}}if(b&&j.props[o]){i=m[j.props[o]];i=i&&i.nodeValue?i.nodeValue:i}if(!i){i=m.getAttribute(o,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(o)){if(m[j.props[o]]===true&&i===""){return o}return i?o:""}if(m.nodeName==="FORM"&&m.getAttributeNode(o)){return m.getAttributeNode(o).nodeValue}if(o==="style"){i=i||m.style.cssText;if(i){i=j.serializeStyle(j.parseStyle(i),m.nodeName);if(j.settings.keep_values&&!j._isRes(i)){m.setAttribute("data-mce-style",i)}}}if(f&&o==="class"&&i){i=i.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(o){case"rowspan":case"colspan":if(i===1){i=""}break;case"size":if(i==="+0"||i===20||i===0){i=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(i===0){i=""}break;case"hspace":if(i===-1){i=""}break;case"maxlength":case"tabindex":if(i===32768||i===2147483647||i==="32768"){i=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(i===65535){return o}return k;case"shape":i=i.toLowerCase();break;default:if(o.indexOf("on")===0&&i){i=e._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+i)}}}return(i!==l&&i!==null&&i!=="")?""+i:k},getPos:function(q,l){var j=this,i=0,p=0,m,o=j.doc,k;q=j.get(q);l=l||o.body;if(q){if(q.getBoundingClientRect){q=q.getBoundingClientRect();m=j.boxModel?o.documentElement:o.body;i=q.left+(o.documentElement.scrollLeft||o.body.scrollLeft)-m.clientTop;p=q.top+(o.documentElement.scrollTop||o.body.scrollTop)-m.clientLeft;return{x:i,y:p}}k=q;while(k&&k!=l&&k.nodeType){i+=k.offsetLeft||0;p+=k.offsetTop||0;k=k.offsetParent}k=q.parentNode;while(k&&k!=l&&k.nodeType){i-=k.scrollLeft||0;p-=k.scrollTop||0;k=k.parentNode}}return{x:i,y:p}},parseStyle:function(i){return this.styles.parse(i)},serializeStyle:function(j,i){return this.styles.serialize(j,i)},addStyle:function(j){var k=this.doc,i;styleElm=k.getElementById("mceDefaultStyles");if(!styleElm){styleElm=k.createElement("style"),styleElm.id="mceDefaultStyles";styleElm.type="text/css";i=k.getElementsByTagName("head")[0];if(i.firstChild){i.insertBefore(styleElm,i.firstChild)}else{i.appendChild(styleElm)}}if(styleElm.styleSheet){styleElm.styleSheet.cssText+=j}else{styleElm.appendChild(k.createTextNode(j))}},loadCSS:function(i){var k=this,l=k.doc,j;if(!i){i=""}j=l.getElementsByTagName("head")[0];g(i.split(","),function(m){var n;if(k.files[m]){return}k.files[m]=true;n=k.create("link",{rel:"stylesheet",href:e._addVer(m)});if(b&&!e.isIE11&&l.documentMode&&l.recalc){n.onload=function(){if(l.recalc){l.recalc()}n.onload=null}}j.appendChild(n)})},addClass:function(i,j){return this.run(i,function(k){var l;if(!j){return 0}if(this.hasClass(k,j)){return k.className}l=this.removeClass(k,j);return k.className=(l!=""?(l+" "):"")+j})},removeClass:function(k,l){var i=this,j;return i.run(k,function(n){var m;if(i.hasClass(n,l)){if(!j){j=new RegExp("(^|\\s+)"+l+"(\\s+|$)","g")}m=n.className.replace(j," ");m=e.trim(m!=" "?m:"");n.className=m;if(!m){n.removeAttribute("class");n.removeAttribute("className")}return m}return n.className})},hasClass:function(j,i){j=this.get(j);if(!j||!i){return false}return(" "+j.className+" ").indexOf(" "+i+" ")!==-1},show:function(i){return this.setStyle(i,"display","block")},hide:function(i){return this.setStyle(i,"display","none")},isHidden:function(i){i=this.get(i);return !i||i.style.display=="none"||this.getStyle(i,"display")=="none"},uniqueId:function(i){return(!i?"mce_":i)+(this.counter++)},setHTML:function(k,j){var i=this;return i.run(k,function(m){if(b){while(m.firstChild){m.removeChild(m.firstChild)}try{m.innerHTML="<br />"+j;m.removeChild(m.firstChild)}catch(l){var n=i.create("div");n.innerHTML="<br />"+j;g(e.grep(n.childNodes),function(p,o){if(o&&m.canHaveHTML){m.appendChild(p)}})}}else{m.innerHTML=j}return j})},getOuterHTML:function(k){var j,i=this;k=i.get(k);if(!k){return null}if(k.nodeType===1&&i.hasOuterHTML){return k.outerHTML}j=(k.ownerDocument||i.doc).createElement("body");j.appendChild(k.cloneNode(true));return j.innerHTML},setOuterHTML:function(l,j,m){var i=this;function k(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){i.insertAfter(s.cloneNode(true),p);s=s.previousSibling}i.remove(p)}return this.run(l,function(o){o=i.get(o);if(o.nodeType==1){m=m||o.ownerDocument||i.doc;if(b){try{if(b&&o.nodeType==1){o.outerHTML=j}else{k(o,j,m)}}catch(n){k(o,j,m)}}else{k(o,j,m)}}})},decode:h.decode,encode:h.encodeAllRaw,insertAfter:function(i,j){j=this.get(j);return this.run(i,function(l){var k,m;k=j.parentNode;m=j.nextSibling;if(m){k.insertBefore(l,m)}else{k.appendChild(l)}return l})},replace:function(m,l,i){var j=this;if(d(l,"array")){m=m.cloneNode(true)}return j.run(l,function(k){if(i){g(e.grep(k.childNodes),function(n){m.appendChild(n)})}return k.parentNode.replaceChild(m,k)})},rename:function(l,i){var k=this,j;if(l.nodeName!=i.toUpperCase()){j=k.create(i);g(k.getAttribs(l),function(m){k.setAttrib(j,m.nodeName,k.getAttrib(l,m.nodeName))});k.replace(j,l,1)}return j||l},findCommonAncestor:function(k,i){var l=k,j;while(l){j=i;while(j&&l!=j){j=j.parentNode}if(l==j){break}l=l.parentNode}if(!l&&k.ownerDocument){return k.ownerDocument.documentElement}return l},toHex:function(i){var k=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(i);function j(l){l=parseInt(l,10).toString(16);return l.length>1?l:"0"+l}if(k){i="#"+j(k[1])+j(k[2])+j(k[3]);return i}return i},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(i){g(i.imports,function(s){q(s)});g(i.cssRules||i.rules,function(t){switch(t.type||1){case 1:if(t.selectorText){g(t.selectorText.split(","),function(r){r=r.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(r)||!/\.[\w\-]+$/.test(r)){return}l=r;r=e._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",r);if(p&&!(r=p(r,l))){return}if(!o[r]){j.push({"class":r});o[r]=1}})}break;case 3:try{q(t.styleSheet)}catch(s){}break}})}try{g(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(l,k,j){var i=this,m;if(i.doc&&typeof(l)==="string"){l=i.get(l)}if(!l){return false}j=j||this;if(!l.nodeType&&(l.length||l.length===0)){m=[];g(l,function(o,n){if(o){if(typeof(o)=="string"){o=i.doc.getElementById(o)}m.push(k.call(j,o,n))}});return m}return k.call(j,l)},getAttribs:function(j){var i;j=this.get(j);if(!j){return[]}if(b){i=[];if(j.nodeName=="OBJECT"){return j.attributes}if(j.nodeName==="OPTION"&&this.getAttrib(j,"selected")){i.push({specified:1,nodeName:"selected"})}j.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(k){i.push({specified:1,nodeName:k})});return i}return j.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p=0;m=m.firstChild;if(m){j=new e.dom.TreeWalker(m,m.parentNode);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){if(l==="br"){p++;continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if(q==8){return false}if((q===3&&!a.test(m.nodeValue))){return false}}while(m=j.next())}return p<=1},destroy:function(j){var i=this;i.win=i.doc=i.root=i.events=i.frag=null;if(!j){e.removeUnload(i.destroy)}},createRng:function(){var i=this.doc;return i.createRange?i.createRange():new e.dom.Range(this)},nodeIndex:function(m,n){var i=0,k,l,j;if(m){for(k=m.nodeType,m=m.previousSibling,l=m;m;m=m.previousSibling){j=m.nodeType;if(n&&j==3){if(j==k||!m.nodeValue.length){continue}}i++;k=j}}return i},split:function(m,l,p){var q=this,i=q.createRng(),n,k,o;function j(v){var t,s=v.childNodes,u=v.nodeType;function x(A){var z=A.previousSibling&&A.previousSibling.nodeName=="SPAN";var y=A.nextSibling&&A.nextSibling.nodeName=="SPAN";return z&&y}if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=s.length-1;t>=0;t--){j(s[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){var r=e.trim(v.nodeValue).length;if(!q.isBlock(v.parentNode)||r>0||r===0&&x(v)){return}}else{if(u==1){s=v.childNodes;if(s.length==1&&s[0]&&s[0].nodeType==1&&s[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(s[0],v)}if(s.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}q.remove(v)}return v}if(m&&l){i.setStart(m.parentNode,q.nodeIndex(m));i.setEnd(l.parentNode,q.nodeIndex(l));n=i.extractContents();i=q.createRng();i.setStart(l.parentNode,q.nodeIndex(l)+1);i.setEnd(m.parentNode,q.nodeIndex(m)+1);k=i.extractContents();o=m.parentNode;o.insertBefore(j(n),m);if(p){o.replaceChild(p,l)}else{o.insertBefore(l,m)}o.insertBefore(j(k),m);q.remove(m);return p||l}},bind:function(l,i,k,j){return this.events.add(l,i,k,j||this)},unbind:function(k,i,j){return this.events.remove(k,i,j)},fire:function(k,j,i){return this.events.fire(k,j,i)},getContentEditable:function(j){var i;if(j.nodeType!=1){return null}i=j.getAttribute("data-mce-contenteditable");if(i&&i!=="inherit"){return i}return j.contentEditable!=="inherit"?j.contentEditable:null},_findSib:function(l,i,j){var k=this,m=i;if(l){if(d(m,"string")){m=function(n){return k.is(n,i)}}for(l=l[j];l;l=l[j]){if(m(l)){return l}}}return null},_isRes:function(i){return/^(top|left|bottom|right|width|height)/i.test(i)||/;\s*(top|left|bottom|right|width|height)/i.test(i)}});e.DOM=new e.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var O=this,e=c.doc,U=0,F=1,j=2,E=true,S=false,W="startOffset",h="startContainer",Q="endContainer",A="endOffset",k=tinymce.extend,n=c.nodeIndex;k(O,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:E,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:J,setEndBefore:K,setEndAfter:u,collapse:B,selectNode:y,selectNodeContents:G,compareBoundaryPoints:v,deleteContents:p,extractContents:I,cloneContents:d,insertNode:D,surroundContents:N,cloneRange:L,toStringIE:T});function x(){return e.createDocumentFragment()}function q(X,t){C(E,X,t)}function s(X,t){C(S,X,t)}function g(t){q(t.parentNode,n(t))}function J(t){q(t.parentNode,n(t)+1)}function K(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function B(t){if(t){O[Q]=O[h];O[A]=O[W]}else{O[h]=O[Q];O[W]=O[A]}O.collapsed=E}function y(t){g(t);u(t)}function G(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(aa,t){var ad=O[h],Y=O[W],ac=O[Q],X=O[A],ab=t.startContainer,af=t.startOffset,Z=t.endContainer,ae=t.endOffset;if(aa===0){return H(ad,Y,ab,af)}if(aa===1){return H(ac,X,ab,af)}if(aa===2){return H(ac,X,Z,ae)}if(aa===3){return H(ad,Y,Z,ae)}}function p(){l(j)}function I(){return l(U)}function d(){return l(F)}function D(aa){var X=this[h],t=this[W],Z,Y;if((X.nodeType===3||X.nodeType===4)&&X.nodeValue){if(!t){X.parentNode.insertBefore(aa,X)}else{if(t>=X.nodeValue.length){c.insertAfter(aa,X)}else{Z=X.splitText(t);X.parentNode.insertBefore(aa,Z)}}}else{if(X.childNodes.length>0){Y=X.childNodes[t]}if(Y){X.insertBefore(aa,Y)}else{X.appendChild(aa)}}}function N(X){var t=O.extractContents();O.insertNode(X);X.appendChild(t);O.selectNode(X)}function L(){return k(new b(c),{startContainer:O[h],startOffset:O[W],endContainer:O[Q],endOffset:O[A],collapsed:O.collapsed,commonAncestorContainer:O.commonAncestorContainer})}function P(t,X){var Y;if(t.nodeType==3){return t}if(X<0){return t}Y=t.firstChild;while(Y&&X>0){--X;Y=Y.nextSibling}if(Y){return Y}return t}function m(){return(O[h]==O[Q]&&O[W]==O[A])}function H(Z,ab,X,aa){var ac,Y,t,ad,af,ae;if(Z==X){if(ab==aa){return 0}if(ab<aa){return -1}return 1}ac=X;while(ac&&ac.parentNode!=Z){ac=ac.parentNode}if(ac){Y=0;t=Z.firstChild;while(t!=ac&&Y<ab){Y++;t=t.nextSibling}if(ab<=Y){return -1}return 1}ac=Z;while(ac&&ac.parentNode!=X){ac=ac.parentNode}if(ac){Y=0;t=X.firstChild;while(t!=ac&&Y<aa){Y++;t=t.nextSibling}if(Y<aa){return -1}return 1}ad=c.findCommonAncestor(Z,X);af=Z;while(af&&af.parentNode!=ad){af=af.parentNode}if(!af){af=ad}ae=X;while(ae&&ae.parentNode!=ad){ae=ae.parentNode}if(!ae){ae=ad}if(af==ae){return 0}t=ad.firstChild;while(t){if(t==af){return -1}if(t==ae){return 1}t=t.nextSibling}}function C(X,aa,Z){var t,Y;if(X){O[h]=aa;O[W]=Z}else{O[Q]=aa;O[A]=Z}t=O[Q];while(t.parentNode){t=t.parentNode}Y=O[h];while(Y.parentNode){Y=Y.parentNode}if(Y==t){if(H(O[h],O[W],O[Q],O[A])>0){O.collapse(X)}}else{O.collapse(X)}O.collapsed=m();O.commonAncestorContainer=c.findCommonAncestor(O[h],O[Q])}function l(ad){var ac,Z=0,af=0,X,ab,Y,aa,t,ae;if(O[h]==O[Q]){return f(ad)}for(ac=O[Q],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[h]){return r(ac,ad)}++Z}for(ac=O[h],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[Q]){return V(ac,ad)}++af}ab=af-Z;Y=O[h];while(ab>0){Y=Y.parentNode;ab--}aa=O[Q];while(ab<0){aa=aa.parentNode;ab++}for(t=Y.parentNode,ae=aa.parentNode;t!=ae;t=t.parentNode,ae=ae.parentNode){Y=t;aa=ae}return o(Y,aa,ad)}function f(ac){var ae,af,t,Y,Z,ad,aa,X,ab;if(ac!=j){ae=x()}if(O[W]==O[A]){return ae}if(O[h].nodeType==3){af=O[h].nodeValue;t=af.substring(O[W],O[A]);if(ac!=F){Y=O[h];X=O[W];ab=O[A]-O[W];if(X===0&&ab>=Y.nodeValue.length-1){Y.parentNode.removeChild(Y)}else{Y.deleteData(X,ab)}O.collapse(E)}if(ac==j){return}if(t.length>0){ae.appendChild(e.createTextNode(t))}return ae}Y=P(O[h],O[W]);Z=O[A]-O[W];while(Y&&Z>0){ad=Y.nextSibling;aa=z(Y,ac);if(ae){ae.appendChild(aa)}--Z;Y=ad}if(ac!=F){O.collapse(E)}return ae}function r(ad,aa){var ac,ab,X,t,Z,Y;if(aa!=j){ac=x()}ab=i(ad,aa);if(ac){ac.appendChild(ab)}X=n(ad);t=X-O[W];if(t<=0){if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}ab=ad.previousSibling;while(t>0){Z=ab.previousSibling;Y=z(ab,aa);if(ac){ac.insertBefore(Y,ac.firstChild)}--t;ab=Z}if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}function V(ab,aa){var ad,X,ac,t,Z,Y;if(aa!=j){ad=x()}ac=R(ab,aa);if(ad){ad.appendChild(ac)}X=n(ab);++X;t=O[A]-X;ac=ab.nextSibling;while(ac&&t>0){Z=ac.nextSibling;Y=z(ac,aa);if(ad){ad.appendChild(Y)}--t;ac=Z}if(aa!=F){O.setStartAfter(ab);O.collapse(E)}return ad}function o(ab,t,ae){var Y,ag,aa,ac,ad,X,af,Z;if(ae!=j){ag=x()}Y=R(ab,ae);if(ag){ag.appendChild(Y)}aa=ab.parentNode;ac=n(ab);ad=n(t);++ac;X=ad-ac;af=ab.nextSibling;while(X>0){Z=af.nextSibling;Y=z(af,ae);if(ag){ag.appendChild(Y)}af=Z;--X}Y=i(t,ae);if(ag){ag.appendChild(Y)}if(ae!=F){O.setStartAfter(ab);O.collapse(E)}return ag}function i(ac,ad){var Y=P(O[Q],O[A]-1),ae,ab,aa,t,X,Z=Y!=O[Q];if(Y==ac){return M(Y,Z,S,ad)}ae=Y.parentNode;ab=M(ae,S,S,ad);while(ae){while(Y){aa=Y.previousSibling;t=M(Y,Z,S,ad);if(ad!=j){ab.insertBefore(t,ab.firstChild)}Z=E;Y=aa}if(ae==ac){return ab}Y=ae.previousSibling;ae=ae.parentNode;X=M(ae,S,S,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function R(ac,ad){var Z=P(O[h],O[W]),aa=Z!=O[h],ae,ab,Y,t,X;if(Z==ac){return M(Z,aa,E,ad)}ae=Z.parentNode;ab=M(ae,S,E,ad);while(ae){while(Z){Y=Z.nextSibling;t=M(Z,aa,E,ad);if(ad!=j){ab.appendChild(t)}aa=E;Z=Y}if(ae==ac){return ab}Z=ae.nextSibling;ae=ae.parentNode;X=M(ae,S,E,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function M(t,aa,ad,ae){var Z,Y,ab,X,ac;if(aa){return z(t,ae)}if(t.nodeType==3){Z=t.nodeValue;if(ad){X=O[W];Y=Z.substring(X);ab=Z.substring(0,X)}else{X=O[A];Y=Z.substring(0,X);ab=Z.substring(X)}if(ae!=F){t.nodeValue=ab}if(ae==j){return}ac=c.clone(t,S);ac.nodeValue=Y;return ac}if(ae==j){return}return c.clone(t,S)}function z(X,t){if(t!=j){return t==F?c.clone(X,E):X}X.parentNode.removeChild(X)}function T(){return c.create("body",null,d()).outerText}return O}a.Range=b;b.prototype.toString=function(){return this.toStringIE()}})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",1)===0||s!=k.parentElement()){break}o++}}else{k.collapse(true);o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",-1)===0||s!=k.parentElement()){break}o++}}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p&&t.nodeType!==9){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,v,q,t,s=d.dom.doc,m=s.body,r,u;function j(C){var y,B,x,A,z;x=h.create("a");y=C?k:v;B=C?p:q;A=n.duplicate();if(y==s||y==s.documentElement){y=m;B=0}if(y.nodeType==3){y.parentNode.insertBefore(x,y);A.moveToElementText(x);A.moveStart("character",B);h.remove(x);n.setEndPoint(C?"StartToStart":"EndToEnd",A)}else{z=y.childNodes;if(z.length){if(B>=z.length){h.insertAfter(x,z[z.length-1])}else{y.insertBefore(x,z[B])}A.moveToElementText(x)}else{if(y.canHaveHTML){y.innerHTML="<span>\uFEFF</span>";x=y.firstChild;A.moveToElementText(x);A.collapse(f)}}n.setEndPoint(C?"StartToStart":"EndToEnd",A);h.remove(x)}}k=i.startContainer;p=i.startOffset;v=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==v&&k.nodeType==1){if(p==q&&!k.hasChildNodes()){if(k.canHaveHTML){t=k.previousSibling;if(t&&!t.hasChildNodes()&&h.isBlock(t)){t.innerHTML="\uFEFF"}else{t=null}k.innerHTML="<span>\uFEFF</span><span>\uFEFF</span>";n.moveToElementText(k.lastChild);n.select();h.doc.selection.clear();k.innerHTML="";if(t){t.innerHTML=""}return}else{p=h.nodeIndex(k);k=k.parentNode}}if(p==q-1){try{u=k.childNodes[p];l=m.createControlRange();l.addElement(u);l.select();r=d.getRng();if(r.item&&u===r.item(0)){return}}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var n=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i="sizcache",o=0,r=Object.prototype.toString,h=false,g=true,q=/\\/g,u=/\r\n/g,x=/\W/;[0,0].sort(function(){g=false;return 0});var d=function(C,e,F,G){F=F||[];e=e||document;var I=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!C||typeof C!=="string"){return F}var z,K,N,y,J,M,L,E,B=true,A=d.isXML(e),D=[],H=C;do{n.exec("");z=n.exec(H);if(z){H=z[3];D.push(z[1]);if(z[2]){y=z[3];break}}}while(z);if(D.length>1&&j.exec(C)){if(D.length===2&&k.relative[D[0]]){K=s(D[0]+D[1],e,G)}else{K=k.relative[D[0]]?[e]:d(D.shift(),e);while(D.length){C=D.shift();if(k.relative[C]){C+=D.shift()}K=s(C,K,G)}}}else{if(!G&&D.length>1&&e.nodeType===9&&!A&&k.match.ID.test(D[0])&&!k.match.ID.test(D[D.length-1])){J=d.find(D.shift(),e,A);e=J.expr?d.filter(J.expr,J.set)[0]:J.set[0]}if(e){J=G?{expr:D.pop(),set:l(G)}:d.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&e.parentNode?e.parentNode:e,A);K=J.expr?d.filter(J.expr,J.set):J.set;if(D.length>0){N=l(K)}else{B=false}while(D.length){M=D.pop();L=M;if(!k.relative[M]){M=""}else{L=D.pop()}if(L==null){L=e}k.relative[M](N,L,A)}}else{N=D=[]}}if(!N){N=K}if(!N){d.error(M||C)}if(r.call(N)==="[object Array]"){if(!B){F.push.apply(F,N)}else{if(e&&e.nodeType===1){for(E=0;N[E]!=null;E++){if(N[E]&&(N[E]===true||N[E].nodeType===1&&d.contains(e,N[E]))){F.push(K[E])}}}else{for(E=0;N[E]!=null;E++){if(N[E]&&N[E].nodeType===1){F.push(K[E])}}}}}else{l(N,F)}if(y){d(y,I,F,G);d.uniqueSort(F)}return F};d.uniqueSort=function(y){if(p){h=g;y.sort(p);if(h){for(var e=1;e<y.length;e++){if(y[e]===y[e-1]){y.splice(e--,1)}}}}return y};d.matches=function(e,y){return d(e,null,null,y)};d.matchesSelector=function(e,y){return d(y,null,null,[e]).length>0};d.find=function(E,e,F){var D,z,B,A,C,y;if(!E){return[]}for(z=0,B=k.order.length;z<B;z++){C=k.order[z];if((A=k.leftMatch[C].exec(E))){y=A[1];A.splice(1,1);if(y.substr(y.length-1)!=="\\"){A[1]=(A[1]||"").replace(q,"");D=k.find[C](A,e,F);if(D!=null){E=E.replace(k.match[C],"");break}}}}if(!D){D=typeof e.getElementsByTagName!=="undefined"?e.getElementsByTagName("*"):[]}return{set:D,expr:E}};d.filter=function(I,H,L,B){var D,e,G,N,K,y,A,C,J,z=I,M=[],F=H,E=H&&H[0]&&d.isXML(H[0]);while(I&&H.length){for(G in k.filter){if((D=k.leftMatch[G].exec(I))!=null&&D[2]){y=k.filter[G];A=D[1];e=false;D.splice(1,1);if(A.substr(A.length-1)==="\\"){continue}if(F===M){M=[]}if(k.preFilter[G]){D=k.preFilter[G](D,F,L,M,B,E);if(!D){e=N=true}else{if(D===true){continue}}}if(D){for(C=0;(K=F[C])!=null;C++){if(K){N=y(K,D,C,F);J=B^N;if(L&&N!=null){if(J){e=true}else{F[C]=false}}else{if(J){M.push(K);e=true}}}}}if(N!==undefined){if(!L){F=M}I=I.replace(k.match[G],"");if(!e){return[]}break}}}if(I===z){if(e==null){d.error(I)}else{break}}z=I}return F};d.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)};var b=d.getText=function(B){var z,A,e=B.nodeType,y="";if(e){if(e===1||e===9||e===11){if(typeof B.textContent==="string"){return B.textContent}else{if(typeof B.innerText==="string"){return B.innerText.replace(u,"")}else{for(B=B.firstChild;B;B=B.nextSibling){y+=b(B)}}}}else{if(e===3||e===4){return B.nodeValue}}}else{for(z=0;(A=B[z]);z++){if(A.nodeType!==8){y+=b(A)}}}return y};var k=d.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")},type:function(e){return e.getAttribute("type")}},relative:{"+":function(D,y){var A=typeof y==="string",C=A&&!x.test(y),E=A&&!C;if(C){y=y.toLowerCase()}for(var z=0,e=D.length,B;z<e;z++){if((B=D[z])){while((B=B.previousSibling)&&B.nodeType!==1){}D[z]=E||B&&B.nodeName.toLowerCase()===y?B||false:B===y}}if(E){d.filter(y,D,true)}},">":function(D,y){var C,B=typeof y==="string",z=0,e=D.length;if(B&&!x.test(y)){y=y.toLowerCase();for(;z<e;z++){C=D[z];if(C){var A=C.parentNode;D[z]=A.nodeName.toLowerCase()===y?A:false}}}else{for(;z<e;z++){C=D[z];if(C){D[z]=B?C.parentNode:C.parentNode===y}}if(B){d.filter(y,D,true)}}},"":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("parentNode",y,z,A,B,C)},"~":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("previousSibling",y,z,A,B,C)}},find:{ID:function(y,z,A){if(typeof z.getElementById!=="undefined"&&!A){var e=z.getElementById(y[1]);return e&&e.parentNode?[e]:[]}},NAME:function(z,C){if(typeof C.getElementsByName!=="undefined"){var y=[],B=C.getElementsByName(z[1]);for(var A=0,e=B.length;A<e;A++){if(B[A].getAttribute("name")===z[1]){y.push(B[A])}}return y.length===0?null:y}},TAG:function(e,y){if(typeof y.getElementsByTagName!=="undefined"){return y.getElementsByTagName(e[1])}}},preFilter:{CLASS:function(A,y,z,e,D,E){A=" "+A[1].replace(q,"")+" ";if(E){return A}for(var B=0,C;(C=y[B])!=null;B++){if(C){if(D^(C.className&&(" "+C.className+" ").replace(/[\t\n\r]/g," ").indexOf(A)>=0)){if(!z){e.push(C)}}else{if(z){y[B]=false}}}}return false},ID:function(e){return e[1].replace(q,"")},TAG:function(y,e){return y[1].replace(q,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){if(!e[2]){d.error(e[0])}e[2]=e[2].replace(/^\+|\s*/g,"");var y=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(y[1]+(y[2]||1))-0;e[3]=y[3]-0}else{if(e[2]){d.error(e[0])}}e[0]=o++;return e},ATTR:function(B,y,z,e,C,D){var A=B[1]=B[1].replace(q,"");if(!D&&k.attrMap[A]){B[1]=k.attrMap[A]}B[4]=(B[4]||B[5]||"").replace(q,"");if(B[2]==="~="){B[4]=" "+B[4]+" "}return B},PSEUDO:function(B,y,z,e,C){if(B[1]==="not"){if((n.exec(B[3])||"").length>1||/^\w/.test(B[3])){B[3]=d(B[3],null,null,y)}else{var A=d.filter(B[3],y,z,true^C);if(!z){e.push.apply(e,A)}return false}}else{if(k.match.POS.test(B[0])||k.match.CHILD.test(B[0])){return true}}return B},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){if(e.parentNode){e.parentNode.selectedIndex}return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(z,y,e){return !!d(e[3],z).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(z){var e=z.getAttribute("type"),y=z.type;return z.nodeName.toLowerCase()==="input"&&"text"===y&&(e===y||e===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"submit"===y.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"reset"===y.type},button:function(y){var e=y.nodeName.toLowerCase();return e==="input"&&"button"===y.type||e==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(y,e){return e===0},last:function(z,y,e,A){return y===A.length-1},even:function(y,e){return e%2===0},odd:function(y,e){return e%2===1},lt:function(z,y,e){return y<e[3]-0},gt:function(z,y,e){return y>e[3]-0},nth:function(z,y,e){return e[3]-0===y},eq:function(z,y,e){return e[3]-0===y}},filter:{PSEUDO:function(z,E,D,F){var e=E[1],y=k.filters[e];if(y){return y(z,D,E,F)}else{if(e==="contains"){return(z.textContent||z.innerText||b([z])||"").indexOf(E[3])>=0}else{if(e==="not"){var A=E[3];for(var C=0,B=A.length;C<B;C++){if(A[C]===z){return false}}return true}else{d.error(e)}}}},CHILD:function(z,B){var A,H,D,G,e,C,F,E=B[1],y=z;switch(E){case"only":case"first":while((y=y.previousSibling)){if(y.nodeType===1){return false}}if(E==="first"){return true}y=z;case"last":while((y=y.nextSibling)){if(y.nodeType===1){return false}}return true;case"nth":A=B[2];H=B[3];if(A===1&&H===0){return true}D=B[0];G=z.parentNode;if(G&&(G[i]!==D||!z.nodeIndex)){C=0;for(y=G.firstChild;y;y=y.nextSibling){if(y.nodeType===1){y.nodeIndex=++C}}G[i]=D}F=z.nodeIndex-H;if(A===0){return F===0}else{return(F%A===0&&F/A>=0)}}},ID:function(y,e){return y.nodeType===1&&y.getAttribute("id")===e},TAG:function(y,e){return(e==="*"&&y.nodeType===1)||!!y.nodeName&&y.nodeName.toLowerCase()===e},CLASS:function(y,e){return(" "+(y.className||y.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(C,A){var z=A[1],e=d.attr?d.attr(C,z):k.attrHandle[z]?k.attrHandle[z](C):C[z]!=null?C[z]:C.getAttribute(z),D=e+"",B=A[2],y=A[4];return e==null?B==="!=":!B&&d.attr?e!=null:B==="="?D===y:B==="*="?D.indexOf(y)>=0:B==="~="?(" "+D+" ").indexOf(y)>=0:!y?D&&e!==false:B==="!="?D!==y:B==="^="?D.indexOf(y)===0:B==="$="?D.substr(D.length-y.length)===y:B==="|="?D===y||D.substr(0,y.length+1)===y+"-":false},POS:function(B,y,z,C){var e=y[2],A=k.setFilters[e];if(A){return A(B,z,y,C)}}}};var j=k.match.POS,c=function(y,e){return"\\"+(e-0+1)};for(var f in k.match){k.match[f]=new RegExp(k.match[f].source+(/(?![^\[]*\])(?![^\(]*\))/.source));k.leftMatch[f]=new RegExp(/(^(?:.|\r|\n)*?)/.source+k.match[f].source.replace(/\\(\d+)/g,c))}k.match.globalPOS=j;var l=function(y,e){y=Array.prototype.slice.call(y,0);if(e){e.push.apply(e,y);return e}return y};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(v){l=function(B,A){var z=0,y=A||[];if(r.call(B)==="[object Array]"){Array.prototype.push.apply(y,B)}else{if(typeof B.length==="number"){for(var e=B.length;z<e;z++){y.push(B[z])}}else{for(;B[z];z++){y.push(B[z])}}}return y}}var p,m;if(document.documentElement.compareDocumentPosition){p=function(y,e){if(y===e){h=true;return 0}if(!y.compareDocumentPosition||!e.compareDocumentPosition){return y.compareDocumentPosition?-1:1}return y.compareDocumentPosition(e)&4?-1:1}}else{p=function(F,E){if(F===E){h=true;return 0}else{if(F.sourceIndex&&E.sourceIndex){return F.sourceIndex-E.sourceIndex}}var C,y,z=[],e=[],B=F.parentNode,D=E.parentNode,G=B;if(B===D){return m(F,E)}else{if(!B){return -1}else{if(!D){return 1}}}while(G){z.unshift(G);G=G.parentNode}G=D;while(G){e.unshift(G);G=G.parentNode}C=z.length;y=e.length;for(var A=0;A<C&&A<y;A++){if(z[A]!==e[A]){return m(z[A],e[A])}}return A===C?m(F,e[A],-1):m(z[A],E,1)};m=function(y,e,z){if(y===e){return z}var A=y.nextSibling;while(A){if(A===e){return -1}A=A.nextSibling}return 1}}(function(){var y=document.createElement("div"),z="script"+(new Date()).getTime(),e=document.documentElement;y.innerHTML="<a name='"+z+"'/>";e.insertBefore(y,e.firstChild);if(document.getElementById(z)){k.find.ID=function(B,C,D){if(typeof C.getElementById!=="undefined"&&!D){var A=C.getElementById(B[1]);return A?A.id===B[1]||typeof A.getAttributeNode!=="undefined"&&A.getAttributeNode("id").nodeValue===B[1]?[A]:undefined:[]}};k.filter.ID=function(C,A){var B=typeof C.getAttributeNode!=="undefined"&&C.getAttributeNode("id");return C.nodeType===1&&B&&B.nodeValue===A}}e.removeChild(y);e=y=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){k.find.TAG=function(y,C){var B=C.getElementsByTagName(y[1]);if(y[1]==="*"){var A=[];for(var z=0;B[z];z++){if(B[z].nodeType===1){A.push(B[z])}}B=A}return B}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){k.attrHandle.href=function(y){return y.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=d,A=document.createElement("div"),z="__sizzle__";A.innerHTML="<p class='TEST'></p>";if(A.querySelectorAll&&A.querySelectorAll(".TEST").length===0){return}d=function(L,C,G,K){C=C||document;if(!K&&!d.isXML(C)){var J=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(L);if(J&&(C.nodeType===1||C.nodeType===9)){if(J[1]){return l(C.getElementsByTagName(L),G)}else{if(J[2]&&k.find.CLASS&&C.getElementsByClassName){return l(C.getElementsByClassName(J[2]),G)}}}if(C.nodeType===9){if(L==="body"&&C.body){return l([C.body],G)}else{if(J&&J[3]){var F=C.getElementById(J[3]);if(F&&F.parentNode){if(F.id===J[3]){return l([F],G)}}else{return l([],G)}}}try{return l(C.querySelectorAll(L),G)}catch(H){}}else{if(C.nodeType===1&&C.nodeName.toLowerCase()!=="object"){var D=C,E=C.getAttribute("id"),B=E||z,N=C.parentNode,M=/^\s*[+~]/.test(L);if(!E){C.setAttribute("id",B)}else{B=B.replace(/'/g,"\\$&")}if(M&&N){C=C.parentNode}try{if(!M||N){return l(C.querySelectorAll("[id='"+B+"'] "+L),G)}}catch(I){}finally{if(!E){D.removeAttribute("id")}}}}}return e(L,C,G,K)};for(var y in e){d[y]=e[y]}A=null})()}(function(){var e=document.documentElement,z=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(z){var B=!z.call(document.createElement("div"),"div"),y=false;try{z.call(document.documentElement,"[test!='']:sizzle")}catch(A){y=true}d.matchesSelector=function(D,F){F=F.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!d.isXML(D)){try{if(y||!k.match.PSEUDO.test(F)&&!/!=/.test(F)){var C=z.call(D,F);if(C||!B||D.document&&D.document.nodeType!==11){return C}}}catch(E){}}return d(F,null,null,[D]).length>0}}})();(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}k.order.splice(1,0,"CLASS");k.find.CLASS=function(y,z,A){if(typeof z.getElementsByClassName!=="undefined"&&!A){return z.getElementsByClassName(y[1])}};e=null})();function a(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1&&!F){e[i]=C;e.sizset=A}if(e.nodeName.toLowerCase()===D){B=e;break}e=e[y]}G[A]=B}}}function t(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1){if(!F){e[i]=C;e.sizset=A}if(typeof D!=="string"){if(e===D){B=true;break}}else{if(d.filter(D,[e]).length>0){B=e;break}}}e=e[y]}G[A]=B}}}if(document.documentElement.contains){d.contains=function(y,e){return y!==e&&(y.contains?y.contains(e):true)}}else{if(document.documentElement.compareDocumentPosition){d.contains=function(y,e){return !!(y.compareDocumentPosition(e)&16)}}else{d.contains=function(){return false}}}d.isXML=function(e){var y=(e?e.ownerDocument||e:0).documentElement;return y?y.nodeName!=="HTML":false};var s=function(z,e,D){var C,E=[],B="",F=e.nodeType?[e]:e;while((C=k.match.PSEUDO.exec(z))){B+=C[0];z=z.replace(k.match.PSEUDO,"")}z=k.relative[z]?z+"*":z;for(var A=0,y=F.length;A<y;A++){d(z,F[A],E,D)}return d.filter(B,E)};window.tinymce.dom.Sizzle=d})();(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j<arguments.length;j++){h.push(arguments[j])}h=e[g].apply(e,h);b.update(g);return h}});a.extend(b,{on:function(i,h,g){return a.dom.Event.add(b.id,i,h,g)},getXY:function(){return{x:parseInt(b.getStyle("left")),y:parseInt(b.getStyle("top"))}},getSize:function(){var g=e.get(b.id);return{w:parseInt(b.getStyle("width")||g.clientWidth),h:parseInt(b.getStyle("height")||g.clientHeight)}},moveTo:function(g,h){b.setStyles({left:g,top:h})},moveBy:function(g,i){var h=b.getXY();b.moveTo(h.x+g,h.y+i)},resizeTo:function(g,i){b.setStyles({width:g,height:i})},resizeBy:function(g,j){var i=b.getSize();b.resizeTo(i.w+g,i.h+j)},update:function(h){var g;if(a.isIE6&&d.blocker){h=h||"";if(h.indexOf("get")===0||h.indexOf("has")===0||h.indexOf("is")===0){return}if(h=="remove"){e.remove(b.blocker);return}if(!b.blocker){b.blocker=e.uniqueId();g=e.add(d.container||e.getRoot(),"iframe",{id:b.blocker,style:"position:absolute;",frameBorder:0,src:'javascript:""'});e.setStyle(g,"opacity",0)}else{g=e.get(b.blocker)}e.setStyles(g,{left:b.getStyle("left",1),top:b.getStyle("top",1),width:b.getStyle("width",1),height:b.getStyle("height",1),display:b.getStyle("display",1),zIndex:parseInt(b.getStyle("zIndex",1)||0)-1})}}})}})(tinymce);(function(d){function f(g){return g.replace(/[\n\r]+/g,"")}var c=d.is,b=d.isIE,e=d.each,a=d.dom.TreeWalker;d.create("tinymce.dom.Selection",{Selection:function(k,j,i,h){var g=this;g.dom=k;g.win=j;g.serializer=i;g.editor=h;e(["onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent"],function(l){g[l]=new d.util.Dispatcher(g)});if(!g.win.getSelection){g.tridentSel=new d.dom.TridentSelection(g)}if(d.isIE&&!d.isIE11&&k.boxModel){this._fixIESelection()}d.addUnload(g.destroy,g)},setCursorLocation:function(i,j){var g=this;var h=g.dom.createRng();h.setStart(i,j);h.setEnd(i,j);g.setRng(h);g.collapse(false)},getContent:function(h){var g=this,i=g.getRng(),m=g.dom.create("body"),k=g.getSel(),j,l,o;h=h||{};j=l="";h.get=true;h.format=h.format||"html";h.forced_root_block="";g.onBeforeGetContent.dispatch(g,h);if(h.format=="text"){return g.isCollapsed()?"":(i.text||(k.toString?k.toString():""))}if(i.cloneContents){o=i.cloneContents();if(o){m.appendChild(o)}}else{if(c(i.item)||c(i.htmlText)){m.innerHTML="<br>"+(i.item?i.item(0).outerHTML:i.htmlText);m.removeChild(m.firstChild)}else{m.innerHTML=i.toString()}}if(/^\s/.test(m.innerHTML)){j=" "}if(/\s+$/.test(m.innerHTML)){l=" "}h.getInner=true;h.content=g.isCollapsed()?"":j+g.serializer.serialize(m,h)+l;g.onGetContent.dispatch(g,h);return h.content},setContent:function(h,j){var o=this,g=o.getRng(),k,l=o.win.document,n,m;j=j||{format:"html"};j.set=true;h=j.content=h;if(!j.no_events){o.onBeforeSetContent.dispatch(o,j)}h=j.content;if(g.insertNode){h+='<span id="__caret">_</span>';if(g.startContainer==l&&g.endContainer==l){l.body.innerHTML=h}else{g.deleteContents();if(l.body.childNodes.length===0){l.body.innerHTML=h}else{if(g.createContextualFragment){g.insertNode(g.createContextualFragment(h))}else{n=l.createDocumentFragment();m=l.createElement("div");n.appendChild(m);m.outerHTML=h;g.insertNode(n)}}}k=o.dom.get("__caret");g=l.createRange();g.setStartBefore(k);g.setEndBefore(k);o.setRng(g);o.dom.remove("__caret");try{o.setRng(g)}catch(i){}}else{if(g.item){l.execCommand("Delete",false,null);g=o.getRng()}if(/^\s+/.test(h)){g.pasteHTML('<span id="__mce_tmp">_</span>'+h);o.dom.remove("__mce_tmp")}else{g.pasteHTML(h)}}if(!j.no_events){o.onSetContent.dispatch(o,j)}},getStart:function(){var i=this,h=i.getRng(),j,g,l,k;if(h.duplicate||h.item){if(h.item){return h.item(0)}l=h.duplicate();l.collapse(1);j=l.parentElement();if(j.ownerDocument!==i.dom.doc){j=i.dom.getRoot()}g=k=h.parentElement();while(k=k.parentNode){if(k==j){j=g;break}}return j}else{j=h.startContainer;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[Math.min(j.childNodes.length-1,h.startOffset)]}if(j&&j.nodeType==3){return j.parentNode}return j}},getEnd:function(){var h=this,g=h.getRng(),j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(0);j=g.parentElement();if(j.ownerDocument!==h.dom.doc){j=h.dom.getRoot()}if(j&&j.nodeName=="BODY"){return j.lastChild||j}return j}else{j=g.endContainer;i=g.endOffset;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[i>0?i-1:i]}if(j&&j.nodeType==3){return j.parentNode}return j}},getBookmark:function(s,v){var y=this,n=y.dom,h,k,j,o,i,p,q,m="\uFEFF",x;function g(z,A){var t=0;e(n.select(z),function(C,B){if(C==A){t=B}});return t}function u(t){function z(E){var A,D,C,B=E?"start":"end";A=t[B+"Container"];D=t[B+"Offset"];if(A.nodeType==1&&A.nodeName=="TR"){C=A.childNodes;A=C[Math.min(E?D:D-1,C.length-1)];if(A){D=E?0:A.childNodes.length;t["set"+(E?"Start":"End")](A,D)}}}z(true);z();return t}function l(){var z=y.getRng(true),t=n.getRoot(),A={};function B(E,J){var D=E[J?"startContainer":"endContainer"],I=E[J?"startOffset":"endOffset"],C=[],F,H,G=0;if(D.nodeType==3){if(v){for(F=D.previousSibling;F&&F.nodeType==3;F=F.previousSibling){I+=F.nodeValue.length}}C.push(I)}else{H=D.childNodes;if(I>=H.length&&H.length){G=1;I=Math.max(0,H.length-1)}C.push(y.dom.nodeIndex(H[I],v)+G)}for(;D&&D!=t;D=D.parentNode){C.push(y.dom.nodeIndex(D,v))}return C}A.start=B(z,true);if(!y.isCollapsed()){A.end=B(z)}return A}if(s==2){if(y.tridentSel){return y.tridentSel.getBookmark(s)}return l()}if(s){h=y.getRng();if(h.setStart){h={startContainer:h.startContainer,startOffset:h.startOffset,endContainer:h.endContainer,endOffset:h.endOffset}}return{rng:h}}h=y.getRng();j=n.uniqueId();o=tinyMCE.activeEditor.selection.isCollapsed();x="overflow:hidden;line-height:0px";if(h.duplicate||h.item){if(!h.item){k=h.duplicate();try{h.collapse();h.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_start" style="'+x+'">'+m+"</span>");if(!o){k.collapse(false);h.moveToElementText(k.parentElement());if(h.compareEndPoints("StartToEnd",k)===0){k.move("character",-1)}k.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_end" style="'+x+'">'+m+"</span>")}}catch(r){return null}}else{p=h.item(0);i=p.nodeName;return{name:i,index:g(i,p)}}}else{p=y.getNode();i=p.nodeName;if(i=="IMG"){return{name:i,index:g(i,p)}}k=u(h.cloneRange());if(!o){k.collapse(false);k.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_end",style:x},m))}h=u(h);h.collapse(true);h.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_start",style:x},m))}y.moveToBookmark({id:j,keep:1});return{id:j}},moveToBookmark:function(q){var v=this,n=v.dom,l,j,g,i,u,k,x,r,s;function h(C){var t=q[C?"start":"end"],z,A,B,y;if(t){B=t[0];for(A=u,z=t.length-1;z>=1;z--){y=A.childNodes;if(t[z]>y.length-1){return}A=y[t[z]]}if(A.nodeType===3){B=Math.min(t[0],A.nodeValue.length)}if(A.nodeType===1){B=Math.min(t[0],A.childNodes.length)}if(C){g.setStart(A,B)}else{g.setEnd(A,B)}}return true}function m(D){var y=n.get(q.id+"_"+D),C,t,A,B,z=q.keep;if(y){C=y.parentNode;if(D=="start"){if(!z){t=n.nodeIndex(y)}else{C=y.firstChild;t=1}k=x=C;r=s=t}else{if(!z){t=n.nodeIndex(y)}else{C=y.firstChild;t=1}x=C;s=t}if(!z){B=y.previousSibling;A=y.nextSibling;e(d.grep(y.childNodes),function(E){if(E.nodeType==3){E.nodeValue=E.nodeValue.replace(/\uFEFF/g,"")}});while(y=n.get(q.id+"_"+D)){n.remove(y,1)}if(B&&A&&B.nodeType==A.nodeType&&B.nodeType==3&&!d.isOpera){t=B.nodeValue.length;B.appendData(A.nodeValue);n.remove(A);if(D=="start"){k=x=B;r=s=t}else{x=B;s=t}}}}}function o(t){if(n.isBlock(t)&&!t.innerHTML&&!b){t.innerHTML='<br data-mce-bogus="1" />'}return t}if(q){if(q.start){g=n.createRng();u=n.getRoot();if(v.tridentSel){return v.tridentSel.moveToBookmark(q)}if(h(true)&&h()){v.setRng(g)}}else{if(q.id){m("start");m("end");if(k){g=n.createRng();g.setStart(o(k),r);g.setEnd(o(x),s);v.setRng(g)}}else{if(q.name){v.select(n.select(q.name)[q.index])}else{if(q.rng){g=q.rng;if(g.startContainer){i=v.dom.createRng();try{i.setStart(g.startContainer,g.startOffset);i.setEnd(g.endContainer,g.endOffset)}catch(p){}g=i}v.setRng(g)}}}}}},select:function(l,k){var j=this,m=j.dom,h=m.createRng(),g;function i(n,p){var o=new a(n,n);do{if(n.nodeType==3&&d.trim(n.nodeValue).length!==0){if(p){h.setStart(n,0)}else{h.setEnd(n,n.nodeValue.length)}return}if(n.nodeName=="BR"){if(p){h.setStartBefore(n)}else{h.setEndBefore(n)}return}}while(n=(p?o.next():o.prev()))}if(l){g=m.nodeIndex(l);h.setStart(l.parentNode,g);h.setEnd(l.parentNode,g+1);if(k){i(l,1);i(l)}j.setRng(h)}return l},isCollapsed:function(){var g=this,i=g.getRng(),h=g.getSel();if(!i||i.item){return false}if(i.compareEndPoints){return i.compareEndPoints("StartToEnd",i)===0}return !h||i.collapsed},collapse:function(g){var i=this,h=i.getRng(),j;if(h.item){j=h.item(0);h=i.win.document.body.createTextRange();h.moveToElementText(j)}h.collapse(!!g);i.setRng(h)},getSel:function(){var h=this,g=this.win;return g.getSelection?g.getSelection():g.document.selection},getRng:function(m){var h=this,j,g,l,k=h.win.document;if(m&&h.tridentSel){return h.tridentSel.getRangeAt(0)}try{if(j=h.getSel()){g=j.rangeCount>0?j.getRangeAt(0):(j.createRange?j.createRange():k.createRange())}}catch(i){}if(d.isIE&&!d.isIE11&&g&&g.setStart&&k.selection.createRange().item){l=k.selection.createRange().item(0);g=k.createRange();g.setStartBefore(l);g.setEndAfter(l)}if(!g){g=k.createRange?k.createRange():k.body.createTextRange()}if(g.setStart&&g.startContainer.nodeType===9&&g.collapsed){l=h.dom.getRoot();g.setStart(l,0);g.setEnd(l,0)}if(h.selectedRange&&h.explicitRange){if(g.compareBoundaryPoints(g.START_TO_START,h.selectedRange)===0&&g.compareBoundaryPoints(g.END_TO_END,h.selectedRange)===0){g=h.explicitRange}else{h.selectedRange=null;h.explicitRange=null}}return g},setRng:function(k,g){var j,i=this;if(!i.tridentSel){j=i.getSel();if(j){i.explicitRange=k;try{j.removeAllRanges()}catch(h){}j.addRange(k);if(g===false&&j.extend){j.collapse(k.endContainer,k.endOffset);j.extend(k.startContainer,k.startOffset)}i.selectedRange=j.rangeCount>0?j.getRangeAt(0):null}}else{if(k.cloneRange){try{i.tridentSel.addRange(k);return}catch(h){}}try{k.select()}catch(h){}}},setNode:function(h){var g=this;g.setContent(g.dom.getOuterHTML(h));return h},getNode:function(){var i=this,h=i.getRng(),j=i.getSel(),m,l=h.startContainer,g=h.endContainer;function k(q,o){var p=q;while(q&&q.nodeType===3&&q.length===0){q=o?q.nextSibling:q.previousSibling}return q||p}if(!h){return i.dom.getRoot()}if(h.setStart){m=h.commonAncestorContainer;if(!h.collapsed){if(h.startContainer==h.endContainer){if(h.endOffset-h.startOffset<2){if(h.startContainer.hasChildNodes()){m=h.startContainer.childNodes[h.startOffset]}}}if(l.nodeType===3&&g.nodeType===3){if(l.length===h.startOffset){l=k(l.nextSibling,true)}else{l=l.parentNode}if(h.endOffset===0){g=k(g.previousSibling,false)}else{g=g.parentNode}if(l&&l===g){return l}}}if(m&&m.nodeType==3){return m.parentNode}return m}return h.item?h.item(0):h.parentElement()},getSelectedBlocks:function(p,h){var o=this,k=o.dom,m,l,i,j=[];m=k.getParent(p||o.getStart(),k.isBlock);l=k.getParent(h||o.getEnd(),k.isBlock);if(m){j.push(m)}if(m&&l&&m!=l){i=m;var g=new a(m,k.getRoot());while((i=g.next())&&i!=l){if(k.isBlock(i)){j.push(i)}}}if(l&&m!=l){j.push(l)}return j},isForward:function(){var i=this.dom,g=this.getSel(),j,h;if(!g||g.anchorNode==null||g.focusNode==null){return true}j=i.createRng();j.setStart(g.anchorNode,g.anchorOffset);j.collapse(true);h=i.createRng();h.setStart(g.focusNode,g.focusOffset);h.collapse(true);return j.compareBoundaryPoints(j.START_TO_START,h)<=0},normalize:function(){var h=this,g,m,l,j,i;function k(p){var o,r,n,s=h.dom,u=s.getRoot(),q,t,v;function y(z,A){var B=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(z=B[A?"prev":"next"]()){if(z.nodeName==="BR"){return true}}}function x(B,z){var C,A;z=z||o;C=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(q=C[B?"prev":"next"]()){if(q.nodeType===3&&q.nodeValue.length>0){o=q;r=B?q.nodeValue.length:0;m=true;return}if(s.isBlock(q)||t[q.nodeName.toLowerCase()]){return}A=q}if(l&&A){o=A;m=true;r=0}}o=g[(p?"start":"end")+"Container"];r=g[(p?"start":"end")+"Offset"];t=s.schema.getNonEmptyElements();if(o.nodeType===9){o=s.getRoot();r=0}if(o===u){if(p){q=o.childNodes[r>0?r-1:0];if(q){v=q.nodeName.toLowerCase();if(t[q.nodeName]||q.nodeName=="TABLE"){return}}}if(o.hasChildNodes()){o=o.childNodes[Math.min(!p&&r>0?r-1:r,o.childNodes.length-1)];r=0;if(o.hasChildNodes()&&!/TABLE/.test(o.nodeName)){q=o;n=new a(o,u);do{if(q.nodeType===3&&q.nodeValue.length>0){r=p?0:q.nodeValue.length;o=q;m=true;break}if(t[q.nodeName.toLowerCase()]){r=s.nodeIndex(q);o=q.parentNode;if(q.nodeName=="IMG"&&!p){r++}m=true;break}}while(q=(p?n.next():n.prev()))}}}if(l){if(o.nodeType===3&&r===0){x(true)}if(o.nodeType===1){q=o.childNodes[r];if(q&&q.nodeName==="BR"&&!y(q)&&!y(q,true)){x(true,o.childNodes[r])}}}if(p&&!l&&o.nodeType===3&&r===o.nodeValue.length){x(false)}if(m){g["set"+(p?"Start":"End")](o,r)}}if(d.isIE){return}g=h.getRng();l=g.collapsed;k(true);if(!l){k()}if(m){if(l){g.collapse(true)}h.setRng(g,h.isForward())}},selectorChanged:function(g,j){var h=this,i;if(!h.selectorChangedData){h.selectorChangedData={};i={};h.editor.onNodeChange.addToTop(function(l,k,o){var p=h.dom,m=p.getParents(o,null,p.getRoot()),n={};e(h.selectorChangedData,function(r,q){e(m,function(s){if(p.is(s,q)){if(!i[q]){e(r,function(t){t(true,{node:s,selector:q,parents:m})});i[q]=r}n[q]=r;return false}})});e(i,function(r,q){if(!n[q]){delete i[q];e(r,function(s){s(false,{node:o,selector:q,parents:m})})}})})}if(!h.selectorChangedData[g]){h.selectorChangedData[g]=[]}h.selectorChangedData[g].push(j);return h},scrollIntoView:function(k){var j,h,g=this,i=g.dom;h=i.getViewPort(g.editor.getWin());j=i.getPos(k).y;if(j<h.y||j+25>h.y+h.h){g.editor.getWin().scrollTo(0,j<h.y?j:j-h.h+25)}},destroy:function(h){var g=this;g.win=null;if(!h){d.removeUnload(g.destroy)}},_fixIESelection:function(){var h=this.dom,n=h.doc,i=n.body,k,o,g;function j(p,s){var q=i.createTextRange();try{q.moveToPoint(p,s)}catch(r){q=null}return q}function m(q){var p;if(q.button){p=j(q.x,q.y);if(p){if(p.compareEndPoints("StartToStart",o)>0){p.setEndPoint("StartToStart",o)}else{p.setEndPoint("EndToEnd",o)}p.select()}}else{l()}}function l(){var p=n.selection.createRange();if(o&&!p.item&&p.compareEndPoints("StartToEnd",p)===0){o.select()}h.unbind(n,"mouseup",l);h.unbind(n,"mousemove",m);o=k=0}n.documentElement.unselectable=true;h.bind(n,["mousedown","contextmenu"],function(p){if(p.target.nodeName==="HTML"){if(k){l()}g=n.documentElement;if(g.scrollHeight>g.clientHeight){return}k=1;o=j(p.x,p.y);if(o){h.bind(n,"mouseup",l);h.bind(n,"mousemove",m);h.win.focus();o.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";e.remove_trailing_brs="remove_trailing_brs" in e?e.remove_trailing_brs:true;h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addAttributeFilter("data-mce-expando",function(j,l,k){var m=j.length;while(m--){j[m].attr(l,null)}});c.addNodeFilter("noscript",function(j){var k=j.length,l;while(k--){l=j[k].firstChild;if(l){l.value=a.html.Entities.decode(l.value)}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// <![CDATA[\n"+j(o)+"\n// ]]>"}}else{if(o.length>0){n.firstChild.value="<!--\n"+j(o)+"\n-->"}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(a.trim(m.getInner?o.innerHTML:i.getOuterHTML(o)),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],e={},d=[],g=0,f;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=document.createElement("script");s.id=n;s.type="text/javascript";s.src=a._addVer(m);if(!a.isIE||a.isIE11){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==f){j.push(m);l[m]=c}if(q){if(!e[m]){e[m]=[]}e[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(e[r],function(s){s.func.call(s.scope)});e[r]=f}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,s){var i=d.startContainer,l=d.startOffset,t=d.endContainer,m=d.endOffset,j,g,o,h,r,q,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(u){s([u])});return}function f(u){var v;v=u[0];if(v.nodeType===3&&v===i&&l>=v.nodeValue.length){u.splice(0,1)}v=u[u.length-1];if(m===0&&u.length>0&&v===t&&v.nodeType===3){u.splice(u.length-1,1)}return u}function p(x,v,u){var y=[];for(;x&&x!=u;x=x[v]){y.push(x)}return y}function n(v,u){do{if(v.parentNode==u){return v}v=v.parentNode}while(v)}function k(x,v,y){var u=y?"nextSibling":"previousSibling";for(h=x,r=h.parentNode;h&&h!=v;h=r){r=h.parentNode;q=p(h==x?h:h[u],u);if(q.length){if(!y){q.reverse()}s(f(q))}}}if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[l]}if(t.nodeType==1&&t.hasChildNodes()){t=t.childNodes[Math.min(m-1,t.childNodes.length-1)]}if(i==t){return s(f([i]))}j=c.findCommonAncestor(i,t);for(h=i;h;h=h.parentNode){if(h===t){return k(i,j,true)}if(h===j){break}}for(h=t;h;h=h.parentNode){if(h===i){return k(t,j)}if(h===j){break}}g=n(i,j)||i;o=n(t,j)||t;k(i,g,true);q=p(g==i?g:g.nextSibling,"nextSibling",o==t?o.nextSibling:o);if(q.length){s(f(q))}k(t,o)};this.split=function(e){var h=e.startContainer,d=e.startOffset,i=e.endContainer,g=e.endOffset;function f(j,k){return j.splitText(k)}if(h==i&&h.nodeType==3){if(d>0&&d<h.nodeValue.length){i=f(h,d);h=i.previousSibling;if(g>d){g=g-d;h=i=f(i,g).previousSibling;g=i.nodeValue.length;d=0}else{g=0}}}else{if(h.nodeType==3&&d>0&&d<h.nodeValue.length){h=f(h,d);d=0}if(i.nodeType==3&&g>0&&g<i.nodeValue.length){i=f(i,g).previousSibling;g=i.nodeValue.length}}return{startContainer:h,startOffset:d,endContainer:i,endOffset:g}}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var q=this,n=e.root,m=e.items,o=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,l=e.excludeFromTabOrder,k,h,p,d,g;f=f||b.DOM;k=function(r){g=r.target.id};h=function(r){f.setAttrib(r.target.id,"tabindex","-1")};d=function(r){var s=f.get(g);f.setAttrib(s,"tabindex","0");s.focus()};q.focus=function(){f.get(g).focus()};q.destroy=function(){c(m,function(s){var t=f.get(s.id);f.unbind(t,"focus",k);f.unbind(t,"blur",h)});var r=f.get(n);f.unbind(r,"focus",d);f.unbind(r,"keydown",p);m=f=n=q.focus=k=h=p=d=null;q.destroy=function(){}};q.moveFocus=function(v,s){var r=-1,u=q.controls,t;if(!g){return}c(m,function(y,x){if(y.id===g){r=x;return false}});r+=v;if(r<0){r=m.length-1}else{if(r>=m.length){r=0}}t=m[r];f.setAttrib(g,"tabindex","-1");f.setAttrib(t.id,"tabindex","0");f.get(t.id).focus();if(e.actOnFocus){e.onAction(t.id)}if(s){a.cancel(s)}};p=function(z){var v=37,u=39,y=38,A=40,r=27,t=14,s=13,x=32;switch(z.keyCode){case v:if(i){q.moveFocus(-1)}a.cancel(z);break;case u:if(i){q.moveFocus(1)}a.cancel(z);break;case y:if(o){q.moveFocus(-1)}a.cancel(z);break;case A:if(o){q.moveFocus(1)}a.cancel(z);break;case r:if(e.onCancel){e.onCancel();a.cancel(z)}break;case t:case s:case x:if(e.onAction){e.onAction(g);a.cancel(z)}break}};c(m,function(t,r){var s,u;if(!t.id){t.id=f.uniqueId("_mce_item_")}u=f.get(t.id);if(l){f.bind(u,"blur",h);s="-1"}else{s=(r===0?"0":"-1")}u.setAttribute("tabindex",s);f.bind(u,"focus",k)});if(m[0]){g=m[0].id}f.setAttrib(n,"tabindex","-1");var j=f.get(n);f.bind(j,"focus",d);f.bind(j,"keydown",p)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.offsetWidth,j.max_width):g.offsetWidth;k=j.max_height?Math.min(g.offsetHeight,j.max_height):g.offsetHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeight<j.max_height){c.setStyle(l,"overflow","hidden")}}},showMenu:function(p,n,r){var z=this,A=z.settings,o,g=c.getViewPort(),u,l,v,q,i=2,k,j,m=z.classPrefix;z.collapse(1);if(z.isMenuVisible){return}if(!z.rendered){o=c.add(z.settings.container,z.renderNode());f(z.items,function(h){h.postRender()});z.element=new b("menu_"+z.id,{blocker:1,container:A.container})}else{o=c.get("menu_"+z.id)}if(!e.isOpera){c.setStyles(o,{left:-65535,top:-65535})}c.show(o);z.update();p+=A.offset_x||0;n+=A.offset_y||0;g.w-=4;g.h-=4;if(A.constrain){u=o.clientWidth-i;l=o.clientHeight-i;v=g.x+g.w;q=g.y+g.h;if((p+A.vp_offset_x+u)>v){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return false}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.get("menu_"+g.id);h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){if(typeof h.settings.style=="function"){h.settings.style=h.settings.style()}c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='<a role="button" id="'+this.id+'" href="javascript:;" class="'+f+" "+f+"Enabled "+e["class"]+(c?" "+f+"Labeled":"")+'" onmousedown="return false;" onclick="return false;" aria-labelledby="'+this.id+'_voice" title="'+a.encode(e.title)+'">';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+='<span class="mceIcon '+e["class"]+'"><img class="mceIcon" src="'+e.image+'" alt="'+a.encode(e.title)+'" /></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}else{d+='<span class="mceIcon '+e["class"]+'"></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}d+='<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="'+this.id+'_voice">'+e.title+"</span>";d+="</a>";return d},postRender:function(){var d=this,e=d.settings,c;if(b.isIE&&d.editor){b.dom.Event.add(d.id,"mousedown",function(f){var g=d.editor.selection.getNode().nodeName;c=g==="IMG"?d.editor.selection.getBookmark():null})}b.dom.Event.add(d.id,"click",function(f){if(!d.isDisabled()){if(b.isIE&&d.editor&&c!==null){d.editor.selection.moveToBookmark(c)}return e.onclick.call(e.scope,f)}});b.dom.Event.add(d.id,"keydown",function(f){if(!d.isDisabled()&&f.keyCode==b.VK.SPACEBAR){b.dom.Event.cancel(f);return e.onclick.call(e.scope,f)}})}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(j,i,g){var h=this;h.parent(j,i,g);h.items=[];h.onChange=new a(h);h.onPostRender=new a(h);h.onAdd=new a(h);h.onRenderMenu=new e.util.Dispatcher(this);h.classPrefix="mceListBox";h.marked={}},select:function(h){var g=this,j,i;g.marked={};if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){var i=this,j,k,h;i.marked={};if(g!=i.selectedIndex){j=d.get(i.id+"_text");h=d.get(i.id+"_voiceDesc");k=i.items[g];if(k){i.selectedValue=k.value;i.selectedIndex=g;d.setHTML(j,d.encode(k.title));d.setHTML(h,i.settings.title+" - "+k.title);d.removeClass(j,"mceTitle");d.setAttrib(i.id,"aria-valuenow",k.title)}else{d.setHTML(j,d.encode(i.settings.title));d.setHTML(h,d.encode(i.settings.title));d.addClass(j,"mceTitle");i.selectedValue=i.selectedIndex=null;d.setAttrib(i.id,"aria-valuenow",i.settings.title)}j=0}},mark:function(g){this.marked[g]=true},add:function(j,g,i){var h=this;i=i||{};i=e.extend(i,{title:j,value:g});h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var j="",g=this,i=g.settings,k=g.classPrefix;j='<span role="listbox" aria-haspopup="true" aria-labelledby="'+g.id+'_voiceDesc" aria-describedby="'+g.id+'_voiceDesc"><table role="presentation" tabindex="0" id="'+g.id+'" cellpadding="0" cellspacing="0" class="'+k+" "+k+"Enabled"+(i["class"]?(" "+i["class"]):"")+'"><tbody><tr>';j+="<td>"+d.createHTML("span",{id:g.id+"_voiceDesc","class":"voiceLabel",style:"display:none;"},g.settings.title);j+=d.createHTML("a",{id:g.id+"_text",tabindex:-1,href:"javascript:;","class":"mceText",onclick:"return false;",onmousedown:"return false;"},d.encode(g.settings.title))+"</td>";j+="<td>"+d.createHTML("a",{id:g.id+"_open",tabindex:-1,href:"javascript:;","class":"mceOpen",onclick:"return false;",onmousedown:"return false;"},'<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>')+"</td>";j+="</tr></tbody></table></span>";return j},showMenu:function(){var h=this,j,i=d.get(this.id),g;if(h.isDisabled()||h.items.length===0){return}if(h.menu&&h.menu.isMenuVisible){return h.hideMenu()}if(!h.isMenuRendered){h.renderMenu();h.isMenuRendered=true}j=d.getPos(i);g=h.menu;g.settings.offset_x=j.x;g.settings.offset_y=j.y;g.settings.keyboard_focus=!e.isOpera;f(h.items,function(k){if(g.items[k.id]){g.items[k.id].setSelected(0)}});f(h.items,function(k){if(g.items[k.id]&&h.marked[k.value]){g.items[k.id].setSelected(1)}if(k.value===h.selectedValue){g.items[k.id].setSelected(1)}});g.showMenu(0,i.clientHeight);b.add(d.doc,"mousedown",h.hideMenu,h);d.addClass(h.id,h.classPrefix+"Selected")},hideMenu:function(h){var g=this;if(g.menu&&g.menu.isMenuVisible){d.removeClass(g.id,g.classPrefix+"Selected");if(h&&h.type=="mousedown"&&(h.target.id==g.id+"_text"||h.target.id==g.id+"_open")){return}if(!h||!d.getParent(h.target,".mceMenu")){d.removeClass(g.id,g.classPrefix+"Selected");b.remove(d.doc,"mousedown",g.hideMenu,g);g.menu.hideMenu()}}},renderMenu:function(){var h=this,g;g=h.settings.control_manager.createDropMenu(h.id+"_menu",{menu_line:1,"class":h.classPrefix+"Menu mceNoIcons",max_width:250,max_height:150});g.onHideMenu.add(function(){h.hideMenu();h.focus()});g.add({title:h.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}});f(h.items,function(i){if(i.value===c){g.add({title:i.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}})}else{i.id=d.uniqueId();i.role="option";i.onclick=function(){if(h.settings.onselect(i.value)!==false){h.select(i.value)}};g.add(i)}});h.onRenderMenu.dispatch(h,g);h.menu=g},postRender:function(){var g=this,h=g.classPrefix;b.add(g.id,"click",g.showMenu,g);b.add(g.id,"keydown",function(i){if(i.keyCode==32){g.showMenu(i);b.cancel(i)}});b.add(g.id,"focus",function(){if(!g._focused){g.keyDownHandler=b.add(g.id,"keydown",function(i){if(i.keyCode==40){g.showMenu();b.cancel(i)}});g.keyPressHandler=b.add(g.id,"keypress",function(j){var i;if(j.keyCode==13){i=g.selectedValue;g.selectedValue=null;b.cancel(j);g.settings.onselect(i)}})}g._focused=1});b.add(g.id,"blur",function(){b.remove(g.id,"keydown",g.keyDownHandler);b.remove(g.id,"keypress",g.keyPressHandler);g._focused=0});if(e.isIE6||!d.boxModel){b.add(g.id,"mouseover",function(){if(!d.hasClass(g.id,h+"Disabled")){d.addClass(g.id,h+"Hover")}});b.add(g.id,"mouseout",function(){if(!d.hasClass(g.id,h+"Disabled")){d.removeClass(g.id,h+"Hover")}})}g.onPostRender.dispatch(g,d.get(g.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(h,g){this.parent(h,g);this.classPrefix="mceNativeListBox"},setDisabled:function(g){d.get(this.id).disabled=g;this.setAriaProperty("disabled",g)},isDisabled:function(){return d.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){d.get(this.id).selectedIndex=g+1;this.selectedValue=this.items[g]?this.items[g].value:null},add:function(k,h,g){var j,i=this;g=g||{};g.value=h;if(i.isRendered()){d.add(d.get(this.id),"option",g,k)}j={title:k,value:h,attribs:g};i.items.push(j);i.onAdd.dispatch(i,j)},getLength:function(){return this.items.length},renderHTML:function(){var i,g=this;i=d.createHTML("option",{value:""},"-- "+g.settings.title+" --");f(g.items,function(h){i+=d.createHTML("option",{value:h.value},h.title)});i=d.createHTML("select",{id:g.id,"class":"mceNativeListBox","aria-labelledby":g.id+"_aria"},i);i+=d.createHTML("span",{id:g.id+"_aria",style:"display: none"},g.settings.title);return i},postRender:function(){var h=this,i,j=true;h.rendered=true;function g(l){var k=h.items[l.target.selectedIndex-1];if(k&&(k=k.value)){h.onChange.dispatch(h,k);if(h.settings.onselect){h.settings.onselect(k)}}}b.add(h.id,"change",g);b.add(h.id,"keydown",function(q){var n,p=37,m=39,l=38,r=40,k=13,o=32;b.remove(h.id,"change",i);j=false;n=b.add(h.id,"blur",function(){if(j){return}j=true;b.add(h.id,"change",g);b.remove(h.id,"blur",n)});if(q.keyCode==k||q.keyCode==o){g(q);return b.cancel(q)}else{if(q.keyCode==r||q.keyCode==l){q.stopImmediatePropagation()}}});h.onPostRender.dispatch(h,d.get(h.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.firstChild.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="<tbody><tr>";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";i+="</tr></tbody>";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.firstChild.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.keyboardNav=new d.ui.KeyboardNavigation({root:f.id+"_menu",items:c.select("a",f.id+"_menu"),onCancel:function(){f.hideMenu();f.focus()}});f.keyboardNav.focus();f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch();f.keyboardNav.destroy()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(m){m=m.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");var i={href:"javascript:;",style:{backgroundColor:"#"+m},title:p.editor.getLang("colors."+m,m),"data-mce-color":"#"+m};if(!d.isIE){i.role="option"}g=c.add(g,"a",i);if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+m;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return false});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){var f=this;f.parent();a.clear(f.id+"_menu");a.clear(f.id+"_more");c.remove(f.id+"_menu");if(f.keyboardNav){f.keyboardNav.destroy()}}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('<div id="'+f.id+'" role="group" aria-labelledby="'+f.id+'_voice">');i.push("<span role='application'>");i.push('<span id="'+f.id+'_voice" class="mceVoiceLabel" style="display:none;">'+d.encode(g.name)+"</span>");j(e,function(h){i.push(h.renderHTML())});i.push("</span>");i.push("</div>");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e<l.length;e++){k=l[e];d=l[e-1];g=l[e+1];if(e===0){j="mceToolbarStart";if(k.Button){j+=" mceToolbarStartButton"}else{if(k.SplitButton){j+=" mceToolbarStartSplitButton"}else{if(k.ListBox){j+=" mceToolbarStartListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,"<!-- IE -->"))}}if(c.stdMode){f+='<td style="position: relative">'+k.renderHTML()+"</td>"}else{f+="<td>"+k.renderHTML()+"</td>"}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,"<!-- IE -->"))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},"<tbody><tr>"+f+"</tr></tbody>")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!==0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(x){var v=this,o,n=j.ScriptLoader,u,l=[],r;function q(t){var s=t.id;if(!s){s=t.name;if(s&&!k.get(s)){s=t.name}else{s=k.uniqueId()}t.setAttribute("id",s)}return s}function m(z,A,t){var y=z[A];if(!y){return}if(j.is(y,"string")){t=y.replace(/\.\w+$/,"");t=t?j.resolve(t):0;y=j.resolve(y)}return y.apply(t||this,Array.prototype.slice.call(arguments,2))}function p(t,s){return s.constructor===RegExp?s.test(t.className):k.hasClass(t,s)}v.settings=x;i.bind(window,"ready",function(){var s,t;m(x,"onpageload");switch(x.mode){case"exact":s=x.elements||"";if(s.length>0){g(e(s),function(y){if(k.get(y)){r=new j.Editor(y,x);l.push(r);r.render(1)}else{g(document.forms,function(z){g(z.elements,function(A){if(A.name===y){y="mce_editor_"+c++;k.setAttrib(A,"id",y);r=new j.Editor(y,x);l.push(r);r.render(1)}})})}})}break;case"textareas":case"specific_textareas":g(k.select("textarea"),function(y){if(x.editor_deselector&&p(y,x.editor_deselector)){return}if(!x.editor_selector||p(y,x.editor_selector)){r=new j.Editor(q(y),x);l.push(r);r.render(1)}});break;default:if(x.types){g(x.types,function(y){g(k.select(y.selector),function(A){var z=new j.Editor(q(A),j.extend({},x,y));l.push(z);z.render(1)})})}else{if(x.selector){g(k.select(x.selector),function(z){var y=new j.Editor(q(z),x);l.push(y);y.render(1)})}}}if(x.oninit){s=t=0;g(l,function(y){t++;if(!y.initialized){y.onInit.add(function(){s++;if(s==t){m(x,"oninit")}})}else{s++}if(s==t){m(x,"oninit")}})}})},get:function(l){if(l===a){return this.editors}if(!this.editors.hasOwnProperty(l)){return a}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l<o.length;l++){if(o[l]==n){o.splice(l,1);break}}if(m.activeEditor==n){m._setActive(o[0])}n.destroy();m.onRemoveEditor.dispatch(m,n);return n},execCommand:function(r,p,o){var q=this,n=q.get(o),l;function m(){n.destroy();l.detachEvent("onunload",m);l=l.tinyMCE=l.tinymce=null}switch(r){case"mceFocus":n.focus();return true;case"mceAddEditor":case"mceAddControl":if(!q.get(o)){new j.Editor(o,q.settings).render()}return true;case"mceAddFrameControl":l=o.window;l.tinyMCE=tinyMCE;l.tinymce=j;j.DOM.doc=l.document;j.DOM.win=l;n=new j.Editor(o.element_id,o);n.render();if(j.isIE&&!j.isIE11){l.attachEvent("onunload",m)}o.page_window=null;return true;case"mceRemoveEditor":case"mceRemoveControl":if(n){n.remove()}return true;case"mceToggleEditor":if(!n){q.execCommand("mceAddControl",0,o);return true}if(n.isHidden()){n.show()}else{n.hide()}return true}if(q.activeEditor){return q.activeEditor.execCommand(r,p,o)}return false},execInstanceCommand:function(p,o,n,m){var l=this.get(p);if(l){return l.execCommand(o,n,m)}return false},triggerSave:function(){g(this.editors,function(l){l.save()})},addI18n:function(n,q){var l,m=this.i18n;if(!j.is(n,"string")){g(n,function(r,p){g(r,function(t,s){g(t,function(v,u){if(s==="common"){m[p+"."+u]=v}else{m[p+"."+s+"."+u]=v}})})})}else{g(q,function(r,p){m[n+"."+p]=r})}},_setActive:function(l){this.selectedInstance=this.activeEditor=l}})})(tinymce);(function(k){var l=k.DOM,j=k.dom.Event,f=k.extend,i=k.each,a=k.isGecko,b=k.isIE,e=k.isWebKit,d=k.is,h=k.ThemeManager,c=k.PluginManager,g=k.explode;k.create("tinymce.Editor",{Editor:function(p,o){var m=this,n=true;m.settings=o=f({id:p,language:"en",theme:"advanced",skin:"default",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:k.documentBaseURL,add_form_submit_trigger:n,submit_patch:n,add_unload_trigger:n,convert_urls:n,relative_urls:n,remove_script_host:n,table_inline_editing:false,object_resizing:n,accessibility_focus:n,doctype:k.isIE6?'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">':"<!DOCTYPE>",visual:n,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",apply_source_formatting:n,directionality:"ltr",forced_root_block:"p",hidden_input:n,padd_empty_editor:n,render_ui:n,indentation:"30px",fix_table_elements:n,inline_styles:n,convert_fonts_to_spans:n,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:n,entity_encoding:"named",url_converter:m.convertURL,url_converter_scope:m,ie7_compat:n},o);m.id=m.editorId=p;m.isNotDirty=false;m.plugins={};m.documentBaseURI=new k.util.URI(o.document_base_url||k.documentBaseURL,{base_uri:tinyMCE.baseURI});m.baseURI=k.baseURI;m.contentCSS=[];m.contentStyles=[];m.setupEvents();m.execCommands={};m.queryStateCommands={};m.queryValueCommands={};m.execCallback("setup",m)},render:function(o){var p=this,q=p.settings,r=p.id,m=k.ScriptLoader;if(!j.domLoaded){j.add(window,"ready",function(){p.render()});return}tinyMCE.settings=q;if(!p.getElement()){return}if(k.isIDevice&&!k.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(p.getElement().nodeName)&&q.hidden_input&&l.getParent(r,"form")){l.insertAfter(l.create("input",{type:"hidden",name:r}),r)}if(!q.content_editable){p.orgVisibility=p.getElement().style.visibility;p.getElement().style.visibility="hidden"}if(k.WindowManager){p.windowManager=new k.WindowManager(p)}if(q.encoding=="xml"){p.onGetContent.add(function(s,t){if(t.save){t.content=l.encode(t.content)}})}if(q.add_form_submit_trigger){p.onSubmit.addToTop(function(){if(p.initialized){p.save();p.isNotDirty=1}})}if(q.add_unload_trigger){p._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(p.initialized&&!p.destroyed&&!p.isHidden()){p.save({format:"raw",no_events:true})}})}k.addUnload(p.destroy,p);if(q.submit_patch){p.onBeforeRenderUI.add(function(){var s=p.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){p.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){k.triggerSave();p.isNotDirty=1;return p.formElement._mceOldSubmit(p.formElement)}}s=null})}function n(){if(q.language&&q.language_load!==false){m.add(k.baseURL+"/langs/"+q.language+".js")}if(q.theme&&typeof q.theme!="function"&&q.theme.charAt(0)!="-"&&!h.urls[q.theme]){h.load(q.theme,"themes/"+q.theme+"/editor_template"+k.suffix+".js")}i(g(q.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(v){var u={prefix:"plugins/",resource:v,suffix:"/editor_plugin"+k.suffix+".js"};v=c.createUrl(u,v);c.load(v.resource,v)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+k.suffix+".js"})}}});m.loadQueue(function(){if(!p.removed){p.init()}})}n()},init:function(){var q,G=this,H=G.settings,D,y,z,C=G.getElement(),p,m,E,v,B,F,x,r=[];k.add(G);H.aria_label=H.aria_label||l.getAttrib(C,"aria-label",G.getLang("aria.rich_text_area"));if(H.theme){if(typeof H.theme!="function"){H.theme=H.theme.replace(/-/,"");p=h.get(H.theme);G.theme=new p();if(G.theme.init){G.theme.init(G,h.urls[H.theme]||k.documentBaseURL.replace(/\/$/,""))}}else{G.theme=H.theme}}function A(s){var t=c.get(s),o=c.urls[s]||k.documentBaseURL.replace(/\/$/,""),n;if(t&&k.inArray(r,s)===-1){i(c.dependencies(s),function(u){A(u)});n=new t(G,o);G.plugins[s]=n;if(n.init){n.init(G,o);r.push(s)}}}i(g(H.plugins.replace(/\-/g,"")),A);if(H.popup_css!==false){if(H.popup_css){H.popup_css=G.documentBaseURI.toAbsolute(H.popup_css)}else{H.popup_css=G.baseURI.toAbsolute("themes/"+H.theme+"/skins/"+H.skin+"/dialog.css")}}if(H.popup_css_add){H.popup_css+=","+G.documentBaseURI.toAbsolute(H.popup_css_add)}G.controlManager=new k.ControlManager(G);G.onBeforeRenderUI.dispatch(G,G.controlManager);if(H.render_ui&&G.theme){G.orgDisplay=C.style.display;if(typeof H.theme!="function"){D=H.width||C.style.width||C.offsetWidth;y=H.height||C.style.height||C.offsetHeight;z=H.min_height||100;F=/^[0-9\.]+(|px)$/i;if(F.test(""+D)){D=Math.max(parseInt(D,10)+(p.deltaWidth||0),100)}if(F.test(""+y)){y=Math.max(parseInt(y,10)+(p.deltaHeight||0),z)}p=G.theme.renderUI({targetNode:C,width:D,height:y,deltaWidth:H.delta_width,deltaHeight:H.delta_height});l.setStyles(p.sizeContainer||p.editorContainer,{width:D,height:y});y=(p.iframeHeight||y)+(typeof(y)=="number"?(p.deltaHeight||0):"");if(y<z){y=z}}else{p=H.theme(G,C);if(p.editorContainer.nodeType){p.editorContainer=p.editorContainer.id=p.editorContainer.id||G.id+"_parent"}if(p.iframeContainer.nodeType){p.iframeContainer=p.iframeContainer.id=p.iframeContainer.id||G.id+"_iframecontainer"}y=p.iframeHeight||C.offsetHeight;if(b){G.onInit.add(function(n){n.dom.bind(n.getBody(),"beforedeactivate keydown keyup",function(){n.bookmark=n.selection.getBookmark(1)})});G.onNodeChange.add(function(n){if(document.activeElement.id==n.id+"_ifr"){n.bookmark=n.selection.getBookmark(1)}})}}G.editorContainer=p.editorContainer}if(H.content_css){i(g(H.content_css),function(n){G.contentCSS.push(G.documentBaseURI.toAbsolute(n))})}if(H.content_style){G.contentStyles.push(H.content_style)}if(H.content_editable){C=q=p=null;return G.initContentBody()}if(document.domain&&location.hostname!=document.domain){k.relaxedDomain=document.domain}G.iframeHTML=H.doctype+'<html><head xmlns="http://www.w3.org/1999/xhtml">';if(H.document_base_url!=k.documentBaseURL){G.iframeHTML+='<base href="'+G.documentBaseURI.getURI()+'" />'}if(k.isIE8){if(H.ie7_compat){G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'}else{G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=edge" />'}}G.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';for(x=0;x<G.contentCSS.length;x++){G.iframeHTML+='<link type="text/css" rel="stylesheet" href="'+G.contentCSS[x]+'" />'}G.contentCSS=[];v=H.body_id||"tinymce";if(v.indexOf("=")!=-1){v=G.getParam("body_id","","hash");v=v[G.id]||v}B=H.body_class||"";if(B.indexOf("=")!=-1){B=G.getParam("body_class","","hash");B=B[G.id]||""}G.iframeHTML+='</head><body id="'+v+'" class="mceContentBody '+B+'" onload="window.parent.tinyMCE.get(\''+G.id+"').onLoad.dispatch();\"><br></body></html>";if(k.relaxedDomain&&(b||(k.isOpera&&parseFloat(opera.version())<11))){E='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+G.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()'}q=l.add(p.iframeContainer,"iframe",{id:G.id+"_ifr",src:E||'javascript:""',frameBorder:"0",allowTransparency:"true",title:H.aria_label,style:{width:"100%",height:y,display:"block"}});G.contentAreaContainer=p.iframeContainer;if(p.editorContainer){l.get(p.editorContainer).style.display=G.orgDisplay}C.style.visibility=G.orgVisibility;l.get(G.id).style.display="none";l.setAttrib(G.id,"aria-hidden",true);if(!k.relaxedDomain||!E){G.initContentBody()}C=q=p=null},initContentBody:function(){var n=this,p=n.settings,q=l.get(n.id),r=n.getDoc(),o,m,s;if((!b||!k.relaxedDomain)&&!p.content_editable){r.open();r.write(n.iframeHTML);r.close();if(k.relaxedDomain){r.domain=k.relaxedDomain}}if(p.content_editable){l.addClass(q,"mceContentBody");n.contentDocument=r=p.content_document||document;n.contentWindow=p.content_window||window;n.bodyElement=q;p.content_document=p.content_window=null}m=n.getBody();m.disabled=true;if(!p.readonly){m.contentEditable=n.getParam("content_editable_state",true)}m.disabled=false;n.schema=new k.html.Schema(p);n.dom=new k.dom.DOMUtils(r,{keep_values:true,url_converter:n.convertURL,url_converter_scope:n,hex_colors:p.force_hex_style_colors,class_filter:p.class_filter,update_styles:true,root_element:p.content_editable?n.id:null,schema:n.schema});n.parser=new k.html.DomParser(p,n.schema);n.parser.addAttributeFilter("src,href,style",function(t,u){var v=t.length,y,A=n.dom,z,x;while(v--){y=t[v];z=y.attr(u);x="data-mce-"+u;if(!y.attributes.map[x]){if(u==="style"){y.attr(x,A.serializeStyle(A.parseStyle(z),y.name))}else{y.attr(x,n.convertURL(z,u,y.name))}}}});n.parser.addNodeFilter("script",function(t,u){var v=t.length,x;while(v--){x=t[v];x.attr("type","mce-"+(x.attr("type")||"text/javascript"))}});n.parser.addNodeFilter("#cdata",function(t,u){var v=t.length,x;while(v--){x=t[v];x.type=8;x.name="#comment";x.value="[CDATA["+x.value+"]]"}});n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(u,v){var x=u.length,y,t=n.schema.getNonEmptyElements();while(x--){y=u[x];if(y.isEmpty(t)){y.empty().append(new k.html.Node("br",1)).shortEnded=true}}});n.serializer=new k.dom.Serializer(p,n.dom,n.schema);n.selection=new k.dom.Selection(n.dom,n.getWin(),n.serializer,n);n.formatter=new k.Formatter(n);n.undoManager=new k.UndoManager(n);n.forceBlocks=new k.ForceBlocks(n);n.enterKey=new k.EnterKey(n);n.editorCommands=new k.EditorCommands(n);n.onExecCommand.add(function(t,u){if(!/^(FontName|FontSize)$/.test(u)){n.nodeChanged()}});n.serializer.onPreProcess.add(function(t,u){return n.onPreProcess.dispatch(n,u,t)});n.serializer.onPostProcess.add(function(t,u){return n.onPostProcess.dispatch(n,u,t)});n.onPreInit.dispatch(n);if(!p.browser_spellcheck&&!p.gecko_spellcheck){r.body.spellcheck=false}if(!p.readonly){n.bindNativeEvents()}n.controlManager.onPostRender.dispatch(n,n.controlManager);n.onPostRender.dispatch(n);n.quirks=k.util.Quirks(n);if(p.directionality){m.dir=p.directionality}if(p.nowrap){m.style.whiteSpace="nowrap"}if(p.protect){n.onBeforeSetContent.add(function(t,u){i(p.protect,function(v){u.content=u.content.replace(v,function(x){return"<!--mce:protected "+escape(x)+"-->"})})})}n.onSetContent.add(function(){n.addVisual(n.getBody())});if(p.padd_empty_editor){n.onPostProcess.add(function(t,u){u.content=u.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")})}n.load({initial:true,format:"html"});n.startContent=n.getContent({format:"raw"});n.initialized=true;n.onInit.dispatch(n);n.execCallback("setupcontent_callback",n.id,m,r);n.execCallback("init_instance_callback",n);n.focus(true);n.nodeChanged({initial:true});if(n.contentStyles.length>0){s="";i(n.contentStyles,function(t){s+=t+"\r\n"});n.dom.addStyle(s)}i(n.contentCSS,function(t){n.dom.loadCSS(t)});if(p.auto_focus){setTimeout(function(){var t=k.get(p.auto_focus);t.selection.select(t.getBody(),1);t.selection.collapse(1);t.getBody().focus();t.getWin().focus()},100)}q=r=m=null},focus:function(p){var o,u=this,t=u.selection,q=u.settings.content_editable,n,r,s=u.getDoc(),m;if(!p){if(u.bookmark){t.moveToBookmark(u.bookmark);u.bookmark=null}n=t.getRng();if(n.item){r=n.item(0)}u._refreshContentEditable();if(!q){u.getWin().focus()}if(k.isGecko||q){m=u.getBody();if(m.setActive&&!k.isIE11){m.setActive()}else{m.focus()}if(q){t.normalize()}}if(r&&r.ownerDocument==s){n=s.body.createControlRange();n.addElement(r);n.select()}}if(k.activeEditor!=u){if((o=k.activeEditor)!=null){o.onDeactivate.dispatch(o,u)}u.onActivate.dispatch(u,o)}k._setActive(u)},execCallback:function(q){var m=this,p=m.settings[q],o;if(!p){return}if(m.callbackLookup&&(o=m.callbackLookup[q])){p=o.func;o=o.scope}if(d(p,"string")){o=p.replace(/\.\w+$/,"");o=o?k.resolve(o):0;p=k.resolve(p);m.callbackLookup=m.callbackLookup||{};m.callbackLookup[q]={func:p,scope:o}}return p.apply(o||m,Array.prototype.slice.call(arguments,1))},translate:function(m){var o=this.settings.language||"en",n=k.i18n;if(!m){return""}return n[o+"."+m]||m.replace(/\{\#([^\}]+)\}/g,function(q,p){return n[o+"."+p]||"{#"+p+"}"})},getLang:function(o,m){return k.i18n[(this.settings.language||"en")+"."+o]||(d(m)?m:"{#"+o+"}")},getParam:function(t,q,m){var r=k.trim,p=d(this.settings[t])?this.settings[t]:q,s;if(m==="hash"){s={};if(d(p,"string")){i(p.indexOf("=")>0?p.split(/[;,](?![^=;,]*(?:[;,]|$))/):p.split(","),function(n){n=n.split("=");if(n.length>1){s[r(n[0])]=r(n[1])}else{s[r(n[0])]=r(n)}})}else{s=p}return s}return p},nodeChanged:function(q){var m=this,n=m.selection,p;if(m.initialized){q=q||{};p=n.getStart()||m.getBody();p=b&&p.ownerDocument!=m.getDoc()?m.getBody():p;q.parents=[];m.dom.getParent(p,function(o){if(o.nodeName=="BODY"){return true}q.parents.push(o)});m.onNodeChange.dispatch(m,q?q.controlManager||m.controlManager:m.controlManager,p,n.isCollapsed(),q)}},addButton:function(n,o){var m=this;m.buttons=m.buttons||{};m.buttons[n]=o},addCommand:function(m,o,n){this.execCommands[m]={func:o,scope:n||this}},addQueryStateHandler:function(m,o,n){this.queryStateCommands[m]={func:o,scope:n||this}},addQueryValueHandler:function(m,o,n){this.queryValueCommands[m]={func:o,scope:n||this}},addShortcut:function(o,q,m,p){var n=this,r;if(n.settings.custom_shortcuts===false){return false}n.shortcuts=n.shortcuts||{};if(d(m,"string")){r=m;m=function(){n.execCommand(r,false,null)}}if(d(m,"object")){r=m;m=function(){n.execCommand(r[0],r[1],r[2])}}i(g(o),function(s){var t={func:m,scope:p||this,desc:n.translate(q),alt:false,ctrl:false,shift:false};i(g(s,"+"),function(u){switch(u){case"alt":case"ctrl":case"shift":t[u]=true;break;default:t.charCode=u.charCodeAt(0);t.keyCode=u.toUpperCase().charCodeAt(0)}});n.shortcuts[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t});return true},execCommand:function(u,r,x,m){var p=this,q=0,v,n;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(u)&&(!m||!m.skip_focus)){p.focus()}m=f({},m);p.onBeforeExecCommand.dispatch(p,u,r,x,m);if(m.terminate){return false}if(p.execCallback("execcommand_callback",p.id,p.selection.getNode(),u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(v=p.execCommands[u]){n=v.func.call(v.scope,r,x);if(n!==true){p.onExecCommand.dispatch(p,u,r,x,m);return n}}i(p.plugins,function(o){if(o.execCommand&&o.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);q=1;return false}});if(q){return true}if(p.theme&&p.theme.execCommand&&p.theme.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(p.editorCommands.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}p.getDoc().execCommand(u,r,x);p.onExecCommand.dispatch(p,u,r,x,m)},queryCommandState:function(q){var n=this,r,p;if(n._isHidden()){return}if(r=n.queryStateCommands[q]){p=r.func.call(r.scope);if(p!==true){return p}}r=n.editorCommands.queryCommandState(q);if(r!==-1){return r}try{return this.getDoc().queryCommandState(q)}catch(m){}},queryCommandValue:function(r){var n=this,q,p;if(n._isHidden()){return}if(q=n.queryValueCommands[r]){p=q.func.call(q.scope);if(p!==true){return p}}q=n.editorCommands.queryCommandValue(r);if(d(q)){return q}try{return this.getDoc().queryCommandValue(r)}catch(m){}},show:function(){var m=this;l.show(m.getContainer());l.hide(m.id);m.load()},hide:function(){var m=this,n=m.getDoc();if(b&&n){n.execCommand("SelectAll")}m.save();l.hide(m.getContainer());l.setStyle(m.id,"display",m.orgDisplay)},isHidden:function(){return !l.isHidden(this.id)},setProgressState:function(m,n,p){this.onSetProgressState.dispatch(this,m,n,p);return m},load:function(q){var m=this,p=m.getElement(),n;if(p){q=q||{};q.load=true;n=m.setContent(d(p.value)?p.value:p.innerHTML,q);q.element=p;if(!q.no_events){m.onLoadContent.dispatch(m,q)}q.element=p=null;return n}},save:function(r){var m=this,q=m.getElement(),n,p;if(!q||!m.initialized){return}r=r||{};r.save=true;r.element=q;n=r.content=m.getContent(r);if(!r.no_events){m.onSaveContent.dispatch(m,r)}n=r.content;if(!/TEXTAREA|INPUT/i.test(q.nodeName)){q.innerHTML=n;if(p=l.getParent(m.id,"form")){i(p.elements,function(o){if(o.name==m.id){o.value=n;return false}})}}else{q.value=n}r.element=q=null;return n},setContent:function(r,p){var o=this,n,m=o.getBody(),q;p=p||{};p.format=p.format||"html";p.set=true;p.content=r;if(!p.no_events){o.onBeforeSetContent.dispatch(o,p)}r=p.content;if(!k.isIE&&(r.length===0||/^\s+$/.test(r))){q=o.settings.forced_root_block;if(q){r="<"+q+'><br data-mce-bogus="1"></'+q+">"}else{r='<br data-mce-bogus="1">'}m.innerHTML=r;o.selection.select(m,true);o.selection.collapse(true);return}if(p.format!=="raw"){r=new k.html.Serializer({},o.schema).serialize(o.parser.parse(r))}p.content=k.trim(r);o.dom.setHTML(m,p.content);if(!p.no_events){o.onSetContent.dispatch(o,p)}if(!o.settings.content_editable||document.activeElement===o.getBody()){o.selection.normalize()}return p.content},getContent:function(o){var n=this,p,m=n.getBody();o=o||{};o.format=o.format||"html";o.get=true;o.getInner=true;if(!o.no_events){n.onBeforeGetContent.dispatch(n,o)}if(o.format=="raw"){p=m.innerHTML}else{if(o.format=="text"){p=m.innerText||m.textContent}else{p=n.serializer.serialize(m,o)}}if(o.format!="text"){o.content=k.trim(p)}else{o.content=p}if(!o.no_events){n.onGetContent.dispatch(n,o)}return o.content},isDirty:function(){var m=this;return k.trim(m.startContent)!=k.trim(m.getContent({format:"raw",no_events:1}))&&!m.isNotDirty},getContainer:function(){var m=this;if(!m.container){m.container=l.get(m.editorContainer||m.id+"_parent")}return m.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return l.get(this.settings.content_element||this.id)},getWin:function(){var m=this,n;if(!m.contentWindow){n=l.get(m.id+"_ifr");if(n){m.contentWindow=n.contentWindow}}return m.contentWindow},getDoc:function(){var m=this,n;if(!m.contentDocument){n=m.getWin();if(n){m.contentDocument=n.document}}return m.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(o,n,q){var m=this,p=m.settings;if(p.urlconverter_callback){return m.execCallback("urlconverter_callback",o,q,true,n)}if(!p.convert_urls||(q&&q.nodeName=="LINK")||o.indexOf("file:")===0){return o}if(p.relative_urls){return m.documentBaseURI.toRelative(o)}o=m.documentBaseURI.toAbsolute(o,p.remove_script_host);return o},addVisual:function(q){var n=this,o=n.settings,p=n.dom,m;q=q||n.getBody();if(!d(n.hasVisual)){n.hasVisual=o.visual}i(p.select("table,a",q),function(s){var r;switch(s.nodeName){case"TABLE":m=o.visual_table_class||"mceItemTable";r=p.getAttrib(s,"border");if(!r||r=="0"){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}return;case"A":if(!p.getAttrib(s,"href",false)){r=p.getAttrib(s,"name")||s.id;m="mceItemAnchor";if(r){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}}return}});n.onVisualAid.dispatch(n,q,n.hasVisual)},remove:function(){var m=this,o=m.getContainer(),n=m.getDoc();if(!m.removed){m.removed=1;if(b&&n){n.execCommand("SelectAll")}m.save();l.setStyle(m.id,"display",m.orgDisplay);if(!m.settings.content_editable){j.unbind(m.getWin());j.unbind(m.getDoc())}j.unbind(m.getBody());j.clear(o);m.execCallback("remove_instance_callback",m);m.onRemove.dispatch(m);m.onExecCommand.listeners=[];k.remove(m);l.remove(o)}},destroy:function(n){var m=this;if(m.destroyed){return}if(a){j.unbind(m.getDoc());j.unbind(m.getWin());j.unbind(m.getBody())}if(!n){k.removeUnload(m.destroy);tinyMCE.onBeforeUnload.remove(m._beforeUnload);if(m.theme&&m.theme.destroy){m.theme.destroy()}m.controlManager.destroy();m.selection.destroy();m.dom.destroy()}if(m.formElement){m.formElement.submit=m.formElement._mceOldSubmit;m.formElement._mceOldSubmit=null}m.contentAreaContainer=m.formElement=m.container=m.settings.content_element=m.bodyElement=m.contentDocument=m.contentWindow=null;if(m.selection){m.selection=m.selection.win=m.selection.dom=m.selection.dom.doc=null}m.destroyed=1},_refreshContentEditable:function(){var n=this,m,o;if(n._isHidden()){m=n.getBody();o=m.parentNode;o.removeChild(m);o.appendChild(m);m.focus()}},_isHidden:function(){var m;if(!a){return 0}m=this.selection.getSel();return(!m||!m.rangeCount||m.rangeCount===0)}})})(tinymce);(function(a){var b=a.each;a.Editor.prototype.setupEvents=function(){var c=this,d=c.settings;b(["onPreInit","onBeforeRenderUI","onPostRender","onLoad","onInit","onRemove","onActivate","onDeactivate","onClick","onEvent","onMouseUp","onMouseDown","onDblClick","onKeyDown","onKeyUp","onKeyPress","onContextMenu","onSubmit","onReset","onPaste","onPreProcess","onPostProcess","onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent","onLoadContent","onSaveContent","onNodeChange","onChange","onBeforeExecCommand","onExecCommand","onUndo","onRedo","onVisualAid","onSetProgressState","onSetAttrib"],function(e){c[e]=new a.util.Dispatcher(c)});if(d.cleanup_callback){c.onBeforeSetContent.add(function(e,f){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)});c.onPreProcess.add(function(e,f){if(f.set){e.execCallback("cleanup_callback","insert_to_editor_dom",f.node,f)}if(f.get){e.execCallback("cleanup_callback","get_from_editor_dom",f.node,f)}});c.onPostProcess.add(function(e,f){if(f.set){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)}if(f.get){f.content=e.execCallback("cleanup_callback","get_from_editor",f.content,f)}})}if(d.save_callback){c.onGetContent.add(function(e,f){if(f.save){f.content=e.execCallback("save_callback",e.id,f.content,e.getBody())}})}if(d.handle_event_callback){c.onEvent.add(function(f,g,h){if(c.execCallback("handle_event_callback",g,f,h)===false){g.preventDefault();g.stopPropagation()}})}if(d.handle_node_change_callback){c.onNodeChange.add(function(f,e,g){f.execCallback("handle_node_change_callback",f.id,g,-1,-1,true,f.selection.isCollapsed())})}if(d.save_callback){c.onSaveContent.add(function(e,g){var f=e.execCallback("save_callback",e.id,g.content,e.getBody());if(f){g.content=f}})}if(d.onchange_callback){c.onChange.add(function(f,e){f.execCallback("onchange_callback",f,e)})}};a.Editor.prototype.bindNativeEvents=function(){var l=this,f,d=l.settings,e=l.dom,h;h={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function c(i,m){var n=i.type;if(l.removed){return}if(l.onEvent.dispatch(l,i,m)!==false){l[h[i.fakeType||i.type]].dispatch(l,i,m)}}function j(i){l.focus(true)}function k(i,m){if(m.keyCode!=65||!a.VK.metaKeyPressed(m)){l.selection.normalize()}l.nodeChanged()}b(h,function(m,n){var i=d.content_editable?l.getBody():l.getDoc();switch(n){case"contextmenu":e.bind(i,n,c);break;case"paste":e.bind(l.getBody(),n,c);break;case"submit":case"reset":e.bind(l.getElement().form||a.DOM.getParent(l.id,"form"),n,c);break;default:e.bind(i,n,c)}});e.bind(d.content_editable?l.getBody():(a.isGecko?l.getDoc():l.getWin()),"focus",function(i){l.focus(true)});if(d.content_editable&&a.isOpera){e.bind(l.getBody(),"click",j);e.bind(l.getBody(),"keydown",j)}l.onMouseUp.add(k);l.onKeyUp.add(function(i,n){var m=n.keyCode;if((m>=33&&m<=36)||(m>=37&&m<=40)||m==13||m==45||m==46||m==8||(a.isMac&&(m==91||m==93))||n.ctrlKey){k(i,n)}});l.onReset.add(function(){l.setContent(l.startContent,{format:"raw"})});function g(m,i){if(m.altKey||m.ctrlKey||m.metaKey){b(l.shortcuts,function(n){var o=a.isMac?m.metaKey:m.ctrlKey;if(n.ctrl!=o||n.alt!=m.altKey||n.shift!=m.shiftKey){return}if(m.keyCode==n.keyCode||(m.charCode&&m.charCode==n.charCode)){m.preventDefault();if(i){n.func.call(n.scope)}return true}})}}l.onKeyUp.add(function(i,m){g(m)});l.onKeyPress.add(function(i,m){g(m)});l.onKeyDown.add(function(i,m){g(m,true)});if(a.isOpera){l.onClick.add(function(i,m){m.preventDefault()})}}})(tinymce);(function(d){var e=d.each,b,a=true,c=false;d.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return c}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return c}function u(v,x){x=x||"exec";e(v,function(z,y){e(y.toLowerCase().split(","),function(A){j[x][A]=z})})}d.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===b){x=c}if(v===b){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:b)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(d.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(c)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);e("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=d.explode(k.font_size_style_values);v=d.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return c}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new d.html.Serializer({},n.schema);v='<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=p.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.y<L.y)||(C.x>L.x+L.w||C.x<L.x)){H=d.isIE?n.getDoc().documentElement:n.getBody();H.scrollLeft=C.x;H.scrollTop=C.y-L.h+25}x=m.createRng();A=D.previousSibling;if(A&&A.nodeType==3){x.setStart(A,A.nodeValue.length)}else{x.setStartBefore(D);x.setEndBefore(D)}m.remove(D);p.setRng(x);p.onSetContent.dispatch(p,G);n.addVisual()},mceInsertRawHTML:function(y,x,v){p.setContent("tiny_mce_marker");n.setContent(n.getContent().replace(/tiny_mce_marker/g,function(){return v}))},mceToggleFormat:function(y,x,v){s(v)},mceSetContent:function(y,x,v){n.setContent(v)},"Indent,Outdent":function(z){var x,v,y;x=k.indentation;v=/[a-z%]+$/i.exec(x);x=parseInt(x);if(!l("InsertUnorderedList")&&!l("InsertOrderedList")){if(!k.forced_root_block&&!m.getParent(p.getNode(),m.isBlock)){q.apply("div")}e(p.getSelectedBlocks(),function(A){if(z=="outdent"){y=Math.max(0,parseInt(A.style.paddingLeft||0)-x);m.setStyle(A,"paddingLeft",y?y+v:"")}else{m.setStyle(A,"paddingLeft",(parseInt(A.style.paddingLeft||0)+x)+v)}})}else{f(z)}},mceRepaint:function(){var x;if(d.isGecko){try{i(a);if(p.getSel()){p.getSel().selectAllChildren(n.getBody())}p.collapse(a);g()}catch(v){}}},mceToggleFormat:function(y,x,v){q.toggle(v)},InsertHorizontalRule:function(){n.execCommand("mceInsertContent",false,"<hr />")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();if(p.getRng().setStart){v.setStart(x,0);v.setEnd(x,x.childNodes.length);p.setRng(v)}else{f("SelectAll")}}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(z){var x="align"+z.substring(7);var v=p.isCollapsed()?[m.getParent(p.getNode(),m.isBlock)]:p.getSelectedBlocks();var y=d.map(v,function(A){return !!q.matchNode(A,x)});return d.inArray(y,a)!==-1},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(x){var v=m.getParent(p.getNode(),"ul,ol");return v&&(x==="insertunorderedlist"&&v.tagName==="UL"||x==="insertorderedlist"&&v.tagName==="OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(h){var l,i=0,e=[],g,k,j,f;function c(){return b.trim(h.getContent({format:"raw",no_events:1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g,""))}function d(){l.typing=false;l.add()}onBeforeAdd=new a(l);k=new a(l);j=new a(l);f=new a(l);k.add(function(m,n){if(m.hasUndo()){return h.onChange.dispatch(h,n,m)}});j.add(function(m,n){return h.onUndo.dispatch(h,n,m)});f.add(function(m,n){return h.onRedo.dispatch(h,n,m)});h.onInit.add(function(){l.add()});h.onBeforeExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.beforeChange()}});h.onExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.add()}});h.onSaveContent.add(d);h.dom.bind(h.dom.getRoot(),"dragend",d);h.dom.bind(h.getBody(),"focusout",function(m){if(!h.removed&&l.typing){d()}});h.onKeyUp.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45||n==13||o.ctrlKey){d()}});h.onKeyDown.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45){if(l.typing){d()}return}if((n<16||n>20)&&n!=224&&n!=91&&!l.typing){l.beforeChange();l.typing=true;l.add()}});h.onMouseDown.add(function(m,n){if(l.typing){d()}});h.addShortcut("ctrl+z","undo_desc","Undo");h.addShortcut("ctrl+y","redo_desc","Redo");l={data:e,typing:false,onBeforeAdd:onBeforeAdd,onAdd:k,onUndo:j,onRedo:f,beforeChange:function(){g=h.selection.getBookmark(2,true)},add:function(p){var m,n=h.settings,o;p=p||{};p.content=c();l.onBeforeAdd.dispatch(l,p);o=e[i];if(o&&o.content==p.content){return null}if(e[i]){e[i].beforeBookmark=g}if(n.custom_undo_redo_levels){if(e.length>n.custom_undo_redo_levels){for(m=0;m<e.length-1;m++){e[m]=e[m+1]}e.length--;i=e.length}}p.bookmark=h.selection.getBookmark(2,true);if(i<e.length-1){e.length=i+1}e.push(p);i=e.length-1;l.onAdd.dispatch(l,p);h.isNotDirty=0;return p},undo:function(){var n,m;if(l.typing){l.add();l.typing=false}if(i>0){n=e[--i];h.setContent(n.content,{format:"raw"});h.selection.moveToBookmark(n.beforeBookmark);l.onUndo.dispatch(l,n)}return n},redo:function(){var m;if(i<e.length-1){m=e[++i];h.setContent(m.content,{format:"raw"});h.selection.moveToBookmark(m.bookmark);l.onRedo.dispatch(l,m)}return m},clear:function(){e=[];i=0;l.typing=false},hasUndo:function(){return i>0||this.typing},hasRedo:function(){return i<e.length-1&&!this.typing}};return l}})(tinymce);tinymce.ForceBlocks=function(c){var b=c.settings,e=c.dom,a=c.selection,d=c.schema.getBlockElements();function f(){var j=a.getStart(),h=c.getBody(),g,k,o,s,q,i,l,m=-16777215,p,r;if(!j||j.nodeType!==1||!b.forced_root_block){return}while(j&&j!=h){if(d[j.nodeName]){return}j=j.parentNode}g=a.getRng();if(g.setStart){k=g.startContainer;o=g.startOffset;s=g.endContainer;q=g.endOffset}else{if(g.item){j=g.item(0);g=c.getDoc().body.createTextRange();g.moveToElementText(j)}r=g.parentElement().ownerDocument===c.getDoc();tmpRng=g.duplicate();tmpRng.collapse(true);o=tmpRng.move("character",m)*-1;if(!tmpRng.collapsed){tmpRng=g.duplicate();tmpRng.collapse(false);q=(tmpRng.move("character",m)*-1)-o}}j=h.firstChild;while(j){if(j.nodeType===3||(j.nodeType==1&&!d[j.nodeName])){if(j.nodeType===3&&j.nodeValue.length==0){l=j;j=j.nextSibling;e.remove(l);continue}if(!i){i=e.create(b.forced_root_block);j.parentNode.insertBefore(i,j);p=true}l=j;j=j.nextSibling;i.appendChild(l)}else{i=null;j=j.nextSibling}}if(p){if(g.setStart){g.setStart(k,o);g.setEnd(s,q);a.setRng(g)}else{if(r){try{g=c.getDoc().body.createTextRange();g.moveToElementText(h);g.collapse(true);g.moveStart("character",o);if(q>0){g.moveEnd("character",q)}g.select()}catch(n){}}}c.nodeChanged()}}if(b.forced_root_block){c.onKeyUp.add(f);c.onNodeChange.add(f)}};(function(c){var b=c.DOM,a=c.dom.Event,d=c.each,e=c.extend;c.create("tinymce.ControlManager",{ControlManager:function(f,j){var h=this,g;j=j||{};h.editor=f;h.controls={};h.onAdd=new c.util.Dispatcher(h);h.onPostRender=new c.util.Dispatcher(h);h.prefix=j.prefix||f.id+"_";h._cls={};h.onPostRender.add(function(){d(h.controls,function(i){i.postRender()})})},get:function(f){return this.controls[this.prefix+f]||this.controls[f]},setActive:function(h,f){var g=null;if(g=this.get(h)){g.setActive(f)}return g},setDisabled:function(h,f){var g=null;if(g=this.get(h)){g.setDisabled(f)}return g},add:function(g){var f=this;if(g){f.controls[g.id]=g;f.onAdd.dispatch(g,f)}return g},createControl:function(j){var o,k,g,h=this,m=h.editor,n,f;if(!h.controlFactories){h.controlFactories=[];d(m.plugins,function(i){if(i.createControl){h.controlFactories.push(i)}})}n=h.controlFactories;for(k=0,g=n.length;k<g;k++){o=n[k].createControl(j,h);if(o){return h.add(o)}}if(j==="|"||j==="separator"){return h.createSeparator()}if(m.buttons&&(o=m.buttons[j])){return h.createButton(j,o)}return h.add(o)},createDropMenu:function(f,n,h){var m=this,i=m.editor,j,g,k,l;n=e({"class":"mceDropDown",constrain:i.settings.constrain_menus},n);n["class"]=n["class"]+" "+i.getParam("skin")+"Skin";if(k=i.getParam("skin_variant")){n["class"]+=" "+i.getParam("skin")+"Skin"+k.substring(0,1).toUpperCase()+k.substring(1)}n["class"]+=i.settings.directionality=="rtl"?" mceRtl":"";f=m.prefix+f;l=h||m._cls.dropmenu||c.ui.DropMenu;j=m.controls[f]=new l(f,n);j.onAddItem.add(function(r,q){var p=q.settings;p.title=i.getLang(p.title,p.title);if(!p.onclick){p.onclick=function(o){if(p.cmd){i.execCommand(p.cmd,p.ui||false,p.value)}}}});i.onRemove.add(function(){j.destroy()});if(c.isIE){j.onShowMenu.add(function(){i.focus();g=i.selection.getBookmark(1)});j.onHideMenu.add(function(){if(g){i.selection.moveToBookmark(g);g=0}})}return m.add(j)},createListBox:function(f,n,h){var l=this,j=l.editor,i,k,m;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,scope:n.scope,control_manager:l},n);f=l.prefix+f;function g(o){return o.settings.use_accessible_selects&&!c.isGecko}if(j.settings.use_native_selects||g(j)){k=new c.ui.NativeListBox(f,n)}else{m=h||l._cls.listbox||c.ui.ListBox;k=new m(f,n,j)}l.controls[f]=k;if(c.isWebKit){k.onPostRender.add(function(p,o){a.add(o,"mousedown",function(){j.bookmark=j.selection.getBookmark(1)});a.add(o,"focus",function(){j.selection.moveToBookmark(j.bookmark);j.bookmark=null})})}if(k.hideMenu){j.onMouseDown.add(k.hideMenu,k)}return l.add(k)},createButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.label=g.translate(i.label);i.scope=i.scope||g;if(!i.onclick&&!i.menu_button){i.onclick=function(){g.execCommand(i.cmd,i.ui||false,i.value)}}i=e({title:i.title,"class":"mce_"+m,unavailable_prefix:g.getLang("unavailable",""),scope:i.scope,control_manager:h},i);m=h.prefix+m;if(i.menu_button){f=l||h._cls.menubutton||c.ui.MenuButton;k=new f(m,i,g);g.onMouseDown.add(k.hideMenu,k)}else{f=h._cls.button||c.ui.Button;k=new f(m,i,g)}return h.add(k)},createMenuButton:function(h,f,g){f=f||{};f.menu_button=1;return this.createButton(h,f,g)},createSplitButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onclick){i.onclick=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;f=l||h._cls.splitbutton||c.ui.SplitButton;k=h.add(new f(m,i,g));g.onMouseDown.add(k.hideMenu,k);return k},createColorSplitButton:function(f,n,h){var l=this,j=l.editor,i,k,m,g;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onclick){n.onclick=function(o){if(c.isIE){g=j.selection.getBookmark(1)}j.execCommand(n.cmd,n.ui||false,o||n.value)}}if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,menu_class:j.getParam("skin")+"Skin",scope:n.scope,more_colors_title:j.getLang("more_colors")},n);f=l.prefix+f;m=h||l._cls.colorsplitbutton||c.ui.ColorSplitButton;k=new m(f,n,j);j.onMouseDown.add(k.hideMenu,k);j.onRemove.add(function(){k.destroy()});if(c.isIE){k.onShowMenu.add(function(){j.focus();g=j.selection.getBookmark(1)});k.onHideMenu.add(function(){if(g){j.selection.moveToBookmark(g);g=0}})}return l.add(k)},createToolbar:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||g._cls.toolbar||c.ui.Toolbar;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createToolbarGroup:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||this._cls.toolbarGroup||c.ui.ToolbarGroup;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createSeparator:function(g){var f=g||this._cls.separator||c.ui.Separator;return new f()},setControlType:function(g,f){return this._cls[g.toLowerCase()]=f},destroy:function(){d(this.controls,function(f){f.destroy()});this.controls=null}})})(tinymce);(function(d){var a=d.util.Dispatcher,e=d.each,c=d.isIE,b=d.isOpera;d.create("tinymce.WindowManager",{WindowManager:function(f){var g=this;g.editor=f;g.onOpen=new a(g);g.onClose=new a(g);g.params={};g.features={}},open:function(z,h){var v=this,k="",n,m,i=v.editor.settings.dialog_type=="modal",q,o,j,g=d.DOM.getViewPort(),r;z=z||{};h=h||{};o=b?g.w:screen.width;j=b?g.h:screen.height;z.name=z.name||"mc_"+new Date().getTime();z.width=parseInt(z.width||320);z.height=parseInt(z.height||240);z.resizable=true;z.left=z.left||parseInt(o/2)-(z.width/2);z.top=z.top||parseInt(j/2)-(z.height/2);h.inline=false;h.mce_width=z.width;h.mce_height=z.height;h.mce_auto_focus=z.auto_focus;if(i){if(c){z.center=true;z.help=false;z.dialogWidth=z.width+"px";z.dialogHeight=z.height+"px";z.scroll=z.scrollbars||false}}e(z,function(p,f){if(d.is(p,"boolean")){p=p?"yes":"no"}if(!/^(name|url)$/.test(f)){if(c&&i){k+=(k?";":"")+f+":"+p}else{k+=(k?",":"")+f+"="+p}}});v.features=z;v.params=h;v.onOpen.dispatch(v,z,h);r=z.url||z.file;r=d._addVer(r);try{if(c&&i){q=1;window.showModalDialog(r,window,k)}else{q=window.open(r,z.name,k)}}catch(l){}if(!q){alert(v.editor.getLang("popup_blocked"))}},close:function(f){f.close();this.onClose.dispatch(this)},createInstance:function(i,h,g,m,l,k){var j=d.resolve(i);return new j(h,g,m,l,k)},confirm:function(h,f,i,g){g=g||window;f.call(i||this,g.confirm(this._decode(this.editor.getLang(h,h))))},alert:function(h,f,j,g){var i=this;g=g||window;g.alert(i._decode(i.editor.getLang(h,h)));if(f){f.call(j||i)}},resizeBy:function(f,g,h){h.resizeBy(f,g)},_decode:function(f){return d.DOM.decode(f).replace(/\\n/g,"\n")}})}(tinymce));(function(a){a.Formatter=function(aa){var Q={},T=a.each,c=aa.dom,r=aa.selection,t=a.dom.TreeWalker,N=new a.dom.RangeUtils(c),d=aa.schema.isValidChild,A=a.isArray,H=c.isBlock,m=aa.settings.forced_root_block,s=c.nodeIndex,G="\uFEFF",e=/^(src|href|style)$/,X=false,C=true,P,D,x=c.getContentEditable;function I(ab){if(ab.nodeType){ab=ab.nodeName}return !!aa.schema.getTextBlockElements()[ab.toLowerCase()]}function n(ac,ab){return c.getParents(ac,ab,c.getRoot())}function b(ab){return ab.nodeType===1&&ab.id==="_mce_caret"}function j(){l({alignleft:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"},defaultBlock:"div"},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"},defaultBlock:"div"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(ab){return true},onformat:function(ad,ab,ac){T(ac,function(af,ae){c.setAttrib(ad,ae,af)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});T("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(ab){l(ab,{block:ab,remove:"all"})});l(aa.settings.formats)}function W(){aa.addShortcut("ctrl+b","bold_desc","Bold");aa.addShortcut("ctrl+i","italic_desc","Italic");aa.addShortcut("ctrl+u","underline_desc","Underline");for(var ab=1;ab<=6;ab++){aa.addShortcut("ctrl+"+ab,"",["FormatBlock",false,"h"+ab])}aa.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);aa.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);aa.addShortcut("ctrl+9","",["FormatBlock",false,"address"])}function V(ab){return ab?Q[ab]:Q}function l(ab,ac){if(ab){if(typeof(ab)!=="string"){T(ab,function(ae,ad){l(ad,ae)})}else{ac=ac.length?ac:[ac];T(ac,function(ad){if(ad.deep===D){ad.deep=!ad.selector}if(ad.split===D){ad.split=!ad.selector||ad.inline}if(ad.remove===D&&ad.selector&&!ad.inline){ad.remove="none"}if(ad.selector&&ad.inline){ad.mixed=true;ad.block_expand=true}if(typeof(ad.classes)==="string"){ad.classes=ad.classes.split(/\s+/)}});Q[ab]=ac}}}var i=function(ac){var ab;aa.dom.getParent(ac,function(ad){ab=aa.dom.getStyle(ad,"text-decoration");return ab&&ab!=="none"});return ab};var L=function(ab){var ac;if(ab.nodeType===1&&ab.parentNode&&ab.parentNode.nodeType===1){ac=i(ab.parentNode);if(aa.dom.getStyle(ab,"color")&&ac){aa.dom.setStyle(ab,"text-decoration",ac)}else{if(aa.dom.getStyle(ab,"textdecoration")===ac){aa.dom.setStyle(ab,"text-decoration",null)}}}};function Y(ae,al,ag){var ah=V(ae),am=ah[0],ak,ac,aj,ai=r.isCollapsed();function ab(aq,ap){ap=ap||am;if(aq){if(ap.onformat){ap.onformat(aq,ap,al,ag)}T(ap.styles,function(at,ar){c.setStyle(aq,ar,q(at,al))});T(ap.attributes,function(at,ar){c.setAttrib(aq,ar,q(at,al))});T(ap.classes,function(ar){ar=q(ar,al);if(!c.hasClass(aq,ar)){c.addClass(aq,ar)}})}}function af(){function ar(ay,aw){var ax=new t(aw);for(ag=ax.current();ag;ag=ax.prev()){if(ag.childNodes.length>1||ag==ay||ag.tagName=="BR"){return ag}}}var aq=aa.selection.getRng();var av=aq.startContainer;var ap=aq.endContainer;if(av!=ap&&aq.endOffset===0){var au=ar(av,ap);var at=au.nodeType==3?au.length:au.childNodes.length;aq.setEnd(au,at)}return aq}function ad(at,ay,aw,av,aq){var ap=[],ar=-1,ax,aA=-1,au=-1,az;T(at.childNodes,function(aC,aB){if(aC.nodeName==="UL"||aC.nodeName==="OL"){ar=aB;ax=aC;return false}});T(at.childNodes,function(aC,aB){if(aC.nodeName==="SPAN"&&c.getAttrib(aC,"data-mce-type")=="bookmark"){if(aC.id==ay.id+"_start"){aA=aB}else{if(aC.id==ay.id+"_end"){au=aB}}}});if(ar<=0||(aA<ar&&au>ar)){T(a.grep(at.childNodes),aq);return 0}else{az=c.clone(aw,X);T(a.grep(at.childNodes),function(aC,aB){if((aA<ar&&aB<ar)||(aA>ar&&aB>ar)){ap.push(aC);aC.parentNode.removeChild(aC)}});if(aA<ar){at.insertBefore(az,ax)}else{if(aA>ar){at.insertBefore(az,ax.nextSibling)}}av.push(az);T(ap,function(aB){az.appendChild(aB)});return az}}function an(aq,at,aw){var ap=[],av,ar,au=true;av=am.inline||am.block;ar=c.create(av);ab(ar);N.walk(aq,function(ax){var ay;function az(aA){var aF,aD,aB,aC,aE;aE=au;aF=aA.nodeName.toLowerCase();aD=aA.parentNode.nodeName.toLowerCase();if(aA.nodeType===1&&x(aA)){aE=au;au=x(aA)==="true";aC=true}if(g(aF,"br")){ay=0;if(am.block){c.remove(aA)}return}if(am.wrapper&&y(aA,ae,al)){ay=0;return}if(au&&!aC&&am.block&&!am.wrapper&&I(aF)){aA=c.rename(aA,av);ab(aA);ap.push(aA);ay=0;return}if(am.selector){T(ah,function(aG){if("collapsed" in aG&&aG.collapsed!==ai){return}if(c.is(aA,aG.selector)&&!b(aA)){ab(aA,aG);aB=true}});if(!am.inline||aB){ay=0;return}}if(au&&!aC&&d(av,aF)&&d(aD,av)&&!(!aw&&aA.nodeType===3&&aA.nodeValue.length===1&&aA.nodeValue.charCodeAt(0)===65279)&&!b(aA)&&(!am.inline||!H(aA))){if(!ay){ay=c.clone(ar,X);aA.parentNode.insertBefore(ay,aA);ap.push(ay)}ay.appendChild(aA)}else{if(aF=="li"&&at){ay=ad(aA,at,ar,ap,az)}else{ay=0;T(a.grep(aA.childNodes),az);if(aC){au=aE}ay=0}}}T(ax,az)});if(am.wrap_links===false){T(ap,function(ax){function ay(aC){var aB,aA,az;if(aC.nodeName==="A"){aA=c.clone(ar,X);ap.push(aA);az=a.grep(aC.childNodes);for(aB=0;aB<az.length;aB++){aA.appendChild(az[aB])}aC.appendChild(aA)}T(a.grep(aC.childNodes),ay)}ay(ax)})}T(ap,function(az){var ax;function aA(aC){var aB=0;T(aC.childNodes,function(aD){if(!f(aD)&&!K(aD)){aB++}});return aB}function ay(aB){var aD,aC;T(aB.childNodes,function(aE){if(aE.nodeType==1&&!K(aE)&&!b(aE)){aD=aE;return X}});if(aD&&h(aD,am)){aC=c.clone(aD,X);ab(aC);c.replace(aC,aB,C);c.remove(aD,1)}return aC||aB}ax=aA(az);if((ap.length>1||!H(az))&&ax===0){c.remove(az,1);return}if(am.inline||am.wrapper){if(!am.exact&&ax===1){az=ay(az)}T(ah,function(aB){T(c.select(aB.inline,az),function(aD){var aC;if(aB.wrap_links===false){aC=aD.parentNode;do{if(aC.nodeName==="A"){return}}while(aC=aC.parentNode)}Z(aB,al,aD,aB.exact?aD:null)})});if(y(az.parentNode,ae,al)){c.remove(az,1);az=0;return C}if(am.merge_with_parents){c.getParent(az.parentNode,function(aB){if(y(aB,ae,al)){c.remove(az,1);az=0;return C}})}if(az&&am.merge_siblings!==false){az=u(E(az),az);az=u(az,E(az,C))}}})}if(am){if(ag){if(ag.nodeType){ac=c.createRng();ac.setStartBefore(ag);ac.setEndAfter(ag);an(p(ac,ah),null,true)}else{an(ag,null,true)}}else{if(!ai||!am.inline||c.select("td.mceSelected,th.mceSelected").length){var ao=aa.selection.getNode();if(!m&&ah[0].defaultBlock&&!c.getParent(ao,c.isBlock)){Y(ah[0].defaultBlock)}aa.selection.setRng(af());ak=r.getBookmark();an(p(r.getRng(C),ah),ak);if(am.styles&&(am.styles.color||am.styles.textDecoration)){a.walk(ao,L,"childNodes");L(ao)}r.moveToBookmark(ak);R(r.getRng(C));aa.nodeChanged()}else{U("apply",ae,al)}}}}function B(ad,am,af){var ag=V(ad),ao=ag[0],ak,aj,ac,al=true;function ae(av){var au,at,ar,aq,ax,aw;if(av.nodeType===3){return}if(av.nodeType===1&&x(av)){ax=al;al=x(av)==="true";aw=true}au=a.grep(av.childNodes);if(al&&!aw){for(at=0,ar=ag.length;at<ar;at++){if(Z(ag[at],am,av,av)){break}}}if(ao.deep){if(au.length){for(at=0,ar=au.length;at<ar;at++){ae(au[at])}if(aw){al=ax}}}}function ah(aq){var ar;T(n(aq.parentNode).reverse(),function(at){var au;if(!ar&&at.id!="_start"&&at.id!="_end"){au=y(at,ad,am);if(au&&au.split!==false){ar=at}}});return ar}function ab(au,aq,aw,az){var aA,ay,ax,at,av,ar;if(au){ar=au.parentNode;for(aA=aq.parentNode;aA&&aA!=ar;aA=aA.parentNode){ay=c.clone(aA,X);for(av=0;av<ag.length;av++){if(Z(ag[av],am,ay,ay)){ay=0;break}}if(ay){if(ax){ay.appendChild(ax)}if(!at){at=ay}ax=ay}}if(az&&(!ao.mixed||!H(au))){aq=c.split(au,aq)}if(ax){aw.parentNode.insertBefore(ax,aw);at.appendChild(aw)}}return aq}function an(aq){return ab(ah(aq),aq,aq,true)}function ai(at){var ar=c.get(at?"_start":"_end"),aq=ar[at?"firstChild":"lastChild"];if(K(aq)){aq=aq[at?"firstChild":"lastChild"]}c.remove(ar,true);return aq}function ap(aq){var at,au,ar;aq=p(aq,ag,C);if(ao.split){at=M(aq,C);au=M(aq);if(at!=au){if(/^(TR|TD)$/.test(at.nodeName)&&at.firstChild){at=(at.nodeName=="TD"?at.firstChild:at.firstChild.firstChild)||at}at=S(at,"span",{id:"_start","data-mce-type":"bookmark"});au=S(au,"span",{id:"_end","data-mce-type":"bookmark"});an(at);an(au);at=ai(C);au=ai()}else{at=au=an(at)}aq.startContainer=at.parentNode;aq.startOffset=s(at);aq.endContainer=au.parentNode;aq.endOffset=s(au)+1}N.walk(aq,function(av){T(av,function(aw){ae(aw);if(aw.nodeType===1&&aa.dom.getStyle(aw,"text-decoration")==="underline"&&aw.parentNode&&i(aw.parentNode)==="underline"){Z({deep:false,exact:true,inline:"span",styles:{textDecoration:"underline"}},null,aw)}})})}if(af){if(af.nodeType){ac=c.createRng();ac.setStartBefore(af);ac.setEndAfter(af);ap(ac)}else{ap(af)}return}if(!r.isCollapsed()||!ao.inline||c.select("td.mceSelected,th.mceSelected").length){ak=r.getBookmark();ap(r.getRng(C));r.moveToBookmark(ak);if(ao.inline&&k(ad,am,r.getStart())){R(r.getRng(true))}aa.nodeChanged()}else{U("remove",ad,am)}}function F(ac,ae,ad){var ab=V(ac);if(k(ac,ae,ad)&&(!("toggle" in ab[0])||ab[0].toggle)){B(ac,ae,ad)}else{Y(ac,ae,ad)}}function y(ac,ab,ah,af){var ad=V(ab),ai,ag,ae;function aj(an,ap,aq){var am,ao,ak=ap[aq],al;if(ap.onmatch){return ap.onmatch(an,ap,aq)}if(ak){if(ak.length===D){for(am in ak){if(ak.hasOwnProperty(am)){if(aq==="attributes"){ao=c.getAttrib(an,am)}else{ao=O(an,am)}if(af&&!ao&&!ap.exact){return}if((!af||ap.exact)&&!g(ao,q(ak[am],ah))){return}}}}else{for(al=0;al<ak.length;al++){if(aq==="attributes"?c.getAttrib(an,ak[al]):O(an,ak[al])){return ap}}}}return ap}if(ad&&ac){for(ag=0;ag<ad.length;ag++){ai=ad[ag];if(h(ac,ai)&&aj(ac,ai,"attributes")&&aj(ac,ai,"styles")){if(ae=ai.classes){for(ag=0;ag<ae.length;ag++){if(!c.hasClass(ac,ae[ag])){return}}}return ai}}}}function k(ad,af,ae){var ac;function ab(ag){ag=c.getParent(ag,function(ah){return !!y(ah,ad,af,true)});return y(ag,ad,af)}if(ae){return ab(ae)}ae=r.getNode();if(ab(ae)){return C}ac=r.getStart();if(ac!=ae){if(ab(ac)){return C}}return X}function v(ai,ah){var af,ag=[],ae={},ad,ac,ab;af=r.getStart();c.getParent(af,function(al){var ak,aj;for(ak=0;ak<ai.length;ak++){aj=ai[ak];if(!ae[aj]&&y(al,aj,ah)){ae[aj]=true;ag.push(aj)}}},c.getRoot());return ag}function z(af){var ah=V(af),ae,ad,ag,ac,ab;if(ah){ae=r.getStart();ad=n(ae);for(ac=ah.length-1;ac>=0;ac--){ab=ah[ac].selector;if(!ab){return C}for(ag=ad.length-1;ag>=0;ag--){if(c.is(ad[ag],ab)){return C}}}}return X}function J(ab,ae,ac){var ad;if(!P){P={};ad={};aa.onNodeChange.addToTop(function(ag,af,ai){var ah=n(ai),aj={};T(P,function(ak,al){T(ah,function(am){if(y(am,al,{},ak.similar)){if(!ad[al]){T(ak,function(an){an(true,{node:am,format:al,parents:ah})});ad[al]=ak}aj[al]=ak;return false}})});T(ad,function(ak,al){if(!aj[al]){delete ad[al];T(ak,function(am){am(false,{node:ai,format:al,parents:ah})})}})})}T(ab.split(","),function(af){if(!P[af]){P[af]=[];P[af].similar=ac}P[af].push(ae)});return this}a.extend(this,{get:V,register:l,apply:Y,remove:B,toggle:F,match:k,matchAll:v,matchNode:y,canApply:z,formatChanged:J});j();W();function h(ab,ac){if(g(ab,ac.inline)){return C}if(g(ab,ac.block)){return C}if(ac.selector){return c.is(ab,ac.selector)}}function g(ac,ab){ac=ac||"";ab=ab||"";ac=""+(ac.nodeName||ac);ab=""+(ab.nodeName||ab);return ac.toLowerCase()==ab.toLowerCase()}function O(ac,ab){var ad=c.getStyle(ac,ab);if(ab=="color"||ab=="backgroundColor"){ad=c.toHex(ad)}if(ab=="fontWeight"&&ad==700){ad="bold"}return""+ad}function q(ab,ac){if(typeof(ab)!="string"){ab=ab(ac)}else{if(ac){ab=ab.replace(/%(\w+)/g,function(ae,ad){return ac[ad]||ae})}}return ab}function f(ab){return ab&&ab.nodeType===3&&/^([\t \r\n]+|)$/.test(ab.nodeValue)}function S(ad,ac,ab){var ae=c.create(ac,ab);ad.parentNode.insertBefore(ae,ad);ae.appendChild(ad);return ae}function p(ab,am,ae){var ap,an,ah,al,ad=ab.startContainer,ai=ab.startOffset,ar=ab.endContainer,ak=ab.endOffset;function ao(aA){var au,ax,az,aw,av,at;au=ax=aA?ad:ar;av=aA?"previousSibling":"nextSibling";at=c.getRoot();function ay(aB){return aB.nodeName=="BR"&&aB.getAttribute("data-mce-bogus")&&!aB.nextSibling}if(au.nodeType==3&&!f(au)){if(aA?ai>0:ak<au.nodeValue.length){return au}}for(;;){if(!am[0].block_expand&&H(ax)){return ax}for(aw=ax[av];aw;aw=aw[av]){if(!K(aw)&&!f(aw)&&!ay(aw)){return ax}}if(ax.parentNode==at){au=ax;break}ax=ax.parentNode}return au}function ag(at,au){if(au===D){au=at.nodeType===3?at.length:at.childNodes.length}while(at&&at.hasChildNodes()){at=at.childNodes[au];if(at){au=at.nodeType===3?at.length:at.childNodes.length}}return{node:at,offset:au}}if(ad.nodeType==1&&ad.hasChildNodes()){an=ad.childNodes.length-1;ad=ad.childNodes[ai>an?an:ai];if(ad.nodeType==3){ai=0}}if(ar.nodeType==1&&ar.hasChildNodes()){an=ar.childNodes.length-1;ar=ar.childNodes[ak>an?an:ak-1];if(ar.nodeType==3){ak=ar.nodeValue.length}}function aq(au){var at=au;while(at){if(at.nodeType===1&&x(at)){return x(at)==="false"?at:au}at=at.parentNode}return au}function aj(au,ay,aA){var ax,av,az,at;function aw(aC,aE){var aF,aB,aD=aC.nodeValue;if(typeof(aE)=="undefined"){aE=aA?aD.length:0}if(aA){aF=aD.lastIndexOf(" ",aE);aB=aD.lastIndexOf("\u00a0",aE);aF=aF>aB?aF:aB;if(aF!==-1&&!ae){aF++}}else{aF=aD.indexOf(" ",aE);aB=aD.indexOf("\u00a0",aE);aF=aF!==-1&&(aB===-1||aF<aB)?aF:aB}return aF}if(au.nodeType===3){az=aw(au,ay);if(az!==-1){return{container:au,offset:az}}at=au}ax=new t(au,c.getParent(au,H)||aa.getBody());while(av=ax[aA?"prev":"next"]()){if(av.nodeType===3){at=av;az=aw(av);if(az!==-1){return{container:av,offset:az}}}else{if(H(av)){break}}}if(at){if(aA){ay=0}else{ay=at.length}return{container:at,offset:ay}}}function af(au,at){var av,aw,ay,ax;if(au.nodeType==3&&au.nodeValue.length===0&&au[at]){au=au[at]}av=n(au);for(aw=0;aw<av.length;aw++){for(ay=0;ay<am.length;ay++){ax=am[ay];if("collapsed" in ax&&ax.collapsed!==ab.collapsed){continue}if(c.is(av[aw],ax.selector)){return av[aw]}}}return au}function ac(au,at,aw){var av;if(!am[0].wrapper){av=c.getParent(au,am[0].block)}if(!av){av=c.getParent(au.nodeType==3?au.parentNode:au,I)}if(av&&am[0].wrapper){av=n(av,"ul,ol").reverse()[0]||av}if(!av){av=au;while(av[at]&&!H(av[at])){av=av[at];if(g(av,"br")){break}}}return av||au}ad=aq(ad);ar=aq(ar);if(K(ad.parentNode)||K(ad)){ad=K(ad)?ad:ad.parentNode;ad=ad.nextSibling||ad;if(ad.nodeType==3){ai=0}}if(K(ar.parentNode)||K(ar)){ar=K(ar)?ar:ar.parentNode;ar=ar.previousSibling||ar;if(ar.nodeType==3){ak=ar.length}}if(am[0].inline){if(ab.collapsed){al=aj(ad,ai,true);if(al){ad=al.container;ai=al.offset}al=aj(ar,ak);if(al){ar=al.container;ak=al.offset}}ah=ag(ar,ak);if(ah.node){while(ah.node&&ah.offset===0&&ah.node.previousSibling){ah=ag(ah.node.previousSibling)}if(ah.node&&ah.offset>0&&ah.node.nodeType===3&&ah.node.nodeValue.charAt(ah.offset-1)===" "){if(ah.offset>1){ar=ah.node;ar.splitText(ah.offset-1)}}}}if(am[0].inline||am[0].block_expand){if(!am[0].inline||(ad.nodeType!=3||ai===0)){ad=ao(true)}if(!am[0].inline||(ar.nodeType!=3||ak===ar.nodeValue.length)){ar=ao()}}if(am[0].selector&&am[0].expand!==X&&!am[0].inline){ad=af(ad,"previousSibling");ar=af(ar,"nextSibling")}if(am[0].block||am[0].selector){ad=ac(ad,"previousSibling");ar=ac(ar,"nextSibling");if(am[0].block){if(!H(ad)){ad=ao(true)}if(!H(ar)){ar=ao()}}}if(ad.nodeType==1){ai=s(ad);ad=ad.parentNode}if(ar.nodeType==1){ak=s(ar)+1;ar=ar.parentNode}return{startContainer:ad,startOffset:ai,endContainer:ar,endOffset:ak}}function Z(ah,ag,ae,ab){var ad,ac,af;if(!h(ae,ah)){return X}if(ah.remove!="all"){T(ah.styles,function(aj,ai){aj=q(aj,ag);if(typeof(ai)==="number"){ai=aj;ab=0}if(!ab||g(O(ab,ai),aj)){c.setStyle(ae,ai,"")}af=1});if(af&&c.getAttrib(ae,"style")==""){ae.removeAttribute("style");ae.removeAttribute("data-mce-style")}T(ah.attributes,function(ak,ai){var aj;ak=q(ak,ag);if(typeof(ai)==="number"){ai=ak;ab=0}if(!ab||g(c.getAttrib(ab,ai),ak)){if(ai=="class"){ak=c.getAttrib(ae,ai);if(ak){aj="";T(ak.split(/\s+/),function(al){if(/mce\w+/.test(al)){aj+=(aj?" ":"")+al}});if(aj){c.setAttrib(ae,ai,aj);return}}}if(ai=="class"){ae.removeAttribute("className")}if(e.test(ai)){ae.removeAttribute("data-mce-"+ai)}ae.removeAttribute(ai)}});T(ah.classes,function(ai){ai=q(ai,ag);if(!ab||c.hasClass(ab,ai)){c.removeClass(ae,ai)}});ac=c.getAttribs(ae);for(ad=0;ad<ac.length;ad++){if(ac[ad].nodeName.indexOf("_")!==0){return X}}}if(ah.remove!="none"){o(ae,ah);return C}}function o(ad,ae){var ab=ad.parentNode,ac;function af(ah,ag,ai){ah=E(ah,ag,ai);return !ah||(ah.nodeName=="BR"||H(ah))}if(ae.block){if(!m){if(H(ad)&&!H(ab)){if(!af(ad,X)&&!af(ad.firstChild,C,1)){ad.insertBefore(c.create("br"),ad.firstChild)}if(!af(ad,C)&&!af(ad.lastChild,X,1)){ad.appendChild(c.create("br"))}}}else{if(ab==c.getRoot()){if(!ae.list_block||!g(ad,ae.list_block)){T(a.grep(ad.childNodes),function(ag){if(d(m,ag.nodeName.toLowerCase())){if(!ac){ac=S(ag,m)}else{ac.appendChild(ag)}}else{ac=0}})}}}}if(ae.selector&&ae.inline&&!g(ae.inline,ad)){return}c.remove(ad,1)}function E(ac,ab,ad){if(ac){ab=ab?"nextSibling":"previousSibling";for(ac=ad?ac:ac[ab];ac;ac=ac[ab]){if(ac.nodeType==1||!f(ac)){return ac}}}}function K(ab){return ab&&ab.nodeType==1&&ab.getAttribute("data-mce-type")=="bookmark"}function u(af,ae){var ab,ad,ac;function ah(ak,aj){if(ak.nodeName!=aj.nodeName){return X}function ai(am){var an={};T(c.getAttribs(am),function(ao){var ap=ao.nodeName.toLowerCase();if(ap.indexOf("_")!==0&&ap!=="style"){an[ap]=c.getAttrib(am,ap)}});return an}function al(ap,ao){var an,am;for(am in ap){if(ap.hasOwnProperty(am)){an=ao[am];if(an===D){return X}if(ap[am]!=an){return X}delete ao[am]}}for(am in ao){if(ao.hasOwnProperty(am)){return X}}return C}if(!al(ai(ak),ai(aj))){return X}if(!al(c.parseStyle(c.getAttrib(ak,"style")),c.parseStyle(c.getAttrib(aj,"style")))){return X}return C}function ag(aj,ai){for(ad=aj;ad;ad=ad[ai]){if(ad.nodeType==3&&ad.nodeValue.length!==0){return aj}if(ad.nodeType==1&&!K(ad)){return ad}}return aj}if(af&&ae){af=ag(af,"previousSibling");ae=ag(ae,"nextSibling");if(ah(af,ae)){for(ad=af.nextSibling;ad&&ad!=ae;){ac=ad;ad=ad.nextSibling;af.appendChild(ac)}c.remove(ae);T(a.grep(ae.childNodes),function(ai){af.appendChild(ai)});return af}}return ae}function M(ac,ag){var ab,af,ad,ae;ab=ac[ag?"startContainer":"endContainer"];af=ac[ag?"startOffset":"endOffset"];if(ab.nodeType==1){ad=ab.childNodes.length-1;if(!ag&&af){af--}ab=ab.childNodes[af>ad?ad:af]}if(ab.nodeType===3&&ag&&af>=ab.nodeValue.length){ab=new t(ab,aa.getBody()).next()||ab}if(ab.nodeType===3&&!ag&&af===0){ab=new t(ab,aa.getBody()).prev()||ab}return ab}function U(ak,ab,ai){var al="_mce_caret",ac=aa.settings.caret_debug;function ad(ap){var ao=c.create("span",{id:al,"data-mce-bogus":true,style:ac?"color:red":""});if(ap){ao.appendChild(aa.getDoc().createTextNode(G))}return ao}function aj(ap,ao){while(ap){if((ap.nodeType===3&&ap.nodeValue!==G)||ap.childNodes.length>1){return false}if(ao&&ap.nodeType===1){ao.push(ap)}ap=ap.firstChild}return true}function ag(ao){while(ao){if(ao.id===al){return ao}ao=ao.parentNode}}function af(ao){var ap;if(ao){ap=new t(ao,ao);for(ao=ap.current();ao;ao=ap.next()){if(ao.nodeType===3){return ao}}}}function ae(aq,ap){var ar,ao;if(!aq){aq=ag(r.getStart());if(!aq){while(aq=c.get(al)){ae(aq,false)}}}else{ao=r.getRng(true);if(aj(aq)){if(ap!==false){ao.setStartBefore(aq);ao.setEndBefore(aq)}c.remove(aq)}else{ar=af(aq);if(ar.nodeValue.charAt(0)===G){ar=ar.deleteData(0,1)}c.remove(aq,1)}r.setRng(ao)}}function ah(){var aq,ao,av,au,ar,ap,at;aq=r.getRng(true);au=aq.startOffset;ap=aq.startContainer;at=ap.nodeValue;ao=ag(r.getStart());if(ao){av=af(ao)}if(at&&au>0&&au<at.length&&/\w/.test(at.charAt(au))&&/\w/.test(at.charAt(au-1))){ar=r.getBookmark();aq.collapse(true);aq=p(aq,V(ab));aq=N.split(aq);Y(ab,ai,aq);r.moveToBookmark(ar)}else{if(!ao||av.nodeValue!==G){ao=ad(true);av=ao.firstChild;aq.insertNode(ao);au=1;Y(ab,ai,ao)}else{Y(ab,ai,ao)}r.setCursorLocation(av,au)}}function am(){var ao=r.getRng(true),ap,at,aw,av,aq,az,ay=[],au,ax;ap=ao.startContainer;at=ao.startOffset;aq=ap;if(ap.nodeType==3){if(at!=ap.nodeValue.length||ap.nodeValue===G){av=true}aq=aq.parentNode}while(aq){if(y(aq,ab,ai)){az=aq;break}if(aq.nextSibling){av=true}ay.push(aq);aq=aq.parentNode}if(!az){return}if(av){aw=r.getBookmark();ao.collapse(true);ao=p(ao,V(ab),true);ao=N.split(ao);B(ab,ai,ao);r.moveToBookmark(aw)}else{ax=ad();aq=ax;for(au=ay.length-1;au>=0;au--){aq.appendChild(c.clone(ay[au],false));aq=aq.firstChild}aq.appendChild(c.doc.createTextNode(G));aq=aq.firstChild;var ar=c.getParent(az,I);if(ar&&c.isEmpty(ar)){az.parentNode.replaceChild(ax,az)}else{c.insertAfter(ax,az)}r.setCursorLocation(aq,1);if(c.isEmpty(az)){c.remove(az)}}}function an(){var ap,ao,aq;ao=ag(r.getStart());if(ao&&!c.isEmpty(ao)){a.walk(ao,function(ar){if(ar.nodeType==1&&ar.id!==al&&!c.isEmpty(ar)){c.setAttrib(ar,"data-mce-bogus",null)}},"childNodes")}}if(!self._hasCaretEvents){aa.onBeforeGetContent.addToTop(function(){var ao=[],ap;if(aj(ag(r.getStart()),ao)){ap=ao.length;while(ap--){c.setAttrib(ao[ap],"data-mce-bogus","1")}}});a.each("onMouseUp onKeyUp".split(" "),function(ao){aa[ao].addToTop(function(){ae();an()})});aa.onKeyDown.addToTop(function(ao,aq){var ap=aq.keyCode;if(ap==8||ap==37||ap==39){ae(ag(r.getStart()))}an()});r.onSetContent.add(an);self._hasCaretEvents=true}if(ak=="apply"){ah()}else{am()}}function R(ac){var ab=ac.startContainer,ai=ac.startOffset,ae,ah,ag,ad,af;if(ab.nodeType==3&&ai>=ab.nodeValue.length){ai=s(ab);ab=ab.parentNode;ae=true}if(ab.nodeType==1){ad=ab.childNodes;ab=ad[Math.min(ai,ad.length-1)];ah=new t(ab,c.getParent(ab,c.isBlock));if(ai>ad.length-1||ae){ah.next()}for(ag=ah.current();ag;ag=ah.next()){if(ag.nodeType==3&&!f(ag)){af=c.create("a",null,G);ag.parentNode.insertBefore(af,ag);ac.setStart(ag,0);r.setRng(ac);c.remove(af);return}}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}if(c.inline_styles){h=e.explode(c.font_size_legacy_values);d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size,10)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}});(function(b){var a=b.dom.TreeWalker;b.EnterKey=function(f){var i=f.dom,e=f.selection,d=f.settings,h=f.undoManager,c=f.schema.getNonEmptyElements();function g(B){var v=e.getRng(true),G,j,A,u,p,M,C,o,k,n,t,J,x,D;function E(N){return N&&i.isBlock(N)&&!/^(TD|TH|CAPTION|FORM)$/.test(N.nodeName)&&!/^(fixed|absolute)/i.test(N.style.position)&&i.getContentEditable(N)!=="true"}function F(O){var N;if(b.isIE&&!b.isIE11&&i.isBlock(O)){N=e.getRng();O.appendChild(i.create("span",null,"\u00a0"));e.select(O);O.lastChild.outerHTML="";e.setRng(N)}}function z(P){var O=P,Q=[],N;while(O=O.firstChild){if(i.isBlock(O)){return}if(O.nodeType==1&&!c[O.nodeName.toLowerCase()]){Q.push(O)}}N=Q.length;while(N--){O=Q[N];if(!O.hasChildNodes()||(O.firstChild==O.lastChild&&O.firstChild.nodeValue==="")){i.remove(O)}else{if(O.nodeName=="A"&&(O.innerText||O.textContent)===" "){i.remove(O)}}}}function m(O){var T,R,N,U,S,Q=O,P;N=i.createRng();if(O.hasChildNodes()){T=new a(O,O);while(R=T.current()){if(R.nodeType==3){N.setStart(R,0);N.setEnd(R,0);break}if(c[R.nodeName.toLowerCase()]){N.setStartBefore(R);N.setEndBefore(R);break}Q=R;R=T.next()}if(!R){N.setStart(Q,0);N.setEnd(Q,0)}}else{if(O.nodeName=="BR"){if(O.nextSibling&&i.isBlock(O.nextSibling)){if(!M||M<9){P=i.create("br");O.parentNode.insertBefore(P,O)}N.setStartBefore(O);N.setEndBefore(O)}else{N.setStartAfter(O);N.setEndAfter(O)}}else{N.setStart(O,0);N.setEnd(O,0)}}e.setRng(N);i.remove(P);S=i.getViewPort(f.getWin());U=i.getPos(O).y;if(U<S.y||U+25>S.y+S.h){f.getWin().scrollTo(0,U<S.y?U:U-S.h+25)}}function r(O){var P=A,R,Q,N;R=O||t=="TABLE"?i.create(O||x):p.cloneNode(false);N=R;if(d.keep_styles!==false){do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(P.nodeName)){if(P.id=="_mce_caret"){continue}Q=P.cloneNode(false);i.setAttrib(Q,"id","");if(R.hasChildNodes()){Q.appendChild(R.firstChild);R.appendChild(Q)}else{N=Q;R.appendChild(Q)}}}while(P=P.parentNode)}if(!b.isIE||b.isIE11){N.innerHTML='<br data-mce-bogus="1">'}return R}function q(Q){var P,O,N;if(A.nodeType==3&&(Q?u>0:u<A.nodeValue.length)){return false}if(A.parentNode==p&&D&&!Q){return true}if(Q&&A.nodeType==1&&A==p.firstChild){return true}if(A.nodeName==="TABLE"||(A.previousSibling&&A.previousSibling.nodeName=="TABLE")){return(D&&!Q)||(!D&&Q)}P=new a(A,p);if(A.nodeType==3){if(Q&&u==0){P.prev()}else{if(!Q&&u==A.nodeValue.length){P.next()}}}while(O=P.current()){if(O.nodeType===1){if(!O.getAttribute("data-mce-bogus")){N=O.nodeName.toLowerCase();if(c[N]&&N!=="br"){return false}}}else{if(O.nodeType===3&&!/^[ \t\r\n]*$/.test(O.nodeValue)){return false}}if(Q){P.prev()}else{P.next()}}return true}function l(N,T){var U,S,P,R,Q,O=x||"P";S=i.getParent(N,i.isBlock);if(!S||!E(S)){S=S||j;if(!S.hasChildNodes()){U=i.create(O);S.appendChild(U);v.setStart(U,0);v.setEnd(U,0);return U}R=N;while(R.parentNode!=S){R=R.parentNode}while(R&&!i.isBlock(R)){P=R;R=R.previousSibling}if(P){U=i.create(O);P.parentNode.insertBefore(U,P);R=P;while(R&&!i.isBlock(R)){Q=R.nextSibling;U.appendChild(R);R=Q}v.setStart(N,T);v.setEnd(N,T)}}return N}function H(){function N(P){var O=n[P?"firstChild":"lastChild"];while(O){if(O.nodeType==1){break}O=O[P?"nextSibling":"previousSibling"]}return O===p}o=x?r(x):i.create("BR");if(N(true)&&N()){i.replace(o,n)}else{if(N(true)){n.parentNode.insertBefore(o,n)}else{if(N()){i.insertAfter(o,n);F(o)}else{G=v.cloneRange();G.setStartAfter(p);G.setEndAfter(n);k=G.extractContents();i.insertAfter(k,n);i.insertAfter(o,n)}}}i.remove(p);m(o);h.add()}function y(){var O=new a(A,p),N;while(N=O.next()){if(c[N.nodeName.toLowerCase()]||N.length>0){return true}}}function L(){var P,O,N;if(A&&A.nodeType==3&&u>=A.nodeValue.length){if((!b.isIE||b.isIE11)&&!y()){P=i.create("br");v.insertNode(P);v.setStartAfter(P);v.setEndAfter(P);O=true}}P=i.create("br");v.insertNode(P);if((b.isIE&&!b.isIE11)&&t=="PRE"&&(!M||M<8)){P.parentNode.insertBefore(i.doc.createTextNode("\r"),P)}N=i.create("span",{},"&nbsp;");P.parentNode.insertBefore(N,P);e.scrollIntoView(N);i.remove(N);if(!O){v.setStartAfter(P);v.setEndAfter(P)}else{v.setStartBefore(P);v.setEndBefore(P)}e.setRng(v);h.add()}function s(N){do{if(N.nodeType===3){N.nodeValue=N.nodeValue.replace(/^[\r\n]+/,"")}N=N.firstChild}while(N)}function K(P){var N=i.getRoot(),O,Q;O=P;while(O!==N&&i.getContentEditable(O)!=="false"){if(i.getContentEditable(O)==="true"){Q=O}O=O.parentNode}return O!==N?Q:N}function I(O){var N;if(!b.isIE||b.isIE11){O.normalize();N=O.lastChild;if(!N||(/^(left|right)$/gi.test(i.getStyle(N,"float",true)))){i.add(O,"br")}}}if(!v.collapsed){f.execCommand("Delete");return}if(B.isDefaultPrevented()){return}A=v.startContainer;u=v.startOffset;x=(d.force_p_newlines?"p":"")||d.forced_root_block;x=x?x.toUpperCase():"";M=i.doc.documentMode;C=B.shiftKey;if(A.nodeType==1&&A.hasChildNodes()){D=u>A.childNodes.length-1;A=A.childNodes[Math.min(u,A.childNodes.length-1)]||A;if(D&&A.nodeType==3){u=A.nodeValue.length}else{u=0}}j=K(A);if(!j){return}h.beforeChange();if(!i.isBlock(j)&&j!=i.getRoot()){if(!x||C){L()}return}if((x&&!C)||(!x&&C)){A=l(A,u)}p=i.getParent(A,i.isBlock);n=p?i.getParent(p.parentNode,i.isBlock):null;t=p?p.nodeName.toUpperCase():"";J=n?n.nodeName.toUpperCase():"";if(J=="LI"&&!B.ctrlKey){p=n;t=J}if(t=="LI"){if(!x&&C){L();return}if(i.isEmpty(p)){if(/^(UL|OL|LI)$/.test(n.parentNode.nodeName)){return false}H();return}}if(t=="PRE"&&d.br_in_pre!==false){if(!C){L();return}}else{if((!x&&!C&&t!="LI")||(x&&C)){L();return}}x=x||"P";if(q()){if(/^(H[1-6]|PRE)$/.test(t)&&J!="HGROUP"){o=r(x)}else{o=r()}if(d.end_container_on_empty_block&&E(n)&&i.isEmpty(p)){o=i.split(n,p)}else{i.insertAfter(o,p)}m(o)}else{if(q(true)){o=p.parentNode.insertBefore(r(),p);F(o)}else{G=v.cloneRange();G.setEndAfter(p);k=G.extractContents();s(k);o=k.firstChild;i.insertAfter(k,p);z(o);I(p);m(o)}}i.setAttrib(o,"id","");h.add()}f.onKeyDown.add(function(k,j){if(j.keyCode==13){if(g(j)!==false){j.preventDefault()}}})}})(tinymce);
     1// 4.0.12 (2013-12-18)
     2
     3/**
     4 * Compiled inline version. (Library mode)
     5 */
     6
     7/*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
     8/*globals $code */
     9
     10(function(exports, undefined) {
     11        "use strict";
     12
     13        var modules = {};
     14
     15        function require(ids, callback) {
     16                var module, defs = [];
     17
     18                for (var i = 0; i < ids.length; ++i) {
     19                        module = modules[ids[i]] || resolve(ids[i]);
     20                        if (!module) {
     21                                throw 'module definition dependecy not found: ' + ids[i];
     22                        }
     23
     24                        defs.push(module);
     25                }
     26
     27                callback.apply(null, defs);
     28        }
     29
     30        function define(id, dependencies, definition) {
     31                if (typeof id !== 'string') {
     32                        throw 'invalid module definition, module id must be defined and be a string';
     33                }
     34
     35                if (dependencies === undefined) {
     36                        throw 'invalid module definition, dependencies must be specified';
     37                }
     38
     39                if (definition === undefined) {
     40                        throw 'invalid module definition, definition function must be specified';
     41                }
     42
     43                require(dependencies, function() {
     44                        modules[id] = definition.apply(null, arguments);
     45                });
     46        }
     47
     48        function defined(id) {
     49                return !!modules[id];
     50        }
     51
     52        function resolve(id) {
     53                var target = exports;
     54                var fragments = id.split(/[.\/]/);
     55
     56                for (var fi = 0; fi < fragments.length; ++fi) {
     57                        if (!target[fragments[fi]]) {
     58                                return;
     59                        }
     60
     61                        target = target[fragments[fi]];
     62                }
     63
     64                return target;
     65        }
     66
     67        function expose(ids) {
     68                for (var i = 0; i < ids.length; i++) {
     69                        var target = exports;
     70                        var id = ids[i];
     71                        var fragments = id.split(/[.\/]/);
     72
     73                        for (var fi = 0; fi < fragments.length - 1; ++fi) {
     74                                if (target[fragments[fi]] === undefined) {
     75                                        target[fragments[fi]] = {};
     76                                }
     77
     78                                target = target[fragments[fi]];
     79                        }
     80
     81                        target[fragments[fragments.length - 1]] = modules[id];
     82                }
     83        }
     84
     85// Included from: js/tinymce/classes/dom/EventUtils.js
     86
     87/**
     88 * EventUtils.js
     89 *
     90 * Copyright, Moxiecode Systems AB
     91 * Released under LGPL License.
     92 *
     93 * License: http://www.tinymce.com/license
     94 * Contributing: http://www.tinymce.com/contributing
     95 */
     96
     97/*jshint loopfunc:true*/
     98
     99define("tinymce/dom/EventUtils", [], function() {
     100        "use strict";
     101
     102        var eventExpandoPrefix = "mce-data-";
     103        var mouseEventRe = /^(?:mouse|contextmenu)|click/;
     104        var deprecated = {keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1};
     105
     106        /**
     107         * Binds a native event to a callback on the speified target.
     108         */
     109        function addEvent(target, name, callback, capture) {
     110                if (target.addEventListener) {
     111                        target.addEventListener(name, callback, capture || false);
     112                } else if (target.attachEvent) {
     113                        target.attachEvent('on' + name, callback);
     114                }
     115        }
     116
     117        /**
     118         * Unbinds a native event callback on the specified target.
     119         */
     120        function removeEvent(target, name, callback, capture) {
     121                if (target.removeEventListener) {
     122                        target.removeEventListener(name, callback, capture || false);
     123                } else if (target.detachEvent) {
     124                        target.detachEvent('on' + name, callback);
     125                }
     126        }
     127
     128        /**
     129         * Normalizes a native event object or just adds the event specific methods on a custom event.
     130         */
     131        function fix(originalEvent, data) {
     132                var name, event = data || {}, undef;
     133
     134                // Dummy function that gets replaced on the delegation state functions
     135                function returnFalse() {
     136                        return false;
     137                }
     138
     139                // Dummy function that gets replaced on the delegation state functions
     140                function returnTrue() {
     141                        return true;
     142                }
     143
     144                // Copy all properties from the original event
     145                for (name in originalEvent) {
     146                        // layerX/layerY is deprecated in Chrome and produces a warning
     147                        if (!deprecated[name]) {
     148                                event[name] = originalEvent[name];
     149                        }
     150                }
     151
     152                // Normalize target IE uses srcElement
     153                if (!event.target) {
     154                        event.target = event.srcElement || document;
     155                }
     156
     157                // Calculate pageX/Y if missing and clientX/Y available
     158                if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
     159                        var eventDoc = event.target.ownerDocument || document;
     160                        var doc = eventDoc.documentElement;
     161                        var body = eventDoc.body;
     162
     163                        event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0 ) -
     164                                ( doc && doc.clientLeft || body && body.clientLeft || 0);
     165
     166                        event.pageY = originalEvent.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0 ) -
     167                                ( doc && doc.clientTop  || body && body.clientTop  || 0);
     168                }
     169
     170                // Add preventDefault method
     171                event.preventDefault = function() {
     172                        event.isDefaultPrevented = returnTrue;
     173
     174                        // Execute preventDefault on the original event object
     175                        if (originalEvent) {
     176                                if (originalEvent.preventDefault) {
     177                                        originalEvent.preventDefault();
     178                                } else {
     179                                        originalEvent.returnValue = false; // IE
     180                                }
     181                        }
     182                };
     183
     184                // Add stopPropagation
     185                event.stopPropagation = function() {
     186                        event.isPropagationStopped = returnTrue;
     187
     188                        // Execute stopPropagation on the original event object
     189                        if (originalEvent) {
     190                                if (originalEvent.stopPropagation) {
     191                                        originalEvent.stopPropagation();
     192                                } else {
     193                                        originalEvent.cancelBubble = true; // IE
     194                                }
     195                         }
     196                };
     197
     198                // Add stopImmediatePropagation
     199                event.stopImmediatePropagation = function() {
     200                        event.isImmediatePropagationStopped = returnTrue;
     201                        event.stopPropagation();
     202                };
     203
     204                // Add event delegation states
     205                if (!event.isDefaultPrevented) {
     206                        event.isDefaultPrevented = returnFalse;
     207                        event.isPropagationStopped = returnFalse;
     208                        event.isImmediatePropagationStopped = returnFalse;
     209                }
     210
     211                return event;
     212        }
     213
     214        /**
     215         * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
     216         * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
     217         */
     218        function bindOnReady(win, callback, eventUtils) {
     219                var doc = win.document, event = {type: 'ready'};
     220
     221                if (eventUtils.domLoaded) {
     222                        callback(event);
     223                        return;
     224                }
     225
     226                // Gets called when the DOM is ready
     227                function readyHandler() {
     228                        if (!eventUtils.domLoaded) {
     229                                eventUtils.domLoaded = true;
     230                                callback(event);
     231                        }
     232                }
     233
     234                function waitForDomLoaded() {
     235                        if (doc.readyState === "complete") {
     236                                removeEvent(doc, "readystatechange", waitForDomLoaded);
     237                                readyHandler();
     238                        }
     239                }
     240
     241                function tryScroll() {
     242                        try {
     243                                // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
     244                                // http://javascript.nwbox.com/IEContentLoaded/
     245                                doc.documentElement.doScroll("left");
     246                        } catch (ex) {
     247                                setTimeout(tryScroll, 0);
     248                                return;
     249                        }
     250
     251                        readyHandler();
     252                }
     253
     254                // Use W3C method
     255                if (doc.addEventListener) {
     256                        if (doc.readyState === "complete") {
     257                                readyHandler();
     258                        } else {
     259                                addEvent(win, 'DOMContentLoaded', readyHandler);
     260                        }
     261                } else {
     262                        // Use IE method
     263                        addEvent(doc, "readystatechange", waitForDomLoaded);
     264
     265                        // Wait until we can scroll, when we can the DOM is initialized
     266                        if (doc.documentElement.doScroll && win === win.top) {
     267                                tryScroll();
     268                        }
     269                }
     270
     271                // Fallback if any of the above methods should fail for some odd reason
     272                addEvent(win, 'load', readyHandler);
     273        }
     274
     275        /**
     276         * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
     277         */
     278        function EventUtils() {
     279                var self = this, events = {}, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
     280
     281                expando = eventExpandoPrefix + (+new Date()).toString(32);
     282                hasMouseEnterLeave = "onmouseenter" in document.documentElement;
     283                hasFocusIn = "onfocusin" in document.documentElement;
     284                mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
     285                count = 1;
     286
     287                // State if the DOMContentLoaded was executed or not
     288                self.domLoaded = false;
     289                self.events = events;
     290
     291                /**
     292                 * Executes all event handler callbacks for a specific event.
     293                 *
     294                 * @private
     295                 * @param {Event} evt Event object.
     296                 * @param {String} id Expando id value to look for.
     297                 */
     298                function executeHandlers(evt, id) {
     299                        var callbackList, i, l, callback, container = events[id];
     300
     301                        callbackList = container && container[evt.type];
     302                        if (callbackList) {
     303                                for (i = 0, l = callbackList.length; i < l; i++) {
     304                                        callback = callbackList[i];
     305
     306                                        // Check if callback exists might be removed if a unbind is called inside the callback
     307                                        if (callback && callback.func.call(callback.scope, evt) === false) {
     308                                                evt.preventDefault();
     309                                        }
     310
     311                                        // Should we stop propagation to immediate listeners
     312                                        if (evt.isImmediatePropagationStopped()) {
     313                                                return;
     314                                        }
     315                                }
     316                        }
     317                }
     318
     319                /**
     320                 * Binds a callback to an event on the specified target.
     321                 *
     322                 * @method bind
     323                 * @param {Object} target Target node/window or custom object.
     324                 * @param {String} names Name of the event to bind.
     325                 * @param {function} callback Callback function to execute when the event occurs.
     326                 * @param {Object} scope Scope to call the callback function on, defaults to target.
     327                 * @return {function} Callback function that got bound.
     328                 */
     329                self.bind = function(target, names, callback, scope) {
     330                        var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
     331
     332                        // Native event handler function patches the event and executes the callbacks for the expando
     333                        function defaultNativeHandler(evt) {
     334                                executeHandlers(fix(evt || win.event), id);
     335                        }
     336
     337                        // Don't bind to text nodes or comments
     338                        if (!target || target.nodeType === 3 || target.nodeType === 8) {
     339                                return;
     340                        }
     341
     342                        // Create or get events id for the target
     343                        if (!target[expando]) {
     344                                id = count++;
     345                                target[expando] = id;
     346                                events[id] = {};
     347                        } else {
     348                                id = target[expando];
     349                        }
     350
     351                        // Setup the specified scope or use the target as a default
     352                        scope = scope || target;
     353
     354                        // Split names and bind each event, enables you to bind multiple events with one call
     355                        names = names.split(' ');
     356                        i = names.length;
     357                        while (i--) {
     358                                name = names[i];
     359                                nativeHandler = defaultNativeHandler;
     360                                fakeName = capture = false;
     361
     362                                // Use ready instead of DOMContentLoaded
     363                                if (name === "DOMContentLoaded") {
     364                                        name = "ready";
     365                                }
     366
     367                                // DOM is already ready
     368                                if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
     369                                        callback.call(scope, fix({type: name}));
     370                                        continue;
     371                                }
     372
     373                                // Handle mouseenter/mouseleaver
     374                                if (!hasMouseEnterLeave) {
     375                                        fakeName = mouseEnterLeave[name];
     376
     377                                        if (fakeName) {
     378                                                nativeHandler = function(evt) {
     379                                                        var current, related;
     380
     381                                                        current = evt.currentTarget;
     382                                                        related = evt.relatedTarget;
     383
     384                                                        // Check if related is inside the current target if it's not then the event should
     385                                                        // be ignored since it's a mouseover/mouseout inside the element
     386                                                        if (related && current.contains) {
     387                                                                // Use contains for performance
     388                                                                related = current.contains(related);
     389                                                        } else {
     390                                                                while (related && related !== current) {
     391                                                                        related = related.parentNode;
     392                                                                }
     393                                                        }
     394
     395                                                        // Fire fake event
     396                                                        if (!related) {
     397                                                                evt = fix(evt || win.event);
     398                                                                evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
     399                                                                evt.target = current;
     400                                                                executeHandlers(evt, id);
     401                                                        }
     402                                                };
     403                                        }
     404                                }
     405
     406                                // Fake bubbeling of focusin/focusout
     407                                if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
     408                                        capture = true;
     409                                        fakeName = name === "focusin" ? "focus" : "blur";
     410                                        nativeHandler = function(evt) {
     411                                                evt = fix(evt || win.event);
     412                                                evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
     413                                                executeHandlers(evt, id);
     414                                        };
     415                                }
     416
     417                                // Setup callback list and bind native event
     418                                callbackList = events[id][name];
     419                                if (!callbackList) {
     420                                        events[id][name] = callbackList = [{func: callback, scope: scope}];
     421                                        callbackList.fakeName = fakeName;
     422                                        callbackList.capture = capture;
     423
     424                                        // Add the nativeHandler to the callback list so that we can later unbind it
     425                                        callbackList.nativeHandler = nativeHandler;
     426
     427                                        // Check if the target has native events support
     428
     429                                        if (name === "ready") {
     430                                                bindOnReady(target, nativeHandler, self);
     431                                        } else {
     432                                                addEvent(target, fakeName || name, nativeHandler, capture);
     433                                        }
     434                                } else {
     435                                        if (name === "ready" && self.domLoaded) {
     436                                                callback({type: name});
     437                                        } else {
     438                                                // If it already has an native handler then just push the callback
     439                                                callbackList.push({func: callback, scope: scope});
     440                                        }
     441                                }
     442                        }
     443
     444                        target = callbackList = 0; // Clean memory for IE
     445
     446                        return callback;
     447                };
     448
     449                /**
     450                 * Unbinds the specified event by name, name and callback or all events on the target.
     451                 *
     452                 * @method unbind
     453                 * @param {Object} target Target node/window or custom object.
     454                 * @param {String} names Optional event name to unbind.
     455                 * @param {function} callback Optional callback function to unbind.
     456                 * @return {EventUtils} Event utils instance.
     457                 */
     458                self.unbind = function(target, names, callback) {
     459                        var id, callbackList, i, ci, name, eventMap;
     460
     461                        // Don't bind to text nodes or comments
     462                        if (!target || target.nodeType === 3 || target.nodeType === 8) {
     463                                return self;
     464                        }
     465
     466                        // Unbind event or events if the target has the expando
     467                        id = target[expando];
     468                        if (id) {
     469                                eventMap = events[id];
     470
     471                                // Specific callback
     472                                if (names) {
     473                                        names = names.split(' ');
     474                                        i = names.length;
     475                                        while (i--) {
     476                                                name = names[i];
     477                                                callbackList = eventMap[name];
     478
     479                                                // Unbind the event if it exists in the map
     480                                                if (callbackList) {
     481                                                        // Remove specified callback
     482                                                        if (callback) {
     483                                                                ci = callbackList.length;
     484                                                                while (ci--) {
     485                                                                        if (callbackList[ci].func === callback) {
     486                                                                                var nativeHandler = callbackList.nativeHandler;
     487
     488                                                                                // Clone callbackList since unbind inside a callback would otherwise break the handlers loop
     489                                                                                callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
     490                                                                                callbackList.nativeHandler = nativeHandler;
     491
     492                                                                                eventMap[name] = callbackList;
     493                                                                        }
     494                                                                }
     495                                                        }
     496
     497                                                        // Remove all callbacks if there isn't a specified callback or there is no callbacks left
     498                                                        if (!callback || callbackList.length === 0) {
     499                                                                delete eventMap[name];
     500                                                                removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
     501                                                        }
     502                                                }
     503                                        }
     504                                } else {
     505                                        // All events for a specific element
     506                                        for (name in eventMap) {
     507                                                callbackList = eventMap[name];
     508                                                removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
     509                                        }
     510
     511                                        eventMap = {};
     512                                }
     513
     514                                // Check if object is empty, if it isn't then we won't remove the expando map
     515                                for (name in eventMap) {
     516                                        return self;
     517                                }
     518
     519                                // Delete event object
     520                                delete events[id];
     521
     522                                // Remove expando from target
     523                                try {
     524                                        // IE will fail here since it can't delete properties from window
     525                                        delete target[expando];
     526                                } catch (ex) {
     527                                        // IE will set it to null
     528                                        target[expando] = null;
     529                                }
     530                        }
     531
     532                        return self;
     533                };
     534
     535                /**
     536                 * Fires the specified event on the specified target.
     537                 *
     538                 * @method fire
     539                 * @param {Object} target Target node/window or custom object.
     540                 * @param {String} name Event name to fire.
     541                 * @param {Object} args Optional arguments to send to the observers.
     542                 * @return {EventUtils} Event utils instance.
     543                 */
     544                self.fire = function(target, name, args) {
     545                        var id;
     546
     547                        // Don't bind to text nodes or comments
     548                        if (!target || target.nodeType === 3 || target.nodeType === 8) {
     549                                return self;
     550                        }
     551
     552                        // Build event object by patching the args
     553                        args = fix(null, args);
     554                        args.type = name;
     555                        args.target = target;
     556
     557                        do {
     558                                // Found an expando that means there is listeners to execute
     559                                id = target[expando];
     560                                if (id) {
     561                                        executeHandlers(args, id);
     562                                }
     563
     564                                // Walk up the DOM
     565                                target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
     566                        } while (target && !args.isPropagationStopped());
     567
     568                        return self;
     569                };
     570
     571                /**
     572                 * Removes all bound event listeners for the specified target. This will also remove any bound
     573                 * listeners to child nodes within that target.
     574                 *
     575                 * @method clean
     576                 * @param {Object} target Target node/window object.
     577                 * @return {EventUtils} Event utils instance.
     578                 */
     579                self.clean = function(target) {
     580                        var i, children, unbind = self.unbind;
     581
     582                        // Don't bind to text nodes or comments
     583                        if (!target || target.nodeType === 3 || target.nodeType === 8) {
     584                                return self;
     585                        }
     586
     587                        // Unbind any element on the specificed target
     588                        if (target[expando]) {
     589                                unbind(target);
     590                        }
     591
     592                        // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
     593                        if (!target.getElementsByTagName) {
     594                                target = target.document;
     595                        }
     596
     597                        // Remove events from each child element
     598                        if (target && target.getElementsByTagName) {
     599                                unbind(target);
     600
     601                                children = target.getElementsByTagName('*');
     602                                i = children.length;
     603                                while (i--) {
     604                                        target = children[i];
     605
     606                                        if (target[expando]) {
     607                                                unbind(target);
     608                                        }
     609                                }
     610                        }
     611
     612                        return self;
     613                };
     614
     615                /**
     616                 * Destroys the event object. Call this on IE to remove memory leaks.
     617                 */
     618                self.destroy = function() {
     619                        events = {};
     620                };
     621
     622                // Legacy function for canceling events
     623                self.cancel = function(e) {
     624                        if (e) {
     625                                e.preventDefault();
     626                                e.stopImmediatePropagation();
     627                        }
     628
     629                        return false;
     630                };
     631        }
     632
     633        EventUtils.Event = new EventUtils();
     634        EventUtils.Event.bind(window, 'ready', function() {});
     635
     636        return EventUtils;
     637});
     638
     639// Included from: js/tinymce/classes/dom/Sizzle.js
     640
     641/**
     642 * Sizzle.js
     643 *
     644 * Copyright, Moxiecode Systems AB
     645 * Released under LGPL License.
     646 *
     647 * License: http://www.tinymce.com/license
     648 * Contributing: http://www.tinymce.com/contributing
     649 *
     650 * @ignore-file
     651 */
     652
     653/*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
     654
     655/*
     656 * Sizzle CSS Selector Engine
     657 *  Copyright, The Dojo Foundation
     658 *  Released under the MIT, BSD, and GPL Licenses.
     659 *  More information: http://sizzlejs.com/
     660 */
     661define("tinymce/dom/Sizzle", [], function() {
     662var i,
     663        cachedruns,
     664        Expr,
     665        getText,
     666        isXML,
     667        compile,
     668        outermostContext,
     669        recompare,
     670        sortInput,
     671
     672        // Local document vars
     673        setDocument,
     674        document,
     675        docElem,
     676        documentIsHTML,
     677        rbuggyQSA,
     678        rbuggyMatches,
     679        matches,
     680        contains,
     681
     682        // Instance-specific data
     683        expando = "sizzle" + -(new Date()),
     684        preferredDoc = window.document,
     685        support = {},
     686        dirruns = 0,
     687        done = 0,
     688        classCache = createCache(),
     689        tokenCache = createCache(),
     690        compilerCache = createCache(),
     691        hasDuplicate = false,
     692        sortOrder = function() { return 0; },
     693
     694        // General-purpose constants
     695        strundefined = typeof undefined,
     696        MAX_NEGATIVE = 1 << 31,
     697
     698        // Array methods
     699        arr = [],
     700        pop = arr.pop,
     701        push_native = arr.push,
     702        push = arr.push,
     703        slice = arr.slice,
     704        // Use a stripped-down indexOf if we can't use a native one
     705        indexOf = arr.indexOf || function( elem ) {
     706                var i = 0,
     707                        len = this.length;
     708                for ( ; i < len; i++ ) {
     709                        if ( this[i] === elem ) {
     710                                return i;
     711                        }
     712                }
     713                return -1;
     714        },
     715
     716
     717        // Regular expressions
     718
     719        // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
     720        whitespace = "[\\x20\\t\\r\\n\\f]",
     721        // http://www.w3.org/TR/css3-syntax/#characters
     722        characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
     723
     724        // Loosely modeled on CSS identifier characters
     725        // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
     726        // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
     727        identifier = characterEncoding.replace( "w", "w#" ),
     728
     729        // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
     730        operators = "([*^$|!~]?=)",
     731        attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
     732                "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
     733
     734        // Prefer arguments quoted,
     735        //   then not containing pseudos/brackets,
     736        //   then attribute selectors/non-parenthetical expressions,
     737        //   then anything else
     738        // These preferences are here to reduce the number of selectors
     739        //   needing tokenize in the PSEUDO preFilter
     740        pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
     741
     742        // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
     743        rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
     744
     745        rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
     746        rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
     747        rpseudo = new RegExp( pseudos ),
     748        ridentifier = new RegExp( "^" + identifier + "$" ),
     749
     750        matchExpr = {
     751                "ID": new RegExp( "^#(" + characterEncoding + ")" ),
     752                "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
     753                "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
     754                "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
     755                "ATTR": new RegExp( "^" + attributes ),
     756                "PSEUDO": new RegExp( "^" + pseudos ),
     757                "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
     758                        "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
     759                        "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
     760                // For use in libraries implementing .is()
     761                // We use this for POS matching in `select`
     762                "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
     763                        whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
     764        },
     765
     766        rsibling = /[\x20\t\r\n\f]*[+~]/,
     767
     768        rnative = /^[^{]+\{\s*\[native code/,
     769
     770        // Easily-parseable/retrievable ID or TAG or CLASS selectors
     771        rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
     772
     773        rinputs = /^(?:input|select|textarea|button)$/i,
     774        rheader = /^h\d$/i,
     775
     776        rescape = /'|\\/g,
     777        rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
     778
     779        // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
     780        runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
     781        funescape = function( _, escaped ) {
     782                var high = "0x" + escaped - 0x10000;
     783                // NaN means non-codepoint
     784                return high !== high ?
     785                        escaped :
     786                        // BMP codepoint
     787                        high < 0 ?
     788                                String.fromCharCode( high + 0x10000 ) :
     789                                // Supplemental Plane codepoint (surrogate pair)
     790                                String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
     791        };
     792
     793// Optimize for push.apply( _, NodeList )
     794try {
     795        push.apply(
     796                (arr = slice.call( preferredDoc.childNodes )),
     797                preferredDoc.childNodes
     798        );
     799        // Support: Android<4.0
     800        // Detect silently failing push.apply
     801        arr[ preferredDoc.childNodes.length ].nodeType;
     802} catch ( e ) {
     803        push = { apply: arr.length ?
     804
     805                // Leverage slice if possible
     806                function( target, els ) {
     807                        push_native.apply( target, slice.call(els) );
     808                } :
     809
     810                // Support: IE<9
     811                // Otherwise append directly
     812                function( target, els ) {
     813                        var j = target.length,
     814                                i = 0;
     815                        // Can't trust NodeList.length
     816                        while ( (target[j++] = els[i++]) ) {}
     817                        target.length = j - 1;
     818                }
     819        };
     820}
     821
     822/**
     823 * For feature detection
     824 * @param {Function} fn The function to test for native support
     825 */
     826function isNative( fn ) {
     827        return rnative.test( fn + "" );
     828}
     829
     830/**
     831 * Create key-value caches of limited size
     832 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
     833 *      property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
     834 *      deleting the oldest entry
     835 */
     836function createCache() {
     837        var cache,
     838                keys = [];
     839
     840        cache = function( key, value ) {
     841                // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
     842                if ( keys.push( key += " " ) > Expr.cacheLength ) {
     843                        // Only keep the most recent entries
     844                        delete cache[ keys.shift() ];
     845                }
     846                cache[ key ] = value;
     847                return value;
     848        };
     849
     850        return cache;
     851}
     852
     853/**
     854 * Mark a function for special use by Sizzle
     855 * @param {Function} fn The function to mark
     856 */
     857function markFunction( fn ) {
     858        fn[ expando ] = true;
     859        return fn;
     860}
     861
     862/**
     863 * Support testing using an element
     864 * @param {Function} fn Passed the created div and expects a boolean result
     865 */
     866function assert( fn ) {
     867        var div = document.createElement("div");
     868
     869        try {
     870                return !!fn( div );
     871        } catch (e) {
     872                return false;
     873        } finally {
     874                // release memory in IE
     875                div = null;
     876        }
     877}
     878
     879function Sizzle( selector, context, results, seed ) {
     880        var match, elem, m, nodeType,
     881                // QSA vars
     882                i, groups, old, nid, newContext, newSelector;
     883
     884        if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
     885                setDocument( context );
     886        }
     887
     888        context = context || document;
     889        results = results || [];
     890
     891        if ( !selector || typeof selector !== "string" ) {
     892                return results;
     893        }
     894
     895        if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
     896                return [];
     897        }
     898
     899        if ( documentIsHTML && !seed ) {
     900
     901                // Shortcuts
     902                if ( (match = rquickExpr.exec( selector )) ) {
     903                        // Speed-up: Sizzle("#ID")
     904                        if ( (m = match[1]) ) {
     905                                if ( nodeType === 9 ) {
     906                                        elem = context.getElementById( m );
     907                                        // Check parentNode to catch when Blackberry 4.6 returns
     908                                        // nodes that are no longer in the document #6963
     909                                        if ( elem && elem.parentNode ) {
     910                                                // Handle the case where IE, Opera, and Webkit return items
     911                                                // by name instead of ID
     912                                                if ( elem.id === m ) {
     913                                                        results.push( elem );
     914                                                        return results;
     915                                                }
     916                                        } else {
     917                                                return results;
     918                                        }
     919                                } else {
     920                                        // Context is not a document
     921                                        if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
     922                                                contains( context, elem ) && elem.id === m ) {
     923                                                results.push( elem );
     924                                                return results;
     925                                        }
     926                                }
     927
     928                        // Speed-up: Sizzle("TAG")
     929                        } else if ( match[2] ) {
     930                                push.apply( results, context.getElementsByTagName( selector ) );
     931                                return results;
     932
     933                        // Speed-up: Sizzle(".CLASS")
     934                        } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
     935                                push.apply( results, context.getElementsByClassName( m ) );
     936                                return results;
     937                        }
     938                }
     939
     940                // QSA path
     941                if ( support.qsa && !rbuggyQSA.test(selector) ) {
     942                        old = true;
     943                        nid = expando;
     944                        newContext = context;
     945                        newSelector = nodeType === 9 && selector;
     946
     947                        // qSA works strangely on Element-rooted queries
     948                        // We can work around this by specifying an extra ID on the root
     949                        // and working up from there (Thanks to Andrew Dupont for the technique)
     950                        // IE 8 doesn't work on object elements
     951                        if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
     952                                groups = tokenize( selector );
     953
     954                                if ( (old = context.getAttribute("id")) ) {
     955                                        nid = old.replace( rescape, "\\$&" );
     956                                } else {
     957                                        context.setAttribute( "id", nid );
     958                                }
     959                                nid = "[id='" + nid + "'] ";
     960
     961                                i = groups.length;
     962                                while ( i-- ) {
     963                                        groups[i] = nid + toSelector( groups[i] );
     964                                }
     965                                newContext = rsibling.test( selector ) && context.parentNode || context;
     966                                newSelector = groups.join(",");
     967                        }
     968
     969                        if ( newSelector ) {
     970                                try {
     971                                        push.apply( results,
     972                                                newContext.querySelectorAll( newSelector )
     973                                        );
     974                                        return results;
     975                                } catch(qsaError) {
     976                                } finally {
     977                                        if ( !old ) {
     978                                                context.removeAttribute("id");
     979                                        }
     980                                }
     981                        }
     982                }
     983        }
     984
     985        // All others
     986        return select( selector.replace( rtrim, "$1" ), context, results, seed );
     987}
     988
     989/**
     990 * Detect xml
     991 * @param {Element|Object} elem An element or a document
     992 */
     993isXML = Sizzle.isXML = function( elem ) {
     994        // documentElement is verified for cases where it doesn't yet exist
     995        // (such as loading iframes in IE - #4833)
     996        var documentElement = elem && (elem.ownerDocument || elem).documentElement;
     997        return documentElement ? documentElement.nodeName !== "HTML" : false;
     998};
     999
     1000/**
     1001 * Sets document-related variables once based on the current document
     1002 * @param {Element|Object} [doc] An element or document object to use to set the document
     1003 * @returns {Object} Returns the current document
     1004 */
     1005setDocument = Sizzle.setDocument = function( node ) {
     1006        var doc = node ? node.ownerDocument || node : preferredDoc;
     1007
     1008        // If no document and documentElement is available, return
     1009        if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
     1010                return document;
     1011        }
     1012
     1013        // Set our document
     1014        document = doc;
     1015        docElem = doc.documentElement;
     1016
     1017        // Support tests
     1018        documentIsHTML = !isXML( doc );
     1019
     1020        // Check if getElementsByTagName("*") returns only elements
     1021        support.getElementsByTagName = assert(function( div ) {
     1022                div.appendChild( doc.createComment("") );
     1023                return !div.getElementsByTagName("*").length;
     1024        });
     1025
     1026        // Check if attributes should be retrieved by attribute nodes
     1027        support.attributes = assert(function( div ) {
     1028                div.innerHTML = "<select></select>";
     1029                var type = typeof div.lastChild.getAttribute("multiple");
     1030                // IE8 returns a string for some attributes even when not present
     1031                return type !== "boolean" && type !== "string";
     1032        });
     1033
     1034        // Check if getElementsByClassName can be trusted
     1035        support.getElementsByClassName = assert(function( div ) {
     1036                // Opera can't find a second classname (in 9.6)
     1037                div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
     1038                if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
     1039                        return false;
     1040                }
     1041
     1042                // Safari 3.2 caches class attributes and doesn't catch changes
     1043                div.lastChild.className = "e";
     1044                return div.getElementsByClassName("e").length === 2;
     1045        });
     1046
     1047        // Check if getElementsByName privileges form controls or returns elements by ID
     1048        // If so, assume (for broader support) that getElementById returns elements by name
     1049        support.getByName = assert(function( div ) {
     1050                // Inject content
     1051                div.id = expando + 0;
     1052                // Support: Windows 8 Native Apps
     1053                // Assigning innerHTML with "name" attributes throws uncatchable exceptions
     1054                // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx
     1055                div.appendChild( document.createElement("a") ).setAttribute( "name", expando );
     1056                div.appendChild( document.createElement("i") ).setAttribute( "name", expando );
     1057                docElem.appendChild( div );
     1058
     1059                // Test
     1060                var pass = doc.getElementsByName &&
     1061                        // buggy browsers will return fewer than the correct 2
     1062                        doc.getElementsByName( expando ).length === 2 +
     1063                        // buggy browsers will return more than the correct 0
     1064                        doc.getElementsByName( expando + 0 ).length;
     1065
     1066                // Cleanup
     1067                docElem.removeChild( div );
     1068
     1069                return pass;
     1070        });
     1071
     1072        // Support: Webkit<537.32
     1073        // Detached nodes confoundingly follow *each other*
     1074        support.sortDetached = assert(function( div1 ) {
     1075                return div1.compareDocumentPosition &&
     1076                        // Should return 1, but Webkit returns 4 (following)
     1077                        (div1.compareDocumentPosition( document.createElement("div") ) & 1);
     1078        });
     1079
     1080        // IE6/7 return modified attributes
     1081        Expr.attrHandle = assert(function( div ) {
     1082                div.innerHTML = "<a href='#'></a>";
     1083                return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
     1084                        div.firstChild.getAttribute("href") === "#";
     1085        }) ?
     1086                {} :
     1087                {
     1088                        "href": function( elem ) {
     1089                                return elem.getAttribute( "href", 2 );
     1090                        },
     1091                        "type": function( elem ) {
     1092                                return elem.getAttribute("type");
     1093                        }
     1094                };
     1095
     1096        // ID find and filter
     1097        if ( support.getByName ) {
     1098                Expr.find["ID"] = function( id, context ) {
     1099                        if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
     1100                                var m = context.getElementById( id );
     1101                                // Check parentNode to catch when Blackberry 4.6 returns
     1102                                // nodes that are no longer in the document #6963
     1103                                return m && m.parentNode ? [m] : [];
     1104                        }
     1105                };
     1106                Expr.filter["ID"] = function( id ) {
     1107                        var attrId = id.replace( runescape, funescape );
     1108                        return function( elem ) {
     1109                                return elem.getAttribute("id") === attrId;
     1110                        };
     1111                };
     1112        } else {
     1113                Expr.find["ID"] = function( id, context ) {
     1114                        if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
     1115                                var m = context.getElementById( id );
     1116
     1117                                return m ?
     1118                                        m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
     1119                                                [m] :
     1120                                                undefined :
     1121                                        [];
     1122                        }
     1123                };
     1124                Expr.filter["ID"] =  function( id ) {
     1125                        var attrId = id.replace( runescape, funescape );
     1126                        return function( elem ) {
     1127                                var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
     1128                                return node && node.value === attrId;
     1129                        };
     1130                };
     1131        }
     1132
     1133        // Tag
     1134        Expr.find["TAG"] = support.getElementsByTagName ?
     1135                function( tag, context ) {
     1136                        if ( typeof context.getElementsByTagName !== strundefined ) {
     1137                                return context.getElementsByTagName( tag );
     1138                        }
     1139                } :
     1140                function( tag, context ) {
     1141                        var elem,
     1142                                tmp = [],
     1143                                i = 0,
     1144                                results = context.getElementsByTagName( tag );
     1145
     1146                        // Filter out possible comments
     1147                        if ( tag === "*" ) {
     1148                                while ( (elem = results[i++]) ) {
     1149                                        if ( elem.nodeType === 1 ) {
     1150                                                tmp.push( elem );
     1151                                        }
     1152                                }
     1153
     1154                                return tmp;
     1155                        }
     1156                        return results;
     1157                };
     1158
     1159        // Name
     1160        Expr.find["NAME"] = support.getByName && function( tag, context ) {
     1161                if ( typeof context.getElementsByName !== strundefined ) {
     1162                        return context.getElementsByName( name );
     1163                }
     1164        };
     1165
     1166        // Class
     1167        Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
     1168                if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
     1169                        return context.getElementsByClassName( className );
     1170                }
     1171        };
     1172
     1173        // QSA and matchesSelector support
     1174
     1175        // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
     1176        rbuggyMatches = [];
     1177
     1178        // qSa(:focus) reports false when true (Chrome 21),
     1179        // no need to also add to buggyMatches since matches checks buggyQSA
     1180        // A support test would require too much code (would include document ready)
     1181        rbuggyQSA = [ ":focus" ];
     1182
     1183        if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
     1184                // Build QSA regex
     1185                // Regex strategy adopted from Diego Perini
     1186                assert(function( div ) {
     1187                        // Select is set to empty string on purpose
     1188                        // This is to test IE's treatment of not explicitly
     1189                        // setting a boolean content attribute,
     1190                        // since its presence should be enough
     1191                        // http://bugs.jquery.com/ticket/12359
     1192                        div.innerHTML = "<select><option selected=''></option></select>";
     1193
     1194                        // IE8 - Some boolean attributes are not treated correctly
     1195                        if ( !div.querySelectorAll("[selected]").length ) {
     1196                                rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
     1197                        }
     1198
     1199                        // Webkit/Opera - :checked should return selected option elements
     1200                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
     1201                        // IE8 throws error here and will not see later tests
     1202                        if ( !div.querySelectorAll(":checked").length ) {
     1203                                rbuggyQSA.push(":checked");
     1204                        }
     1205                });
     1206
     1207                assert(function( div ) {
     1208
     1209                        // Opera 10-12/IE8 - ^= $= *= and empty values
     1210                        // Should not select anything
     1211                        div.innerHTML = "<input type='hidden' i=''/>";
     1212                        if ( div.querySelectorAll("[i^='']").length ) {
     1213                                rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
     1214                        }
     1215
     1216                        // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
     1217                        // IE8 throws error here and will not see later tests
     1218                        if ( !div.querySelectorAll(":enabled").length ) {
     1219                                rbuggyQSA.push( ":enabled", ":disabled" );
     1220                        }
     1221
     1222                        // Opera 10-11 does not throw on post-comma invalid pseudos
     1223                        div.querySelectorAll("*,:x");
     1224                        rbuggyQSA.push(",.*:");
     1225                });
     1226        }
     1227
     1228        if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
     1229                docElem.mozMatchesSelector ||
     1230                docElem.webkitMatchesSelector ||
     1231                docElem.oMatchesSelector ||
     1232                docElem.msMatchesSelector) )) ) {
     1233
     1234                assert(function( div ) {
     1235                        // Check to see if it's possible to do matchesSelector
     1236                        // on a disconnected node (IE 9)
     1237                        support.disconnectedMatch = matches.call( div, "div" );
     1238
     1239                        // This should fail with an exception
     1240                        // Gecko does not error, returns false instead
     1241                        matches.call( div, "[s!='']:x" );
     1242                        rbuggyMatches.push( "!=", pseudos );
     1243                });
     1244        }
     1245
     1246        rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
     1247        rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
     1248
     1249        // Element contains another
     1250        // Purposefully does not implement inclusive descendant
     1251        // As in, an element does not contain itself
     1252        contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
     1253                function( a, b ) {
     1254                        var adown = a.nodeType === 9 ? a.documentElement : a,
     1255                                bup = b && b.parentNode;
     1256                        return a === bup || !!( bup && bup.nodeType === 1 && (
     1257                                adown.contains ?
     1258                                        adown.contains( bup ) :
     1259                                        a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
     1260                        ));
     1261                } :
     1262                function( a, b ) {
     1263                        if ( b ) {
     1264                                while ( (b = b.parentNode) ) {
     1265                                        if ( b === a ) {
     1266                                                return true;
     1267                                        }
     1268                                }
     1269                        }
     1270                        return false;
     1271                };
     1272
     1273        // Document order sorting
     1274        sortOrder = docElem.compareDocumentPosition ?
     1275        function( a, b ) {
     1276
     1277                // Flag for duplicate removal
     1278                if ( a === b ) {
     1279                        hasDuplicate = true;
     1280                        return 0;
     1281                }
     1282
     1283                var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b );
     1284
     1285                if ( compare ) {
     1286                        // Disconnected nodes
     1287                        if ( compare & 1 ||
     1288                                (recompare && b.compareDocumentPosition( a ) === compare) ) {
     1289
     1290                                // Choose the first element that is related to our preferred document
     1291                                if ( a === doc || contains(preferredDoc, a) ) {
     1292                                        return -1;
     1293                                }
     1294                                if ( b === doc || contains(preferredDoc, b) ) {
     1295                                        return 1;
     1296                                }
     1297
     1298                                // Maintain original order
     1299                                return sortInput ?
     1300                                        ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
     1301                                        0;
     1302                        }
     1303
     1304                        return compare & 4 ? -1 : 1;
     1305                }
     1306
     1307                // Not directly comparable, sort on existence of method
     1308                return a.compareDocumentPosition ? -1 : 1;
     1309        } :
     1310        function( a, b ) {
     1311                var cur,
     1312                        i = 0,
     1313                        aup = a.parentNode,
     1314                        bup = b.parentNode,
     1315                        ap = [ a ],
     1316                        bp = [ b ];
     1317
     1318                // Exit early if the nodes are identical
     1319                if ( a === b ) {
     1320                        hasDuplicate = true;
     1321                        return 0;
     1322
     1323                // Parentless nodes are either documents or disconnected
     1324                } else if ( !aup || !bup ) {
     1325                        return a === doc ? -1 :
     1326                                b === doc ? 1 :
     1327                                aup ? -1 :
     1328                                bup ? 1 :
     1329                                0;
     1330
     1331                // If the nodes are siblings, we can do a quick check
     1332                } else if ( aup === bup ) {
     1333                        return siblingCheck( a, b );
     1334                }
     1335
     1336                // Otherwise we need full lists of their ancestors for comparison
     1337                cur = a;
     1338                while ( (cur = cur.parentNode) ) {
     1339                        ap.unshift( cur );
     1340                }
     1341                cur = b;
     1342                while ( (cur = cur.parentNode) ) {
     1343                        bp.unshift( cur );
     1344                }
     1345
     1346                // Walk down the tree looking for a discrepancy
     1347                while ( ap[i] === bp[i] ) {
     1348                        i++;
     1349                }
     1350
     1351                return i ?
     1352                        // Do a sibling check if the nodes have a common ancestor
     1353                        siblingCheck( ap[i], bp[i] ) :
     1354
     1355                        // Otherwise nodes in our document sort first
     1356                        ap[i] === preferredDoc ? -1 :
     1357                        bp[i] === preferredDoc ? 1 :
     1358                        0;
     1359        };
     1360
     1361        return document;
     1362};
     1363
     1364Sizzle.matches = function( expr, elements ) {
     1365        return Sizzle( expr, null, null, elements );
     1366};
     1367
     1368Sizzle.matchesSelector = function( elem, expr ) {
     1369        // Set document vars if needed
     1370        if ( ( elem.ownerDocument || elem ) !== document ) {
     1371                setDocument( elem );
     1372        }
     1373
     1374        // Make sure that attribute selectors are quoted
     1375        expr = expr.replace( rattributeQuotes, "='$1']" );
     1376
     1377        // rbuggyQSA always contains :focus, so no need for an existence check
     1378        if ( support.matchesSelector && documentIsHTML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
     1379                try {
     1380                        var ret = matches.call( elem, expr );
     1381
     1382                        // IE 9's matchesSelector returns false on disconnected nodes
     1383                        if ( ret || support.disconnectedMatch ||
     1384                                        // As well, disconnected nodes are said to be in a document
     1385                                        // fragment in IE 9
     1386                                        elem.document && elem.document.nodeType !== 11 ) {
     1387                                return ret;
     1388                        }
     1389                } catch(e) {}
     1390        }
     1391
     1392        return Sizzle( expr, document, null, [elem] ).length > 0;
     1393};
     1394
     1395Sizzle.contains = function( context, elem ) {
     1396        // Set document vars if needed
     1397        if ( ( context.ownerDocument || context ) !== document ) {
     1398                setDocument( context );
     1399        }
     1400        return contains( context, elem );
     1401};
     1402
     1403Sizzle.attr = function( elem, name ) {
     1404        var val;
     1405
     1406        // Set document vars if needed
     1407        if ( ( elem.ownerDocument || elem ) !== document ) {
     1408                setDocument( elem );
     1409        }
     1410
     1411        if ( documentIsHTML ) {
     1412                name = name.toLowerCase();
     1413        }
     1414        if ( (val = Expr.attrHandle[ name ]) ) {
     1415                return val( elem );
     1416        }
     1417        if ( !documentIsHTML || support.attributes ) {
     1418                return elem.getAttribute( name );
     1419        }
     1420        return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
     1421                name :
     1422                val && val.specified ? val.value : null;
     1423};
     1424
     1425Sizzle.error = function( msg ) {
     1426        throw new Error( "Syntax error, unrecognized expression: " + msg );
     1427};
     1428
     1429// Document sorting and removing duplicates
     1430Sizzle.uniqueSort = function( results ) {
     1431        var elem,
     1432                duplicates = [],
     1433                j = 0,
     1434                i = 0;
     1435
     1436        // Unless we *know* we can detect duplicates, assume their presence
     1437        hasDuplicate = !support.detectDuplicates;
     1438        // Compensate for sort limitations
     1439        recompare = !support.sortDetached;
     1440        sortInput = !support.sortStable && results.slice( 0 );
     1441        results.sort( sortOrder );
     1442
     1443        if ( hasDuplicate ) {
     1444                while ( (elem = results[i++]) ) {
     1445                        if ( elem === results[ i ] ) {
     1446                                j = duplicates.push( i );
     1447                        }
     1448                }
     1449                while ( j-- ) {
     1450                        results.splice( duplicates[ j ], 1 );
     1451                }
     1452        }
     1453
     1454        return results;
     1455};
     1456
     1457/**
     1458 * Checks document order of two siblings
     1459 * @param {Element} a
     1460 * @param {Element} b
     1461 * @returns Returns -1 if a precedes b, 1 if a follows b
     1462 */
     1463function siblingCheck( a, b ) {
     1464        var cur = b && a,
     1465                diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
     1466
     1467        // Use IE sourceIndex if available on both nodes
     1468        if ( diff ) {
     1469                return diff;
     1470        }
     1471
     1472        // Check if b follows a
     1473        if ( cur ) {
     1474                while ( (cur = cur.nextSibling) ) {
     1475                        if ( cur === b ) {
     1476                                return -1;
     1477                        }
     1478                }
     1479        }
     1480
     1481        return a ? 1 : -1;
     1482}
     1483
     1484// Returns a function to use in pseudos for input types
     1485function createInputPseudo( type ) {
     1486        return function( elem ) {
     1487                var name = elem.nodeName.toLowerCase();
     1488                return name === "input" && elem.type === type;
     1489        };
     1490}
     1491
     1492// Returns a function to use in pseudos for buttons
     1493function createButtonPseudo( type ) {
     1494        return function( elem ) {
     1495                var name = elem.nodeName.toLowerCase();
     1496                return (name === "input" || name === "button") && elem.type === type;
     1497        };
     1498}
     1499
     1500// Returns a function to use in pseudos for positionals
     1501function createPositionalPseudo( fn ) {
     1502        return markFunction(function( argument ) {
     1503                argument = +argument;
     1504                return markFunction(function( seed, matches ) {
     1505                        var j,
     1506                                matchIndexes = fn( [], seed.length, argument ),
     1507                                i = matchIndexes.length;
     1508
     1509                        // Match elements found at the specified indexes
     1510                        while ( i-- ) {
     1511                                if ( seed[ (j = matchIndexes[i]) ] ) {
     1512                                        seed[j] = !(matches[j] = seed[j]);
     1513                                }
     1514                        }
     1515                });
     1516        });
     1517}
     1518
     1519/**
     1520 * Utility function for retrieving the text value of an array of DOM nodes
     1521 * @param {Array|Element} elem
     1522 */
     1523getText = Sizzle.getText = function( elem ) {
     1524        var node,
     1525                ret = "",
     1526                i = 0,
     1527                nodeType = elem.nodeType;
     1528
     1529        if ( !nodeType ) {
     1530                // If no nodeType, this is expected to be an array
     1531                for ( ; (node = elem[i]); i++ ) {
     1532                        // Do not traverse comment nodes
     1533                        ret += getText( node );
     1534                }
     1535        } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
     1536                // Use textContent for elements
     1537                // innerText usage removed for consistency of new lines (see #11153)
     1538                if ( typeof elem.textContent === "string" ) {
     1539                        return elem.textContent;
     1540                } else {
     1541                        // Traverse its children
     1542                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
     1543                                ret += getText( elem );
     1544                        }
     1545                }
     1546        } else if ( nodeType === 3 || nodeType === 4 ) {
     1547                return elem.nodeValue;
     1548        }
     1549        // Do not include comment or processing instruction nodes
     1550
     1551        return ret;
     1552};
     1553
     1554Expr = Sizzle.selectors = {
     1555
     1556        // Can be adjusted by the user
     1557        cacheLength: 50,
     1558
     1559        createPseudo: markFunction,
     1560
     1561        match: matchExpr,
     1562
     1563        find: {},
     1564
     1565        relative: {
     1566                ">": { dir: "parentNode", first: true },
     1567                " ": { dir: "parentNode" },
     1568                "+": { dir: "previousSibling", first: true },
     1569                "~": { dir: "previousSibling" }
     1570        },
     1571
     1572        preFilter: {
     1573                "ATTR": function( match ) {
     1574                        match[1] = match[1].replace( runescape, funescape );
     1575
     1576                        // Move the given value to match[3] whether quoted or unquoted
     1577                        match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
     1578
     1579                        if ( match[2] === "~=" ) {
     1580                                match[3] = " " + match[3] + " ";
     1581                        }
     1582
     1583                        return match.slice( 0, 4 );
     1584                },
     1585
     1586                "CHILD": function( match ) {
     1587                        /* matches from matchExpr["CHILD"]
     1588                                1 type (only|nth|...)
     1589                                2 what (child|of-type)
     1590                                3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
     1591                                4 xn-component of xn+y argument ([+-]?\d*n|)
     1592                                5 sign of xn-component
     1593                                6 x of xn-component
     1594                                7 sign of y-component
     1595                                8 y of y-component
     1596                        */
     1597                        match[1] = match[1].toLowerCase();
     1598
     1599                        if ( match[1].slice( 0, 3 ) === "nth" ) {
     1600                                // nth-* requires argument
     1601                                if ( !match[3] ) {
     1602                                        Sizzle.error( match[0] );
     1603                                }
     1604
     1605                                // numeric x and y parameters for Expr.filter.CHILD
     1606                                // remember that false/true cast respectively to 0/1
     1607                                match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
     1608                                match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
     1609
     1610                        // other types prohibit arguments
     1611                        } else if ( match[3] ) {
     1612                                Sizzle.error( match[0] );
     1613                        }
     1614
     1615                        return match;
     1616                },
     1617
     1618                "PSEUDO": function( match ) {
     1619                        var excess,
     1620                                unquoted = !match[5] && match[2];
     1621
     1622                        if ( matchExpr["CHILD"].test( match[0] ) ) {
     1623                                return null;
     1624                        }
     1625
     1626                        // Accept quoted arguments as-is
     1627                        if ( match[4] ) {
     1628                                match[2] = match[4];
     1629
     1630                        // Strip excess characters from unquoted arguments
     1631                        } else if ( unquoted && rpseudo.test( unquoted ) &&
     1632                                // Get excess from tokenize (recursively)
     1633                                (excess = tokenize( unquoted, true )) &&
     1634                                // advance to the next closing parenthesis
     1635                                (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
     1636
     1637                                // excess is a negative index
     1638                                match[0] = match[0].slice( 0, excess );
     1639                                match[2] = unquoted.slice( 0, excess );
     1640                        }
     1641
     1642                        // Return only captures needed by the pseudo filter method (type and argument)
     1643                        return match.slice( 0, 3 );
     1644                }
     1645        },
     1646
     1647        filter: {
     1648
     1649                "TAG": function( nodeName ) {
     1650                        if ( nodeName === "*" ) {
     1651                                return function() { return true; };
     1652                        }
     1653
     1654                        nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
     1655                        return function( elem ) {
     1656                                return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
     1657                        };
     1658                },
     1659
     1660                "CLASS": function( className ) {
     1661                        var pattern = classCache[ className + " " ];
     1662
     1663                        return pattern ||
     1664                                (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
     1665                                classCache( className, function( elem ) {
     1666                                        return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
     1667                                });
     1668                },
     1669
     1670                "ATTR": function( name, operator, check ) {
     1671                        return function( elem ) {
     1672                                var result = Sizzle.attr( elem, name );
     1673
     1674                                if ( result == null ) {
     1675                                        return operator === "!=";
     1676                                }
     1677                                if ( !operator ) {
     1678                                        return true;
     1679                                }
     1680
     1681                                result += "";
     1682
     1683                                return operator === "=" ? result === check :
     1684                                        operator === "!=" ? result !== check :
     1685                                        operator === "^=" ? check && result.indexOf( check ) === 0 :
     1686                                        operator === "*=" ? check && result.indexOf( check ) > -1 :
     1687                                        operator === "$=" ? check && result.slice( -check.length ) === check :
     1688                                        operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
     1689                                        operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
     1690                                        false;
     1691                        };
     1692                },
     1693
     1694                "CHILD": function( type, what, argument, first, last ) {
     1695                        var simple = type.slice( 0, 3 ) !== "nth",
     1696                                forward = type.slice( -4 ) !== "last",
     1697                                ofType = what === "of-type";
     1698
     1699                        return first === 1 && last === 0 ?
     1700
     1701                                // Shortcut for :nth-*(n)
     1702                                function( elem ) {
     1703                                        return !!elem.parentNode;
     1704                                } :
     1705
     1706                                function( elem, context, xml ) {
     1707                                        var cache, outerCache, node, diff, nodeIndex, start,
     1708                                                dir = simple !== forward ? "nextSibling" : "previousSibling",
     1709                                                parent = elem.parentNode,
     1710                                                name = ofType && elem.nodeName.toLowerCase(),
     1711                                                useCache = !xml && !ofType;
     1712
     1713                                        if ( parent ) {
     1714
     1715                                                // :(first|last|only)-(child|of-type)
     1716                                                if ( simple ) {
     1717                                                        while ( dir ) {
     1718                                                                node = elem;
     1719                                                                while ( (node = node[ dir ]) ) {
     1720                                                                        if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
     1721                                                                                return false;
     1722                                                                        }
     1723                                                                }
     1724                                                                // Reverse direction for :only-* (if we haven't yet done so)
     1725                                                                start = dir = type === "only" && !start && "nextSibling";
     1726                                                        }
     1727                                                        return true;
     1728                                                }
     1729
     1730                                                start = [ forward ? parent.firstChild : parent.lastChild ];
     1731
     1732                                                // non-xml :nth-child(...) stores cache data on `parent`
     1733                                                if ( forward && useCache ) {
     1734                                                        // Seek `elem` from a previously-cached index
     1735                                                        outerCache = parent[ expando ] || (parent[ expando ] = {});
     1736                                                        cache = outerCache[ type ] || [];
     1737                                                        nodeIndex = cache[0] === dirruns && cache[1];
     1738                                                        diff = cache[0] === dirruns && cache[2];
     1739                                                        node = nodeIndex && parent.childNodes[ nodeIndex ];
     1740
     1741                                                        while ( (node = ++nodeIndex && node && node[ dir ] ||
     1742
     1743                                                                // Fallback to seeking `elem` from the start
     1744                                                                (diff = nodeIndex = 0) || start.pop()) ) {
     1745
     1746                                                                // When found, cache indexes on `parent` and break
     1747                                                                if ( node.nodeType === 1 && ++diff && node === elem ) {
     1748                                                                        outerCache[ type ] = [ dirruns, nodeIndex, diff ];
     1749                                                                        break;
     1750                                                                }
     1751                                                        }
     1752
     1753                                                // Use previously-cached element index if available
     1754                                                } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
     1755                                                        diff = cache[1];
     1756
     1757                                                // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
     1758                                                } else {
     1759                                                        // Use the same loop as above to seek `elem` from the start
     1760                                                        while ( (node = ++nodeIndex && node && node[ dir ] ||
     1761                                                                (diff = nodeIndex = 0) || start.pop()) ) {
     1762
     1763                                                                if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
     1764                                                                        // Cache the index of each encountered element
     1765                                                                        if ( useCache ) {
     1766                                                                                (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
     1767                                                                        }
     1768
     1769                                                                        if ( node === elem ) {
     1770                                                                                break;
     1771                                                                        }
     1772                                                                }
     1773                                                        }
     1774                                                }
     1775
     1776                                                // Incorporate the offset, then check against cycle size
     1777                                                diff -= last;
     1778                                                return diff === first || ( diff % first === 0 && diff / first >= 0 );
     1779                                        }
     1780                                };
     1781                },
     1782
     1783                "PSEUDO": function( pseudo, argument ) {
     1784                        // pseudo-class names are case-insensitive
     1785                        // http://www.w3.org/TR/selectors/#pseudo-classes
     1786                        // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
     1787                        // Remember that setFilters inherits from pseudos
     1788                        var args,
     1789                                fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
     1790                                        Sizzle.error( "unsupported pseudo: " + pseudo );
     1791
     1792                        // The user may use createPseudo to indicate that
     1793                        // arguments are needed to create the filter function
     1794                        // just as Sizzle does
     1795                        if ( fn[ expando ] ) {
     1796                                return fn( argument );
     1797                        }
     1798
     1799                        // But maintain support for old signatures
     1800                        if ( fn.length > 1 ) {
     1801                                args = [ pseudo, pseudo, "", argument ];
     1802                                return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
     1803                                        markFunction(function( seed, matches ) {
     1804                                                var idx,
     1805                                                        matched = fn( seed, argument ),
     1806                                                        i = matched.length;
     1807                                                while ( i-- ) {
     1808                                                        idx = indexOf.call( seed, matched[i] );
     1809                                                        seed[ idx ] = !( matches[ idx ] = matched[i] );
     1810                                                }
     1811                                        }) :
     1812                                        function( elem ) {
     1813                                                return fn( elem, 0, args );
     1814                                        };
     1815                        }
     1816
     1817                        return fn;
     1818                }
     1819        },
     1820
     1821        pseudos: {
     1822                // Potentially complex pseudos
     1823                "not": markFunction(function( selector ) {
     1824                        // Trim the selector passed to compile
     1825                        // to avoid treating leading and trailing
     1826                        // spaces as combinators
     1827                        var input = [],
     1828                                results = [],
     1829                                matcher = compile( selector.replace( rtrim, "$1" ) );
     1830
     1831                        return matcher[ expando ] ?
     1832                                markFunction(function( seed, matches, context, xml ) {
     1833                                        var elem,
     1834                                                unmatched = matcher( seed, null, xml, [] ),
     1835                                                i = seed.length;
     1836
     1837                                        // Match elements unmatched by `matcher`
     1838                                        while ( i-- ) {
     1839                                                if ( (elem = unmatched[i]) ) {
     1840                                                        seed[i] = !(matches[i] = elem);
     1841                                                }
     1842                                        }
     1843                                }) :
     1844                                function( elem, context, xml ) {
     1845                                        input[0] = elem;
     1846                                        matcher( input, null, xml, results );
     1847                                        return !results.pop();
     1848                                };
     1849                }),
     1850
     1851                "has": markFunction(function( selector ) {
     1852                        return function( elem ) {
     1853                                return Sizzle( selector, elem ).length > 0;
     1854                        };
     1855                }),
     1856
     1857                "contains": markFunction(function( text ) {
     1858                        return function( elem ) {
     1859                                return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
     1860                        };
     1861                }),
     1862
     1863                // "Whether an element is represented by a :lang() selector
     1864                // is based solely on the element's language value
     1865                // being equal to the identifier C,
     1866                // or beginning with the identifier C immediately followed by "-".
     1867                // The matching of C against the element's language value is performed case-insensitively.
     1868                // The identifier C does not have to be a valid language name."
     1869                // http://www.w3.org/TR/selectors/#lang-pseudo
     1870                "lang": markFunction( function( lang ) {
     1871                        // lang value must be a valid identifier
     1872                        if ( !ridentifier.test(lang || "") ) {
     1873                                Sizzle.error( "unsupported lang: " + lang );
     1874                        }
     1875                        lang = lang.replace( runescape, funescape ).toLowerCase();
     1876                        return function( elem ) {
     1877                                var elemLang;
     1878                                do {
     1879                                        if ( (elemLang = documentIsHTML ?
     1880                                                elem.lang :
     1881                                                elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
     1882
     1883                                                elemLang = elemLang.toLowerCase();
     1884                                                return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
     1885                                        }
     1886                                } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
     1887                                return false;
     1888                        };
     1889                }),
     1890
     1891                // Miscellaneous
     1892                "target": function( elem ) {
     1893                        var hash = window.location && window.location.hash;
     1894                        return hash && hash.slice( 1 ) === elem.id;
     1895                },
     1896
     1897                "root": function( elem ) {
     1898                        return elem === docElem;
     1899                },
     1900
     1901                "focus": function( elem ) {
     1902                        return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
     1903                },
     1904
     1905                // Boolean properties
     1906                "enabled": function( elem ) {
     1907                        return elem.disabled === false;
     1908                },
     1909
     1910                "disabled": function( elem ) {
     1911                        return elem.disabled === true;
     1912                },
     1913
     1914                "checked": function( elem ) {
     1915                        // In CSS3, :checked should return both checked and selected elements
     1916                        // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
     1917                        var nodeName = elem.nodeName.toLowerCase();
     1918                        return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
     1919                },
     1920
     1921                "selected": function( elem ) {
     1922                        // Accessing this property makes selected-by-default
     1923                        // options in Safari work properly
     1924                        if ( elem.parentNode ) {
     1925                                elem.parentNode.selectedIndex;
     1926                        }
     1927
     1928                        return elem.selected === true;
     1929                },
     1930
     1931                // Contents
     1932                "empty": function( elem ) {
     1933                        // http://www.w3.org/TR/selectors/#empty-pseudo
     1934                        // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
     1935                        //   not comment, processing instructions, or others
     1936                        // Thanks to Diego Perini for the nodeName shortcut
     1937                        //   Greater than "@" means alpha characters (specifically not starting with "#" or "?")
     1938                        for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
     1939                                if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
     1940                                        return false;
     1941                                }
     1942                        }
     1943                        return true;
     1944                },
     1945
     1946                "parent": function( elem ) {
     1947                        return !Expr.pseudos["empty"]( elem );
     1948                },
     1949
     1950                // Element/input types
     1951                "header": function( elem ) {
     1952                        return rheader.test( elem.nodeName );
     1953                },
     1954
     1955                "input": function( elem ) {
     1956                        return rinputs.test( elem.nodeName );
     1957                },
     1958
     1959                "button": function( elem ) {
     1960                        var name = elem.nodeName.toLowerCase();
     1961                        return name === "input" && elem.type === "button" || name === "button";
     1962                },
     1963
     1964                "text": function( elem ) {
     1965                        var attr;
     1966                        // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
     1967                        // use getAttribute instead to test this case
     1968                        return elem.nodeName.toLowerCase() === "input" &&
     1969                                elem.type === "text" &&
     1970                                ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
     1971                },
     1972
     1973                // Position-in-collection
     1974                "first": createPositionalPseudo(function() {
     1975                        return [ 0 ];
     1976                }),
     1977
     1978                "last": createPositionalPseudo(function( matchIndexes, length ) {
     1979                        return [ length - 1 ];
     1980                }),
     1981
     1982                "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
     1983                        return [ argument < 0 ? argument + length : argument ];
     1984                }),
     1985
     1986                "even": createPositionalPseudo(function( matchIndexes, length ) {
     1987                        var i = 0;
     1988                        for ( ; i < length; i += 2 ) {
     1989                                matchIndexes.push( i );
     1990                        }
     1991                        return matchIndexes;
     1992                }),
     1993
     1994                "odd": createPositionalPseudo(function( matchIndexes, length ) {
     1995                        var i = 1;
     1996                        for ( ; i < length; i += 2 ) {
     1997                                matchIndexes.push( i );
     1998                        }
     1999                        return matchIndexes;
     2000                }),
     2001
     2002                "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
     2003                        var i = argument < 0 ? argument + length : argument;
     2004                        for ( ; --i >= 0; ) {
     2005                                matchIndexes.push( i );
     2006                        }
     2007                        return matchIndexes;
     2008                }),
     2009
     2010                "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
     2011                        var i = argument < 0 ? argument + length : argument;
     2012                        for ( ; ++i < length; ) {
     2013                                matchIndexes.push( i );
     2014                        }
     2015                        return matchIndexes;
     2016                })
     2017        }
     2018};
     2019
     2020// Add button/input type pseudos
     2021for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
     2022        Expr.pseudos[ i ] = createInputPseudo( i );
     2023}
     2024for ( i in { submit: true, reset: true } ) {
     2025        Expr.pseudos[ i ] = createButtonPseudo( i );
     2026}
     2027
     2028function tokenize( selector, parseOnly ) {
     2029        var matched, match, tokens, type,
     2030                soFar, groups, preFilters,
     2031                cached = tokenCache[ selector + " " ];
     2032
     2033        if ( cached ) {
     2034                return parseOnly ? 0 : cached.slice( 0 );
     2035        }
     2036
     2037        soFar = selector;
     2038        groups = [];
     2039        preFilters = Expr.preFilter;
     2040
     2041        while ( soFar ) {
     2042
     2043                // Comma and first run
     2044                if ( !matched || (match = rcomma.exec( soFar )) ) {
     2045                        if ( match ) {
     2046                                // Don't consume trailing commas as valid
     2047                                soFar = soFar.slice( match[0].length ) || soFar;
     2048                        }
     2049                        groups.push( tokens = [] );
     2050                }
     2051
     2052                matched = false;
     2053
     2054                // Combinators
     2055                if ( (match = rcombinators.exec( soFar )) ) {
     2056                        matched = match.shift();
     2057                        tokens.push( {
     2058                                value: matched,
     2059                                // Cast descendant combinators to space
     2060                                type: match[0].replace( rtrim, " " )
     2061                        } );
     2062                        soFar = soFar.slice( matched.length );
     2063                }
     2064
     2065                // Filters
     2066                for ( type in Expr.filter ) {
     2067                        if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
     2068                                (match = preFilters[ type ]( match ))) ) {
     2069                                matched = match.shift();
     2070                                tokens.push( {
     2071                                        value: matched,
     2072                                        type: type,
     2073                                        matches: match
     2074                                } );
     2075                                soFar = soFar.slice( matched.length );
     2076                        }
     2077                }
     2078
     2079                if ( !matched ) {
     2080                        break;
     2081                }
     2082        }
     2083
     2084        // Return the length of the invalid excess
     2085        // if we're just parsing
     2086        // Otherwise, throw an error or return tokens
     2087        return parseOnly ?
     2088                soFar.length :
     2089                soFar ?
     2090                        Sizzle.error( selector ) :
     2091                        // Cache the tokens
     2092                        tokenCache( selector, groups ).slice( 0 );
     2093}
     2094
     2095function toSelector( tokens ) {
     2096        var i = 0,
     2097                len = tokens.length,
     2098                selector = "";
     2099        for ( ; i < len; i++ ) {
     2100                selector += tokens[i].value;
     2101        }
     2102        return selector;
     2103}
     2104
     2105function addCombinator( matcher, combinator, base ) {
     2106        var dir = combinator.dir,
     2107                checkNonElements = base && dir === "parentNode",
     2108                doneName = done++;
     2109
     2110        return combinator.first ?
     2111                // Check against closest ancestor/preceding element
     2112                function( elem, context, xml ) {
     2113                        while ( (elem = elem[ dir ]) ) {
     2114                                if ( elem.nodeType === 1 || checkNonElements ) {
     2115                                        return matcher( elem, context, xml );
     2116                                }
     2117                        }
     2118                } :
     2119
     2120                // Check against all ancestor/preceding elements
     2121                function( elem, context, xml ) {
     2122                        var data, cache, outerCache,
     2123                                dirkey = dirruns + " " + doneName;
     2124
     2125                        // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
     2126                        if ( xml ) {
     2127                                while ( (elem = elem[ dir ]) ) {
     2128                                        if ( elem.nodeType === 1 || checkNonElements ) {
     2129                                                if ( matcher( elem, context, xml ) ) {
     2130                                                        return true;
     2131                                                }
     2132                                        }
     2133                                }
     2134                        } else {
     2135                                while ( (elem = elem[ dir ]) ) {
     2136                                        if ( elem.nodeType === 1 || checkNonElements ) {
     2137                                                outerCache = elem[ expando ] || (elem[ expando ] = {});
     2138                                                if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
     2139                                                        if ( (data = cache[1]) === true || data === cachedruns ) {
     2140                                                                return data === true;
     2141                                                        }
     2142                                                } else {
     2143                                                        cache = outerCache[ dir ] = [ dirkey ];
     2144                                                        cache[1] = matcher( elem, context, xml ) || cachedruns;
     2145                                                        if ( cache[1] === true ) {
     2146                                                                return true;
     2147                                                        }
     2148                                                }
     2149                                        }
     2150                                }
     2151                        }
     2152                };
     2153}
     2154
     2155function elementMatcher( matchers ) {
     2156        return matchers.length > 1 ?
     2157                function( elem, context, xml ) {
     2158                        var i = matchers.length;
     2159                        while ( i-- ) {
     2160                                if ( !matchers[i]( elem, context, xml ) ) {
     2161                                        return false;
     2162                                }
     2163                        }
     2164                        return true;
     2165                } :
     2166                matchers[0];
     2167}
     2168
     2169function condense( unmatched, map, filter, context, xml ) {
     2170        var elem,
     2171                newUnmatched = [],
     2172                i = 0,
     2173                len = unmatched.length,
     2174                mapped = map != null;
     2175
     2176        for ( ; i < len; i++ ) {
     2177                if ( (elem = unmatched[i]) ) {
     2178                        if ( !filter || filter( elem, context, xml ) ) {
     2179                                newUnmatched.push( elem );
     2180                                if ( mapped ) {
     2181                                        map.push( i );
     2182                                }
     2183                        }
     2184                }
     2185        }
     2186
     2187        return newUnmatched;
     2188}
     2189
     2190function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
     2191        if ( postFilter && !postFilter[ expando ] ) {
     2192                postFilter = setMatcher( postFilter );
     2193        }
     2194        if ( postFinder && !postFinder[ expando ] ) {
     2195                postFinder = setMatcher( postFinder, postSelector );
     2196        }
     2197        return markFunction(function( seed, results, context, xml ) {
     2198                var temp, i, elem,
     2199                        preMap = [],
     2200                        postMap = [],
     2201                        preexisting = results.length,
     2202
     2203                        // Get initial elements from seed or context
     2204                        elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
     2205
     2206                        // Prefilter to get matcher input, preserving a map for seed-results synchronization
     2207                        matcherIn = preFilter && ( seed || !selector ) ?
     2208                                condense( elems, preMap, preFilter, context, xml ) :
     2209                                elems,
     2210
     2211                        matcherOut = matcher ?
     2212                                // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
     2213                                postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
     2214
     2215                                        // ...intermediate processing is necessary
     2216                                        [] :
     2217
     2218                                        // ...otherwise use results directly
     2219                                        results :
     2220                                matcherIn;
     2221
     2222                // Find primary matches
     2223                if ( matcher ) {
     2224                        matcher( matcherIn, matcherOut, context, xml );
     2225                }
     2226
     2227                // Apply postFilter
     2228                if ( postFilter ) {
     2229                        temp = condense( matcherOut, postMap );
     2230                        postFilter( temp, [], context, xml );
     2231
     2232                        // Un-match failing elements by moving them back to matcherIn
     2233                        i = temp.length;
     2234                        while ( i-- ) {
     2235                                if ( (elem = temp[i]) ) {
     2236                                        matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
     2237                                }
     2238                        }
     2239                }
     2240
     2241                if ( seed ) {
     2242                        if ( postFinder || preFilter ) {
     2243                                if ( postFinder ) {
     2244                                        // Get the final matcherOut by condensing this intermediate into postFinder contexts
     2245                                        temp = [];
     2246                                        i = matcherOut.length;
     2247                                        while ( i-- ) {
     2248                                                if ( (elem = matcherOut[i]) ) {
     2249                                                        // Restore matcherIn since elem is not yet a final match
     2250                                                        temp.push( (matcherIn[i] = elem) );
     2251                                                }
     2252                                        }
     2253                                        postFinder( null, (matcherOut = []), temp, xml );
     2254                                }
     2255
     2256                                // Move matched elements from seed to results to keep them synchronized
     2257                                i = matcherOut.length;
     2258                                while ( i-- ) {
     2259                                        if ( (elem = matcherOut[i]) &&
     2260                                                (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
     2261
     2262                                                seed[temp] = !(results[temp] = elem);
     2263                                        }
     2264                                }
     2265                        }
     2266
     2267                // Add elements to results, through postFinder if defined
     2268                } else {
     2269                        matcherOut = condense(
     2270                                matcherOut === results ?
     2271                                        matcherOut.splice( preexisting, matcherOut.length ) :
     2272                                        matcherOut
     2273                        );
     2274                        if ( postFinder ) {
     2275                                postFinder( null, results, matcherOut, xml );
     2276                        } else {
     2277                                push.apply( results, matcherOut );
     2278                        }
     2279                }
     2280        });
     2281}
     2282
     2283function matcherFromTokens( tokens ) {
     2284        var checkContext, matcher, j,
     2285                len = tokens.length,
     2286                leadingRelative = Expr.relative[ tokens[0].type ],
     2287                implicitRelative = leadingRelative || Expr.relative[" "],
     2288                i = leadingRelative ? 1 : 0,
     2289
     2290                // The foundational matcher ensures that elements are reachable from top-level context(s)
     2291                matchContext = addCombinator( function( elem ) {
     2292                        return elem === checkContext;
     2293                }, implicitRelative, true ),
     2294                matchAnyContext = addCombinator( function( elem ) {
     2295                        return indexOf.call( checkContext, elem ) > -1;
     2296                }, implicitRelative, true ),
     2297                matchers = [ function( elem, context, xml ) {
     2298                        return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
     2299                                (checkContext = context).nodeType ?
     2300                                        matchContext( elem, context, xml ) :
     2301                                        matchAnyContext( elem, context, xml ) );
     2302                } ];
     2303
     2304        for ( ; i < len; i++ ) {
     2305                if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
     2306                        matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
     2307                } else {
     2308                        matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
     2309
     2310                        // Return special upon seeing a positional matcher
     2311                        if ( matcher[ expando ] ) {
     2312                                // Find the next relative operator (if any) for proper handling
     2313                                j = ++i;
     2314                                for ( ; j < len; j++ ) {
     2315                                        if ( Expr.relative[ tokens[j].type ] ) {
     2316                                                break;
     2317                                        }
     2318                                }
     2319                                return setMatcher(
     2320                                        i > 1 && elementMatcher( matchers ),
     2321                                        i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
     2322                                        matcher,
     2323                                        i < j && matcherFromTokens( tokens.slice( i, j ) ),
     2324                                        j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
     2325                                        j < len && toSelector( tokens )
     2326                                );
     2327                        }
     2328                        matchers.push( matcher );
     2329                }
     2330        }
     2331
     2332        return elementMatcher( matchers );
     2333}
     2334
     2335function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
     2336        // A counter to specify which element is currently being matched
     2337        var matcherCachedRuns = 0,
     2338                bySet = setMatchers.length > 0,
     2339                byElement = elementMatchers.length > 0,
     2340                superMatcher = function( seed, context, xml, results, expandContext ) {
     2341                        var elem, j, matcher,
     2342                                setMatched = [],
     2343                                matchedCount = 0,
     2344                                i = "0",
     2345                                unmatched = seed && [],
     2346                                outermost = expandContext != null,
     2347                                contextBackup = outermostContext,
     2348                                // We must always have either seed elements or context
     2349                                elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
     2350                                // Use integer dirruns iff this is the outermost matcher
     2351                                dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
     2352
     2353                        if ( outermost ) {
     2354                                outermostContext = context !== document && context;
     2355                                cachedruns = matcherCachedRuns;
     2356                        }
     2357
     2358                        // Add elements passing elementMatchers directly to results
     2359                        // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
     2360                        for ( ; (elem = elems[i]) != null; i++ ) {
     2361                                if ( byElement && elem ) {
     2362                                        j = 0;
     2363                                        while ( (matcher = elementMatchers[j++]) ) {
     2364                                                if ( matcher( elem, context, xml ) ) {
     2365                                                        results.push( elem );
     2366                                                        break;
     2367                                                }
     2368                                        }
     2369                                        if ( outermost ) {
     2370                                                dirruns = dirrunsUnique;
     2371                                                cachedruns = ++matcherCachedRuns;
     2372                                        }
     2373                                }
     2374
     2375                                // Track unmatched elements for set filters
     2376                                if ( bySet ) {
     2377                                        // They will have gone through all possible matchers
     2378                                        if ( (elem = !matcher && elem) ) {
     2379                                                matchedCount--;
     2380                                        }
     2381
     2382                                        // Lengthen the array for every element, matched or not
     2383                                        if ( seed ) {
     2384                                                unmatched.push( elem );
     2385                                        }
     2386                                }
     2387                        }
     2388
     2389                        // Apply set filters to unmatched elements
     2390                        matchedCount += i;
     2391                        if ( bySet && i !== matchedCount ) {
     2392                                j = 0;
     2393                                while ( (matcher = setMatchers[j++]) ) {
     2394                                        matcher( unmatched, setMatched, context, xml );
     2395                                }
     2396
     2397                                if ( seed ) {
     2398                                        // Reintegrate element matches to eliminate the need for sorting
     2399                                        if ( matchedCount > 0 ) {
     2400                                                while ( i-- ) {
     2401                                                        if ( !(unmatched[i] || setMatched[i]) ) {
     2402                                                                setMatched[i] = pop.call( results );
     2403                                                        }
     2404                                                }
     2405                                        }
     2406
     2407                                        // Discard index placeholder values to get only actual matches
     2408                                        setMatched = condense( setMatched );
     2409                                }
     2410
     2411                                // Add matches to results
     2412                                push.apply( results, setMatched );
     2413
     2414                                // Seedless set matches succeeding multiple successful matchers stipulate sorting
     2415                                if ( outermost && !seed && setMatched.length > 0 &&
     2416                                        ( matchedCount + setMatchers.length ) > 1 ) {
     2417
     2418                                        Sizzle.uniqueSort( results );
     2419                                }
     2420                        }
     2421
     2422                        // Override manipulation of globals by nested matchers
     2423                        if ( outermost ) {
     2424                                dirruns = dirrunsUnique;
     2425                                outermostContext = contextBackup;
     2426                        }
     2427
     2428                        return unmatched;
     2429                };
     2430
     2431        return bySet ?
     2432                markFunction( superMatcher ) :
     2433                superMatcher;
     2434}
     2435
     2436compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
     2437        var i,
     2438                setMatchers = [],
     2439                elementMatchers = [],
     2440                cached = compilerCache[ selector + " " ];
     2441
     2442        if ( !cached ) {
     2443                // Generate a function of recursive functions that can be used to check each element
     2444                if ( !group ) {
     2445                        group = tokenize( selector );
     2446                }
     2447                i = group.length;
     2448                while ( i-- ) {
     2449                        cached = matcherFromTokens( group[i] );
     2450                        if ( cached[ expando ] ) {
     2451                                setMatchers.push( cached );
     2452                        } else {
     2453                                elementMatchers.push( cached );
     2454                        }
     2455                }
     2456
     2457                // Cache the compiled function
     2458                cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
     2459        }
     2460        return cached;
     2461};
     2462
     2463function multipleContexts( selector, contexts, results ) {
     2464        var i = 0,
     2465                len = contexts.length;
     2466        for ( ; i < len; i++ ) {
     2467                Sizzle( selector, contexts[i], results );
     2468        }
     2469        return results;
     2470}
     2471
     2472function select( selector, context, results, seed ) {
     2473        var i, tokens, token, type, find,
     2474                match = tokenize( selector );
     2475
     2476        if ( !seed ) {
     2477                // Try to minimize operations if there is only one group
     2478                if ( match.length === 1 ) {
     2479
     2480                        // Take a shortcut and set the context if the root selector is an ID
     2481                        tokens = match[0] = match[0].slice( 0 );
     2482                        if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
     2483                                        context.nodeType === 9 && documentIsHTML &&
     2484                                        Expr.relative[ tokens[1].type ] ) {
     2485
     2486                                context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
     2487                                if ( !context ) {
     2488                                        return results;
     2489                                }
     2490
     2491                                selector = selector.slice( tokens.shift().value.length );
     2492                        }
     2493
     2494                        // Fetch a seed set for right-to-left matching
     2495                        i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
     2496                        while ( i-- ) {
     2497                                token = tokens[i];
     2498
     2499                                // Abort if we hit a combinator
     2500                                if ( Expr.relative[ (type = token.type) ] ) {
     2501                                        break;
     2502                                }
     2503                                if ( (find = Expr.find[ type ]) ) {
     2504                                        // Search, expanding context for leading sibling combinators
     2505                                        if ( (seed = find(
     2506                                                token.matches[0].replace( runescape, funescape ),
     2507                                                rsibling.test( tokens[0].type ) && context.parentNode || context
     2508                                        )) ) {
     2509
     2510                                                // If seed is empty or no tokens remain, we can return early
     2511                                                tokens.splice( i, 1 );
     2512                                                selector = seed.length && toSelector( tokens );
     2513                                                if ( !selector ) {
     2514                                                        push.apply( results, seed );
     2515                                                        return results;
     2516                                                }
     2517
     2518                                                break;
     2519                                        }
     2520                                }
     2521                        }
     2522                }
     2523        }
     2524
     2525        // Compile and execute a filtering function
     2526        // Provide `match` to avoid retokenization if we modified the selector above
     2527        compile( selector, match )(
     2528                seed,
     2529                context,
     2530                !documentIsHTML,
     2531                results,
     2532                rsibling.test( selector )
     2533        );
     2534        return results;
     2535}
     2536
     2537// Deprecated
     2538Expr.pseudos["nth"] = Expr.pseudos["eq"];
     2539
     2540// Easy API for creating new setFilters
     2541function setFilters() {}
     2542setFilters.prototype = Expr.filters = Expr.pseudos;
     2543Expr.setFilters = new setFilters();
     2544
     2545// Check sort stability
     2546support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
     2547
     2548// Initialize with the default document
     2549setDocument();
     2550
     2551// Always assume the presence of duplicates if sort doesn't
     2552// pass them to our comparison function (as in Google Chrome).
     2553[0, 0].sort( sortOrder );
     2554support.detectDuplicates = hasDuplicate;
     2555
     2556/*
     2557// EXPOSE
     2558if ( typeof define === "function" && define.amd ) {
     2559        define(function() { return Sizzle; });
     2560} else {
     2561        window.Sizzle = Sizzle;
     2562}
     2563*/
     2564
     2565// EXPOSE
     2566return Sizzle;
     2567});
     2568
     2569// Included from: js/tinymce/classes/dom/DomQuery.js
     2570
     2571/**
     2572 * DomQuery.js
     2573 *
     2574 * Copyright, Moxiecode Systems AB
     2575 * Released under LGPL License.
     2576 *
     2577 * License: http://www.tinymce.com/license
     2578 * Contributing: http://www.tinymce.com/contributing
     2579 *
     2580 * Some of this logic is based on jQuery code that is released under
     2581 * MIT license that grants us to sublicense it under LGPL.
     2582 *
     2583 * @ignore-file
     2584 */
     2585
     2586/**
     2587 * @class tinymce.dom.DomQuery
     2588 */
     2589define("tinymce/dom/DomQuery", [
     2590        "tinymce/dom/EventUtils",
     2591        "tinymce/dom/Sizzle"
     2592], function(EventUtils, Sizzle) {
     2593        var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
     2594        var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
     2595        var Event = EventUtils.Event;
     2596
     2597        function isDefined(obj) {
     2598                return typeof obj !== "undefined";
     2599        }
     2600
     2601        function isString(obj) {
     2602                return typeof obj === "string";
     2603        }
     2604
     2605        function createFragment(html) {
     2606                var frag, node, container;
     2607
     2608                container = doc.createElement("div");
     2609                frag = doc.createDocumentFragment();
     2610                container.innerHTML = html;
     2611
     2612                while ((node = container.firstChild)) {
     2613                        frag.appendChild(node);
     2614                }
     2615
     2616                return frag;
     2617        }
     2618
     2619        function domManipulate(targetNodes, sourceItem, callback) {
     2620                var i;
     2621
     2622                if (typeof sourceItem === "string") {
     2623                        sourceItem = createFragment(sourceItem);
     2624                } else if (sourceItem.length) {
     2625                        for (i = 0; i < sourceItem.length; i++) {
     2626                                domManipulate(targetNodes, sourceItem[i], callback);
     2627                        }
     2628
     2629                        return targetNodes;
     2630                }
     2631
     2632                i = targetNodes.length;
     2633                while (i--) {
     2634                        callback.call(targetNodes[i], sourceItem.parentNode ? sourceItem : sourceItem);
     2635                }
     2636
     2637                return targetNodes;
     2638        }
     2639
     2640        function hasClass(node, className) {
     2641                return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
     2642        }
     2643
     2644        /**
     2645         * Makes a map object out of a string that gets separated by a delimiter.
     2646         *
     2647         * @method makeMap
     2648         * @param {String} items Item string to split.
     2649         * @param {Object} map Optional object to add items to.
     2650         * @return {Object} name/value object with items as keys.
     2651         */
     2652        function makeMap(items, map) {
     2653                var i;
     2654
     2655                items = items || [];
     2656
     2657                if (typeof(items) == "string") {
     2658                        items = items.split(' ');
     2659                }
     2660
     2661                map = map || {};
     2662
     2663                i = items.length;
     2664                while (i--) {
     2665                        map[items[i]] = {};
     2666                }
     2667
     2668                return map;
     2669        }
     2670
     2671        var numericCssMap = makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom');
     2672
     2673        function DomQuery(selector, context) {
     2674                return new DomQuery.fn.init(selector, context);
     2675        }
     2676
     2677        /**
     2678         * Extends the specified object with another object.
     2679         *
     2680         * @method extend
     2681         * @param {Object} target Object to extend.
     2682         * @param {Object..} obj Multiple objects to extend with.
     2683         * @return {Object} Same as target, the extended object.
     2684         */
     2685        function extend(target) {
     2686                var args = arguments, arg, i, key;
     2687
     2688                for (i = 1; i < args.length; i++) {
     2689                        arg = args[i];
     2690
     2691                        for (key in arg) {
     2692                                target[key] = arg[key];
     2693                        }
     2694                }
     2695
     2696                return target;
     2697        }
     2698
     2699        /**
     2700         * Converts the specified object into a real JavaScript array.
     2701         *
     2702         * @method toArray
     2703         * @param {Object} obj Object to convert into array.
     2704         * @return {Array} Array object based in input.
     2705         */
     2706        function toArray(obj) {
     2707                var array = [], i, l;
     2708
     2709                for (i = 0, l = obj.length; i < l; i++) {
     2710                        array[i] = obj[i];
     2711                }
     2712
     2713                return array;
     2714        }
     2715
     2716        /**
     2717         * Returns the index of the specified item inside the array.
     2718         *
     2719         * @method inArray
     2720         * @param {Object} item Item to look for.
     2721         * @param {Array} array Array to look for item in.
     2722         * @return {Number} Index of the item or -1.
     2723         */
     2724        function inArray(item, array) {
     2725                var i;
     2726
     2727                if (array.indexOf) {
     2728                        return array.indexOf(item);
     2729                }
     2730
     2731                i = array.length;
     2732                while (i--) {
     2733                        if (array[i] === item) {
     2734                                return i;
     2735                        }
     2736                }
     2737
     2738                return -1;
     2739        }
     2740
     2741        /**
     2742         * Returns true/false if the specified object is an array.
     2743         *
     2744         * @method isArray
     2745         * @param {Object} obj Object to check if it's an array.
     2746         * @return {Boolean} true/false if the input object is array or not.
     2747         */
     2748        var isArray = Array.isArray || function(obj) {
     2749                return Object.prototype.toString.call(obj) === "[object Array]";
     2750        };
     2751
     2752        var whiteSpaceRegExp = /^\s*|\s*$/g;
     2753        var trim = function(str) {
     2754                return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
     2755        };
     2756
     2757        /**
     2758         * Executes the callback function for each item in array/object. If you return false in the
     2759         * callback it will break the loop.
     2760         *
     2761         * @method each
     2762         * @param {Object} obj Object to iterate.
     2763         * @param {function} callback Callback function to execute for each item.
     2764         */
     2765        function each(obj, callback) {
     2766                var length, key, i, undef, value;
     2767
     2768                if (obj) {
     2769                        length = obj.length;
     2770
     2771                        if (length === undef) {
     2772                                // Loop object items
     2773                                for (key in obj) {
     2774                                        if (obj.hasOwnProperty(key)) {
     2775                                                value = obj[key];
     2776                                                if (callback.call(value, value, key) === false) {
     2777                                                        break;
     2778                                                }
     2779                                        }
     2780                                }
     2781                        } else {
     2782                                // Loop array items
     2783                                for (i = 0; i < length; i++) {
     2784                                        value = obj[i];
     2785                                        if (callback.call(value, value, key) === false) {
     2786                                                break;
     2787                                        }
     2788                                }
     2789                        }
     2790                }
     2791
     2792                return obj;
     2793        }
     2794
     2795        DomQuery.fn = DomQuery.prototype = {
     2796                constructor: DomQuery,
     2797                selector: "",
     2798                length: 0,
     2799
     2800                init: function(selector, context) {
     2801                        var self = this, match, node;
     2802
     2803                        if (!selector) {
     2804                                return self;
     2805                        }
     2806
     2807                        if (selector.nodeType) {
     2808                                self.context = self[0] = selector;
     2809                                self.length = 1;
     2810
     2811                                return self;
     2812                        }
     2813
     2814                        if (isString(selector)) {
     2815                                if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
     2816                                        match = [null, selector, null];
     2817                                } else {
     2818                                        match = rquickExpr.exec(selector);
     2819                                }
     2820
     2821                                if (match) {
     2822                                        if (match[1]) {
     2823                                                node = createFragment(selector).firstChild;
     2824                                                while (node) {
     2825                                                        this.add(node);
     2826                                                        node = node.nextSibling;
     2827                                                }
     2828                                        } else {
     2829                                                node = doc.getElementById(match[2]);
     2830
     2831                                                if (node.id !== match[2]) {
     2832                                                        return self.find(selector);
     2833                                                }
     2834
     2835                                                self.length = 1;
     2836                                                self[0] = node;
     2837                                        }
     2838                                } else {
     2839                                        return DomQuery(context || document).find(selector);
     2840                                }
     2841                        } else {
     2842                                this.add(selector);
     2843                        }
     2844
     2845                        return self;
     2846                },
     2847
     2848                toArray: function() {
     2849                        return toArray(this);
     2850                },
     2851
     2852                add: function(items) {
     2853                        var self = this;
     2854
     2855                        // Force single item into array
     2856                        if (!isArray(items)) {
     2857                                if (items instanceof DomQuery) {
     2858                                        self.add(items.toArray());
     2859                                } else {
     2860                                        push.call(self, items);
     2861                                }
     2862                        } else {
     2863                                push.apply(self, items);
     2864                        }
     2865
     2866                        return self;
     2867                },
     2868
     2869                attr: function(name, value) {
     2870                        var self = this;
     2871
     2872                        if (typeof name === "object") {
     2873                                each(name, function(value, name) {
     2874                                        self.attr(name, value);
     2875                                });
     2876                        } else if (isDefined(value)) {
     2877                                this.each(function() {
     2878                                        if (this.nodeType === 1) {
     2879                                                this.setAttribute(name, value);
     2880                                        }
     2881                                });
     2882                        } else {
     2883                                return self[0] && self[0].nodeType === 1 ? self[0].getAttribute(name) : undefined;
     2884                        }
     2885
     2886                        return self;
     2887                },
     2888
     2889                css: function(name, value) {
     2890                        var self = this;
     2891
     2892                        if (typeof name === "object") {
     2893                                each(name, function(value, name) {
     2894                                        self.css(name, value);
     2895                                });
     2896                        } else {
     2897                                // Camelcase it, if needed
     2898                                name = name.replace(/-(\D)/g, function(a, b) {
     2899                                        return b.toUpperCase();
     2900                                });
     2901
     2902                                if (isDefined(value)) {
     2903                                        // Default px suffix on these
     2904                                        if (typeof(value) === 'number' && !numericCssMap[name]) {
     2905                                                value += 'px';
     2906                                        }
     2907
     2908                                        self.each(function() {
     2909                                                var style = this.style;
     2910
     2911                                                // IE specific opacity
     2912                                                if (name === "opacity" && this.runtimeStyle && typeof(this.runtimeStyle.opacity) === "undefined") {
     2913                                                        style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
     2914                                                }
     2915
     2916                                                try {
     2917                                                        style[name] = value;
     2918                                                } catch (ex) {
     2919                                                        // Ignore
     2920                                                }
     2921                                        });
     2922                                } else {
     2923                                        return self[0] ? self[0].style[name] : undefined;
     2924                                }
     2925                        }
     2926
     2927                        return self;
     2928                },
     2929
     2930                remove: function() {
     2931                        var self = this, node, i = this.length;
     2932
     2933                        while (i--) {
     2934                                node = self[i];
     2935                                Event.clean(node);
     2936
     2937                                if (node.parentNode) {
     2938                                        node.parentNode.removeChild(node);
     2939                                }
     2940                        }
     2941
     2942                        return this;
     2943                },
     2944
     2945                empty: function() {
     2946                        var self = this, node, i = this.length;
     2947
     2948                        while (i--) {
     2949                                node = self[i];
     2950                                while (node.firstChild) {
     2951                                        node.removeChild(node.firstChild);
     2952                                }
     2953                        }
     2954
     2955                        return this;
     2956                },
     2957
     2958                html: function(value) {
     2959                        var self = this, i;
     2960
     2961                        if (isDefined(value)) {
     2962                                i = self.length;
     2963                                while (i--) {
     2964                                        self[i].innerHTML = value;
     2965                                }
     2966
     2967                                return self;
     2968                        }
     2969
     2970                        return self[0] ? self[0].innerHTML : '';
     2971                },
     2972
     2973                text: function(value) {
     2974                        var self = this, i;
     2975
     2976                        if (isDefined(value)) {
     2977                                i = self.length;
     2978                                while (i--) {
     2979                                        self[i].innerText = self[0].textContent = value;
     2980                                }
     2981
     2982                                return self;
     2983                        }
     2984
     2985                        return self[0] ? self[0].innerText || self[0].textContent : '';
     2986                },
     2987
     2988                append: function() {
     2989                        return domManipulate(this, arguments, function(node) {
     2990                                if (this.nodeType === 1) {
     2991                                        this.appendChild(node);
     2992                                }
     2993                        });
     2994                },
     2995
     2996                prepend: function() {
     2997                        return domManipulate(this, arguments, function(node) {
     2998                                if (this.nodeType === 1) {
     2999                                        this.insertBefore(node, this.firstChild);
     3000                                }
     3001                        });
     3002                },
     3003
     3004                before: function() {
     3005                        var self = this;
     3006
     3007                        if (self[0] && self[0].parentNode) {
     3008                                return domManipulate(self, arguments, function(node) {
     3009                                        this.parentNode.insertBefore(node, this.nextSibling);
     3010                                });
     3011                        }
     3012
     3013                        return self;
     3014                },
     3015
     3016                after: function() {
     3017                        var self = this;
     3018
     3019                        if (self[0] && self[0].parentNode) {
     3020                                return domManipulate(self, arguments, function(node) {
     3021                                        this.parentNode.insertBefore(node, this);
     3022                                });
     3023                        }
     3024
     3025                        return self;
     3026                },
     3027
     3028                appendTo: function(val) {
     3029                        DomQuery(val).append(this);
     3030
     3031                        return this;
     3032                },
     3033
     3034                addClass: function(className) {
     3035                        return this.toggleClass(className, true);
     3036                },
     3037
     3038                removeClass: function(className) {
     3039                        return this.toggleClass(className, false);
     3040                },
     3041
     3042                toggleClass: function(className, state) {
     3043                        var self = this;
     3044
     3045                        if (className.indexOf(' ') !== -1) {
     3046                                each(className.split(' '), function() {
     3047                                        self.toggleClass(this, state);
     3048                                });
     3049                        } else {
     3050                                self.each(function() {
     3051                                        var node = this, existingClassName;
     3052
     3053                                        if (hasClass(node, className) !== state) {
     3054                                                existingClassName = node.className;
     3055
     3056                                                if (state) {
     3057                                                        node.className += existingClassName ? ' ' + className : className;
     3058                                                } else {
     3059                                                        node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
     3060                                                }
     3061                                        }
     3062                                });
     3063                        }
     3064
     3065                        return self;
     3066                },
     3067
     3068                hasClass: function(className) {
     3069                        return hasClass(this[0], className);
     3070                },
     3071
     3072                each: function(callback) {
     3073                        return each(this, callback);
     3074                },
     3075
     3076                on: function(name, callback) {
     3077                        return this.each(function() {
     3078                                Event.bind(this, name, callback);
     3079                        });
     3080                },
     3081
     3082                off: function(name, callback) {
     3083                        return this.each(function() {
     3084                                Event.unbind(this, name, callback);
     3085                        });
     3086                },
     3087
     3088                show: function() {
     3089                        return this.css('display', '');
     3090                },
     3091
     3092                hide: function() {
     3093                        return this.css('display', 'none');
     3094                },
     3095
     3096                slice: function() {
     3097                        return new DomQuery(slice.apply(this, arguments));
     3098                },
     3099
     3100                eq: function(index) {
     3101                        return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
     3102                },
     3103
     3104                first: function() {
     3105                        return this.eq(0);
     3106                },
     3107
     3108                last: function() {
     3109                        return this.eq(-1);
     3110                },
     3111
     3112                replaceWith: function(content) {
     3113                        var self = this;
     3114
     3115                        if (self[0]) {
     3116                                self[0].parentNode.replaceChild(DomQuery(content)[0], self[0]);
     3117                        }
     3118
     3119                        return self;
     3120                },
     3121
     3122                wrap: function(wrapper) {
     3123                        wrapper = DomQuery(wrapper)[0];
     3124
     3125                        return this.each(function() {
     3126                                var self = this, newWrapper = wrapper.cloneNode(false);
     3127                                self.parentNode.insertBefore(newWrapper, self);
     3128                                newWrapper.appendChild(self);
     3129                        });
     3130                },
     3131
     3132                unwrap: function() {
     3133                        return this.each(function() {
     3134                                var self = this, node = self.firstChild, currentNode;
     3135
     3136                                while (node) {
     3137                                        currentNode = node;
     3138                                        node = node.nextSibling;
     3139                                        self.parentNode.insertBefore(currentNode, self);
     3140                                }
     3141                        });
     3142                },
     3143
     3144                clone: function() {
     3145                        var result = [];
     3146
     3147                        this.each(function() {
     3148                                result.push(this.cloneNode(true));
     3149                        });
     3150
     3151                        return DomQuery(result);
     3152                },
     3153
     3154                find: function(selector) {
     3155                        var i, l, ret = [];
     3156
     3157                        for (i = 0, l = this.length; i < l; i++) {
     3158                                DomQuery.find(selector, this[i], ret);
     3159                        }
     3160
     3161                        return DomQuery(ret);
     3162                },
     3163
     3164                push: push,
     3165                sort: [].sort,
     3166                splice: [].splice
     3167        };
     3168
     3169        // Static members
     3170        extend(DomQuery, {
     3171                extend: extend,
     3172                toArray: toArray,
     3173                inArray: inArray,
     3174                isArray: isArray,
     3175                each: each,
     3176                trim: trim,
     3177                makeMap: makeMap,
     3178
     3179                // Sizzle
     3180                find: Sizzle,
     3181                expr: Sizzle.selectors,
     3182                unique: Sizzle.uniqueSort,
     3183                text: Sizzle.getText,
     3184                isXMLDoc: Sizzle.isXML,
     3185                contains: Sizzle.contains,
     3186                filter: function(expr, elems, not) {
     3187                        if (not) {
     3188                                expr = ":not(" + expr + ")";
     3189                        }
     3190
     3191                        return elems.length === 1 ?
     3192                                DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [] :
     3193                                DomQuery.find.matches(expr, elems);
     3194                }
     3195        });
     3196
     3197        function dir(el, prop, until) {
     3198                var matched = [], cur = el[prop];
     3199
     3200                while (cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !DomQuery(cur).is(until))) {
     3201                        if (cur.nodeType === 1) {
     3202                                matched.push(cur);
     3203                        }
     3204
     3205                        cur = cur[prop];
     3206                }
     3207
     3208                return matched;
     3209        }
     3210
     3211        function sibling(n, el, siblingName, nodeType) {
     3212                var r = [];
     3213
     3214                for(; n; n = n[siblingName]) {
     3215                        if ((!nodeType || n.nodeType === nodeType) && n !== el) {
     3216                                r.push(n);
     3217                        }
     3218                }
     3219
     3220                return r;
     3221        }
     3222
     3223        each({
     3224                parent: function(node) {
     3225                        var parent = node.parentNode;
     3226
     3227                        return parent && parent.nodeType !== 11 ? parent : null;
     3228                },
     3229
     3230                parents: function(node) {
     3231                        return dir(node, "parentNode");
     3232                },
     3233
     3234                parentsUntil: function(node, until) {
     3235                        return dir(node, "parentNode", until);
     3236                },
     3237
     3238                next: function(node) {
     3239                        return sibling(node, 'nextSibling', 1);
     3240                },
     3241
     3242                prev: function(node) {
     3243                        return sibling(node, 'previousSibling', 1);
     3244                },
     3245
     3246                nextNodes: function(node) {
     3247                        return sibling(node, 'nextSibling');
     3248                },
     3249
     3250                prevNodes: function(node) {
     3251                        return sibling(node, 'previousSibling');
     3252                },
     3253
     3254                children: function(node) {
     3255                        return sibling(node.firstChild, 'nextSibling', 1);
     3256                },
     3257
     3258                contents: function(node) {
     3259                        return toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
     3260                }
     3261        }, function(name, fn){
     3262                DomQuery.fn[name] = function(selector) {
     3263                        var self = this, result;
     3264
     3265                        if (self.length > 1) {
     3266                                throw new Error("DomQuery only supports traverse functions on a single node.");
     3267                        }
     3268
     3269                        if (self[0]) {
     3270                                result = fn(self[0], selector);
     3271                        }
     3272
     3273                        result = DomQuery(result);
     3274
     3275                        if (selector && name !== "parentsUntil") {
     3276                                return result.filter(selector);
     3277                        }
     3278
     3279                        return result;
     3280                };
     3281        });
     3282
     3283        DomQuery.fn.filter = function(selector) {
     3284                return DomQuery.filter(selector);
     3285        };
     3286
     3287        DomQuery.fn.is = function(selector) {
     3288                return !!selector && this.filter(selector).length > 0;
     3289        };
     3290
     3291        DomQuery.fn.init.prototype = DomQuery.fn;
     3292
     3293        return DomQuery;
     3294});
     3295
     3296// Included from: js/tinymce/classes/html/Styles.js
     3297
     3298/**
     3299 * Styles.js
     3300 *
     3301 * Copyright, Moxiecode Systems AB
     3302 * Released under LGPL License.
     3303 *
     3304 * License: http://www.tinymce.com/license
     3305 * Contributing: http://www.tinymce.com/contributing
     3306 */
     3307
     3308/**
     3309 * This class is used to parse CSS styles it also compresses styles to reduce the output size.
     3310 *
     3311 * @example
     3312 * var Styles = new tinymce.html.Styles({
     3313 *    url_converter: function(url) {
     3314 *       return url;
     3315 *    }
     3316 * });
     3317 *
     3318 * styles = Styles.parse('border: 1px solid red');
     3319 * styles.color = 'red';
     3320 *
     3321 * console.log(new tinymce.html.StyleSerializer().serialize(styles));
     3322 *
     3323 * @class tinymce.html.Styles
     3324 * @version 3.4
     3325 */
     3326define("tinymce/html/Styles", [], function() {
     3327        return function(settings, schema) {
     3328                /*jshint maxlen:255 */
     3329                var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
     3330                        urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
     3331                        styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
     3332                        trimRightRegExp = /\s+$/,
     3333                        undef, i, encodingLookup = {}, encodingItems, invisibleChar = '\uFEFF';
     3334
     3335                settings = settings || {};
     3336
     3337                encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
     3338                for (i = 0; i < encodingItems.length; i++) {
     3339                        encodingLookup[encodingItems[i]] = invisibleChar + i;
     3340                        encodingLookup[invisibleChar + i] = encodingItems[i];
     3341                }
     3342
     3343                function toHex(match, r, g, b) {
     3344                        function hex(val) {
     3345                                val = parseInt(val, 10).toString(16);
     3346
     3347                                return val.length > 1 ? val : '0' + val; // 0 -> 00
     3348                        }
     3349
     3350                        return '#' + hex(r) + hex(g) + hex(b);
     3351                }
     3352
     3353                return {
     3354                        /**
     3355                         * Parses the specified RGB color value and returns a hex version of that color.
     3356                         *
     3357                         * @method toHex
     3358                         * @param {String} color RGB string value like rgb(1,2,3)
     3359                         * @return {String} Hex version of that RGB value like #FF00FF.
     3360                         */
     3361                        toHex: function(color) {
     3362                                return color.replace(rgbRegExp, toHex);
     3363                        },
     3364
     3365                        /**
     3366                         * Parses the specified style value into an object collection. This parser will also
     3367                         * merge and remove any redundant items that browsers might have added. It will also convert non hex
     3368                         * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
     3369                         *
     3370                         * @method parse
     3371                         * @param {String} css Style value to parse for example: border:1px solid red;.
     3372                         * @return {Object} Object representation of that style like {border: '1px solid red'}
     3373                         */
     3374                        parse: function(css) {
     3375                                var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
     3376                                var urlConverterScope = settings.url_converter_scope || this;
     3377
     3378                                function compress(prefix, suffix, noJoin) {
     3379                                        var top, right, bottom, left;
     3380
     3381                                        top = styles[prefix + '-top' + suffix];
     3382                                        if (!top) {
     3383                                                return;
     3384                                        }
     3385
     3386                                        right = styles[prefix + '-right' + suffix];
     3387                                        if (!right) {
     3388                                                return;
     3389                                        }
     3390
     3391                                        bottom = styles[prefix + '-bottom' + suffix];
     3392                                        if (!bottom) {
     3393                                                return;
     3394                                        }
     3395
     3396                                        left = styles[prefix + '-left' + suffix];
     3397                                        if (!left) {
     3398                                                return;
     3399                                        }
     3400
     3401                                        var box = [top, right, bottom, left];
     3402                                        i = box.length - 1;
     3403                                        while (i--) {
     3404                                                if (box[i] !== box[i + 1]) {
     3405                                                        break;
     3406                                                }
     3407                                        }
     3408
     3409                                        if (i > -1 && noJoin) {
     3410                                                return;
     3411                                        }
     3412
     3413                                        styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
     3414                                        delete styles[prefix + '-top' + suffix];
     3415                                        delete styles[prefix + '-right' + suffix];
     3416                                        delete styles[prefix + '-bottom' + suffix];
     3417                                        delete styles[prefix + '-left' + suffix];
     3418                                }
     3419
     3420                                /**
     3421                                 * Checks if the specific style can be compressed in other words if all border-width are equal.
     3422                                 */
     3423                                function canCompress(key) {
     3424                                        var value = styles[key], i;
     3425
     3426                                        if (!value) {
     3427                                                return;
     3428                                        }
     3429
     3430                                        value = value.split(' ');
     3431                                        i = value.length;
     3432                                        while (i--) {
     3433                                                if (value[i] !== value[0]) {
     3434                                                        return false;
     3435                                                }
     3436                                        }
     3437
     3438                                        styles[key] = value[0];
     3439
     3440                                        return true;
     3441                                }
     3442
     3443                                /**
     3444                                 * Compresses multiple styles into one style.
     3445                                 */
     3446                                function compress2(target, a, b, c) {
     3447                                        if (!canCompress(a)) {
     3448                                                return;
     3449                                        }
     3450
     3451                                        if (!canCompress(b)) {
     3452                                                return;
     3453                                        }
     3454
     3455                                        if (!canCompress(c)) {
     3456                                                return;
     3457                                        }
     3458
     3459                                        // Compress
     3460                                        styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
     3461                                        delete styles[a];
     3462                                        delete styles[b];
     3463                                        delete styles[c];
     3464                                }
     3465
     3466                                // Encodes the specified string by replacing all \" \' ; : with _<num>
     3467                                function encode(str) {
     3468                                        isEncoded = true;
     3469
     3470                                        return encodingLookup[str];
     3471                                }
     3472
     3473                                // Decodes the specified string by replacing all _<num> with it's original value \" \' etc
     3474                                // It will also decode the \" \' if keep_slashes is set to fale or omitted
     3475                                function decode(str, keep_slashes) {
     3476                                        if (isEncoded) {
     3477                                                str = str.replace(/\uFEFF[0-9]/g, function(str) {
     3478                                                        return encodingLookup[str];
     3479                                                });
     3480                                        }
     3481
     3482                                        if (!keep_slashes) {
     3483                                                str = str.replace(/\\([\'\";:])/g, "$1");
     3484                                        }
     3485
     3486                                        return str;
     3487                                }
     3488
     3489                                function processUrl(match, url, url2, url3, str, str2) {
     3490                                        str = str || str2;
     3491
     3492                                        if (str) {
     3493                                                str = decode(str);
     3494
     3495                                                // Force strings into single quote format
     3496                                                return "'" + str.replace(/\'/g, "\\'") + "'";
     3497                                        }
     3498
     3499                                        url = decode(url || url2 || url3);
     3500
     3501                                        if (!settings.allow_script_urls && /(java|vb)script:/i.test(url.replace(/[\s\r\n]+/, ''))) {
     3502                                                return "";
     3503                                        }
     3504
     3505                                        // Convert the URL to relative/absolute depending on config
     3506                                        if (urlConverter) {
     3507                                                url = urlConverter.call(urlConverterScope, url, 'style');
     3508                                        }
     3509
     3510                                        // Output new URL format
     3511                                        return "url('" + url.replace(/\'/g, "\\'") + "')";
     3512                                }
     3513
     3514                                if (css) {
     3515                                        css = css.replace(/[\u0000-\u001F]/g, '');
     3516
     3517                                        // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
     3518                                        css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
     3519                                                return str.replace(/[;:]/g, encode);
     3520                                        });
     3521
     3522                                        // Parse styles
     3523                                        while ((matches = styleRegExp.exec(css))) {
     3524                                                name = matches[1].replace(trimRightRegExp, '').toLowerCase();
     3525                                                value = matches[2].replace(trimRightRegExp, '');
     3526
     3527                                                if (name && value.length > 0) {
     3528                                                        if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(/.test(value))) {
     3529                                                                continue;
     3530                                                        }
     3531
     3532                                                        // Opera will produce 700 instead of bold in their style values
     3533                                                        if (name === 'font-weight' && value === '700') {
     3534                                                                value = 'bold';
     3535                                                        } else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
     3536                                                                value = value.toLowerCase();
     3537                                                        }
     3538
     3539                                                        // Convert RGB colors to HEX
     3540                                                        value = value.replace(rgbRegExp, toHex);
     3541
     3542                                                        // Convert URLs and force them into url('value') format
     3543                                                        value = value.replace(urlOrStrRegExp, processUrl);
     3544                                                        styles[name] = isEncoded ? decode(value, true) : value;
     3545                                                }
     3546
     3547                                                styleRegExp.lastIndex = matches.index + matches[0].length;
     3548                                        }
     3549                                        // Compress the styles to reduce it's size for example IE will expand styles
     3550                                        compress("border", "", true);
     3551                                        compress("border", "-width");
     3552                                        compress("border", "-color");
     3553                                        compress("border", "-style");
     3554                                        compress("padding", "");
     3555                                        compress("margin", "");
     3556                                        compress2('border', 'border-width', 'border-style', 'border-color');
     3557
     3558                                        // Remove pointless border, IE produces these
     3559                                        if (styles.border === 'medium none') {
     3560                                                delete styles.border;
     3561                                        }
     3562
     3563                                        // IE 11 will produce a border-image: none when getting the style attribute from <p style="border: 1px solid red"></p>
     3564                                        // So lets asume it shouldn't be there
     3565                                        if (styles['border-image'] === 'none') {
     3566                                                delete styles['border-image'];
     3567                                        }
     3568                                }
     3569
     3570                                return styles;
     3571                        },
     3572
     3573                        /**
     3574                         * Serializes the specified style object into a string.
     3575                         *
     3576                         * @method serialize
     3577                         * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
     3578                         * @param {String} element_name Optional element name, if specified only the styles that matches the schema will be serialized.
     3579                         * @return {String} String representation of the style object for example: border: 1px solid red.
     3580                         */
     3581                        serialize: function(styles, element_name) {
     3582                                var css = '', name, value;
     3583
     3584                                function serializeStyles(name) {
     3585                                        var styleList, i, l, value;
     3586
     3587                                        styleList = schema.styles[name];
     3588                                        if (styleList) {
     3589                                                for (i = 0, l = styleList.length; i < l; i++) {
     3590                                                        name = styleList[i];
     3591                                                        value = styles[name];
     3592
     3593                                                        if (value !== undef && value.length > 0) {
     3594                                                                css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
     3595                                                        }
     3596                                                }
     3597                                        }
     3598                                }
     3599
     3600                                // Serialize styles according to schema
     3601                                if (element_name && schema && schema.styles) {
     3602                                        // Serialize global styles and element specific styles
     3603                                        serializeStyles('*');
     3604                                        serializeStyles(element_name);
     3605                                } else {
     3606                                        // Output the styles in the order they are inside the object
     3607                                        for (name in styles) {
     3608                                                value = styles[name];
     3609
     3610                                                if (value !== undef && value.length > 0) {
     3611                                                        css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
     3612                                                }
     3613                                        }
     3614                                }
     3615
     3616                                return css;
     3617                        }
     3618                };
     3619        };
     3620});
     3621
     3622// Included from: js/tinymce/classes/dom/TreeWalker.js
     3623
     3624/**
     3625 * TreeWalker.js
     3626 *
     3627 * Copyright, Moxiecode Systems AB
     3628 * Released under LGPL License.
     3629 *
     3630 * License: http://www.tinymce.com/license
     3631 * Contributing: http://www.tinymce.com/contributing
     3632 */
     3633
     3634/**
     3635 * TreeWalker class enables you to walk the DOM in a linear manner.
     3636 *
     3637 * @class tinymce.dom.TreeWalker
     3638 */
     3639define("tinymce/dom/TreeWalker", [], function() {
     3640        return function(start_node, root_node) {
     3641                var node = start_node;
     3642
     3643                function findSibling(node, start_name, sibling_name, shallow) {
     3644                        var sibling, parent;
     3645
     3646                        if (node) {
     3647                                // Walk into nodes if it has a start
     3648                                if (!shallow && node[start_name]) {
     3649                                        return node[start_name];
     3650                                }
     3651
     3652                                // Return the sibling if it has one
     3653                                if (node != root_node) {
     3654                                        sibling = node[sibling_name];
     3655                                        if (sibling) {
     3656                                                return sibling;
     3657                                        }
     3658
     3659                                        // Walk up the parents to look for siblings
     3660                                        for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
     3661                                                sibling = parent[sibling_name];
     3662                                                if (sibling) {
     3663                                                        return sibling;
     3664                                                }
     3665                                        }
     3666                                }
     3667                        }
     3668                }
     3669
     3670                /**
     3671                 * Returns the current node.
     3672                 *
     3673                 * @method current
     3674                 * @return {Node} Current node where the walker is.
     3675                 */
     3676                this.current = function() {
     3677                        return node;
     3678                };
     3679
     3680                /**
     3681                 * Walks to the next node in tree.
     3682                 *
     3683                 * @method next
     3684                 * @return {Node} Current node where the walker is after moving to the next node.
     3685                 */
     3686                this.next = function(shallow) {
     3687                        node = findSibling(node, 'firstChild', 'nextSibling', shallow);
     3688                        return node;
     3689                };
     3690
     3691                /**
     3692                 * Walks to the previous node in tree.
     3693                 *
     3694                 * @method prev
     3695                 * @return {Node} Current node where the walker is after moving to the previous node.
     3696                 */
     3697                this.prev = function(shallow) {
     3698                        node = findSibling(node, 'lastChild', 'previousSibling', shallow);
     3699                        return node;
     3700                };
     3701        };
     3702});
     3703
     3704// Included from: js/tinymce/classes/util/Tools.js
     3705
     3706/**
     3707 * Tools.js
     3708 *
     3709 * Copyright, Moxiecode Systems AB
     3710 * Released under LGPL License.
     3711 *
     3712 * License: http://www.tinymce.com/license
     3713 * Contributing: http://www.tinymce.com/contributing
     3714 */
     3715
     3716/**
     3717 * This class contains various utlity functions. These are also exposed
     3718 * directly on the tinymce namespace.
     3719 *
     3720 * @class tinymce.util.Tools
     3721 */
     3722define("tinymce/util/Tools", [], function() {
     3723        /**
     3724         * Removes whitespace from the beginning and end of a string.
     3725         *
     3726         * @method trim
     3727         * @param {String} s String to remove whitespace from.
     3728         * @return {String} New string with removed whitespace.
     3729         */
     3730        var whiteSpaceRegExp = /^\s*|\s*$/g;
     3731        var trim = function(str) {
     3732                return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
     3733        };
     3734
     3735        /**
     3736         * Returns true/false if the object is an array or not.
     3737         *
     3738         * @method isArray
     3739         * @param {Object} obj Object to check.
     3740         * @return {boolean} true/false state if the object is an array or not.
     3741         */
     3742        var isArray = Array.isArray || function(obj) {
     3743                return Object.prototype.toString.call(obj) === "[object Array]";
     3744        };
     3745
     3746        /**
     3747         * Checks if a object is of a specific type for example an array.
     3748         *
     3749         * @method is
     3750         * @param {Object} o Object to check type of.
     3751         * @param {string} t Optional type to check for.
     3752         * @return {Boolean} true/false if the object is of the specified type.
     3753         */
     3754        function is(o, t) {
     3755                if (!t) {
     3756                        return o !== undefined;
     3757                }
     3758
     3759                if (t == 'array' && isArray(o)) {
     3760                        return true;
     3761                }
     3762
     3763                return typeof(o) == t;
     3764        }
     3765
     3766        /**
     3767         * Converts the specified object into a real JavaScript array.
     3768         *
     3769         * @method toArray
     3770         * @param {Object} obj Object to convert into array.
     3771         * @return {Array} Array object based in input.
     3772         */
     3773        function toArray(obj) {
     3774                var array = [], i, l;
     3775
     3776                for (i = 0, l = obj.length; i < l; i++) {
     3777                        array[i] = obj[i];
     3778                }
     3779
     3780                return array;
     3781        }
     3782
     3783        /**
     3784         * Makes a name/object map out of an array with names.
     3785         *
     3786         * @method makeMap
     3787         * @param {Array/String} items Items to make map out of.
     3788         * @param {String} delim Optional delimiter to split string by.
     3789         * @param {Object} map Optional map to add items to.
     3790         * @return {Object} Name/value map of items.
     3791         */
     3792        function makeMap(items, delim, map) {
     3793                var i;
     3794
     3795                items = items || [];
     3796                delim = delim || ',';
     3797
     3798                if (typeof(items) == "string") {
     3799                        items = items.split(delim);
     3800                }
     3801
     3802                map = map || {};
     3803
     3804                i = items.length;
     3805                while (i--) {
     3806                        map[items[i]] = {};
     3807                }
     3808
     3809                return map;
     3810        }
     3811
     3812        /**
     3813         * Performs an iteration of all items in a collection such as an object or array. This method will execure the
     3814         * callback function for each item in the collection, if the callback returns false the iteration will terminate.
     3815         * The callback has the following format: cb(value, key_or_index).
     3816         *
     3817         * @method each
     3818         * @param {Object} o Collection to iterate.
     3819         * @param {function} cb Callback function to execute for each item.
     3820         * @param {Object} s Optional scope to execute the callback in.
     3821         * @example
     3822         * // Iterate an array
     3823         * tinymce.each([1,2,3], function(v, i) {
     3824         *     console.debug("Value: " + v + ", Index: " + i);
     3825         * });
     3826         *
     3827         * // Iterate an object
     3828         * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
     3829         *     console.debug("Value: " + v + ", Key: " + k);
     3830         * });
     3831         */
     3832        function each(o, cb, s) {
     3833                var n, l;
     3834
     3835                if (!o) {
     3836                        return 0;
     3837                }
     3838
     3839                s = s || o;
     3840
     3841                if (o.length !== undefined) {
     3842                        // Indexed arrays, needed for Safari
     3843                        for (n=0, l = o.length; n < l; n++) {
     3844                                if (cb.call(s, o[n], n, o) === false) {
     3845                                        return 0;
     3846                                }
     3847                        }
     3848                } else {
     3849                        // Hashtables
     3850                        for (n in o) {
     3851                                if (o.hasOwnProperty(n)) {
     3852                                        if (cb.call(s, o[n], n, o) === false) {
     3853                                                return 0;
     3854                                        }
     3855                                }
     3856                        }
     3857                }
     3858
     3859                return 1;
     3860        }
     3861
     3862        /**
     3863         * Creates a new array by the return value of each iteration function call. This enables you to convert
     3864         * one array list into another.
     3865         *
     3866         * @method map
     3867         * @param {Array} a Array of items to iterate.
     3868         * @param {function} f Function to call for each item. It's return value will be the new value.
     3869         * @return {Array} Array with new values based on function return values.
     3870         */
     3871        function map(a, f) {
     3872                var o = [];
     3873
     3874                each(a, function(v) {
     3875                        o.push(f(v));
     3876                });
     3877
     3878                return o;
     3879        }
     3880
     3881        /**
     3882         * Filters out items from the input array by calling the specified function for each item.
     3883         * If the function returns false the item will be excluded if it returns true it will be included.
     3884         *
     3885         * @method grep
     3886         * @param {Array} a Array of items to loop though.
     3887         * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
     3888         * @return {Array} New array with values imported and filtered based in input.
     3889         * @example
     3890         * // Filter out some items, this will return an array with 4 and 5
     3891         * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
     3892         */
     3893        function grep(a, f) {
     3894                var o = [];
     3895
     3896                each(a, function(v) {
     3897                        if (!f || f(v)) {
     3898                                o.push(v);
     3899                        }
     3900                });
     3901
     3902                return o;
     3903        }
     3904
     3905        /**
     3906         * Creates a class, subclass or static singleton.
     3907         * More details on this method can be found in the Wiki.
     3908         *
     3909         * @method create
     3910         * @param {String} s Class name, inheritage and prefix.
     3911         * @param {Object} p Collection of methods to add to the class.
     3912         * @param {Object} root Optional root object defaults to the global window object.
     3913         * @example
     3914         * // Creates a basic class
     3915         * tinymce.create('tinymce.somepackage.SomeClass', {
     3916         *     SomeClass: function() {
     3917         *         // Class constructor
     3918         *     },
     3919         *
     3920         *     method: function() {
     3921         *         // Some method
     3922         *     }
     3923         * });
     3924         *
     3925         * // Creates a basic subclass class
     3926         * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
     3927         *     SomeSubClass: function() {
     3928         *         // Class constructor
     3929         *         this.parent(); // Call parent constructor
     3930         *     },
     3931         *
     3932         *     method: function() {
     3933         *         // Some method
     3934         *         this.parent(); // Call parent method
     3935         *     },
     3936         *
     3937         *     'static': {
     3938         *         staticMethod: function() {
     3939         *             // Static method
     3940         *         }
     3941         *     }
     3942         * });
     3943         *
     3944         * // Creates a singleton/static class
     3945         * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
     3946         *     method: function() {
     3947         *         // Some method
     3948         *     }
     3949         * });
     3950         */
     3951        function create(s, p, root) {
     3952                var t = this, sp, ns, cn, scn, c, de = 0;
     3953
     3954                // Parse : <prefix> <class>:<super class>
     3955                s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
     3956                cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
     3957
     3958                // Create namespace for new class
     3959                ns = t.createNS(s[3].replace(/\.\w+$/, ''), root);
     3960
     3961                // Class already exists
     3962                if (ns[cn]) {
     3963                        return;
     3964                }
     3965
     3966                // Make pure static class
     3967                if (s[2] == 'static') {
     3968                        ns[cn] = p;
     3969
     3970                        if (this.onCreate) {
     3971                                this.onCreate(s[2], s[3], ns[cn]);
     3972                        }
     3973
     3974                        return;
     3975                }
     3976
     3977                // Create default constructor
     3978                if (!p[cn]) {
     3979                        p[cn] = function() {};
     3980                        de = 1;
     3981                }
     3982
     3983                // Add constructor and methods
     3984                ns[cn] = p[cn];
     3985                t.extend(ns[cn].prototype, p);
     3986
     3987                // Extend
     3988                if (s[5]) {
     3989                        sp = t.resolve(s[5]).prototype;
     3990                        scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
     3991
     3992                        // Extend constructor
     3993                        c = ns[cn];
     3994                        if (de) {
     3995                                // Add passthrough constructor
     3996                                ns[cn] = function() {
     3997                                        return sp[scn].apply(this, arguments);
     3998                                };
     3999                        } else {
     4000                                // Add inherit constructor
     4001                                ns[cn] = function() {
     4002                                        this.parent = sp[scn];
     4003                                        return c.apply(this, arguments);
     4004                                };
     4005                        }
     4006                        ns[cn].prototype[cn] = ns[cn];
     4007
     4008                        // Add super methods
     4009                        t.each(sp, function(f, n) {
     4010                                ns[cn].prototype[n] = sp[n];
     4011                        });
     4012
     4013                        // Add overridden methods
     4014                        t.each(p, function(f, n) {
     4015                                // Extend methods if needed
     4016                                if (sp[n]) {
     4017                                        ns[cn].prototype[n] = function() {
     4018                                                this.parent = sp[n];
     4019                                                return f.apply(this, arguments);
     4020                                        };
     4021                                } else {
     4022                                        if (n != cn) {
     4023                                                ns[cn].prototype[n] = f;
     4024                                        }
     4025                                }
     4026                        });
     4027                }
     4028
     4029                // Add static methods
     4030                /*jshint sub:true*/
     4031                t.each(p['static'], function(f, n) {
     4032                        ns[cn][n] = f;
     4033                });
     4034        }
     4035
     4036        /**
     4037         * Returns the index of a value in an array, this method will return -1 if the item wasn't found.
     4038         *
     4039         * @method inArray
     4040         * @param {Array} a Array/Object to search for value in.
     4041         * @param {Object} v Value to check for inside the array.
     4042         * @return {Number/String} Index of item inside the array inside an object. Or -1 if it wasn't found.
     4043         * @example
     4044         * // Get index of value in array this will alert 1 since 2 is at that index
     4045         * alert(tinymce.inArray([1,2,3], 2));
     4046         */
     4047        function inArray(a, v) {
     4048                var i, l;
     4049
     4050                if (a) {
     4051                        for (i = 0, l = a.length; i < l; i++) {
     4052                                if (a[i] === v) {
     4053                                        return i;
     4054                                }
     4055                        }
     4056                }
     4057
     4058                return -1;
     4059        }
     4060
     4061        function extend(obj, ext) {
     4062                var i, l, name, args = arguments, value;
     4063
     4064                for (i = 1, l = args.length; i < l; i++) {
     4065                        ext = args[i];
     4066                        for (name in ext) {
     4067                                if (ext.hasOwnProperty(name)) {
     4068                                        value = ext[name];
     4069
     4070                                        if (value !== undefined) {
     4071                                                obj[name] = value;
     4072                                        }
     4073                                }
     4074                        }
     4075                }
     4076
     4077                return obj;
     4078        }
     4079
     4080        /**
     4081         * Executed the specified function for each item in a object tree.
     4082         *
     4083         * @method walk
     4084         * @param {Object} o Object tree to walk though.
     4085         * @param {function} f Function to call for each item.
     4086         * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
     4087         * @param {String} s Optional scope to execute the function in.
     4088         */
     4089        function walk(o, f, n, s) {
     4090                s = s || this;
     4091
     4092                if (o) {
     4093                        if (n) {
     4094                                o = o[n];
     4095                        }
     4096
     4097                        each(o, function(o, i) {
     4098                                if (f.call(s, o, i, n) === false) {
     4099                                        return false;
     4100                                }
     4101
     4102                                walk(o, f, n, s);
     4103                        });
     4104                }
     4105        }
     4106
     4107        /**
     4108         * Creates a namespace on a specific object.
     4109         *
     4110         * @method createNS
     4111         * @param {String} n Namespace to create for example a.b.c.d.
     4112         * @param {Object} o Optional object to add namespace to, defaults to window.
     4113         * @return {Object} New namespace object the last item in path.
     4114         * @example
     4115         * // Create some namespace
     4116         * tinymce.createNS('tinymce.somepackage.subpackage');
     4117         *
     4118         * // Add a singleton
     4119         * var tinymce.somepackage.subpackage.SomeSingleton = {
     4120         *     method: function() {
     4121         *         // Some method
     4122         *     }
     4123         * };
     4124         */
     4125        function createNS(n, o) {
     4126                var i, v;
     4127
     4128                o = o || window;
     4129
     4130                n = n.split('.');
     4131                for (i=0; i<n.length; i++) {
     4132                        v = n[i];
     4133
     4134                        if (!o[v]) {
     4135                                o[v] = {};
     4136                        }
     4137
     4138                        o = o[v];
     4139                }
     4140
     4141                return o;
     4142        }
     4143
     4144        /**
     4145         * Resolves a string and returns the object from a specific structure.
     4146         *
     4147         * @method resolve
     4148         * @param {String} n Path to resolve for example a.b.c.d.
     4149         * @param {Object} o Optional object to search though, defaults to window.
     4150         * @return {Object} Last object in path or null if it couldn't be resolved.
     4151         * @example
     4152         * // Resolve a path into an object reference
     4153         * var obj = tinymce.resolve('a.b.c.d');
     4154         */
     4155        function resolve(n, o) {
     4156                var i, l;
     4157
     4158                o = o || window;
     4159
     4160                n = n.split('.');
     4161                for (i = 0, l = n.length; i < l; i++) {
     4162                        o = o[n[i]];
     4163
     4164                        if (!o) {
     4165                                break;
     4166                        }
     4167                }
     4168
     4169                return o;
     4170        }
     4171
     4172        /**
     4173         * Splits a string but removes the whitespace before and after each value.
     4174         *
     4175         * @method explode
     4176         * @param {string} s String to split.
     4177         * @param {string} d Delimiter to split by.
     4178         * @example
     4179         * // Split a string into an array with a,b,c
     4180         * var arr = tinymce.explode('a, b,   c');
     4181         */
     4182        function explode(s, d) {
     4183                if (!s || is(s, 'array')) {
     4184                        return s;
     4185                }
     4186
     4187                return map(s.split(d || ','), trim);
     4188        }
     4189
     4190        return {
     4191                trim: trim,
     4192                isArray: isArray,
     4193                is: is,
     4194                toArray: toArray,
     4195                makeMap: makeMap,
     4196                each: each,
     4197                map: map,
     4198                grep: grep,
     4199                inArray: inArray,
     4200                extend: extend,
     4201                create: create,
     4202                walk: walk,
     4203                createNS: createNS,
     4204                resolve: resolve,
     4205                explode: explode
     4206        };
     4207});
     4208
     4209// Included from: js/tinymce/classes/dom/Range.js
     4210
     4211/**
     4212 * Range.js
     4213 *
     4214 * Copyright, Moxiecode Systems AB
     4215 * Released under LGPL License.
     4216 *
     4217 * License: http://www.tinymce.com/license
     4218 * Contributing: http://www.tinymce.com/contributing
     4219 */
     4220
     4221define("tinymce/dom/Range", [
     4222        "tinymce/util/Tools"
     4223], function(Tools) {
     4224        // Range constructor
     4225        function Range(dom) {
     4226                var t = this,
     4227                        doc = dom.doc,
     4228                        EXTRACT = 0,
     4229                        CLONE = 1,
     4230                        DELETE = 2,
     4231                        TRUE = true,
     4232                        FALSE = false,
     4233                        START_OFFSET = 'startOffset',
     4234                        START_CONTAINER = 'startContainer',
     4235                        END_CONTAINER = 'endContainer',
     4236                        END_OFFSET = 'endOffset',
     4237                        extend = Tools.extend,
     4238                        nodeIndex = dom.nodeIndex;
     4239
     4240                function createDocumentFragment() {
     4241                        return doc.createDocumentFragment();
     4242                }
     4243
     4244                function setStart(n, o) {
     4245                        _setEndPoint(TRUE, n, o);
     4246                }
     4247
     4248                function setEnd(n, o) {
     4249                        _setEndPoint(FALSE, n, o);
     4250                }
     4251
     4252                function setStartBefore(n) {
     4253                        setStart(n.parentNode, nodeIndex(n));
     4254                }
     4255
     4256                function setStartAfter(n) {
     4257                        setStart(n.parentNode, nodeIndex(n) + 1);
     4258                }
     4259
     4260                function setEndBefore(n) {
     4261                        setEnd(n.parentNode, nodeIndex(n));
     4262                }
     4263
     4264                function setEndAfter(n) {
     4265                        setEnd(n.parentNode, nodeIndex(n) + 1);
     4266                }
     4267
     4268                function collapse(ts) {
     4269                        if (ts) {
     4270                                t[END_CONTAINER] = t[START_CONTAINER];
     4271                                t[END_OFFSET] = t[START_OFFSET];
     4272                        } else {
     4273                                t[START_CONTAINER] = t[END_CONTAINER];
     4274                                t[START_OFFSET] = t[END_OFFSET];
     4275                        }
     4276
     4277                        t.collapsed = TRUE;
     4278                }
     4279
     4280                function selectNode(n) {
     4281                        setStartBefore(n);
     4282                        setEndAfter(n);
     4283                }
     4284
     4285                function selectNodeContents(n) {
     4286                        setStart(n, 0);
     4287                        setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
     4288                }
     4289
     4290                function compareBoundaryPoints(h, r) {
     4291                        var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET],
     4292                        rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
     4293
     4294                        // Check START_TO_START
     4295                        if (h === 0) {
     4296                                return _compareBoundaryPoints(sc, so, rsc, rso);
     4297                        }
     4298
     4299                        // Check START_TO_END
     4300                        if (h === 1) {
     4301                                return _compareBoundaryPoints(ec, eo, rsc, rso);
     4302                        }
     4303
     4304                        // Check END_TO_END
     4305                        if (h === 2) {
     4306                                return _compareBoundaryPoints(ec, eo, rec, reo);
     4307                        }
     4308
     4309                        // Check END_TO_START
     4310                        if (h === 3) {
     4311                                return _compareBoundaryPoints(sc, so, rec, reo);
     4312                        }
     4313                }
     4314
     4315                function deleteContents() {
     4316                        _traverse(DELETE);
     4317                }
     4318
     4319                function extractContents() {
     4320                        return _traverse(EXTRACT);
     4321                }
     4322
     4323                function cloneContents() {
     4324                        return _traverse(CLONE);
     4325                }
     4326
     4327                function insertNode(n) {
     4328                        var startContainer = this[START_CONTAINER],
     4329                                startOffset = this[START_OFFSET], nn, o;
     4330
     4331                        // Node is TEXT_NODE or CDATA
     4332                        if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
     4333                                if (!startOffset) {
     4334                                        // At the start of text
     4335                                        startContainer.parentNode.insertBefore(n, startContainer);
     4336                                } else if (startOffset >= startContainer.nodeValue.length) {
     4337                                        // At the end of text
     4338                                        dom.insertAfter(n, startContainer);
     4339                                } else {
     4340                                        // Middle, need to split
     4341                                        nn = startContainer.splitText(startOffset);
     4342                                        startContainer.parentNode.insertBefore(n, nn);
     4343                                }
     4344                        } else {
     4345                                // Insert element node
     4346                                if (startContainer.childNodes.length > 0) {
     4347                                        o = startContainer.childNodes[startOffset];
     4348                                }
     4349
     4350                                if (o) {
     4351                                        startContainer.insertBefore(n, o);
     4352                                } else {
     4353                                        if (startContainer.nodeType == 3) {
     4354                                                dom.insertAfter(n, startContainer);
     4355                                        } else {
     4356                                                startContainer.appendChild(n);
     4357                                        }
     4358                                }
     4359                        }
     4360                }
     4361
     4362                function surroundContents(n) {
     4363                        var f = t.extractContents();
     4364
     4365                        t.insertNode(n);
     4366                        n.appendChild(f);
     4367                        t.selectNode(n);
     4368                }
     4369
     4370                function cloneRange() {
     4371                        return extend(new Range(dom), {
     4372                                startContainer: t[START_CONTAINER],
     4373                                startOffset: t[START_OFFSET],
     4374                                endContainer: t[END_CONTAINER],
     4375                                endOffset: t[END_OFFSET],
     4376                                collapsed: t.collapsed,
     4377                                commonAncestorContainer: t.commonAncestorContainer
     4378                        });
     4379                }
     4380
     4381                // Private methods
     4382
     4383                function _getSelectedNode(container, offset) {
     4384                        var child;
     4385
     4386                        if (container.nodeType == 3 /* TEXT_NODE */) {
     4387                                return container;
     4388                        }
     4389
     4390                        if (offset < 0) {
     4391                                return container;
     4392                        }
     4393
     4394                        child = container.firstChild;
     4395                        while (child && offset > 0) {
     4396                                --offset;
     4397                                child = child.nextSibling;
     4398                        }
     4399
     4400                        if (child) {
     4401                                return child;
     4402                        }
     4403
     4404                        return container;
     4405                }
     4406
     4407                function _isCollapsed() {
     4408                        return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]);
     4409                }
     4410
     4411                function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
     4412                        var c, offsetC, n, cmnRoot, childA, childB;
     4413
     4414                        // In the first case the boundary-points have the same container. A is before B
     4415                        // if its offset is less than the offset of B, A is equal to B if its offset is
     4416                        // equal to the offset of B, and A is after B if its offset is greater than the
     4417                        // offset of B.
     4418                        if (containerA == containerB) {
     4419                                if (offsetA == offsetB) {
     4420                                        return 0; // equal
     4421                                }
     4422
     4423                                if (offsetA < offsetB) {
     4424                                        return -1; // before
     4425                                }
     4426
     4427                                return 1; // after
     4428                        }
     4429
     4430                        // In the second case a child node C of the container of A is an ancestor
     4431                        // container of B. In this case, A is before B if the offset of A is less than or
     4432                        // equal to the index of the child node C and A is after B otherwise.
     4433                        c = containerB;
     4434                        while (c && c.parentNode != containerA) {
     4435                                c = c.parentNode;
     4436                        }
     4437
     4438                        if (c) {
     4439                                offsetC = 0;
     4440                                n = containerA.firstChild;
     4441
     4442                                while (n != c && offsetC < offsetA) {
     4443                                        offsetC++;
     4444                                        n = n.nextSibling;
     4445                                }
     4446
     4447                                if (offsetA <= offsetC) {
     4448                                        return -1; // before
     4449                                }
     4450
     4451                                return 1; // after
     4452                        }
     4453
     4454                        // In the third case a child node C of the container of B is an ancestor container
     4455                        // of A. In this case, A is before B if the index of the child node C is less than
     4456                        // the offset of B and A is after B otherwise.
     4457                        c = containerA;
     4458                        while (c && c.parentNode != containerB) {
     4459                                c = c.parentNode;
     4460                        }
     4461
     4462                        if (c) {
     4463                                offsetC = 0;
     4464                                n = containerB.firstChild;
     4465
     4466                                while (n != c && offsetC < offsetB) {
     4467                                        offsetC++;
     4468                                        n = n.nextSibling;
     4469                                }
     4470
     4471                                if (offsetC < offsetB) {
     4472                                        return -1; // before
     4473                                }
     4474
     4475                                return 1; // after
     4476                        }
     4477
     4478                        // In the fourth case, none of three other cases hold: the containers of A and B
     4479                        // are siblings or descendants of sibling nodes. In this case, A is before B if
     4480                        // the container of A is before the container of B in a pre-order traversal of the
     4481                        // Ranges' context tree and A is after B otherwise.
     4482                        cmnRoot = dom.findCommonAncestor(containerA, containerB);
     4483                        childA = containerA;
     4484
     4485                        while (childA && childA.parentNode != cmnRoot) {
     4486                                childA = childA.parentNode;
     4487                        }
     4488
     4489                        if (!childA) {
     4490                                childA = cmnRoot;
     4491                        }
     4492
     4493                        childB = containerB;
     4494                        while (childB && childB.parentNode != cmnRoot) {
     4495                                childB = childB.parentNode;
     4496                        }
     4497
     4498                        if (!childB) {
     4499                                childB = cmnRoot;
     4500                        }
     4501
     4502                        if (childA == childB) {
     4503                                return 0; // equal
     4504                        }
     4505
     4506                        n = cmnRoot.firstChild;
     4507                        while (n) {
     4508                                if (n == childA) {
     4509                                        return -1; // before
     4510                                }
     4511
     4512                                if (n == childB) {
     4513                                        return 1; // after
     4514                                }
     4515
     4516                                n = n.nextSibling;
     4517                        }
     4518                }
     4519
     4520                function _setEndPoint(st, n, o) {
     4521                        var ec, sc;
     4522
     4523                        if (st) {
     4524                                t[START_CONTAINER] = n;
     4525                                t[START_OFFSET] = o;
     4526                        } else {
     4527                                t[END_CONTAINER] = n;
     4528                                t[END_OFFSET] = o;
     4529                        }
     4530
     4531                        // If one boundary-point of a Range is set to have a root container
     4532                        // other than the current one for the Range, the Range is collapsed to
     4533                        // the new position. This enforces the restriction that both boundary-
     4534                        // points of a Range must have the same root container.
     4535                        ec = t[END_CONTAINER];
     4536                        while (ec.parentNode) {
     4537                                ec = ec.parentNode;
     4538                        }
     4539
     4540                        sc = t[START_CONTAINER];
     4541                        while (sc.parentNode) {
     4542                                sc = sc.parentNode;
     4543                        }
     4544
     4545                        if (sc == ec) {
     4546                                // The start position of a Range is guaranteed to never be after the
     4547                                // end position. To enforce this restriction, if the start is set to
     4548                                // be at a position after the end, the Range is collapsed to that
     4549                                // position.
     4550                                if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0) {
     4551                                        t.collapse(st);
     4552                                }
     4553                        } else {
     4554                                t.collapse(st);
     4555                        }
     4556
     4557                        t.collapsed = _isCollapsed();
     4558                        t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]);
     4559                }
     4560
     4561                function _traverse(how) {
     4562                        var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
     4563
     4564                        if (t[START_CONTAINER] == t[END_CONTAINER]) {
     4565                                return _traverseSameContainer(how);
     4566                        }
     4567
     4568                        for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
     4569                                if (p == t[START_CONTAINER]) {
     4570                                        return _traverseCommonStartContainer(c, how);
     4571                                }
     4572
     4573                                ++endContainerDepth;
     4574                        }
     4575
     4576                        for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
     4577                                if (p == t[END_CONTAINER]) {
     4578                                        return _traverseCommonEndContainer(c, how);
     4579                                }
     4580
     4581                                ++startContainerDepth;
     4582                        }
     4583
     4584                        depthDiff = startContainerDepth - endContainerDepth;
     4585
     4586                        startNode = t[START_CONTAINER];
     4587                        while (depthDiff > 0) {
     4588                                startNode = startNode.parentNode;
     4589                                depthDiff--;
     4590                        }
     4591
     4592                        endNode = t[END_CONTAINER];
     4593                        while (depthDiff < 0) {
     4594                                endNode = endNode.parentNode;
     4595                                depthDiff++;
     4596                        }
     4597
     4598                        // ascend the ancestor hierarchy until we have a common parent.
     4599                        for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
     4600                                startNode = sp;
     4601                                endNode = ep;
     4602                        }
     4603
     4604                        return _traverseCommonAncestors(startNode, endNode, how);
     4605                }
     4606
     4607                 function _traverseSameContainer(how) {
     4608                        var frag, s, sub, n, cnt, sibling, xferNode, start, len;
     4609
     4610                        if (how != DELETE) {
     4611                                frag = createDocumentFragment();
     4612                        }
     4613
     4614                        // If selection is empty, just return the fragment
     4615                        if (t[START_OFFSET] == t[END_OFFSET]) {
     4616                                return frag;
     4617                        }
     4618
     4619                        // Text node needs special case handling
     4620                        if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) {
     4621                                // get the substring
     4622                                s = t[START_CONTAINER].nodeValue;
     4623                                sub = s.substring(t[START_OFFSET], t[END_OFFSET]);
     4624
     4625                                // set the original text node to its new value
     4626                                if (how != CLONE) {
     4627                                        n = t[START_CONTAINER];
     4628                                        start = t[START_OFFSET];
     4629                                        len = t[END_OFFSET] - t[START_OFFSET];
     4630
     4631                                        if (start === 0 && len >= n.nodeValue.length - 1) {
     4632                                                n.parentNode.removeChild(n);
     4633                                        } else {
     4634                                                n.deleteData(start, len);
     4635                                        }
     4636
     4637                                        // Nothing is partially selected, so collapse to start point
     4638                                        t.collapse(TRUE);
     4639                                }
     4640
     4641                                if (how == DELETE) {
     4642                                        return;
     4643                                }
     4644
     4645                                if (sub.length > 0) {
     4646                                        frag.appendChild(doc.createTextNode(sub));
     4647                                }
     4648
     4649                                return frag;
     4650                        }
     4651
     4652                        // Copy nodes between the start/end offsets.
     4653                        n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);
     4654                        cnt = t[END_OFFSET] - t[START_OFFSET];
     4655
     4656                        while (n && cnt > 0) {
     4657                                sibling = n.nextSibling;
     4658                                xferNode = _traverseFullySelected(n, how);
     4659
     4660                                if (frag) {
     4661                                        frag.appendChild(xferNode);
     4662                                }
     4663
     4664                                --cnt;
     4665                                n = sibling;
     4666                        }
     4667
     4668                        // Nothing is partially selected, so collapse to start point
     4669                        if (how != CLONE) {
     4670                                t.collapse(TRUE);
     4671                        }
     4672
     4673                        return frag;
     4674                }
     4675
     4676                function _traverseCommonStartContainer(endAncestor, how) {
     4677                        var frag, n, endIdx, cnt, sibling, xferNode;
     4678
     4679                        if (how != DELETE) {
     4680                                frag = createDocumentFragment();
     4681                        }
     4682
     4683                        n = _traverseRightBoundary(endAncestor, how);
     4684
     4685                        if (frag) {
     4686                                frag.appendChild(n);
     4687                        }
     4688
     4689                        endIdx = nodeIndex(endAncestor);
     4690                        cnt = endIdx - t[START_OFFSET];
     4691
     4692                        if (cnt <= 0) {
     4693                                // Collapse to just before the endAncestor, which
     4694                                // is partially selected.
     4695                                if (how != CLONE) {
     4696                                        t.setEndBefore(endAncestor);
     4697                                        t.collapse(FALSE);
     4698                                }
     4699
     4700                                return frag;
     4701                        }
     4702
     4703                        n = endAncestor.previousSibling;
     4704                        while (cnt > 0) {
     4705                                sibling = n.previousSibling;
     4706                                xferNode = _traverseFullySelected(n, how);
     4707
     4708                                if (frag) {
     4709                                        frag.insertBefore(xferNode, frag.firstChild);
     4710                                }
     4711
     4712                                --cnt;
     4713                                n = sibling;
     4714                        }
     4715
     4716                        // Collapse to just before the endAncestor, which
     4717                        // is partially selected.
     4718                        if (how != CLONE) {
     4719                                t.setEndBefore(endAncestor);
     4720                                t.collapse(FALSE);
     4721                        }
     4722
     4723                        return frag;
     4724                }
     4725
     4726                function _traverseCommonEndContainer(startAncestor, how) {
     4727                        var frag, startIdx, n, cnt, sibling, xferNode;
     4728
     4729                        if (how != DELETE) {
     4730                                frag = createDocumentFragment();
     4731                        }
     4732
     4733                        n = _traverseLeftBoundary(startAncestor, how);
     4734                        if (frag) {
     4735                                frag.appendChild(n);
     4736                        }
     4737
     4738                        startIdx = nodeIndex(startAncestor);
     4739                        ++startIdx; // Because we already traversed it
     4740
     4741                        cnt = t[END_OFFSET] - startIdx;
     4742                        n = startAncestor.nextSibling;
     4743                        while (n && cnt > 0) {
     4744                                sibling = n.nextSibling;
     4745                                xferNode = _traverseFullySelected(n, how);
     4746
     4747                                if (frag) {
     4748                                        frag.appendChild(xferNode);
     4749                                }
     4750
     4751                                --cnt;
     4752                                n = sibling;
     4753                        }
     4754
     4755                        if (how != CLONE) {
     4756                                t.setStartAfter(startAncestor);
     4757                                t.collapse(TRUE);
     4758                        }
     4759
     4760                        return frag;
     4761                }
     4762
     4763                function _traverseCommonAncestors(startAncestor, endAncestor, how) {
     4764                        var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
     4765
     4766                        if (how != DELETE) {
     4767                                frag = createDocumentFragment();
     4768                        }
     4769
     4770                        n = _traverseLeftBoundary(startAncestor, how);
     4771                        if (frag) {
     4772                                frag.appendChild(n);
     4773                        }
     4774
     4775                        commonParent = startAncestor.parentNode;
     4776                        startOffset = nodeIndex(startAncestor);
     4777                        endOffset = nodeIndex(endAncestor);
     4778                        ++startOffset;
     4779
     4780                        cnt = endOffset - startOffset;
     4781                        sibling = startAncestor.nextSibling;
     4782
     4783                        while (cnt > 0) {
     4784                                nextSibling = sibling.nextSibling;
     4785                                n = _traverseFullySelected(sibling, how);
     4786
     4787                                if (frag) {
     4788                                        frag.appendChild(n);
     4789                                }
     4790
     4791                                sibling = nextSibling;
     4792                                --cnt;
     4793                        }
     4794
     4795                        n = _traverseRightBoundary(endAncestor, how);
     4796
     4797                        if (frag) {
     4798                                frag.appendChild(n);
     4799                        }
     4800
     4801                        if (how != CLONE) {
     4802                                t.setStartAfter(startAncestor);
     4803                                t.collapse(TRUE);
     4804                        }
     4805
     4806                        return frag;
     4807                }
     4808
     4809                function _traverseRightBoundary(root, how) {
     4810                        var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent;
     4811                        var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER];
     4812
     4813                        if (next == root) {
     4814                                return _traverseNode(next, isFullySelected, FALSE, how);
     4815                        }
     4816
     4817                        parent = next.parentNode;
     4818                        clonedParent = _traverseNode(parent, FALSE, FALSE, how);
     4819
     4820                        while (parent) {
     4821                                while (next) {
     4822                                        prevSibling = next.previousSibling;
     4823                                        clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
     4824
     4825                                        if (how != DELETE) {
     4826                                                clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
     4827                                        }
     4828
     4829                                        isFullySelected = TRUE;
     4830                                        next = prevSibling;
     4831                                }
     4832
     4833                                if (parent == root) {
     4834                                        return clonedParent;
     4835                                }
     4836
     4837                                next = parent.previousSibling;
     4838                                parent = parent.parentNode;
     4839
     4840                                clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
     4841
     4842                                if (how != DELETE) {
     4843                                        clonedGrandParent.appendChild(clonedParent);
     4844                                }
     4845
     4846                                clonedParent = clonedGrandParent;
     4847                        }
     4848                }
     4849
     4850                function _traverseLeftBoundary(root, how) {
     4851                        var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER];
     4852                        var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
     4853
     4854                        if (next == root) {
     4855                                return _traverseNode(next, isFullySelected, TRUE, how);
     4856                        }
     4857
     4858                        parent = next.parentNode;
     4859                        clonedParent = _traverseNode(parent, FALSE, TRUE, how);
     4860
     4861                        while (parent) {
     4862                                while (next) {
     4863                                        nextSibling = next.nextSibling;
     4864                                        clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
     4865
     4866                                        if (how != DELETE) {
     4867                                                clonedParent.appendChild(clonedChild);
     4868                                        }
     4869
     4870                                        isFullySelected = TRUE;
     4871                                        next = nextSibling;
     4872                                }
     4873
     4874                                if (parent == root) {
     4875                                        return clonedParent;
     4876                                }
     4877
     4878                                next = parent.nextSibling;
     4879                                parent = parent.parentNode;
     4880
     4881                                clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
     4882
     4883                                if (how != DELETE) {
     4884                                        clonedGrandParent.appendChild(clonedParent);
     4885                                }
     4886
     4887                                clonedParent = clonedGrandParent;
     4888                        }
     4889                }
     4890
     4891                function _traverseNode(n, isFullySelected, isLeft, how) {
     4892                        var txtValue, newNodeValue, oldNodeValue, offset, newNode;
     4893
     4894                        if (isFullySelected) {
     4895                                return _traverseFullySelected(n, how);
     4896                        }
     4897
     4898                        if (n.nodeType == 3 /* TEXT_NODE */) {
     4899                                txtValue = n.nodeValue;
     4900
     4901                                if (isLeft) {
     4902                                        offset = t[START_OFFSET];
     4903                                        newNodeValue = txtValue.substring(offset);
     4904                                        oldNodeValue = txtValue.substring(0, offset);
     4905                                } else {
     4906                                        offset = t[END_OFFSET];
     4907                                        newNodeValue = txtValue.substring(0, offset);
     4908                                        oldNodeValue = txtValue.substring(offset);
     4909                                }
     4910
     4911                                if (how != CLONE) {
     4912                                        n.nodeValue = oldNodeValue;
     4913                                }
     4914
     4915                                if (how == DELETE) {
     4916                                        return;
     4917                                }
     4918
     4919                                newNode = dom.clone(n, FALSE);
     4920                                newNode.nodeValue = newNodeValue;
     4921
     4922                                return newNode;
     4923                        }
     4924
     4925                        if (how == DELETE) {
     4926                                return;
     4927                        }
     4928
     4929                        return dom.clone(n, FALSE);
     4930                }
     4931
     4932                function _traverseFullySelected(n, how) {
     4933                        if (how != DELETE) {
     4934                                return how == CLONE ? dom.clone(n, TRUE) : n;
     4935                        }
     4936
     4937                        n.parentNode.removeChild(n);
     4938                }
     4939
     4940                function toStringIE() {
     4941                        return dom.create('body', null, cloneContents()).outerText;
     4942                }
     4943
     4944                extend(t, {
     4945                        // Inital states
     4946                        startContainer: doc,
     4947                        startOffset: 0,
     4948                        endContainer: doc,
     4949                        endOffset: 0,
     4950                        collapsed: TRUE,
     4951                        commonAncestorContainer: doc,
     4952
     4953                        // Range constants
     4954                        START_TO_START: 0,
     4955                        START_TO_END: 1,
     4956                        END_TO_END: 2,
     4957                        END_TO_START: 3,
     4958
     4959                        // Public methods
     4960                        setStart: setStart,
     4961                        setEnd: setEnd,
     4962                        setStartBefore: setStartBefore,
     4963                        setStartAfter: setStartAfter,
     4964                        setEndBefore: setEndBefore,
     4965                        setEndAfter: setEndAfter,
     4966                        collapse: collapse,
     4967                        selectNode: selectNode,
     4968                        selectNodeContents: selectNodeContents,
     4969                        compareBoundaryPoints: compareBoundaryPoints,
     4970                        deleteContents: deleteContents,
     4971                        extractContents: extractContents,
     4972                        cloneContents: cloneContents,
     4973                        insertNode: insertNode,
     4974                        surroundContents: surroundContents,
     4975                        cloneRange: cloneRange,
     4976                        toStringIE: toStringIE
     4977                });
     4978
     4979                return t;
     4980        }
     4981
     4982        // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
     4983        Range.prototype.toString = function() {
     4984                return this.toStringIE();
     4985        };
     4986
     4987        return Range;
     4988});
     4989
     4990// Included from: js/tinymce/classes/html/Entities.js
     4991
     4992/**
     4993 * Entities.js
     4994 *
     4995 * Copyright, Moxiecode Systems AB
     4996 * Released under LGPL License.
     4997 *
     4998 * License: http://www.tinymce.com/license
     4999 * Contributing: http://www.tinymce.com/contributing
     5000 */
     5001
     5002/*jshint bitwise:false */
     5003
     5004/**
     5005 * Entity encoder class.
     5006 *
     5007 * @class tinymce.html.Entities
     5008 * @static
     5009 * @version 3.4
     5010 */
     5011define("tinymce/html/Entities", [
     5012        "tinymce/util/Tools"
     5013], function(Tools) {
     5014        var makeMap = Tools.makeMap;
     5015
     5016        var namedEntities, baseEntities, reverseEntities,
     5017                attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
     5018                textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
     5019                rawCharsRegExp = /[<>&\"\']/g,
     5020                entityRegExp = /&(#x|#)?([\w]+);/g,
     5021                asciiMap = {
     5022                        128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
     5023                        135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
     5024                        142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
     5025                        150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
     5026                        156: "\u0153", 158: "\u017E", 159: "\u0178"
     5027                };
     5028
     5029        // Raw entities
     5030        baseEntities = {
     5031                '\"': '&quot;', // Needs to be escaped since the YUI compressor would otherwise break the code
     5032                "'": '&#39;',
     5033                '<': '&lt;',
     5034                '>': '&gt;',
     5035                '&': '&amp;'
     5036        };
     5037
     5038        // Reverse lookup table for raw entities
     5039        reverseEntities = {
     5040                '&lt;': '<',
     5041                '&gt;': '>',
     5042                '&amp;': '&',
     5043                '&quot;': '"',
     5044                '&apos;': "'"
     5045        };
     5046
     5047        // Decodes text by using the browser
     5048        function nativeDecode(text) {
     5049                var elm;
     5050
     5051                elm = document.createElement("div");
     5052                elm.innerHTML = text;
     5053
     5054                return elm.textContent || elm.innerText || text;
     5055        }
     5056
     5057        // Build a two way lookup table for the entities
     5058        function buildEntitiesLookup(items, radix) {
     5059                var i, chr, entity, lookup = {};
     5060
     5061                if (items) {
     5062                        items = items.split(',');
     5063                        radix = radix || 10;
     5064
     5065                        // Build entities lookup table
     5066                        for (i = 0; i < items.length; i += 2) {
     5067                                chr = String.fromCharCode(parseInt(items[i], radix));
     5068
     5069                                // Only add non base entities
     5070                                if (!baseEntities[chr]) {
     5071                                        entity = '&' + items[i + 1] + ';';
     5072                                        lookup[chr] = entity;
     5073                                        lookup[entity] = chr;
     5074                                }
     5075                        }
     5076
     5077                        return lookup;
     5078                }
     5079        }
     5080
     5081        // Unpack entities lookup where the numbers are in radix 32 to reduce the size
     5082        namedEntities = buildEntitiesLookup(
     5083                '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
     5084                '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
     5085                '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
     5086                '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
     5087                '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
     5088                '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
     5089                '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
     5090                '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
     5091                '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
     5092                '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
     5093                'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
     5094                'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
     5095                't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
     5096                'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
     5097                'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
     5098                '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
     5099                '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
     5100                '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
     5101                '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
     5102                '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
     5103                'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
     5104                'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
     5105                'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
     5106                '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
     5107                '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
     5108
     5109        var Entities = {
     5110                /**
     5111                 * Encodes the specified string using raw entities. This means only the required XML base entities will be endoded.
     5112                 *
     5113                 * @method encodeRaw
     5114                 * @param {String} text Text to encode.
     5115                 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
     5116                 * @return {String} Entity encoded text.
     5117                 */
     5118                encodeRaw: function(text, attr) {
     5119                        return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
     5120                                return baseEntities[chr] || chr;
     5121                        });
     5122                },
     5123
     5124                /**
     5125                 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
     5126                 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
     5127                 * and is exposed as the DOMUtils.encode function.
     5128                 *
     5129                 * @method encodeAllRaw
     5130                 * @param {String} text Text to encode.
     5131                 * @return {String} Entity encoded text.
     5132                 */
     5133                encodeAllRaw: function(text) {
     5134                        return ('' + text).replace(rawCharsRegExp, function(chr) {
     5135                                return baseEntities[chr] || chr;
     5136                        });
     5137                },
     5138
     5139                /**
     5140                 * Encodes the specified string using numeric entities. The core entities will be
     5141                 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
     5142                 *
     5143                 * @method encodeNumeric
     5144                 * @param {String} text Text to encode.
     5145                 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
     5146                 * @return {String} Entity encoded text.
     5147                 */
     5148                encodeNumeric: function(text, attr) {
     5149                        return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
     5150                                // Multi byte sequence convert it to a single entity
     5151                                if (chr.length > 1) {
     5152                                        return '&#' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
     5153                                }
     5154
     5155                                return baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
     5156                        });
     5157                },
     5158
     5159                /**
     5160                 * Encodes the specified string using named entities. The core entities will be encoded
     5161                 * as named ones but all non lower ascii characters will be encoded into named entities.
     5162                 *
     5163                 * @method encodeNamed
     5164                 * @param {String} text Text to encode.
     5165                 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
     5166                 * @param {Object} entities Optional parameter with entities to use.
     5167                 * @return {String} Entity encoded text.
     5168                 */
     5169                encodeNamed: function(text, attr, entities) {
     5170                        entities = entities || namedEntities;
     5171
     5172                        return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
     5173                                return baseEntities[chr] || entities[chr] || chr;
     5174                        });
     5175                },
     5176
     5177                /**
     5178                 * Returns an encode function based on the name(s) and it's optional entities.
     5179                 *
     5180                 * @method getEncodeFunc
     5181                 * @param {String} name Comma separated list of encoders for example named,numeric.
     5182                 * @param {String} entities Optional parameter with entities to use instead of the built in set.
     5183                 * @return {function} Encode function to be used.
     5184                 */
     5185                getEncodeFunc: function(name, entities) {
     5186                        entities = buildEntitiesLookup(entities) || namedEntities;
     5187
     5188                        function encodeNamedAndNumeric(text, attr) {
     5189                                return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
     5190                                        return baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
     5191                                });
     5192                        }
     5193
     5194                        function encodeCustomNamed(text, attr) {
     5195                                return Entities.encodeNamed(text, attr, entities);
     5196                        }
     5197
     5198                        // Replace + with , to be compatible with previous TinyMCE versions
     5199                        name = makeMap(name.replace(/\+/g, ','));
     5200
     5201                        // Named and numeric encoder
     5202                        if (name.named && name.numeric) {
     5203                                return encodeNamedAndNumeric;
     5204                        }
     5205
     5206                        // Named encoder
     5207                        if (name.named) {
     5208                                // Custom names
     5209                                if (entities) {
     5210                                        return encodeCustomNamed;
     5211                                }
     5212
     5213                                return Entities.encodeNamed;
     5214                        }
     5215
     5216                        // Numeric
     5217                        if (name.numeric) {
     5218                                return Entities.encodeNumeric;
     5219                        }
     5220
     5221                        // Raw encoder
     5222                        return Entities.encodeRaw;
     5223                },
     5224
     5225                /**
     5226                 * Decodes the specified string, this will replace entities with raw UTF characters.
     5227                 *
     5228                 * @method decode
     5229                 * @param {String} text Text to entity decode.
     5230                 * @return {String} Entity decoded string.
     5231                 */
     5232                decode: function(text) {
     5233                        return text.replace(entityRegExp, function(all, numeric, value) {
     5234                                if (numeric) {
     5235                                        value = parseInt(value, numeric.length === 2 ? 16 : 10);
     5236
     5237                                        // Support upper UTF
     5238                                        if (value > 0xFFFF) {
     5239                                                value -= 0x10000;
     5240
     5241                                                return String.fromCharCode(0xD800 + (value >> 10), 0xDC00 + (value & 0x3FF));
     5242                                        } else {
     5243                                                return asciiMap[value] || String.fromCharCode(value);
     5244                                        }
     5245                                }
     5246
     5247                                return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
     5248                        });
     5249                }
     5250        };
     5251
     5252        return Entities;
     5253});
     5254
     5255// Included from: js/tinymce/classes/Env.js
     5256
     5257/**
     5258 * Env.js
     5259 *
     5260 * Copyright, Moxiecode Systems AB
     5261 * Released under LGPL License.
     5262 *
     5263 * License: http://www.tinymce.com/license
     5264 * Contributing: http://www.tinymce.com/contributing
     5265 */
     5266
     5267/**
     5268 * This class contains various environment constants like browser versions etc.
     5269 * Normally you don't want to sniff specific browser versions but sometimes you have
     5270 * to when it's impossible to feature detect. So use this with care.
     5271 *
     5272 * @class tinymce.Env
     5273 * @static
     5274 */
     5275define("tinymce/Env", [], function() {
     5276        var nav = navigator, userAgent = nav.userAgent;
     5277        var opera, webkit, ie, ie11, gecko, mac, iDevice;
     5278
     5279        opera = window.opera && window.opera.buildNumber;
     5280        webkit = /WebKit/.test(userAgent);
     5281        ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
     5282        ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
     5283        ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
     5284        ie = ie || ie11;
     5285        gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
     5286        mac = userAgent.indexOf('Mac') != -1;
     5287        iDevice = /(iPad|iPhone)/.test(userAgent);
     5288
     5289        // Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
     5290        // says it has contentEditable support but there is no visible caret.
     5291        var contentEditable = !iDevice || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
     5292
     5293        return {
     5294                /**
     5295                 * Constant that is true if the browser is Opera.
     5296                 *
     5297                 * @property opera
     5298                 * @type Boolean
     5299                 * @final
     5300                 */
     5301                opera: opera,
     5302
     5303                /**
     5304                 * Constant that is true if the browser is WebKit (Safari/Chrome).
     5305                 *
     5306                 * @property webKit
     5307                 * @type Boolean
     5308                 * @final
     5309                 */
     5310                webkit: webkit,
     5311
     5312                /**
     5313                 * Constant that is more than zero if the browser is IE.
     5314                 *
     5315                 * @property ie
     5316                 * @type Boolean
     5317                 * @final
     5318                 */
     5319                ie: ie,
     5320
     5321                /**
     5322                 * Constant that is true if the browser is Gecko.
     5323                 *
     5324                 * @property gecko
     5325                 * @type Boolean
     5326                 * @final
     5327                 */
     5328                gecko: gecko,
     5329
     5330                /**
     5331                 * Constant that is true if the os is Mac OS.
     5332                 *
     5333                 * @property mac
     5334                 * @type Boolean
     5335                 * @final
     5336                 */
     5337                mac: mac,
     5338
     5339                /**
     5340                 * Constant that is true if the os is iOS.
     5341                 *
     5342                 * @property iOS
     5343                 * @type Boolean
     5344                 * @final
     5345                 */
     5346                iOS: iDevice,
     5347
     5348                /**
     5349                 * Constant that is true if the browser supports editing.
     5350                 *
     5351                 * @property contentEditable
     5352                 * @type Boolean
     5353                 * @final
     5354                 */
     5355                contentEditable: contentEditable,
     5356
     5357                /**
     5358                 * Transparent image data url.
     5359                 *
     5360                 * @property transparentSrc
     5361                 * @type Boolean
     5362                 * @final
     5363                 */
     5364                transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
     5365
     5366                /**
     5367                 * Returns true/false if the browser can or can't place the caret after a inline block like an image.
     5368                 *
     5369                 * @property noCaretAfter
     5370                 * @type Boolean
     5371                 * @final
     5372                 */
     5373                caretAfter: ie != 8,
     5374
     5375                /**
     5376                 * Constant that is true if the browser supports native DOM Ranges. IE 9+.
     5377                 *
     5378                 * @property range
     5379                 * @type Boolean
     5380                 */
     5381                range: window.getSelection && "Range" in window,
     5382
     5383                /**
     5384                 * Returns the IE document mode for non IE browsers this will fake IE 10.
     5385                 *
     5386                 * @property documentMode
     5387                 * @type Number
     5388                 */
     5389                documentMode: ie ? (document.documentMode || 7) : 10
     5390        };
     5391});
     5392
     5393// Included from: js/tinymce/classes/dom/DOMUtils.js
     5394
     5395/**
     5396 * DOMUtils.js
     5397 *
     5398 * Copyright, Moxiecode Systems AB
     5399 * Released under LGPL License.
     5400 *
     5401 * License: http://www.tinymce.com/license
     5402 * Contributing: http://www.tinymce.com/contributing
     5403 */
     5404
     5405/**
     5406 * Utility class for various DOM manipulation and retrieval functions.
     5407 *
     5408 * @class tinymce.dom.DOMUtils
     5409 * @example
     5410 * // Add a class to an element by id in the page
     5411 * tinymce.DOM.addClass('someid', 'someclass');
     5412 *
     5413 * // Add a class to an element by id inside the editor
     5414 * tinymce.activeEditor.dom.addClass('someid', 'someclass');
     5415 */
     5416define("tinymce/dom/DOMUtils", [
     5417        "tinymce/dom/Sizzle",
     5418        "tinymce/html/Styles",
     5419        "tinymce/dom/EventUtils",
     5420        "tinymce/dom/TreeWalker",
     5421        "tinymce/dom/Range",
     5422        "tinymce/html/Entities",
     5423        "tinymce/Env",
     5424        "tinymce/util/Tools"
     5425], function(Sizzle, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools) {
     5426        // Shorten names
     5427        var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim, extend = Tools.extend;
     5428        var isWebKit = Env.webkit, isIE = Env.ie;
     5429        var simpleSelectorRe = /^([a-z0-9],?)+$/i;
     5430        var whiteSpaceRegExp = /^[ \t\r\n]*$/;
     5431        var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
     5432
     5433        /**
     5434         * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
     5435         *
     5436         * @constructor
     5437         * @method DOMUtils
     5438         * @param {Document} d Document reference to bind the utility class to.
     5439         * @param {settings} s Optional settings collection.
     5440         */
     5441        function DOMUtils(doc, settings) {
     5442                var self = this, blockElementsMap;
     5443
     5444                self.doc = doc;
     5445                self.win = window;
     5446                self.files = {};
     5447                self.counter = 0;
     5448                self.stdMode = !isIE || doc.documentMode >= 8;
     5449                self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
     5450                self.hasOuterHTML = "outerHTML" in doc.createElement("a");
     5451                this.boundEvents = [];
     5452
     5453                self.settings = settings = extend({
     5454                        keep_values: false,
     5455                        hex_colors: 1
     5456                }, settings);
     5457
     5458                self.schema = settings.schema;
     5459                self.styles = new Styles({
     5460                        url_converter: settings.url_converter,
     5461                        url_converter_scope: settings.url_converter_scope
     5462                }, settings.schema);
     5463
     5464                self.fixDoc(doc);
     5465                self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
     5466                blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
     5467
     5468                /**
     5469                 * Returns true/false if the specified element is a block element or not.
     5470                 *
     5471                 * @method isBlock
     5472                 * @param {Node/String} node Element/Node to check.
     5473                 * @return {Boolean} True/False state if the node is a block element or not.
     5474                 */
     5475                self.isBlock = function(node) {
     5476                        // Fix for #5446
     5477                        if (!node) {
     5478                                return false;
     5479                        }
     5480
     5481                        // This function is called in module pattern style since it might be executed with the wrong this scope
     5482                        var type = node.nodeType;
     5483
     5484                        // If it's a node then check the type and use the nodeName
     5485                        if (type) {
     5486                                return !!(type === 1 && blockElementsMap[node.nodeName]);
     5487                        }
     5488
     5489                        return !!blockElementsMap[node];
     5490                };
     5491        }
     5492
     5493        DOMUtils.prototype = {
     5494                root: null,
     5495                props: {
     5496                        "for": "htmlFor",
     5497                        "class": "className",
     5498                        className: "className",
     5499                        checked: "checked",
     5500                        disabled: "disabled",
     5501                        maxlength: "maxLength",
     5502                        readonly: "readOnly",
     5503                        selected: "selected",
     5504                        value: "value",
     5505                        id: "id",
     5506                        name: "name",
     5507                        type: "type"
     5508                },
     5509
     5510                fixDoc: function(doc) {
     5511                        var settings = this.settings, name;
     5512
     5513                        if (isIE && settings.schema) {
     5514                                // Add missing HTML 4/5 elements to IE
     5515                                ('abbr article aside audio canvas ' +
     5516                                'details figcaption figure footer ' +
     5517                                'header hgroup mark menu meter nav ' +
     5518                                'output progress section summary ' +
     5519                                'time video').replace(/\w+/g, function(name) {
     5520                                        doc.createElement(name);
     5521                                });
     5522
     5523                                // Create all custom elements
     5524                                for (name in settings.schema.getCustomElements()) {
     5525                                        doc.createElement(name);
     5526                                }
     5527                        }
     5528                },
     5529
     5530                clone: function(node, deep) {
     5531                        var self = this, clone, doc;
     5532
     5533                        // TODO: Add feature detection here in the future
     5534                        if (!isIE || node.nodeType !== 1 || deep) {
     5535                                return node.cloneNode(deep);
     5536                        }
     5537
     5538                        doc = self.doc;
     5539
     5540                        // Make a HTML5 safe shallow copy
     5541                        if (!deep) {
     5542                                clone = doc.createElement(node.nodeName);
     5543
     5544                                // Copy attribs
     5545                                each(self.getAttribs(node), function(attr) {
     5546                                        self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
     5547                                });
     5548
     5549                                return clone;
     5550                        }
     5551/*
     5552                        // Setup HTML5 patched document fragment
     5553                        if (!self.frag) {
     5554                                self.frag = doc.createDocumentFragment();
     5555                                self.fixDoc(self.frag);
     5556                        }
     5557
     5558                        // Make a deep copy by adding it to the document fragment then removing it this removed the :section
     5559                        clone = doc.createElement('div');
     5560                        self.frag.appendChild(clone);
     5561                        clone.innerHTML = node.outerHTML;
     5562                        self.frag.removeChild(clone);
     5563*/
     5564                        return clone.firstChild;
     5565                },
     5566
     5567                /**
     5568                 * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
     5569                 * go above the point of this root node.
     5570                 *
     5571                 * @method getRoot
     5572                 * @return {Element} Root element for the utility class.
     5573                 */
     5574                getRoot: function() {
     5575                        var self = this;
     5576
     5577                        return self.get(self.settings.root_element) || self.doc.body;
     5578                },
     5579
     5580                /**
     5581                 * Returns the viewport of the window.
     5582                 *
     5583                 * @method getViewPort
     5584                 * @param {Window} win Optional window to get viewport of.
     5585                 * @return {Object} Viewport object with fields x, y, w and h.
     5586                 */
     5587                getViewPort: function(win) {
     5588                        var doc, rootElm;
     5589
     5590                        win = !win ? this.win : win;
     5591                        doc = win.document;
     5592                        rootElm = this.boxModel ? doc.documentElement : doc.body;
     5593
     5594                        // Returns viewport size excluding scrollbars
     5595                        return {
     5596                                x: win.pageXOffset || rootElm.scrollLeft,
     5597                                y: win.pageYOffset || rootElm.scrollTop,
     5598                                w: win.innerWidth || rootElm.clientWidth,
     5599                                h: win.innerHeight || rootElm.clientHeight
     5600                        };
     5601                },
     5602
     5603                /**
     5604                 * Returns the rectangle for a specific element.
     5605                 *
     5606                 * @method getRect
     5607                 * @param {Element/String} elm Element object or element ID to get rectangle from.
     5608                 * @return {object} Rectangle for specified element object with x, y, w, h fields.
     5609                 */
     5610                getRect: function(elm) {
     5611                        var self = this, pos, size;
     5612
     5613                        elm = self.get(elm);
     5614                        pos = self.getPos(elm);
     5615                        size = self.getSize(elm);
     5616
     5617                        return {
     5618                                x: pos.x, y: pos.y,
     5619                                w: size.w, h: size.h
     5620                        };
     5621                },
     5622
     5623                /**
     5624                 * Returns the size dimensions of the specified element.
     5625                 *
     5626                 * @method getSize
     5627                 * @param {Element/String} elm Element object or element ID to get rectangle from.
     5628                 * @return {object} Rectangle for specified element object with w, h fields.
     5629                 */
     5630                getSize: function(elm) {
     5631                        var self = this, w, h;
     5632
     5633                        elm = self.get(elm);
     5634                        w = self.getStyle(elm, 'width');
     5635                        h = self.getStyle(elm, 'height');
     5636
     5637                        // Non pixel value, then force offset/clientWidth
     5638                        if (w.indexOf('px') === -1) {
     5639                                w = 0;
     5640                        }
     5641
     5642                        // Non pixel value, then force offset/clientWidth
     5643                        if (h.indexOf('px') === -1) {
     5644                                h = 0;
     5645                        }
     5646
     5647                        return {
     5648                                w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
     5649                                h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
     5650                        };
     5651                },
     5652
     5653                /**
     5654                 * Returns a node by the specified selector function. This function will
     5655                 * loop through all parent nodes and call the specified function for each node.
     5656                 * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
     5657                 * and the node it found will be returned.
     5658                 *
     5659                 * @method getParent
     5660                 * @param {Node/String} node DOM node to search parents on or ID string.
     5661                 * @param {function} selector Selection function or CSS selector to execute on each node.
     5662                 * @param {Node} root Optional root element, never go below this point.
     5663                 * @return {Node} DOM Node or null if it wasn't found.
     5664                 */
     5665                getParent: function(node, selector, root) {
     5666                        return this.getParents(node, selector, root, false);
     5667                },
     5668
     5669                /**
     5670                 * Returns a node list of all parents matching the specified selector function or pattern.
     5671                 * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
     5672                 *
     5673                 * @method getParents
     5674                 * @param {Node/String} node DOM node to search parents on or ID string.
     5675                 * @param {function} selector Selection function to execute on each node or CSS pattern.
     5676                 * @param {Node} root Optional root element, never go below this point.
     5677                 * @return {Array} Array of nodes or null if it wasn't found.
     5678                 */
     5679                getParents: function(node, selector, root, collect) {
     5680                        var self = this, selectorVal, result = [];
     5681
     5682                        node = self.get(node);
     5683                        collect = collect === undefined;
     5684
     5685                        // Default root on inline mode
     5686                        root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
     5687
     5688                        // Wrap node name as func
     5689                        if (is(selector, 'string')) {
     5690                                selectorVal = selector;
     5691
     5692                                if (selector === '*') {
     5693                                        selector = function(node) {return node.nodeType == 1;};
     5694                                } else {
     5695                                        selector = function(node) {
     5696                                                return self.is(node, selectorVal);
     5697                                        };
     5698                                }
     5699                        }
     5700
     5701                        while (node) {
     5702                                if (node == root || !node.nodeType || node.nodeType === 9) {
     5703                                        break;
     5704                                }
     5705
     5706                                if (!selector || selector(node)) {
     5707                                        if (collect) {
     5708                                                result.push(node);
     5709                                        } else {
     5710                                                return node;
     5711                                        }
     5712                                }
     5713
     5714                                node = node.parentNode;
     5715                        }
     5716
     5717                        return collect ? result : null;
     5718                },
     5719
     5720                /**
     5721                 * Returns the specified element by ID or the input element if it isn't a string.
     5722                 *
     5723                 * @method get
     5724                 * @param {String/Element} n Element id to look for or element to just pass though.
     5725                 * @return {Element} Element matching the specified id or null if it wasn't found.
     5726                 */
     5727                get: function(elm) {
     5728                        var name;
     5729
     5730                        if (elm && this.doc && typeof(elm) == 'string') {
     5731                                name = elm;
     5732                                elm = this.doc.getElementById(elm);
     5733
     5734                                // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
     5735                                if (elm && elm.id !== name) {
     5736                                        return this.doc.getElementsByName(name)[1];
     5737                                }
     5738                        }
     5739
     5740                        return elm;
     5741                },
     5742
     5743                /**
     5744                 * Returns the next node that matches selector or function
     5745                 *
     5746                 * @method getNext
     5747                 * @param {Node} node Node to find siblings from.
     5748                 * @param {String/function} selector Selector CSS expression or function.
     5749                 * @return {Node} Next node item matching the selector or null if it wasn't found.
     5750                 */
     5751                getNext: function(node, selector) {
     5752                        return this._findSib(node, selector, 'nextSibling');
     5753                },
     5754
     5755                /**
     5756                 * Returns the previous node that matches selector or function
     5757                 *
     5758                 * @method getPrev
     5759                 * @param {Node} node Node to find siblings from.
     5760                 * @param {String/function} selector Selector CSS expression or function.
     5761                 * @return {Node} Previous node item matching the selector or null if it wasn't found.
     5762                 */
     5763                getPrev: function(node, selector) {
     5764                        return this._findSib(node, selector, 'previousSibling');
     5765                },
     5766
     5767                // #ifndef jquery
     5768
     5769                /**
     5770                 * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
     5771                 * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
     5772                 * on more complex patterns.
     5773                 *
     5774                 * @method select
     5775                 * @param {String} selector CSS level 3 pattern to select/find elements by.
     5776                 * @param {Object} scope Optional root element/scope element to search in.
     5777                 * @return {Array} Array with all matched elements.
     5778                 * @example
     5779                 * // Adds a class to all paragraphs in the currently active editor
     5780                 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
     5781                 *
     5782                 * // Adds a class to all spans that have the test class in the currently active editor
     5783                 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
     5784                 */
     5785                select: function(selector, scope) {
     5786                        var self = this;
     5787
     5788                        //Sizzle.selectors.cacheLength = 0;
     5789                        return Sizzle(selector, self.get(scope) || self.get(self.settings.root_element) || self.doc, []);
     5790                },
     5791
     5792                /**
     5793                 * Returns true/false if the specified element matches the specified css pattern.
     5794                 *
     5795                 * @method is
     5796                 * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
     5797                 * @param {String} selector CSS pattern to match the element against.
     5798                 */
     5799                is: function(elm, selector) {
     5800                        var i;
     5801
     5802                        // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
     5803                        if (elm.length === undefined) {
     5804                                // Simple all selector
     5805                                if (selector === '*') {
     5806                                        return elm.nodeType == 1;
     5807                                }
     5808
     5809                                // Simple selector just elements
     5810                                if (simpleSelectorRe.test(selector)) {
     5811                                        selector = selector.toLowerCase().split(/,/);
     5812                                        elm = elm.nodeName.toLowerCase();
     5813
     5814                                        for (i = selector.length - 1; i >= 0; i--) {
     5815                                                if (selector[i] == elm) {
     5816                                                        return true;
     5817                                                }
     5818                                        }
     5819
     5820                                        return false;
     5821                                }
     5822                        }
     5823
     5824                        // Is non element
     5825                        if (elm.nodeType && elm.nodeType != 1) {
     5826                                return false;
     5827                        }
     5828
     5829                        return Sizzle.matches(selector, elm.nodeType ? [elm] : elm).length > 0;
     5830                },
     5831
     5832                // #endif
     5833
     5834                /**
     5835                 * Adds the specified element to another element or elements.
     5836                 *
     5837                 * @method add
     5838                 * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
     5839                 * @param {String/Element} name Name of new element to add or existing element to add.
     5840                 * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
     5841                 * @param {String} html Optional inner HTML contents to add for each element.
     5842                 * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
     5843                 * were passed in.
     5844                 * @example
     5845                 * // Adds a new paragraph to the end of the active editor
     5846                 * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
     5847                 */
     5848                add: function(parentElm, name, attrs, html, create) {
     5849                        var self = this;
     5850
     5851                        return this.run(parentElm, function(parentElm) {
     5852                                var newElm;
     5853
     5854                                newElm = is(name, 'string') ? self.doc.createElement(name) : name;
     5855                                self.setAttribs(newElm, attrs);
     5856
     5857                                if (html) {
     5858                                        if (html.nodeType) {
     5859                                                newElm.appendChild(html);
     5860                                        } else {
     5861                                                self.setHTML(newElm, html);
     5862                                        }
     5863                                }
     5864
     5865                                return !create ? parentElm.appendChild(newElm) : newElm;
     5866                        });
     5867                },
     5868
     5869                /**
     5870                 * Creates a new element.
     5871                 *
     5872                 * @method create
     5873                 * @param {String} name Name of new element.
     5874                 * @param {Object} attrs Optional object name/value collection with element attributes.
     5875                 * @param {String} html Optional HTML string to set as inner HTML of the element.
     5876                 * @return {Element} HTML DOM node element that got created.
     5877                 * @example
     5878                 * // Adds an element where the caret/selection is in the active editor
     5879                 * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
     5880                 * tinymce.activeEditor.selection.setNode(el);
     5881                 */
     5882                create: function(name, attrs, html) {
     5883                        return this.add(this.doc.createElement(name), name, attrs, html, 1);
     5884                },
     5885
     5886                /**
     5887                 * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
     5888                 *
     5889                 * @method createHTML
     5890                 * @param {String} name Name of new element.
     5891                 * @param {Object} attrs Optional object name/value collection with element attributes.
     5892                 * @param {String} html Optional HTML string to set as inner HTML of the element.
     5893                 * @return {String} String with new HTML element, for example: <a href="#">test</a>.
     5894                 * @example
     5895                 * // Creates a html chunk and inserts it at the current selection/caret location
     5896                 * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
     5897                 */
     5898                createHTML: function(name, attrs, html) {
     5899                        var outHtml = '', key;
     5900
     5901                        outHtml += '<' + name;
     5902
     5903                        for (key in attrs) {
     5904                                if (attrs.hasOwnProperty(key) && attrs[key] !== null) {
     5905                                        outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
     5906                                }
     5907                        }
     5908
     5909                        // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
     5910                        if (typeof(html) != "undefined") {
     5911                                return outHtml + '>' + html + '</' + name + '>';
     5912                        }
     5913
     5914                        return outHtml + ' />';
     5915                },
     5916
     5917                /**
     5918                 * Creates a document fragment out of the specified HTML string.
     5919                 *
     5920                 * @method createFragment
     5921                 * @param {String} html Html string to create fragment from.
     5922                 * @return {DocumentFragment} Document fragment node.
     5923                 */
     5924                createFragment: function(html) {
     5925                        var frag, node, doc = this.doc, container;
     5926
     5927                        container = doc.createElement("div");
     5928                        frag = doc.createDocumentFragment();
     5929
     5930                        if (html) {
     5931                                container.innerHTML = html;
     5932                        }
     5933
     5934                        while ((node = container.firstChild)) {
     5935                                frag.appendChild(node);
     5936                        }
     5937
     5938                        return frag;
     5939                },
     5940
     5941                /**
     5942                 * Removes/deletes the specified element(s) from the DOM.
     5943                 *
     5944                 * @method remove
     5945                 * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
     5946                 * @param {Boolean} keep_children Optional state to keep children or not. If set to true all children will be
     5947                 * placed at the location of the removed element.
     5948                 * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
     5949                 * were passed in.
     5950                 * @example
     5951                 * // Removes all paragraphs in the active editor
     5952                 * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
     5953                 *
     5954                 * // Removes an element by id in the document
     5955                 * tinymce.DOM.remove('mydiv');
     5956                 */
     5957                remove: function(node, keep_children) {
     5958                        return this.run(node, function(node) {
     5959                                var child, parent = node.parentNode;
     5960
     5961                                if (!parent) {
     5962                                        return null;
     5963                                }
     5964
     5965                                if (keep_children) {
     5966                                        while ((child = node.firstChild)) {
     5967                                                // IE 8 will crash if you don't remove completely empty text nodes
     5968                                                if (!isIE || child.nodeType !== 3 || child.nodeValue) {
     5969                                                        parent.insertBefore(child, node);
     5970                                                } else {
     5971                                                        node.removeChild(child);
     5972                                                }
     5973                                        }
     5974                                }
     5975
     5976                                return parent.removeChild(node);
     5977                        });
     5978                },
     5979
     5980                /**
     5981                 * Sets the CSS style value on a HTML element. The name can be a camelcase string
     5982                 * or the CSS style name like background-color.
     5983                 *
     5984                 * @method setStyle
     5985                 * @param {String/Element/Array} n HTML element/Element ID or Array of elements/ids to set CSS style value on.
     5986                 * @param {String} na Name of the style value to set.
     5987                 * @param {String} v Value to set on the style.
     5988                 * @example
     5989                 * // Sets a style value on all paragraphs in the currently active editor
     5990                 * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
     5991                 *
     5992                 * // Sets a style value to an element by id in the current document
     5993                 * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
     5994                 */
     5995                setStyle: function(elm, name, value) {
     5996                        return this.run(elm, function(elm) {
     5997                                var self = this, style, key;
     5998
     5999                                if (name) {
     6000                                        if (typeof(name) === 'string') {
     6001                                                style = elm.style;
     6002
     6003                                                // Camelcase it, if needed
     6004                                                name = name.replace(/-(\D)/g, function(a, b) {
     6005                                                        return b.toUpperCase();
     6006                                                });
     6007
     6008                                                // Default px suffix on these
     6009                                                if (typeof(value) === 'number' && !numericCssMap[name]) {
     6010                                                        value += 'px';
     6011                                                }
     6012
     6013                                                // IE specific opacity
     6014                                                if (name === "opacity" && elm.runtimeStyle && typeof(elm.runtimeStyle.opacity) === "undefined") {
     6015                                                        style.filter = value === '' ? '' : "alpha(opacity=" + (value * 100) + ")";
     6016                                                }
     6017
     6018                                                if (name == "float") {
     6019                                                        // Old IE vs modern browsers
     6020                                                        name = "cssFloat" in elm.style ? "cssFloat" : "styleFloat";
     6021                                                }
     6022
     6023                                                try {
     6024                                                        style[name] = value;
     6025                                                } catch (ex) {
     6026                                                        // Ignore IE errors
     6027                                                }
     6028
     6029                                                // Force update of the style data
     6030                                                if (self.settings.update_styles) {
     6031                                                        elm.removeAttribute('data-mce-style');
     6032                                                }
     6033                                        } else {
     6034                                                for (key in name) {
     6035                                                        self.setStyle(elm, key, name[key]);
     6036                                                }
     6037                                        }
     6038                                }
     6039                        });
     6040                },
     6041
     6042                /**
     6043                 * Returns the current style or runtime/computed value of an element.
     6044                 *
     6045                 * @method getStyle
     6046                 * @param {String/Element} elm HTML element or element id string to get style from.
     6047                 * @param {String} name Style name to return.
     6048                 * @param {Boolean} computed Computed style.
     6049                 * @return {String} Current style or computed style value of an element.
     6050                 */
     6051                getStyle: function(elm, name, computed) {
     6052                        elm = this.get(elm);
     6053
     6054                        if (!elm) {
     6055                                return;
     6056                        }
     6057
     6058                        // W3C
     6059                        if (this.doc.defaultView && computed) {
     6060                                // Remove camelcase
     6061                                name = name.replace(/[A-Z]/g, function(a){
     6062                                        return '-' + a;
     6063                                });
     6064
     6065                                try {
     6066                                        return this.doc.defaultView.getComputedStyle(elm, null).getPropertyValue(name);
     6067                                } catch (ex) {
     6068                                        // Old safari might fail
     6069                                        return null;
     6070                                }
     6071                        }
     6072
     6073                        // Camelcase it, if needed
     6074                        name = name.replace(/-(\D)/g, function(a, b) {
     6075                                return b.toUpperCase();
     6076                        });
     6077
     6078                        if (name == 'float') {
     6079                                name = isIE ? 'styleFloat' : 'cssFloat';
     6080                        }
     6081
     6082                        // IE & Opera
     6083                        if (elm.currentStyle && computed) {
     6084                                return elm.currentStyle[name];
     6085                        }
     6086
     6087                        return elm.style ? elm.style[name] : undefined;
     6088                },
     6089
     6090                /**
     6091                 * Sets multiple styles on the specified element(s).
     6092                 *
     6093                 * @method setStyles
     6094                 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set styles on.
     6095                 * @param {Object} o Name/Value collection of style items to add to the element(s).
     6096                 * @example
     6097                 * // Sets styles on all paragraphs in the currently active editor
     6098                 * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
     6099                 *
     6100                 * // Sets styles to an element by id in the current document
     6101                 * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
     6102                 */
     6103                setStyles: function(elm, styles) {
     6104                        this.setStyle(elm, styles);
     6105                },
     6106
     6107                css: function(elm, name, value) {
     6108                        this.setStyle(elm, name, value);
     6109                },
     6110
     6111                /**
     6112                 * Removes all attributes from an element or elements.
     6113                 *
     6114                 * @method removeAllAttribs
     6115                 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
     6116                 */
     6117                removeAllAttribs: function(e) {
     6118                        return this.run(e, function(e) {
     6119                                var i, attrs = e.attributes;
     6120                                for (i = attrs.length - 1; i >= 0; i--) {
     6121                                        e.removeAttributeNode(attrs.item(i));
     6122                                }
     6123                        });
     6124                },
     6125
     6126                /**
     6127                 * Sets the specified attribute of an element or elements.
     6128                 *
     6129                 * @method setAttrib
     6130                 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set attribute on.
     6131                 * @param {String} n Name of attribute to set.
     6132                 * @param {String} v Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove the attribute instead.
     6133                 * @example
     6134                 * // Sets class attribute on all paragraphs in the active editor
     6135                 * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
     6136                 *
     6137                 * // Sets class attribute on a specific element in the current page
     6138                 * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
     6139                 */
     6140                setAttrib: function(e, n, v) {
     6141                        var t = this;
     6142
     6143                        // What's the point
     6144                        if (!e || !n) {
     6145                                return;
     6146                        }
     6147
     6148                        return this.run(e, function(e) {
     6149                                var s = t.settings;
     6150                                var originalValue = e.getAttribute(n);
     6151                                if (v !== null) {
     6152                                        switch (n) {
     6153                                                case "style":
     6154                                                        if (!is(v, 'string')) {
     6155                                                                each(v, function(v, n) {
     6156                                                                        t.setStyle(e, n, v);
     6157                                                                });
     6158
     6159                                                                return;
     6160                                                        }
     6161
     6162                                                        // No mce_style for elements with these since they might get resized by the user
     6163                                                        if (s.keep_values) {
     6164                                                                if (v) {
     6165                                                                        e.setAttribute('data-mce-style', v, 2);
     6166                                                                } else {
     6167                                                                        e.removeAttribute('data-mce-style', 2);
     6168                                                                }
     6169                                                        }
     6170
     6171                                                        e.style.cssText = v;
     6172                                                        break;
     6173
     6174                                                case "class":
     6175                                                        e.className = v || ''; // Fix IE null bug
     6176                                                        break;
     6177
     6178                                                case "src":
     6179                                                case "href":
     6180                                                        if (s.keep_values) {
     6181                                                                if (s.url_converter) {
     6182                                                                        v = s.url_converter.call(s.url_converter_scope || t, v, n, e);
     6183                                                                }
     6184
     6185                                                                t.setAttrib(e, 'data-mce-' + n, v, 2);
     6186                                                        }
     6187
     6188                                                        break;
     6189
     6190                                                case "shape":
     6191                                                        e.setAttribute('data-mce-style', v);
     6192                                                        break;
     6193                                        }
     6194                                }
     6195                                if (is(v) && v !== null && v.length !== 0) {
     6196                                        e.setAttribute(n, '' + v, 2);
     6197                                } else {
     6198                                        e.removeAttribute(n, 2);
     6199                                }
     6200
     6201                                // fire onChangeAttrib event for attributes that have changed
     6202                                if (originalValue != v && s.onSetAttrib) {
     6203                                        s.onSetAttrib({attrElm: e, attrName: n, attrValue: v});
     6204                                }
     6205                        });
     6206                },
     6207
     6208                /**
     6209                 * Sets two or more specified attributes of an element or elements.
     6210                 *
     6211                 * @method setAttribs
     6212                 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
     6213                 * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
     6214                 * @example
     6215                 * // Sets class and title attributes on all paragraphs in the active editor
     6216                 * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
     6217                 *
     6218                 * // Sets class and title attributes on a specific element in the current page
     6219                 * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
     6220                 */
     6221                setAttribs: function(elm, attrs) {
     6222                        var self = this;
     6223
     6224                        return this.run(elm, function(elm) {
     6225                                each(attrs, function(value, name) {
     6226                                        self.setAttrib(elm, name, value);
     6227                                });
     6228                        });
     6229                },
     6230
     6231                /**
     6232                 * Returns the specified attribute by name.
     6233                 *
     6234                 * @method getAttrib
     6235                 * @param {String/Element} elm Element string id or DOM element to get attribute from.
     6236                 * @param {String} name Name of attribute to get.
     6237                 * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
     6238                 * @return {String} Attribute value string, default value or null if the attribute wasn't found.
     6239                 */
     6240                getAttrib: function(elm, name, defaultVal) {
     6241                        var value, self = this, undef;
     6242
     6243                        elm = self.get(elm);
     6244
     6245                        if (!elm || elm.nodeType !== 1) {
     6246                                return defaultVal === undef ? false : defaultVal;
     6247                        }
     6248
     6249                        if (!is(defaultVal)) {
     6250                                defaultVal = '';
     6251                        }
     6252
     6253                        // Try the mce variant for these
     6254                        if (/^(src|href|style|coords|shape)$/.test(name)) {
     6255                                value = elm.getAttribute("data-mce-" + name);
     6256
     6257                                if (value) {
     6258                                        return value;
     6259                                }
     6260                        }
     6261
     6262                        if (isIE && self.props[name]) {
     6263                                value = elm[self.props[name]];
     6264                                value = value && value.nodeValue ? value.nodeValue : value;
     6265                        }
     6266
     6267                        if (!value) {
     6268                                value = elm.getAttribute(name, 2);
     6269                        }
     6270
     6271                        // Check boolean attribs
     6272                        if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(name)) {
     6273                                if (elm[self.props[name]] === true && value === '') {
     6274                                        return name;
     6275                                }
     6276
     6277                                return value ? name : '';
     6278                        }
     6279
     6280                        // Inner input elements will override attributes on form elements
     6281                        if (elm.nodeName === "FORM" && elm.getAttributeNode(name)) {
     6282                                return elm.getAttributeNode(name).nodeValue;
     6283                        }
     6284
     6285                        if (name === 'style') {
     6286                                value = value || elm.style.cssText;
     6287
     6288                                if (value) {
     6289                                        value = self.serializeStyle(self.parseStyle(value), elm.nodeName);
     6290
     6291                                        if (self.settings.keep_values) {
     6292                                                elm.setAttribute('data-mce-style', value);
     6293                                        }
     6294                                }
     6295                        }
     6296
     6297                        // Remove Apple and WebKit stuff
     6298                        if (isWebKit && name === "class" && value) {
     6299                                value = value.replace(/(apple|webkit)\-[a-z\-]+/gi, '');
     6300                        }
     6301
     6302                        // Handle IE issues
     6303                        if (isIE) {
     6304                                switch (name) {
     6305                                        case 'rowspan':
     6306                                        case 'colspan':
     6307                                                // IE returns 1 as default value
     6308                                                if (value === 1) {
     6309                                                        value = '';
     6310                                                }
     6311
     6312                                                break;
     6313
     6314                                        case 'size':
     6315                                                // IE returns +0 as default value for size
     6316                                                if (value === '+0' || value === 20 || value === 0) {
     6317                                                        value = '';
     6318                                                }
     6319
     6320                                                break;
     6321
     6322                                        case 'width':
     6323                                        case 'height':
     6324                                        case 'vspace':
     6325                                        case 'checked':
     6326                                        case 'disabled':
     6327                                        case 'readonly':
     6328                                                if (value === 0) {
     6329                                                        value = '';
     6330                                                }
     6331
     6332                                                break;
     6333
     6334                                        case 'hspace':
     6335                                                // IE returns -1 as default value
     6336                                                if (value === -1) {
     6337                                                        value = '';
     6338                                                }
     6339
     6340                                                break;
     6341
     6342                                        case 'maxlength':
     6343                                        case 'tabindex':
     6344                                                // IE returns default value
     6345                                                if (value === 32768 || value === 2147483647 || value === '32768') {
     6346                                                        value = '';
     6347                                                }
     6348
     6349                                                break;
     6350
     6351                                        case 'multiple':
     6352                                        case 'compact':
     6353                                        case 'noshade':
     6354                                        case 'nowrap':
     6355                                                if (value === 65535) {
     6356                                                        return name;
     6357                                                }
     6358
     6359                                                return defaultVal;
     6360
     6361                                        case 'shape':
     6362                                                value = value.toLowerCase();
     6363                                                break;
     6364
     6365                                        default:
     6366                                                // IE has odd anonymous function for event attributes
     6367                                                if (name.indexOf('on') === 0 && value) {
     6368                                                        value = ('' + value).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1');
     6369                                                }
     6370                                }
     6371                        }
     6372
     6373                        return (value !== undef && value !== null && value !== '') ? '' + value : defaultVal;
     6374                },
     6375
     6376                /**
     6377                 * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
     6378                 *
     6379                 * @method getPos
     6380                 * @param {Element/String} elm HTML element or element id to get x, y position from.
     6381                 * @param {Element} rootElm Optional root element to stop calculations at.
     6382                 * @return {object} Absolute position of the specified element object with x, y fields.
     6383                 */
     6384                getPos: function(elm, rootElm) {
     6385                        var self = this, x = 0, y = 0, offsetParent, doc = self.doc, pos;
     6386
     6387                        elm = self.get(elm);
     6388                        rootElm = rootElm || doc.body;
     6389
     6390                        if (elm) {
     6391                                // Use getBoundingClientRect if it exists since it's faster than looping offset nodes
     6392                                if (rootElm === doc.body && elm.getBoundingClientRect) {
     6393                                        pos = elm.getBoundingClientRect();
     6394                                        rootElm = self.boxModel ? doc.documentElement : doc.body;
     6395
     6396                                        // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
     6397                                        // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
     6398                                        x = pos.left + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - rootElm.clientTop;
     6399                                        y = pos.top + (doc.documentElement.scrollTop || doc.body.scrollTop) - rootElm.clientLeft;
     6400
     6401                                        return {x: x, y: y};
     6402                                }
     6403
     6404                                offsetParent = elm;
     6405                                while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
     6406                                        x += offsetParent.offsetLeft || 0;
     6407                                        y += offsetParent.offsetTop || 0;
     6408                                        offsetParent = offsetParent.offsetParent;
     6409                                }
     6410
     6411                                offsetParent = elm.parentNode;
     6412                                while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
     6413                                        x -= offsetParent.scrollLeft || 0;
     6414                                        y -= offsetParent.scrollTop || 0;
     6415                                        offsetParent = offsetParent.parentNode;
     6416                                }
     6417                        }
     6418
     6419                        return {x: x, y: y};
     6420                },
     6421
     6422                /**
     6423                 * Parses the specified style value into an object collection. This parser will also
     6424                 * merge and remove any redundant items that browsers might have added. It will also convert non-hex
     6425                 * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
     6426                 *
     6427                 * @method parseStyle
     6428                 * @param {String} cssText Style value to parse, for example: border:1px solid red;.
     6429                 * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
     6430                 */
     6431                parseStyle: function(cssText) {
     6432                        return this.styles.parse(cssText);
     6433                },
     6434
     6435                /**
     6436                 * Serializes the specified style object into a string.
     6437                 *
     6438                 * @method serializeStyle
     6439                 * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
     6440                 * @param {String} name Optional element name.
     6441                 * @return {String} String representation of the style object, for example: border: 1px solid red.
     6442                 */
     6443                serializeStyle: function(styles, name) {
     6444                        return this.styles.serialize(styles, name);
     6445                },
     6446
     6447                /**
     6448                 * Adds a style element at the top of the document with the specified cssText content.
     6449                 *
     6450                 * @method addStyle
     6451                 * @param {String} cssText CSS Text style to add to top of head of document.
     6452                 */
     6453                addStyle: function(cssText) {
     6454                        var self = this, doc = self.doc, head, styleElm;
     6455
     6456                        // Prevent inline from loading the same styles twice
     6457                        if (self !== DOMUtils.DOM && doc === document) {
     6458                                var addedStyles = DOMUtils.DOM.addedStyles;
     6459
     6460                                addedStyles = addedStyles || [];
     6461                                if (addedStyles[cssText]) {
     6462                                        return;
     6463                                }
     6464
     6465                                addedStyles[cssText] = true;
     6466                                DOMUtils.DOM.addedStyles = addedStyles;
     6467                        }
     6468
     6469                        // Create style element if needed
     6470                        styleElm = doc.getElementById('mceDefaultStyles');
     6471                        if (!styleElm) {
     6472                                styleElm = doc.createElement('style');
     6473                                styleElm.id = 'mceDefaultStyles';
     6474                                styleElm.type = 'text/css';
     6475
     6476                                head = doc.getElementsByTagName('head')[0];
     6477                                if (head.firstChild) {
     6478                                        head.insertBefore(styleElm, head.firstChild);
     6479                                } else {
     6480                                        head.appendChild(styleElm);
     6481                                }
     6482                        }
     6483
     6484                        // Append style data to old or new style element
     6485                        if (styleElm.styleSheet) {
     6486                                styleElm.styleSheet.cssText += cssText;
     6487                        } else {
     6488                                styleElm.appendChild(doc.createTextNode(cssText));
     6489                        }
     6490                },
     6491
     6492                /**
     6493                 * Imports/loads the specified CSS file into the document bound to the class.
     6494                 *
     6495                 * @method loadCSS
     6496                 * @param {String} u URL to CSS file to load.
     6497                 * @example
     6498                 * // Loads a CSS file dynamically into the current document
     6499                 * tinymce.DOM.loadCSS('somepath/some.css');
     6500                 *
     6501                 * // Loads a CSS file into the currently active editor instance
     6502                 * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
     6503                 *
     6504                 * // Loads a CSS file into an editor instance by id
     6505                 * tinymce.get('someid').dom.loadCSS('somepath/some.css');
     6506                 *
     6507                 * // Loads multiple CSS files into the current document
     6508                 * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
     6509                 */
     6510                loadCSS: function(url) {
     6511                        var self = this, doc = self.doc, head;
     6512
     6513                        // Prevent inline from loading the same CSS file twice
     6514                        if (self !== DOMUtils.DOM && doc === document) {
     6515                                DOMUtils.DOM.loadCSS(url);
     6516                                return;
     6517                        }
     6518
     6519                        if (!url) {
     6520                                url = '';
     6521                        }
     6522
     6523                        head = doc.getElementsByTagName('head')[0];
     6524
     6525                        each(url.split(','), function(url) {
     6526                                var link;
     6527
     6528                                if (self.files[url]) {
     6529                                        return;
     6530                                }
     6531
     6532                                self.files[url] = true;
     6533                                link = self.create('link', {rel: 'stylesheet', href: url});
     6534
     6535                                // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
     6536                                // This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
     6537                                // It's ugly but it seems to work fine.
     6538                                if (isIE && doc.documentMode && doc.recalc) {
     6539                                        link.onload = function() {
     6540                                                if (doc.recalc) {
     6541                                                        doc.recalc();
     6542                                                }
     6543
     6544                                                link.onload = null;
     6545                                        };
     6546                                }
     6547
     6548                                head.appendChild(link);
     6549                        });
     6550                },
     6551
     6552                /**
     6553                 * Adds a class to the specified element or elements.
     6554                 *
     6555                 * @method addClass
     6556                 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
     6557                 * @param {String} cls Class name to add to each element.
     6558                 * @return {String/Array} String with new class value or array with new class values for all elements.
     6559                 * @example
     6560                 * // Adds a class to all paragraphs in the active editor
     6561                 * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
     6562                 *
     6563                 * // Adds a class to a specific element in the current page
     6564                 * tinymce.DOM.addClass('mydiv', 'myclass');
     6565                 */
     6566                addClass: function(elm, cls) {
     6567                        return this.run(elm, function(elm) {
     6568                                var clsVal;
     6569
     6570                                if (!cls) {
     6571                                        return 0;
     6572                                }
     6573
     6574                                if (this.hasClass(elm, cls)) {
     6575                                        return elm.className;
     6576                                }
     6577
     6578                                clsVal = this.removeClass(elm, cls);
     6579                                elm.className = clsVal = (clsVal !== '' ? (clsVal + ' ') : '') + cls;
     6580
     6581                                return clsVal;
     6582                        });
     6583                },
     6584
     6585                /**
     6586                 * Removes a class from the specified element or elements.
     6587                 *
     6588                 * @method removeClass
     6589                 * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
     6590                 * @param {String} cls Class name to remove from each element.
     6591                 * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
     6592                 * were passed in.
     6593                 * @example
     6594                 * // Removes a class from all paragraphs in the active editor
     6595                 * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
     6596                 *
     6597                 * // Removes a class from a specific element in the current page
     6598                 * tinymce.DOM.removeClass('mydiv', 'myclass');
     6599                 */
     6600                removeClass: function(elm, cls) {
     6601                        var self = this, re;
     6602
     6603                        return self.run(elm, function(elm) {
     6604                                var val;
     6605
     6606                                if (self.hasClass(elm, cls)) {
     6607                                        if (!re) {
     6608                                                re = new RegExp("(^|\\s+)" + cls + "(\\s+|$)", "g");
     6609                                        }
     6610
     6611                                        val = elm.className.replace(re, ' ');
     6612                                        val = trim(val != ' ' ? val : '');
     6613
     6614                                        elm.className = val;
     6615
     6616                                        // Empty class attr
     6617                                        if (!val) {
     6618                                                elm.removeAttribute('class');
     6619                                                elm.removeAttribute('className');
     6620                                        }
     6621
     6622                                        return val;
     6623                                }
     6624
     6625                                return elm.className;
     6626                        });
     6627                },
     6628
     6629                /**
     6630                 * Returns true if the specified element has the specified class.
     6631                 *
     6632                 * @method hasClass
     6633                 * @param {String/Element} n HTML element or element id string to check CSS class on.
     6634                 * @param {String} c CSS class to check for.
     6635                 * @return {Boolean} true/false if the specified element has the specified class.
     6636                 */
     6637                hasClass: function(elm, cls) {
     6638                        elm = this.get(elm);
     6639
     6640                        if (!elm || !cls) {
     6641                                return false;
     6642                        }
     6643
     6644                        return (' ' + elm.className + ' ').indexOf(' ' + cls + ' ') !== -1;
     6645                },
     6646
     6647                /**
     6648                 * Toggles the specified class on/off.
     6649                 *
     6650                 * @method toggleClass
     6651                 * @param {Element} elm Element to toggle class on.
     6652                 * @param {[type]} cls Class to toggle on/off.
     6653                 * @param {[type]} state Optional state to set.
     6654                 */
     6655                toggleClass: function(elm, cls, state) {
     6656                        state = state === undefined ? !this.hasClass(elm, cls) : state;
     6657
     6658                        if (this.hasClass(elm, cls) !== state) {
     6659                                if (state) {
     6660                                        this.addClass(elm, cls);
     6661                                } else {
     6662                                        this.removeClass(elm, cls);
     6663                                }
     6664                        }
     6665                },
     6666
     6667                /**
     6668                 * Shows the specified element(s) by ID by setting the "display" style.
     6669                 *
     6670                 * @method show
     6671                 * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
     6672                 */
     6673                show: function(elm) {
     6674                        return this.setStyle(elm, 'display', 'block');
     6675                },
     6676
     6677                /**
     6678                 * Hides the specified element(s) by ID by setting the "display" style.
     6679                 *
     6680                 * @method hide
     6681                 * @param {String/Element/Array} e ID of DOM element or DOM element or array with elements or IDs to hide.
     6682                 * @example
     6683                 * // Hides an element by id in the document
     6684                 * tinymce.DOM.hide('myid');
     6685                 */
     6686                hide: function(elm) {
     6687                        return this.setStyle(elm, 'display', 'none');
     6688                },
     6689
     6690                /**
     6691                 * Returns true/false if the element is hidden or not by checking the "display" style.
     6692                 *
     6693                 * @method isHidden
     6694                 * @param {String/Element} e Id or element to check display state on.
     6695                 * @return {Boolean} true/false if the element is hidden or not.
     6696                 */
     6697                isHidden: function(elm) {
     6698                        elm = this.get(elm);
     6699
     6700                        return !elm || elm.style.display == 'none' || this.getStyle(elm, 'display') == 'none';
     6701                },
     6702
     6703                /**
     6704                 * Returns a unique id. This can be useful when generating elements on the fly.
     6705                 * This method will not check if the element already exists.
     6706                 *
     6707                 * @method uniqueId
     6708                 * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
     6709                 * @return {String} Unique id.
     6710                 */
     6711                uniqueId: function(prefix) {
     6712                        return (!prefix ? 'mce_' : prefix) + (this.counter++);
     6713                },
     6714
     6715                /**
     6716                 * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
     6717                 * URLs will get converted, hex color values fixed etc. Check processHTML for details.
     6718                 *
     6719                 * @method setHTML
     6720                 * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to set HTML inside of.
     6721                 * @param {String} h HTML content to set as inner HTML of the element.
     6722                 * @example
     6723                 * // Sets the inner HTML of all paragraphs in the active editor
     6724                 * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
     6725                 *
     6726                 * // Sets the inner HTML of an element by id in the document
     6727                 * tinymce.DOM.setHTML('mydiv', 'some inner html');
     6728                 */
     6729                setHTML: function(element, html) {
     6730                        var self = this;
     6731
     6732                        return self.run(element, function(element) {
     6733                                if (isIE) {
     6734                                        // Remove all child nodes, IE keeps empty text nodes in DOM
     6735                                        while (element.firstChild) {
     6736                                                element.removeChild(element.firstChild);
     6737                                        }
     6738
     6739                                        try {
     6740                                                // IE will remove comments from the beginning
     6741                                                // unless you padd the contents with something
     6742                                                element.innerHTML = '<br />' + html;
     6743                                                element.removeChild(element.firstChild);
     6744                                        } catch (ex) {
     6745                                                // IE sometimes produces an unknown runtime error on innerHTML if it's a block element
     6746                                                // within a block element for example a div inside a p
     6747                                                // This seems to fix this problem
     6748
     6749                                                // Create new div with HTML contents and a BR in front to keep comments
     6750                                                var newElement = self.create('div');
     6751                                                newElement.innerHTML = '<br />' + html;
     6752
     6753                                                // Add all children from div to target
     6754                                                each (grep(newElement.childNodes), function(node, i) {
     6755                                                        // Skip br element
     6756                                                        if (i && element.canHaveHTML) {
     6757                                                                element.appendChild(node);
     6758                                                        }
     6759                                                });
     6760                                        }
     6761                                } else {
     6762                                        element.innerHTML = html;
     6763                                }
     6764
     6765                                return html;
     6766                        });
     6767                },
     6768
     6769                /**
     6770                 * Returns the outer HTML of an element.
     6771                 *
     6772                 * @method getOuterHTML
     6773                 * @param {String/Element} elm Element ID or element object to get outer HTML from.
     6774                 * @return {String} Outer HTML string.
     6775                 * @example
     6776                 * tinymce.DOM.getOuterHTML(editorElement);
     6777                 * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
     6778                 */
     6779                getOuterHTML: function(elm) {
     6780                        var doc, self = this;
     6781
     6782                        elm = self.get(elm);
     6783
     6784                        if (!elm) {
     6785                                return null;
     6786                        }
     6787
     6788                        if (elm.nodeType === 1 && self.hasOuterHTML) {
     6789                                return elm.outerHTML;
     6790                        }
     6791
     6792                        doc = (elm.ownerDocument || self.doc).createElement("body");
     6793                        doc.appendChild(elm.cloneNode(true));
     6794
     6795                        return doc.innerHTML;
     6796                },
     6797
     6798                /**
     6799                 * Sets the specified outer HTML on an element or elements.
     6800                 *
     6801                 * @method setOuterHTML
     6802                 * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
     6803                 * @param {Object} html HTML code to set as outer value for the element.
     6804                 * @param {Document} doc Optional document scope to use in this process - defaults to the document of the DOM class.
     6805                 * @example
     6806                 * // Sets the outer HTML of all paragraphs in the active editor
     6807                 * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '<div>some html</div>');
     6808                 *
     6809                 * // Sets the outer HTML of an element by id in the document
     6810                 * tinymce.DOM.setOuterHTML('mydiv', '<div>some html</div>');
     6811                 */
     6812                setOuterHTML: function(elm, html, doc) {
     6813                        var self = this;
     6814
     6815                        return self.run(elm, function(elm) {
     6816                                function set() {
     6817                                        var node, tempElm;
     6818
     6819                                        tempElm = doc.createElement("body");
     6820                                        tempElm.innerHTML = html;
     6821
     6822                                        node = tempElm.lastChild;
     6823                                        while (node) {
     6824                                                self.insertAfter(node.cloneNode(true), elm);
     6825                                                node = node.previousSibling;
     6826                                        }
     6827
     6828                                        self.remove(elm);
     6829                                }
     6830
     6831                                // Only set HTML on elements
     6832                                if (elm.nodeType == 1) {
     6833                                        doc = doc || elm.ownerDocument || self.doc;
     6834
     6835                                        if (isIE) {
     6836                                                try {
     6837                                                        // Try outerHTML for IE it sometimes produces an unknown runtime error
     6838                                                        if (elm.nodeType == 1 && self.hasOuterHTML) {
     6839                                                                elm.outerHTML = html;
     6840                                                        } else {
     6841                                                                set();
     6842                                                        }
     6843                                                } catch (ex) {
     6844                                                        // Fix for unknown runtime error
     6845                                                        set();
     6846                                                }
     6847                                        } else {
     6848                                                set();
     6849                                        }
     6850                                }
     6851                        });
     6852                },
     6853
     6854                /**
     6855                 * Entity decodes a string. This method decodes any HTML entities, such as &aring;.
     6856                 *
     6857                 * @method decode
     6858                 * @param {String} s String to decode entities on.
     6859                 * @return {String} Entity decoded string.
     6860                 */
     6861                decode: Entities.decode,
     6862
     6863                /**
     6864                 * Entity encodes a string. This method encodes the most common entities, such as <>"&.
     6865                 *
     6866                 * @method encode
     6867                 * @param {String} text String to encode with entities.
     6868                 * @return {String} Entity encoded string.
     6869                 */
     6870                encode: Entities.encodeAllRaw,
     6871
     6872                /**
     6873                 * Inserts an element after the reference element.
     6874                 *
     6875                 * @method insertAfter
     6876                 * @param {Element} node Element to insert after the reference.
     6877                 * @param {Element/String/Array} reference_node Reference element, element id or array of elements to insert after.
     6878                 * @return {Element/Array} Element that got added or an array with elements.
     6879                 */
     6880                insertAfter: function(node, reference_node) {
     6881                        reference_node = this.get(reference_node);
     6882
     6883                        return this.run(node, function(node) {
     6884                                var parent, nextSibling;
     6885
     6886                                parent = reference_node.parentNode;
     6887                                nextSibling = reference_node.nextSibling;
     6888
     6889                                if (nextSibling) {
     6890                                        parent.insertBefore(node, nextSibling);
     6891                                } else {
     6892                                        parent.appendChild(node);
     6893                                }
     6894
     6895                                return node;
     6896                        });
     6897                },
     6898
     6899                /**
     6900                 * Replaces the specified element or elements with the new element specified. The new element will
     6901                 * be cloned if multiple input elements are passed in.
     6902                 *
     6903                 * @method replace
     6904                 * @param {Element} newElm New element to replace old ones with.
     6905                 * @param {Element/String/Array} oldELm Element DOM node, element id or array of elements or ids to replace.
     6906                 * @param {Boolean} k Optional keep children state, if set to true child nodes from the old object will be added to new ones.
     6907                 */
     6908                replace: function(newElm, oldElm, keepChildren) {
     6909                        var self = this;
     6910
     6911                        return self.run(oldElm, function(oldElm) {
     6912                                if (is(oldElm, 'array')) {
     6913                                        newElm = newElm.cloneNode(true);
     6914                                }
     6915
     6916                                if (keepChildren) {
     6917                                        each(grep(oldElm.childNodes), function(node) {
     6918                                                newElm.appendChild(node);
     6919                                        });
     6920                                }
     6921
     6922                                return oldElm.parentNode.replaceChild(newElm, oldElm);
     6923                        });
     6924                },
     6925
     6926                /**
     6927                 * Renames the specified element and keeps its attributes and children.
     6928                 *
     6929                 * @method rename
     6930                 * @param {Element} elm Element to rename.
     6931                 * @param {String} name Name of the new element.
     6932                 * @return {Element} New element or the old element if it needed renaming.
     6933                 */
     6934                rename: function(elm, name) {
     6935                        var self = this, newElm;
     6936
     6937                        if (elm.nodeName != name.toUpperCase()) {
     6938                                // Rename block element
     6939                                newElm = self.create(name);
     6940
     6941                                // Copy attribs to new block
     6942                                each(self.getAttribs(elm), function(attr_node) {
     6943                                        self.setAttrib(newElm, attr_node.nodeName, self.getAttrib(elm, attr_node.nodeName));
     6944                                });
     6945
     6946                                // Replace block
     6947                                self.replace(newElm, elm, 1);
     6948                        }
     6949
     6950                        return newElm || elm;
     6951                },
     6952
     6953                /**
     6954                 * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
     6955                 *
     6956                 * @method findCommonAncestor
     6957                 * @param {Element} a Element to find common ancestor of.
     6958                 * @param {Element} b Element to find common ancestor of.
     6959                 * @return {Element} Common ancestor element of the two input elements.
     6960                 */
     6961                findCommonAncestor: function(a, b) {
     6962                        var ps = a, pe;
     6963
     6964                        while (ps) {
     6965                                pe = b;
     6966
     6967                                while (pe && ps != pe) {
     6968                                        pe = pe.parentNode;
     6969                                }
     6970
     6971                                if (ps == pe) {
     6972                                        break;
     6973                                }
     6974
     6975                                ps = ps.parentNode;
     6976                        }
     6977
     6978                        if (!ps && a.ownerDocument) {
     6979                                return a.ownerDocument.documentElement;
     6980                        }
     6981
     6982                        return ps;
     6983                },
     6984
     6985                /**
     6986                 * Parses the specified RGB color value and returns a hex version of that color.
     6987                 *
     6988                 * @method toHex
     6989                 * @param {String} rgbVal RGB string value like rgb(1,2,3)
     6990                 * @return {String} Hex version of that RGB value like #FF00FF.
     6991                 */
     6992                toHex: function(rgbVal) {
     6993                        return this.styles.toHex(Tools.trim(rgbVal));
     6994                },
     6995
     6996                /**
     6997                 * Executes the specified function on the element by id or dom element node or array of elements/id.
     6998                 *
     6999                 * @method run
     7000                 * @param {String/Element/Array} Element ID or DOM element object or array with ids or elements.
     7001                 * @param {function} f Function to execute for each item.
     7002                 * @param {Object} s Optional scope to execute the function in.
     7003                 * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
     7004                 */
     7005                run: function(elm, func, scope) {
     7006                        var self = this, result;
     7007
     7008                        if (typeof(elm) === 'string') {
     7009                                elm = self.get(elm);
     7010                        }
     7011
     7012                        if (!elm) {
     7013                                return false;
     7014                        }
     7015
     7016                        scope = scope || this;
     7017                        if (!elm.nodeType && (elm.length || elm.length === 0)) {
     7018                                result = [];
     7019
     7020                                each(elm, function(elm, i) {
     7021                                        if (elm) {
     7022                                                if (typeof(elm) == 'string') {
     7023                                                        elm = self.get(elm);
     7024                                                }
     7025
     7026                                                result.push(func.call(scope, elm, i));
     7027                                        }
     7028                                });
     7029
     7030                                return result;
     7031                        }
     7032
     7033                        return func.call(scope, elm);
     7034                },
     7035
     7036                /**
     7037                 * Returns a NodeList with attributes for the element.
     7038                 *
     7039                 * @method getAttribs
     7040                 * @param {HTMLElement/string} elm Element node or string id to get attributes from.
     7041                 * @return {NodeList} NodeList with attributes.
     7042                 */
     7043                getAttribs: function(elm) {
     7044                        var attrs;
     7045
     7046                        elm = this.get(elm);
     7047
     7048                        if (!elm) {
     7049                                return [];
     7050                        }
     7051
     7052                        if (isIE) {
     7053                                attrs = [];
     7054
     7055                                // Object will throw exception in IE
     7056                                if (elm.nodeName == 'OBJECT') {
     7057                                        return elm.attributes;
     7058                                }
     7059
     7060                                // IE doesn't keep the selected attribute if you clone option elements
     7061                                if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
     7062                                        attrs.push({specified: 1, nodeName: 'selected'});
     7063                                }
     7064
     7065                                // It's crazy that this is faster in IE but it's because it returns all attributes all the time
     7066                                var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
     7067                                elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
     7068                                        attrs.push({specified: 1, nodeName: a});
     7069                                });
     7070
     7071                                return attrs;
     7072                        }
     7073
     7074                        return elm.attributes;
     7075                },
     7076
     7077                /**
     7078                 * Returns true/false if the specified node is to be considered empty or not.
     7079                 *
     7080                 * @example
     7081                 * tinymce.DOM.isEmpty(node, {img: true});
     7082                 * @method isEmpty
     7083                 * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
     7084                 * @return {Boolean} true/false if the node is empty or not.
     7085                 */
     7086                isEmpty: function(node, elements) {
     7087                        var self = this, i, attributes, type, walker, name, brCount = 0;
     7088
     7089                        node = node.firstChild;
     7090                        if (node) {
     7091                                walker = new TreeWalker(node, node.parentNode);
     7092                                elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
     7093
     7094                                do {
     7095                                        type = node.nodeType;
     7096
     7097                                        if (type === 1) {
     7098                                                // Ignore bogus elements
     7099                                                if (node.getAttribute('data-mce-bogus')) {
     7100                                                        continue;
     7101                                                }
     7102
     7103                                                // Keep empty elements like <img />
     7104                                                name = node.nodeName.toLowerCase();
     7105                                                if (elements && elements[name]) {
     7106                                                        // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
     7107                                                        if (name === 'br') {
     7108                                                                brCount++;
     7109                                                                continue;
     7110                                                        }
     7111
     7112                                                        return false;
     7113                                                }
     7114
     7115                                                // Keep elements with data-bookmark attributes or name attribute like <a name="1"></a>
     7116                                                attributes = self.getAttribs(node);
     7117                                                i = node.attributes.length;
     7118                                                while (i--) {
     7119                                                        name = node.attributes[i].nodeName;
     7120                                                        if (name === "name" || name === 'data-mce-bookmark') {
     7121                                                                return false;
     7122                                                        }
     7123                                                }
     7124                                        }
     7125
     7126                                        // Keep comment nodes
     7127                                        if (type == 8) {
     7128                                                return false;
     7129                                        }
     7130
     7131                                        // Keep non whitespace text nodes
     7132                                        if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue))) {
     7133                                                return false;
     7134                                        }
     7135                                } while ((node = walker.next()));
     7136                        }
     7137
     7138                        return brCount <= 1;
     7139                },
     7140
     7141                /**
     7142                 * Creates a new DOM Range object. This will use the native DOM Range API if it's
     7143                 * available. If it's not, it will fall back to the custom TinyMCE implementation.
     7144                 *
     7145                 * @method createRng
     7146                 * @return {DOMRange} DOM Range object.
     7147                 * @example
     7148                 * var rng = tinymce.DOM.createRng();
     7149                 * alert(rng.startContainer + "," + rng.startOffset);
     7150                 */
     7151                createRng: function() {
     7152                        var doc = this.doc;
     7153
     7154                        return doc.createRange ? doc.createRange() : new Range(this);
     7155                },
     7156
     7157                /**
     7158                 * Returns the index of the specified node within its parent.
     7159                 *
     7160                 * @method nodeIndex
     7161                 * @param {Node} node Node to look for.
     7162                 * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
     7163                 * @return {Number} Index of the specified node.
     7164                 */
     7165                nodeIndex: function(node, normalized) {
     7166                        var idx = 0, lastNodeType, lastNode, nodeType;
     7167
     7168                        if (node) {
     7169                                for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
     7170                                        nodeType = node.nodeType;
     7171
     7172                                        // Normalize text nodes
     7173                                        if (normalized && nodeType == 3) {
     7174                                                if (nodeType == lastNodeType || !node.nodeValue.length) {
     7175                                                        continue;
     7176                                                }
     7177                                        }
     7178                                        idx++;
     7179                                        lastNodeType = nodeType;
     7180                                }
     7181                        }
     7182
     7183                        return idx;
     7184                },
     7185
     7186                /**
     7187                 * Splits an element into two new elements and places the specified split
     7188                 * element or elements between the new ones. For example splitting the paragraph at the bold element in
     7189                 * this example <p>abc<b>abc</b>123</p> would produce <p>abc</p><b>abc</b><p>123</p>.
     7190                 *
     7191                 * @method split
     7192                 * @param {Element} parentElm Parent element to split.
     7193                 * @param {Element} splitElm Element to split at.
     7194                 * @param {Element} replacementElm Optional replacement element to replace the split element with.
     7195                 * @return {Element} Returns the split element or the replacement element if that is specified.
     7196                 */
     7197                split: function(parentElm, splitElm, replacementElm) {
     7198                        var self = this, r = self.createRng(), bef, aft, pa;
     7199
     7200                        // W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
     7201                        // but we don't want that in our code since it serves no purpose for the end user
     7202                        // For example splitting this html at the bold element:
     7203                        //   <p>text 1<span><b>CHOP</b></span>text 2</p>
     7204                        // would produce:
     7205                        //   <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p>
     7206                        // this function will then trim off empty edges and produce:
     7207                        //   <p>text 1</p><b>CHOP</b><p>text 2</p>
     7208                        function trimNode(node) {
     7209                                var i, children = node.childNodes, type = node.nodeType;
     7210
     7211                                function surroundedBySpans(node) {
     7212                                        var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
     7213                                        var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
     7214                                        return previousIsSpan && nextIsSpan;
     7215                                }
     7216
     7217                                if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
     7218                                        return;
     7219                                }
     7220
     7221                                for (i = children.length - 1; i >= 0; i--) {
     7222                                        trimNode(children[i]);
     7223                                }
     7224
     7225                                if (type != 9) {
     7226                                        // Keep non whitespace text nodes
     7227                                        if (type == 3 && node.nodeValue.length > 0) {
     7228                                                // If parent element isn't a block or there isn't any useful contents for example "<p>   </p>"
     7229                                                // Also keep text nodes with only spaces if surrounded by spans.
     7230                                                // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
     7231                                                var trimmedLength = trim(node.nodeValue).length;
     7232                                                if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
     7233                                                        return;
     7234                                                }
     7235                                        } else if (type == 1) {
     7236                                                // If the only child is a bookmark then move it up
     7237                                                children = node.childNodes;
     7238
     7239                                                // TODO fix this complex if
     7240                                                if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
     7241                                                        children[0].getAttribute('data-mce-type') == 'bookmark') {
     7242                                                        node.parentNode.insertBefore(children[0], node);
     7243                                                }
     7244
     7245                                                // Keep non empty elements or img, hr etc
     7246                                                if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
     7247                                                        return;
     7248                                                }
     7249                                        }
     7250
     7251                                        self.remove(node);
     7252                                }
     7253
     7254                                return node;
     7255                        }
     7256
     7257                        if (parentElm && splitElm) {
     7258                                // Get before chunk
     7259                                r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
     7260                                r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
     7261                                bef = r.extractContents();
     7262
     7263                                // Get after chunk
     7264                                r = self.createRng();
     7265                                r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
     7266                                r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
     7267                                aft = r.extractContents();
     7268
     7269                                // Insert before chunk
     7270                                pa = parentElm.parentNode;
     7271                                pa.insertBefore(trimNode(bef), parentElm);
     7272
     7273                                // Insert middle chunk
     7274                                if (replacementElm) {
     7275                                        pa.replaceChild(replacementElm, splitElm);
     7276                                } else {
     7277                                        pa.insertBefore(splitElm, parentElm);
     7278                                }
     7279
     7280                                // Insert after chunk
     7281                                pa.insertBefore(trimNode(aft), parentElm);
     7282                                self.remove(parentElm);
     7283
     7284                                return replacementElm || splitElm;
     7285                        }
     7286                },
     7287
     7288                /**
     7289                 * Adds an event handler to the specified object.
     7290                 *
     7291                 * @method bind
     7292                 * @param {Element/Document/Window/Array} target Target element to bind events to.
     7293                 * handler to or an array of elements/ids/documents.
     7294                 * @param {String} name Name of event handler to add, for example: click.
     7295                 * @param {function} func Function to execute when the event occurs.
     7296                 * @param {Object} scope Optional scope to execute the function in.
     7297                 * @return {function} Function callback handler the same as the one passed in.
     7298                 */
     7299                bind: function(target, name, func, scope) {
     7300                        var self = this;
     7301
     7302                        if (Tools.isArray(target)) {
     7303                                var i = target.length;
     7304
     7305                                while (i--) {
     7306                                        target[i] = self.bind(target[i], name, func, scope);
     7307                                }
     7308
     7309                                return target;
     7310                        }
     7311
     7312                        // Collect all window/document events bound by editor instance
     7313                        if (self.settings.collect && (target === self.doc || target === self.win)) {
     7314                                self.boundEvents.push([target, name, func, scope]);
     7315                        }
     7316
     7317                        return self.events.bind(target, name, func, scope || self);
     7318                },
     7319
     7320                /**
     7321                 * Removes the specified event handler by name and function from an element or collection of elements.
     7322                 *
     7323                 * @method unbind
     7324                 * @param {Element/Document/Window/Array} target Target element to unbind events on.
     7325                 * @param {String} name Event handler name, for example: "click"
     7326                 * @param {function} func Function to remove.
     7327                 * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
     7328                 * were passed in.
     7329                 */
     7330                unbind: function(target, name, func) {
     7331                        var self = this, i;
     7332
     7333                        if (Tools.isArray(target)) {
     7334                                i = target.length;
     7335
     7336                                while (i--) {
     7337                                        target[i] = self.unbind(target[i], name, func);
     7338                                }
     7339
     7340                                return target;
     7341                        }
     7342
     7343                        // Remove any bound events matching the input
     7344                        if (self.boundEvents && (target === self.doc || target === self.win)) {
     7345                                i = self.boundEvents.length;
     7346
     7347                                while (i--) {
     7348                                        var item = self.boundEvents[i];
     7349
     7350                                        if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
     7351                                                this.events.unbind(item[0], item[1], item[2]);
     7352                                        }
     7353                                }
     7354                        }
     7355
     7356                        return this.events.unbind(target, name, func);
     7357                },
     7358
     7359                /**
     7360                 * Fires the specified event name with object on target.
     7361                 *
     7362                 * @method fire
     7363                 * @param {Node/Document/Window} target Target element or object to fire event on.
     7364                 * @param {String} name Name of the event to fire.
     7365                 * @param {Object} evt Event object to send.
     7366                 * @return {Event} Event object.
     7367                 */
     7368                fire: function(target, name, evt) {
     7369                        return this.events.fire(target, name, evt);
     7370                },
     7371
     7372                // Returns the content editable state of a node
     7373                getContentEditable: function(node) {
     7374                        var contentEditable;
     7375
     7376                        // Check type
     7377                        if (node.nodeType != 1) {
     7378                                return null;
     7379                        }
     7380
     7381                        // Check for fake content editable
     7382                        contentEditable = node.getAttribute("data-mce-contenteditable");
     7383                        if (contentEditable && contentEditable !== "inherit") {
     7384                                return contentEditable;
     7385                        }
     7386
     7387                        // Check for real content editable
     7388                        return node.contentEditable !== "inherit" ? node.contentEditable : null;
     7389                },
     7390
     7391                /**
     7392                 * Destroys all internal references to the DOM to solve IE leak issues.
     7393                 *
     7394                 * @method destroy
     7395                 */
     7396                destroy: function() {
     7397                        var self = this;
     7398
     7399                        // Unbind all events bound to window/document by editor instance
     7400                        if (self.boundEvents) {
     7401                                var i = self.boundEvents.length;
     7402
     7403                                while (i--) {
     7404                                        var item = self.boundEvents[i];
     7405                                        this.events.unbind(item[0], item[1], item[2]);
     7406                                }
     7407
     7408                                self.boundEvents = null;
     7409                        }
     7410
     7411                        // Restore sizzle document to window.document
     7412                        // Since the current document might be removed producing "Permission denied" on IE see #6325
     7413                        if (Sizzle.setDocument) {
     7414                                Sizzle.setDocument();
     7415                        }
     7416
     7417                        self.win = self.doc = self.root = self.events = self.frag = null;
     7418                },
     7419
     7420                // #ifdef debug
     7421
     7422                dumpRng: function(r) {
     7423                        return (
     7424                                'startContainer: ' + r.startContainer.nodeName +
     7425                                ', startOffset: ' + r.startOffset +
     7426                                ', endContainer: ' + r.endContainer.nodeName +
     7427                                ', endOffset: ' + r.endOffset
     7428                        );
     7429                },
     7430
     7431                // #endif
     7432
     7433                _findSib: function(node, selector, name) {
     7434                        var self = this, func = selector;
     7435
     7436                        if (node) {
     7437                                // If expression make a function of it using is
     7438                                if (typeof(func) == 'string') {
     7439                                        func = function(node) {
     7440                                                return self.is(node, selector);
     7441                                        };
     7442                                }
     7443
     7444                                // Loop all siblings
     7445                                for (node = node[name]; node; node = node[name]) {
     7446                                        if (func(node)) {
     7447                                                return node;
     7448                                        }
     7449                                }
     7450                        }
     7451
     7452                        return null;
     7453                }
     7454        };
     7455
     7456        /**
     7457         * Instance of DOMUtils for the current document.
     7458         *
     7459         * @static
     7460         * @property DOM
     7461         * @type tinymce.dom.DOMUtils
     7462         * @example
     7463         * // Example of how to add a class to some element by id
     7464         * tinymce.DOM.addClass('someid', 'someclass');
     7465         */
     7466        DOMUtils.DOM = new DOMUtils(document);
     7467
     7468        return DOMUtils;
     7469});
     7470
     7471// Included from: js/tinymce/classes/dom/ScriptLoader.js
     7472
     7473/**
     7474 * ScriptLoader.js
     7475 *
     7476 * Copyright, Moxiecode Systems AB
     7477 * Released under LGPL License.
     7478 *
     7479 * License: http://www.tinymce.com/license
     7480 * Contributing: http://www.tinymce.com/contributing
     7481 */
     7482
     7483/*globals console*/
     7484
     7485/**
     7486 * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
     7487 * when various items gets loaded. This class is useful to load external JavaScript files.
     7488 *
     7489 * @class tinymce.dom.ScriptLoader
     7490 * @example
     7491 * // Load a script from a specific URL using the global script loader
     7492 * tinymce.ScriptLoader.load('somescript.js');
     7493 *
     7494 * // Load a script using a unique instance of the script loader
     7495 * var scriptLoader = new tinymce.dom.ScriptLoader();
     7496 *
     7497 * scriptLoader.load('somescript.js');
     7498 *
     7499 * // Load multiple scripts
     7500 * var scriptLoader = new tinymce.dom.ScriptLoader();
     7501 *
     7502 * scriptLoader.add('somescript1.js');
     7503 * scriptLoader.add('somescript2.js');
     7504 * scriptLoader.add('somescript3.js');
     7505 *
     7506 * scriptLoader.loadQueue(function() {
     7507 *    alert('All scripts are now loaded.');
     7508 * });
     7509 */
     7510define("tinymce/dom/ScriptLoader", [
     7511        "tinymce/dom/DOMUtils",
     7512        "tinymce/util/Tools"
     7513], function(DOMUtils, Tools) {
     7514        var DOM = DOMUtils.DOM;
     7515        var each = Tools.each, grep = Tools.grep;
     7516
     7517        function ScriptLoader() {
     7518                var QUEUED = 0,
     7519                        LOADING = 1,
     7520                        LOADED = 2,
     7521                        states = {},
     7522                        queue = [],
     7523                        scriptLoadedCallbacks = {},
     7524                        queueLoadedCallbacks = [],
     7525                        loading = 0,
     7526                        undef;
     7527
     7528                /**
     7529                 * Loads a specific script directly without adding it to the load queue.
     7530                 *
     7531                 * @method load
     7532                 * @param {String} url Absolute URL to script to add.
     7533                 * @param {function} callback Optional callback function to execute ones this script gets loaded.
     7534                 * @param {Object} scope Optional scope to execute callback in.
     7535                 */
     7536                function loadScript(url, callback) {
     7537                        var dom = DOM, elm, id;
     7538
     7539                        // Execute callback when script is loaded
     7540                        function done() {
     7541                                dom.remove(id);
     7542
     7543                                if (elm) {
     7544                                        elm.onreadystatechange = elm.onload = elm = null;
     7545                                }
     7546
     7547                                callback();
     7548                        }
     7549
     7550                        function error() {
     7551                                // Report the error so it's easier for people to spot loading errors
     7552                                if (typeof(console) !== "undefined" && console.log) {
     7553                                        console.log("Failed to load: " + url);
     7554                                }
     7555
     7556                                // We can't mark it as done if there is a load error since
     7557                                // A) We don't want to produce 404 errors on the server and
     7558                                // B) the onerror event won't fire on all browsers.
     7559                                // done();
     7560                        }
     7561
     7562                        id = dom.uniqueId();
     7563
     7564                        // Create new script element
     7565                        elm = document.createElement('script');
     7566                        elm.id = id;
     7567                        elm.type = 'text/javascript';
     7568                        elm.src = url;
     7569
     7570                        // Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
     7571                        if ("onreadystatechange" in elm) {
     7572                                elm.onreadystatechange = function() {
     7573                                        if (/loaded|complete/.test(elm.readyState)) {
     7574                                                done();
     7575                                        }
     7576                                };
     7577                        } else {
     7578                                elm.onload = done;
     7579                        }
     7580
     7581                        // Add onerror event will get fired on some browsers but not all of them
     7582                        elm.onerror = error;
     7583
     7584                        // Add script to document
     7585                        (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
     7586                }
     7587
     7588                /**
     7589                 * Returns true/false if a script has been loaded or not.
     7590                 *
     7591                 * @method isDone
     7592                 * @param {String} url URL to check for.
     7593                 * @return {Boolean} true/false if the URL is loaded.
     7594                 */
     7595                this.isDone = function(url) {
     7596                        return states[url] == LOADED;
     7597                };
     7598
     7599                /**
     7600                 * Marks a specific script to be loaded. This can be useful if a script got loaded outside
     7601                 * the script loader or to skip it from loading some script.
     7602                 *
     7603                 * @method markDone
     7604                 * @param {string} u Absolute URL to the script to mark as loaded.
     7605                 */
     7606                this.markDone = function(url) {
     7607                        states[url] = LOADED;
     7608                };
     7609
     7610                /**
     7611                 * Adds a specific script to the load queue of the script loader.
     7612                 *
     7613                 * @method add
     7614                 * @param {String} url Absolute URL to script to add.
     7615                 * @param {function} callback Optional callback function to execute ones this script gets loaded.
     7616                 * @param {Object} scope Optional scope to execute callback in.
     7617                 */
     7618                this.add = this.load = function(url, callback, scope) {
     7619                        var state = states[url];
     7620
     7621                        // Add url to load queue
     7622                        if (state == undef) {
     7623                                queue.push(url);
     7624                                states[url] = QUEUED;
     7625                        }
     7626
     7627                        if (callback) {
     7628                                // Store away callback for later execution
     7629                                if (!scriptLoadedCallbacks[url]) {
     7630                                        scriptLoadedCallbacks[url] = [];
     7631                                }
     7632
     7633                                scriptLoadedCallbacks[url].push({
     7634                                        func: callback,
     7635                                        scope: scope || this
     7636                                });
     7637                        }
     7638                };
     7639
     7640                /**
     7641                 * Starts the loading of the queue.
     7642                 *
     7643                 * @method loadQueue
     7644                 * @param {function} callback Optional callback to execute when all queued items are loaded.
     7645                 * @param {Object} scope Optional scope to execute the callback in.
     7646                 */
     7647                this.loadQueue = function(callback, scope) {
     7648                        this.loadScripts(queue, callback, scope);
     7649                };
     7650
     7651                /**
     7652                 * Loads the specified queue of files and executes the callback ones they are loaded.
     7653                 * This method is generally not used outside this class but it might be useful in some scenarios.
     7654                 *
     7655                 * @method loadScripts
     7656                 * @param {Array} scripts Array of queue items to load.
     7657                 * @param {function} callback Optional callback to execute ones all items are loaded.
     7658                 * @param {Object} scope Optional scope to execute callback in.
     7659                 */
     7660                this.loadScripts = function(scripts, callback, scope) {
     7661                        var loadScripts;
     7662
     7663                        function execScriptLoadedCallbacks(url) {
     7664                                // Execute URL callback functions
     7665                                each(scriptLoadedCallbacks[url], function(callback) {
     7666                                        callback.func.call(callback.scope);
     7667                                });
     7668
     7669                                scriptLoadedCallbacks[url] = undef;
     7670                        }
     7671
     7672                        queueLoadedCallbacks.push({
     7673                                func: callback,
     7674                                scope: scope || this
     7675                        });
     7676
     7677                        loadScripts = function() {
     7678                                var loadingScripts = grep(scripts);
     7679
     7680                                // Current scripts has been handled
     7681                                scripts.length = 0;
     7682
     7683                                // Load scripts that needs to be loaded
     7684                                each(loadingScripts, function(url) {
     7685                                        // Script is already loaded then execute script callbacks directly
     7686                                        if (states[url] == LOADED) {
     7687                                                execScriptLoadedCallbacks(url);
     7688                                                return;
     7689                                        }
     7690
     7691                                        // Is script not loading then start loading it
     7692                                        if (states[url] != LOADING) {
     7693                                                states[url] = LOADING;
     7694                                                loading++;
     7695
     7696                                                loadScript(url, function() {
     7697                                                        states[url] = LOADED;
     7698                                                        loading--;
     7699
     7700                                                        execScriptLoadedCallbacks(url);
     7701
     7702                                                        // Load more scripts if they where added by the recently loaded script
     7703                                                        loadScripts();
     7704                                                });
     7705                                        }
     7706                                });
     7707
     7708                                // No scripts are currently loading then execute all pending queue loaded callbacks
     7709                                if (!loading) {
     7710                                        each(queueLoadedCallbacks, function(callback) {
     7711                                                callback.func.call(callback.scope);
     7712                                        });
     7713
     7714                                        queueLoadedCallbacks.length = 0;
     7715                                }
     7716                        };
     7717
     7718                        loadScripts();
     7719                };
     7720        }
     7721
     7722        ScriptLoader.ScriptLoader = new ScriptLoader();
     7723
     7724        return ScriptLoader;
     7725});
     7726
     7727// Included from: js/tinymce/classes/AddOnManager.js
     7728
     7729/**
     7730 * AddOnManager.js
     7731 *
     7732 * Copyright, Moxiecode Systems AB
     7733 * Released under LGPL License.
     7734 *
     7735 * License: http://www.tinymce.com/license
     7736 * Contributing: http://www.tinymce.com/contributing
     7737 */
     7738
     7739/**
     7740 * This class handles the loading of themes/plugins or other add-ons and their language packs.
     7741 *
     7742 * @class tinymce.AddOnManager
     7743 */
     7744define("tinymce/AddOnManager", [
     7745        "tinymce/dom/ScriptLoader",
     7746        "tinymce/util/Tools"
     7747], function(ScriptLoader, Tools) {
     7748        var each = Tools.each;
     7749
     7750        function AddOnManager() {
     7751                var self = this;
     7752
     7753                self.items = [];
     7754                self.urls = {};
     7755                self.lookup = {};
     7756        }
     7757
     7758        AddOnManager.prototype = {
     7759                /**
     7760                 * Returns the specified add on by the short name.
     7761                 *
     7762                 * @method get
     7763                 * @param {String} name Add-on to look for.
     7764                 * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
     7765                 */
     7766                get: function(name) {
     7767                        if (this.lookup[name]) {
     7768                                return this.lookup[name].instance;
     7769                        } else {
     7770                                return undefined;
     7771                        }
     7772                },
     7773
     7774                dependencies: function(name) {
     7775                        var result;
     7776
     7777                        if (this.lookup[name]) {
     7778                                result = this.lookup[name].dependencies;
     7779                        }
     7780
     7781                        return result || [];
     7782                },
     7783
     7784                /**
     7785                 * Loads a language pack for the specified add-on.
     7786                 *
     7787                 * @method requireLangPack
     7788                 * @param {String} name Short name of the add-on.
     7789                 * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
     7790                 */
     7791                requireLangPack: function(name, languages) {
     7792                        if (AddOnManager.language && AddOnManager.languageLoad !== false) {
     7793                                if (languages && new RegExp('([, ]|\\b)' + AddOnManager.language + '([, ]|\\b)').test(languages) === false) {
     7794                                        return;
     7795                                }
     7796
     7797                                ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + AddOnManager.language + '.js');
     7798                        }
     7799                },
     7800
     7801                /**
     7802                 * Adds a instance of the add-on by it's short name.
     7803                 *
     7804                 * @method add
     7805                 * @param {String} id Short name/id for the add-on.
     7806                 * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
     7807                 * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
     7808                 * @example
     7809                 * // Create a simple plugin
     7810                 * tinymce.create('tinymce.plugins.TestPlugin', {
     7811                 *   TestPlugin: function(ed, url) {
     7812                 *   ed.on('click', function(e) {
     7813                 *      ed.windowManager.alert('Hello World!');
     7814                 *   });
     7815                 *   }
     7816                 * });
     7817                 *
     7818                 * // Register plugin using the add method
     7819                 * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
     7820                 *
     7821                 * // Initialize TinyMCE
     7822                 * tinymce.init({
     7823                 *  ...
     7824                 *  plugins: '-test' // Init the plugin but don't try to load it
     7825                 * });
     7826                 */
     7827                add: function(id, addOn, dependencies) {
     7828                        this.items.push(addOn);
     7829                        this.lookup[id] = {instance: addOn, dependencies: dependencies};
     7830
     7831                        return addOn;
     7832                },
     7833
     7834                createUrl: function(baseUrl, dep) {
     7835                        if (typeof dep === "object") {
     7836                                return dep;
     7837                        } else {
     7838                                return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
     7839                        }
     7840                },
     7841
     7842                /**
     7843                 * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
     7844                 * This should be used in development mode.  A new compressor/javascript munger process will ensure that the
     7845                 * components are put together into the plugin.js file and compressed correctly.
     7846                 *
     7847                 * @method addComponents
     7848                 * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
     7849                 * @param {Array} scripts Array containing the names of the scripts to load.
     7850                 */
     7851                addComponents: function(pluginName, scripts) {
     7852                        var pluginUrl = this.urls[pluginName];
     7853
     7854                        each(scripts, function(script) {
     7855                                ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
     7856                        });
     7857                },
     7858
     7859                /**
     7860                 * Loads an add-on from a specific url.
     7861                 *
     7862                 * @method load
     7863                 * @param {String} n Short name of the add-on that gets loaded.
     7864                 * @param {String} u URL to the add-on that will get loaded.
     7865                 * @param {function} cb Optional callback to execute ones the add-on is loaded.
     7866                 * @param {Object} s Optional scope to execute the callback in.
     7867                 * @example
     7868                 * // Loads a plugin from an external URL
     7869                 * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
     7870                 *
     7871                 * // Initialize TinyMCE
     7872                 * tinymce.init({
     7873                 *  ...
     7874                 *  plugins: '-myplugin' // Don't try to load it again
     7875                 * });
     7876                 */
     7877                load: function(n, u, cb, s) {
     7878                        var t = this, url = u;
     7879
     7880                        function loadDependencies() {
     7881                                var dependencies = t.dependencies(n);
     7882
     7883                                each(dependencies, function(dep) {
     7884                                        var newUrl = t.createUrl(u, dep);
     7885
     7886                                        t.load(newUrl.resource, newUrl, undefined, undefined);
     7887                                });
     7888
     7889                                if (cb) {
     7890                                        if (s) {
     7891                                                cb.call(s);
     7892                                        } else {
     7893                                                cb.call(ScriptLoader);
     7894                                        }
     7895                                }
     7896                        }
     7897
     7898                        if (t.urls[n]) {
     7899                                return;
     7900                        }
     7901
     7902                        if (typeof u === "object") {
     7903                                url = u.prefix + u.resource + u.suffix;
     7904                        }
     7905
     7906                        if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
     7907                                url = AddOnManager.baseURL + '/' + url;
     7908                        }
     7909
     7910                        t.urls[n] = url.substring(0, url.lastIndexOf('/'));
     7911
     7912                        if (t.lookup[n]) {
     7913                                loadDependencies();
     7914                        } else {
     7915                                ScriptLoader.ScriptLoader.add(url, loadDependencies, s);
     7916                        }
     7917                }
     7918        };
     7919
     7920        AddOnManager.PluginManager = new AddOnManager();
     7921        AddOnManager.ThemeManager = new AddOnManager();
     7922
     7923        return AddOnManager;
     7924});
     7925
     7926/**
     7927 * TinyMCE theme class.
     7928 *
     7929 * @class tinymce.Theme
     7930 */
     7931
     7932/**
     7933 * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
     7934 *
     7935 * @method renderUI
     7936 * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
     7937 * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
     7938 */
     7939
     7940/**
     7941 * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
     7942 *
     7943 * @class tinymce.Plugin
     7944 * @example
     7945 * tinymce.PluginManager.add('example', function(editor, url) {
     7946 *     // Add a button that opens a window
     7947 *     editor.addButton('example', {
     7948 *         text: 'My button',
     7949 *         icon: false,
     7950 *         onclick: function() {
     7951 *             // Open window
     7952 *             editor.windowManager.open({
     7953 *                 title: 'Example plugin',
     7954 *                 body: [
     7955 *                     {type: 'textbox', name: 'title', label: 'Title'}
     7956 *                 ],
     7957 *                 onsubmit: function(e) {
     7958 *                     // Insert content when the window form is submitted
     7959 *                     editor.insertContent('Title: ' + e.data.title);
     7960 *                 }
     7961 *             });
     7962 *         }
     7963 *     });
     7964 *
     7965 *     // Adds a menu item to the tools menu
     7966 *     editor.addMenuItem('example', {
     7967 *         text: 'Example plugin',
     7968 *         context: 'tools',
     7969 *         onclick: function() {
     7970 *             // Open window with a specific url
     7971 *             editor.windowManager.open({
     7972 *                 title: 'TinyMCE site',
     7973 *                 url: 'http://www.tinymce.com',
     7974 *                 width: 800,
     7975 *                 height: 600,
     7976 *                 buttons: [{
     7977 *                     text: 'Close',
     7978 *                     onclick: 'close'
     7979 *                 }]
     7980 *             });
     7981 *         }
     7982 *     });
     7983 * });
     7984 */
     7985
     7986// Included from: js/tinymce/classes/html/Node.js
     7987
     7988/**
     7989 * Node.js
     7990 *
     7991 * Copyright, Moxiecode Systems AB
     7992 * Released under LGPL License.
     7993 *
     7994 * License: http://www.tinymce.com/license
     7995 * Contributing: http://www.tinymce.com/contributing
     7996 */
     7997
     7998/**
     7999 * This class is a minimalistic implementation of a DOM like node used by the DomParser class.
     8000 *
     8001 * @example
     8002 * var node = new tinymce.html.Node('strong', 1);
     8003 * someRoot.append(node);
     8004 *
     8005 * @class tinymce.html.Node
     8006 * @version 3.4
     8007 */
     8008define("tinymce/html/Node", [], function() {
     8009        var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
     8010                '#text': 3,
     8011                '#comment': 8,
     8012                '#cdata': 4,
     8013                '#pi': 7,
     8014                '#doctype': 10,
     8015                '#document-fragment': 11
     8016        };
     8017
     8018        // Walks the tree left/right
     8019        function walk(node, root_node, prev) {
     8020                var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
     8021
     8022                // Walk into nodes if it has a start
     8023                if (node[startName]) {
     8024                        return node[startName];
     8025                }
     8026
     8027                // Return the sibling if it has one
     8028                if (node !== root_node) {
     8029                        sibling = node[siblingName];
     8030
     8031                        if (sibling) {
     8032                                return sibling;
     8033                        }
     8034
     8035                        // Walk up the parents to look for siblings
     8036                        for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
     8037                                sibling = parent[siblingName];
     8038
     8039                                if (sibling) {
     8040                                        return sibling;
     8041                                }
     8042                        }
     8043                }
     8044        }
     8045
     8046        /**
     8047         * Constructs a new Node instance.
     8048         *
     8049         * @constructor
     8050         * @method Node
     8051         * @param {String} name Name of the node type.
     8052         * @param {Number} type Numeric type representing the node.
     8053         */
     8054        function Node(name, type) {
     8055                this.name = name;
     8056                this.type = type;
     8057
     8058                if (type === 1) {
     8059                        this.attributes = [];
     8060                        this.attributes.map = {};
     8061                }
     8062        }
     8063
     8064        Node.prototype = {
     8065                /**
     8066                 * Replaces the current node with the specified one.
     8067                 *
     8068                 * @example
     8069                 * someNode.replace(someNewNode);
     8070                 *
     8071                 * @method replace
     8072                 * @param {tinymce.html.Node} node Node to replace the current node with.
     8073                 * @return {tinymce.html.Node} The old node that got replaced.
     8074                 */
     8075                replace: function(node) {
     8076                        var self = this;
     8077
     8078                        if (node.parent) {
     8079                                node.remove();
     8080                        }
     8081
     8082                        self.insert(node, self);
     8083                        self.remove();
     8084
     8085                        return self;
     8086                },
     8087
     8088                /**
     8089                 * Gets/sets or removes an attribute by name.
     8090                 *
     8091                 * @example
     8092                 * someNode.attr("name", "value"); // Sets an attribute
     8093                 * console.log(someNode.attr("name")); // Gets an attribute
     8094                 * someNode.attr("name", null); // Removes an attribute
     8095                 *
     8096                 * @method attr
     8097                 * @param {String} name Attribute name to set or get.
     8098                 * @param {String} value Optional value to set.
     8099                 * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
     8100                 */
     8101                attr: function(name, value) {
     8102                        var self = this, attrs, i, undef;
     8103
     8104                        if (typeof name !== "string") {
     8105                                for (i in name) {
     8106                                        self.attr(i, name[i]);
     8107                                }
     8108
     8109                                return self;
     8110                        }
     8111
     8112                        if ((attrs = self.attributes)) {
     8113                                if (value !== undef) {
     8114                                        // Remove attribute
     8115                                        if (value === null) {
     8116                                                if (name in attrs.map) {
     8117                                                        delete attrs.map[name];
     8118
     8119                                                        i = attrs.length;
     8120                                                        while (i--) {
     8121                                                                if (attrs[i].name === name) {
     8122                                                                        attrs = attrs.splice(i, 1);
     8123                                                                        return self;
     8124                                                                }
     8125                                                        }
     8126                                                }
     8127
     8128                                                return self;
     8129                                        }
     8130
     8131                                        // Set attribute
     8132                                        if (name in attrs.map) {
     8133                                                // Set attribute
     8134                                                i = attrs.length;
     8135                                                while (i--) {
     8136                                                        if (attrs[i].name === name) {
     8137                                                                attrs[i].value = value;
     8138                                                                break;
     8139                                                        }
     8140                                                }
     8141                                        } else {
     8142                                                attrs.push({name: name, value: value});
     8143                                        }
     8144
     8145                                        attrs.map[name] = value;
     8146
     8147                                        return self;
     8148                                } else {
     8149                                        return attrs.map[name];
     8150                                }
     8151                        }
     8152                },
     8153
     8154                /**
     8155                 * Does a shallow clones the node into a new node. It will also exclude id attributes since
     8156                 * there should only be one id per document.
     8157                 *
     8158                 * @example
     8159                 * var clonedNode = node.clone();
     8160                 *
     8161                 * @method clone
     8162                 * @return {tinymce.html.Node} New copy of the original node.
     8163                 */
     8164                clone: function() {
     8165                        var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
     8166
     8167                        // Clone element attributes
     8168                        if ((selfAttrs = self.attributes)) {
     8169                                cloneAttrs = [];
     8170                                cloneAttrs.map = {};
     8171
     8172                                for (i = 0, l = selfAttrs.length; i < l; i++) {
     8173                                        selfAttr = selfAttrs[i];
     8174
     8175                                        // Clone everything except id
     8176                                        if (selfAttr.name !== 'id') {
     8177                                                cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
     8178                                                cloneAttrs.map[selfAttr.name] = selfAttr.value;
     8179                                        }
     8180                                }
     8181
     8182                                clone.attributes = cloneAttrs;
     8183                        }
     8184
     8185                        clone.value = self.value;
     8186                        clone.shortEnded = self.shortEnded;
     8187
     8188                        return clone;
     8189                },
     8190
     8191                /**
     8192                 * Wraps the node in in another node.
     8193                 *
     8194                 * @example
     8195                 * node.wrap(wrapperNode);
     8196                 *
     8197                 * @method wrap
     8198                 */
     8199                wrap: function(wrapper) {
     8200                        var self = this;
     8201
     8202                        self.parent.insert(wrapper, self);
     8203                        wrapper.append(self);
     8204
     8205                        return self;
     8206                },
     8207
     8208                /**
     8209                 * Unwraps the node in other words it removes the node but keeps the children.
     8210                 *
     8211                 * @example
     8212                 * node.unwrap();
     8213                 *
     8214                 * @method unwrap
     8215                 */
     8216                unwrap: function() {
     8217                        var self = this, node, next;
     8218
     8219                        for (node = self.firstChild; node; ) {
     8220                                next = node.next;
     8221                                self.insert(node, self, true);
     8222                                node = next;
     8223                        }
     8224
     8225                        self.remove();
     8226                },
     8227
     8228                /**
     8229                 * Removes the node from it's parent.
     8230                 *
     8231                 * @example
     8232                 * node.remove();
     8233                 *
     8234                 * @method remove
     8235                 * @return {tinymce.html.Node} Current node that got removed.
     8236                 */
     8237                remove: function() {
     8238                        var self = this, parent = self.parent, next = self.next, prev = self.prev;
     8239
     8240                        if (parent) {
     8241                                if (parent.firstChild === self) {
     8242                                        parent.firstChild = next;
     8243
     8244                                        if (next) {
     8245                                                next.prev = null;
     8246                                        }
     8247                                } else {
     8248                                        prev.next = next;
     8249                                }
     8250
     8251                                if (parent.lastChild === self) {
     8252                                        parent.lastChild = prev;
     8253
     8254                                        if (prev) {
     8255                                                prev.next = null;
     8256                                        }
     8257                                } else {
     8258                                        next.prev = prev;
     8259                                }
     8260
     8261                                self.parent = self.next = self.prev = null;
     8262                        }
     8263
     8264                        return self;
     8265                },
     8266
     8267                /**
     8268                 * Appends a new node as a child of the current node.
     8269                 *
     8270                 * @example
     8271                 * node.append(someNode);
     8272                 *
     8273                 * @method append
     8274                 * @param {tinymce.html.Node} node Node to append as a child of the current one.
     8275                 * @return {tinymce.html.Node} The node that got appended.
     8276                 */
     8277                append: function(node) {
     8278                        var self = this, last;
     8279
     8280                        if (node.parent) {
     8281                                node.remove();
     8282                        }
     8283
     8284                        last = self.lastChild;
     8285                        if (last) {
     8286                                last.next = node;
     8287                                node.prev = last;
     8288                                self.lastChild = node;
     8289                        } else {
     8290                                self.lastChild = self.firstChild = node;
     8291                        }
     8292
     8293                        node.parent = self;
     8294
     8295                        return node;
     8296                },
     8297
     8298                /**
     8299                 * Inserts a node at a specific position as a child of the current node.
     8300                 *
     8301                 * @example
     8302                 * parentNode.insert(newChildNode, oldChildNode);
     8303                 *
     8304                 * @method insert
     8305                 * @param {tinymce.html.Node} node Node to insert as a child of the current node.
     8306                 * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
     8307                 * @param {Boolean} before Optional state to insert the node before the reference node.
     8308                 * @return {tinymce.html.Node} The node that got inserted.
     8309                 */
     8310                insert: function(node, ref_node, before) {
     8311                        var parent;
     8312
     8313                        if (node.parent) {
     8314                                node.remove();
     8315                        }
     8316
     8317                        parent = ref_node.parent || this;
     8318
     8319                        if (before) {
     8320                                if (ref_node === parent.firstChild) {
     8321                                        parent.firstChild = node;
     8322                                } else {
     8323                                        ref_node.prev.next = node;
     8324                                }
     8325
     8326                                node.prev = ref_node.prev;
     8327                                node.next = ref_node;
     8328                                ref_node.prev = node;
     8329                        } else {
     8330                                if (ref_node === parent.lastChild) {
     8331                                        parent.lastChild = node;
     8332                                } else {
     8333                                        ref_node.next.prev = node;
     8334                                }
     8335
     8336                                node.next = ref_node.next;
     8337                                node.prev = ref_node;
     8338                                ref_node.next = node;
     8339                        }
     8340
     8341                        node.parent = parent;
     8342
     8343                        return node;
     8344                },
     8345
     8346                /**
     8347                 * Get all children by name.
     8348                 *
     8349                 * @method getAll
     8350                 * @param {String} name Name of the child nodes to collect.
     8351                 * @return {Array} Array with child nodes matchin the specified name.
     8352                 */
     8353                getAll: function(name) {
     8354                        var self = this, node, collection = [];
     8355
     8356                        for (node = self.firstChild; node; node = walk(node, self)) {
     8357                                if (node.name === name) {
     8358                                        collection.push(node);
     8359                                }
     8360                        }
     8361
     8362                        return collection;
     8363                },
     8364
     8365                /**
     8366                 * Removes all children of the current node.
     8367                 *
     8368                 * @method empty
     8369                 * @return {tinymce.html.Node} The current node that got cleared.
     8370                 */
     8371                empty: function() {
     8372                        var self = this, nodes, i, node;
     8373
     8374                        // Remove all children
     8375                        if (self.firstChild) {
     8376                                nodes = [];
     8377
     8378                                // Collect the children
     8379                                for (node = self.firstChild; node; node = walk(node, self)) {
     8380                                        nodes.push(node);
     8381                                }
     8382
     8383                                // Remove the children
     8384                                i = nodes.length;
     8385                                while (i--) {
     8386                                        node = nodes[i];
     8387                                        node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
     8388                                }
     8389                        }
     8390
     8391                        self.firstChild = self.lastChild = null;
     8392
     8393                        return self;
     8394                },
     8395
     8396                /**
     8397                 * Returns true/false if the node is to be considered empty or not.
     8398                 *
     8399                 * @example
     8400                 * node.isEmpty({img: true});
     8401                 * @method isEmpty
     8402                 * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
     8403                 * @return {Boolean} true/false if the node is empty or not.
     8404                 */
     8405                isEmpty: function(elements) {
     8406                        var self = this, node = self.firstChild, i, name;
     8407
     8408                        if (node) {
     8409                                do {
     8410                                        if (node.type === 1) {
     8411                                                // Ignore bogus elements
     8412                                                if (node.attributes.map['data-mce-bogus']) {
     8413                                                        continue;
     8414                                                }
     8415
     8416                                                // Keep empty elements like <img />
     8417                                                if (elements[node.name]) {
     8418                                                        return false;
     8419                                                }
     8420
     8421                                                // Keep elements with data attributes or name attribute like <a name="1"></a>
     8422                                                i = node.attributes.length;
     8423                                                while (i--) {
     8424                                                        name = node.attributes[i].name;
     8425                                                        if (name === "name" || name.indexOf('data-mce-') === 0) {
     8426                                                                return false;
     8427                                                        }
     8428                                                }
     8429                                        }
     8430
     8431                                        // Keep comments
     8432                                        if (node.type === 8) {
     8433                                                return false;
     8434                                        }
     8435
     8436                                        // Keep non whitespace text nodes
     8437                                        if ((node.type === 3 && !whiteSpaceRegExp.test(node.value))) {
     8438                                                return false;
     8439                                        }
     8440                                } while ((node = walk(node, self)));
     8441                        }
     8442
     8443                        return true;
     8444                },
     8445
     8446                /**
     8447                 * Walks to the next or previous node and returns that node or null if it wasn't found.
     8448                 *
     8449                 * @method walk
     8450                 * @param {Boolean} prev Optional previous node state defaults to false.
     8451                 * @return {tinymce.html.Node} Node that is next to or previous of the current node.
     8452                 */
     8453                walk: function(prev) {
     8454                        return walk(this, null, prev);
     8455                }
     8456        };
     8457
     8458        /**
     8459         * Creates a node of a specific type.
     8460         *
     8461         * @static
     8462         * @method create
     8463         * @param {String} name Name of the node type to create for example "b" or "#text".
     8464         * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
     8465         */
     8466        Node.create = function(name, attrs) {
     8467                var node, attrName;
     8468
     8469                // Create node
     8470                node = new Node(name, typeLookup[name] || 1);
     8471
     8472                // Add attributes if needed
     8473                if (attrs) {
     8474                        for (attrName in attrs) {
     8475                                node.attr(attrName, attrs[attrName]);
     8476                        }
     8477                }
     8478
     8479                return node;
     8480        };
     8481
     8482        return Node;
     8483});
     8484
     8485// Included from: js/tinymce/classes/html/Schema.js
     8486
     8487/**
     8488 * Schema.js
     8489 *
     8490 * Copyright, Moxiecode Systems AB
     8491 * Released under LGPL License.
     8492 *
     8493 * License: http://www.tinymce.com/license
     8494 * Contributing: http://www.tinymce.com/contributing
     8495 */
     8496
     8497/**
     8498 * Schema validator class.
     8499 *
     8500 * @class tinymce.html.Schema
     8501 * @example
     8502 *  if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
     8503 *    alert('span is valid child of p.');
     8504 *
     8505 *  if (tinymce.activeEditor.schema.getElementRule('p'))
     8506 *    alert('P is a valid element.');
     8507 *
     8508 * @class tinymce.html.Schema
     8509 * @version 3.4
     8510 */
     8511define("tinymce/html/Schema", [
     8512        "tinymce/util/Tools"
     8513], function(Tools) {
     8514        var mapCache = {};
     8515        var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
     8516
     8517        function split(items, delim) {
     8518                return items ? items.split(delim || ' ') : [];
     8519        }
     8520
     8521        /**
     8522         * Builds a schema lookup table
     8523         *
     8524         * @private
     8525         * @param {String} type html4, html5 or html5-strict schema type.
     8526         * @return {Object} Schema lookup table.
     8527         */
     8528        function compileSchema(type) {
     8529                var schema = {}, globalAttributes, eventAttributes, blockContent;
     8530                var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
     8531
     8532                function add(name, attributes, children) {
     8533                        var ni, i, attributesOrder, args = arguments;
     8534
     8535                        function arrayToMap(array) {
     8536                                var map = {}, i, l;
     8537
     8538                                for (i = 0, l = array.length; i < l; i++) {
     8539                                        map[array[i]] = {};
     8540                                }
     8541
     8542                                return map;
     8543                        }
     8544
     8545                        children = children || [];
     8546                        attributes = attributes || "";
     8547
     8548                        if (typeof(children) === "string") {
     8549                                children = split(children);
     8550                        }
     8551
     8552                        // Split string children
     8553                        for (i = 3; i < args.length; i++) {
     8554                                if (typeof(args[i]) === "string") {
     8555                                        args[i] = split(args[i]);
     8556                                }
     8557
     8558                                children.push.apply(children, args[i]);
     8559                        }
     8560
     8561                        name = split(name);
     8562                        ni = name.length;
     8563                        while (ni--) {
     8564                                attributesOrder = [].concat(globalAttributes, split(attributes));
     8565                                schema[name[ni]] = {
     8566                                        attributes: arrayToMap(attributesOrder),
     8567                                        attributesOrder: attributesOrder,
     8568                                        children: arrayToMap(children)
     8569                                };
     8570                        }
     8571                }
     8572
     8573                function addAttrs(name, attributes) {
     8574                        var ni, schemaItem, i, l;
     8575
     8576                        name = split(name);
     8577                        ni = name.length;
     8578                        attributes = split(attributes);
     8579                        while (ni--) {
     8580                                schemaItem = schema[name[ni]];
     8581                                for (i = 0, l = attributes.length; i < l; i++) {
     8582                                        schemaItem.attributes[attributes[i]] = {};
     8583                                        schemaItem.attributesOrder.push(attributes[i]);
     8584                                }
     8585                        }
     8586                }
     8587
     8588                // Use cached schema
     8589                if (mapCache[type]) {
     8590                        return mapCache[type];
     8591                }
     8592
     8593                // Attributes present on all elements
     8594                globalAttributes = split("id accesskey class dir lang style tabindex title");
     8595
     8596                // Event attributes can be opt-in/opt-out
     8597                eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
     8598                                "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
     8599                                "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
     8600                                "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
     8601                                "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
     8602                                "onwaiting"
     8603                );
     8604
     8605                // Block content elements
     8606                blockContent = split(
     8607                        "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul"
     8608                );
     8609
     8610                // Phrasing content elements from the HTML5 spec (inline)
     8611                phrasingContent = split(
     8612                        "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
     8613                        "label map noscript object q s samp script select small span strong sub sup " +
     8614                        "textarea u var #text #comment"
     8615                );
     8616
     8617                // Add HTML5 items to globalAttributes, blockContent, phrasingContent
     8618                if (type != "html4") {
     8619                        globalAttributes.push.apply(globalAttributes, split("contenteditable contextmenu draggable dropzone " +
     8620                                "hidden spellcheck translate"));
     8621                        blockContent.push.apply(blockContent, split("article aside details dialog figure header footer hgroup section nav"));
     8622                        phrasingContent.push.apply(phrasingContent, split("audio canvas command datalist mark meter output progress time wbr " +
     8623                                "video ruby bdi keygen"));
     8624                }
     8625
     8626                // Add HTML4 elements unless it's html5-strict
     8627                if (type != "html5-strict") {
     8628                        globalAttributes.push("xml:lang");
     8629
     8630                        html4PhrasingContent = split("acronym applet basefont big font strike tt");
     8631                        phrasingContent.push.apply(phrasingContent, html4PhrasingContent);
     8632
     8633                        each(html4PhrasingContent, function(name) {
     8634                                add(name, "", phrasingContent);
     8635                        });
     8636
     8637                        html4BlockContent = split("center dir isindex noframes");
     8638                        blockContent.push.apply(blockContent, html4BlockContent);
     8639
     8640                        // Flow content elements from the HTML5 spec (block+inline)
     8641                        flowContent = [].concat(blockContent, phrasingContent);
     8642
     8643                        each(html4BlockContent, function(name) {
     8644                                add(name, "", flowContent);
     8645                        });
     8646                }
     8647
     8648                // Flow content elements from the HTML5 spec (block+inline)
     8649                flowContent = flowContent || [].concat(blockContent, phrasingContent);
     8650
     8651                // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
     8652                // Schema items <element name>, <specific attributes>, <children ..>
     8653                add("html", "manifest", "head body");
     8654                add("head", "", "base command link meta noscript script style title");
     8655                add("title hr noscript br");
     8656                add("base", "href target");
     8657                add("link", "href rel media hreflang type sizes hreflang");
     8658                add("meta", "name http-equiv content charset");
     8659                add("style", "media type scoped");
     8660                add("script", "src async defer type charset");
     8661                add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
     8662                                "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
     8663                                "onpopstate onresize onscroll onstorage onunload", flowContent);
     8664                add("address dt dd div caption", "", flowContent);
     8665                add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
     8666                add("blockquote", "cite", flowContent);
     8667                add("ol", "reversed start type", "li");
     8668                add("ul", "", "li");
     8669                add("li", "value", flowContent);
     8670                add("dl", "", "dt dd");
     8671                add("a", "href target rel media hreflang type", phrasingContent);
     8672                add("q", "cite", phrasingContent);
     8673                add("ins del", "cite datetime", flowContent);
     8674                add("img", "src alt usemap ismap width height");
     8675                add("iframe", "src name width height", flowContent);
     8676                add("embed", "src type width height");
     8677                add("object", "data type typemustmatch name usemap form width height", flowContent, "param");
     8678                add("param", "name value");
     8679                add("map", "name", flowContent, "area");
     8680                add("area", "alt coords shape href target rel media hreflang type");
     8681                add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
     8682                add("colgroup", "span", "col");
     8683                add("col", "span");
     8684                add("tbody thead tfoot", "", "tr");
     8685                add("tr", "", "td th");
     8686                add("td", "colspan rowspan headers", flowContent);
     8687                add("th", "colspan rowspan headers scope abbr", flowContent);
     8688                add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
     8689                add("fieldset", "disabled form name", flowContent, "legend");
     8690                add("label", "form for", phrasingContent);
     8691                add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
     8692                                "formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
     8693                );
     8694                add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
     8695                        type == "html4" ? flowContent : phrasingContent);
     8696                add("select", "disabled form multiple name required size", "option optgroup");
     8697                add("optgroup", "disabled label", "option");
     8698                add("option", "disabled label selected value");
     8699                add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
     8700                add("menu", "type label", flowContent, "li");
     8701                add("noscript", "", flowContent);
     8702
     8703                // Extend with HTML5 elements
     8704                if (type != "html4") {
     8705                        add("wbr");
     8706                        add("ruby", "", phrasingContent, "rt rp");
     8707                        add("figcaption", "", flowContent);
     8708                        add("mark rt rp summary bdi", "", phrasingContent);
     8709                        add("canvas", "width height", flowContent);
     8710                        add("video", "src crossorigin poster preload autoplay mediagroup loop " +
     8711                                "muted controls width height", flowContent, "track source");
     8712                        add("audio", "src crossorigin preload autoplay mediagroup loop muted controls", flowContent, "track source");
     8713                        add("source", "src type media");
     8714                        add("track", "kind src srclang label default");
     8715                        add("datalist", "", phrasingContent, "option");
     8716                        add("article section nav aside header footer", "", flowContent);
     8717                        add("hgroup", "", "h1 h2 h3 h4 h5 h6");
     8718                        add("figure", "", flowContent, "figcaption");
     8719                        add("time", "datetime", phrasingContent);
     8720                        add("dialog", "open", flowContent);
     8721                        add("command", "type label icon disabled checked radiogroup command");
     8722                        add("output", "for form name", phrasingContent);
     8723                        add("progress", "value max", phrasingContent);
     8724                        add("meter", "value min max low high optimum", phrasingContent);
     8725                        add("details", "open", flowContent, "summary");
     8726                        add("keygen", "autofocus challenge disabled form keytype name");
     8727                }
     8728
     8729                // Extend with HTML4 attributes unless it's html5-strict
     8730                if (type != "html5-strict") {
     8731                        addAttrs("script", "language xml:space");
     8732                        addAttrs("style", "xml:space");
     8733                        addAttrs("object", "declare classid codebase codetype archive standby align border hspace vspace");
     8734                        addAttrs("param", "valuetype type");
     8735                        addAttrs("a", "charset name rev shape coords");
     8736                        addAttrs("br", "clear");
     8737                        addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
     8738                        addAttrs("img", "name longdesc align border hspace vspace");
     8739                        addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
     8740                        addAttrs("font basefont", "size color face");
     8741                        addAttrs("input", "usemap align");
     8742                        addAttrs("select", "onchange");
     8743                        addAttrs("textarea");
     8744                        addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
     8745                        addAttrs("ul", "type compact");
     8746                        addAttrs("li", "type");
     8747                        addAttrs("ol dl menu dir", "compact");
     8748                        addAttrs("pre", "width xml:space");
     8749                        addAttrs("hr", "align noshade size width");
     8750                        addAttrs("isindex", "prompt");
     8751                        addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
     8752                        addAttrs("col", "width align char charoff valign");
     8753                        addAttrs("colgroup", "width align char charoff valign");
     8754                        addAttrs("thead", "align char charoff valign");
     8755                        addAttrs("tr", "align char charoff valign bgcolor");
     8756                        addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
     8757                        addAttrs("form", "accept");
     8758                        addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
     8759                        addAttrs("tfoot", "align char charoff valign");
     8760                        addAttrs("tbody", "align char charoff valign");
     8761                        addAttrs("area", "nohref");
     8762                        addAttrs("body", "background bgcolor text link vlink alink");
     8763                }
     8764
     8765                // Extend with HTML5 attributes unless it's html4
     8766                if (type != "html4") {
     8767                        addAttrs("input button select textarea", "autofocus");
     8768                        addAttrs("input textarea", "placeholder");
     8769                        addAttrs("a", "download");
     8770                        addAttrs("link script img", "crossorigin");
     8771                        addAttrs("iframe", "srcdoc sandbox seamless allowfullscreen");
     8772                }
     8773
     8774                // Special: iframe, ruby, video, audio, label
     8775
     8776                // Delete children of the same name from it's parent
     8777                // For example: form can't have a child of the name form
     8778                each(split('a form meter progress dfn'), function(name) {
     8779                        if (schema[name]) {
     8780                                delete schema[name].children[name];
     8781                        }
     8782                });
     8783
     8784                // Delete header, footer, sectioning and heading content descendants
     8785                /*each('dt th address', function(name) {
     8786                        delete schema[name].children[name];
     8787                });*/
     8788
     8789                // Caption can't have tables
     8790                delete schema.caption.children.table;
     8791
     8792                // TODO: LI:s can only have value if parent is OL
     8793
     8794                // TODO: Handle transparent elements
     8795                // a ins del canvas map
     8796
     8797                mapCache[type] = schema;
     8798
     8799                return schema;
     8800        }
     8801
     8802        /**
     8803         * Constructs a new Schema instance.
     8804         *
     8805         * @constructor
     8806         * @method Schema
     8807         * @param {Object} settings Name/value settings object.
     8808         */
     8809        return function(settings) {
     8810                var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
     8811                var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap;
     8812                var blockElementsMap, nonEmptyElementsMap, textBlockElementsMap, customElementsMap = {}, specialElements = {};
     8813
     8814                // Creates an lookup table map object for the specified option or the default value
     8815                function createLookupTable(option, default_value, extendWith) {
     8816                        var value = settings[option];
     8817
     8818                        if (!value) {
     8819                                // Get cached default map or make it if needed
     8820                                value = mapCache[option];
     8821
     8822                                if (!value) {
     8823                                        value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
     8824                                        value = extend(value, extendWith);
     8825
     8826                                        mapCache[option] = value;
     8827                                }
     8828                        } else {
     8829                                // Create custom map
     8830                                value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));
     8831                        }
     8832
     8833                        return value;
     8834                }
     8835
     8836                settings = settings || {};
     8837                schemaItems = compileSchema(settings.schema);
     8838
     8839                // Allow all elements and attributes if verify_html is set to false
     8840                if (settings.verify_html === false) {
     8841                        settings.valid_elements = '*[*]';
     8842                }
     8843
     8844                // Build styles list
     8845                if (settings.valid_styles) {
     8846                        validStyles = {};
     8847
     8848                        // Convert styles into a rule list
     8849                        each(settings.valid_styles, function(value, key) {
     8850                                validStyles[key] = explode(value);
     8851                        });
     8852                }
     8853
     8854                // Setup map objects
     8855                whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea video audio iframe object');
     8856                selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
     8857                shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
     8858                        'meta param embed source wbr track');
     8859                boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
     8860                        'noshade nowrap readonly selected autoplay loop controls');
     8861                nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object script', shortEndedElementsMap);
     8862                textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
     8863                                                'blockquote center dir fieldset header footer article section hgroup aside nav figure');
     8864                blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
     8865                                                'th tr td li ol ul caption dl dt dd noscript menu isindex samp option ' +
     8866                                                'datalist select optgroup', textBlockElementsMap);
     8867
     8868                each((settings.special || 'script noscript style textarea').split(' '), function(name) {
     8869                        specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
     8870                });
     8871
     8872                // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
     8873                function patternToRegExp(str) {
     8874                        return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
     8875                }
     8876
     8877                // Parses the specified valid_elements string and adds to the current rules
     8878                // This function is a bit hard to read since it's heavily optimized for speed
     8879                function addValidElements(valid_elements) {
     8880                        var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
     8881                                prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
     8882                                elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
     8883                                attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
     8884                                hasPatternsRegExp = /[*?+]/;
     8885
     8886                        if (valid_elements) {
     8887                                // Split valid elements into an array with rules
     8888                                valid_elements = split(valid_elements, ',');
     8889
     8890                                if (elements['@']) {
     8891                                        globalAttributes = elements['@'].attributes;
     8892                                        globalAttributesOrder = elements['@'].attributesOrder;
     8893                                }
     8894
     8895                                // Loop all rules
     8896                                for (ei = 0, el = valid_elements.length; ei < el; ei++) {
     8897                                        // Parse element rule
     8898                                        matches = elementRuleRegExp.exec(valid_elements[ei]);
     8899                                        if (matches) {
     8900                                                // Setup local names for matches
     8901                                                prefix = matches[1];
     8902                                                elementName = matches[2];
     8903                                                outputName = matches[3];
     8904                                                attrData = matches[5];
     8905
     8906                                                // Create new attributes and attributesOrder
     8907                                                attributes = {};
     8908                                                attributesOrder = [];
     8909
     8910                                                // Create the new element
     8911                                                element = {
     8912                                                        attributes: attributes,
     8913                                                        attributesOrder: attributesOrder
     8914                                                };
     8915
     8916                                                // Padd empty elements prefix
     8917                                                if (prefix === '#') {
     8918                                                        element.paddEmpty = true;
     8919                                                }
     8920
     8921                                                // Remove empty elements prefix
     8922                                                if (prefix === '-') {
     8923                                                        element.removeEmpty = true;
     8924                                                }
     8925
     8926                                                if (matches[4] === '!') {
     8927                                                        element.removeEmptyAttrs = true;
     8928                                                }
     8929
     8930                                                // Copy attributes from global rule into current rule
     8931                                                if (globalAttributes) {
     8932                                                        for (key in globalAttributes) {
     8933                                                                attributes[key] = globalAttributes[key];
     8934                                                        }
     8935
     8936                                                        attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
     8937                                                }
     8938
     8939                                                // Attributes defined
     8940                                                if (attrData) {
     8941                                                        attrData = split(attrData, '|');
     8942                                                        for (ai = 0, al = attrData.length; ai < al; ai++) {
     8943                                                                matches = attrRuleRegExp.exec(attrData[ai]);
     8944                                                                if (matches) {
     8945                                                                        attr = {};
     8946                                                                        attrType = matches[1];
     8947                                                                        attrName = matches[2].replace(/::/g, ':');
     8948                                                                        prefix = matches[3];
     8949                                                                        value = matches[4];
     8950
     8951                                                                        // Required
     8952                                                                        if (attrType === '!') {
     8953                                                                                element.attributesRequired = element.attributesRequired || [];
     8954                                                                                element.attributesRequired.push(attrName);
     8955                                                                                attr.required = true;
     8956                                                                        }
     8957
     8958                                                                        // Denied from global
     8959                                                                        if (attrType === '-') {
     8960                                                                                delete attributes[attrName];
     8961                                                                                attributesOrder.splice(inArray(attributesOrder, attrName), 1);
     8962                                                                                continue;
     8963                                                                        }
     8964
     8965                                                                        // Default value
     8966                                                                        if (prefix) {
     8967                                                                                // Default value
     8968                                                                                if (prefix === '=') {
     8969                                                                                        element.attributesDefault = element.attributesDefault || [];
     8970                                                                                        element.attributesDefault.push({name: attrName, value: value});
     8971                                                                                        attr.defaultValue = value;
     8972                                                                                }
     8973
     8974                                                                                // Forced value
     8975                                                                                if (prefix === ':') {
     8976                                                                                        element.attributesForced = element.attributesForced || [];
     8977                                                                                        element.attributesForced.push({name: attrName, value: value});
     8978                                                                                        attr.forcedValue = value;
     8979                                                                                }
     8980
     8981                                                                                // Required values
     8982                                                                                if (prefix === '<') {
     8983                                                                                        attr.validValues = makeMap(value, '?');
     8984                                                                                }
     8985                                                                        }
     8986
     8987                                                                        // Check for attribute patterns
     8988                                                                        if (hasPatternsRegExp.test(attrName)) {
     8989                                                                                element.attributePatterns = element.attributePatterns || [];
     8990                                                                                attr.pattern = patternToRegExp(attrName);
     8991                                                                                element.attributePatterns.push(attr);
     8992                                                                        } else {
     8993                                                                                // Add attribute to order list if it doesn't already exist
     8994                                                                                if (!attributes[attrName]) {
     8995                                                                                        attributesOrder.push(attrName);
     8996                                                                                }
     8997
     8998                                                                                attributes[attrName] = attr;
     8999                                                                        }
     9000                                                                }
     9001                                                        }
     9002                                                }
     9003
     9004                                                // Global rule, store away these for later usage
     9005                                                if (!globalAttributes && elementName == '@') {
     9006                                                        globalAttributes = attributes;
     9007                                                        globalAttributesOrder = attributesOrder;
     9008                                                }
     9009
     9010                                                // Handle substitute elements such as b/strong
     9011                                                if (outputName) {
     9012                                                        element.outputName = elementName;
     9013                                                        elements[outputName] = element;
     9014                                                }
     9015
     9016                                                // Add pattern or exact element
     9017                                                if (hasPatternsRegExp.test(elementName)) {
     9018                                                        element.pattern = patternToRegExp(elementName);
     9019                                                        patternElements.push(element);
     9020                                                } else {
     9021                                                        elements[elementName] = element;
     9022                                                }
     9023                                        }
     9024                                }
     9025                        }
     9026                }
     9027
     9028                function setValidElements(valid_elements) {
     9029                        elements = {};
     9030                        patternElements = [];
     9031
     9032                        addValidElements(valid_elements);
     9033
     9034                        each(schemaItems, function(element, name) {
     9035                                children[name] = element.children;
     9036                        });
     9037                }
     9038
     9039                // Adds custom non HTML elements to the schema
     9040                function addCustomElements(custom_elements) {
     9041                        var customElementRegExp = /^(~)?(.+)$/;
     9042
     9043                        if (custom_elements) {
     9044                                each(split(custom_elements, ','), function(rule) {
     9045                                        var matches = customElementRegExp.exec(rule),
     9046                                                inline = matches[1] === '~',
     9047                                                cloneName = inline ? 'span' : 'div',
     9048                                                name = matches[2];
     9049
     9050                                        children[name] = children[cloneName];
     9051                                        customElementsMap[name] = cloneName;
     9052
     9053                                        // If it's not marked as inline then add it to valid block elements
     9054                                        if (!inline) {
     9055                                                blockElementsMap[name.toUpperCase()] = {};
     9056                                                blockElementsMap[name] = {};
     9057                                        }
     9058
     9059                                        // Add elements clone if needed
     9060                                        if (!elements[name]) {
     9061                                                var customRule = elements[cloneName];
     9062
     9063                                                customRule = extend({}, customRule);
     9064                                                delete customRule.removeEmptyAttrs;
     9065                                                delete customRule.removeEmpty;
     9066
     9067                                                elements[name] = customRule;
     9068                                        }
     9069
     9070                                        // Add custom elements at span/div positions
     9071                                        each(children, function(element) {
     9072                                                if (element[cloneName]) {
     9073                                                        element[name] = element[cloneName];
     9074                                                }
     9075                                        });
     9076                                });
     9077                        }
     9078                }
     9079
     9080                // Adds valid children to the schema object
     9081                function addValidChildren(valid_children) {
     9082                        var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
     9083
     9084                        if (valid_children) {
     9085                                each(split(valid_children, ','), function(rule) {
     9086                                        var matches = childRuleRegExp.exec(rule), parent, prefix;
     9087
     9088                                        if (matches) {
     9089                                                prefix = matches[1];
     9090
     9091                                                // Add/remove items from default
     9092                                                if (prefix) {
     9093                                                        parent = children[matches[2]];
     9094                                                } else {
     9095                                                        parent = children[matches[2]] = {'#comment': {}};
     9096                                                }
     9097
     9098                                                parent = children[matches[2]];
     9099
     9100                                                each(split(matches[3], '|'), function(child) {
     9101                                                        if (prefix === '-') {
     9102                                                                delete parent[child];
     9103                                                        } else {
     9104                                                                parent[child] = {};
     9105                                                        }
     9106                                                });
     9107                                        }
     9108                                });
     9109                        }
     9110                }
     9111
     9112                function getElementRule(name) {
     9113                        var element = elements[name], i;
     9114
     9115                        // Exact match found
     9116                        if (element) {
     9117                                return element;
     9118                        }
     9119
     9120                        // No exact match then try the patterns
     9121                        i = patternElements.length;
     9122                        while (i--) {
     9123                                element = patternElements[i];
     9124
     9125                                if (element.pattern.test(name)) {
     9126                                        return element;
     9127                                }
     9128                        }
     9129                }
     9130
     9131                if (!settings.valid_elements) {
     9132                        // No valid elements defined then clone the elements from the schema spec
     9133                        each(schemaItems, function(element, name) {
     9134                                elements[name] = {
     9135                                        attributes: element.attributes,
     9136                                        attributesOrder: element.attributesOrder
     9137                                };
     9138
     9139                                children[name] = element.children;
     9140                        });
     9141
     9142                        // Switch these on HTML4
     9143                        if (settings.schema != "html5") {
     9144                                each(split('strong/b em/i'), function(item) {
     9145                                        item = split(item, '/');
     9146                                        elements[item[1]].outputName = item[0];
     9147                                });
     9148                        }
     9149
     9150                        // Add default alt attribute for images
     9151                        elements.img.attributesDefault = [{name: 'alt', value: ''}];
     9152
     9153                        // Remove these if they are empty by default
     9154                        each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
     9155                                if (elements[name]) {
     9156                                        elements[name].removeEmpty = true;
     9157                                }
     9158                        });
     9159
     9160                        // Padd these by default
     9161                        each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
     9162                                elements[name].paddEmpty = true;
     9163                        });
     9164
     9165                        // Remove these if they have no attributes
     9166                        each(split('span'), function(name) {
     9167                                elements[name].removeEmptyAttrs = true;
     9168                        });
     9169
     9170                        // Remove these by default
     9171                        // TODO: Reenable in 4.1
     9172                        /*each(split('script style'), function(name) {
     9173                                delete elements[name];
     9174                        });*/
     9175                } else {
     9176                        setValidElements(settings.valid_elements);
     9177                }
     9178
     9179                addCustomElements(settings.custom_elements);
     9180                addValidChildren(settings.valid_children);
     9181                addValidElements(settings.extended_valid_elements);
     9182
     9183                // Todo: Remove this when we fix list handling to be valid
     9184                addValidChildren('+ol[ul|ol],+ul[ul|ol]');
     9185
     9186                // Delete invalid elements
     9187                if (settings.invalid_elements) {
     9188                        each(explode(settings.invalid_elements), function(item) {
     9189                                if (elements[item]) {
     9190                                        delete elements[item];
     9191                                }
     9192                        });
     9193                }
     9194
     9195                // If the user didn't allow span only allow internal spans
     9196                if (!getElementRule('span')) {
     9197                        addValidElements('span[!data-mce-type|*]');
     9198                }
     9199
     9200                /**
     9201                 * Name/value map object with valid parents and children to those parents.
     9202                 *
     9203                 * @example
     9204                 * children = {
     9205                 *    div:{p:{}, h1:{}}
     9206                 * };
     9207                 * @field children
     9208                 * @type Object
     9209                 */
     9210                self.children = children;
     9211
     9212                /**
     9213                 * Name/value map object with valid styles for each element.
     9214                 *
     9215                 * @field styles
     9216                 * @type Object
     9217                 */
     9218                self.styles = validStyles;
     9219
     9220                /**
     9221                 * Returns a map with boolean attributes.
     9222                 *
     9223                 * @method getBoolAttrs
     9224                 * @return {Object} Name/value lookup map for boolean attributes.
     9225                 */
     9226                self.getBoolAttrs = function() {
     9227                        return boolAttrMap;
     9228                };
     9229
     9230                /**
     9231                 * Returns a map with block elements.
     9232                 *
     9233                 * @method getBlockElements
     9234                 * @return {Object} Name/value lookup map for block elements.
     9235                 */
     9236                self.getBlockElements = function() {
     9237                        return blockElementsMap;
     9238                };
     9239
     9240                /**
     9241                 * Returns a map with text block elements. Such as: p,h1-h6,div,address
     9242                 *
     9243                 * @method getTextBlockElements
     9244                 * @return {Object} Name/value lookup map for block elements.
     9245                 */
     9246                self.getTextBlockElements = function() {
     9247                        return textBlockElementsMap;
     9248                };
     9249
     9250                /**
     9251                 * Returns a map with short ended elements such as BR or IMG.
     9252                 *
     9253                 * @method getShortEndedElements
     9254                 * @return {Object} Name/value lookup map for short ended elements.
     9255                 */
     9256                self.getShortEndedElements = function() {
     9257                        return shortEndedElementsMap;
     9258                };
     9259
     9260                /**
     9261                 * Returns a map with self closing tags such as <li>.
     9262                 *
     9263                 * @method getSelfClosingElements
     9264                 * @return {Object} Name/value lookup map for self closing tags elements.
     9265                 */
     9266                self.getSelfClosingElements = function() {
     9267                        return selfClosingElementsMap;
     9268                };
     9269
     9270                /**
     9271                 * Returns a map with elements that should be treated as contents regardless if it has text
     9272                 * content in them or not such as TD, VIDEO or IMG.
     9273                 *
     9274                 * @method getNonEmptyElements
     9275                 * @return {Object} Name/value lookup map for non empty elements.
     9276                 */
     9277                self.getNonEmptyElements = function() {
     9278                        return nonEmptyElementsMap;
     9279                };
     9280
     9281                /**
     9282                 * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
     9283                 *
     9284                 * @method getWhiteSpaceElements
     9285                 * @return {Object} Name/value lookup map for white space elements.
     9286                 */
     9287                self.getWhiteSpaceElements = function() {
     9288                        return whiteSpaceElementsMap;
     9289                };
     9290
     9291                /**
     9292                 * Returns a map with special elements. These are elements that needs to be parsed
     9293                 * in a special way such as script, style, textarea etc. The map object values
     9294                 * are regexps used to find the end of the element.
     9295                 *
     9296                 * @method getSpecialElements
     9297                 * @return {Object} Name/value lookup map for special elements.
     9298                 */
     9299                self.getSpecialElements = function() {
     9300                        return specialElements;
     9301                };
     9302
     9303                /**
     9304                 * Returns true/false if the specified element and it's child is valid or not
     9305                 * according to the schema.
     9306                 *
     9307                 * @method isValidChild
     9308                 * @param {String} name Element name to check for.
     9309                 * @param {String} child Element child to verify.
     9310                 * @return {Boolean} True/false if the element is a valid child of the specified parent.
     9311                 */
     9312                self.isValidChild = function(name, child) {
     9313                        var parent = children[name];
     9314
     9315                        return !!(parent && parent[child]);
     9316                };
     9317
     9318                /**
     9319                 * Returns true/false if the specified element name and optional attribute is
     9320                 * valid according to the schema.
     9321                 *
     9322                 * @method isValid
     9323                 * @param {String} name Name of element to check.
     9324                 * @param {String} attr Optional attribute name to check for.
     9325                 * @return {Boolean} True/false if the element and attribute is valid.
     9326                 */
     9327                self.isValid = function(name, attr) {
     9328                        var attrPatterns, i, rule = getElementRule(name);
     9329
     9330                        // Check if it's a valid element
     9331                        if (rule) {
     9332                                if (attr) {
     9333                                        // Check if attribute name exists
     9334                                        if (rule.attributes[attr]) {
     9335                                                return true;
     9336                                        }
     9337
     9338                                        // Check if attribute matches a regexp pattern
     9339                                        attrPatterns = rule.attributePatterns;
     9340                                        if (attrPatterns) {
     9341                                                i = attrPatterns.length;
     9342                                                while (i--) {
     9343                                                        if (attrPatterns[i].pattern.test(name)) {
     9344                                                                return true;
     9345                                                        }
     9346                                                }
     9347                                        }
     9348                                } else {
     9349                                        return true;
     9350                                }
     9351                        }
     9352
     9353                        // No match
     9354                        return false;
     9355                };
     9356
     9357                /**
     9358                 * Returns true/false if the specified element is valid or not
     9359                 * according to the schema.
     9360                 *
     9361                 * @method getElementRule
     9362                 * @param {String} name Element name to check for.
     9363                 * @return {Object} Element object or undefined if the element isn't valid.
     9364                 */
     9365                self.getElementRule = getElementRule;
     9366
     9367                /**
     9368                 * Returns an map object of all custom elements.
     9369                 *
     9370                 * @method getCustomElements
     9371                 * @return {Object} Name/value map object of all custom elements.
     9372                 */
     9373                self.getCustomElements = function() {
     9374                        return customElementsMap;
     9375                };
     9376
     9377                /**
     9378                 * Parses a valid elements string and adds it to the schema. The valid elements
     9379                  format is for example "element[attr=default|otherattr]".
     9380                 * Existing rules will be replaced with the ones specified, so this extends the schema.
     9381                 *
     9382                 * @method addValidElements
     9383                 * @param {String} valid_elements String in the valid elements format to be parsed.
     9384                 */
     9385                self.addValidElements = addValidElements;
     9386
     9387                /**
     9388                 * Parses a valid elements string and sets it to the schema. The valid elements
     9389                 * format is for example "element[attr=default|otherattr]".
     9390                 * Existing rules will be replaced with the ones specified, so this extends the schema.
     9391                 *
     9392                 * @method setValidElements
     9393                 * @param {String} valid_elements String in the valid elements format to be parsed.
     9394                 */
     9395                self.setValidElements = setValidElements;
     9396
     9397                /**
     9398                 * Adds custom non HTML elements to the schema.
     9399                 *
     9400                 * @method addCustomElements
     9401                 * @param {String} custom_elements Comma separated list of custom elements to add.
     9402                 */
     9403                self.addCustomElements = addCustomElements;
     9404
     9405                /**
     9406                 * Parses a valid children string and adds them to the schema structure. The valid children
     9407                 * format is for example: "element[child1|child2]".
     9408                 *
     9409                 * @method addValidChildren
     9410                 * @param {String} valid_children Valid children elements string to parse
     9411                 */
     9412                self.addValidChildren = addValidChildren;
     9413
     9414                self.elements = elements;
     9415        };
     9416});
     9417
     9418// Included from: js/tinymce/classes/html/SaxParser.js
     9419
     9420/**
     9421 * SaxParser.js
     9422 *
     9423 * Copyright, Moxiecode Systems AB
     9424 * Released under LGPL License.
     9425 *
     9426 * License: http://www.tinymce.com/license
     9427 * Contributing: http://www.tinymce.com/contributing
     9428 */
     9429
     9430/**
     9431 * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
     9432 * always execute the events in the right order for tag soup code like <b><p></b></p>. It will also remove elements
     9433 * and attributes that doesn't fit the schema if the validate setting is enabled.
     9434 *
     9435 * @example
     9436 * var parser = new tinymce.html.SaxParser({
     9437 *     validate: true,
     9438 *
     9439 *     comment: function(text) {
     9440 *         console.log('Comment:', text);
     9441 *     },
     9442 *
     9443 *     cdata: function(text) {
     9444 *         console.log('CDATA:', text);
     9445 *     },
     9446 *
     9447 *     text: function(text, raw) {
     9448 *         console.log('Text:', text, 'Raw:', raw);
     9449 *     },
     9450 *
     9451 *     start: function(name, attrs, empty) {
     9452 *         console.log('Start:', name, attrs, empty);
     9453 *     },
     9454 *
     9455 *     end: function(name) {
     9456 *         console.log('End:', name);
     9457 *     },
     9458 *
     9459 *     pi: function(name, text) {
     9460 *         console.log('PI:', name, text);
     9461 *     },
     9462 *
     9463 *     doctype: function(text) {
     9464 *         console.log('DocType:', text);
     9465 *     }
     9466 * }, schema);
     9467 * @class tinymce.html.SaxParser
     9468 * @version 3.4
     9469 */
     9470define("tinymce/html/SaxParser", [
     9471        "tinymce/html/Schema",
     9472        "tinymce/html/Entities",
     9473        "tinymce/util/Tools"
     9474], function(Schema, Entities, Tools) {
     9475        var each = Tools.each;
     9476
     9477        /**
     9478         * Constructs a new SaxParser instance.
     9479         *
     9480         * @constructor
     9481         * @method SaxParser
     9482         * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
     9483         * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
     9484         */
     9485        return function(settings, schema) {
     9486                var self = this, noop = function() {};
     9487
     9488                settings = settings || {};
     9489                self.schema = schema = schema || new Schema();
     9490
     9491                if (settings.fix_self_closing !== false) {
     9492                        settings.fix_self_closing = true;
     9493                }
     9494
     9495                // Add handler functions from settings and setup default handlers
     9496                each('comment cdata text start end pi doctype'.split(' '), function(name) {
     9497                        if (name) {
     9498                                self[name] = settings[name] || noop;
     9499                        }
     9500                });
     9501
     9502                /**
     9503                 * Parses the specified HTML string and executes the callbacks for each item it finds.
     9504                 *
     9505                 * @example
     9506                 * new SaxParser({...}).parse('<b>text</b>');
     9507                 * @method parse
     9508                 * @param {String} html Html string to sax parse.
     9509                 */
     9510                self.parse = function(html) {
     9511                        var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
     9512                        var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
     9513                        var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
     9514                        var attributesRequired, attributesDefault, attributesForced;
     9515                        var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
     9516                        var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href');
     9517                        var scriptUriRegExp = /(java|vb)script:/i;
     9518
     9519                        function processEndTag(name) {
     9520                                var pos, i;
     9521
     9522                                // Find position of parent of the same type
     9523                                pos = stack.length;
     9524                                while (pos--) {
     9525                                        if (stack[pos].name === name) {
     9526                                                break;
     9527                                        }
     9528                                }
     9529
     9530                                // Found parent
     9531                                if (pos >= 0) {
     9532                                        // Close all the open elements
     9533                                        for (i = stack.length - 1; i >= pos; i--) {
     9534                                                name = stack[i];
     9535
     9536                                                if (name.valid) {
     9537                                                        self.end(name.name);
     9538                                                }
     9539                                        }
     9540
     9541                                        // Remove the open elements from the stack
     9542                                        stack.length = pos;
     9543                                }
     9544                        }
     9545
     9546                        function parseAttribute(match, name, value, val2, val3) {
     9547                                var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
     9548
     9549                                name = name.toLowerCase();
     9550                                value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
     9551
     9552                                // Validate name and value pass through all data- attributes
     9553                                if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
     9554                                        attrRule = validAttributesMap[name];
     9555
     9556                                        // Find rule by pattern matching
     9557                                        if (!attrRule && validAttributePatterns) {
     9558                                                i = validAttributePatterns.length;
     9559                                                while (i--) {
     9560                                                        attrRule = validAttributePatterns[i];
     9561                                                        if (attrRule.pattern.test(name)) {
     9562                                                                break;
     9563                                                        }
     9564                                                }
     9565
     9566                                                // No rule matched
     9567                                                if (i === -1) {
     9568                                                        attrRule = null;
     9569                                                }
     9570                                        }
     9571
     9572                                        // No attribute rule found
     9573                                        if (!attrRule) {
     9574                                                return;
     9575                                        }
     9576
     9577                                        // Validate value
     9578                                        if (attrRule.validValues && !(value in attrRule.validValues)) {
     9579                                                return;
     9580                                        }
     9581                                }
     9582
     9583                                // Block any javascript: urls
     9584                                if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
     9585                                        var uri = value.replace(trimRegExp, '');
     9586
     9587                                        try {
     9588                                                // Might throw malformed URI sequence
     9589                                                uri = decodeURIComponent(uri);
     9590                                                if (scriptUriRegExp.test(uri)) {
     9591                                                        return;
     9592                                                }
     9593                                        } catch (ex) {
     9594                                                // Fallback to non UTF-8 decoder
     9595                                                uri = unescape(uri);
     9596                                                if (scriptUriRegExp.test(uri)) {
     9597                                                        return;
     9598                                                }
     9599                                        }
     9600                                }
     9601
     9602                                // Add attribute to list and map
     9603                                attrList.map[name] = value;
     9604                                attrList.push({
     9605                                        name: name,
     9606                                        value: value
     9607                                });
     9608                        }
     9609
     9610                        // Precompile RegExps and map objects
     9611                        tokenRegExp = new RegExp('<(?:' +
     9612                                '(?:!--([\\w\\W]*?)-->)|' + // Comment
     9613                                '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
     9614                                '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
     9615                                '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
     9616                                '(?:\\/([^>]+)>)|' + // End element
     9617                                '(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
     9618                        ')', 'g');
     9619
     9620                        attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
     9621
     9622                        // Setup lookup tables for empty elements and boolean attributes
     9623                        shortEndedElements = schema.getShortEndedElements();
     9624                        selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
     9625                        fillAttrsMap = schema.getBoolAttrs();
     9626                        validate = settings.validate;
     9627                        removeInternalElements = settings.remove_internals;
     9628                        fixSelfClosing = settings.fix_self_closing;
     9629                        specialElements = schema.getSpecialElements();
     9630
     9631                        while ((matches = tokenRegExp.exec(html))) {
     9632                                // Text
     9633                                if (index < matches.index) {
     9634                                        self.text(decode(html.substr(index, matches.index - index)));
     9635                                }
     9636
     9637                                if ((value = matches[6])) { // End element
     9638                                        value = value.toLowerCase();
     9639
     9640                                        // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
     9641                                        if (value.charAt(0) === ':') {
     9642                                                value = value.substr(1);
     9643                                        }
     9644
     9645                                        processEndTag(value);
     9646                                } else if ((value = matches[7])) { // Start element
     9647                                        value = value.toLowerCase();
     9648
     9649                                        // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
     9650                                        if (value.charAt(0) === ':') {
     9651                                                value = value.substr(1);
     9652                                        }
     9653
     9654                                        isShortEnded = value in shortEndedElements;
     9655
     9656                                        // Is self closing tag for example an <li> after an open <li>
     9657                                        if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
     9658                                                processEndTag(value);
     9659                                        }
     9660
     9661                                        // Validate element
     9662            &nb