~cdparra/gelee/trunk

« back to all changes in this revision

Viewing changes to webui/web/extjs/source/widgets/form/HtmlEditor.js

  • Committer: parra
  • Date: 2010-03-15 15:56:56 UTC
  • Revision ID: svn-v4:ac5bba68-f036-4e09-846e-8f32731cc928:trunk/gelee:1448
merged gelee at svn

Show diffs side-by-side

added added

removed removed

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