Make WordPress Core

Changeset 26876


Ignore:
Timestamp:
12/28/2013 11:52:04 PM (11 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                    if (!validate || (elementRule = schema.getElementRule(value))) {
     9663                        isValidElement = true;
     9664
     9665                        // Grab attributes map and patters when validation is enabled
     9666                        if (validate) {
     9667                            validAttributesMap = elementRule.attributes;
     9668                            validAttributePatterns = elementRule.attributePatterns;
     9669                        }
     9670
     9671                        // Parse attributes
     9672                        if ((attribsValue = matches[8])) {
     9673                            isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
     9674
     9675                            // If the element has internal attributes then remove it if we are told to do so
     9676                            if (isInternalElement && removeInternalElements) {
     9677                                isValidElement = false;
     9678                            }
     9679
     9680                            attrList = [];
     9681                            attrList.map = {};
     9682
     9683                            attribsValue.replace(attrRegExp, parseAttribute);
     9684                        } else {
     9685                            attrList = [];
     9686                            attrList.map = {};
     9687                        }
     9688
     9689                        // Process attributes if validation is enabled
     9690                        if (validate && !isInternalElement) {
     9691                            attributesRequired = elementRule.attributesRequired;
     9692                            attributesDefault = elementRule.attributesDefault;
     9693                            attributesForced = elementRule.attributesForced;
     9694                            anyAttributesRequired = elementRule.removeEmptyAttrs;
     9695
     9696                            // Check if any attribute exists
     9697                            if (anyAttributesRequired && !attrList.length) {
     9698                                isValidElement = false;
     9699                            }
     9700
     9701                            // Handle forced attributes
     9702                            if (attributesForced) {
     9703                                i = attributesForced.length;
     9704                                while (i--) {
     9705                                    attr = attributesForced[i];
     9706                                    name = attr.name;
     9707                                    attrValue = attr.value;
     9708
     9709                                    if (attrValue === '{$uid}') {
     9710                                        attrValue = 'mce_' + idCount++;
     9711                                    }
     9712
     9713                                    attrList.map[name] = attrValue;
     9714                                    attrList.push({name: name, value: attrValue});
     9715                                }
     9716                            }
     9717
     9718                            // Handle default attributes
     9719                            if (attributesDefault) {
     9720                                i = attributesDefault.length;
     9721                                while (i--) {
     9722                                    attr = attributesDefault[i];
     9723                                    name = attr.name;
     9724
     9725                                    if (!(name in attrList.map)) {
     9726                                        attrValue = attr.value;
     9727
     9728                                        if (attrValue === '{$uid}') {
     9729                                            attrValue = 'mce_' + idCount++;
     9730                                        }
     9731
     9732                                        attrList.map[name] = attrValue;
     9733                                        attrList.push({name: name, value: attrValue});
     9734                                    }
     9735                                }
     9736                            }
     9737
     9738                            // Handle required attributes
     9739                            if (attributesRequired) {
     9740                                i = attributesRequired.length;
     9741                                while (i--) {
     9742                                    if (attributesRequired[i] in attrList.map) {
     9743                                        break;
     9744                                    }
     9745                                }
     9746
     9747                                // None of the required attributes where found
     9748                                if (i === -1) {
     9749                                    isValidElement = false;
     9750                                }
     9751                            }
     9752
     9753                            // Invalidate element if it's marked as bogus
     9754                            if (attrList.map['data-mce-bogus']) {
     9755                                isValidElement = false;
     9756                            }
     9757                        }
     9758
     9759                        if (isValidElement) {
     9760                            self.start(value, attrList, isShortEnded);
     9761                        }
     9762                    } else {
     9763                        isValidElement = false;
     9764                    }
     9765
     9766                    // Treat script, noscript and style a bit different since they may include code that looks like elements
     9767                    if ((endRegExp = specialElements[value])) {
     9768                        endRegExp.lastIndex = index = matches.index + matches[0].length;
     9769
     9770                        if ((matches = endRegExp.exec(html))) {
     9771                            if (isValidElement) {
     9772                                text = html.substr(index, matches.index - index);
     9773                            }
     9774
     9775                            index = matches.index + matches[0].length;
     9776                        } else {
     9777                            text = html.substr(index);
     9778                            index = html.length;
     9779                        }
     9780
     9781                        if (isValidElement) {
     9782                            if (text.length > 0) {
     9783                                self.text(text, true);
     9784                            }
     9785
     9786                            self.end(value);
     9787                        }
     9788
     9789                        tokenRegExp.lastIndex = index;
     9790                        continue;
     9791                    }
     9792
     9793                    // Push value on to stack
     9794                    if (!isShortEnded) {
     9795                        if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
     9796                            stack.push({name: value, valid: isValidElement});
     9797                        } else if (isValidElement) {
     9798                            self.end(value);
     9799                        }
     9800                    }
     9801                } else if ((value = matches[1])) { // Comment
     9802                    // Padd comment value to avoid browsers from parsing invalid comments as HTML
     9803                    if (value.charAt(0) === '>') {
     9804                        value = ' ' + value;
     9805                    }
     9806
     9807                    if (!settings.allow_conditional_comments && value.substr(0, 3) === '[if') {
     9808                        value = ' ' + value;
     9809                    }
     9810
     9811                    self.comment(value);
     9812                } else if ((value = matches[2])) { // CDATA
     9813                    self.cdata(value);
     9814                } else if ((value = matches[3])) { // DOCTYPE
     9815                    self.doctype(value);
     9816                } else if ((value = matches[4])) { // PI
     9817                    self.pi(value, matches[5]);
     9818                }
     9819
     9820                index = matches.index + matches[0].length;
     9821            }
     9822
     9823            // Text
     9824            if (index < html.length) {
     9825                self.text(decode(html.substr(index)));
     9826            }
     9827
     9828            // Close any open elements
     9829            for (i = stack.length - 1; i >= 0; i--) {
     9830                value = stack[i];
     9831
     9832                if (value.valid) {
     9833                    self.end(value.name);
     9834                }
     9835            }
     9836        };
     9837    };
     9838});
     9839
     9840// Included from: js/tinymce/classes/html/DomParser.js
     9841
     9842/**
     9843 * DomParser.js
     9844 *
     9845 * Copyright, Moxiecode Systems AB
     9846 * Released under LGPL License.
     9847 *
     9848 * License: http://www.tinymce.com/license
     9849 * Contributing: http://www.tinymce.com/contributing
     9850 */
     9851
     9852/**
     9853 * This class parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
     9854 * sure that the node tree is valid according to the specified schema.
     9855 * So for example: <p>a<p>b</p>c</p> will become <p>a</p><p>b</p><p>c</p>
     9856 *
     9857 * @example
     9858 * var parser = new tinymce.html.DomParser({validate: true}, schema);
     9859 * var rootNode = parser.parse('<h1>content</h1>');
     9860 *
     9861 * @class tinymce.html.DomParser
     9862 * @version 3.4
     9863 */
     9864define("tinymce/html/DomParser", [
     9865    "tinymce/html/Node",
     9866    "tinymce/html/Schema",
     9867    "tinymce/html/SaxParser",
     9868    "tinymce/util/Tools"
     9869], function(Node, Schema, SaxParser, Tools) {
     9870    var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
     9871
     9872    /**
     9873     * Constructs a new DomParser instance.
     9874     *
     9875     * @constructor
     9876     * @method DomParser
     9877     * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
     9878     * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
     9879     */
     9880    return function(settings, schema) {
     9881        var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
     9882
     9883        settings = settings || {};
     9884        settings.validate = "validate" in settings ? settings.validate : true;
     9885        settings.root_name = settings.root_name || 'body';
     9886        self.schema = schema = schema || new Schema();
     9887
     9888        function fixInvalidChildren(nodes) {
     9889            var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
     9890            var nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
     9891
     9892            nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
     9893            nonEmptyElements = schema.getNonEmptyElements();
     9894            textBlockElements = schema.getTextBlockElements();
     9895
     9896            for (ni = 0; ni < nodes.length; ni++) {
     9897                node = nodes[ni];
     9898
     9899                // Already removed or fixed
     9900                if (!node.parent || node.fixed) {
     9901                    continue;
     9902                }
     9903
     9904                // If the invalid element is a text block and the text block is within a parent LI element
     9905                // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
     9906                if (textBlockElements[node.name] && node.parent.name == 'li') {
     9907                    // Move sibling text blocks after LI element
     9908                    sibling = node.next;
     9909                    while (sibling) {
     9910                        if (textBlockElements[sibling.name]) {
     9911                            sibling.name = 'li';
     9912                            sibling.fixed = true;
     9913                            node.parent.insert(sibling, node.parent);
     9914                        } else {
     9915                            break;
     9916                        }
     9917
     9918                        sibling = sibling.next;
     9919                    }
     9920
     9921                    // Unwrap current text block
     9922                    node.unwrap(node);
     9923                    continue;
     9924                }
     9925
     9926                // Get list of all parent nodes until we find a valid parent to stick the child into
     9927                parents = [node];
     9928                for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
     9929                    !nonSplitableElements[parent.name]; parent = parent.parent) {
     9930                    parents.push(parent);
     9931                }
     9932
     9933                // Found a suitable parent
     9934                if (parent && parents.length > 1) {
     9935                    // Reverse the array since it makes looping easier
     9936                    parents.reverse();
     9937
     9938                    // Clone the related parent and insert that after the moved node
     9939                    newParent = currentNode = self.filterNode(parents[0].clone());
     9940
     9941                    // Start cloning and moving children on the left side of the target node
     9942                    for (i = 0; i < parents.length - 1; i++) {
     9943                        if (schema.isValidChild(currentNode.name, parents[i].name)) {
     9944                            tempNode = self.filterNode(parents[i].clone());
     9945                            currentNode.append(tempNode);
     9946                        } else {
     9947                            tempNode = currentNode;
     9948                        }
     9949
     9950                        for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1]; ) {
     9951                            nextNode = childNode.next;
     9952                            tempNode.append(childNode);
     9953                            childNode = nextNode;
     9954                        }
     9955
     9956                        currentNode = tempNode;
     9957                    }
     9958
     9959                    if (!newParent.isEmpty(nonEmptyElements)) {
     9960                        parent.insert(newParent, parents[0], true);
     9961                        parent.insert(node, newParent);
     9962                    } else {
     9963                        parent.insert(node, parents[0], true);
     9964                    }
     9965
     9966                    // Check if the element is empty by looking through it's contents and special treatment for <p><br /></p>
     9967                    parent = parents[0];
     9968                    if (parent.isEmpty(nonEmptyElements) || parent.firstChild === parent.lastChild && parent.firstChild.name === 'br') {
     9969                        parent.empty().remove();
     9970                    }
     9971                } else if (node.parent) {
     9972                    // If it's an LI try to find a UL/OL for it or wrap it
     9973                    if (node.name === 'li') {
     9974                        sibling = node.prev;
     9975                        if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
     9976                            sibling.append(node);
     9977                            continue;
     9978                        }
     9979
     9980                        sibling = node.next;
     9981                        if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
     9982                            sibling.insert(node, sibling.firstChild, true);
     9983                            continue;
     9984                        }
     9985
     9986                        node.wrap(self.filterNode(new Node('ul', 1)));
     9987                        continue;
     9988                    }
     9989
     9990                    // Try wrapping the element in a DIV
     9991                    if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
     9992                        node.wrap(self.filterNode(new Node('div', 1)));
     9993                    } else {
     9994                        // We failed wrapping it, then remove or unwrap it
     9995                        if (node.name === 'style' || node.name === 'script') {
     9996                            node.empty().remove();
     9997                        } else {
     9998                            node.unwrap();
     9999                        }
     10000                    }
     10001                }
     10002            }
     10003        }
     10004
     10005        /**
     10006         * Runs the specified node though the element and attributes filters.
     10007         *
     10008         * @method filterNode
     10009         * @param {tinymce.html.Node} Node the node to run filters on.
     10010         * @return {tinymce.html.Node} The passed in node.
     10011         */
     10012        self.filterNode = function(node) {
     10013            var i, name, list;
     10014
     10015            // Run element filters
     10016            if (name in nodeFilters) {
     10017                list = matchedNodes[name];
     10018
     10019                if (list) {
     10020                    list.push(node);
     10021                } else {
     10022                    matchedNodes[name] = [node];
     10023                }
     10024            }
     10025
     10026            // Run attribute filters
     10027            i = attributeFilters.length;
     10028            while (i--) {
     10029                name = attributeFilters[i].name;
     10030
     10031                if (name in node.attributes.map) {
     10032                    list = matchedAttributes[name];
     10033
     10034                    if (list) {
     10035                        list.push(node);
     10036                    } else {
     10037                        matchedAttributes[name] = [node];
     10038                    }
     10039                }
     10040            }
     10041
     10042            return node;
     10043        };
     10044
     10045        /**
     10046         * Adds a node filter function to the parser, the parser will collect the specified nodes by name
     10047         * and then execute the callback ones it has finished parsing the document.
     10048         *
     10049         * @example
     10050         * parser.addNodeFilter('p,h1', function(nodes, name) {
     10051         *      for (var i = 0; i < nodes.length; i++) {
     10052         *          console.log(nodes[i].name);
     10053         *      }
     10054         * });
     10055         * @method addNodeFilter
     10056         * @method {String} name Comma separated list of nodes to collect.
     10057         * @param {function} callback Callback function to execute once it has collected nodes.
     10058         */
     10059        self.addNodeFilter = function(name, callback) {
     10060            each(explode(name), function(name) {
     10061                var list = nodeFilters[name];
     10062
     10063                if (!list) {
     10064                    nodeFilters[name] = list = [];
     10065                }
     10066
     10067                list.push(callback);
     10068            });
     10069        };
     10070
     10071        /**
     10072         * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
     10073         * and then execute the callback ones it has finished parsing the document.
     10074         *
     10075         * @example
     10076         * parser.addAttributeFilter('src,href', function(nodes, name) {
     10077         *      for (var i = 0; i < nodes.length; i++) {
     10078         *          console.log(nodes[i].name);
     10079         *      }
     10080         * });
     10081         * @method addAttributeFilter
     10082         * @method {String} name Comma separated list of nodes to collect.
     10083         * @param {function} callback Callback function to execute once it has collected nodes.
     10084         */
     10085        self.addAttributeFilter = function(name, callback) {
     10086            each(explode(name), function(name) {
     10087                var i;
     10088
     10089                for (i = 0; i < attributeFilters.length; i++) {
     10090                    if (attributeFilters[i].name === name) {
     10091                        attributeFilters[i].callbacks.push(callback);
     10092                        return;
     10093                    }
     10094                }
     10095
     10096                attributeFilters.push({name: name, callbacks: [callback]});
     10097            });
     10098        };
     10099
     10100        /**
     10101         * Parses the specified HTML string into a DOM like node tree and returns the result.
     10102         *
     10103         * @example
     10104         * var rootNode = new DomParser({...}).parse('<b>text</b>');
     10105         * @method parse
     10106         * @param {String} html Html string to sax parse.
     10107         * @param {Object} args Optional args object that gets passed to all filter functions.
     10108         * @return {tinymce.html.Node} Root node containing the tree.
     10109         */
     10110        self.parse = function(html, args) {
     10111            var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
     10112            var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
     10113            var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
     10114            var children, nonEmptyElements, rootBlockName;
     10115
     10116            args = args || {};
     10117            matchedNodes = {};
     10118            matchedAttributes = {};
     10119            blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
     10120            nonEmptyElements = schema.getNonEmptyElements();
     10121            children = schema.children;
     10122            validate = settings.validate;
     10123            rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
     10124
     10125            whiteSpaceElements = schema.getWhiteSpaceElements();
     10126            startWhiteSpaceRegExp = /^[ \t\r\n]+/;
     10127            endWhiteSpaceRegExp = /[ \t\r\n]+$/;
     10128            allWhiteSpaceRegExp = /[ \t\r\n]+/g;
     10129            isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
     10130
     10131            function addRootBlocks() {
     10132                var node = rootNode.firstChild, next, rootBlockNode;
     10133
     10134                // Removes whitespace at beginning and end of block so:
     10135                // <p> x </p> -> <p>x</p>
     10136                function trim(rootBlockNode) {
     10137                    if (rootBlockNode) {
     10138                        node = rootBlockNode.firstChild;
     10139                        if (node && node.type == 3) {
     10140                            node.value = node.value.replace(startWhiteSpaceRegExp, '');
     10141                        }
     10142
     10143                        node = rootBlockNode.lastChild;
     10144                        if (node && node.type == 3) {
     10145                            node.value = node.value.replace(endWhiteSpaceRegExp, '');
     10146                        }
     10147                    }
     10148                }
     10149
     10150                // Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
     10151                if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
     10152                    return;
     10153                }
     10154
     10155                while (node) {
     10156                    next = node.next;
     10157
     10158                    if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
     10159                        !blockElements[node.name] && !node.attr('data-mce-type'))) {
     10160                        if (!rootBlockNode) {
     10161                            // Create a new root block element
     10162                            rootBlockNode = createNode(rootBlockName, 1);
     10163                            rootBlockNode.attr(settings.forced_root_block_attrs);
     10164                            rootNode.insert(rootBlockNode, node);
     10165                            rootBlockNode.append(node);
     10166                        } else {
     10167                            rootBlockNode.append(node);
     10168                        }
     10169                    } else {
     10170                        trim(rootBlockNode);
     10171                        rootBlockNode = null;
     10172                    }
     10173
     10174                    node = next;
     10175                }
     10176
     10177                trim(rootBlockNode);
     10178            }
     10179
     10180            function createNode(name, type) {
     10181                var node = new Node(name, type), list;
     10182
     10183                if (name in nodeFilters) {
     10184                    list = matchedNodes[name];
     10185
     10186                    if (list) {
     10187                        list.push(node);
     10188                    } else {
     10189                        matchedNodes[name] = [node];
     10190                    }
     10191                }
     10192
     10193                return node;
     10194            }
     10195
     10196            function removeWhitespaceBefore(node) {
     10197                var textNode, textVal, sibling;
     10198
     10199                for (textNode = node.prev; textNode && textNode.type === 3; ) {
     10200                    textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
     10201
     10202                    if (textVal.length > 0) {
     10203                        textNode.value = textVal;
     10204                        textNode = textNode.prev;
     10205                    } else {
     10206                        sibling = textNode.prev;
     10207                        textNode.remove();
     10208                        textNode = sibling;
     10209                    }
     10210                }
     10211            }
     10212
     10213            function cloneAndExcludeBlocks(input) {
     10214                var name, output = {};
     10215
     10216                for (name in input) {
     10217                    if (name !== 'li' && name != 'p') {
     10218                        output[name] = input[name];
     10219                    }
     10220                }
     10221
     10222                return output;
     10223            }
     10224
     10225            parser = new SaxParser({
     10226                validate: validate,
     10227                allow_script_urls: settings.allow_script_urls,
     10228                allow_conditional_comments: settings.allow_conditional_comments,
     10229
     10230                // Exclude P and LI from DOM parsing since it's treated better by the DOM parser
     10231                self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
     10232
     10233                cdata: function(text) {
     10234                    node.append(createNode('#cdata', 4)).value = text;
     10235                },
     10236
     10237                text: function(text, raw) {
     10238                    var textNode;
     10239
     10240                    // Trim all redundant whitespace on non white space elements
     10241                    if (!isInWhiteSpacePreservedElement) {
     10242                        text = text.replace(allWhiteSpaceRegExp, ' ');
     10243
     10244                        if (node.lastChild && blockElements[node.lastChild.name]) {
     10245                            text = text.replace(startWhiteSpaceRegExp, '');
     10246                        }
     10247                    }
     10248
     10249                    // Do we need to create the node
     10250                    if (text.length !== 0) {
     10251                        textNode = createNode('#text', 3);
     10252                        textNode.raw = !!raw;
     10253                        node.append(textNode).value = text;
     10254                    }
     10255                },
     10256
     10257                comment: function(text) {
     10258                    node.append(createNode('#comment', 8)).value = text;
     10259                },
     10260
     10261                pi: function(name, text) {
     10262                    node.append(createNode(name, 7)).value = text;
     10263                    removeWhitespaceBefore(node);
     10264                },
     10265
     10266                doctype: function(text) {
     10267                    var newNode;
     10268
     10269                    newNode = node.append(createNode('#doctype', 10));
     10270                    newNode.value = text;
     10271                    removeWhitespaceBefore(node);
     10272                },
     10273
     10274                start: function(name, attrs, empty) {
     10275                    var newNode, attrFiltersLen, elementRule, attrName, parent;
     10276
     10277                    elementRule = validate ? schema.getElementRule(name) : {};
     10278                    if (elementRule) {
     10279                        newNode = createNode(elementRule.outputName || name, 1);
     10280                        newNode.attributes = attrs;
     10281                        newNode.shortEnded = empty;
     10282
     10283                        node.append(newNode);
     10284
     10285                        // Check if node is valid child of the parent node is the child is
     10286                        // unknown we don't collect it since it's probably a custom element
     10287                        parent = children[node.name];
     10288                        if (parent && children[newNode.name] && !parent[newNode.name]) {
     10289                            invalidChildren.push(newNode);
     10290                        }
     10291
     10292                        attrFiltersLen = attributeFilters.length;
     10293                        while (attrFiltersLen--) {
     10294                            attrName = attributeFilters[attrFiltersLen].name;
     10295
     10296                            if (attrName in attrs.map) {
     10297                                list = matchedAttributes[attrName];
     10298
     10299                                if (list) {
     10300                                    list.push(newNode);
     10301                                } else {
     10302                                    matchedAttributes[attrName] = [newNode];
     10303                                }
     10304                            }
     10305                        }
     10306
     10307                        // Trim whitespace before block
     10308                        if (blockElements[name]) {
     10309                            removeWhitespaceBefore(newNode);
     10310                        }
     10311
     10312                        // Change current node if the element wasn't empty i.e not <br /> or <img />
     10313                        if (!empty) {
     10314                            node = newNode;
     10315                        }
     10316
     10317                        // Check if we are inside a whitespace preserved element
     10318                        if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
     10319                            isInWhiteSpacePreservedElement = true;
     10320                        }
     10321                    }
     10322                },
     10323
     10324                end: function(name) {
     10325                    var textNode, elementRule, text, sibling, tempNode;
     10326
     10327                    elementRule = validate ? schema.getElementRule(name) : {};
     10328                    if (elementRule) {
     10329                        if (blockElements[name]) {
     10330                            if (!isInWhiteSpacePreservedElement) {
     10331                                // Trim whitespace of the first node in a block
     10332                                textNode = node.firstChild;
     10333                                if (textNode && textNode.type === 3) {
     10334                                    text = textNode.value.replace(startWhiteSpaceRegExp, '');
     10335
     10336                                    // Any characters left after trim or should we remove it
     10337                                    if (text.length > 0) {
     10338                                        textNode.value = text;
     10339                                        textNode = textNode.next;
     10340                                    } else {
     10341                                        sibling = textNode.next;
     10342                                        textNode.remove();
     10343                                        textNode = sibling;
     10344
     10345                                        // Remove any pure whitespace siblings
     10346                                        while (textNode && textNode.type === 3) {
     10347                                            text = textNode.value;
     10348                                            sibling = textNode.next;
     10349
     10350                                            if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
     10351                                                textNode.remove();
     10352                                                textNode = sibling;
     10353                                            }
     10354
     10355                                            textNode = sibling;
     10356                                        }
     10357                                    }
     10358                                }
     10359
     10360                                // Trim whitespace of the last node in a block
     10361                                textNode = node.lastChild;
     10362                                if (textNode && textNode.type === 3) {
     10363                                    text = textNode.value.replace(endWhiteSpaceRegExp, '');
     10364
     10365                                    // Any characters left after trim or should we remove it
     10366                                    if (text.length > 0) {
     10367                                        textNode.value = text;
     10368                                        textNode = textNode.prev;
     10369                                    } else {
     10370                                        sibling = textNode.prev;
     10371                                        textNode.remove();
     10372                                        textNode = sibling;
     10373
     10374                                        // Remove any pure whitespace siblings
     10375                                        while (textNode && textNode.type === 3) {
     10376                                            text = textNode.value;
     10377                                            sibling = textNode.prev;
     10378
     10379                                            if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
     10380                                                textNode.remove();
     10381                                                textNode = sibling;
     10382                                            }
     10383
     10384                                            textNode = sibling;
     10385                                        }
     10386                                    }
     10387                                }
     10388                            }
     10389
     10390                            // Trim start white space
     10391                            // Removed due to: #5424
     10392                            /*textNode = node.prev;
     10393                            if (textNode && textNode.type === 3) {
     10394                                text = textNode.value.replace(startWhiteSpaceRegExp, '');
     10395
     10396                                if (text.length > 0)
     10397                                    textNode.value = text;
     10398                                else
     10399                                    textNode.remove();
     10400                            }*/
     10401                        }
     10402
     10403                        // Check if we exited a whitespace preserved element
     10404                        if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
     10405                            isInWhiteSpacePreservedElement = false;
     10406                        }
     10407
     10408                        // Handle empty nodes
     10409                        if (elementRule.removeEmpty || elementRule.paddEmpty) {
     10410                            if (node.isEmpty(nonEmptyElements)) {
     10411                                if (elementRule.paddEmpty) {
     10412                                    node.empty().append(new Node('#text', '3')).value = '\u00a0';
     10413                                } else {
     10414                                    // Leave nodes that have a name like <a name="name">
     10415                                    if (!node.attributes.map.name && !node.attributes.map.id) {
     10416                                        tempNode = node.parent;
     10417                                        node.empty().remove();
     10418                                        node = tempNode;
     10419                                        return;
     10420                                    }
     10421                                }
     10422                            }
     10423                        }
     10424
     10425                        node = node.parent;
     10426                    }
     10427                }
     10428            }, schema);
     10429
     10430            rootNode = node = new Node(args.context || settings.root_name, 11);
     10431
     10432            parser.parse(html);
     10433
     10434            // Fix invalid children or report invalid children in a contextual parsing
     10435            if (validate && invalidChildren.length) {
     10436                if (!args.context) {
     10437                    fixInvalidChildren(invalidChildren);
     10438                } else {
     10439                    args.invalid = true;
     10440                }
     10441            }
     10442
     10443            // Wrap nodes in the root into block elements if the root is body
     10444            if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
     10445                addRootBlocks();
     10446            }
     10447
     10448            // Run filters only when the contents is valid
     10449            if (!args.invalid) {
     10450                // Run node filters
     10451                for (name in matchedNodes) {
     10452                    list = nodeFilters[name];
     10453                    nodes = matchedNodes[name];
     10454
     10455                    // Remove already removed children
     10456                    fi = nodes.length;
     10457                    while (fi--) {
     10458                        if (!nodes[fi].parent) {
     10459                            nodes.splice(fi, 1);
     10460                        }
     10461                    }
     10462
     10463                    for (i = 0, l = list.length; i < l; i++) {
     10464                        list[i](nodes, name, args);
     10465                    }
     10466                }
     10467
     10468                // Run attribute filters
     10469                for (i = 0, l = attributeFilters.length; i < l; i++) {
     10470                    list = attributeFilters[i];
     10471
     10472                    if (list.name in matchedAttributes) {
     10473                        nodes = matchedAttributes[list.name];
     10474
     10475                        // Remove already removed children
     10476                        fi = nodes.length;
     10477                        while (fi--) {
     10478                            if (!nodes[fi].parent) {
     10479                                nodes.splice(fi, 1);
     10480                            }
     10481                        }
     10482
     10483                        for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
     10484                            list.callbacks[fi](nodes, list.name, args);
     10485                        }
     10486                    }
     10487                }
     10488            }
     10489
     10490            return rootNode;
     10491        };
     10492
     10493        // Remove <br> at end of block elements Gecko and WebKit injects BR elements to
     10494        // make it possible to place the caret inside empty blocks. This logic tries to remove
     10495        // these elements and keep br elements that where intended to be there intact
     10496        if (settings.remove_trailing_brs) {
     10497            self.addNodeFilter('br', function(nodes) {
     10498                var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
     10499                var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
     10500                var elementRule, textNode;
     10501
     10502                // Remove brs from body element as well
     10503                blockElements.body = 1;
     10504
     10505                // Must loop forwards since it will otherwise remove all brs in <p>a<br><br><br></p>
     10506                for (i = 0; i < l; i++) {
     10507                    node = nodes[i];
     10508                    parent = node.parent;
     10509
     10510                    if (blockElements[node.parent.name] && node === parent.lastChild) {
     10511                        // Loop all nodes to the left of the current node and check for other BR elements
     10512                        // excluding bookmarks since they are invisible
     10513                        prev = node.prev;
     10514                        while (prev) {
     10515                            prevName = prev.name;
     10516
     10517                            // Ignore bookmarks
     10518                            if (prevName !== "span" || prev.attr('data-mce-type') !== 'bookmark') {
     10519                                // Found a non BR element
     10520                                if (prevName !== "br") {
     10521                                    break;
     10522                                }
     10523
     10524                                // Found another br it's a <br><br> structure then don't remove anything
     10525                                if (prevName === 'br') {
     10526                                    node = null;
     10527                                    break;
     10528                                }
     10529                            }
     10530
     10531                            prev = prev.prev;
     10532                        }
     10533
     10534                        if (node) {
     10535                            node.remove();
     10536
     10537                            // Is the parent to be considered empty after we removed the BR
     10538                            if (parent.isEmpty(nonEmptyElements)) {
     10539                                elementRule = schema.getElementRule(parent.name);
     10540
     10541                                // Remove or padd the element depending on schema rule
     10542                                if (elementRule) {
     10543                                    if (elementRule.removeEmpty) {
     10544                                        parent.remove();
     10545                                    } else if (elementRule.paddEmpty) {
     10546                                        parent.empty().append(new Node('#text', 3)).value = '\u00a0';
     10547                                    }
     10548                                }
     10549                            }
     10550                        }
     10551                    } else {
     10552                        // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p>
     10553                        // so they become <p><b><i>&nbsp;</i></b></p>
     10554                        lastParent = node;
     10555                        while (parent && parent.firstChild === lastParent && parent.lastChild === lastParent) {
     10556                            lastParent = parent;
     10557
     10558                            if (blockElements[parent.name]) {
     10559                                break;
     10560                            }
     10561
     10562                            parent = parent.parent;
     10563                        }
     10564
     10565                        if (lastParent === parent) {
     10566                            textNode = new Node('#text', 3);
     10567                            textNode.value = '\u00a0';
     10568                            node.replace(textNode);
     10569                        }
     10570                    }
     10571                }
     10572            });
     10573        }
     10574
     10575        // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
     10576        if (!settings.allow_html_in_named_anchor) {
     10577            self.addAttributeFilter('id,name', function(nodes) {
     10578                var i = nodes.length, sibling, prevSibling, parent, node;
     10579
     10580                while (i--) {
     10581                    node = nodes[i];
     10582                    if (node.name === 'a' && node.firstChild && !node.attr('href')) {
     10583                        parent = node.parent;
     10584
     10585                        // Move children after current node
     10586                        sibling = node.lastChild;
     10587                        do {
     10588                            prevSibling = sibling.prev;
     10589                            parent.insert(sibling, node);
     10590                            sibling = prevSibling;
     10591                        } while (sibling);
     10592                    }
     10593                }
     10594            });
     10595        }
     10596    };
     10597});
     10598
     10599// Included from: js/tinymce/classes/html/Writer.js
     10600
     10601/**
     10602 * Writer.js
     10603 *
     10604 * Copyright, Moxiecode Systems AB
     10605 * Released under LGPL License.
     10606 *
     10607 * License: http://www.tinymce.com/license
     10608 * Contributing: http://www.tinymce.com/contributing
     10609 */
     10610
     10611/**
     10612 * This class is used to write HTML tags out it can be used with the Serializer or the SaxParser.
     10613 *
     10614 * @class tinymce.html.Writer
     10615 * @example
     10616 * var writer = new tinymce.html.Writer({indent: true});
     10617 * var parser = new tinymce.html.SaxParser(writer).parse('<p><br></p>');
     10618 * console.log(writer.getContent());
     10619 *
     10620 * @class tinymce.html.Writer
     10621 * @version 3.4
     10622 */
     10623define("tinymce/html/Writer", [
     10624    "tinymce/html/Entities",
     10625    "tinymce/util/Tools"
     10626], function(Entities, Tools) {
     10627    var makeMap = Tools.makeMap;
     10628
     10629    /**
     10630     * Constructs a new Writer instance.
     10631     *
     10632     * @constructor
     10633     * @method Writer
     10634     * @param {Object} settings Name/value settings object.
     10635     */
     10636    return function(settings) {
     10637        var html = [], indent, indentBefore, indentAfter, encode, htmlOutput;
     10638
     10639        settings = settings || {};
     10640        indent = settings.indent;
     10641        indentBefore = makeMap(settings.indent_before || '');
     10642        indentAfter = makeMap(settings.indent_after || '');
     10643        encode = Entities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
     10644        htmlOutput = settings.element_format == "html";
     10645
     10646        return {
     10647            /**
     10648             * Writes the a start element such as <p id="a">.
     10649             *
     10650             * @method start
     10651             * @param {String} name Name of the element.
     10652             * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
     10653             * @param {Boolean} empty Optional empty state if the tag should end like <br />.
     10654             */
     10655            start: function(name, attrs, empty) {
     10656                var i, l, attr, value;
     10657
     10658                if (indent && indentBefore[name] && html.length > 0) {
     10659                    value = html[html.length - 1];
     10660
     10661                    if (value.length > 0 && value !== '\n') {
     10662                        html.push('\n');
     10663                    }
     10664                }
     10665
     10666                html.push('<', name);
     10667
     10668                if (attrs) {
     10669                    for (i = 0, l = attrs.length; i < l; i++) {
     10670                        attr = attrs[i];
     10671                        html.push(' ', attr.name, '="', encode(attr.value, true), '"');
     10672                    }
     10673                }
     10674
     10675                if (!empty || htmlOutput) {
     10676                    html[html.length] = '>';
     10677                } else {
     10678                    html[html.length] = ' />';
     10679                }
     10680
     10681                if (empty && indent && indentAfter[name] && html.length > 0) {
     10682                    value = html[html.length - 1];
     10683
     10684                    if (value.length > 0 && value !== '\n') {
     10685                        html.push('\n');
     10686                    }
     10687                }
     10688            },
     10689
     10690            /**
     10691             * Writes the a end element such as </p>.
     10692             *
     10693             * @method end
     10694             * @param {String} name Name of the element.
     10695             */
     10696            end: function(name) {
     10697                var value;
     10698
     10699                /*if (indent && indentBefore[name] && html.length > 0) {
     10700                    value = html[html.length - 1];
     10701
     10702                    if (value.length > 0 && value !== '\n')
     10703                        html.push('\n');
     10704                }*/
     10705
     10706                html.push('</', name, '>');
     10707
     10708                if (indent && indentAfter[name] && html.length > 0) {
     10709                    value = html[html.length - 1];
     10710
     10711                    if (value.length > 0 && value !== '\n') {
     10712                        html.push('\n');
     10713                    }
     10714                }
     10715            },
     10716
     10717            /**
     10718             * Writes a text node.
     10719             *
     10720             * @method text
     10721             * @param {String} text String to write out.
     10722             * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
     10723             */
     10724            text: function(text, raw) {
     10725                if (text.length > 0) {
     10726                    html[html.length] = raw ? text : encode(text);
     10727                }
     10728            },
     10729
     10730            /**
     10731             * Writes a cdata node such as <![CDATA[data]]>.
     10732             *
     10733             * @method cdata
     10734             * @param {String} text String to write out inside the cdata.
     10735             */
     10736            cdata: function(text) {
     10737                html.push('<![CDATA[', text, ']]>');
     10738            },
     10739
     10740            /**
     10741             * Writes a comment node such as <!-- Comment -->.
     10742             *
     10743             * @method cdata
     10744             * @param {String} text String to write out inside the comment.
     10745             */
     10746            comment: function(text) {
     10747                html.push('<!--', text, '-->');
     10748            },
     10749
     10750            /**
     10751             * Writes a PI node such as <?xml attr="value" ?>.
     10752             *
     10753             * @method pi
     10754             * @param {String} name Name of the pi.
     10755             * @param {String} text String to write out inside the pi.
     10756             */
     10757            pi: function(name, text) {
     10758                if (text) {
     10759                    html.push('<?', name, ' ', text, '?>');
     10760                } else {
     10761                    html.push('<?', name, '?>');
     10762                }
     10763
     10764                if (indent) {
     10765                    html.push('\n');
     10766                }
     10767            },
     10768
     10769            /**
     10770             * Writes a doctype node such as <!DOCTYPE data>.
     10771             *
     10772             * @method doctype
     10773             * @param {String} text String to write out inside the doctype.
     10774             */
     10775            doctype: function(text) {
     10776                html.push('<!DOCTYPE', text, '>', indent ? '\n' : '');
     10777            },
     10778
     10779            /**
     10780             * Resets the internal buffer if one wants to reuse the writer.
     10781             *
     10782             * @method reset
     10783             */
     10784            reset: function() {
     10785                html.length = 0;
     10786            },
     10787
     10788            /**
     10789             * Returns the contents that got serialized.
     10790             *
     10791             * @method getContent
     10792             * @return {String} HTML contents that got written down.
     10793             */
     10794            getContent: function() {
     10795                return html.join('').replace(/\n$/, '');
     10796            }
     10797        };
     10798    };
     10799});
     10800
     10801// Included from: js/tinymce/classes/html/Serializer.js
     10802
     10803/**
     10804 * Serializer.js
     10805 *
     10806 * Copyright, Moxiecode Systems AB
     10807 * Released under LGPL License.
     10808 *
     10809 * License: http://www.tinymce.com/license
     10810 * Contributing: http://www.tinymce.com/contributing
     10811 */
     10812
     10813/**
     10814 * This class is used to serialize down the DOM tree into a string using a Writer instance.
     10815 *
     10816 *
     10817 * @example
     10818 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
     10819 * @class tinymce.html.Serializer
     10820 * @version 3.4
     10821 */
     10822define("tinymce/html/Serializer", [
     10823    "tinymce/html/Writer",
     10824    "tinymce/html/Schema"
     10825], function(Writer, Schema) {
     10826    /**
     10827     * Constructs a new Serializer instance.
     10828     *
     10829     * @constructor
     10830     * @method Serializer
     10831     * @param {Object} settings Name/value settings object.
     10832     * @param {tinymce.html.Schema} schema Schema instance to use.
     10833     */
     10834    return function(settings, schema) {
     10835        var self = this, writer = new Writer(settings);
     10836
     10837        settings = settings || {};
     10838        settings.validate = "validate" in settings ? settings.validate : true;
     10839
     10840        self.schema = schema = schema || new Schema();
     10841        self.writer = writer;
     10842
     10843        /**
     10844         * Serializes the specified node into a string.
     10845         *
     10846         * @example
     10847         * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
     10848         * @method serialize
     10849         * @param {tinymce.html.Node} node Node instance to serialize.
     10850         * @return {String} String with HTML based on DOM tree.
     10851         */
     10852        self.serialize = function(node) {
     10853            var handlers, validate;
     10854
     10855            validate = settings.validate;
     10856
     10857            handlers = {
     10858                // #text
     10859                3: function(node) {
     10860                    writer.text(node.value, node.raw);
     10861                },
     10862
     10863                // #comment
     10864                8: function(node) {
     10865                    writer.comment(node.value);
     10866                },
     10867
     10868                // Processing instruction
     10869                7: function(node) {
     10870                    writer.pi(node.name, node.value);
     10871                },
     10872
     10873                // Doctype
     10874                10: function(node) {
     10875                    writer.doctype(node.value);
     10876                },
     10877
     10878                // CDATA
     10879                4: function(node) {
     10880                    writer.cdata(node.value);
     10881                },
     10882
     10883                // Document fragment
     10884                11: function(node) {
     10885                    if ((node = node.firstChild)) {
     10886                        do {
     10887                            walk(node);
     10888                        } while ((node = node.next));
     10889                    }
     10890                }
     10891            };
     10892
     10893            writer.reset();
     10894
     10895            function walk(node) {
     10896                var handler = handlers[node.type], name, isEmpty, attrs, attrName, attrValue, sortedAttrs, i, l, elementRule;
     10897
     10898                if (!handler) {
     10899                    name = node.name;
     10900                    isEmpty = node.shortEnded;
     10901                    attrs = node.attributes;
     10902
     10903                    // Sort attributes
     10904                    if (validate && attrs && attrs.length > 1) {
     10905                        sortedAttrs = [];
     10906                        sortedAttrs.map = {};
     10907
     10908                        elementRule = schema.getElementRule(node.name);
     10909                        for (i = 0, l = elementRule.attributesOrder.length; i < l; i++) {
     10910                            attrName = elementRule.attributesOrder[i];
     10911
     10912                            if (attrName in attrs.map) {
     10913                                attrValue = attrs.map[attrName];
     10914                                sortedAttrs.map[attrName] = attrValue;
     10915                                sortedAttrs.push({name: attrName, value: attrValue});
     10916                            }
     10917                        }
     10918
     10919                        for (i = 0, l = attrs.length; i < l; i++) {
     10920                            attrName = attrs[i].name;
     10921
     10922                            if (!(attrName in sortedAttrs.map)) {
     10923                                attrValue = attrs.map[attrName];
     10924                                sortedAttrs.map[attrName] = attrValue;
     10925                                sortedAttrs.push({name: attrName, value: attrValue});
     10926                            }
     10927                        }
     10928
     10929                        attrs = sortedAttrs;
     10930                    }
     10931
     10932                    writer.start(node.name, attrs, isEmpty);
     10933
     10934                    if (!isEmpty) {
     10935                        if ((node = node.firstChild)) {
     10936                            do {
     10937                                walk(node);
     10938                            } while ((node = node.next));
     10939                        }
     10940
     10941                        writer.end(name);
     10942                    }
     10943                } else {
     10944                    handler(node);
     10945                }
     10946            }
     10947
     10948            // Serialize element and treat all non elements as fragments
     10949            if (node.type == 1 && !settings.inner) {
     10950                walk(node);
     10951            } else {
     10952                handlers[11](node);
     10953            }
     10954
     10955            return writer.getContent();
     10956        };
     10957    };
     10958});
     10959
     10960// Included from: js/tinymce/classes/dom/Serializer.js
     10961
     10962/**
     10963 * Serializer.js
     10964 *
     10965 * Copyright, Moxiecode Systems AB
     10966 * Released under LGPL License.
     10967 *
     10968 * License: http://www.tinymce.com/license
     10969 * Contributing: http://www.tinymce.com/contributing
     10970 */
     10971
     10972/**
     10973 * This class is used to serialize DOM trees into a string. Consult the TinyMCE Wiki API for
     10974 * more details and examples on how to use this class.
     10975 *
     10976 * @class tinymce.dom.Serializer
     10977 */
     10978define("tinymce/dom/Serializer", [
     10979    "tinymce/dom/DOMUtils",
     10980    "tinymce/html/DomParser",
     10981    "tinymce/html/Entities",
     10982    "tinymce/html/Serializer",
     10983    "tinymce/html/Node",
     10984    "tinymce/html/Schema",
     10985    "tinymce/Env",
     10986    "tinymce/util/Tools"
     10987], function(DOMUtils, DomParser, Entities, Serializer, Node, Schema, Env, Tools) {
     10988    var each = Tools.each, trim = Tools.trim;
     10989    var DOM = DOMUtils.DOM;
     10990
     10991    /**
     10992     * Constructs a new DOM serializer class.
     10993     *
     10994     * @constructor
     10995     * @method Serializer
     10996     * @param {Object} settings Serializer settings object.
     10997     * @param {tinymce.Editor} editor Optional editor to bind events to and get schema/dom from.
     10998     */
     10999    return function(settings, editor) {
     11000        var dom, schema, htmlParser;
     11001
     11002        if (editor) {
     11003            dom = editor.dom;
     11004            schema = editor.schema;
     11005        }
     11006
     11007        // Default DOM and Schema if they are undefined
     11008        dom = dom || DOM;
     11009        schema = schema || new Schema(settings);
     11010        settings.entity_encoding = settings.entity_encoding || 'named';
     11011        settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
     11012
     11013        htmlParser = new DomParser(settings, schema);
     11014
     11015        // Convert move data-mce-src, data-mce-href and data-mce-style into nodes or process them if needed
     11016        htmlParser.addAttributeFilter('src,href,style', function(nodes, name) {
     11017            var i = nodes.length, node, value, internalName = 'data-mce-' + name;
     11018            var urlConverter = settings.url_converter, urlConverterScope = settings.url_converter_scope, undef;
     11019
     11020            while (i--) {
     11021                node = nodes[i];
     11022
     11023                value = node.attributes.map[internalName];
     11024                if (value !== undef) {
     11025                    // Set external name to internal value and remove internal
     11026                    node.attr(name, value.length > 0 ? value : null);
     11027                    node.attr(internalName, null);
     11028                } else {
     11029                    // No internal attribute found then convert the value we have in the DOM
     11030                    value = node.attributes.map[name];
     11031
     11032                    if (name === "style") {
     11033                        value = dom.serializeStyle(dom.parseStyle(value), node.name);
     11034                    } else if (urlConverter) {
     11035                        value = urlConverter.call(urlConverterScope, value, name, node.name);
     11036                    }
     11037
     11038                    node.attr(name, value.length > 0 ? value : null);
     11039                }
     11040            }
     11041        });
     11042
     11043        // Remove internal classes mceItem<..> or mceSelected
     11044        htmlParser.addAttributeFilter('class', function(nodes) {
     11045            var i = nodes.length, node, value;
     11046
     11047            while (i--) {
     11048                node = nodes[i];
     11049                value = node.attr('class').replace(/(?:^|\s)mce-item-\w+(?!\S)/g, '');
     11050                node.attr('class', value.length > 0 ? value : null);
     11051            }
     11052        });
     11053
     11054        // Remove bookmark elements
     11055        htmlParser.addAttributeFilter('data-mce-type', function(nodes, name, args) {
     11056            var i = nodes.length, node;
     11057
     11058            while (i--) {
     11059                node = nodes[i];
     11060
     11061                if (node.attributes.map['data-mce-type'] === 'bookmark' && !args.cleanup) {
     11062                    node.remove();
     11063                }
     11064            }
     11065        });
     11066
     11067        // Remove expando attributes
     11068        htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name) {
     11069            var i = nodes.length;
     11070
     11071            while (i--) {
     11072                nodes[i].attr(name, null);
     11073            }
     11074        });
     11075
     11076        htmlParser.addNodeFilter('noscript', function(nodes) {
     11077            var i = nodes.length, node;
     11078
     11079            while (i--) {
     11080                node = nodes[i].firstChild;
     11081
     11082                if (node) {
     11083                    node.value = Entities.decode(node.value);
     11084                }
     11085            }
     11086        });
     11087
     11088        // Force script into CDATA sections and remove the mce- prefix also add comments around styles
     11089        htmlParser.addNodeFilter('script,style', function(nodes, name) {
     11090            var i = nodes.length, node, value;
     11091
     11092            function trim(value) {
     11093                /*jshint maxlen:255 */
     11094                return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
     11095                        .replace(/^[\r\n]*|[\r\n]*$/g, '')
     11096                        .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
     11097                        .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
     11098            }
     11099
     11100            while (i--) {
     11101                node = nodes[i];
     11102                value = node.firstChild ? node.firstChild.value : '';
     11103
     11104                if (name === "script") {
     11105                    // Remove mce- prefix from script elements and remove default text/javascript mime type (HTML5)
     11106                    var type = (node.attr('type') || 'text/javascript').replace(/^mce\-/, '');
     11107                    node.attr('type', type === 'text/javascript' ? null : type);
     11108
     11109                    if (value.length > 0) {
     11110                        node.firstChild.value = '// <![CDATA[\n' + trim(value) + '\n// ]]>';
     11111                    }
     11112                } else {
     11113                    if (value.length > 0) {
     11114                        node.firstChild.value = '<!--\n' + trim(value) + '\n-->';
     11115                    }
     11116                }
     11117            }
     11118        });
     11119
     11120        // Convert comments to cdata and handle protected comments
     11121        htmlParser.addNodeFilter('#comment', function(nodes) {
     11122            var i = nodes.length, node;
     11123
     11124            while (i--) {
     11125                node = nodes[i];
     11126
     11127                if (node.value.indexOf('[CDATA[') === 0) {
     11128                    node.name = '#cdata';
     11129                    node.type = 4;
     11130                    node.value = node.value.replace(/^\[CDATA\[|\]\]$/g, '');
     11131                } else if (node.value.indexOf('mce:protected ') === 0) {
     11132                    node.name = "#text";
     11133                    node.type = 3;
     11134                    node.raw = true;
     11135                    node.value = unescape(node.value).substr(14);
     11136                }
     11137            }
     11138        });
     11139
     11140        htmlParser.addNodeFilter('xml:namespace,input', function(nodes, name) {
     11141            var i = nodes.length, node;
     11142
     11143            while (i--) {
     11144                node = nodes[i];
     11145                if (node.type === 7) {
     11146                    node.remove();
     11147                } else if (node.type === 1) {
     11148                    if (name === "input" && !("type" in node.attributes.map)) {
     11149                        node.attr('type', 'text');
     11150                    }
     11151                }
     11152            }
     11153        });
     11154
     11155        // Fix list elements, TODO: Replace this later
     11156        if (settings.fix_list_elements) {
     11157            htmlParser.addNodeFilter('ul,ol', function(nodes) {
     11158                var i = nodes.length, node, parentNode;
     11159
     11160                while (i--) {
     11161                    node = nodes[i];
     11162                    parentNode = node.parent;
     11163
     11164                    if (parentNode.name === 'ul' || parentNode.name === 'ol') {
     11165                        if (node.prev && node.prev.name === 'li') {
     11166                            node.prev.append(node);
     11167                        }
     11168                    }
     11169                }
     11170            });
     11171        }
     11172
     11173        // Remove internal data attributes
     11174        htmlParser.addAttributeFilter('data-mce-src,data-mce-href,data-mce-style,data-mce-selected', function(nodes, name) {
     11175            var i = nodes.length;
     11176
     11177            while (i--) {
     11178                nodes[i].attr(name, null);
     11179            }
     11180        });
     11181
     11182        // Return public methods
     11183        return {
     11184            /**
     11185             * Schema instance that was used to when the Serializer was constructed.
     11186             *
     11187             * @field {tinymce.html.Schema} schema
     11188             */
     11189            schema: schema,
     11190
     11191            /**
     11192             * Adds a node filter function to the parser used by the serializer, the parser will collect the specified nodes by name
     11193             * and then execute the callback ones it has finished parsing the document.
     11194             *
     11195             * @example
     11196             * parser.addNodeFilter('p,h1', function(nodes, name) {
     11197             *      for (var i = 0; i < nodes.length; i++) {
     11198             *          console.log(nodes[i].name);
     11199             *      }
     11200             * });
     11201             * @method addNodeFilter
     11202             * @method {String} name Comma separated list of nodes to collect.
     11203             * @param {function} callback Callback function to execute once it has collected nodes.
     11204             */
     11205            addNodeFilter: htmlParser.addNodeFilter,
     11206
     11207            /**
     11208             * Adds a attribute filter function to the parser used by the serializer, the parser will
     11209             * collect nodes that has the specified attributes
     11210             * and then execute the callback ones it has finished parsing the document.
     11211             *
     11212             * @example
     11213             * parser.addAttributeFilter('src,href', function(nodes, name) {
     11214             *      for (var i = 0; i < nodes.length; i++) {
     11215             *          console.log(nodes[i].name);
     11216             *      }
     11217             * });
     11218             * @method addAttributeFilter
     11219             * @method {String} name Comma separated list of nodes to collect.
     11220             * @param {function} callback Callback function to execute once it has collected nodes.
     11221             */
     11222            addAttributeFilter: htmlParser.addAttributeFilter,
     11223
     11224            /**
     11225             * Serializes the specified browser DOM node into a HTML string.
     11226             *
     11227             * @method serialize
     11228             * @param {DOMNode} node DOM node to serialize.
     11229             * @param {Object} args Arguments option that gets passed to event handlers.
     11230             */
     11231            serialize: function(node, args) {
     11232                var self = this, impl, doc, oldDoc, htmlSerializer, content;
     11233
     11234                // Explorer won't clone contents of script and style and the
     11235                // selected index of select elements are cleared on a clone operation.
     11236                if (Env.ie && dom.select('script,style,select,map').length > 0) {
     11237                    content = node.innerHTML;
     11238                    node = node.cloneNode(false);
     11239                    dom.setHTML(node, content);
     11240                } else {
     11241                    node = node.cloneNode(true);
     11242                }
     11243
     11244                // Nodes needs to be attached to something in WebKit/Opera
     11245                // This fix will make DOM ranges and make Sizzle happy!
     11246                impl = node.ownerDocument.implementation;
     11247                if (impl.createHTMLDocument) {
     11248                    // Create an empty HTML document
     11249                    doc = impl.createHTMLDocument("");
     11250
     11251                    // Add the element or it's children if it's a body element to the new document
     11252                    each(node.nodeName == 'BODY' ? node.childNodes : [node], function(node) {
     11253                        doc.body.appendChild(doc.importNode(node, true));
     11254                    });
     11255
     11256                    // Grab first child or body element for serialization
     11257                    if (node.nodeName != 'BODY') {
     11258                        node = doc.body.firstChild;
     11259                    } else {
     11260                        node = doc.body;
     11261                    }
     11262
     11263                    // set the new document in DOMUtils so createElement etc works
     11264                    oldDoc = dom.doc;
     11265                    dom.doc = doc;
     11266                }
     11267
     11268                args = args || {};
     11269                args.format = args.format || 'html';
     11270
     11271                // Don't wrap content if we want selected html
     11272                if (args.selection) {
     11273                    args.forced_root_block = '';
     11274                }
     11275
     11276                // Pre process
     11277                if (!args.no_events) {
     11278                    args.node = node;
     11279                    self.onPreProcess(args);
     11280                }
     11281
     11282                // Setup serializer
     11283                htmlSerializer = new Serializer(settings, schema);
     11284
     11285                // Parse and serialize HTML
     11286                args.content = htmlSerializer.serialize(
     11287                    htmlParser.parse(trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
     11288                );
     11289
     11290                // Replace all BOM characters for now until we can find a better solution
     11291                if (!args.cleanup) {
     11292                    args.content = args.content.replace(/\uFEFF/g, '');
     11293                }
     11294
     11295                // Post process
     11296                if (!args.no_events) {
     11297                    self.onPostProcess(args);
     11298                }
     11299
     11300                // Restore the old document if it was changed
     11301                if (oldDoc) {
     11302                    dom.doc = oldDoc;
     11303                }
     11304
     11305                args.node = null;
     11306
     11307                return args.content;
     11308            },
     11309
     11310            /**
     11311             * Adds valid elements rules to the serializers schema instance this enables you to specify things
     11312             * like what elements should be outputted and what attributes specific elements might have.
     11313             * Consult the Wiki for more details on this format.
     11314             *
     11315             * @method addRules
     11316             * @param {String} rules Valid elements rules string to add to schema.
     11317             */
     11318            addRules: function(rules) {
     11319                schema.addValidElements(rules);
     11320            },
     11321
     11322            /**
     11323             * Sets the valid elements rules to the serializers schema instance this enables you to specify things
     11324             * like what elements should be outputted and what attributes specific elements might have.
     11325             * Consult the Wiki for more details on this format.
     11326             *
     11327             * @method setRules
     11328             * @param {String} rules Valid elements rules string.
     11329             */
     11330            setRules: function(rules) {
     11331                schema.setValidElements(rules);
     11332            },
     11333
     11334            onPreProcess: function(args) {
     11335                if (editor) {
     11336                    editor.fire('PreProcess', args);
     11337                }
     11338            },
     11339
     11340            onPostProcess: function(args) {
     11341                if (editor) {
     11342                    editor.fire('PostProcess', args);
     11343                }
     11344            }
     11345        };
     11346    };
     11347});
     11348
     11349// Included from: js/tinymce/classes/dom/TridentSelection.js
     11350
     11351/**
     11352 * TridentSelection.js
     11353 *
     11354 * Copyright, Moxiecode Systems AB
     11355 * Released under LGPL License.
     11356 *
     11357 * License: http://www.tinymce.com/license
     11358 * Contributing: http://www.tinymce.com/contributing
     11359 */
     11360
     11361/**
     11362 * Selection class for old explorer versions. This one fakes the
     11363 * native selection object available on modern browsers.
     11364 *
     11365 * @class tinymce.dom.TridentSelection
     11366 */
     11367define("tinymce/dom/TridentSelection", [], function() {
     11368    function Selection(selection) {
     11369        var self = this, dom = selection.dom, FALSE = false;
     11370
     11371        function getPosition(rng, start) {
     11372            var checkRng, startIndex = 0, endIndex, inside,
     11373                children, child, offset, index, position = -1, parent;
     11374
     11375            // Setup test range, collapse it and get the parent
     11376            checkRng = rng.duplicate();
     11377            checkRng.collapse(start);
     11378            parent = checkRng.parentElement();
     11379
     11380            // Check if the selection is within the right document
     11381            if (parent.ownerDocument !== selection.dom.doc) {
     11382                return;
     11383            }
     11384
     11385            // IE will report non editable elements as it's parent so look for an editable one
     11386            while (parent.contentEditable === "false") {
     11387                parent = parent.parentNode;
     11388            }
     11389
     11390            // If parent doesn't have any children then return that we are inside the element
     11391            if (!parent.hasChildNodes()) {
     11392                return {node: parent, inside: 1};
     11393            }
     11394
     11395            // Setup node list and endIndex
     11396            children = parent.children;
     11397            endIndex = children.length - 1;
     11398
     11399            // Perform a binary search for the position
     11400            while (startIndex <= endIndex) {
     11401                index = Math.floor((startIndex + endIndex) / 2);
     11402
     11403                // Move selection to node and compare the ranges
     11404                child = children[index];
     11405                checkRng.moveToElementText(child);
     11406                position = checkRng.compareEndPoints(start ? 'StartToStart' : 'EndToEnd', rng);
     11407
     11408                // Before/after or an exact match
     11409                if (position > 0) {
     11410                    endIndex = index - 1;
     11411                } else if (position < 0) {
     11412                    startIndex = index + 1;
     11413                } else {
     11414                    return {node: child};
     11415                }
     11416            }
     11417
     11418            // Check if child position is before or we didn't find a position
     11419            if (position < 0) {
     11420                // No element child was found use the parent element and the offset inside that
     11421                if (!child) {
     11422                    checkRng.moveToElementText(parent);
     11423                    checkRng.collapse(true);
     11424                    child = parent;
     11425                    inside = true;
     11426                } else {
     11427                    checkRng.collapse(false);
     11428                }
     11429
     11430                // Walk character by character in text node until we hit the selected range endpoint,
     11431                // hit the end of document or parent isn't the right one
     11432                // We need to walk char by char since rng.text or rng.htmlText will trim line endings
     11433                offset = 0;
     11434                while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
     11435                    if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
     11436                        break;
     11437                    }
     11438
     11439                    offset++;
     11440                }
     11441            } else {
     11442                // Child position is after the selection endpoint
     11443                checkRng.collapse(true);
     11444
     11445                // Walk character by character in text node until we hit the selected range endpoint, hit
     11446                // the end of document or parent isn't the right one
     11447                offset = 0;
     11448                while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
     11449                    if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
     11450                        break;
     11451                    }
     11452
     11453                    offset++;
     11454                }
     11455            }
     11456
     11457            return {node: child, position: position, offset: offset, inside: inside};
     11458        }
     11459
     11460        // Returns a W3C DOM compatible range object by using the IE Range API
     11461        function getRange() {
     11462            var ieRange = selection.getRng(), domRange = dom.createRng(), element, collapsed, tmpRange, element2, bookmark;
     11463
     11464            // If selection is outside the current document just return an empty range
     11465            element = ieRange.item ? ieRange.item(0) : ieRange.parentElement();
     11466            if (element.ownerDocument != dom.doc) {
     11467                return domRange;
     11468            }
     11469
     11470            collapsed = selection.isCollapsed();
     11471
     11472            // Handle control selection
     11473            if (ieRange.item) {
     11474                domRange.setStart(element.parentNode, dom.nodeIndex(element));
     11475                domRange.setEnd(domRange.startContainer, domRange.startOffset + 1);
     11476
     11477                return domRange;
     11478            }
     11479
     11480            function findEndPoint(start) {
     11481                var endPoint = getPosition(ieRange, start), container, offset, textNodeOffset = 0, sibling, undef, nodeValue;
     11482
     11483                container = endPoint.node;
     11484                offset = endPoint.offset;
     11485
     11486                if (endPoint.inside && !container.hasChildNodes()) {
     11487                    domRange[start ? 'setStart' : 'setEnd'](container, 0);
     11488                    return;
     11489                }
     11490
     11491                if (offset === undef) {
     11492                    domRange[start ? 'setStartBefore' : 'setEndAfter'](container);
     11493                    return;
     11494                }
     11495
     11496                if (endPoint.position < 0) {
     11497                    sibling = endPoint.inside ? container.firstChild : container.nextSibling;
     11498
     11499                    if (!sibling) {
     11500                        domRange[start ? 'setStartAfter' : 'setEndAfter'](container);
     11501                        return;
     11502                    }
     11503
     11504                    if (!offset) {
     11505                        if (sibling.nodeType == 3) {
     11506                            domRange[start ? 'setStart' : 'setEnd'](sibling, 0);
     11507                        } else {
     11508                            domRange[start ? 'setStartBefore' : 'setEndBefore'](sibling);
     11509                        }
     11510
     11511                        return;
     11512                    }
     11513
     11514                    // Find the text node and offset
     11515                    while (sibling) {
     11516                        nodeValue = sibling.nodeValue;
     11517                        textNodeOffset += nodeValue.length;
     11518
     11519                        // We are at or passed the position we where looking for
     11520                        if (textNodeOffset >= offset) {
     11521                            container = sibling;
     11522                            textNodeOffset -= offset;
     11523                            textNodeOffset = nodeValue.length - textNodeOffset;
     11524                            break;
     11525                        }
     11526
     11527                        sibling = sibling.nextSibling;
     11528                    }
     11529                } else {
     11530                    // Find the text node and offset
     11531                    sibling = container.previousSibling;
     11532
     11533                    if (!sibling) {
     11534                        return domRange[start ? 'setStartBefore' : 'setEndBefore'](container);
     11535                    }
     11536
     11537                    // If there isn't any text to loop then use the first position
     11538                    if (!offset) {
     11539                        if (container.nodeType == 3) {
     11540                            domRange[start ? 'setStart' : 'setEnd'](sibling, container.nodeValue.length);
     11541                        } else {
     11542                            domRange[start ? 'setStartAfter' : 'setEndAfter'](sibling);
     11543                        }
     11544
     11545                        return;
     11546                    }
     11547
     11548                    while (sibling) {
     11549                        textNodeOffset += sibling.nodeValue.length;
     11550
     11551                        // We are at or passed the position we where looking for
     11552                        if (textNodeOffset >= offset) {
     11553                            container = sibling;
     11554                            textNodeOffset -= offset;
     11555                            break;
     11556                        }
     11557
     11558                        sibling = sibling.previousSibling;
     11559                    }
     11560                }
     11561
     11562                domRange[start ? 'setStart' : 'setEnd'](container, textNodeOffset);
     11563            }
     11564
     11565            try {
     11566                // Find start point
     11567                findEndPoint(true);
     11568
     11569                // Find end point if needed
     11570                if (!collapsed) {
     11571                    findEndPoint();
     11572                }
     11573            } catch (ex) {
     11574                // IE has a nasty bug where text nodes might throw "invalid argument" when you
     11575                // access the nodeValue or other properties of text nodes. This seems to happend when
     11576                // text nodes are split into two nodes by a delete/backspace call. So lets detect it and try to fix it.
     11577                if (ex.number == -2147024809) {
     11578                    // Get the current selection
     11579                    bookmark = self.getBookmark(2);
     11580
     11581                    // Get start element
     11582                    tmpRange = ieRange.duplicate();
     11583                    tmpRange.collapse(true);
     11584                    element = tmpRange.parentElement();
     11585
     11586                    // Get end element
     11587                    if (!collapsed) {
     11588                        tmpRange = ieRange.duplicate();
     11589                        tmpRange.collapse(false);
     11590                        element2 = tmpRange.parentElement();
     11591                        element2.innerHTML = element2.innerHTML;
     11592                    }
     11593
     11594                    // Remove the broken elements
     11595                    element.innerHTML = element.innerHTML;
     11596
     11597                    // Restore the selection
     11598                    self.moveToBookmark(bookmark);
     11599
     11600                    // Since the range has moved we need to re-get it
     11601                    ieRange = selection.getRng();
     11602
     11603                    // Find start point
     11604                    findEndPoint(true);
     11605
     11606                    // Find end point if needed
     11607                    if (!collapsed) {
     11608                        findEndPoint();
     11609                    }
     11610                } else {
     11611                    throw ex; // Throw other errors
     11612                }
     11613            }
     11614
     11615            return domRange;
     11616        }
     11617
     11618        this.getBookmark = function(type) {
     11619            var rng = selection.getRng(), bookmark = {};
     11620
     11621            function getIndexes(node) {
     11622                var parent, root, children, i, indexes = [];
     11623
     11624                parent = node.parentNode;
     11625                root = dom.getRoot().parentNode;
     11626
     11627                while (parent != root && parent.nodeType !== 9) {
     11628                    children = parent.children;
     11629
     11630                    i = children.length;
     11631                    while (i--) {
     11632                        if (node === children[i]) {
     11633                            indexes.push(i);
     11634                            break;
     11635                        }
     11636                    }
     11637
     11638                    node = parent;
     11639                    parent = parent.parentNode;
     11640                }
     11641
     11642                return indexes;
     11643            }
     11644
     11645            function getBookmarkEndPoint(start) {
     11646                var position;
     11647
     11648                position = getPosition(rng, start);
     11649                if (position) {
     11650                    return {
     11651                        position: position.position,
     11652                        offset: position.offset,
     11653                        indexes: getIndexes(position.node),
     11654                        inside: position.inside
     11655                    };
     11656                }
     11657            }
     11658
     11659            // Non ubstructive bookmark
     11660            if (type === 2) {
     11661                // Handle text selection
     11662                if (!rng.item) {
     11663                    bookmark.start = getBookmarkEndPoint(true);
     11664
     11665                    if (!selection.isCollapsed()) {
     11666                        bookmark.end = getBookmarkEndPoint();
     11667                    }
     11668                } else {
     11669                    bookmark.start = {ctrl: true, indexes: getIndexes(rng.item(0))};
     11670                }
     11671            }
     11672
     11673            return bookmark;
     11674        };
     11675
     11676        this.moveToBookmark = function(bookmark) {
     11677            var rng, body = dom.doc.body;
     11678
     11679            function resolveIndexes(indexes) {
     11680                var node, i, idx, children;
     11681
     11682                node = dom.getRoot();
     11683                for (i = indexes.length - 1; i >= 0; i--) {
     11684                    children = node.children;
     11685                    idx = indexes[i];
     11686
     11687                    if (idx <= children.length - 1) {
     11688                        node = children[idx];
     11689                    }
     11690                }
     11691
     11692                return node;
     11693            }
     11694
     11695            function setBookmarkEndPoint(start) {
     11696                var endPoint = bookmark[start ? 'start' : 'end'], moveLeft, moveRng, undef, offset;
     11697
     11698                if (endPoint) {
     11699                    moveLeft = endPoint.position > 0;
     11700
     11701                    moveRng = body.createTextRange();
     11702                    moveRng.moveToElementText(resolveIndexes(endPoint.indexes));
     11703
     11704                    offset = endPoint.offset;
     11705                    if (offset !== undef) {
     11706                        moveRng.collapse(endPoint.inside || moveLeft);
     11707                        moveRng.moveStart('character', moveLeft ? -offset : offset);
     11708                    } else {
     11709                        moveRng.collapse(start);
     11710                    }
     11711
     11712                    rng.setEndPoint(start ? 'StartToStart' : 'EndToStart', moveRng);
     11713
     11714                    if (start) {
     11715                        rng.collapse(true);
     11716                    }
     11717                }
     11718            }
     11719
     11720            if (bookmark.start) {
     11721                if (bookmark.start.ctrl) {
     11722                    rng = body.createControlRange();
     11723                    rng.addElement(resolveIndexes(bookmark.start.indexes));
     11724                    rng.select();
     11725                } else {
     11726                    rng = body.createTextRange();
     11727                    setBookmarkEndPoint(true);
     11728                    setBookmarkEndPoint();
     11729                    rng.select();
     11730                }
     11731            }
     11732        };
     11733
     11734        this.addRange = function(rng) {
     11735            var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
     11736                doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
     11737
     11738            function setEndPoint(start) {
     11739                var container, offset, marker, tmpRng, nodes;
     11740
     11741                marker = dom.create('a');
     11742                container = start ? startContainer : endContainer;
     11743                offset = start ? startOffset : endOffset;
     11744                tmpRng = ieRng.duplicate();
     11745
     11746                if (container == doc || container == doc.documentElement) {
     11747                    container = body;
     11748                    offset = 0;
     11749                }
     11750
     11751                if (container.nodeType == 3) {
     11752                    container.parentNode.insertBefore(marker, container);
     11753                    tmpRng.moveToElementText(marker);
     11754                    tmpRng.moveStart('character', offset);
     11755                    dom.remove(marker);
     11756                    ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
     11757                } else {
     11758                    nodes = container.childNodes;
     11759
     11760                    if (nodes.length) {
     11761                        if (offset >= nodes.length) {
     11762                            dom.insertAfter(marker, nodes[nodes.length - 1]);
     11763                        } else {
     11764                            container.insertBefore(marker, nodes[offset]);
     11765                        }
     11766
     11767                        tmpRng.moveToElementText(marker);
     11768                    } else if (container.canHaveHTML) {
     11769                        // Empty node selection for example <div>|</div>
     11770                        // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
     11771                        container.innerHTML = '<span>&#xFEFF;</span>';
     11772                        marker = container.firstChild;
     11773                        tmpRng.moveToElementText(marker);
     11774                        tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
     11775                    }
     11776
     11777                    ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
     11778                    dom.remove(marker);
     11779                }
     11780            }
     11781
     11782            // Setup some shorter versions
     11783            startContainer = rng.startContainer;
     11784            startOffset = rng.startOffset;
     11785            endContainer = rng.endContainer;
     11786            endOffset = rng.endOffset;
     11787            ieRng = body.createTextRange();
     11788
     11789            // If single element selection then try making a control selection out of it
     11790            if (startContainer == endContainer && startContainer.nodeType == 1) {
     11791                // Trick to place the caret inside an empty block element like <p></p>
     11792                if (startOffset == endOffset && !startContainer.hasChildNodes()) {
     11793                    if (startContainer.canHaveHTML) {
     11794                        // Check if previous sibling is an empty block if it is then we need to render it
     11795                        // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
     11796                        // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
     11797                        sibling = startContainer.previousSibling;
     11798                        if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
     11799                            sibling.innerHTML = '&#xFEFF;';
     11800                        } else {
     11801                            sibling = null;
     11802                        }
     11803
     11804                        startContainer.innerHTML = '<span>&#xFEFF;</span><span>&#xFEFF;</span>';
     11805                        ieRng.moveToElementText(startContainer.lastChild);
     11806                        ieRng.select();
     11807                        dom.doc.selection.clear();
     11808                        startContainer.innerHTML = '';
     11809
     11810                        if (sibling) {
     11811                            sibling.innerHTML = '';
     11812                        }
     11813                        return;
     11814                    } else {
     11815                        startOffset = dom.nodeIndex(startContainer);
     11816                        startContainer = startContainer.parentNode;
     11817                    }
     11818                }
     11819
     11820                if (startOffset == endOffset - 1) {
     11821                    try {
     11822                        ctrlElm = startContainer.childNodes[startOffset];
     11823                        ctrlRng = body.createControlRange();
     11824                        ctrlRng.addElement(ctrlElm);
     11825                        ctrlRng.select();
     11826
     11827                        // Check if the range produced is on the correct element and is a control range
     11828                        // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
     11829                        nativeRng = selection.getRng();
     11830                        if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
     11831                            return;
     11832                        }
     11833                    } catch (ex) {
     11834                        // Ignore
     11835                    }
     11836                }
     11837            }
     11838
     11839            // Set start/end point of selection
     11840            setEndPoint(true);
     11841            setEndPoint();
     11842
     11843            // Select the new range and scroll it into view
     11844            ieRng.select();
     11845        };
     11846
     11847        // Expose range method
     11848        this.getRangeAt = getRange;
     11849    }
     11850
     11851    return Selection;
     11852});
     11853
     11854// Included from: js/tinymce/classes/util/VK.js
     11855
     11856/**
     11857 * VK.js
     11858 *
     11859 * Copyright, Moxiecode Systems AB
     11860 * Released under LGPL License.
     11861 *
     11862 * License: http://www.tinymce.com/license
     11863 * Contributing: http://www.tinymce.com/contributing
     11864 */
     11865
     11866/**
     11867 * This file exposes a set of the common KeyCodes for use.  Please grow it as needed.
     11868 */
     11869define("tinymce/util/VK", [
     11870    "tinymce/Env"
     11871], function(Env) {
     11872    return {
     11873        BACKSPACE: 8,
     11874        DELETE: 46,
     11875        DOWN: 40,
     11876        ENTER: 13,
     11877        LEFT: 37,
     11878        RIGHT: 39,
     11879        SPACEBAR: 32,
     11880        TAB: 9,
     11881        UP: 38,
     11882
     11883        modifierPressed: function(e) {
     11884            return e.shiftKey || e.ctrlKey || e.altKey;
     11885        },
     11886
     11887        metaKeyPressed: function(e) {
     11888            // Check if ctrl or meta key is pressed also check if alt is false for Polish users
     11889            return (Env.mac ? e.metaKey : e.ctrlKey) && !e.altKey;
     11890        }
     11891    };
     11892});
     11893
     11894// Included from: js/tinymce/classes/dom/ControlSelection.js
     11895
     11896/**
     11897 * ControlSelection.js
     11898 *
     11899 * Copyright, Moxiecode Systems AB
     11900 * Released under LGPL License.
     11901 *
     11902 * License: http://www.tinymce.com/license
     11903 * Contributing: http://www.tinymce.com/contributing
     11904 */
     11905
     11906/**
     11907 * This class handles control selection of elements. Controls are elements
     11908 * that can be resized and needs to be selected as a whole. It adds custom resize handles
     11909 * to all browser engines that support properly disabling the built in resize logic.
     11910 *
     11911 * @class tinymce.dom.ControlSelection
     11912 */
     11913define("tinymce/dom/ControlSelection", [
     11914    "tinymce/util/VK",
     11915    "tinymce/util/Tools",
     11916    "tinymce/Env"
     11917], function(VK, Tools, Env) {
     11918    return function(selection, editor) {
     11919        var dom = editor.dom, each = Tools.each;
     11920        var selectedElm, selectedElmGhost, resizeHandles, selectedHandle, lastMouseDownEvent;
     11921        var startX, startY, selectedElmX, selectedElmY, startW, startH, ratio, resizeStarted;
     11922        var width, height, editableDoc = editor.getDoc(), rootDocument = document, isIE = Env.ie && Env.ie < 11;
     11923
     11924        // Details about each resize handle how to scale etc
     11925        resizeHandles = {
     11926            // Name: x multiplier, y multiplier, delta size x, delta size y
     11927            n:  [0.5,   0,     0,   -1],
     11928            e:  [1,    0.5,    1,    0],
     11929            s:  [0.5,   1,     0,    1],
     11930            w:  [0,    0.5,   -1,    0],
     11931            nw: [0,     0,    -1,   -1],
     11932            ne: [1,     0,     1,   -1],
     11933            se: [1,     1,     1,    1],
     11934            sw: [0,     1,    -1,    1]
     11935        };
     11936
     11937        // Add CSS for resize handles, cloned element and selected
     11938        var rootClass = '.mce-content-body';
     11939        editor.contentStyles.push(
     11940            rootClass + ' div.mce-resizehandle {' +
     11941                'position: absolute;' +
     11942                'border: 1px solid black;' +
     11943                'background: #FFF;' +
     11944                'width: 5px;' +
     11945                'height: 5px;' +
     11946                'z-index: 10000' +
     11947            '}' +
     11948            rootClass + ' .mce-resizehandle:hover {' +
     11949                'background: #000' +
     11950            '}' +
     11951            rootClass + ' img[data-mce-selected], hr[data-mce-selected] {' +
     11952                'outline: 1px solid black;' +
     11953                'resize: none' + // Have been talks about implementing this in browsers
     11954            '}' +
     11955            rootClass + ' .mce-clonedresizable {' +
     11956                'position: absolute;' +
     11957                (Env.gecko ? '' : 'outline: 1px dashed black;') + // Gecko produces trails while resizing
     11958                'opacity: .5;' +
     11959                'filter: alpha(opacity=50);' +
     11960                'z-index: 10000' +
     11961            '}'
     11962        );
     11963
     11964        function isResizable(elm) {
     11965            if (editor.settings.object_resizing === false) {
     11966                return false;
     11967            }
     11968
     11969            if (!/TABLE|IMG|DIV/.test(elm.nodeName)) {
     11970                return false;
     11971            }
     11972
     11973            if (elm.getAttribute('data-mce-resize') === 'false') {
     11974                return false;
     11975            }
     11976
     11977            return true;
     11978        }
     11979
     11980        function resizeGhostElement(e) {
     11981            var deltaX, deltaY;
     11982
     11983            // Calc new width/height
     11984            deltaX = e.screenX - startX;
     11985            deltaY = e.screenY - startY;
     11986
     11987            // Calc new size
     11988            width = deltaX * selectedHandle[2] + startW;
     11989            height = deltaY * selectedHandle[3] + startH;
     11990
     11991            // Never scale down lower than 5 pixels
     11992            width = width < 5 ? 5 : width;
     11993            height = height < 5 ? 5 : height;
     11994
     11995            // Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
     11996            if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
     11997                width = Math.round(height / ratio);
     11998                height = Math.round(width * ratio);
     11999            }
     12000
     12001            // Update ghost size
     12002            dom.setStyles(selectedElmGhost, {
     12003                width: width,
     12004                height: height
     12005            });
     12006
     12007            // Update ghost X position if needed
     12008            if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
     12009                dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
     12010            }
     12011
     12012            // Update ghost Y position if needed
     12013            if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
     12014                dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
     12015            }
     12016
     12017            if (!resizeStarted) {
     12018                editor.fire('ObjectResizeStart', {target: selectedElm, width: startW, height: startH});
     12019                resizeStarted = true;
     12020            }
     12021        }
     12022
     12023        function endGhostResize() {
     12024            resizeStarted = false;
     12025
     12026            function setSizeProp(name, value) {
     12027                if (value) {
     12028                    // Resize by using style or attribute
     12029                    if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
     12030                        dom.setStyle(selectedElm, name, value);
     12031                    } else {
     12032                        dom.setAttrib(selectedElm, name, value);
     12033                    }
     12034                }
     12035            }
     12036
     12037            // Set width/height properties
     12038            setSizeProp('width', width);
     12039            setSizeProp('height', height);
     12040
     12041            dom.unbind(editableDoc, 'mousemove', resizeGhostElement);
     12042            dom.unbind(editableDoc, 'mouseup', endGhostResize);
     12043
     12044            if (rootDocument != editableDoc) {
     12045                dom.unbind(rootDocument, 'mousemove', resizeGhostElement);
     12046                dom.unbind(rootDocument, 'mouseup', endGhostResize);
     12047            }
     12048
     12049            // Remove ghost and update resize handle positions
     12050            dom.remove(selectedElmGhost);
     12051
     12052            if (!isIE || selectedElm.nodeName == "TABLE") {
     12053                showResizeRect(selectedElm);
     12054            }
     12055
     12056            editor.fire('ObjectResized', {target: selectedElm, width: width, height: height});
     12057            editor.nodeChanged();
     12058        }
     12059
     12060        function showResizeRect(targetElm, mouseDownHandleName, mouseDownEvent) {
     12061            var position, targetWidth, targetHeight, e, rect, offsetParent = editor.getBody();
     12062
     12063            // Get position and size of target
     12064            position = dom.getPos(targetElm, offsetParent);
     12065            selectedElmX = position.x;
     12066            selectedElmY = position.y;
     12067            rect = targetElm.getBoundingClientRect(); // Fix for Gecko offsetHeight for table with caption
     12068            targetWidth = rect.width || (rect.right - rect.left);
     12069            targetHeight = rect.height || (rect.bottom - rect.top);
     12070
     12071            // Reset width/height if user selects a new image/table
     12072            if (selectedElm != targetElm) {
     12073                detachResizeStartListener();
     12074                selectedElm = targetElm;
     12075                width = height = 0;
     12076            }
     12077
     12078            // Makes it possible to disable resizing
     12079            e = editor.fire('ObjectSelected', {target: targetElm});
     12080
     12081            if (isResizable(targetElm) && !e.isDefaultPrevented()) {
     12082                each(resizeHandles, function(handle, name) {
     12083                    var handleElm, handlerContainerElm;
     12084
     12085                    function startDrag(e) {
     12086                        resizeStarted = true;
     12087
     12088                        startX = e.screenX;
     12089                        startY = e.screenY;
     12090                        startW = selectedElm.clientWidth;
     12091                        startH = selectedElm.clientHeight;
     12092                        ratio = startH / startW;
     12093                        selectedHandle = handle;
     12094
     12095                        selectedElmGhost = selectedElm.cloneNode(true);
     12096                        dom.addClass(selectedElmGhost, 'mce-clonedresizable');
     12097                        selectedElmGhost.contentEditable = false; // Hides IE move layer cursor
     12098                        selectedElmGhost.unSelectabe = true;
     12099                        dom.setStyles(selectedElmGhost, {
     12100                            left: selectedElmX,
     12101                            top: selectedElmY,
     12102                            margin: 0
     12103                        });
     12104
     12105                        selectedElmGhost.removeAttribute('data-mce-selected');
     12106                        editor.getBody().appendChild(selectedElmGhost);
     12107
     12108                        dom.bind(editableDoc, 'mousemove', resizeGhostElement);
     12109                        dom.bind(editableDoc, 'mouseup', endGhostResize);
     12110
     12111                        if (rootDocument != editableDoc) {
     12112                            dom.bind(rootDocument, 'mousemove', resizeGhostElement);
     12113                            dom.bind(rootDocument, 'mouseup', endGhostResize);
     12114                        }
     12115                    }
     12116
     12117                    if (mouseDownHandleName) {
     12118                        // Drag started by IE native resizestart
     12119                        if (name == mouseDownHandleName) {
     12120                            startDrag(mouseDownEvent);
     12121                        }
     12122
     12123                        return;
     12124                    }
     12125
     12126                    // Get existing or render resize handle
     12127                    handleElm = dom.get('mceResizeHandle' + name);
     12128                    if (!handleElm) {
     12129                        handlerContainerElm = editor.getBody();
     12130
     12131                        handleElm = dom.add(handlerContainerElm, 'div', {
     12132                            id: 'mceResizeHandle' + name,
     12133                            'data-mce-bogus': true,
     12134                            'class': 'mce-resizehandle',
     12135                            contentEditable: false, // Hides IE move layer cursor
     12136                            unSelectabe: true,
     12137                            style: 'cursor:' + name + '-resize; margin:0; padding:0'
     12138                        });
     12139
     12140                        dom.bind(handleElm, 'mousedown', function(e) {
     12141                            e.preventDefault();
     12142                            startDrag(e);
     12143                        });
     12144                    } else {
     12145                        dom.show(handleElm);
     12146                    }
     12147
     12148                    /*
     12149                    var halfHandleW = handleElm.offsetWidth / 2;
     12150                    var halfHandleH = handleElm.offsetHeight / 2;
     12151
     12152                    // Position element
     12153                    dom.setStyles(handleElm, {
     12154                        left: Math.floor((targetWidth * handle[0] + selectedElmX) - halfHandleW + (handle[2] * halfHandleW)),
     12155                        top: Math.floor((targetHeight * handle[1] + selectedElmY) - halfHandleH + (handle[3] * halfHandleH))
     12156                    });
     12157                    */
     12158
     12159                    // Position element
     12160                    dom.setStyles(handleElm, {
     12161                        left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
     12162                        top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
     12163                    });
     12164                });
     12165            } else {
     12166                hideResizeRect();
     12167            }
     12168
     12169            selectedElm.setAttribute('data-mce-selected', '1');
     12170        }
     12171
     12172        function hideResizeRect() {
     12173            var name, handleElm;
     12174
     12175            if (selectedElm) {
     12176                selectedElm.removeAttribute('data-mce-selected');
     12177            }
     12178
     12179            for (name in resizeHandles) {
     12180                handleElm = dom.get('mceResizeHandle' + name);
     12181                if (handleElm) {
     12182                    dom.unbind(handleElm);
     12183                    dom.remove(handleElm);
     12184                }
     12185            }
     12186        }
     12187
     12188        function updateResizeRect(e) {
     12189            var controlElm;
     12190
     12191            function isChildOrEqual(node, parent) {
     12192                do {
     12193                    if (node === parent) {
     12194                        return true;
     12195                    }
     12196                } while ((node = node.parentNode));
     12197            }
     12198
     12199            // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
     12200            each(dom.select('img[data-mce-selected],hr[data-mce-selected]'), function(img) {
     12201                img.removeAttribute('data-mce-selected');
     12202            });
     12203
     12204            controlElm = e.type == 'mousedown' ? e.target : selection.getNode();
     12205            controlElm = dom.getParent(controlElm, isIE ? 'table' : 'table,img,hr');
     12206
     12207            if (controlElm) {
     12208                disableGeckoResize();
     12209
     12210                if (isChildOrEqual(selection.getStart(), controlElm) && isChildOrEqual(selection.getEnd(), controlElm)) {
     12211                    if (!isIE || (controlElm != selection.getStart() && selection.getStart().nodeName !== 'IMG')) {
     12212                        showResizeRect(controlElm);
     12213                        return;
     12214                    }
     12215                }
     12216            }
     12217
     12218            hideResizeRect();
     12219        }
     12220
     12221        function attachEvent(elm, name, func) {
     12222            if (elm && elm.attachEvent) {
     12223                elm.attachEvent('on' + name, func);
     12224            }
     12225        }
     12226
     12227        function detachEvent(elm, name, func) {
     12228            if (elm && elm.detachEvent) {
     12229                elm.detachEvent('on' + name, func);
     12230            }
     12231        }
     12232
     12233        function resizeNativeStart(e) {
     12234            var target = e.srcElement, pos, name, corner, cornerX, cornerY, relativeX, relativeY;
     12235
     12236            pos = target.getBoundingClientRect();
     12237            relativeX = lastMouseDownEvent.clientX - pos.left;
     12238            relativeY = lastMouseDownEvent.clientY - pos.top;
     12239
     12240            // Figure out what corner we are draging on
     12241            for (name in resizeHandles) {
     12242                corner = resizeHandles[name];
     12243
     12244                cornerX = target.offsetWidth * corner[0];
     12245                cornerY = target.offsetHeight * corner[1];
     12246
     12247                if (Math.abs(cornerX - relativeX) < 8 && Math.abs(cornerY - relativeY) < 8) {
     12248                    selectedHandle = corner;
     12249                    break;
     12250                }
     12251            }
     12252
     12253            // Remove native selection and let the magic begin
     12254            resizeStarted = true;
     12255            editor.getDoc().selection.empty();
     12256            showResizeRect(target, name, lastMouseDownEvent);
     12257        }
     12258
     12259        function nativeControlSelect(e) {
     12260            var target = e.srcElement;
     12261
     12262            if (target != selectedElm) {
     12263                detachResizeStartListener();
     12264
     12265                if (target.id.indexOf('mceResizeHandle') === 0) {
     12266                    e.returnValue = false;
     12267                    return;
     12268                }
     12269
     12270                if (target.nodeName == 'IMG' || target.nodeName == 'TABLE') {
     12271                    hideResizeRect();
     12272                    selectedElm = target;
     12273                    attachEvent(target, 'resizestart', resizeNativeStart);
     12274                }
     12275            }
     12276        }
     12277
     12278        function detachResizeStartListener() {
     12279            detachEvent(selectedElm, 'resizestart', resizeNativeStart);
     12280        }
     12281
     12282        function disableGeckoResize() {
     12283            try {
     12284                // Disable object resizing on Gecko
     12285                editor.getDoc().execCommand('enableObjectResizing', false, false);
     12286            } catch (ex) {
     12287                // Ignore
     12288            }
     12289        }
     12290
     12291        function controlSelect(elm) {
     12292            var ctrlRng;
     12293
     12294            if (!isIE) {
     12295                return;
     12296            }
     12297
     12298            ctrlRng = editableDoc.body.createControlRange();
     12299
     12300            try {
     12301                ctrlRng.addElement(elm);
     12302                ctrlRng.select();
     12303                return true;
     12304            } catch (ex) {
     12305                // Ignore since the element can't be control selected for example a P tag
     12306            }
     12307        }
     12308
     12309        editor.on('init', function() {
     12310            if (isIE) {
     12311                // Hide the resize rect on resize and reselect the image
     12312                editor.on('ObjectResized', function(e) {
     12313                    if (e.target.nodeName != 'TABLE') {
     12314                        hideResizeRect();
     12315                        controlSelect(e.target);
     12316                    }
     12317                });
     12318
     12319                attachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
     12320
     12321                editor.on('mousedown', function(e) {
     12322                    lastMouseDownEvent = e;
     12323                });
     12324            } else {
     12325                disableGeckoResize();
     12326
     12327                if (Env.ie >= 11) {
     12328                    // TODO: Drag/drop doesn't work
     12329                    editor.on('mouseup', function(e) {
     12330                        var nodeName = e.target.nodeName;
     12331
     12332                        if (/^(TABLE|IMG|HR)$/.test(nodeName)) {
     12333                            editor.selection.select(e.target, nodeName == 'TABLE');
     12334                            editor.nodeChanged();
     12335                        }
     12336                    });
     12337
     12338                    editor.dom.bind(editor.getBody(), 'mscontrolselect', function(e) {
     12339                        if (/^(TABLE|IMG|HR)$/.test(e.target.nodeName)) {
     12340                            e.preventDefault();
     12341                        }
     12342                    });
     12343                }
     12344            }
     12345
     12346            editor.on('nodechange mousedown mouseup ResizeEditor', updateResizeRect);
     12347
     12348            // Update resize rect while typing in a table
     12349            editor.on('keydown keyup', function(e) {
     12350                if (selectedElm && selectedElm.nodeName == "TABLE") {
     12351                    updateResizeRect(e);
     12352                }
     12353            });
     12354
     12355            // Hide rect on focusout since it would float on top of windows otherwise
     12356            //editor.on('focusout', hideResizeRect);
     12357        });
     12358
     12359        function destroy() {
     12360            selectedElm = selectedElmGhost = null;
     12361
     12362            if (isIE) {
     12363                detachResizeStartListener();
     12364                detachEvent(editor.getBody(), 'controlselect', nativeControlSelect);
     12365            }
     12366        }
     12367
     12368        return {
     12369            controlSelect: controlSelect,
     12370            destroy: destroy
     12371        };
     12372    };
     12373});
     12374
     12375// Included from: js/tinymce/classes/dom/Selection.js
     12376
     12377/**
     12378 * Selection.js
     12379 *
     12380 * Copyright, Moxiecode Systems AB
     12381 * Released under LGPL License.
     12382 *
     12383 * License: http://www.tinymce.com/license
     12384 * Contributing: http://www.tinymce.com/contributing
     12385 */
     12386
     12387/**
     12388 * This class handles text and control selection it's an crossbrowser utility class.
     12389 * Consult the TinyMCE Wiki API for more details and examples on how to use this class.
     12390 *
     12391 * @class tinymce.dom.Selection
     12392 * @example
     12393 * // Getting the currently selected node for the active editor
     12394 * alert(tinymce.activeEditor.selection.getNode().nodeName);
     12395 */
     12396define("tinymce/dom/Selection", [
     12397    "tinymce/dom/TreeWalker",
     12398    "tinymce/dom/TridentSelection",
     12399    "tinymce/dom/ControlSelection",
     12400    "tinymce/Env",
     12401    "tinymce/util/Tools"
     12402], function(TreeWalker, TridentSelection, ControlSelection, Env, Tools) {
     12403    var each = Tools.each, grep = Tools.grep, trim = Tools.trim;
     12404    var isIE = Env.ie, isOpera = Env.opera;
     12405
     12406    /**
     12407     * Constructs a new selection instance.
     12408     *
     12409     * @constructor
     12410     * @method Selection
     12411     * @param {tinymce.dom.DOMUtils} dom DOMUtils object reference.
     12412     * @param {Window} win Window to bind the selection object to.
     12413     * @param {tinymce.dom.Serializer} serializer DOM serialization class to use for getContent.
     12414     */
     12415    function Selection(dom, win, serializer, editor) {
     12416        var self = this;
     12417
     12418        self.dom = dom;
     12419        self.win = win;
     12420        self.serializer = serializer;
     12421        self.editor = editor;
     12422
     12423        self.controlSelection = new ControlSelection(self, editor);
     12424
     12425        // No W3C Range support
     12426        if (!self.win.getSelection) {
     12427            self.tridentSel = new TridentSelection(self);
     12428        }
     12429    }
     12430
     12431    Selection.prototype = {
     12432        /**
     12433         * Move the selection cursor range to the specified node and offset.
     12434         * If there is no node specified it will move it to the first suitable location within the body.
     12435         *
     12436         * @method setCursorLocation
     12437         * @param {Node} node Optional node to put the cursor in.
     12438         * @param {Number} offset Optional offset from the start of the node to put the cursor at.
     12439         */
     12440        setCursorLocation: function(node, offset) {
     12441            var self = this, rng = self.dom.createRng();
     12442
     12443            if (!node) {
     12444                self._moveEndPoint(rng, self.editor.getBody(), true);
     12445                self.setRng(rng);
     12446            } else {
     12447                rng.setStart(node, offset);
     12448                rng.setEnd(node, offset);
     12449                self.setRng(rng);
     12450                self.collapse(false);
     12451            }
     12452        },
     12453
     12454        /**
     12455         * Returns the selected contents using the DOM serializer passed in to this class.
     12456         *
     12457         * @method getContent
     12458         * @param {Object} s Optional settings class with for example output format text or html.
     12459         * @return {String} Selected contents in for example HTML format.
     12460         * @example
     12461         * // Alerts the currently selected contents
     12462         * alert(tinymce.activeEditor.selection.getContent());
     12463         *
     12464         * // Alerts the currently selected contents as plain text
     12465         * alert(tinymce.activeEditor.selection.getContent({format: 'text'}));
     12466         */
     12467        getContent: function(args) {
     12468            var self = this, rng = self.getRng(), tmpElm = self.dom.create("body");
     12469            var se = self.getSel(), whiteSpaceBefore, whiteSpaceAfter, fragment;
     12470
     12471            args = args || {};
     12472            whiteSpaceBefore = whiteSpaceAfter = '';
     12473            args.get = true;
     12474            args.format = args.format || 'html';
     12475            args.selection = true;
     12476            self.editor.fire('BeforeGetContent', args);
     12477
     12478            if (args.format == 'text') {
     12479                return self.isCollapsed() ? '' : (rng.text || (se.toString ? se.toString() : ''));
     12480            }
     12481
     12482            if (rng.cloneContents) {
     12483                fragment = rng.cloneContents();
     12484
     12485                if (fragment) {
     12486                    tmpElm.appendChild(fragment);
     12487                }
     12488            } else if (rng.item !== undefined || rng.htmlText !== undefined) {
     12489                // IE will produce invalid markup if elements are present that
     12490                // it doesn't understand like custom elements or HTML5 elements.
     12491                // Adding a BR in front of the contents and then remoiving it seems to fix it though.
     12492                tmpElm.innerHTML = '<br>' + (rng.item ? rng.item(0).outerHTML : rng.htmlText);
     12493                tmpElm.removeChild(tmpElm.firstChild);
     12494            } else {
     12495                tmpElm.innerHTML = rng.toString();
     12496            }
     12497
     12498            // Keep whitespace before and after
     12499            if (/^\s/.test(tmpElm.innerHTML)) {
     12500                whiteSpaceBefore = ' ';
     12501            }
     12502
     12503            if (/\s+$/.test(tmpElm.innerHTML)) {
     12504                whiteSpaceAfter = ' ';
     12505            }
     12506
     12507            args.getInner = true;
     12508
     12509            args.content = self.isCollapsed() ? '' : whiteSpaceBefore + self.serializer.serialize(tmpElm, args) + whiteSpaceAfter;
     12510            self.editor.fire('GetContent', args);
     12511
     12512            return args.content;
     12513        },
     12514
     12515        /**
     12516         * Sets the current selection to the specified content. If any contents is selected it will be replaced
     12517         * with the contents passed in to this function. If there is no selection the contents will be inserted
     12518         * where the caret is placed in the editor/page.
     12519         *
     12520         * @method setContent
     12521         * @param {String} content HTML contents to set could also be other formats depending on settings.
     12522         * @param {Object} args Optional settings object with for example data format.
     12523         * @example
     12524         * // Inserts some HTML contents at the current selection
     12525         * tinymce.activeEditor.selection.setContent('<strong>Some contents</strong>');
     12526         */
     12527        setContent: function(content, args) {
     12528            var self = this, rng = self.getRng(), caretNode, doc = self.win.document, frag, temp;
     12529
     12530            args = args || {format: 'html'};
     12531            args.set = true;
     12532            args.selection = true;
     12533            content = args.content = content;
     12534
     12535            // Dispatch before set content event
     12536            if (!args.no_events) {
     12537                self.editor.fire('BeforeSetContent', args);
     12538            }
     12539
     12540            content = args.content;
     12541
     12542            if (rng.insertNode) {
     12543                // Make caret marker since insertNode places the caret in the beginning of text after insert
     12544                content += '<span id="__caret">_</span>';
     12545
     12546                // Delete and insert new node
     12547                if (rng.startContainer == doc && rng.endContainer == doc) {
     12548                    // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
     12549                    doc.body.innerHTML = content;
     12550                } else {
     12551                    rng.deleteContents();
     12552
     12553                    if (doc.body.childNodes.length === 0) {
     12554                        doc.body.innerHTML = content;
     12555                    } else {
     12556                        // createContextualFragment doesn't exists in IE 9 DOMRanges
     12557                        if (rng.createContextualFragment) {
     12558                            rng.insertNode(rng.createContextualFragment(content));
     12559                        } else {
     12560                            // Fake createContextualFragment call in IE 9
     12561                            frag = doc.createDocumentFragment();
     12562                            temp = doc.createElement('div');
     12563
     12564                            frag.appendChild(temp);
     12565                            temp.outerHTML = content;
     12566
     12567                            rng.insertNode(frag);
     12568                        }
     12569                    }
     12570                }
     12571
     12572                // Move to caret marker
     12573                caretNode = self.dom.get('__caret');
     12574
     12575                // Make sure we wrap it compleatly, Opera fails with a simple select call
     12576                rng = doc.createRange();
     12577                rng.setStartBefore(caretNode);
     12578                rng.setEndBefore(caretNode);
     12579                self.setRng(rng);
     12580
     12581                // Remove the caret position
     12582                self.dom.remove('__caret');
     12583
     12584                try {
     12585                    self.setRng(rng);
     12586                } catch (ex) {
     12587                    // Might fail on Opera for some odd reason
     12588                }
     12589            } else {
     12590                if (rng.item) {
     12591                    // Delete content and get caret text selection
     12592                    doc.execCommand('Delete', false, null);
     12593                    rng = self.getRng();
     12594                }
     12595
     12596                // Explorer removes spaces from the beginning of pasted contents
     12597                if (/^\s+/.test(content)) {
     12598                    rng.pasteHTML('<span id="__mce_tmp">_</span>' + content);
     12599                    self.dom.remove('__mce_tmp');
     12600                } else {
     12601                    rng.pasteHTML(content);
     12602                }
     12603            }
     12604
     12605            // Dispatch set content event
     12606            if (!args.no_events) {
     12607                self.editor.fire('SetContent', args);
     12608            }
     12609        },
     12610
     12611        /**
     12612         * Returns the start element of a selection range. If the start is in a text
     12613         * node the parent element will be returned.
     12614         *
     12615         * @method getStart
     12616         * @return {Element} Start element of selection range.
     12617         */
     12618        getStart: function() {
     12619            var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
     12620
     12621            if (rng.duplicate || rng.item) {
     12622                // Control selection, return first item
     12623                if (rng.item) {
     12624                    return rng.item(0);
     12625                }
     12626
     12627                // Get start element
     12628                checkRng = rng.duplicate();
     12629                checkRng.collapse(1);
     12630                startElement = checkRng.parentElement();
     12631                if (startElement.ownerDocument !== self.dom.doc) {
     12632                    startElement = self.dom.getRoot();
     12633                }
     12634
     12635                // Check if range parent is inside the start element, then return the inner parent element
     12636                // This will fix issues when a single element is selected, IE would otherwise return the wrong start element
     12637                parentElement = node = rng.parentElement();
     12638                while ((node = node.parentNode)) {
     12639                    if (node == startElement) {
     12640                        startElement = parentElement;
     12641                        break;
     12642                    }
     12643                }
     12644
     12645                return startElement;
     12646            } else {
     12647                startElement = rng.startContainer;
     12648
     12649                if (startElement.nodeType == 1 && startElement.hasChildNodes()) {
     12650                    startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
     12651                }
     12652
     12653                if (startElement && startElement.nodeType == 3) {
     12654                    return startElement.parentNode;
     12655                }
     12656
     12657                return startElement;
     12658            }
     12659        },
     12660
     12661        /**
     12662         * Returns the end element of a selection range. If the end is in a text
     12663         * node the parent element will be returned.
     12664         *
     12665         * @method getEnd
     12666         * @return {Element} End element of selection range.
     12667         */
     12668        getEnd: function() {
     12669            var self = this, rng = self.getRng(), endElement, endOffset;
     12670
     12671            if (rng.duplicate || rng.item) {
     12672                if (rng.item) {
     12673                    return rng.item(0);
     12674                }
     12675
     12676                rng = rng.duplicate();
     12677                rng.collapse(0);
     12678                endElement = rng.parentElement();
     12679                if (endElement.ownerDocument !== self.dom.doc) {
     12680                    endElement = self.dom.getRoot();
     12681                }
     12682
     12683                if (endElement && endElement.nodeName == 'BODY') {
     12684                    return endElement.lastChild || endElement;
     12685                }
     12686
     12687                return endElement;
     12688            } else {
     12689                endElement = rng.endContainer;
     12690                endOffset = rng.endOffset;
     12691
     12692                if (endElement.nodeType == 1 && endElement.hasChildNodes()) {
     12693                    endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
     12694                }
     12695
     12696                if (endElement && endElement.nodeType == 3) {
     12697                    return endElement.parentNode;
     12698                }
     12699
     12700                return endElement;
     12701            }
     12702        },
     12703
     12704        /**
     12705         * Returns a bookmark location for the current selection. This bookmark object
     12706         * can then be used to restore the selection after some content modification to the document.
     12707         *
     12708         * @method getBookmark
     12709         * @param {Number} type Optional state if the bookmark should be simple or not. Default is complex.
     12710         * @param {Boolean} normalized Optional state that enables you to get a position that it would be after normalization.
     12711         * @return {Object} Bookmark object, use moveToBookmark with this object to restore the selection.
     12712         * @example
     12713         * // Stores a bookmark of the current selection
     12714         * var bm = tinymce.activeEditor.selection.getBookmark();
     12715         *
     12716         * tinymce.activeEditor.setContent(tinymce.activeEditor.getContent() + 'Some new content');
     12717         *
     12718         * // Restore the selection bookmark
     12719         * tinymce.activeEditor.selection.moveToBookmark(bm);
     12720         */
     12721        getBookmark: function(type, normalized) {
     12722            var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, chr = '&#xFEFF;', styles;
     12723
     12724            function findIndex(name, element) {
     12725                var index = 0;
     12726
     12727                each(dom.select(name), function(node, i) {
     12728                    if (node == element) {
     12729                        index = i;
     12730                    }
     12731                });
     12732
     12733                return index;
     12734            }
     12735
     12736            function normalizeTableCellSelection(rng) {
     12737                function moveEndPoint(start) {
     12738                    var container, offset, childNodes, prefix = start ? 'start' : 'end';
     12739
     12740                    container = rng[prefix + 'Container'];
     12741                    offset = rng[prefix + 'Offset'];
     12742
     12743                    if (container.nodeType == 1 && container.nodeName == "TR") {
     12744                        childNodes = container.childNodes;
     12745                        container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
     12746                        if (container) {
     12747                            offset = start ? 0 : container.childNodes.length;
     12748                            rng['set' + (start ? 'Start' : 'End')](container, offset);
     12749                        }
     12750                    }
     12751                }
     12752
     12753                moveEndPoint(true);
     12754                moveEndPoint();
     12755
     12756                return rng;
     12757            }
     12758
     12759            function getLocation() {
     12760                var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
     12761
     12762                function getPoint(rng, start) {
     12763                    var container = rng[start ? 'startContainer' : 'endContainer'],
     12764                        offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
     12765
     12766                    if (container.nodeType == 3) {
     12767                        if (normalized) {
     12768                            for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) {
     12769                                offset += node.nodeValue.length;
     12770                            }
     12771                        }
     12772
     12773                        point.push(offset);
     12774                    } else {
     12775                        childNodes = container.childNodes;
     12776
     12777                        if (offset >= childNodes.length && childNodes.length) {
     12778                            after = 1;
     12779                            offset = Math.max(0, childNodes.length - 1);
     12780                        }
     12781
     12782                        point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
     12783                    }
     12784
     12785                    for (; container && container != root; container = container.parentNode) {
     12786                        point.push(t.dom.nodeIndex(container, normalized));
     12787                    }
     12788
     12789                    return point;
     12790                }
     12791
     12792                bookmark.start = getPoint(rng, true);
     12793
     12794                if (!t.isCollapsed()) {
     12795                    bookmark.end = getPoint(rng);
     12796                }
     12797
     12798                return bookmark;
     12799            }
     12800
     12801            if (type == 2) {
     12802                element = t.getNode();
     12803                name = element.nodeName;
     12804
     12805                if (name == 'IMG') {
     12806                    return {name: name, index: findIndex(name, element)};
     12807                }
     12808
     12809                if (t.tridentSel) {
     12810                    return t.tridentSel.getBookmark(type);
     12811                }
     12812
     12813                return getLocation();
     12814            }
     12815
     12816            // Handle simple range
     12817            if (type) {
     12818                return {rng: t.getRng()};
     12819            }
     12820
     12821            rng = t.getRng();
     12822            id = dom.uniqueId();
     12823            collapsed = t.isCollapsed();
     12824            styles = 'overflow:hidden;line-height:0px';
     12825
     12826            // Explorer method
     12827            if (rng.duplicate || rng.item) {
     12828                // Text selection
     12829                if (!rng.item) {
     12830                    rng2 = rng.duplicate();
     12831
     12832                    try {
     12833                        // Insert start marker
     12834                        rng.collapse();
     12835                        rng.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>');
     12836
     12837                        // Insert end marker
     12838                        if (!collapsed) {
     12839                            rng2.collapse(false);
     12840
     12841                            // Detect the empty space after block elements in IE and move the
     12842                            // end back one character <p></p>] becomes <p>]</p>
     12843                            rng.moveToElementText(rng2.parentElement());
     12844                            if (rng.compareEndPoints('StartToEnd', rng2) === 0) {
     12845                                rng2.move('character', -1);
     12846                            }
     12847
     12848                            rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
     12849                        }
     12850                    } catch (ex) {
     12851                        // IE might throw unspecified error so lets ignore it
     12852                        return null;
     12853                    }
     12854                } else {
     12855                    // Control selection
     12856                    element = rng.item(0);
     12857                    name = element.nodeName;
     12858
     12859                    return {name: name, index: findIndex(name, element)};
     12860                }
     12861            } else {
     12862                element = t.getNode();
     12863                name = element.nodeName;
     12864                if (name == 'IMG') {
     12865                    return {name: name, index: findIndex(name, element)};
     12866                }
     12867
     12868                // W3C method
     12869                rng2 = normalizeTableCellSelection(rng.cloneRange());
     12870
     12871                // Insert end marker
     12872                if (!collapsed) {
     12873                    rng2.collapse(false);
     12874                    rng2.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_end', style: styles}, chr));
     12875                }
     12876
     12877                rng = normalizeTableCellSelection(rng);
     12878                rng.collapse(true);
     12879                rng.insertNode(dom.create('span', {'data-mce-type': "bookmark", id: id + '_start', style: styles}, chr));
     12880            }
     12881
     12882            t.moveToBookmark({id: id, keep: 1});
     12883
     12884            return {id: id};
     12885        },