source: trunk/web/addons/job_monarch/lib/extjs/source/widgets/form/HtmlEditor.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 35.2 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.form.HtmlEditor
11 * @extends Ext.form.Field
12 * Provides a lightweight HTML Editor component. Some toolbar features are not supported by Safari and will be
13 * automatically hidden when needed.  These are noted in the config options where appropriate.
14 * <br><br>The editor's toolbar buttons have tooltips defined in the {@link #buttonTips} property, but they are not
15 * enabled by default unless the global {@link Ext.QuickTips} singleton is {@link Ext.QuickTips#init initialized}.
16 * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
17 * supported by this editor.</b>
18 * <br><br>An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
19 * any element that has display set to 'none' can cause problems in Safari and Firefox due to their default iframe reloading bugs.
20 * <br><br>Example usage:
21 * <pre><code>
22// Simple example rendered with default options:
23Ext.QuickTips.init();  // enable tooltips
24new Ext.form.HtmlEditor({
25    renderTo: Ext.getBody(),
26    width: 800,
27    height: 300
28});
29
30// Passed via xtype into a container and with custom options:
31Ext.QuickTips.init();  // enable tooltips
32new Ext.Panel({
33    title: 'HTML Editor',
34    renderTo: Ext.getBody(),
35    width: 600,
36    height: 300,
37    frame: true,
38    layout: 'fit',
39    items: {
40        xtype: 'htmleditor',
41        enableColors: false,
42        enableAlignments: false
43    }
44});
45</code></pre>
46 * @constructor
47 * Create a new HtmlEditor
48 * @param {Object} config
49 */
50
51Ext.form.HtmlEditor = Ext.extend(Ext.form.Field, {
52    /**
53     * @cfg {Boolean} enableFormat Enable the bold, italic and underline buttons (defaults to true)
54     */
55    enableFormat : true,
56    /**
57     * @cfg {Boolean} enableFontSize Enable the increase/decrease font size buttons (defaults to true)
58     */
59    enableFontSize : true,
60    /**
61     * @cfg {Boolean} enableColors Enable the fore/highlight color buttons (defaults to true)
62     */
63    enableColors : true,
64    /**
65     * @cfg {Boolean} enableAlignments Enable the left, center, right alignment buttons (defaults to true)
66     */
67    enableAlignments : true,
68    /**
69     * @cfg {Boolean} enableLists Enable the bullet and numbered list buttons. Not available in Safari. (defaults to true)
70     */
71    enableLists : true,
72    /**
73     * @cfg {Boolean} enableSourceEdit Enable the switch to source edit button. Not available in Safari. (defaults to true)
74     */
75    enableSourceEdit : true,
76    /**
77     * @cfg {Boolean} enableLinks Enable the create link button. Not available in Safari. (defaults to true)
78     */
79    enableLinks : true,
80    /**
81     * @cfg {Boolean} enableFont Enable font selection. Not available in Safari. (defaults to true)
82     */
83    enableFont : true,
84    /**
85     * @cfg {String} createLinkText The default text for the create link prompt
86     */
87    createLinkText : 'Please enter the URL for the link:',
88    /**
89     * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
90     */
91    defaultLinkValue : 'http:/'+'/',
92    /**
93     * @cfg {Array} fontFamilies An array of available font families
94     */
95    fontFamilies : [
96        'Arial',
97        'Courier New',
98        'Tahoma',
99        'Times New Roman',
100        'Verdana'
101    ],
102    defaultFont: 'tahoma',
103
104    // private properties
105    validationEvent : false,
106    deferHeight: true,
107    initialized : false,
108    activated : false,
109    sourceEditMode : false,
110    onFocus : Ext.emptyFn,
111    iframePad:3,
112    hideMode:'offsets',
113    defaultAutoCreate : {
114        tag: "textarea",
115        style:"width:500px;height:300px;",
116        autocomplete: "off"
117    },
118
119    // private
120    initComponent : function(){
121        this.addEvents(
122            /**
123             * @event initialize
124             * Fires when the editor is fully initialized (including the iframe)
125             * @param {HtmlEditor} this
126             */
127            'initialize',
128            /**
129             * @event activate
130             * Fires when the editor is first receives the focus. Any insertion must wait
131             * until after this event.
132             * @param {HtmlEditor} this
133             */
134            'activate',
135             /**
136             * @event beforesync
137             * Fires before the textarea is updated with content from the editor iframe. Return false
138             * to cancel the sync.
139             * @param {HtmlEditor} this
140             * @param {String} html
141             */
142            'beforesync',
143             /**
144             * @event beforepush
145             * Fires before the iframe editor is updated with content from the textarea. Return false
146             * to cancel the push.
147             * @param {HtmlEditor} this
148             * @param {String} html
149             */
150            'beforepush',
151             /**
152             * @event sync
153             * Fires when the textarea is updated with content from the editor iframe.
154             * @param {HtmlEditor} this
155             * @param {String} html
156             */
157            'sync',
158             /**
159             * @event push
160             * Fires when the iframe editor is updated with content from the textarea.
161             * @param {HtmlEditor} this
162             * @param {String} html
163             */
164            'push',
165             /**
166             * @event editmodechange
167             * Fires when the editor switches edit modes
168             * @param {HtmlEditor} this
169             * @param {Boolean} sourceEdit True if source edit, false if standard editing.
170             */
171            'editmodechange'
172        )
173    },
174
175    // private
176    createFontOptions : function(){
177        var buf = [], fs = this.fontFamilies, ff, lc;
178        for(var i = 0, len = fs.length; i< len; i++){
179            ff = fs[i];
180            lc = ff.toLowerCase();
181            buf.push(
182                '<option value="',lc,'" style="font-family:',ff,';"',
183                    (this.defaultFont == lc ? ' selected="true">' : '>'),
184                    ff,
185                '</option>'
186            );
187        }
188        return buf.join('');
189    },
190   
191    /*
192     * Protected method that will not generally be called directly. It
193     * is called when the editor creates its toolbar. Override this method if you need to
194     * add custom toolbar buttons.
195     * @param {HtmlEditor} editor
196     */
197    createToolbar : function(editor){
198       
199        var tipsEnabled = Ext.QuickTips && Ext.QuickTips.isEnabled();
200       
201        function btn(id, toggle, handler){
202            return {
203                itemId : id,
204                cls : 'x-btn-icon x-edit-'+id,
205                enableToggle:toggle !== false,
206                scope: editor,
207                handler:handler||editor.relayBtnCmd,
208                clickEvent:'mousedown',
209                tooltip: tipsEnabled ? editor.buttonTips[id] || undefined : undefined,
210                tabIndex:-1
211            };
212        }
213
214        // build the toolbar
215        var tb = new Ext.Toolbar({
216            renderTo:this.wrap.dom.firstChild
217        });
218
219        // stop form submits
220        tb.el.on('click', function(e){
221            e.preventDefault();
222        });
223
224        if(this.enableFont && !Ext.isSafari2){
225            this.fontSelect = tb.el.createChild({
226                tag:'select',
227                cls:'x-font-select',
228                html: this.createFontOptions()
229            });
230            this.fontSelect.on('change', function(){
231                var font = this.fontSelect.dom.value;
232                this.relayCmd('fontname', font);
233                this.deferFocus();
234            }, this);
235            tb.add(
236                this.fontSelect.dom,
237                '-'
238            );
239        };
240
241        if(this.enableFormat){
242            tb.add(
243                btn('bold'),
244                btn('italic'),
245                btn('underline')
246            );
247        };
248
249        if(this.enableFontSize){
250            tb.add(
251                '-',
252                btn('increasefontsize', false, this.adjustFont),
253                btn('decreasefontsize', false, this.adjustFont)
254            );
255        };
256
257        if(this.enableColors){
258            tb.add(
259                '-', {
260                    itemId:'forecolor',
261                    cls:'x-btn-icon x-edit-forecolor',
262                    clickEvent:'mousedown',
263                    tooltip: tipsEnabled ? editor.buttonTips['forecolor'] || undefined : undefined,
264                    tabIndex:-1,
265                    menu : new Ext.menu.ColorMenu({
266                        allowReselect: true,
267                        focus: Ext.emptyFn,
268                        value:'000000',
269                        plain:true,
270                        selectHandler: function(cp, color){
271                            this.execCmd('forecolor', Ext.isSafari || Ext.isIE ? '#'+color : color);
272                            this.deferFocus();
273                        },
274                        scope: this,
275                        clickEvent:'mousedown'
276                    })
277                }, {
278                    itemId:'backcolor',
279                    cls:'x-btn-icon x-edit-backcolor',
280                    clickEvent:'mousedown',
281                    tooltip: tipsEnabled ? editor.buttonTips['backcolor'] || undefined : undefined,
282                    tabIndex:-1,
283                    menu : new Ext.menu.ColorMenu({
284                        focus: Ext.emptyFn,
285                        value:'FFFFFF',
286                        plain:true,
287                        allowReselect: true,
288                        selectHandler: function(cp, color){
289                            if(Ext.isGecko){
290                                this.execCmd('useCSS', false);
291                                this.execCmd('hilitecolor', color);
292                                this.execCmd('useCSS', true);
293                                this.deferFocus();
294                            }else{
295                                this.execCmd(Ext.isOpera ? 'hilitecolor' : 'backcolor', Ext.isSafari || Ext.isIE ? '#'+color : color);
296                                this.deferFocus();
297                            }
298                        },
299                        scope:this,
300                        clickEvent:'mousedown'
301                    })
302                }
303            );
304        };
305
306        if(this.enableAlignments){
307            tb.add(
308                '-',
309                btn('justifyleft'),
310                btn('justifycenter'),
311                btn('justifyright')
312            );
313        };
314
315        if(!Ext.isSafari2){
316            if(this.enableLinks){
317                tb.add(
318                    '-',
319                    btn('createlink', false, this.createLink)
320                );
321            };
322
323            if(this.enableLists){
324                tb.add(
325                    '-',
326                    btn('insertorderedlist'),
327                    btn('insertunorderedlist')
328                );
329            }
330            if(this.enableSourceEdit){
331                tb.add(
332                    '-',
333                    btn('sourceedit', true, function(btn){
334                        this.toggleSourceEdit(btn.pressed);
335                    })
336                );
337            }
338        }
339
340        this.tb = tb;
341    },
342
343    /**
344     * Protected method that will not generally be called directly. It
345     * is called when the editor initializes the iframe with HTML contents. Override this method if you
346     * want to change the initialization markup of the iframe (e.g. to add stylesheets).
347     */
348    getDocMarkup : function(){
349        return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
350    },
351
352    // private
353    getEditorBody : function(){
354        return this.doc.body || this.doc.documentElement;
355    },
356
357    // private
358    getDoc : function(){
359        return Ext.isIE ? this.getWin().document : (this.iframe.contentDocument || this.getWin().document);
360    },
361
362    // private
363    getWin : function(){
364        return Ext.isIE ? this.iframe.contentWindow : window.frames[this.iframe.name];
365    },
366
367    // private
368    onRender : function(ct, position){
369        Ext.form.HtmlEditor.superclass.onRender.call(this, ct, position);
370        this.el.dom.style.border = '0 none';
371        this.el.dom.setAttribute('tabIndex', -1);
372        this.el.addClass('x-hidden');
373        if(Ext.isIE){ // fix IE 1px bogus margin
374            this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
375        }
376        this.wrap = this.el.wrap({
377            cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
378        });
379
380        this.createToolbar(this);
381
382        this.tb.items.each(function(item){
383           if(item.itemId != 'sourceedit'){
384                item.disable();
385            }
386        });
387
388        var iframe = document.createElement('iframe');
389        iframe.name = Ext.id();
390        iframe.frameBorder = '0';
391
392        iframe.src = Ext.isIE ? Ext.SSL_SECURE_URL : "javascript:;";
393
394        this.wrap.dom.appendChild(iframe);
395
396        this.iframe = iframe;
397
398        this.initFrame();
399
400        if(this.autoMonitorDesignMode !== false){
401            this.monitorTask = Ext.TaskMgr.start({
402                run: this.checkDesignMode,
403                scope: this,
404                interval:100
405            });
406        }
407
408        if(!this.width){
409            var sz = this.el.getSize();
410            this.setSize(sz.width, this.height || sz.height);
411        }
412    },
413
414    initFrame : function(){
415        this.doc = this.getDoc();
416        this.win = this.getWin();
417
418        this.doc.open();
419        this.doc.write(this.getDocMarkup());
420        this.doc.close();
421
422        var task = { // must defer to wait for browser to be ready
423            run : function(){
424                if(this.doc.body || this.doc.readyState == 'complete'){
425                    Ext.TaskMgr.stop(task);
426                    this.doc.designMode="on";
427                    this.initEditor.defer(10, this);
428                }
429            },
430            interval : 10,
431            duration:10000,
432            scope: this
433        };
434        Ext.TaskMgr.start(task);
435    },
436
437
438    checkDesignMode : function(){
439        if(this.wrap && this.wrap.dom.offsetWidth){
440            var doc = this.getDoc();
441            if(!doc){
442                return;
443            }
444            if(!doc.editorInitialized || String(doc.designMode).toLowerCase() != 'on'){
445                this.initFrame();
446            }
447        }
448    },
449
450    // private
451    onResize : function(w, h){
452        Ext.form.HtmlEditor.superclass.onResize.apply(this, arguments);
453        if(this.el && this.iframe){
454            if(typeof w == 'number'){
455                var aw = w - this.wrap.getFrameWidth('lr');
456                this.el.setWidth(this.adjustWidth('textarea', aw));
457                this.iframe.style.width = Math.max(aw, 0) + 'px';
458            }
459            if(typeof h == 'number'){
460                var ah = h - this.wrap.getFrameWidth('tb') - this.tb.el.getHeight();
461                this.el.setHeight(this.adjustWidth('textarea', ah));
462                this.iframe.style.height = Math.max(ah, 0) + 'px';
463                if(this.doc){
464                    this.getEditorBody().style.height = Math.max((ah - (this.iframePad*2)), 0) + 'px';
465                }
466            }
467        }
468    },
469
470    /**
471     * Toggles the editor between standard and source edit mode.
472     * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
473     */
474    toggleSourceEdit : function(sourceEditMode){
475        if(sourceEditMode === undefined){
476            sourceEditMode = !this.sourceEditMode;
477        }
478        this.sourceEditMode = sourceEditMode === true;
479        var btn = this.tb.items.get('sourceedit');
480        if(btn.pressed !== this.sourceEditMode){
481            btn.toggle(this.sourceEditMode);
482            return;
483        }
484        if(this.sourceEditMode){
485            this.tb.items.each(function(item){
486                if(item.itemId != 'sourceedit'){
487                    item.disable();
488                }
489            });
490            this.syncValue();
491            this.iframe.className = 'x-hidden';
492            this.el.removeClass('x-hidden');
493            this.el.dom.removeAttribute('tabIndex');
494            this.el.focus();
495        }else{
496            if(this.initialized){
497                this.tb.items.each(function(item){
498                    item.enable();
499                });
500            }
501            this.pushValue();
502            this.iframe.className = '';
503            this.el.addClass('x-hidden');
504            this.el.dom.setAttribute('tabIndex', -1);
505            this.deferFocus();
506        }
507        var lastSize = this.lastSize;
508        if(lastSize){
509            delete this.lastSize;
510            this.setSize(lastSize);
511        }
512        this.fireEvent('editmodechange', this, this.sourceEditMode);
513    },
514
515    // private used internally
516    createLink : function(){
517        var url = prompt(this.createLinkText, this.defaultLinkValue);
518        if(url && url != 'http:/'+'/'){
519            this.relayCmd('createlink', url);
520        }
521    },
522
523    // private (for BoxComponent)
524    adjustSize : Ext.BoxComponent.prototype.adjustSize,
525
526    // private (for BoxComponent)
527    getResizeEl : function(){
528        return this.wrap;
529    },
530
531    // private (for BoxComponent)
532    getPositionEl : function(){
533        return this.wrap;
534    },
535
536    // private
537    initEvents : function(){
538        this.originalValue = this.getValue();
539    },
540
541    /**
542     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
543     * @method
544     */
545    markInvalid : Ext.emptyFn,
546   
547    /**
548     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
549     * @method
550     */
551    clearInvalid : Ext.emptyFn,
552
553    // docs inherit from Field
554    setValue : function(v){
555        Ext.form.HtmlEditor.superclass.setValue.call(this, v);
556        this.pushValue();
557    },
558
559    /**
560     * Protected method that will not generally be called directly. If you need/want
561     * custom HTML cleanup, this is the method you should override.
562     * @param {String} html The HTML to be cleaned
563     * @return {String} The cleaned HTML
564     */
565    cleanHtml : function(html){
566        html = String(html);
567        if(html.length > 5){
568            if(Ext.isSafari){ // strip safari nonsense
569                html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
570            }
571        }
572        if(html == '&nbsp;'){
573            html = '';
574        }
575        return html;
576    },
577
578    /**
579     * Protected method that will not generally be called directly. Syncs the contents
580     * of the editor iframe with the textarea.
581     */
582    syncValue : function(){
583        if(this.initialized){
584            var bd = this.getEditorBody();
585            var html = bd.innerHTML;
586            if(Ext.isSafari){
587                var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
588                var m = bs.match(/text-align:(.*?);/i);
589                if(m && m[1]){
590                    html = '<div style="'+m[0]+'">' + html + '</div>';
591                }
592            }
593            html = this.cleanHtml(html);
594            if(this.fireEvent('beforesync', this, html) !== false){
595                this.el.dom.value = html;
596                this.fireEvent('sync', this, html);
597            }
598        }
599    },
600   
601    //docs inherit from Field
602    getValue : function() {
603        this.syncValue();
604        return Ext.form.HtmlEditor.superclass.getValue.call(this);
605    },
606
607
608    /**
609     * Protected method that will not generally be called directly. Pushes the value of the textarea
610     * into the iframe editor.
611     */
612    pushValue : function(){
613        if(this.initialized){
614            var v = this.el.dom.value;
615            if(!this.activated && v.length < 1){
616                v = '&nbsp;';
617            }
618            if(this.fireEvent('beforepush', this, v) !== false){
619                this.getEditorBody().innerHTML = v;
620                this.fireEvent('push', this, v);
621            }
622        }
623    },
624
625    // private
626    deferFocus : function(){
627        this.focus.defer(10, this);
628    },
629
630    // docs inherit from Field
631    focus : function(){
632        if(this.win && !this.sourceEditMode){
633            this.win.focus();
634        }else{
635            this.el.focus();
636        }
637    },
638
639    // private
640    initEditor : function(){
641        var dbody = this.getEditorBody();
642        var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
643        ss['background-attachment'] = 'fixed'; // w3c
644        dbody.bgProperties = 'fixed'; // ie
645
646        Ext.DomHelper.applyStyles(dbody, ss);
647
648        if(this.doc){
649            try{
650                Ext.EventManager.removeAll(this.doc);
651            }catch(e){}
652        }
653
654        this.doc = this.getDoc();
655
656        Ext.EventManager.on(this.doc, {
657            'mousedown': this.onEditorEvent,
658            'dblclick': this.onEditorEvent,
659            'click': this.onEditorEvent,
660            'keyup': this.onEditorEvent,
661            buffer:100,
662            scope: this
663        });
664
665        if(Ext.isGecko){
666            Ext.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
667        }
668        if(Ext.isIE || Ext.isSafari || Ext.isOpera){
669            Ext.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
670        }
671        this.initialized = true;
672
673        this.fireEvent('initialize', this);
674
675        this.doc.editorInitialized = true;
676
677        this.pushValue();
678    },
679
680    // private
681    onDestroy : function(){
682        if(this.monitorTask){
683            Ext.TaskMgr.stop(this.monitorTask);
684        }
685        if(this.rendered){
686            this.tb.items.each(function(item){
687                if(item.menu){
688                    item.menu.removeAll();
689                    if(item.menu.el){
690                        item.menu.el.destroy();
691                    }
692                }
693                item.destroy();
694            });
695            this.wrap.dom.innerHTML = '';
696            this.wrap.remove();
697        }
698    },
699
700    // private
701    onFirstFocus : function(){
702        this.activated = true;
703        this.tb.items.each(function(item){
704           item.enable();
705        });
706        if(Ext.isGecko){ // prevent silly gecko errors
707            this.win.focus();
708            var s = this.win.getSelection();
709            if(!s.focusNode || s.focusNode.nodeType != 3){
710                var r = s.getRangeAt(0);
711                r.selectNodeContents(this.getEditorBody());
712                r.collapse(true);
713                this.deferFocus();
714            }
715            try{
716                this.execCmd('useCSS', true);
717                this.execCmd('styleWithCSS', false);
718            }catch(e){}
719        }
720        this.fireEvent('activate', this);
721    },
722
723    // private
724    adjustFont: function(btn){
725        var adjust = btn.itemId == 'increasefontsize' ? 1 : -1;
726
727        var v = parseInt(this.doc.queryCommandValue('FontSize') || 2, 10);
728        if(Ext.isSafari3 || Ext.isAir){
729            // Safari 3 values
730            // 1 = 10px, 2 = 13px, 3 = 16px, 4 = 18px, 5 = 24px, 6 = 32px
731            if(v <= 10){
732                v = 1 + adjust;
733            }else if(v <= 13){
734                v = 2 + adjust;
735            }else if(v <= 16){
736                v = 3 + adjust;
737            }else if(v <= 18){
738                v = 4 + adjust;
739            }else if(v <= 24){
740                v = 5 + adjust;
741            }else {
742                v = 6 + adjust;
743            }
744            v = v.constrain(1, 6);
745        }else{
746            if(Ext.isSafari){ // safari
747                adjust *= 2;
748            }
749            v = Math.max(1, v+adjust) + (Ext.isSafari ? 'px' : 0);
750        }
751        this.execCmd('FontSize', v);
752    },
753
754    // private
755    onEditorEvent : function(e){
756        this.updateToolbar();
757    },
758
759
760    /**
761     * Protected method that will not generally be called directly. It triggers
762     * a toolbar update by reading the markup state of the current selection in the editor.
763     */
764    updateToolbar: function(){
765
766        if(!this.activated){
767            this.onFirstFocus();
768            return;
769        }
770
771        var btns = this.tb.items.map, doc = this.doc;
772
773        if(this.enableFont && !Ext.isSafari2){
774            var name = (this.doc.queryCommandValue('FontName')||this.defaultFont).toLowerCase();
775            if(name != this.fontSelect.dom.value){
776                this.fontSelect.dom.value = name;
777            }
778        }
779        if(this.enableFormat){
780            btns.bold.toggle(doc.queryCommandState('bold'));
781            btns.italic.toggle(doc.queryCommandState('italic'));
782            btns.underline.toggle(doc.queryCommandState('underline'));
783        }
784        if(this.enableAlignments){
785            btns.justifyleft.toggle(doc.queryCommandState('justifyleft'));
786            btns.justifycenter.toggle(doc.queryCommandState('justifycenter'));
787            btns.justifyright.toggle(doc.queryCommandState('justifyright'));
788        }
789        if(!Ext.isSafari2 && this.enableLists){
790            btns.insertorderedlist.toggle(doc.queryCommandState('insertorderedlist'));
791            btns.insertunorderedlist.toggle(doc.queryCommandState('insertunorderedlist'));
792        }
793       
794        Ext.menu.MenuMgr.hideAll();
795
796        this.syncValue();
797    },
798
799    // private
800    relayBtnCmd : function(btn){
801        this.relayCmd(btn.itemId);
802    },
803
804    /**
805     * Executes a Midas editor command on the editor document and performs necessary focus and
806     * toolbar updates. <b>This should only be called after the editor is initialized.</b>
807     * @param {String} cmd The Midas command
808     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
809     */
810    relayCmd : function(cmd, value){
811        (function(){
812            this.focus();
813            this.execCmd(cmd, value);
814            this.updateToolbar();
815        }).defer(10, this);
816    },
817
818    /**
819     * Executes a Midas editor command directly on the editor document.
820     * For visual commands, you should use {@link #relayCmd} instead.
821     * <b>This should only be called after the editor is initialized.</b>
822     * @param {String} cmd The Midas command
823     * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
824     */
825    execCmd : function(cmd, value){
826        this.doc.execCommand(cmd, false, value === undefined ? null : value);
827        this.syncValue();
828    },
829
830    // private
831    applyCommand : function(e){
832        if(e.ctrlKey){
833            var c = e.getCharCode(), cmd;
834            if(c > 0){
835                c = String.fromCharCode(c);
836                switch(c){
837                    case 'b':
838                        cmd = 'bold';
839                    break;
840                    case 'i':
841                        cmd = 'italic';
842                    break;
843                    case 'u':
844                        cmd = 'underline';
845                    break;
846                }
847                if(cmd){
848                    this.win.focus();
849                    this.execCmd(cmd);
850                    this.deferFocus();
851                    e.preventDefault();
852                }
853            }
854        }
855    },
856
857    /**
858     * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
859     * to insert text.
860     * @param {String} text
861     */
862    insertAtCursor : function(text){
863        if(!this.activated){
864            return;
865        }
866        if(Ext.isIE){
867            this.win.focus();
868            var r = this.doc.selection.createRange();
869            if(r){
870                r.collapse(true);
871                r.pasteHTML(text);
872                this.syncValue();
873                this.deferFocus();
874            }
875        }else if(Ext.isGecko || Ext.isOpera){
876            this.win.focus();
877            this.execCmd('InsertHTML', text);
878            this.deferFocus();
879        }else if(Ext.isSafari){
880            this.execCmd('InsertText', text);
881            this.deferFocus();
882        }
883    },
884
885    // private
886    fixKeys : function(){ // load time branching for fastest keydown performance
887        if(Ext.isIE){
888            return function(e){
889                var k = e.getKey(), r;
890                if(k == e.TAB){
891                    e.stopEvent();
892                    r = this.doc.selection.createRange();
893                    if(r){
894                        r.collapse(true);
895                        r.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
896                        this.deferFocus();
897                    }
898                }else if(k == e.ENTER){
899                    r = this.doc.selection.createRange();
900                    if(r){
901                        var target = r.parentElement();
902                        if(!target || target.tagName.toLowerCase() != 'li'){
903                            e.stopEvent();
904                            r.pasteHTML('<br />');
905                            r.collapse(false);
906                            r.select();
907                        }
908                    }
909                }
910            };
911        }else if(Ext.isOpera){
912            return function(e){
913                var k = e.getKey();
914                if(k == e.TAB){
915                    e.stopEvent();
916                    this.win.focus();
917                    this.execCmd('InsertHTML','&nbsp;&nbsp;&nbsp;&nbsp;');
918                    this.deferFocus();
919                }
920            };
921        }else if(Ext.isSafari){
922            return function(e){
923                var k = e.getKey();
924                if(k == e.TAB){
925                    e.stopEvent();
926                    this.execCmd('InsertText','\t');
927                    this.deferFocus();
928                }
929             };
930        }
931    }(),
932
933    /**
934     * Returns the editor's toolbar. <b>This is only available after the editor has been rendered.</b>
935     * @return {Ext.Toolbar}
936     */
937    getToolbar : function(){
938        return this.tb;
939    },
940
941    /**
942     * Object collection of toolbar tooltips for the buttons in the editor. The key
943     * is the command id associated with that button and the value is a valid QuickTips object.
944     * For example:
945<pre><code>
946{
947    bold : {
948        title: 'Bold (Ctrl+B)',
949        text: 'Make the selected text bold.',
950        cls: 'x-html-editor-tip'
951    },
952    italic : {
953        title: 'Italic (Ctrl+I)',
954        text: 'Make the selected text italic.',
955        cls: 'x-html-editor-tip'
956    },
957    ...
958</code></pre>
959    * @type Object
960     */
961    buttonTips : {
962        bold : {
963            title: 'Bold (Ctrl+B)',
964            text: 'Make the selected text bold.',
965            cls: 'x-html-editor-tip'
966        },
967        italic : {
968            title: 'Italic (Ctrl+I)',
969            text: 'Make the selected text italic.',
970            cls: 'x-html-editor-tip'
971        },
972        underline : {
973            title: 'Underline (Ctrl+U)',
974            text: 'Underline the selected text.',
975            cls: 'x-html-editor-tip'
976        },
977        increasefontsize : {
978            title: 'Grow Text',
979            text: 'Increase the font size.',
980            cls: 'x-html-editor-tip'
981        },
982        decreasefontsize : {
983            title: 'Shrink Text',
984            text: 'Decrease the font size.',
985            cls: 'x-html-editor-tip'
986        },
987        backcolor : {
988            title: 'Text Highlight Color',
989            text: 'Change the background color of the selected text.',
990            cls: 'x-html-editor-tip'
991        },
992        forecolor : {
993            title: 'Font Color',
994            text: 'Change the color of the selected text.',
995            cls: 'x-html-editor-tip'
996        },
997        justifyleft : {
998            title: 'Align Text Left',
999            text: 'Align text to the left.',
1000            cls: 'x-html-editor-tip'
1001        },
1002        justifycenter : {
1003            title: 'Center Text',
1004            text: 'Center text in the editor.',
1005            cls: 'x-html-editor-tip'
1006        },
1007        justifyright : {
1008            title: 'Align Text Right',
1009            text: 'Align text to the right.',
1010            cls: 'x-html-editor-tip'
1011        },
1012        insertunorderedlist : {
1013            title: 'Bullet List',
1014            text: 'Start a bulleted list.',
1015            cls: 'x-html-editor-tip'
1016        },
1017        insertorderedlist : {
1018            title: 'Numbered List',
1019            text: 'Start a numbered list.',
1020            cls: 'x-html-editor-tip'
1021        },
1022        createlink : {
1023            title: 'Hyperlink',
1024            text: 'Make the selected text a hyperlink.',
1025            cls: 'x-html-editor-tip'
1026        },
1027        sourceedit : {
1028            title: 'Source Edit',
1029            text: 'Switch to source editing mode.',
1030            cls: 'x-html-editor-tip'
1031        }
1032    }
1033
1034    // hide stuff that is not compatible
1035    /**
1036     * @event blur
1037     * @hide
1038     */
1039    /**
1040     * @event change
1041     * @hide
1042     */
1043    /**
1044     * @event focus
1045     * @hide
1046     */
1047    /**
1048     * @event specialkey
1049     * @hide
1050     */
1051    /**
1052     * @cfg {String} fieldClass @hide
1053     */
1054    /**
1055     * @cfg {String} focusClass @hide
1056     */
1057    /**
1058     * @cfg {String} autoCreate @hide
1059     */
1060    /**
1061     * @cfg {String} inputType @hide
1062     */
1063    /**
1064     * @cfg {String} invalidClass @hide
1065     */
1066    /**
1067     * @cfg {String} invalidText @hide
1068     */
1069    /**
1070     * @cfg {String} msgFx @hide
1071     */
1072    /**
1073     * @cfg {String} validateOnBlur @hide
1074     */
1075    /**
1076     * @cfg {Boolean} allowDomMove  @hide
1077     */
1078    /**
1079     * @cfg {String} applyTo @hide
1080     */
1081    /**
1082     * @cfg {String} autoHeight  @hide
1083     */
1084    /**
1085     * @cfg {String} autoWidth  @hide
1086     */
1087    /**
1088     * @cfg {String} cls  @hide
1089     */
1090    /**
1091     * @cfg {String} disabled  @hide
1092     */
1093    /**
1094     * @cfg {String} disabledClass  @hide
1095     */
1096    /**
1097     * @cfg {String} msgTarget  @hide
1098     */
1099    /**
1100     * @cfg {String} readOnly  @hide
1101     */
1102    /**
1103     * @cfg {String} style  @hide
1104     */
1105    /**
1106     * @cfg {String} validationDelay  @hide
1107     */
1108    /**
1109     * @cfg {String} validationEvent  @hide
1110     */
1111    /**
1112     * @cfg {String} tabIndex  @hide
1113     */
1114    /**
1115     * @property disabled
1116     * @hide
1117     */
1118    /**
1119     * @method applyToMarkup
1120     * @hide
1121     */
1122    /**
1123     * @method disable
1124     * @hide
1125     */
1126    /**
1127     * @method enable
1128     * @hide
1129     */
1130    /**
1131     * @method validate
1132     * @hide
1133     */
1134    /**
1135     * @event valid
1136     * @hide
1137     */
1138    /**
1139     * @method setDisabled
1140     * @hide
1141     */
1142    /**
1143     * @cfg keys
1144     * @hide
1145     */
1146});
1147Ext.reg('htmleditor', Ext.form.HtmlEditor);
Note: See TracBrowser for help on using the repository browser.