| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-form-field-HtmlEditor'>/**</span> * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be * automatically hidden when needed. These are noted in the config options where appropriate. * * The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not * enabled by default unless the global {@link Ext.tip.QuickTipManager} singleton is * {@link Ext.tip.QuickTipManager#init initialized}. * * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an * Editor within any element that has display set to 'none' can cause problems in Safari and Firefox due to their * default iframe reloading bugs. * * # Example usage * * Simple example rendered with default options: * *     @example *     Ext.tip.QuickTipManager.init();  // enable tooltips *     Ext.create('Ext.form.HtmlEditor', { *         width: 580, *         height: 250, *         renderTo: Ext.getBody() *     }); * * Passed via xtype into a container and with custom options: * *     @example *     Ext.tip.QuickTipManager.init();  // enable tooltips *     new Ext.panel.Panel({ *         title: 'HTML Editor', *         renderTo: Ext.getBody(), *         width: 550, *         height: 250, *         frame: true, *         layout: 'fit', *         items: { *             xtype: 'htmleditor', *             enableColors: false, *             enableAlignments: false *         } *     }); *      * # Reflow issues *  * In some browsers, a layout reflow will cause the underlying editor iframe to be reset. This * is most commonly seen when using the editor in collapsed panels with animation. In these cases * it is best to avoid animation. More information can be found here: https://bugzilla.mozilla.org/show_bug.cgi?id=90268  */Ext.define('Ext.form.field.HtmlEditor', {    extend:'Ext.Component',    mixins: {        labelable: 'Ext.form.Labelable',        field: 'Ext.form.field.Field'    },    alias: 'widget.htmleditor',    alternateClassName: 'Ext.form.HtmlEditor',    requires: [        'Ext.tip.QuickTipManager',        'Ext.picker.Color',        'Ext.toolbar.Item',        'Ext.toolbar.Toolbar',        'Ext.util.Format',        'Ext.layout.component.field.HtmlEditor'    ],    childEls: [        'iframeEl', 'textareaEl'    ],    fieldSubTpl: [        '{beforeTextAreaTpl}',        '<textarea id="{cmpId}-textareaEl" name="{name}" tabIndex="-1" {inputAttrTpl}',                 ' class="{textareaCls}" style="{size}" autocomplete="off">',            '{[Ext.util.Format.htmlEncode(values.value)]}',        '</textarea>',        '{afterTextAreaTpl}',        '{beforeIFrameTpl}',        '<iframe id="{cmpId}-iframeEl" name="{iframeName}" frameBorder="0" {iframeAttrTpl}',               ' style="overflow:auto;{size}" src="{iframeSrc}"></iframe>',        '{afterIFrameTpl}',        {            disableFormats: true        }    ],    subTplInsertions: [<span id='Ext-form-field-HtmlEditor-cfg-beforeTextAreaTpl'>        /**</span>         * @cfg {String/Array/Ext.XTemplate} beforeTextAreaTpl         * An optional string or `XTemplate` configuration to insert in the field markup         * before the textarea element. If an `XTemplate` is used, the component's         * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.         */        'beforeTextAreaTpl',<span id='Ext-form-field-HtmlEditor-cfg-afterTextAreaTpl'>        /**</span>         * @cfg {String/Array/Ext.XTemplate} afterTextAreaTpl         * An optional string or `XTemplate` configuration to insert in the field markup         * after the textarea element. If an `XTemplate` is used, the component's         * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.         */        'afterTextAreaTpl',<span id='Ext-form-field-HtmlEditor-cfg-beforeIFrameTpl'>        /**</span>         * @cfg {String/Array/Ext.XTemplate} beforeIFrameTpl         * An optional string or `XTemplate` configuration to insert in the field markup         * before the iframe element. If an `XTemplate` is used, the component's         * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.         */        'beforeIFrameTpl',<span id='Ext-form-field-HtmlEditor-cfg-afterIFrameTpl'>        /**</span>         * @cfg {String/Array/Ext.XTemplate} afterIFrameTpl         * An optional string or `XTemplate` configuration to insert in the field markup         * after the iframe element. If an `XTemplate` is used, the component's         * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.         */        'afterIFrameTpl',<span id='Ext-form-field-HtmlEditor-cfg-iframeAttrTpl'>        /**</span>         * @cfg {String/Array/Ext.XTemplate} iframeAttrTpl         * An optional string or `XTemplate` configuration to insert in the field markup         * inside the iframe element (as attributes). If an `XTemplate` is used, the component's         * {@link Ext.form.field.Base#getSubTplData subTpl data} serves as the context.         */        'iframeAttrTpl',        // inherited        'inputAttrTpl'    ],<span id='Ext-form-field-HtmlEditor-cfg-enableFormat'>    /**</span>     * @cfg {Boolean} enableFormat     * Enable the bold, italic and underline buttons     */    enableFormat : true,<span id='Ext-form-field-HtmlEditor-cfg-enableFontSize'>    /**</span>     * @cfg {Boolean} enableFontSize     * Enable the increase/decrease font size buttons     */    enableFontSize : true,<span id='Ext-form-field-HtmlEditor-cfg-enableColors'>    /**</span>     * @cfg {Boolean} enableColors     * Enable the fore/highlight color buttons     */    enableColors : true,<span id='Ext-form-field-HtmlEditor-cfg-enableAlignments'>    /**</span>     * @cfg {Boolean} enableAlignments     * Enable the left, center, right alignment buttons     */    enableAlignments : true,<span id='Ext-form-field-HtmlEditor-cfg-enableLists'>    /**</span>     * @cfg {Boolean} enableLists     * Enable the bullet and numbered list buttons. Not available in Safari.     */    enableLists : true,<span id='Ext-form-field-HtmlEditor-cfg-enableSourceEdit'>    /**</span>     * @cfg {Boolean} enableSourceEdit     * Enable the switch to source edit button. Not available in Safari.     */    enableSourceEdit : true,<span id='Ext-form-field-HtmlEditor-cfg-enableLinks'>    /**</span>     * @cfg {Boolean} enableLinks     * Enable the create link button. Not available in Safari.     */    enableLinks : true,<span id='Ext-form-field-HtmlEditor-cfg-enableFont'>    /**</span>     * @cfg {Boolean} enableFont     * Enable font selection. Not available in Safari.     */    enableFont : true,    //<locale><span id='Ext-form-field-HtmlEditor-cfg-createLinkText'>    /**</span>     * @cfg {String} createLinkText     * The default text for the create link prompt     */    createLinkText : 'Please enter the URL for the link:',    //</locale><span id='Ext-form-field-HtmlEditor-cfg-defaultLinkValue'>    /**</span>     * @cfg {String} [defaultLinkValue='http://']     * The default value for the create link prompt     */    defaultLinkValue : 'http:/'+'/',<span id='Ext-form-field-HtmlEditor-cfg-fontFamilies'>    /**</span>     * @cfg {String[]} fontFamilies     * An array of available font families     */    fontFamilies : [        'Arial',        'Courier New',        'Tahoma',        'Times New Roman',        'Verdana'    ],    defaultFont: 'tahoma',<span id='Ext-form-field-HtmlEditor-cfg-defaultValue'>    /**</span>     * @cfg {String} defaultValue     * A default value to be put into the editor to resolve focus issues.     *     * Defaults to (Non-breaking space) in Opera and IE6,     * (Zero-width space) in all other browsers.     */    defaultValue: (Ext.isOpera || Ext.isIE6) ? '&#160;' : '&#8203;',    editorWrapCls: Ext.baseCSSPrefix + 'html-editor-wrap',    componentLayout: 'htmleditor',    // private properties    initialized : false,    activated : false,    sourceEditMode : false,    iframePad:3,    hideMode:'offsets',    afterBodyEl: '</div>',    maskOnDisable: true,    // private    initComponent : function(){        var me = this;        me.addEvents(<span id='Ext-form-field-HtmlEditor-event-initialize'>            /**</span>             * @event initialize             * Fires when the editor is fully initialized (including the iframe)             * @param {Ext.form.field.HtmlEditor} this             */            'initialize',<span id='Ext-form-field-HtmlEditor-event-activate'>            /**</span>             * @event activate             * Fires when the editor is first receives the focus. Any insertion must wait until after this event.             * @param {Ext.form.field.HtmlEditor} this             */            'activate',<span id='Ext-form-field-HtmlEditor-event-beforesync'>             /**</span>             * @event beforesync             * Fires before the textarea is updated with content from the editor iframe. Return false to cancel the             * sync.             * @param {Ext.form.field.HtmlEditor} this             * @param {String} html             */            'beforesync',<span id='Ext-form-field-HtmlEditor-event-beforepush'>             /**</span>             * @event beforepush             * Fires before the iframe editor is updated with content from the textarea. Return false to cancel the             * push.             * @param {Ext.form.field.HtmlEditor} this             * @param {String} html             */            'beforepush',<span id='Ext-form-field-HtmlEditor-event-sync'>             /**</span>             * @event sync             * Fires when the textarea is updated with content from the editor iframe.             * @param {Ext.form.field.HtmlEditor} this             * @param {String} html             */            'sync',<span id='Ext-form-field-HtmlEditor-event-push'>             /**</span>             * @event push             * Fires when the iframe editor is updated with content from the textarea.             * @param {Ext.form.field.HtmlEditor} this             * @param {String} html             */            'push',<span id='Ext-form-field-HtmlEditor-event-editmodechange'>             /**</span>             * @event editmodechange             * Fires when the editor switches edit modes             * @param {Ext.form.field.HtmlEditor} this             * @param {Boolean} sourceEdit True if source edit, false if standard editing.             */            'editmodechange'        );        me.callParent(arguments);        me.createToolbar(me);        // Init mixins        me.initLabelable();        me.initField();    },<span id='Ext-form-field-HtmlEditor-method-getRefItems'>    /**</span>     * @private     * Must define this function to allow the Layout base class to collect all descendant layouts to be run.     */    getRefItems: function() {        return [ this.toolbar ];    },    /*     * Called when the editor creates its toolbar. Override this method if you need to     * add custom toolbar buttons.     * @param {Ext.form.field.HtmlEditor} editor     * @protected     */    createToolbar : function(editor){        var me = this,            items = [], i,            tipsEnabled = Ext.tip.QuickTipManager && Ext.tip.QuickTipManager.isEnabled(),            baseCSSPrefix = Ext.baseCSSPrefix,            fontSelectItem, toolbar, undef;        function btn(id, toggle, handler){            return {                itemId : id,                cls : baseCSSPrefix + 'btn-icon',                iconCls: baseCSSPrefix + 'edit-'+id,                enableToggle:toggle !== false,                scope: editor,                handler:handler||editor.relayBtnCmd,                clickEvent: 'mousedown',                tooltip: tipsEnabled ? editor.buttonTips[id] || undef : undef,                overflowText: editor.buttonTips[id].title || undef,                tabIndex: -1            };        }        if (me.enableFont && !Ext.isSafari2) {            fontSelectItem = Ext.widget('component', {                renderTpl: [                    '<select id="{id}-selectEl" class="{cls}">',                        '<tpl for="fonts">',                            '<option value="{[values.toLowerCase()]}" style="font-family:{.}"<tpl if="values.toLowerCase()==parent.defaultFont"> selected</tpl>>{.}</option>',                        '</tpl>',                    '</select>'                ],                renderData: {                    cls: baseCSSPrefix + 'font-select',                    fonts: me.fontFamilies,                    defaultFont: me.defaultFont                },                childEls: ['selectEl'],                afterRender: function() {                    me.fontSelect = this.selectEl;                    Ext.Component.prototype.afterRender.apply(this, arguments);                },                onDisable: function() {                    var selectEl = this.selectEl;                    if (selectEl) {                        selectEl.dom.disabled = true;                    }                    Ext.Component.prototype.onDisable.apply(this, arguments);                },                onEnable: function() {                    var selectEl = this.selectEl;                    if (selectEl) {                        selectEl.dom.disabled = false;                    }                    Ext.Component.prototype.onEnable.apply(this, arguments);                },                listeners: {                    change: function() {                        me.relayCmd('fontname', me.fontSelect.dom.value);                        me.deferFocus();                    },                    element: 'selectEl'                }            });            items.push(                fontSelectItem,                '-'            );        }        if (me.enableFormat) {            items.push(                btn('bold'),                btn('italic'),                btn('underline')            );        }        if (me.enableFontSize) {            items.push(                '-',                btn('increasefontsize', false, me.adjustFont),                btn('decreasefontsize', false, me.adjustFont)            );        }        if (me.enableColors) {            items.push(                '-', {                    itemId: 'forecolor',                    cls: baseCSSPrefix + 'btn-icon',                    iconCls: baseCSSPrefix + 'edit-forecolor',                    overflowText: editor.buttonTips.forecolor.title,                    tooltip: tipsEnabled ? editor.buttonTips.forecolor || undef : undef,                    tabIndex:-1,                    menu : Ext.widget('menu', {                        plain: true,                        items: [{                            xtype: 'colorpicker',                            allowReselect: true,                            focus: Ext.emptyFn,                            value: '000000',                            plain: true,                            clickEvent: 'mousedown',                            handler: function(cp, color) {                                me.execCmd('forecolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);                                me.deferFocus();                                this.up('menu').hide();                            }                        }]                    })                }, {                    itemId: 'backcolor',                    cls: baseCSSPrefix + 'btn-icon',                    iconCls: baseCSSPrefix + 'edit-backcolor',                    overflowText: editor.buttonTips.backcolor.title,                    tooltip: tipsEnabled ? editor.buttonTips.backcolor || undef : undef,                    tabIndex:-1,                    menu : Ext.widget('menu', {                        plain: true,                        items: [{                            xtype: 'colorpicker',                            focus: Ext.emptyFn,                            value: 'FFFFFF',                            plain: true,                            allowReselect: true,                            clickEvent: 'mousedown',                            handler: function(cp, color) {                                if (Ext.isGecko) {                                    me.execCmd('useCSS', false);                                    me.execCmd('hilitecolor', color);                                    me.execCmd('useCSS', true);                                    me.deferFocus();                                } else {                                    me.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isWebKit || Ext.isIE ? '#'+color : color);                                    me.deferFocus();                                }                                this.up('menu').hide();                            }                        }]                    })                }            );        }        if (me.enableAlignments) {            items.push(                '-',                btn('justifyleft'),                btn('justifycenter'),                btn('justifyright')            );        }        if (!Ext.isSafari2) {            if (me.enableLinks) {                items.push(                    '-',                    btn('createlink', false, me.createLink)                );            }            if (me.enableLists) {                items.push(                    '-',                    btn('insertorderedlist'),                    btn('insertunorderedlist')                );            }            if (me.enableSourceEdit) {                items.push(                    '-',                    btn('sourceedit', true, function(btn){                        me.toggleSourceEdit(!me.sourceEditMode);                    })                );            }        }                // Everything starts disabled.        for (i = 0; i < items.length; i++) {            if (items[i].itemId !== 'sourceedit') {                items[i].disabled = true;            }        }        // build the toolbar        // Automatically rendered in AbstractComponent.afterRender's renderChildren call        toolbar = Ext.widget('toolbar', {            id: me.id + '-toolbar',            ownerCt: me,            cls: Ext.baseCSSPrefix + 'html-editor-tb',            enableOverflow: true,            items: items,            ownerLayout: me.getComponentLayout(),            // stop form submits            listeners: {                click: function(e){                    e.preventDefault();                },                element: 'el'            }        });        me.toolbar = toolbar;    },        getMaskTarget: function(){        return this.bodyEl;        },<span id='Ext-form-field-HtmlEditor-method-setReadOnly'>    /**</span>     * Sets the read only state of this field.     * @param {Boolean} readOnly Whether the field should be read only.     */    setReadOnly: function(readOnly) {        var me = this,            textareaEl = me.textareaEl,            iframeEl = me.iframeEl,            body;        me.readOnly = readOnly;        if (textareaEl) {            textareaEl.dom.readOnly = readOnly;        }        if (me.initialized) {            body = me.getEditorBody();            if (Ext.isIE) {                // Hide the iframe while setting contentEditable so it doesn't grab focus                iframeEl.setDisplayed(false);                body.contentEditable = !readOnly;                iframeEl.setDisplayed(true);            } else {                me.setDesignMode(!readOnly);            }            if (body) {                body.style.cursor = readOnly ? 'default' : 'text';            }            me.disableItems(readOnly);        }    },<span id='Ext-form-field-HtmlEditor-method-getDocMarkup'>    /**</span>     * Called when the editor initializes the iframe with HTML contents. Override this method if you     * want to change the initialization markup of the iframe (e.g. to add stylesheets).     *     * **Note:** IE8-Standards has unwanted scroller behavior, so the default meta tag forces IE7 compatibility.     * Also note that forcing IE7 mode works when the page is loaded normally, but if you are using IE's Web     * Developer Tools to manually set the document mode, that will take precedence and override what this     * code sets by default. This can be confusing when developing, but is not a user-facing issue.     * @protected     */    getDocMarkup: function() {        var me = this,            h = me.iframeEl.getHeight() - me.iframePad * 2;        return Ext.String.format('<html><head><style type="text/css">body{border:0;margin:0;padding:{0}px;height:{1}px;box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box;cursor:text}</style></head><body></body></html>', me.iframePad, h);    },    // private    getEditorBody: function() {        var doc = this.getDoc();        return doc.body || doc.documentElement;    },    // private    getDoc: function() {        return (!Ext.isIE && this.iframeEl.dom.contentDocument) || this.getWin().document;    },    // private    getWin: function() {        return Ext.isIE ? this.iframeEl.dom.contentWindow : window.frames[this.iframeEl.dom.name];    },    // Do the job of a container layout at this point even though we are not a Container.    // TODO: Refactor as a Container.    finishRenderChildren: function () {        this.callParent();        this.toolbar.finishRender();    },    // private    onRender: function() {        var me = this;        me.callParent(arguments);        // The input element is interrogated by the layout to extract height when labelAlign is 'top'        // It must be set, and then switched between the iframe and the textarea        me.inputEl = me.iframeEl;        // Start polling for when the iframe document is ready to be manipulated        me.monitorTask = Ext.TaskManager.start({            run: me.checkDesignMode,            scope: me,            interval: 100        });    },    initRenderTpl: function() {        var me = this;        if (!me.hasOwnProperty('renderTpl')) {            me.renderTpl = me.getTpl('labelableRenderTpl');        }        return me.callParent();    },    initRenderData: function() {        this.beforeSubTpl = '<div class="' + this.editorWrapCls + '">' + Ext.DomHelper.markup(this.toolbar.getRenderTree());        return Ext.applyIf(this.callParent(), this.getLabelableRenderData());    },    getSubTplData: function() {        return {            $comp       : this,            cmpId       : this.id,            id          : this.getInputId(),            textareaCls : Ext.baseCSSPrefix + 'hidden',            value       : this.value,            iframeName  : Ext.id(),            iframeSrc   : Ext.SSL_SECURE_URL,            size        : 'height:100px;width:100%'        };    },    getSubTplMarkup: function() {        return this.getTpl('fieldSubTpl').apply(this.getSubTplData());    },    initFrameDoc: function() {        var me = this,            doc, task;        Ext.TaskManager.stop(me.monitorTask);        doc = me.getDoc();        me.win = me.getWin();        doc.open();        doc.write(me.getDocMarkup());        doc.close();        task = { // must defer to wait for browser to be ready            run: function() {                var doc = me.getDoc();                if (doc.body || doc.readyState === 'complete') {                    Ext.TaskManager.stop(task);                    me.setDesignMode(true);                    Ext.defer(me.initEditor, 10, me);                }            },            interval : 10,            duration:10000,            scope: me        };        Ext.TaskManager.start(task);    },    checkDesignMode: function() {        var me = this,            doc = me.getDoc();        if (doc && (!doc.editorInitialized || me.getDesignMode() !== 'on')) {            me.initFrameDoc();        }    },<span id='Ext-form-field-HtmlEditor-method-setDesignMode'>    /**</span>     * @private     * Sets current design mode. To enable, mode can be true or 'on', off otherwise     */    setDesignMode: function(mode) {        var me = this,            doc = me.getDoc();        if (doc) {            if (me.readOnly) {                mode = false;            }            doc.designMode = (/on|true/i).test(String(mode).toLowerCase()) ?'on':'off';        }    },    // private    getDesignMode: function() {        var doc = this.getDoc();        return !doc ? '' : String(doc.designMode).toLowerCase();    },    disableItems: function(disabled) {        var items = this.getToolbar().items.items,            i,            iLen  = items.length,            item;        for (i = 0; i < iLen; i++) {            item = items[i];            if (item.getItemId() !== 'sourceedit') {                item.setDisabled(disabled);            }        }    },<span id='Ext-form-field-HtmlEditor-method-toggleSourceEdit'>    /**</span>     * Toggles the editor between standard and source edit mode.     * @param {Boolean} [sourceEditMode] True for source edit, false for standard     */    toggleSourceEdit: function(sourceEditMode) {        var me = this,            iframe = me.iframeEl,            textarea = me.textareaEl,            hiddenCls = Ext.baseCSSPrefix + 'hidden',            btn = me.getToolbar().getComponent('sourceedit');        if (!Ext.isBoolean(sourceEditMode)) {            sourceEditMode = !me.sourceEditMode;        }        me.sourceEditMode = sourceEditMode;        if (btn.pressed !== sourceEditMode) {            btn.toggle(sourceEditMode);        }        if (sourceEditMode) {            me.disableItems(true);            me.syncValue();            iframe.addCls(hiddenCls);            textarea.removeCls(hiddenCls);            textarea.dom.removeAttribute('tabIndex');            textarea.focus();            me.inputEl = textarea;        }        else {            if (me.initialized) {                me.disableItems(me.readOnly);            }            me.pushValue();            iframe.removeCls(hiddenCls);            textarea.addCls(hiddenCls);            textarea.dom.setAttribute('tabIndex', -1);            me.deferFocus();            me.inputEl = iframe;        }        me.fireEvent('editmodechange', me, sourceEditMode);        me.updateLayout();    },    // private used internally    createLink : function() {        var url = prompt(this.createLinkText, this.defaultLinkValue);        if (url && url !== 'http:/'+'/') {            this.relayCmd('createlink', url);        }    },    clearInvalid: Ext.emptyFn,    // docs inherit from Field    setValue: function(value) {        var me = this,            textarea = me.textareaEl;        me.mixins.field.setValue.call(me, value);        if (value === null || value === undefined) {            value = '';        }        if (textarea) {            textarea.dom.value = value;        }        me.pushValue();        return this;    },<span id='Ext-form-field-HtmlEditor-method-cleanHtml'>    /**</span>     * If you need/want custom HTML cleanup, this is the method you should override.     * @param {String} html The HTML to be cleaned     * @return {String} The cleaned HTML     * @protected     */    cleanHtml: function(html) {        html = String(html);        if (Ext.isWebKit) { // strip safari nonsense            html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');        }        /*         * Neat little hack. Strips out all the non-digit characters from the default         * value and compares it to the character code of the first character in the string         * because it can cause encoding issues when posted to the server. We need the         * parseInt here because charCodeAt will return a number.         */        if (html.charCodeAt(0) === parseInt(this.defaultValue.replace(/\D/g, ''), 10)) {            html = html.substring(1);        }        return html;    },<span id='Ext-form-field-HtmlEditor-method-syncValue'>    /**</span>     * Syncs the contents of the editor iframe with the textarea.     * @protected     */    syncValue : function(){        var me = this,            body, changed, html, bodyStyle, match;        if (me.initialized) {            body = me.getEditorBody();            html = body.innerHTML;            if (Ext.isWebKit) {                bodyStyle = body.getAttribute('style'); // Safari puts text-align styles on the body element!                match = bodyStyle.match(/text-align:(.*?);/i);                if (match && match[1]) {                    html = '<div style="' + match[0] + '">' + html + '</div>';                }            }            html = me.cleanHtml(html);            if (me.fireEvent('beforesync', me, html) !== false) {                if (me.textareaEl.dom.value != html) {                    me.textareaEl.dom.value = html;                    changed = true;                }                me.fireEvent('sync', me, html);                if (changed) {                    // we have to guard this to avoid infinite recursion because getValue                    // calls this method...                    me.checkChange();                }            }        }    },    //docs inherit from Field    getValue : function() {        var me = this,            value;        if (!me.sourceEditMode) {            me.syncValue();        }        value = me.rendered ? me.textareaEl.dom.value : me.value;        me.value = value;        return value;    },<span id='Ext-form-field-HtmlEditor-method-pushValue'>    /**</span>     * Pushes the value of the textarea into the iframe editor.     * @protected     */    pushValue: function() {        var me = this,            v;        if(me.initialized){            v = me.textareaEl.dom.value || '';            if (!me.activated && v.length < 1) {                v = me.defaultValue;            }            if (me.fireEvent('beforepush', me, v) !== false) {                me.getEditorBody().innerHTML = v;                if (Ext.isGecko) {                    // Gecko hack, see: https://bugzilla.mozilla.org/show_bug.cgi?id=232791#c8                    me.setDesignMode(false);  //toggle off first                    me.setDesignMode(true);                }                me.fireEvent('push', me, v);            }        }    },    // private    deferFocus : function(){         this.focus(false, true);    },    getFocusEl: function() {        var me = this,            win = me.win;        return win && !me.sourceEditMode ? win : me.textareaEl;    },    // private    initEditor : function(){        //Destroying the component during/before initEditor can cause issues.        try {            var me = this,                dbody = me.getEditorBody(),                ss = me.textareaEl.getStyles('font-size', 'font-family', 'background-image', 'background-repeat', 'background-color', 'color'),                doc,                fn;            ss['background-attachment'] = 'fixed'; // w3c            dbody.bgProperties = 'fixed'; // ie            Ext.DomHelper.applyStyles(dbody, ss);            doc = me.getDoc();            if (doc) {                try {                    Ext.EventManager.removeAll(doc);                } catch(e) {}            }            /*             * We need to use createDelegate here, because when using buffer, the delayed task is added             * as a property to the function. When the listener is removed, the task is deleted from the function.             * Since onEditorEvent is shared on the prototype, if we have multiple html editors, the first time one of the editors             * is destroyed, it causes the fn to be deleted from the prototype, which causes errors. Essentially, we're just anonymizing the function.             */            fn = Ext.Function.bind(me.onEditorEvent, me);            Ext.EventManager.on(doc, {                mousedown: fn,                dblclick: fn,                click: fn,                keyup: fn,                buffer:100            });            // These events need to be relayed from the inner document (where they stop            // bubbling) up to the outer document. This has to be done at the DOM level so            // the event reaches listeners on elements like the document body. The effected            // mechanisms that depend on this bubbling behavior are listed to the right            // of the event.            fn = me.onRelayedEvent;            Ext.EventManager.on(doc, {                mousedown: fn, // menu dismisal (MenuManager) and Window onMouseDown (toFront)                mousemove: fn, // window resize drag detection                mouseup: fn,   // window resize termination                click: fn,     // not sure, but just to be safe                dblclick: fn,  // not sure again                scope: me            });            if (Ext.isGecko) {                Ext.EventManager.on(doc, 'keypress', me.applyCommand, me);            }            if (me.fixKeys) {                Ext.EventManager.on(doc, 'keydown', me.fixKeys, me);            }            // We need to be sure we remove all our events from the iframe on unload or we're going to LEAK!            Ext.EventManager.on(window, 'unload', me.beforeDestroy, me);            doc.editorInitialized = true;            me.initialized = true;            me.pushValue();            me.setReadOnly(me.readOnly);            me.fireEvent('initialize', me);        } catch(ex) {            // ignore (why?)        }    },    // private    beforeDestroy : function(){        var me = this,            monitorTask = me.monitorTask,            doc, prop;        if (monitorTask) {            Ext.TaskManager.stop(monitorTask);        }        if (me.rendered) {            try {                doc = me.getDoc();                if (doc) {                    // removeAll() doesn't currently know how to handle iframe document,                    // so for now we have to wrap it in an Ext.Element using Ext.fly,                    // or else IE6/7 will leak big time when the page is refreshed.                    // TODO: this may not be needed once we find a more permanent fix.                    // see EXTJSIV-5891.                    Ext.EventManager.removeAll(Ext.fly(doc));                    for (prop in doc) {                        if (doc.hasOwnProperty && doc.hasOwnProperty(prop)) {                            delete doc[prop];                        }                    }                }            } catch(e) {                // ignore (why?)            }            Ext.destroyMembers(me, 'toolbar', 'iframeEl', 'textareaEl');        }        me.callParent();    },    // private    onRelayedEvent: function (event) {        // relay event from the iframe's document to the document that owns the iframe...        var iframeEl = this.iframeEl,            iframeXY = iframeEl.getXY(),            eventXY = event.getXY();        // the event from the inner document has XY relative to that document's origin,        // so adjust it to use the origin of the iframe in the outer document:        event.xy = [iframeXY[0] + eventXY[0], iframeXY[1] + eventXY[1]];        event.injectEvent(iframeEl); // blame the iframe for the event...        event.xy = eventXY; // restore the original XY (just for safety)    },    // private    onFirstFocus : function(){        var me = this,            selection, range;        me.activated = true;        me.disableItems(me.readOnly);        if (Ext.isGecko) { // prevent silly gecko errors            me.win.focus();            selection = me.win.getSelection();            if (!selection.focusNode || selection.focusNode.nodeType !== 3) {                range = selection.getRangeAt(0);                range.selectNodeContents(me.getEditorBody());                range.collapse(true);                me.deferFocus();            }            try {                me.execCmd('useCSS', true);                me.execCmd('styleWithCSS', false);            } catch(e) {                // ignore (why?)            }        }        me.fireEvent('activate', me);    },    // private    adjustFont: function(btn) {        var adjust = btn.getItemId() === 'increasefontsize' ? 1 : -1,            size = this.getDoc().queryCommandValue('FontSize') || '2',            isPxSize = Ext.isString(size) && size.indexOf('px') !== -1,            isSafari;        size = parseInt(size, 10);        if (isPxSize) {            // Safari 3 values            // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px            if (size <= 10) {                size = 1 + adjust;            }            else if (size <= 13) {                size = 2 + adjust;            }            else if (size <= 16) {                size = 3 + adjust;            }            else if (size <= 18) {                size = 4 + adjust;            }            else if (size <= 24) {                size = 5 + adjust;            }            else {                size = 6 + adjust;            }            size = Ext.Number.constrain(size, 1, 6);        } else {            isSafari = Ext.isSafari;            if (isSafari) { // safari                adjust *= 2;            }            size = Math.max(1, size + adjust) + (isSafari ? 'px' : 0);        }        this.execCmd('FontSize', size);    },    // private    onEditorEvent: function(e) {        this.updateToolbar();    },<span id='Ext-form-field-HtmlEditor-method-updateToolbar'>    /**</span>     * Triggers a toolbar update by reading the markup state of the current selection in the editor.     * @protected     */    updateToolbar: function() {        var me = this,            btns, doc, name, fontSelect;        if (me.readOnly) {            return;        }        if (!me.activated) {            me.onFirstFocus();            return;        }        btns = me.getToolbar().items.map;        doc = me.getDoc();        if (me.enableFont && !Ext.isSafari2) {            name = (doc.queryCommandValue('FontName') || me.defaultFont).toLowerCase();            fontSelect = me.fontSelect.dom;            if (name !== fontSelect.value) {                fontSelect.value = name;            }        }        function updateButtons() {            for (var i = 0, l = arguments.length, name; i < l; i++) {                name = arguments[i];                btns[name].toggle(doc.queryCommandState(name));            }        }        if(me.enableFormat){            updateButtons('bold', 'italic', 'underline');        }        if(me.enableAlignments){            updateButtons('justifyleft', 'justifycenter', 'justifyright');        }        if(!Ext.isSafari2 && me.enableLists){            updateButtons('insertorderedlist', 'insertunorderedlist');        }        Ext.menu.Manager.hideAll();        me.syncValue();    },    // private    relayBtnCmd: function(btn) {        this.relayCmd(btn.getItemId());    },<span id='Ext-form-field-HtmlEditor-method-relayCmd'>    /**</span>     * Executes a Midas editor command on the editor document and performs necessary focus and toolbar updates.     * **This should only be called after the editor is initialized.**     * @param {String} cmd The Midas command     * @param {String/Boolean} [value=null] The value to pass to the command     */    relayCmd: function(cmd, value) {        Ext.defer(function() {            var me = this;            me.focus();            me.execCmd(cmd, value);            me.updateToolbar();        }, 10, this);    },<span id='Ext-form-field-HtmlEditor-method-execCmd'>    /**</span>     * Executes a Midas editor command directly on the editor document. For visual commands, you should use     * {@link #relayCmd} instead. **This should only be called after the editor is initialized.**     * @param {String} cmd The Midas command     * @param {String/Boolean} [value=null] The value to pass to the command     */    execCmd : function(cmd, value){        var me = this,            doc = me.getDoc(),            undef;        doc.execCommand(cmd, false, value === undef ? null : value);        me.syncValue();    },    // private    applyCommand : function(e){        if (e.ctrlKey) {            var me = this,                c = e.getCharCode(), cmd;            if (c > 0) {                c = String.fromCharCode(c);                switch (c) {                    case 'b':                        cmd = 'bold';                    break;                    case 'i':                        cmd = 'italic';                    break;                    case 'u':                        cmd = 'underline';                    break;                }                if (cmd) {                    me.win.focus();                    me.execCmd(cmd);                    me.deferFocus();                    e.preventDefault();                }            }        }    },<span id='Ext-form-field-HtmlEditor-method-insertAtCursor'>    /**</span>     * Inserts the passed text at the current cursor position.     * Note: the editor must be initialized and activated to insert text.     * @param {String} text     */    insertAtCursor : function(text){        var me = this,            range;        if (me.activated) {            me.win.focus();            if (Ext.isIE) {                range = me.getDoc().selection.createRange();                if (range) {                    range.pasteHTML(text);                    me.syncValue();                    me.deferFocus();                }            }else{                me.execCmd('InsertHTML', text);                me.deferFocus();            }        }    },    // private    fixKeys: (function() { // load time branching for fastest keydown performance        if (Ext.isIE) {            return function(e){                var me = this,                    k = e.getKey(),                    doc = me.getDoc(),                    readOnly = me.readOnly,                    range, target;                if (k === e.TAB) {                    e.stopEvent();                    if (!readOnly) {                        range = doc.selection.createRange();                        if(range){                            range.collapse(true);                            range.pasteHTML('&#160;&#160;&#160;&#160;');                            me.deferFocus();                        }                    }                }                else if (k === e.ENTER) {                    if (!readOnly) {                        range = doc.selection.createRange();                        if (range) {                            target = range.parentElement();                            if(!target || target.tagName.toLowerCase() !== 'li'){                                e.stopEvent();                                range.pasteHTML('<br />');                                range.collapse(false);                                range.select();                            }                        }                    }                }            };        }        if (Ext.isOpera) {            return function(e){                var me = this;                if (e.getKey() === e.TAB) {                    e.stopEvent();                    if (!me.readOnly) {                        me.win.focus();                        me.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');                        me.deferFocus();                    }                }            };        }        if (Ext.isWebKit) {            return function(e){                var me = this,                    k = e.getKey(),                    readOnly = me.readOnly;                if (k === e.TAB) {                    e.stopEvent();                    if (!readOnly) {                        me.execCmd('InsertText','\t');                        me.deferFocus();                    }                }                else if (k === e.ENTER) {                    e.stopEvent();                    if (!readOnly) {                        me.execCmd('InsertHtml','<br /><br />');                        me.deferFocus();                    }                }            };        }        return null; // not needed, so null    }()),<span id='Ext-form-field-HtmlEditor-method-getToolbar'>    /**</span>     * Returns the editor's toolbar. **This is only available after the editor has been rendered.**     * @return {Ext.toolbar.Toolbar}     */    getToolbar : function(){        return this.toolbar;    },    //<locale><span id='Ext-form-field-HtmlEditor-property-buttonTips'>    /**</span>     * @property {Object} buttonTips     * Object collection of toolbar tooltips for the buttons in the editor. The key is the command id associated with     * that button and the value is a valid QuickTips object. For example:     *     *     {     *         bold : {     *             title: 'Bold (Ctrl+B)',     *             text: 'Make the selected text bold.',     *             cls: 'x-html-editor-tip'     *         },     *         italic : {     *             title: 'Italic (Ctrl+I)',     *             text: 'Make the selected text italic.',     *             cls: 'x-html-editor-tip'     *         },     *         ...     */    buttonTips : {        bold : {            title: 'Bold (Ctrl+B)',            text: 'Make the selected text bold.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        italic : {            title: 'Italic (Ctrl+I)',            text: 'Make the selected text italic.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        underline : {            title: 'Underline (Ctrl+U)',            text: 'Underline the selected text.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        increasefontsize : {            title: 'Grow Text',            text: 'Increase the font size.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        decreasefontsize : {            title: 'Shrink Text',            text: 'Decrease the font size.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        backcolor : {            title: 'Text Highlight Color',            text: 'Change the background color of the selected text.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        forecolor : {            title: 'Font Color',            text: 'Change the color of the selected text.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        justifyleft : {            title: 'Align Text Left',            text: 'Align text to the left.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        justifycenter : {            title: 'Center Text',            text: 'Center text in the editor.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        justifyright : {            title: 'Align Text Right',            text: 'Align text to the right.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        insertunorderedlist : {            title: 'Bullet List',            text: 'Start a bulleted list.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        insertorderedlist : {            title: 'Numbered List',            text: 'Start a numbered list.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        createlink : {            title: 'Hyperlink',            text: 'Make the selected text a hyperlink.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        },        sourceedit : {            title: 'Source Edit',            text: 'Switch to source editing mode.',            cls: Ext.baseCSSPrefix + 'html-editor-tip'        }    }    //</locale>    // hide stuff that is not compatible<span id='Ext-form-field-HtmlEditor-event-blur'>    /**</span>     * @event blur     * @private     */<span id='Ext-form-field-HtmlEditor-event-focus'>    /**</span>     * @event focus     * @private     */<span id='Ext-form-field-HtmlEditor-event-specialkey'>    /**</span>     * @event specialkey     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-fieldCls'>    /**</span>     * @cfg {String} fieldCls     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-focusCls'>    /**</span>     * @cfg {String} focusCls     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-autoCreate'>    /**</span>     * @cfg {String} autoCreate     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-inputType'>    /**</span>     * @cfg {String} inputType     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-invalidCls'>    /**</span>     * @cfg {String} invalidCls     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-invalidText'>    /**</span>     * @cfg {String} invalidText     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-msgFx'>    /**</span>     * @cfg {String} msgFx     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-allowDomMove'>    /**</span>     * @cfg {Boolean} allowDomMove     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-applyTo'>    /**</span>     * @cfg {String} applyTo     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-readOnly'>    /**</span>     * @cfg {String} readOnly     * @private     */<span id='Ext-form-field-HtmlEditor-cfg-tabIndex'>    /**</span>     * @cfg {String} tabIndex     * @private     */<span id='Ext-form-field-HtmlEditor-method-validate'>    /**</span>     * @method validate     * @private     */});</pre></body></html>
 |