Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-simpleeditor', function(Y) {
2
    var YAHOO    = Y.YUI2;
3
    /*
4
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
5
Code licensed under the BSD License:
6
http://developer.yahoo.com/yui/license.html
7
version: 2.9.0
8
*/
9
(function() {
10
var Dom = YAHOO.util.Dom,
11
    Event = YAHOO.util.Event,
12
    Lang = YAHOO.lang;
13
    /**
14
     * @module editor
15
     * @description <p>Creates a rich custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar</p>
16
     * @class ToolbarButtonAdvanced
17
     * @namespace YAHOO.widget
18
     * @requires yahoo, dom, element, event, container_core, menu, button
19
     *
20
     * Provides a toolbar button based on the button and menu widgets.
21
     * @constructor
22
     * @class ToolbarButtonAdvanced
23
     * @param {String/HTMLElement} el The element to turn into a button.
24
     * @param {Object} attrs Object liternal containing configuration parameters.
25
    */
26
    if (YAHOO.widget.Button) {
27
        YAHOO.widget.ToolbarButtonAdvanced = YAHOO.widget.Button;
28
        /**
29
        * @property buttonType
30
        * @private
31
        * @description Tells if the Button is a Rich Button or a Simple Button
32
        */
33
        YAHOO.widget.ToolbarButtonAdvanced.prototype.buttonType = 'rich';
34
        /**
35
        * @method checkValue
36
        * @param {String} value The value of the option that we want to mark as selected
37
        * @description Select an option by value
38
        */
39
        YAHOO.widget.ToolbarButtonAdvanced.prototype.checkValue = function(value) {
40
            var _menuItems = this.getMenu().getItems();
41
            if (_menuItems.length === 0) {
42
                this.getMenu()._onBeforeShow();
43
                _menuItems = this.getMenu().getItems();
44
            }
45
            for (var i = 0; i < _menuItems.length; i++) {
46
                _menuItems[i].cfg.setProperty('checked', false);
47
                if (_menuItems[i].value == value) {
48
                    _menuItems[i].cfg.setProperty('checked', true);
49
                }
50
            }
51
        };
52
    } else {
53
        YAHOO.widget.ToolbarButtonAdvanced = function() {};
54
    }
55
 
56
 
57
    /**
58
     * @description <p>Creates a basic custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar</p><p>Provides a toolbar button based on the button and menu widgets, &lt;select&gt; elements are used in place of menu's.</p>
59
     * @class ToolbarButton
60
     * @namespace YAHOO.widget
61
     * @requires yahoo, dom, element, event
62
     * @extends YAHOO.util.Element
63
     *
64
     *
65
     * @constructor
66
     * @param {String/HTMLElement} el The element to turn into a button.
67
     * @param {Object} attrs Object liternal containing configuration parameters.
68
    */
69
 
70
    YAHOO.widget.ToolbarButton = function(el, attrs) {
71
 
72
        if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
73
            attrs = el;
74
        }
75
        var local_attrs = (attrs || {});
76
 
77
        var oConfig = {
78
            element: null,
79
            attributes: local_attrs
80
        };
81
 
82
        if (!oConfig.attributes.type) {
83
            oConfig.attributes.type = 'push';
84
        }
85
 
86
        oConfig.element = document.createElement('span');
87
        oConfig.element.setAttribute('unselectable', 'on');
88
        oConfig.element.className = 'yui-button yui-' + oConfig.attributes.type + '-button';
89
        oConfig.element.innerHTML = '<span class="first-child"><a href="#">LABEL</a></span>';
90
        oConfig.element.firstChild.firstChild.tabIndex = '-1';
91
        oConfig.attributes.id = (oConfig.attributes.id || Dom.generateId());
92
        oConfig.element.id = oConfig.attributes.id;
93
 
94
        YAHOO.widget.ToolbarButton.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
95
    };
96
 
97
    YAHOO.extend(YAHOO.widget.ToolbarButton, YAHOO.util.Element, {
98
        /**
99
        * @property buttonType
100
        * @private
101
        * @description Tells if the Button is a Rich Button or a Simple Button
102
        */
103
        buttonType: 'normal',
104
        /**
105
        * @method _handleMouseOver
106
        * @private
107
        * @description Adds classes to the button elements on mouseover (hover)
108
        */
109
        _handleMouseOver: function() {
110
            if (!this.get('disabled')) {
111
                this.addClass('yui-button-hover');
112
                this.addClass('yui-' + this.get('type') + '-button-hover');
113
            }
114
        },
115
        /**
116
        * @method _handleMouseOut
117
        * @private
118
        * @description Removes classes from the button elements on mouseout (hover)
119
        */
120
        _handleMouseOut: function() {
121
            this.removeClass('yui-button-hover');
122
            this.removeClass('yui-' + this.get('type') + '-button-hover');
123
        },
124
        /**
125
        * @method checkValue
126
        * @param {String} value The value of the option that we want to mark as selected
127
        * @description Select an option by value
128
        */
129
        checkValue: function(value) {
130
            if (this.get('type') == 'menu') {
131
                var opts = this._button.options;
132
                if (opts) {
133
                    for (var i = 0; i < opts.length; i++) {
134
                        if (opts[i].value == value) {
135
                            opts.selectedIndex = i;
136
                        }
137
                    }
138
                }
139
            }
140
        },
141
        /**
142
        * @method init
143
        * @description The ToolbarButton class's initialization method
144
        */
145
        init: function(p_oElement, p_oAttributes) {
146
            YAHOO.widget.ToolbarButton.superclass.init.call(this, p_oElement, p_oAttributes);
147
 
148
            this.on('mouseover', this._handleMouseOver, this, true);
149
            this.on('mouseout', this._handleMouseOut, this, true);
150
            this.on('click', function(ev) {
151
                Event.stopEvent(ev);
152
                return false;
153
            }, this, true);
154
        },
155
        /**
156
        * @method initAttributes
157
        * @description Initializes all of the configuration attributes used to create
158
        * the toolbar.
159
        * @param {Object} attr Object literal specifying a set of
160
        * configuration attributes used to create the toolbar.
161
        */
162
        initAttributes: function(attr) {
163
            YAHOO.widget.ToolbarButton.superclass.initAttributes.call(this, attr);
164
            /**
165
            * @attribute value
166
            * @description The value of the button
167
            * @type String
168
            */
169
            this.setAttributeConfig('value', {
170
                value: attr.value
171
            });
172
            /**
173
            * @attribute menu
174
            * @description The menu attribute, see YAHOO.widget.Button
175
            * @type Object
176
            */
177
            this.setAttributeConfig('menu', {
178
                value: attr.menu || false
179
            });
180
            /**
181
            * @attribute type
182
            * @description The type of button to create: push, menu, color, select, spin
183
            * @type String
184
            */
185
            this.setAttributeConfig('type', {
186
                value: attr.type,
187
                writeOnce: true,
188
                method: function(type) {
189
                    var el, opt;
190
                    if (!this._button) {
191
                        this._button = this.get('element').getElementsByTagName('a')[0];
192
                    }
193
                    switch (type) {
194
                        case 'select':
195
                        case 'menu':
196
                            el = document.createElement('select');
197
                            el.id = this.get('id');
198
                            var menu = this.get('menu');
199
                            for (var i = 0; i < menu.length; i++) {
200
                                opt = document.createElement('option');
201
                                opt.innerHTML = menu[i].text;
202
                                opt.value = menu[i].value;
203
                                if (menu[i].checked) {
204
                                    opt.selected = true;
205
                                }
206
                                el.appendChild(opt);
207
                            }
208
                            this._button.parentNode.replaceChild(el, this._button);
209
                            Event.on(el, 'change', this._handleSelect, this, true);
210
                            this._button = el;
211
                            break;
212
                    }
213
                }
214
            });
215
 
216
            /**
217
            * @attribute disabled
218
            * @description Set the button into a disabled state
219
            * @type String
220
            */
221
            this.setAttributeConfig('disabled', {
222
                value: attr.disabled || false,
223
                method: function(disabled) {
224
                    if (disabled) {
225
                        this.addClass('yui-button-disabled');
226
                        this.addClass('yui-' + this.get('type') + '-button-disabled');
227
                    } else {
228
                        this.removeClass('yui-button-disabled');
229
                        this.removeClass('yui-' + this.get('type') + '-button-disabled');
230
                    }
231
                    if ((this.get('type') == 'menu') || (this.get('type') == 'select')) {
232
                        this._button.disabled = disabled;
233
                    }
234
                }
235
            });
236
 
237
            /**
238
            * @attribute label
239
            * @description The text label for the button
240
            * @type String
241
            */
242
            this.setAttributeConfig('label', {
243
                value: attr.label,
244
                method: function(label) {
245
                    if (!this._button) {
246
                        this._button = this.get('element').getElementsByTagName('a')[0];
247
                    }
248
                    if (this.get('type') == 'push') {
249
                        this._button.innerHTML = label;
250
                    }
251
                }
252
            });
253
 
254
            /**
255
            * @attribute title
256
            * @description The title of the button
257
            * @type String
258
            */
259
            this.setAttributeConfig('title', {
260
                value: attr.title
261
            });
262
 
263
            /**
264
            * @config container
265
            * @description The container that the button is rendered to, handled by Toolbar
266
            * @type String
267
            */
268
            this.setAttributeConfig('container', {
269
                value: null,
270
                writeOnce: true,
271
                method: function(cont) {
272
                    this.appendTo(cont);
273
                }
274
            });
275
 
276
        },
277
        /**
278
        * @private
279
        * @method _handleSelect
280
        * @description The event fired when a change event gets fired on a select element
281
        * @param {Event} ev The change event.
282
        */
283
        _handleSelect: function(ev) {
284
            var tar = Event.getTarget(ev);
285
            var value = tar.options[tar.selectedIndex].value;
286
            this.fireEvent('change', {type: 'change', value: value });
287
        },
288
        /**
289
        * @method getMenu
290
        * @description A stub function to mimic YAHOO.widget.Button's getMenu method
291
        */
292
        getMenu: function() {
293
            return this.get('menu');
294
        },
295
        /**
296
        * @method destroy
297
        * @description Destroy the button
298
        */
299
        destroy: function() {
300
            Event.purgeElement(this.get('element'), true);
301
            this.get('element').parentNode.removeChild(this.get('element'));
302
            //Brutal Object Destroy
303
            for (var i in this) {
304
                if (Lang.hasOwnProperty(this, i)) {
305
                    this[i] = null;
306
                }
307
            }
308
        },
309
        /**
310
        * @method fireEvent
311
        * @description Overridden fireEvent method to prevent DOM events from firing if the button is disabled.
312
        */
313
        fireEvent: function(p_sType, p_aArgs) {
314
            //  Disabled buttons should not respond to DOM events
315
            if (this.DOM_EVENTS[p_sType] && this.get('disabled')) {
316
                Event.stopEvent(p_aArgs);
317
                return;
318
            }
319
 
320
            YAHOO.widget.ToolbarButton.superclass.fireEvent.call(this, p_sType, p_aArgs);
321
        },
322
        /**
323
        * @method toString
324
        * @description Returns a string representing the toolbar.
325
        * @return {String}
326
        */
327
        toString: function() {
328
            return 'ToolbarButton (' + this.get('id') + ')';
329
        }
330
 
331
    });
332
})();
333
/**
334
 * @module editor
335
 * @description <p>Creates a rich Toolbar widget based on Button. Primarily used with the Rich Text Editor</p>
336
 * @namespace YAHOO.widget
337
 * @requires yahoo, dom, element, event, toolbarbutton
338
 * @optional container_core, dragdrop
339
 */
340
(function() {
341
var Dom = YAHOO.util.Dom,
342
    Event = YAHOO.util.Event,
343
    Lang = YAHOO.lang;
344
 
345
    var getButton = function(id) {
346
        var button = id;
347
        if (Lang.isString(id)) {
348
            button = this.getButtonById(id);
349
        }
350
        if (Lang.isNumber(id)) {
351
            button = this.getButtonByIndex(id);
352
        }
353
        if ((!(button instanceof YAHOO.widget.ToolbarButton)) && (!(button instanceof YAHOO.widget.ToolbarButtonAdvanced))) {
354
            button = this.getButtonByValue(id);
355
        }
356
        if ((button instanceof YAHOO.widget.ToolbarButton) || (button instanceof YAHOO.widget.ToolbarButtonAdvanced)) {
357
            return button;
358
        }
359
        return false;
360
    };
361
 
362
    /**
363
     * Provides a rich toolbar widget based on the button and menu widgets
364
     * @constructor
365
     * @class Toolbar
366
     * @extends YAHOO.util.Element
367
     * @param {String/HTMLElement} el The element to turn into a toolbar.
368
     * @param {Object} attrs Object liternal containing configuration parameters.
369
    */
370
    YAHOO.widget.Toolbar = function(el, attrs) {
371
 
372
        if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
373
            attrs = el;
374
        }
375
        var local_attrs = {};
376
        if (attrs) {
377
            Lang.augmentObject(local_attrs, attrs); //Break the config reference
378
        }
379
 
380
 
381
        var oConfig = {
382
            element: null,
383
            attributes: local_attrs
384
        };
385
 
386
 
387
        if (Lang.isString(el) && Dom.get(el)) {
388
            oConfig.element = Dom.get(el);
389
        } else if (Lang.isObject(el) && Dom.get(el) && Dom.get(el).nodeType) {
390
            oConfig.element = Dom.get(el);
391
        }
392
 
393
 
394
        if (!oConfig.element) {
395
            oConfig.element = document.createElement('DIV');
396
            oConfig.element.id = Dom.generateId();
397
 
398
            if (local_attrs.container && Dom.get(local_attrs.container)) {
399
                Dom.get(local_attrs.container).appendChild(oConfig.element);
400
            }
401
        }
402
 
403
 
404
        if (!oConfig.element.id) {
405
            oConfig.element.id = ((Lang.isString(el)) ? el : Dom.generateId());
406
        }
407
 
408
        var fs = document.createElement('fieldset');
409
        var lg = document.createElement('legend');
410
        lg.innerHTML = 'Toolbar';
411
        fs.appendChild(lg);
412
 
413
        var cont = document.createElement('DIV');
414
        oConfig.attributes.cont = cont;
415
        Dom.addClass(cont, 'yui-toolbar-subcont');
416
        fs.appendChild(cont);
417
        oConfig.element.appendChild(fs);
418
 
419
        oConfig.element.tabIndex = -1;
420
 
421
 
422
        oConfig.attributes.element = oConfig.element;
423
        oConfig.attributes.id = oConfig.element.id;
424
 
425
        this._configuredButtons = [];
426
 
427
        YAHOO.widget.Toolbar.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
428
 
429
    };
430
 
431
    YAHOO.extend(YAHOO.widget.Toolbar, YAHOO.util.Element, {
432
        /**
433
        * @protected
434
        * @property _configuredButtons
435
        * @type Array
436
        */
437
        _configuredButtons: null,
438
        /**
439
        * @method _addMenuClasses
440
        * @private
441
        * @description This method is called from Menu's renderEvent to add a few more classes to the menu items
442
        * @param {String} ev The event that fired.
443
        * @param {Array} na Array of event information.
444
        * @param {Object} o Button config object.
445
        */
446
        _addMenuClasses: function(ev, na, o) {
447
            Dom.addClass(this.element, 'yui-toolbar-' + o.get('value') + '-menu');
448
            if (Dom.hasClass(o._button.parentNode.parentNode, 'yui-toolbar-select')) {
449
                Dom.addClass(this.element, 'yui-toolbar-select-menu');
450
            }
451
            var items = this.getItems();
452
            for (var i = 0; i < items.length; i++) {
453
                Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-').toLowerCase() : items[i]._oText.nodeValue.replace(/ /g, '-').toLowerCase()));
454
                Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-') : items[i]._oText.nodeValue.replace(/ /g, '-')));
455
            }
456
        },
457
        /**
458
        * @property buttonType
459
        * @description The default button to use
460
        * @type Object
461
        */
462
        buttonType: YAHOO.widget.ToolbarButton,
463
        /**
464
        * @property dd
465
        * @description The DragDrop instance associated with the Toolbar
466
        * @type Object
467
        */
468
        dd: null,
469
        /**
470
        * @property _colorData
471
        * @description Object reference containing colors hex and text values.
472
        * @type Object
473
        */
474
        _colorData: {
475
/* {{{ _colorData */
476
    '#111111': 'Obsidian',
477
    '#2D2D2D': 'Dark Gray',
478
    '#434343': 'Shale',
479
    '#5B5B5B': 'Flint',
480
    '#737373': 'Gray',
481
    '#8B8B8B': 'Concrete',
482
    '#A2A2A2': 'Gray',
483
    '#B9B9B9': 'Titanium',
484
    '#000000': 'Black',
485
    '#D0D0D0': 'Light Gray',
486
    '#E6E6E6': 'Silver',
487
    '#FFFFFF': 'White',
488
    '#BFBF00': 'Pumpkin',
489
    '#FFFF00': 'Yellow',
490
    '#FFFF40': 'Banana',
491
    '#FFFF80': 'Pale Yellow',
492
    '#FFFFBF': 'Butter',
493
    '#525330': 'Raw Siena',
494
    '#898A49': 'Mildew',
495
    '#AEA945': 'Olive',
496
    '#7F7F00': 'Paprika',
497
    '#C3BE71': 'Earth',
498
    '#E0DCAA': 'Khaki',
499
    '#FCFAE1': 'Cream',
500
    '#60BF00': 'Cactus',
501
    '#80FF00': 'Chartreuse',
502
    '#A0FF40': 'Green',
503
    '#C0FF80': 'Pale Lime',
504
    '#DFFFBF': 'Light Mint',
505
    '#3B5738': 'Green',
506
    '#668F5A': 'Lime Gray',
507
    '#7F9757': 'Yellow',
508
    '#407F00': 'Clover',
509
    '#8A9B55': 'Pistachio',
510
    '#B7C296': 'Light Jade',
511
    '#E6EBD5': 'Breakwater',
512
    '#00BF00': 'Spring Frost',
513
    '#00FF80': 'Pastel Green',
514
    '#40FFA0': 'Light Emerald',
515
    '#80FFC0': 'Sea Foam',
516
    '#BFFFDF': 'Sea Mist',
517
    '#033D21': 'Dark Forrest',
518
    '#438059': 'Moss',
519
    '#7FA37C': 'Medium Green',
520
    '#007F40': 'Pine',
521
    '#8DAE94': 'Yellow Gray Green',
522
    '#ACC6B5': 'Aqua Lung',
523
    '#DDEBE2': 'Sea Vapor',
524
    '#00BFBF': 'Fog',
525
    '#00FFFF': 'Cyan',
526
    '#40FFFF': 'Turquoise Blue',
527
    '#80FFFF': 'Light Aqua',
528
    '#BFFFFF': 'Pale Cyan',
529
    '#033D3D': 'Dark Teal',
530
    '#347D7E': 'Gray Turquoise',
531
    '#609A9F': 'Green Blue',
532
    '#007F7F': 'Seaweed',
533
    '#96BDC4': 'Green Gray',
534
    '#B5D1D7': 'Soapstone',
535
    '#E2F1F4': 'Light Turquoise',
536
    '#0060BF': 'Summer Sky',
537
    '#0080FF': 'Sky Blue',
538
    '#40A0FF': 'Electric Blue',
539
    '#80C0FF': 'Light Azure',
540
    '#BFDFFF': 'Ice Blue',
541
    '#1B2C48': 'Navy',
542
    '#385376': 'Biscay',
543
    '#57708F': 'Dusty Blue',
544
    '#00407F': 'Sea Blue',
545
    '#7792AC': 'Sky Blue Gray',
546
    '#A8BED1': 'Morning Sky',
547
    '#DEEBF6': 'Vapor',
548
    '#0000BF': 'Deep Blue',
549
    '#0000FF': 'Blue',
550
    '#4040FF': 'Cerulean Blue',
551
    '#8080FF': 'Evening Blue',
552
    '#BFBFFF': 'Light Blue',
553
    '#212143': 'Deep Indigo',
554
    '#373E68': 'Sea Blue',
555
    '#444F75': 'Night Blue',
556
    '#00007F': 'Indigo Blue',
557
    '#585E82': 'Dockside',
558
    '#8687A4': 'Blue Gray',
559
    '#D2D1E1': 'Light Blue Gray',
560
    '#6000BF': 'Neon Violet',
561
    '#8000FF': 'Blue Violet',
562
    '#A040FF': 'Violet Purple',
563
    '#C080FF': 'Violet Dusk',
564
    '#DFBFFF': 'Pale Lavender',
565
    '#302449': 'Cool Shale',
566
    '#54466F': 'Dark Indigo',
567
    '#655A7F': 'Dark Violet',
568
    '#40007F': 'Violet',
569
    '#726284': 'Smoky Violet',
570
    '#9E8FA9': 'Slate Gray',
571
    '#DCD1DF': 'Violet White',
572
    '#BF00BF': 'Royal Violet',
573
    '#FF00FF': 'Fuchsia',
574
    '#FF40FF': 'Magenta',
575
    '#FF80FF': 'Orchid',
576
    '#FFBFFF': 'Pale Magenta',
577
    '#4A234A': 'Dark Purple',
578
    '#794A72': 'Medium Purple',
579
    '#936386': 'Cool Granite',
580
    '#7F007F': 'Purple',
581
    '#9D7292': 'Purple Moon',
582
    '#C0A0B6': 'Pale Purple',
583
    '#ECDAE5': 'Pink Cloud',
584
    '#BF005F': 'Hot Pink',
585
    '#FF007F': 'Deep Pink',
586
    '#FF409F': 'Grape',
587
    '#FF80BF': 'Electric Pink',
588
    '#FFBFDF': 'Pink',
589
    '#451528': 'Purple Red',
590
    '#823857': 'Purple Dino',
591
    '#A94A76': 'Purple Gray',
592
    '#7F003F': 'Rose',
593
    '#BC6F95': 'Antique Mauve',
594
    '#D8A5BB': 'Cool Marble',
595
    '#F7DDE9': 'Pink Granite',
596
    '#C00000': 'Apple',
597
    '#FF0000': 'Fire Truck',
598
    '#FF4040': 'Pale Red',
599
    '#FF8080': 'Salmon',
600
    '#FFC0C0': 'Warm Pink',
601
    '#441415': 'Sepia',
602
    '#82393C': 'Rust',
603
    '#AA4D4E': 'Brick',
604
    '#800000': 'Brick Red',
605
    '#BC6E6E': 'Mauve',
606
    '#D8A3A4': 'Shrimp Pink',
607
    '#F8DDDD': 'Shell Pink',
608
    '#BF5F00': 'Dark Orange',
609
    '#FF7F00': 'Orange',
610
    '#FF9F40': 'Grapefruit',
611
    '#FFBF80': 'Canteloupe',
612
    '#FFDFBF': 'Wax',
613
    '#482C1B': 'Dark Brick',
614
    '#855A40': 'Dirt',
615
    '#B27C51': 'Tan',
616
    '#7F3F00': 'Nutmeg',
617
    '#C49B71': 'Mustard',
618
    '#E1C4A8': 'Pale Tan',
619
    '#FDEEE0': 'Marble'
620
/* }}} */
621
        },
622
        /**
623
        * @property _colorPicker
624
        * @description The HTML Element containing the colorPicker
625
        * @type HTMLElement
626
        */
627
        _colorPicker: null,
628
        /**
629
        * @property STR_COLLAPSE
630
        * @description String for Toolbar Collapse Button
631
        * @type String
632
        */
633
        STR_COLLAPSE: 'Collapse Toolbar',
634
        /**
635
        * @property STR_EXPAND
636
        * @description String for Toolbar Collapse Button - Expand
637
        * @type String
638
        */
639
        STR_EXPAND: 'Expand Toolbar',
640
        /**
641
        * @property STR_SPIN_LABEL
642
        * @description String for spinbutton dynamic label. Note the {VALUE} will be replaced with YAHOO.lang.substitute
643
        * @type String
644
        */
645
        STR_SPIN_LABEL: 'Spin Button with value {VALUE}. Use Control Shift Up Arrow and Control Shift Down arrow keys to increase or decrease the value.',
646
        /**
647
        * @property STR_SPIN_UP
648
        * @description String for spinbutton up
649
        * @type String
650
        */
651
        STR_SPIN_UP: 'Click to increase the value of this input',
652
        /**
653
        * @property STR_SPIN_DOWN
654
        * @description String for spinbutton down
655
        * @type String
656
        */
657
        STR_SPIN_DOWN: 'Click to decrease the value of this input',
658
        /**
659
        * @property _titlebar
660
        * @description Object reference to the titlebar
661
        * @type HTMLElement
662
        */
663
        _titlebar: null,
664
        /**
665
        * @property browser
666
        * @description Standard browser detection
667
        * @type Object
668
        */
669
        browser: YAHOO.env.ua,
670
        /**
671
        * @protected
672
        * @property _buttonList
673
        * @description Internal property list of current buttons in the toolbar
674
        * @type Array
675
        */
676
        _buttonList: null,
677
        /**
678
        * @protected
679
        * @property _buttonGroupList
680
        * @description Internal property list of current button groups in the toolbar
681
        * @type Array
682
        */
683
        _buttonGroupList: null,
684
        /**
685
        * @protected
686
        * @property _sep
687
        * @description Internal reference to the separator HTML Element for cloning
688
        * @type HTMLElement
689
        */
690
        _sep: null,
691
        /**
692
        * @protected
693
        * @property _sepCount
694
        * @description Internal refernce for counting separators, so we can give them a useful class name for styling
695
        * @type Number
696
        */
697
        _sepCount: null,
698
        /**
699
        * @protected
700
        * @property draghandle
701
        * @type HTMLElement
702
        */
703
        _dragHandle: null,
704
        /**
705
        * @protected
706
        * @property _toolbarConfigs
707
        * @type Object
708
        */
709
        _toolbarConfigs: {
710
            renderer: true
711
        },
712
        /**
713
        * @protected
714
        * @property CLASS_CONTAINER
715
        * @description Default CSS class to apply to the toolbar container element
716
        * @type String
717
        */
718
        CLASS_CONTAINER: 'yui-toolbar-container',
719
        /**
720
        * @protected
721
        * @property CLASS_DRAGHANDLE
722
        * @description Default CSS class to apply to the toolbar's drag handle element
723
        * @type String
724
        */
725
        CLASS_DRAGHANDLE: 'yui-toolbar-draghandle',
726
        /**
727
        * @protected
728
        * @property CLASS_SEPARATOR
729
        * @description Default CSS class to apply to all separators in the toolbar
730
        * @type String
731
        */
732
        CLASS_SEPARATOR: 'yui-toolbar-separator',
733
        /**
734
        * @protected
735
        * @property CLASS_DISABLED
736
        * @description Default CSS class to apply when the toolbar is disabled
737
        * @type String
738
        */
739
        CLASS_DISABLED: 'yui-toolbar-disabled',
740
        /**
741
        * @protected
742
        * @property CLASS_PREFIX
743
        * @description Default prefix for dynamically created class names
744
        * @type String
745
        */
746
        CLASS_PREFIX: 'yui-toolbar',
747
        /**
748
        * @method init
749
        * @description The Toolbar class's initialization method
750
        */
751
        init: function(p_oElement, p_oAttributes) {
752
            YAHOO.widget.Toolbar.superclass.init.call(this, p_oElement, p_oAttributes);
753
        },
754
        /**
755
        * @method initAttributes
756
        * @description Initializes all of the configuration attributes used to create
757
        * the toolbar.
758
        * @param {Object} attr Object literal specifying a set of
759
        * configuration attributes used to create the toolbar.
760
        */
761
        initAttributes: function(attr) {
762
            YAHOO.widget.Toolbar.superclass.initAttributes.call(this, attr);
763
            this.addClass(this.CLASS_CONTAINER);
764
 
765
            /**
766
            * @attribute buttonType
767
            * @description The buttonType to use (advanced or basic)
768
            * @type String
769
            */
770
            this.setAttributeConfig('buttonType', {
771
                value: attr.buttonType || 'basic',
772
                writeOnce: true,
773
                validator: function(type) {
774
                    switch (type) {
775
                        case 'advanced':
776
                        case 'basic':
777
                            return true;
778
                    }
779
                    return false;
780
                },
781
                method: function(type) {
782
                    if (type == 'advanced') {
783
                        if (YAHOO.widget.Button) {
784
                            this.buttonType = YAHOO.widget.ToolbarButtonAdvanced;
785
                        } else {
786
                            this.buttonType = YAHOO.widget.ToolbarButton;
787
                        }
788
                    } else {
789
                        this.buttonType = YAHOO.widget.ToolbarButton;
790
                    }
791
                }
792
            });
793
 
794
 
795
            /**
796
            * @attribute buttons
797
            * @description Object specifying the buttons to include in the toolbar
798
            * Example:
799
            * <code><pre>
800
            * {
801
            *   { id: 'b3', type: 'button', label: 'Underline', value: 'underline' },
802
            *   { type: 'separator' },
803
            *   { id: 'b4', type: 'menu', label: 'Align', value: 'align',
804
            *       menu: [
805
            *           { text: "Left", value: 'alignleft' },
806
            *           { text: "Center", value: 'aligncenter' },
807
            *           { text: "Right", value: 'alignright' }
808
            *       ]
809
            *   }
810
            * }
811
            * </pre></code>
812
            * @type Array
813
            */
814
 
815
            this.setAttributeConfig('buttons', {
816
                value: [],
817
                writeOnce: true,
818
                method: function(data) {
819
                    var i, button, buttons, len, b;
820
                    for (i in data) {
821
                        if (Lang.hasOwnProperty(data, i)) {
822
                            if (data[i].type == 'separator') {
823
                                this.addSeparator();
824
                            } else if (data[i].group !== undefined) {
825
                                buttons = this.addButtonGroup(data[i]);
826
                                if (buttons) {
827
                                    len = buttons.length;
828
                                    for(b = 0; b < len; b++) {
829
                                        if (buttons[b]) {
830
                                            this._configuredButtons[this._configuredButtons.length] = buttons[b].id;
831
                                        }
832
                                    }
833
                                }
834
 
835
                            } else {
836
                                button = this.addButton(data[i]);
837
                                if (button) {
838
                                    this._configuredButtons[this._configuredButtons.length] = button.id;
839
                                }
840
                            }
841
                        }
842
                    }
843
                }
844
            });
845
 
846
            /**
847
            * @attribute disabled
848
            * @description Boolean indicating if the toolbar should be disabled. It will also disable the draggable attribute if it is on.
849
            * @default false
850
            * @type Boolean
851
            */
852
            this.setAttributeConfig('disabled', {
853
                value: false,
854
                method: function(disabled) {
855
                    if (this.get('disabled') === disabled) {
856
                        return false;
857
                    }
858
                    if (disabled) {
859
                        this.addClass(this.CLASS_DISABLED);
860
                        this.set('draggable', false);
861
                        this.disableAllButtons();
862
                    } else {
863
                        this.removeClass(this.CLASS_DISABLED);
864
                        if (this._configs.draggable._initialConfig.value) {
865
                            //Draggable by default, set it back
866
                            this.set('draggable', true);
867
                        }
868
                        this.resetAllButtons();
869
                    }
870
                }
871
            });
872
 
873
            /**
874
            * @config cont
875
            * @description The container for the toolbar.
876
            * @type HTMLElement
877
            */
878
            this.setAttributeConfig('cont', {
879
                value: attr.cont,
880
                readOnly: true
881
            });
882
 
883
 
884
            /**
885
            * @attribute grouplabels
886
            * @description Boolean indicating if the toolbar should show the group label's text string.
887
            * @default true
888
            * @type Boolean
889
            */
890
            this.setAttributeConfig('grouplabels', {
891
                value: ((attr.grouplabels === false) ? false : true),
892
                method: function(grouplabels) {
893
                    if (grouplabels) {
894
                        Dom.removeClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
895
                    } else {
896
                        Dom.addClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
897
                    }
898
                }
899
            });
900
            /**
901
            * @attribute titlebar
902
            * @description Boolean indicating if the toolbar should have a titlebar. If
903
            * passed a string, it will use that as the titlebar text
904
            * @default false
905
            * @type Boolean or String
906
            */
907
            this.setAttributeConfig('titlebar', {
908
                value: false,
909
                method: function(titlebar) {
910
                    if (titlebar) {
911
                        if (this._titlebar && this._titlebar.parentNode) {
912
                            this._titlebar.parentNode.removeChild(this._titlebar);
913
                        }
914
                        this._titlebar = document.createElement('DIV');
915
                        this._titlebar.tabIndex = '-1';
916
                        Event.on(this._titlebar, 'focus', function() {
917
                            this._handleFocus();
918
                        }, this, true);
919
                        Dom.addClass(this._titlebar, this.CLASS_PREFIX + '-titlebar');
920
                        if (Lang.isString(titlebar)) {
921
                            var h2 = document.createElement('h2');
922
                            h2.tabIndex = '-1';
923
                            h2.innerHTML = '<a href="#" tabIndex="0">' + titlebar + '</a>';
924
                            this._titlebar.appendChild(h2);
925
                            Event.on(h2.firstChild, 'click', function(ev) {
926
                                Event.stopEvent(ev);
927
                            });
928
                            Event.on([h2, h2.firstChild], 'focus', function() {
929
                                this._handleFocus();
930
                            }, this, true);
931
                        }
932
                        if (this.get('firstChild')) {
933
                            this.insertBefore(this._titlebar, this.get('firstChild'));
934
                        } else {
935
                            this.appendChild(this._titlebar);
936
                        }
937
                        if (this.get('collapse')) {
938
                            this.set('collapse', true);
939
                        }
940
                    } else if (this._titlebar) {
941
                        if (this._titlebar && this._titlebar.parentNode) {
942
                            this._titlebar.parentNode.removeChild(this._titlebar);
943
                        }
944
                    }
945
                }
946
            });
947
 
948
 
949
            /**
950
            * @attribute collapse
951
            * @description Boolean indicating if the the titlebar should have a collapse button.
952
            * The collapse button will not remove the toolbar, it will minimize it to the titlebar
953
            * @default false
954
            * @type Boolean
955
            */
956
            this.setAttributeConfig('collapse', {
957
                value: false,
958
                method: function(collapse) {
959
                    if (this._titlebar) {
960
                        var collapseEl = null;
961
                        var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
962
                        if (collapse) {
963
                            if (el.length > 0) {
964
                                //There is already a collapse button
965
                                return true;
966
                            }
967
                            collapseEl = document.createElement('SPAN');
968
                            collapseEl.innerHTML = 'X';
969
                            collapseEl.title = this.STR_COLLAPSE;
970
 
971
                            Dom.addClass(collapseEl, 'collapse');
972
                            this._titlebar.appendChild(collapseEl);
973
                            Event.addListener(collapseEl, 'click', function() {
974
                                if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
975
                                    this.collapse(false); //Expand Toolbar
976
                                } else {
977
                                    this.collapse(); //Collapse Toolbar
978
                                }
979
                            }, this, true);
980
                        } else {
981
                            collapseEl = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
982
                            if (collapseEl[0]) {
983
                                if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
984
                                    //We are closed, reopen the titlebar..
985
                                    this.collapse(false); //Expand Toolbar
986
                                }
987
                                collapseEl[0].parentNode.removeChild(collapseEl[0]);
988
                            }
989
                        }
990
                    }
991
                }
992
            });
993
 
994
            /**
995
            * @attribute draggable
996
            * @description Boolean indicating if the toolbar should be draggable.
997
            * @default false
998
            * @type Boolean
999
            */
1000
 
1001
            this.setAttributeConfig('draggable', {
1002
                value: (attr.draggable || false),
1003
                method: function(draggable) {
1004
                    if (draggable && !this.get('titlebar')) {
1005
                        if (!this._dragHandle) {
1006
                            this._dragHandle = document.createElement('SPAN');
1007
                            this._dragHandle.innerHTML = '|';
1008
                            this._dragHandle.setAttribute('title', 'Click to drag the toolbar');
1009
                            this._dragHandle.id = this.get('id') + '_draghandle';
1010
                            Dom.addClass(this._dragHandle, this.CLASS_DRAGHANDLE);
1011
                            if (this.get('cont').hasChildNodes()) {
1012
                                this.get('cont').insertBefore(this._dragHandle, this.get('cont').firstChild);
1013
                            } else {
1014
                                this.get('cont').appendChild(this._dragHandle);
1015
                            }
1016
                            this.dd = new YAHOO.util.DD(this.get('id'));
1017
                            this.dd.setHandleElId(this._dragHandle.id);
1018
 
1019
                        }
1020
                    } else {
1021
                        if (this._dragHandle) {
1022
                            this._dragHandle.parentNode.removeChild(this._dragHandle);
1023
                            this._dragHandle = null;
1024
                            this.dd = null;
1025
                        }
1026
                    }
1027
                    if (this._titlebar) {
1028
                        if (draggable) {
1029
                            this.dd = new YAHOO.util.DD(this.get('id'));
1030
                            this.dd.setHandleElId(this._titlebar);
1031
                            Dom.addClass(this._titlebar, 'draggable');
1032
                        } else {
1033
                            Dom.removeClass(this._titlebar, 'draggable');
1034
                            if (this.dd) {
1035
                                this.dd.unreg();
1036
                                this.dd = null;
1037
                            }
1038
                        }
1039
                    }
1040
                },
1041
                validator: function(value) {
1042
                    var ret = true;
1043
                    if (!YAHOO.util.DD) {
1044
                        ret = false;
1045
                    }
1046
                    return ret;
1047
                }
1048
            });
1049
 
1050
        },
1051
        /**
1052
        * @method addButtonGroup
1053
        * @description Add a new button group to the toolbar. (uses addButton)
1054
        * @param {Object} oGroup Object literal reference to the Groups Config (contains an array of button configs as well as the group label)
1055
        */
1056
        addButtonGroup: function(oGroup) {
1057
            if (!this.get('element')) {
1058
                this._queue[this._queue.length] = ['addButtonGroup', arguments];
1059
                return false;
1060
            }
1061
 
1062
            if (!this.hasClass(this.CLASS_PREFIX + '-grouped')) {
1063
                this.addClass(this.CLASS_PREFIX + '-grouped');
1064
            }
1065
            var div = document.createElement('DIV');
1066
            Dom.addClass(div, this.CLASS_PREFIX + '-group');
1067
            Dom.addClass(div, this.CLASS_PREFIX + '-group-' + oGroup.group);
1068
            if (oGroup.label) {
1069
                var label = document.createElement('h3');
1070
                label.innerHTML = oGroup.label;
1071
                div.appendChild(label);
1072
            }
1073
            if (!this.get('grouplabels')) {
1074
                Dom.addClass(this.get('cont'), this.CLASS_PREFIX, '-nogrouplabels');
1075
            }
1076
 
1077
            this.get('cont').appendChild(div);
1078
 
1079
            //For accessibility, let's put all of the group buttons in an Unordered List
1080
            var ul = document.createElement('ul');
1081
            div.appendChild(ul);
1082
 
1083
            if (!this._buttonGroupList) {
1084
                this._buttonGroupList = {};
1085
            }
1086
 
1087
            this._buttonGroupList[oGroup.group] = ul;
1088
 
1089
            //An array of the button ids added to this group
1090
            //This is used for destruction later...
1091
            var addedButtons = [],
1092
                button;
1093
 
1094
 
1095
            for (var i = 0; i < oGroup.buttons.length; i++) {
1096
                var li = document.createElement('li');
1097
                li.className = this.CLASS_PREFIX + '-groupitem';
1098
                ul.appendChild(li);
1099
                if ((oGroup.buttons[i].type !== undefined) && oGroup.buttons[i].type == 'separator') {
1100
                    this.addSeparator(li);
1101
                } else {
1102
                    oGroup.buttons[i].container = li;
1103
                    button = this.addButton(oGroup.buttons[i]);
1104
                    if (button) {
1105
                        addedButtons[addedButtons.length]  = button.id;
1106
                    }
1107
                }
1108
            }
1109
            return addedButtons;
1110
        },
1111
        /**
1112
        * @method addButtonToGroup
1113
        * @description Add a new button to a toolbar group. Buttons supported:
1114
        *   push, split, menu, select, color, spin
1115
        * @param {Object} oButton Object literal reference to the Button's Config
1116
        * @param {String} group The Group identifier passed into the initial config
1117
        * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1118
        */
1119
        addButtonToGroup: function(oButton, group, after) {
1120
            var groupCont = this._buttonGroupList[group],
1121
                li = document.createElement('li');
1122
 
1123
            li.className = this.CLASS_PREFIX + '-groupitem';
1124
            oButton.container = li;
1125
            this.addButton(oButton, after);
1126
            groupCont.appendChild(li);
1127
        },
1128
        /**
1129
        * @method addButton
1130
        * @description Add a new button to the toolbar. Buttons supported:
1131
        *   push, split, menu, select, color, spin
1132
        * @param {Object} oButton Object literal reference to the Button's Config
1133
        * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1134
        */
1135
        addButton: function(oButton, after) {
1136
            if (!this.get('element')) {
1137
                this._queue[this._queue.length] = ['addButton', arguments];
1138
                return false;
1139
            }
1140
            if (!this._buttonList) {
1141
                this._buttonList = [];
1142
            }
1143
            if (!oButton.container) {
1144
                oButton.container = this.get('cont');
1145
            }
1146
 
1147
            if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
1148
                if (Lang.isArray(oButton.menu)) {
1149
                    for (var i in oButton.menu) {
1150
                        if (Lang.hasOwnProperty(oButton.menu, i)) {
1151
                            var funcObject = {
1152
                                fn: function(ev, x, oMenu) {
1153
                                    if (!oButton.menucmd) {
1154
                                        oButton.menucmd = oButton.value;
1155
                                    }
1156
                                    oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
1157
                                },
1158
                                scope: this
1159
                            };
1160
                            oButton.menu[i].onclick = funcObject;
1161
                        }
1162
                    }
1163
                }
1164
            }
1165
            var _oButton = {}, skip = false;
1166
            for (var o in oButton) {
1167
                if (Lang.hasOwnProperty(oButton, o)) {
1168
                    if (!this._toolbarConfigs[o]) {
1169
                        _oButton[o] = oButton[o];
1170
                    }
1171
                }
1172
            }
1173
            if (oButton.type == 'select') {
1174
                _oButton.type = 'menu';
1175
            }
1176
            if (oButton.type == 'spin') {
1177
                _oButton.type = 'push';
1178
            }
1179
            if (_oButton.type == 'color') {
1180
                if (YAHOO.widget.Overlay) {
1181
                    _oButton = this._makeColorButton(_oButton);
1182
                } else {
1183
                    skip = true;
1184
                }
1185
            }
1186
            if (_oButton.menu) {
1187
                if ((YAHOO.widget.Overlay) && (oButton.menu instanceof YAHOO.widget.Overlay)) {
1188
                    oButton.menu.showEvent.subscribe(function() {
1189
                        this._button = _oButton;
1190
                    });
1191
                } else {
1192
                    for (var m = 0; m < _oButton.menu.length; m++) {
1193
                        if (!_oButton.menu[m].value) {
1194
                            _oButton.menu[m].value = _oButton.menu[m].text;
1195
                        }
1196
                    }
1197
                    if (this.browser.webkit) {
1198
                        _oButton.focusmenu = false;
1199
                    }
1200
                }
1201
            }
1202
            if (skip) {
1203
                oButton = false;
1204
            } else {
1205
                //Add to .get('buttons') manually
1206
                this._configs.buttons.value[this._configs.buttons.value.length] = oButton;
1207
 
1208
                var tmp = new this.buttonType(_oButton);
1209
                tmp.get('element').tabIndex = '-1';
1210
                tmp.get('element').setAttribute('role', 'button');
1211
                tmp._selected = true;
1212
 
1213
                if (this.get('disabled')) {
1214
                    //Toolbar is disabled, disable the new button too!
1215
                    tmp.set('disabled', true);
1216
                }
1217
                if (!oButton.id) {
1218
                    oButton.id = tmp.get('id');
1219
                }
1220
 
1221
                if (after) {
1222
                    var el = tmp.get('element');
1223
                    var nextSib = null;
1224
                    if (after.get) {
1225
                        nextSib = after.get('element').nextSibling;
1226
                    } else if (after.nextSibling) {
1227
                        nextSib = after.nextSibling;
1228
                    }
1229
                    if (nextSib) {
1230
                        nextSib.parentNode.insertBefore(el, nextSib);
1231
                    }
1232
                }
1233
                tmp.addClass(this.CLASS_PREFIX + '-' + tmp.get('value'));
1234
 
1235
                var icon = document.createElement('span');
1236
                icon.className = this.CLASS_PREFIX + '-icon';
1237
                tmp.get('element').insertBefore(icon, tmp.get('firstChild'));
1238
                if (tmp._button.tagName.toLowerCase() == 'button') {
1239
                    tmp.get('element').setAttribute('unselectable', 'on');
1240
                    //Replace the Button HTML Element with an a href if it exists
1241
                    var a = document.createElement('a');
1242
                    a.innerHTML = tmp._button.innerHTML;
1243
                    a.href = '#';
1244
                    a.tabIndex = '-1';
1245
                    Event.on(a, 'click', function(ev) {
1246
                        Event.stopEvent(ev);
1247
                    });
1248
                    tmp._button.parentNode.replaceChild(a, tmp._button);
1249
                    tmp._button = a;
1250
                }
1251
 
1252
                if (oButton.type == 'select') {
1253
                    if (tmp._button.tagName.toLowerCase() == 'select') {
1254
                        icon.parentNode.removeChild(icon);
1255
                        var iel = tmp._button,
1256
                            parEl = tmp.get('element');
1257
                        parEl.parentNode.replaceChild(iel, parEl);
1258
                        //The 'element' value is currently the orphaned element
1259
                        //In order for "destroy" to execute we need to get('element') to reference the correct node.
1260
                        //I'm not sure if there is a direct approach to setting this value.
1261
                        tmp._configs.element.value = iel;
1262
                    } else {
1263
                        //Don't put a class on it if it's a real select element
1264
                        tmp.addClass(this.CLASS_PREFIX + '-select');
1265
                    }
1266
                }
1267
                if (oButton.type == 'spin') {
1268
                    if (!Lang.isArray(oButton.range)) {
1269
                        oButton.range = [ 10, 100 ];
1270
                    }
1271
                    this._makeSpinButton(tmp, oButton);
1272
                }
1273
                tmp.get('element').setAttribute('title', tmp.get('label'));
1274
                if (oButton.type != 'spin') {
1275
                    if ((YAHOO.widget.Overlay) && (_oButton.menu instanceof YAHOO.widget.Overlay)) {
1276
                        var showPicker = function(ev) {
1277
                            var exec = true;
1278
                            if (ev.keyCode && (ev.keyCode == 9)) {
1279
                                exec = false;
1280
                            }
1281
                            if (exec) {
1282
                                if (this._colorPicker) {
1283
                                    this._colorPicker._button = oButton.value;
1284
                                }
1285
                                var menuEL = tmp.getMenu().element;
1286
                                if (Dom.getStyle(menuEL, 'visibility') == 'hidden') {
1287
                                    tmp.getMenu().show();
1288
                                } else {
1289
                                    tmp.getMenu().hide();
1290
                                }
1291
                            }
1292
                            YAHOO.util.Event.stopEvent(ev);
1293
                        };
1294
                        tmp.on('mousedown', showPicker, oButton, this);
1295
                        tmp.on('keydown', showPicker, oButton, this);
1296
 
1297
                    } else if ((oButton.type != 'menu') && (oButton.type != 'select')) {
1298
                        tmp.on('keypress', this._buttonClick, oButton, this);
1299
                        tmp.on('mousedown', function(ev) {
1300
                            YAHOO.util.Event.stopEvent(ev);
1301
                            this._buttonClick(ev, oButton);
1302
                        }, oButton, this);
1303
                        tmp.on('click', function(ev) {
1304
                            YAHOO.util.Event.stopEvent(ev);
1305
                        });
1306
                    } else {
1307
                        //Stop the mousedown event so we can trap the selection in the editor!
1308
                        tmp.on('mousedown', function(ev) {
1309
                            //YAHOO.util.Event.stopEvent(ev);
1310
                        });
1311
                        tmp.on('click', function(ev) {
1312
                            //YAHOO.util.Event.stopEvent(ev);
1313
                        });
1314
                        tmp.on('change', function(ev) {
1315
                            if (!ev.target) {
1316
                                if (!oButton.menucmd) {
1317
                                    oButton.menucmd = oButton.value;
1318
                                }
1319
                                oButton.value = ev.value;
1320
                                this._buttonClick(ev, oButton);
1321
                            }
1322
                        }, this, true);
1323
 
1324
                        var self = this;
1325
                        //Hijack the mousedown event in the menu and make it fire a button click..
1326
                        tmp.on('appendTo', function() {
1327
                            var tmp = this;
1328
                            if (tmp.getMenu() && tmp.getMenu().mouseDownEvent) {
1329
                                tmp.getMenu().mouseDownEvent.subscribe(function(ev, args) {
1330
                                    var oMenu = args[1];
1331
                                    YAHOO.util.Event.stopEvent(args[0]);
1332
                                    tmp._onMenuClick(args[0], tmp);
1333
                                    if (!oButton.menucmd) {
1334
                                        oButton.menucmd = oButton.value;
1335
                                    }
1336
                                    oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
1337
                                    self._buttonClick.call(self, args[1], oButton);
1338
                                    tmp._hideMenu();
1339
                                    return false;
1340
                                });
1341
                                tmp.getMenu().clickEvent.subscribe(function(ev, args) {
1342
                                    YAHOO.util.Event.stopEvent(args[0]);
1343
                                });
1344
                                tmp.getMenu().mouseUpEvent.subscribe(function(ev, args) {
1345
                                    YAHOO.util.Event.stopEvent(args[0]);
1346
                                });
1347
                            }
1348
                        });
1349
 
1350
                    }
1351
                } else {
1352
                    //Stop the mousedown event so we can trap the selection in the editor!
1353
                    tmp.on('mousedown', function(ev) {
1354
                        YAHOO.util.Event.stopEvent(ev);
1355
                    });
1356
                    tmp.on('click', function(ev) {
1357
                        YAHOO.util.Event.stopEvent(ev);
1358
                    });
1359
                }
1360
                if (this.browser.ie) {
1361
                    /*
1362
                    //Add a couple of new events for IE
1363
                    tmp.DOM_EVENTS.focusin = true;
1364
                    tmp.DOM_EVENTS.focusout = true;
1365
 
1366
                    //Stop them so we don't loose focus in the Editor
1367
                    tmp.on('focusin', function(ev) {
1368
                        YAHOO.util.Event.stopEvent(ev);
1369
                    }, oButton, this);
1370
 
1371
                    tmp.on('focusout', function(ev) {
1372
                        YAHOO.util.Event.stopEvent(ev);
1373
                    }, oButton, this);
1374
                    tmp.on('click', function(ev) {
1375
                        YAHOO.util.Event.stopEvent(ev);
1376
                    }, oButton, this);
1377
                    */
1378
                }
1379
                if (this.browser.webkit) {
1380
                    //This will keep the document from gaining focus and the editor from loosing it..
1381
                    //Forcefully remove the focus calls in button!
1382
                    tmp.hasFocus = function() {
1383
                        return true;
1384
                    };
1385
                }
1386
                this._buttonList[this._buttonList.length] = tmp;
1387
                if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
1388
                    if (Lang.isArray(oButton.menu)) {
1389
                        var menu = tmp.getMenu();
1390
                        if (menu && menu.renderEvent) {
1391
                            menu.renderEvent.subscribe(this._addMenuClasses, tmp);
1392
                            if (oButton.renderer) {
1393
                                menu.renderEvent.subscribe(oButton.renderer, tmp);
1394
                            }
1395
                        }
1396
                    }
1397
                }
1398
            }
1399
            return oButton;
1400
        },
1401
        /**
1402
        * @method addSeparator
1403
        * @description Add a new button separator to the toolbar.
1404
        * @param {HTMLElement} cont Optional HTML element to insert this button into.
1405
        * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1406
        */
1407
        addSeparator: function(cont, after) {
1408
            if (!this.get('element')) {
1409
                this._queue[this._queue.length] = ['addSeparator', arguments];
1410
                return false;
1411
            }
1412
            var sepCont = ((cont) ? cont : this.get('cont'));
1413
            if (!this.get('element')) {
1414
                this._queue[this._queue.length] = ['addSeparator', arguments];
1415
                return false;
1416
            }
1417
            if (this._sepCount === null) {
1418
                this._sepCount = 0;
1419
            }
1420
            if (!this._sep) {
1421
                this._sep = document.createElement('SPAN');
1422
                Dom.addClass(this._sep, this.CLASS_SEPARATOR);
1423
                this._sep.innerHTML = '|';
1424
            }
1425
            var _sep = this._sep.cloneNode(true);
1426
            this._sepCount++;
1427
            Dom.addClass(_sep, this.CLASS_SEPARATOR + '-' + this._sepCount);
1428
            if (after) {
1429
                var nextSib = null;
1430
                if (after.get) {
1431
                    nextSib = after.get('element').nextSibling;
1432
                } else if (after.nextSibling) {
1433
                    nextSib = after.nextSibling;
1434
                } else {
1435
                    nextSib = after;
1436
                }
1437
                if (nextSib) {
1438
                    if (nextSib == after) {
1439
                        nextSib.parentNode.appendChild(_sep);
1440
                    } else {
1441
                        nextSib.parentNode.insertBefore(_sep, nextSib);
1442
                    }
1443
                }
1444
            } else {
1445
                sepCont.appendChild(_sep);
1446
            }
1447
            return _sep;
1448
        },
1449
        /**
1450
        * @method _createColorPicker
1451
        * @private
1452
        * @description Creates the core DOM reference to the color picker menu item.
1453
        * @param {String} id the id of the toolbar to prefix this DOM container with.
1454
        */
1455
        _createColorPicker: function(id) {
1456
            if (Dom.get(id + '_colors')) {
1457
               Dom.get(id + '_colors').parentNode.removeChild(Dom.get(id + '_colors'));
1458
            }
1459
            var picker = document.createElement('div');
1460
            picker.className = 'yui-toolbar-colors';
1461
            picker.id = id + '_colors';
1462
            picker.style.display = 'none';
1463
            Event.on(window, 'load', function() {
1464
                document.body.appendChild(picker);
1465
            }, this, true);
1466
 
1467
            this._colorPicker = picker;
1468
 
1469
            var html = '';
1470
            for (var i in this._colorData) {
1471
                if (Lang.hasOwnProperty(this._colorData, i)) {
1472
                    html += '<a style="background-color: ' + i + '" href="#">' + i.replace('#', '') + '</a>';
1473
                }
1474
            }
1475
            html += '<span><em>X</em><strong></strong></span>';
1476
            window.setTimeout(function() {
1477
                picker.innerHTML = html;
1478
            }, 0);
1479
 
1480
            Event.on(picker, 'mouseover', function(ev) {
1481
                var picker = this._colorPicker;
1482
                var em = picker.getElementsByTagName('em')[0];
1483
                var strong = picker.getElementsByTagName('strong')[0];
1484
                var tar = Event.getTarget(ev);
1485
                if (tar.tagName.toLowerCase() == 'a') {
1486
                    em.style.backgroundColor = tar.style.backgroundColor;
1487
                    strong.innerHTML = this._colorData['#' + tar.innerHTML] + '<br>' + tar.innerHTML;
1488
                }
1489
            }, this, true);
1490
            Event.on(picker, 'focus', function(ev) {
1491
                Event.stopEvent(ev);
1492
            });
1493
            Event.on(picker, 'click', function(ev) {
1494
                Event.stopEvent(ev);
1495
            });
1496
            Event.on(picker, 'mousedown', function(ev) {
1497
                Event.stopEvent(ev);
1498
                var tar = Event.getTarget(ev);
1499
                if (tar.tagName.toLowerCase() == 'a') {
1500
                    var retVal = this.fireEvent('colorPickerClicked', { type: 'colorPickerClicked', target: this, button: this._colorPicker._button, color: tar.innerHTML, colorName: this._colorData['#' + tar.innerHTML] } );
1501
                    if (retVal !== false) {
1502
                        var info = {
1503
                            color: tar.innerHTML,
1504
                            colorName: this._colorData['#' + tar.innerHTML],
1505
                            value: this._colorPicker._button
1506
                        };
1507
 
1508
                        this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
1509
                    }
1510
                    this.getButtonByValue(this._colorPicker._button).getMenu().hide();
1511
                }
1512
            }, this, true);
1513
        },
1514
        /**
1515
        * @method _resetColorPicker
1516
        * @private
1517
        * @description Clears the currently selected color or mouseover color in the color picker.
1518
        */
1519
        _resetColorPicker: function() {
1520
            var em = this._colorPicker.getElementsByTagName('em')[0];
1521
            var strong = this._colorPicker.getElementsByTagName('strong')[0];
1522
            em.style.backgroundColor = 'transparent';
1523
            strong.innerHTML = '';
1524
        },
1525
        /**
1526
        * @method _makeColorButton
1527
        * @private
1528
        * @description Called to turn a "color" button into a menu button with an Overlay for the menu.
1529
        * @param {Object} _oButton <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
1530
        */
1531
        _makeColorButton: function(_oButton) {
1532
            if (!this._colorPicker) {
1533
                this._createColorPicker(this.get('id'));
1534
            }
1535
            _oButton.type = 'color';
1536
            _oButton.menu = new YAHOO.widget.Overlay(this.get('id') + '_' + _oButton.value + '_menu', { visible: false, position: 'absolute', iframe: true });
1537
            _oButton.menu.setBody('');
1538
            _oButton.menu.render(this.get('cont'));
1539
            Dom.addClass(_oButton.menu.element, 'yui-button-menu');
1540
            Dom.addClass(_oButton.menu.element, 'yui-color-button-menu');
1541
            _oButton.menu.beforeShowEvent.subscribe(function() {
1542
                _oButton.menu.cfg.setProperty('zindex', 5); //Re Adjust the overlays zIndex.. not sure why.
1543
                _oButton.menu.cfg.setProperty('context', [this.getButtonById(_oButton.id).get('element'), 'tl', 'bl']); //Re Adjust the overlay.. not sure why.
1544
                //Move the DOM reference of the color picker to the Overlay that we are about to show.
1545
                this._resetColorPicker();
1546
                var _p = this._colorPicker;
1547
                if (_p.parentNode) {
1548
                    _p.parentNode.removeChild(_p);
1549
                }
1550
                _oButton.menu.setBody('');
1551
                _oButton.menu.appendToBody(_p);
1552
                this._colorPicker.style.display = 'block';
1553
            }, this, true);
1554
            return _oButton;
1555
        },
1556
        /**
1557
        * @private
1558
        * @method _makeSpinButton
1559
        * @description Create a button similar to an OS Spin button.. It has an up/down arrow combo to scroll through a range of int values.
1560
        * @param {Object} _button <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
1561
        * @param {Object} oButton Object literal containing the buttons initial config
1562
        */
1563
        _makeSpinButton: function(_button, oButton) {
1564
            _button.addClass(this.CLASS_PREFIX + '-spinbutton');
1565
            var self = this,
1566
                _par = _button._button.parentNode.parentNode, //parentNode of Button Element for appending child
1567
                range = oButton.range,
1568
                _b1 = document.createElement('a'),
1569
                _b2 = document.createElement('a');
1570
                _b1.href = '#';
1571
                _b2.href = '#';
1572
                _b1.tabIndex = '-1';
1573
                _b2.tabIndex = '-1';
1574
 
1575
            //Setup the up and down arrows
1576
            _b1.className = 'up';
1577
            _b1.title = this.STR_SPIN_UP;
1578
            _b1.innerHTML = this.STR_SPIN_UP;
1579
            _b2.className = 'down';
1580
            _b2.title = this.STR_SPIN_DOWN;
1581
            _b2.innerHTML = this.STR_SPIN_DOWN;
1582
 
1583
            //Append them to the container
1584
            _par.appendChild(_b1);
1585
            _par.appendChild(_b2);
1586
 
1587
            var label = YAHOO.lang.substitute(this.STR_SPIN_LABEL, { VALUE: _button.get('label') });
1588
            _button.set('title', label);
1589
 
1590
            var cleanVal = function(value) {
1591
                value = ((value < range[0]) ? range[0] : value);
1592
                value = ((value > range[1]) ? range[1] : value);
1593
                return value;
1594
            };
1595
 
1596
            var br = this.browser;
1597
            var tbar = false;
1598
            var strLabel = this.STR_SPIN_LABEL;
1599
            if (this._titlebar && this._titlebar.firstChild) {
1600
                tbar = this._titlebar.firstChild;
1601
            }
1602
 
1603
            var _intUp = function(ev) {
1604
                YAHOO.util.Event.stopEvent(ev);
1605
                if (!_button.get('disabled') && (ev.keyCode != 9)) {
1606
                    var value = parseInt(_button.get('label'), 10);
1607
                    value++;
1608
                    value = cleanVal(value);
1609
                    _button.set('label', ''+value);
1610
                    var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1611
                    _button.set('title', label);
1612
                    if (!br.webkit && tbar) {
1613
                        //tbar.focus(); //We do this for accessibility, on the re-focus of the element, a screen reader will re-read the title that was just changed
1614
                        //_button.focus();
1615
                    }
1616
                    self._buttonClick(ev, oButton);
1617
                }
1618
            };
1619
 
1620
            var _intDown = function(ev) {
1621
                YAHOO.util.Event.stopEvent(ev);
1622
                if (!_button.get('disabled') && (ev.keyCode != 9)) {
1623
                    var value = parseInt(_button.get('label'), 10);
1624
                    value--;
1625
                    value = cleanVal(value);
1626
 
1627
                    _button.set('label', ''+value);
1628
                    var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1629
                    _button.set('title', label);
1630
                    if (!br.webkit && tbar) {
1631
                        //tbar.focus(); //We do this for accessibility, on the re-focus of the element, a screen reader will re-read the title that was just changed
1632
                        //_button.focus();
1633
                    }
1634
                    self._buttonClick(ev, oButton);
1635
                }
1636
            };
1637
 
1638
            var _intKeyUp = function(ev) {
1639
                if (ev.keyCode == 38) {
1640
                    _intUp(ev);
1641
                } else if (ev.keyCode == 40) {
1642
                    _intDown(ev);
1643
                } else if (ev.keyCode == 107 && ev.shiftKey) {  //Plus Key
1644
                    _intUp(ev);
1645
                } else if (ev.keyCode == 109 && ev.shiftKey) {  //Minus Key
1646
                    _intDown(ev);
1647
                }
1648
            };
1649
 
1650
            //Handle arrow keys..
1651
            _button.on('keydown', _intKeyUp, this, true);
1652
 
1653
            //Listen for the click on the up button and act on it
1654
            //Listen for the click on the down button and act on it
1655
            Event.on(_b1, 'mousedown',function(ev) {
1656
                Event.stopEvent(ev);
1657
            }, this, true);
1658
            Event.on(_b2, 'mousedown', function(ev) {
1659
                Event.stopEvent(ev);
1660
            }, this, true);
1661
            Event.on(_b1, 'click', _intUp, this, true);
1662
            Event.on(_b2, 'click', _intDown, this, true);
1663
        },
1664
        /**
1665
        * @protected
1666
        * @method _buttonClick
1667
        * @description Click handler for all buttons in the toolbar.
1668
        * @param {String} ev The event that was passed in.
1669
        * @param {Object} info Object literal of information about the button that was clicked.
1670
        */
1671
        _buttonClick: function(ev, info) {
1672
            var doEvent = true;
1673
 
1674
            if (ev && ev.type == 'keypress') {
1675
                if (ev.keyCode == 9) {
1676
                    doEvent = false;
1677
                } else if ((ev.keyCode === 13) || (ev.keyCode === 0) || (ev.keyCode === 32)) {
1678
                } else {
1679
                    doEvent = false;
1680
                }
1681
            }
1682
 
1683
            if (doEvent) {
1684
                var fireNextEvent = true,
1685
                    retValue = false;
1686
 
1687
                info.isSelected = this.isSelected(info.id);
1688
 
1689
                if (info.value) {
1690
                    retValue = this.fireEvent(info.value + 'Click', { type: info.value + 'Click', target: this.get('element'), button: info });
1691
                    if (retValue === false) {
1692
                        fireNextEvent = false;
1693
                    }
1694
                }
1695
 
1696
                if (info.menucmd && fireNextEvent) {
1697
                    retValue = this.fireEvent(info.menucmd + 'Click', { type: info.menucmd + 'Click', target: this.get('element'), button: info });
1698
                    if (retValue === false) {
1699
                        fireNextEvent = false;
1700
                    }
1701
                }
1702
                if (fireNextEvent) {
1703
                    this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
1704
                }
1705
 
1706
                if (info.type == 'select') {
1707
                    var button = this.getButtonById(info.id);
1708
                    if (button.buttonType == 'rich') {
1709
                        var txt = info.value;
1710
                        for (var i = 0; i < info.menu.length; i++) {
1711
                            if (info.menu[i].value == info.value) {
1712
                                txt = info.menu[i].text;
1713
                                break;
1714
                            }
1715
                        }
1716
                        button.set('label', '<span class="yui-toolbar-' + info.menucmd + '-' + (info.value).replace(/ /g, '-').toLowerCase() + '">' + txt + '</span>');
1717
                        var _items = button.getMenu().getItems();
1718
                        for (var m = 0; m < _items.length; m++) {
1719
                            if (_items[m].value.toLowerCase() == info.value.toLowerCase()) {
1720
                                _items[m].cfg.setProperty('checked', true);
1721
                            } else {
1722
                                _items[m].cfg.setProperty('checked', false);
1723
                            }
1724
                        }
1725
                    }
1726
                }
1727
                if (ev) {
1728
                    Event.stopEvent(ev);
1729
                }
1730
            }
1731
        },
1732
        /**
1733
        * @private
1734
        * @property _keyNav
1735
        * @description Flag to determine if the arrow nav listeners have been attached
1736
        * @type Boolean
1737
        */
1738
        _keyNav: null,
1739
        /**
1740
        * @private
1741
        * @property _navCounter
1742
        * @description Internal counter for walking the buttons in the toolbar with the arrow keys
1743
        * @type Number
1744
        */
1745
        _navCounter: null,
1746
        /**
1747
        * @private
1748
        * @method _navigateButtons
1749
        * @description Handles the navigation/focus of toolbar buttons with the Arrow Keys
1750
        * @param {Event} ev The Key Event
1751
        */
1752
        _navigateButtons: function(ev) {
1753
            switch (ev.keyCode) {
1754
                case 37:
1755
                case 39:
1756
                    if (ev.keyCode == 37) {
1757
                        this._navCounter--;
1758
                    } else {
1759
                        this._navCounter++;
1760
                    }
1761
                    if (this._navCounter > (this._buttonList.length - 1)) {
1762
                        this._navCounter = 0;
1763
                    }
1764
                    if (this._navCounter < 0) {
1765
                        this._navCounter = (this._buttonList.length - 1);
1766
                    }
1767
                    if (this._buttonList[this._navCounter]) {
1768
                        var el = this._buttonList[this._navCounter].get('element');
1769
                        if (this.browser.ie) {
1770
                            el = this._buttonList[this._navCounter].get('element').getElementsByTagName('a')[0];
1771
                        }
1772
                        if (this._buttonList[this._navCounter].get('disabled')) {
1773
                            this._navigateButtons(ev);
1774
                        } else {
1775
                            el.focus();
1776
                        }
1777
                    }
1778
                    break;
1779
            }
1780
        },
1781
        /**
1782
        * @private
1783
        * @method _handleFocus
1784
        * @description Sets up the listeners for the arrow key navigation
1785
        */
1786
        _handleFocus: function() {
1787
            if (!this._keyNav) {
1788
                var ev = 'keypress';
1789
                if (this.browser.ie) {
1790
                    ev = 'keydown';
1791
                }
1792
                Event.on(this.get('element'), ev, this._navigateButtons, this, true);
1793
                this._keyNav = true;
1794
                this._navCounter = -1;
1795
            }
1796
        },
1797
        /**
1798
        * @method getButtonById
1799
        * @description Gets a button instance from the toolbar by is Dom id.
1800
        * @param {String} id The Dom id to query for.
1801
        * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
1802
        */
1803
        getButtonById: function(id) {
1804
            var len = this._buttonList.length;
1805
            for (var i = 0; i < len; i++) {
1806
                if (this._buttonList[i] && this._buttonList[i].get('id') == id) {
1807
                    return this._buttonList[i];
1808
                }
1809
            }
1810
            return false;
1811
        },
1812
        /**
1813
        * @method getButtonByValue
1814
        * @description Gets a button instance or a menuitem instance from the toolbar by it's value.
1815
        * @param {String} value The button value to query for.
1816
        * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> or <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a>}
1817
        */
1818
        getButtonByValue: function(value) {
1819
            var _buttons = this.get('buttons');
1820
            if (!_buttons) {
1821
                return false;
1822
            }
1823
            var len = _buttons.length;
1824
            for (var i = 0; i < len; i++) {
1825
                if (_buttons[i].group !== undefined) {
1826
                    for (var m = 0; m < _buttons[i].buttons.length; m++) {
1827
                        if ((_buttons[i].buttons[m].value == value) || (_buttons[i].buttons[m].menucmd == value)) {
1828
                            return this.getButtonById(_buttons[i].buttons[m].id);
1829
                        }
1830
                        if (_buttons[i].buttons[m].menu) { //Menu Button, loop through the values
1831
                            for (var s = 0; s < _buttons[i].buttons[m].menu.length; s++) {
1832
                                if (_buttons[i].buttons[m].menu[s].value == value) {
1833
                                    return this.getButtonById(_buttons[i].buttons[m].id);
1834
                                }
1835
                            }
1836
                        }
1837
                    }
1838
                } else {
1839
                    if ((_buttons[i].value == value) || (_buttons[i].menucmd == value)) {
1840
                        return this.getButtonById(_buttons[i].id);
1841
                    }
1842
                    if (_buttons[i].menu) { //Menu Button, loop through the values
1843
                        for (var j = 0; j < _buttons[i].menu.length; j++) {
1844
                            if (_buttons[i].menu[j].value == value) {
1845
                                return this.getButtonById(_buttons[i].id);
1846
                            }
1847
                        }
1848
                    }
1849
                }
1850
            }
1851
            return false;
1852
        },
1853
        /**
1854
        * @method getButtonByIndex
1855
        * @description Gets a button instance from the toolbar by is index in _buttonList.
1856
        * @param {Number} index The index of the button in _buttonList.
1857
        * @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
1858
        */
1859
        getButtonByIndex: function(index) {
1860
            if (this._buttonList[index]) {
1861
                return this._buttonList[index];
1862
            } else {
1863
                return false;
1864
            }
1865
        },
1866
        /**
1867
        * @method getButtons
1868
        * @description Returns an array of buttons in the current toolbar
1869
        * @return {Array}
1870
        */
1871
        getButtons: function() {
1872
            return this._buttonList;
1873
        },
1874
        /**
1875
        * @method disableButton
1876
        * @description Disables a button in the toolbar.
1877
        * @param {String/Number} id Disable a button by it's id, index or value.
1878
        * @return {Boolean}
1879
        */
1880
        disableButton: function(id) {
1881
            var button = getButton.call(this, id);
1882
            if (button) {
1883
                button.set('disabled', true);
1884
            } else {
1885
                return false;
1886
            }
1887
        },
1888
        /**
1889
        * @method enableButton
1890
        * @description Enables a button in the toolbar.
1891
        * @param {String/Number} id Enable a button by it's id, index or value.
1892
        * @return {Boolean}
1893
        */
1894
        enableButton: function(id) {
1895
            if (this.get('disabled')) {
1896
                return false;
1897
            }
1898
            var button = getButton.call(this, id);
1899
            if (button) {
1900
                if (button.get('disabled')) {
1901
                    button.set('disabled', false);
1902
                }
1903
            } else {
1904
                return false;
1905
            }
1906
        },
1907
        /**
1908
        * @method isSelected
1909
        * @description Tells if a button is selected or not.
1910
        * @param {String/Number} id A button by it's id, index or value.
1911
        * @return {Boolean}
1912
        */
1913
        isSelected: function(id) {
1914
            var button = getButton.call(this, id);
1915
            if (button) {
1916
                return button._selected;
1917
            }
1918
            return false;
1919
        },
1920
        /**
1921
        * @method selectButton
1922
        * @description Selects a button in the toolbar.
1923
        * @param {String/Number} id Select a button by it's id, index or value.
1924
        * @param {String} value If this is a Menu Button, check this item in the menu
1925
        * @return {Boolean}
1926
        */
1927
        selectButton: function(id, value) {
1928
            var button = getButton.call(this, id);
1929
            if (button) {
1930
                button.addClass('yui-button-selected');
1931
                button.addClass('yui-button-' + button.get('value') + '-selected');
1932
                button._selected = true;
1933
                if (value) {
1934
                    if (button.buttonType == 'rich') {
1935
                        var _items = button.getMenu().getItems();
1936
                        for (var m = 0; m < _items.length; m++) {
1937
                            if (_items[m].value == value) {
1938
                                _items[m].cfg.setProperty('checked', true);
1939
                                button.set('label', '<span class="yui-toolbar-' + button.get('value') + '-' + (value).replace(/ /g, '-').toLowerCase() + '">' + _items[m]._oText.nodeValue + '</span>');
1940
                            } else {
1941
                                _items[m].cfg.setProperty('checked', false);
1942
                            }
1943
                        }
1944
                    }
1945
                }
1946
            } else {
1947
                return false;
1948
            }
1949
        },
1950
        /**
1951
        * @method deselectButton
1952
        * @description Deselects a button in the toolbar.
1953
        * @param {String/Number} id Deselect a button by it's id, index or value.
1954
        * @return {Boolean}
1955
        */
1956
        deselectButton: function(id) {
1957
            var button = getButton.call(this, id);
1958
            if (button) {
1959
                button.removeClass('yui-button-selected');
1960
                button.removeClass('yui-button-' + button.get('value') + '-selected');
1961
                button.removeClass('yui-button-hover');
1962
                button._selected = false;
1963
            } else {
1964
                return false;
1965
            }
1966
        },
1967
        /**
1968
        * @method deselectAllButtons
1969
        * @description Deselects all buttons in the toolbar.
1970
        * @return {Boolean}
1971
        */
1972
        deselectAllButtons: function() {
1973
            var len = this._buttonList.length;
1974
            for (var i = 0; i < len; i++) {
1975
                this.deselectButton(this._buttonList[i]);
1976
            }
1977
        },
1978
        /**
1979
        * @method disableAllButtons
1980
        * @description Disables all buttons in the toolbar.
1981
        * @return {Boolean}
1982
        */
1983
        disableAllButtons: function() {
1984
            if (this.get('disabled')) {
1985
                return false;
1986
            }
1987
            var len = this._buttonList.length;
1988
            for (var i = 0; i < len; i++) {
1989
                this.disableButton(this._buttonList[i]);
1990
            }
1991
        },
1992
        /**
1993
        * @method enableAllButtons
1994
        * @description Enables all buttons in the toolbar.
1995
        * @return {Boolean}
1996
        */
1997
        enableAllButtons: function() {
1998
            if (this.get('disabled')) {
1999
                return false;
2000
            }
2001
            var len = this._buttonList.length;
2002
            for (var i = 0; i < len; i++) {
2003
                this.enableButton(this._buttonList[i]);
2004
            }
2005
        },
2006
        /**
2007
        * @method resetAllButtons
2008
        * @description Resets all buttons to their initial state.
2009
        * @param {Object} _ex Except these buttons
2010
        * @return {Boolean}
2011
        */
2012
        resetAllButtons: function(_ex) {
2013
            if (!Lang.isObject(_ex)) {
2014
                _ex = {};
2015
            }
2016
            if (this.get('disabled') || !this._buttonList) {
2017
                return false;
2018
            }
2019
            var len = this._buttonList.length;
2020
            for (var i = 0; i < len; i++) {
2021
                var _button = this._buttonList[i];
2022
                if (_button) {
2023
                    var disabled = _button._configs.disabled._initialConfig.value;
2024
                    if (_ex[_button.get('id')]) {
2025
                        this.enableButton(_button);
2026
                        this.selectButton(_button);
2027
                    } else {
2028
                        if (disabled) {
2029
                            this.disableButton(_button);
2030
                        } else {
2031
                            this.enableButton(_button);
2032
                        }
2033
                        this.deselectButton(_button);
2034
                    }
2035
                }
2036
            }
2037
        },
2038
        /**
2039
        * @method destroyButton
2040
        * @description Destroy a button in the toolbar.
2041
        * @param {String/Number} id Destroy a button by it's id or index.
2042
        * @return {Boolean}
2043
        */
2044
        destroyButton: function(id) {
2045
            var button = getButton.call(this, id);
2046
            if (button) {
2047
                var thisID = button.get('id'),
2048
                    new_list = [], i = 0,
2049
                    len = this._buttonList.length;
2050
 
2051
                button.destroy();
2052
 
2053
                for (i = 0; i < len; i++) {
2054
                    if (this._buttonList[i].get('id') != thisID) {
2055
                        new_list[new_list.length]= this._buttonList[i];
2056
                    }
2057
                }
2058
 
2059
                this._buttonList = new_list;
2060
            } else {
2061
                return false;
2062
            }
2063
        },
2064
        /**
2065
        * @method destroy
2066
        * @description Destroys the toolbar, all of it's elements and objects.
2067
        * @return {Boolean}
2068
        */
2069
        destroy: function() {
2070
            var len = this._configuredButtons.length, j, i, b;
2071
            for(b = 0; b < len; b++) {
2072
                this.destroyButton(this._configuredButtons[b]);
2073
            }
2074
 
2075
            this._configuredButtons = null;
2076
 
2077
            this.get('element').innerHTML = '';
2078
            this.get('element').className = '';
2079
            //Brutal Object Destroy
2080
            for (i in this) {
2081
                if (Lang.hasOwnProperty(this, i)) {
2082
                    this[i] = null;
2083
                }
2084
            }
2085
            return true;
2086
        },
2087
        /**
2088
        * @method collapse
2089
        * @description Programatically collapse the toolbar.
2090
        * @param {Boolean} collapse True to collapse, false to expand.
2091
        */
2092
        collapse: function(collapse) {
2093
            var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
2094
            if (collapse === false) {
2095
                Dom.removeClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
2096
                if (el[0]) {
2097
                    Dom.removeClass(el[0], 'collapsed');
2098
                    el[0].title = this.STR_COLLAPSE;
2099
                }
2100
                this.fireEvent('toolbarExpanded', { type: 'toolbarExpanded', target: this });
2101
            } else {
2102
                if (el[0]) {
2103
                    Dom.addClass(el[0], 'collapsed');
2104
                    el[0].title = this.STR_EXPAND;
2105
                }
2106
                Dom.addClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
2107
                this.fireEvent('toolbarCollapsed', { type: 'toolbarCollapsed', target: this });
2108
            }
2109
        },
2110
        /**
2111
        * @method toString
2112
        * @description Returns a string representing the toolbar.
2113
        * @return {String}
2114
        */
2115
        toString: function() {
2116
            return 'Toolbar (#' + this.get('element').id + ') with ' + this._buttonList.length + ' buttons.';
2117
        }
2118
    });
2119
/**
2120
* @event buttonClick
2121
* @param {Object} o The object passed to this handler is the button config used to create the button.
2122
* @description Fires when any botton receives a click event. Passes back a single object representing the buttons config object. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2123
* @type YAHOO.util.CustomEvent
2124
*/
2125
/**
2126
* @event valueClick
2127
* @param {Object} o The object passed to this handler is the button config used to create the button.
2128
* @description This is a special dynamic event that is created and dispatched based on the value property
2129
* of the button config. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2130
* Example:
2131
* <code><pre>
2132
* buttons : [
2133
*   { type: 'button', value: 'test', value: 'testButton' }
2134
* ]</pre>
2135
* </code>
2136
* With the valueClick event you could subscribe to this buttons click event with this:
2137
* tbar.in('testButtonClick', function() { alert('test button clicked'); })
2138
* @type YAHOO.util.CustomEvent
2139
*/
2140
/**
2141
* @event toolbarExpanded
2142
* @description Fires when the toolbar is expanded via the collapse button. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2143
* @type YAHOO.util.CustomEvent
2144
*/
2145
/**
2146
* @event toolbarCollapsed
2147
* @description Fires when the toolbar is collapsed via the collapse button. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2148
* @type YAHOO.util.CustomEvent
2149
*/
2150
})();
2151
/**
2152
 * @module editor
2153
 * @description <p>The Rich Text Editor is a UI control that replaces a standard HTML textarea; it allows for the rich formatting of text content, including common structural treatments like lists, formatting treatments like bold and italic text, and drag-and-drop inclusion and sizing of images. The Rich Text Editor's toolbar is extensible via a plugin architecture so that advanced implementations can achieve a high degree of customization.</p>
2154
 * @namespace YAHOO.widget
2155
 * @requires yahoo, dom, element, event, toolbar
2156
 * @optional animation, container_core, resize, dragdrop
2157
 */
2158
 
2159
(function() {
2160
var Dom = YAHOO.util.Dom,
2161
    Event = YAHOO.util.Event,
2162
    Lang = YAHOO.lang,
2163
    Toolbar = YAHOO.widget.Toolbar;
2164
 
2165
    /**
2166
     * The Rich Text Editor is a UI control that replaces a standard HTML textarea; it allows for the rich formatting of text content, including common structural treatments like lists, formatting treatments like bold and italic text, and drag-and-drop inclusion and sizing of images. The Rich Text Editor's toolbar is extensible via a plugin architecture so that advanced implementations can achieve a high degree of customization.
2167
     * @constructor
2168
     * @class SimpleEditor
2169
     * @extends YAHOO.util.Element
2170
     * @param {String/HTMLElement} el The textarea element to turn into an editor.
2171
     * @param {Object} attrs Object liternal containing configuration parameters.
2172
    */
2173
 
2174
    YAHOO.widget.SimpleEditor = function(el, attrs) {
2175
 
2176
        var o = {};
2177
        if (Lang.isObject(el) && (!el.tagName) && !attrs) {
2178
            Lang.augmentObject(o, el); //Break the config reference
2179
            el = document.createElement('textarea');
2180
            this.DOMReady = true;
2181
            if (o.container) {
2182
                var c = Dom.get(o.container);
2183
                c.appendChild(el);
2184
            } else {
2185
                document.body.appendChild(el);
2186
            }
2187
        } else {
2188
            if (attrs) {
2189
                Lang.augmentObject(o, attrs); //Break the config reference
2190
            }
2191
        }
2192
 
2193
        var oConfig = {
2194
            element: null,
2195
            attributes: o
2196
        }, id = null;
2197
 
2198
        if (Lang.isString(el)) {
2199
            id = el;
2200
        } else {
2201
            if (oConfig.attributes.id) {
2202
                id = oConfig.attributes.id;
2203
            } else {
2204
                this.DOMReady = true;
2205
                id = Dom.generateId(el);
2206
            }
2207
        }
2208
        oConfig.element = el;
2209
 
2210
        var element_cont = document.createElement('DIV');
2211
        oConfig.attributes.element_cont = new YAHOO.util.Element(element_cont, {
2212
            id: id + '_container'
2213
        });
2214
        var div = document.createElement('div');
2215
        Dom.addClass(div, 'first-child');
2216
        oConfig.attributes.element_cont.appendChild(div);
2217
 
2218
        if (!oConfig.attributes.toolbar_cont) {
2219
            oConfig.attributes.toolbar_cont = document.createElement('DIV');
2220
            oConfig.attributes.toolbar_cont.id = id + '_toolbar';
2221
            div.appendChild(oConfig.attributes.toolbar_cont);
2222
        }
2223
        var editorWrapper = document.createElement('DIV');
2224
        div.appendChild(editorWrapper);
2225
        oConfig.attributes.editor_wrapper = editorWrapper;
2226
 
2227
        YAHOO.widget.SimpleEditor.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
2228
    };
2229
 
2230
 
2231
    YAHOO.extend(YAHOO.widget.SimpleEditor, YAHOO.util.Element, {
2232
        /**
2233
        * @private
2234
        * @property _resizeConfig
2235
        * @description The default config for the Resize Utility
2236
        */
2237
        _resizeConfig: {
2238
            handles: ['br'],
2239
            autoRatio: true,
2240
            status: true,
2241
            proxy: true,
2242
            useShim: true,
2243
            setSize: false
2244
        },
2245
        /**
2246
        * @private
2247
        * @method _setupResize
2248
        * @description Creates the Resize instance and binds its events.
2249
        */
2250
        _setupResize: function() {
2251
            if (!YAHOO.util.DD || !YAHOO.util.Resize) { return false; }
2252
            if (this.get('resize')) {
2253
                var config = {};
2254
                Lang.augmentObject(config, this._resizeConfig); //Break the config reference
2255
                this.resize = new YAHOO.util.Resize(this.get('element_cont').get('element'), config);
2256
                this.resize.on('resize', function(args) {
2257
                    var anim = this.get('animate');
2258
                    this.set('animate', false);
2259
                    this.set('width', args.width + 'px');
2260
                    var h = args.height,
2261
                        th = (this.toolbar.get('element').clientHeight + 2),
2262
                        dh = 0;
2263
                    if (this.dompath) {
2264
                        dh = (this.dompath.clientHeight + 1); //It has a 1px top border..
2265
                    }
2266
                    var newH = (h - th - dh);
2267
                    this.set('height', newH + 'px');
2268
                    this.get('element_cont').setStyle('height', '');
2269
                    this.set('animate', anim);
2270
                }, this, true);
2271
            }
2272
        },
2273
        /**
2274
        * @property resize
2275
        * @description A reference to the Resize object
2276
        * @type YAHOO.util.Resize
2277
        */
2278
        resize: null,
2279
        /**
2280
        * @private
2281
        * @method _setupDD
2282
        * @description Sets up the DD instance used from the 'drag' config option.
2283
        */
2284
        _setupDD: function() {
2285
            if (!YAHOO.util.DD) { return false; }
2286
            if (this.get('drag')) {
2287
                var d = this.get('drag'),
2288
                    dd = YAHOO.util.DD;
2289
                if (d === 'proxy') {
2290
                    dd = YAHOO.util.DDProxy;
2291
                }
2292
 
2293
                this.dd = new dd(this.get('element_cont').get('element'));
2294
                this.toolbar.addClass('draggable');
2295
                this.dd.setHandleElId(this.toolbar._titlebar);
2296
            }
2297
        },
2298
        /**
2299
        * @property dd
2300
        * @description A reference to the DragDrop object.
2301
        * @type YAHOO.util.DD/YAHOO.util.DDProxy
2302
        */
2303
        dd: null,
2304
        /**
2305
        * @private
2306
        * @property _lastCommand
2307
        * @description A cache of the last execCommand (used for Undo/Redo so they don't mark an undo level)
2308
        * @type String
2309
        */
2310
        _lastCommand: null,
2311
        _undoNodeChange: function() {},
2312
        _storeUndo: function() {},
2313
        /**
2314
        * @private
2315
        * @method _checkKey
2316
        * @description Checks a keyMap entry against a key event
2317
        * @param {Object} k The _keyMap object
2318
        * @param {Event} e The Mouse Event
2319
        * @return {Boolean}
2320
        */
2321
        _checkKey: function(k, e) {
2322
            var ret = false;
2323
            if ((e.keyCode === k.key)) {
2324
                if (k.mods && (k.mods.length > 0)) {
2325
                    var val = 0;
2326
                    for (var i = 0; i < k.mods.length; i++) {
2327
                        if (this.browser.mac) {
2328
                            if (k.mods[i] == 'ctrl') {
2329
                                k.mods[i] = 'meta';
2330
                            }
2331
                        }
2332
                        if (e[k.mods[i] + 'Key'] === true) {
2333
                            val++;
2334
                        }
2335
                    }
2336
                    if (val === k.mods.length) {
2337
                        ret = true;
2338
                    }
2339
                } else {
2340
                    ret = true;
2341
                }
2342
            }
2343
            return ret;
2344
        },
2345
        /**
2346
        * @private
2347
        * @property _keyMap
2348
        * @description Named key maps for various actions in the Editor. Example: <code>CLOSE_WINDOW: { key: 87, mods: ['shift', 'ctrl'] }</code>.
2349
        * This entry shows that when key 87 (W) is found with the modifiers of shift and control, the window will close. You can customize this object to tweak keyboard shortcuts.
2350
        * @type {Object/Mixed}
2351
        */
2352
        _keyMap: {
2353
            SELECT_ALL: {
2354
                key: 65, //A key
2355
                mods: ['ctrl']
2356
            },
2357
            CLOSE_WINDOW: {
2358
                key: 87, //W key
2359
                mods: ['shift', 'ctrl']
2360
            },
2361
            FOCUS_TOOLBAR: {
2362
                key: 27,
2363
                mods: ['shift']
2364
            },
2365
            FOCUS_AFTER: {
2366
                key: 27
2367
            },
2368
            FONT_SIZE_UP: {
2369
                key: 38,
2370
                mods: ['shift', 'ctrl']
2371
            },
2372
            FONT_SIZE_DOWN: {
2373
                key: 40,
2374
                mods: ['shift', 'ctrl']
2375
            },
2376
            CREATE_LINK: {
2377
                key: 76,
2378
                mods: ['shift', 'ctrl']
2379
            },
2380
            BOLD: {
2381
                key: 66,
2382
                mods: ['shift', 'ctrl']
2383
            },
2384
            ITALIC: {
2385
                key: 73,
2386
                mods: ['shift', 'ctrl']
2387
            },
2388
            UNDERLINE: {
2389
                key: 85,
2390
                mods: ['shift', 'ctrl']
2391
            },
2392
            UNDO: {
2393
                key: 90,
2394
                mods: ['ctrl']
2395
            },
2396
            REDO: {
2397
                key: 90,
2398
                mods: ['shift', 'ctrl']
2399
            },
2400
            JUSTIFY_LEFT: {
2401
                key: 219,
2402
                mods: ['shift', 'ctrl']
2403
            },
2404
            JUSTIFY_CENTER: {
2405
                key: 220,
2406
                mods: ['shift', 'ctrl']
2407
            },
2408
            JUSTIFY_RIGHT: {
2409
                key: 221,
2410
                mods: ['shift', 'ctrl']
2411
            }
2412
        },
2413
        /**
2414
        * @private
2415
        * @method _cleanClassName
2416
        * @description Makes a useable classname from dynamic data, by dropping it to lowercase and replacing spaces with -'s.
2417
        * @param {String} str The classname to clean up
2418
        * @return {String}
2419
        */
2420
        _cleanClassName: function(str) {
2421
            return str.replace(/ /g, '-').toLowerCase();
2422
        },
2423
        /**
2424
        * @property _textarea
2425
        * @description Flag to determine if we are using a textarea or an HTML Node.
2426
        * @type Boolean
2427
        */
2428
        _textarea: null,
2429
        /**
2430
        * @property _docType
2431
        * @description The DOCTYPE to use in the editable container.
2432
        * @type String
2433
        */
2434
        _docType: '<!DOCTYPE HTML PUBLIC "-/'+'/W3C/'+'/DTD HTML 4.01/'+'/EN" "http:/'+'/www.w3.org/TR/html4/strict.dtd">',
2435
        /**
2436
        * @property editorDirty
2437
        * @description This flag will be set when certain things in the Editor happen. It is to be used by the developer to check to see if content has changed.
2438
        * @type Boolean
2439
        */
2440
        editorDirty: null,
2441
        /**
2442
        * @property _defaultCSS
2443
        * @description The default CSS used in the config for 'css'. This way you can add to the config like this: { css: YAHOO.widget.SimpleEditor.prototype._defaultCSS + 'ADD MYY CSS HERE' }
2444
        * @type String
2445
        */
2446
        _defaultCSS: 'html { height: 95%; } body { padding: 7px; background-color: #fff; font: 13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } a, a:visited, a:hover { color: blue !important; text-decoration: underline !important; cursor: text !important; } .warning-localfile { border-bottom: 1px dashed red !important; } .yui-busy { cursor: wait !important; } img.selected { border: 2px dotted #808080; } img { cursor: pointer !important; border: none; } body.ptags.webkit div.yui-wk-p { margin: 11px 0; } body.ptags.webkit div.yui-wk-div { margin: 0; }',
2447
        /**
2448
        * @property _defaultToolbar
2449
        * @private
2450
        * @description Default toolbar config.
2451
        * @type Object
2452
        */
2453
        _defaultToolbar: null,
2454
        /**
2455
        * @property _lastButton
2456
        * @private
2457
        * @description The last button pressed, so we don't disable it.
2458
        * @type Object
2459
        */
2460
        _lastButton: null,
2461
        /**
2462
        * @property _baseHREF
2463
        * @private
2464
        * @description The base location of the editable page (this page) so that relative paths for image work.
2465
        * @type String
2466
        */
2467
        _baseHREF: function() {
2468
            var href = document.location.href;
2469
            if (href.indexOf('?') !== -1) { //Remove the query string
2470
                href = href.substring(0, href.indexOf('?'));
2471
            }
2472
            href = href.substring(0, href.lastIndexOf('/')) + '/';
2473
            return href;
2474
        }(),
2475
        /**
2476
        * @property _lastImage
2477
        * @private
2478
        * @description Safari reference for the last image selected (for styling as selected).
2479
        * @type HTMLElement
2480
        */
2481
        _lastImage: null,
2482
        /**
2483
        * @property _blankImageLoaded
2484
        * @private
2485
        * @description Don't load the blank image more than once..
2486
        * @type Boolean
2487
        */
2488
        _blankImageLoaded: null,
2489
        /**
2490
        * @property _fixNodesTimer
2491
        * @private
2492
        * @description Holder for the fixNodes timer
2493
        * @type Date
2494
        */
2495
        _fixNodesTimer: null,
2496
        /**
2497
        * @property _nodeChangeTimer
2498
        * @private
2499
        * @description Holds a reference to the nodeChange setTimeout call
2500
        * @type Number
2501
        */
2502
        _nodeChangeTimer: null,
2503
        /**
2504
        * @property _nodeChangeDelayTimer
2505
        * @private
2506
        * @description Holds a reference to the nodeChangeDelay setTimeout call
2507
        * @type Number
2508
        */
2509
        _nodeChangeDelayTimer: null,
2510
        /**
2511
        * @property _lastNodeChangeEvent
2512
        * @private
2513
        * @description Flag to determine the last event that fired a node change
2514
        * @type Event
2515
        */
2516
        _lastNodeChangeEvent: null,
2517
        /**
2518
        * @property _lastNodeChange
2519
        * @private
2520
        * @description Flag to determine when the last node change was fired
2521
        * @type Date
2522
        */
2523
        _lastNodeChange: 0,
2524
        /**
2525
        * @property _rendered
2526
        * @private
2527
        * @description Flag to determine if editor has been rendered or not
2528
        * @type Boolean
2529
        */
2530
        _rendered: null,
2531
        /**
2532
        * @property DOMReady
2533
        * @private
2534
        * @description Flag to determine if DOM is ready or not
2535
        * @type Boolean
2536
        */
2537
        DOMReady: null,
2538
        /**
2539
        * @property _selection
2540
        * @private
2541
        * @description Holder for caching iframe selections
2542
        * @type Object
2543
        */
2544
        _selection: null,
2545
        /**
2546
        * @property _mask
2547
        * @private
2548
        * @description DOM Element holder for the editor Mask when disabled
2549
        * @type Object
2550
        */
2551
        _mask: null,
2552
        /**
2553
        * @property _showingHiddenElements
2554
        * @private
2555
        * @description Status of the hidden elements button
2556
        * @type Boolean
2557
        */
2558
        _showingHiddenElements: null,
2559
        /**
2560
        * @property currentWindow
2561
        * @description A reference to the currently open EditorWindow
2562
        * @type Object
2563
        */
2564
        currentWindow: null,
2565
        /**
2566
        * @property currentEvent
2567
        * @description A reference to the current editor event
2568
        * @type Event
2569
        */
2570
        currentEvent: null,
2571
        /**
2572
        * @property operaEvent
2573
        * @private
2574
        * @description setTimeout holder for Opera and Image DoubleClick event..
2575
        * @type Object
2576
        */
2577
        operaEvent: null,
2578
        /**
2579
        * @property currentFont
2580
        * @description A reference to the last font selected from the Toolbar
2581
        * @type HTMLElement
2582
        */
2583
        currentFont: null,
2584
        /**
2585
        * @property currentElement
2586
        * @description A reference to the current working element in the editor
2587
        * @type Array
2588
        */
2589
        currentElement: null,
2590
        /**
2591
        * @property dompath
2592
        * @description A reference to the dompath container for writing the current working dom path to.
2593
        * @type HTMLElement
2594
        */
2595
        dompath: null,
2596
        /**
2597
        * @property beforeElement
2598
        * @description A reference to the H2 placed before the editor for Accessibilty.
2599
        * @type HTMLElement
2600
        */
2601
        beforeElement: null,
2602
        /**
2603
        * @property afterElement
2604
        * @description A reference to the H2 placed after the editor for Accessibilty.
2605
        * @type HTMLElement
2606
        */
2607
        afterElement: null,
2608
        /**
2609
        * @property invalidHTML
2610
        * @description Contains a list of HTML elements that are invalid inside the editor. They will be removed when they are found. If you set the value of a key to "{ keepContents: true }", then the element will be replaced with a yui-non span to be filtered out when cleanHTML is called. The only tag that is ignored here is the span tag as it will force the Editor into a loop and freeze the browser. However.. all of these tags will be removed in the cleanHTML routine.
2611
        * @type Object
2612
        */
2613
        invalidHTML: {
2614
            form: true,
2615
            input: true,
2616
            button: true,
2617
            select: true,
2618
            link: true,
2619
            html: true,
2620
            body: true,
2621
            iframe: true,
2622
            script: true,
2623
            style: true,
2624
            textarea: true
2625
        },
2626
        /**
2627
        * @property toolbar
2628
        * @description Local property containing the <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a> instance
2629
        * @type <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>
2630
        */
2631
        toolbar: null,
2632
        /**
2633
        * @private
2634
        * @property _contentTimer
2635
        * @description setTimeout holder for documentReady check
2636
        */
2637
        _contentTimer: null,
2638
        /**
2639
        * @private
2640
        * @property _contentTimerMax
2641
        * @description The number of times the loaded content should be checked before giving up. Default: 500
2642
        */
2643
        _contentTimerMax: 500,
2644
        /**
2645
        * @private
2646
        * @property _contentTimerCounter
2647
        * @description Counter to check the number of times the body is polled for before giving up
2648
        * @type Number
2649
        */
2650
        _contentTimerCounter: 0,
2651
        /**
2652
        * @private
2653
        * @property _disabled
2654
        * @description The Toolbar items that should be disabled if there is no selection present in the editor.
2655
        * @type Array
2656
        */
2657
        _disabled: [ 'createlink', 'fontname', 'fontsize', 'forecolor', 'backcolor' ],
2658
        /**
2659
        * @private
2660
        * @property _alwaysDisabled
2661
        * @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
2662
        * @type Object
2663
        */
2664
        _alwaysDisabled: { undo: true, redo: true },
2665
        /**
2666
        * @private
2667
        * @property _alwaysEnabled
2668
        * @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
2669
        * @type Object
2670
        */
2671
        _alwaysEnabled: { },
2672
        /**
2673
        * @private
2674
        * @property _semantic
2675
        * @description The Toolbar commands that we should attempt to make tags out of instead of using styles.
2676
        * @type Object
2677
        */
2678
        _semantic: { 'bold': true, 'italic' : true, 'underline' : true },
2679
        /**
2680
        * @private
2681
        * @property _tag2cmd
2682
        * @description A tag map of HTML tags to convert to the different types of commands so we can select the proper toolbar button.
2683
        * @type Object
2684
        */
2685
        _tag2cmd: {
2686
            'b': 'bold',
2687
            'strong': 'bold',
2688
            'i': 'italic',
2689
            'em': 'italic',
2690
            'u': 'underline',
2691
            'sup': 'superscript',
2692
            'sub': 'subscript',
2693
            'img': 'insertimage',
2694
            'a' : 'createlink',
2695
            'ul' : 'insertunorderedlist',
2696
            'ol' : 'insertorderedlist'
2697
        },
2698
 
2699
        /**
2700
        * @private _createIframe
2701
        * @description Creates the DOM and YUI Element for the iFrame editor area.
2702
        * @param {String} id The string ID to prefix the iframe with
2703
        * @return {Object} iFrame object
2704
        */
2705
        _createIframe: function() {
2706
            var ifrmDom = document.createElement('iframe');
2707
            ifrmDom.id = this.get('id') + '_editor';
2708
            var config = {
2709
                border: '0',
2710
                frameBorder: '0',
2711
                marginWidth: '0',
2712
                marginHeight: '0',
2713
                leftMargin: '0',
2714
                topMargin: '0',
2715
                allowTransparency: 'true',
2716
                width: '100%'
2717
            };
2718
            if (this.get('autoHeight')) {
2719
                config.scrolling = 'no';
2720
            }
2721
            for (var i in config) {
2722
                if (Lang.hasOwnProperty(config, i)) {
2723
                    ifrmDom.setAttribute(i, config[i]);
2724
                }
2725
            }
2726
            var isrc = 'javascript:;';
2727
            if (this.browser.ie) {
2728
                //isrc = 'about:blank';
2729
                //TODO - Check this, I have changed it before..
2730
                isrc = 'javascript:false;';
2731
            }
2732
            ifrmDom.setAttribute('src', isrc);
2733
            var ifrm = new YAHOO.util.Element(ifrmDom);
2734
            ifrm.setStyle('visibility', 'hidden');
2735
            return ifrm;
2736
        },
2737
        /**
2738
        * @private _isElement
2739
        * @description Checks to see if an Element reference is a valid one and has a certain tag type
2740
        * @param {HTMLElement} el The element to check
2741
        * @param {String} tag The tag that the element needs to be
2742
        * @return {Boolean}
2743
        */
2744
        _isElement: function(el, tag) {
2745
            if (el && el.tagName && (el.tagName.toLowerCase() == tag)) {
2746
                return true;
2747
            }
2748
            if (el && el.getAttribute && (el.getAttribute('tag') == tag)) {
2749
                return true;
2750
            }
2751
            return false;
2752
        },
2753
        /**
2754
        * @private _hasParent
2755
        * @description Checks to see if an Element reference or one of it's parents is a valid one and has a certain tag type
2756
        * @param {HTMLElement} el The element to check
2757
        * @param {String} tag The tag that the element needs to be
2758
        * @return HTMLElement
2759
        */
2760
        _hasParent: function(el, tag) {
2761
            if (!el || !el.parentNode) {
2762
                return false;
2763
            }
2764
 
2765
            while (el.parentNode) {
2766
                if (this._isElement(el, tag)) {
2767
                    return el;
2768
                }
2769
                if (el.parentNode) {
2770
                    el = el.parentNode;
2771
                } else {
2772
                    return false;
2773
                }
2774
            }
2775
            return false;
2776
        },
2777
        /**
2778
        * @private
2779
        * @method _getDoc
2780
        * @description Get the Document of the IFRAME
2781
        * @return {Object}
2782
        */
2783
        _getDoc: function() {
2784
            var value = false;
2785
            try {
2786
                if (this.get('iframe').get('element').contentWindow.document) {
2787
                    value = this.get('iframe').get('element').contentWindow.document;
2788
                    return value;
2789
                }
2790
            } catch (e) {
2791
                return false;
2792
            }
2793
        },
2794
        /**
2795
        * @private
2796
        * @method _getWindow
2797
        * @description Get the Window of the IFRAME
2798
        * @return {Object}
2799
        */
2800
        _getWindow: function() {
2801
            return this.get('iframe').get('element').contentWindow;
2802
        },
2803
        /**
2804
        * @method focus
2805
        * @description Attempt to set the focus of the iframes window.
2806
        */
2807
        focus: function() {
2808
            this._getWindow().focus();
2809
        },
2810
        /**
2811
        * @private
2812
        * @depreciated - This should not be used, moved to this.focus();
2813
        * @method _focusWindow
2814
        * @description Attempt to set the focus of the iframes window.
2815
        */
2816
        _focusWindow: function() {
2817
            this.focus();
2818
        },
2819
        /**
2820
        * @private
2821
        * @method _hasSelection
2822
        * @description Determines if there is a selection in the editor document.
2823
        * @return {Boolean}
2824
        */
2825
        _hasSelection: function() {
2826
            var sel = this._getSelection();
2827
            var range = this._getRange();
2828
            var hasSel = false;
2829
 
2830
            if (!sel || !range) {
2831
                return hasSel;
2832
            }
2833
 
2834
            //Internet Explorer
2835
            if (this.browser.ie) {
2836
                if (range.text) {
2837
                    hasSel = true;
2838
                }
2839
                if (range.html) {
2840
                    hasSel = true;
2841
                }
2842
            } else {
2843
                if (this.browser.webkit) {
2844
                    if (sel+'' !== '') {
2845
                        hasSel = true;
2846
                    }
2847
                } else {
2848
                    if (sel && (sel.toString() !== '') && (sel !== undefined)) {
2849
                        hasSel = true;
2850
                    }
2851
                }
2852
            }
2853
            return hasSel;
2854
        },
2855
        /**
2856
        * @private
2857
        * @method _getSelection
2858
        * @description Handles the different selection objects across the A-Grade list.
2859
        * @return {Object} Selection Object
2860
        */
2861
        _getSelection: function() {
2862
            var _sel = null;
2863
            if (this._getDoc() && this._getWindow()) {
2864
                if (this._getDoc().selection &&! this.browser.opera) {
2865
                    _sel = this._getDoc().selection;
2866
                } else {
2867
                    _sel = this._getWindow().getSelection();
2868
                }
2869
                //Handle Safari's lack of Selection Object
2870
                if (this.browser.webkit) {
2871
                    if (_sel.baseNode) {
2872
                            this._selection = {};
2873
                            this._selection.baseNode = _sel.baseNode;
2874
                            this._selection.baseOffset = _sel.baseOffset;
2875
                            this._selection.extentNode = _sel.extentNode;
2876
                            this._selection.extentOffset = _sel.extentOffset;
2877
                    } else if (this._selection !== null) {
2878
                        _sel = this._getWindow().getSelection();
2879
                        _sel.setBaseAndExtent(
2880
                            this._selection.baseNode,
2881
                            this._selection.baseOffset,
2882
                            this._selection.extentNode,
2883
                            this._selection.extentOffset);
2884
                        this._selection = null;
2885
                    }
2886
                }
2887
            }
2888
            return _sel;
2889
        },
2890
        /**
2891
        * @private
2892
        * @method _selectNode
2893
        * @description Places the highlight around a given node
2894
        * @param {HTMLElement} node The node to select
2895
        */
2896
        _selectNode: function(node, collapse) {
2897
            if (!node) {
2898
                return false;
2899
            }
2900
            var sel = this._getSelection(),
2901
                range = null;
2902
 
2903
            if (this.browser.ie) {
2904
                try { //IE freaks out here sometimes..
2905
                    range = this._getDoc().body.createTextRange();
2906
                    range.moveToElementText(node);
2907
                    range.select();
2908
                } catch (e) {
2909
                }
2910
            } else if (this.browser.webkit) {
2911
                if (collapse) {
2912
				    sel.setBaseAndExtent(node, 1, node, node.innerText.length);
2913
                } else {
2914
				    sel.setBaseAndExtent(node, 0, node, node.innerText.length);
2915
                }
2916
            } else if (this.browser.opera) {
2917
                sel = this._getWindow().getSelection();
2918
                range = this._getDoc().createRange();
2919
                range.selectNode(node);
2920
                sel.removeAllRanges();
2921
                sel.addRange(range);
2922
            } else {
2923
                range = this._getDoc().createRange();
2924
                range.selectNodeContents(node);
2925
                sel.removeAllRanges();
2926
                sel.addRange(range);
2927
            }
2928
            //TODO - Check Performance
2929
            this.nodeChange();
2930
        },
2931
        /**
2932
        * @private
2933
        * @method _getRange
2934
        * @description Handles the different range objects across the A-Grade list.
2935
        * @return {Object} Range Object
2936
        */
2937
        _getRange: function() {
2938
            var sel = this._getSelection();
2939
 
2940
            if (sel === null) {
2941
                return null;
2942
            }
2943
 
2944
            if (this.browser.webkit && !sel.getRangeAt) {
2945
                var _range = this._getDoc().createRange();
2946
                try {
2947
                    _range.setStart(sel.anchorNode, sel.anchorOffset);
2948
                    _range.setEnd(sel.focusNode, sel.focusOffset);
2949
                } catch (e) {
2950
                    _range = this._getWindow().getSelection()+'';
2951
                }
2952
                return _range;
2953
            }
2954
 
2955
            if (this.browser.ie) {
2956
                try {
2957
                    return sel.createRange();
2958
                } catch (e2) {
2959
                    return null;
2960
                }
2961
            }
2962
 
2963
            if (sel.rangeCount > 0) {
2964
                return sel.getRangeAt(0);
2965
            }
2966
            return null;
2967
        },
2968
        /**
2969
        * @private
2970
        * @method _setDesignMode
2971
        * @description Sets the designMode property of the iFrame document's body.
2972
        * @param {String} state This should be either on or off
2973
        */
2974
        _setDesignMode: function(state) {
2975
            if (this.get('setDesignMode')) {
2976
                try {
2977
                    this._getDoc().designMode = ((state.toLowerCase() == 'off') ? 'off' : 'on');
2978
                } catch(e) { }
2979
            }
2980
        },
2981
        /**
2982
        * @private
2983
        * @method _toggleDesignMode
2984
        * @description Toggles the designMode property of the iFrame document on and off.
2985
        * @return {String} The state that it was set to.
2986
        */
2987
        _toggleDesignMode: function() {
2988
            var _dMode = this._getDoc().designMode,
2989
                _state = ((_dMode.toLowerCase() == 'on') ? 'off' : 'on');
2990
            this._setDesignMode(_state);
2991
            return _state;
2992
        },
2993
        /**
2994
        * @private
2995
        * @property _focused
2996
        * @description Holder for trapping focus/blur state and prevent double events
2997
        * @type Boolean
2998
        */
2999
        _focused: null,
3000
        /**
3001
        * @private
3002
        * @method _handleFocus
3003
        * @description Handles the focus of the iframe. Note, this is window focus event, not an Editor focus event.
3004
        * @param {Event} e The DOM Event
3005
        */
3006
        _handleFocus: function(e) {
3007
            if (!this._focused) {
3008
                this._focused = true;
3009
                this.fireEvent('editorWindowFocus', { type: 'editorWindowFocus', target: this });
3010
            }
3011
        },
3012
        /**
3013
        * @private
3014
        * @method _handleBlur
3015
        * @description Handles the blur of the iframe. Note, this is window blur event, not an Editor blur event.
3016
        * @param {Event} e The DOM Event
3017
        */
3018
        _handleBlur: function(e) {
3019
            if (this._focused) {
3020
                this._focused = false;
3021
                this.fireEvent('editorWindowBlur', { type: 'editorWindowBlur', target: this });
3022
            }
3023
        },
3024
        /**
3025
        * @private
3026
        * @method _initEditorEvents
3027
        * @description This method sets up the listeners on the Editors document.
3028
        */
3029
        _initEditorEvents: function() {
3030
            //Setup Listeners on iFrame
3031
            var doc = this._getDoc(),
3032
                win = this._getWindow();
3033
 
3034
            Event.on(doc, 'mouseup', this._handleMouseUp, this, true);
3035
            Event.on(doc, 'mousedown', this._handleMouseDown, this, true);
3036
            Event.on(doc, 'click', this._handleClick, this, true);
3037
            Event.on(doc, 'dblclick', this._handleDoubleClick, this, true);
3038
            Event.on(doc, 'keypress', this._handleKeyPress, this, true);
3039
            Event.on(doc, 'keyup', this._handleKeyUp, this, true);
3040
            Event.on(doc, 'keydown', this._handleKeyDown, this, true);
3041
            /* TODO -- Everyone but Opera works here..
3042
            Event.on(doc, 'paste', function() {
3043
            }, this, true);
3044
            */
3045
 
3046
            //Focus and blur..
3047
            Event.on(win, 'focus', this._handleFocus, this, true);
3048
            Event.on(win, 'blur', this._handleBlur, this, true);
3049
        },
3050
        /**
3051
        * @private
3052
        * @method _removeEditorEvents
3053
        * @description This method removes the listeners on the Editors document (for disabling).
3054
        */
3055
        _removeEditorEvents: function() {
3056
            //Remove Listeners on iFrame
3057
            var doc = this._getDoc(),
3058
                win = this._getWindow();
3059
 
3060
            Event.removeListener(doc, 'mouseup', this._handleMouseUp, this, true);
3061
            Event.removeListener(doc, 'mousedown', this._handleMouseDown, this, true);
3062
            Event.removeListener(doc, 'click', this._handleClick, this, true);
3063
            Event.removeListener(doc, 'dblclick', this._handleDoubleClick, this, true);
3064
            Event.removeListener(doc, 'keypress', this._handleKeyPress, this, true);
3065
            Event.removeListener(doc, 'keyup', this._handleKeyUp, this, true);
3066
            Event.removeListener(doc, 'keydown', this._handleKeyDown, this, true);
3067
 
3068
            //Focus and blur..
3069
            Event.removeListener(win, 'focus', this._handleFocus, this, true);
3070
            Event.removeListener(win, 'blur', this._handleBlur, this, true);
3071
        },
3072
        _fixWebkitDivs: function() {
3073
            if (this.browser.webkit) {
3074
                var divs = this._getDoc().body.getElementsByTagName('div');
3075
                Dom.addClass(divs, 'yui-wk-div');
3076
            }
3077
        },
3078
        /**
3079
        * @private
3080
        * @method _initEditor
3081
        * @param {Boolean} raw Don't add events.
3082
        * @description This method is fired from _checkLoaded when the document is ready. It turns on designMode and set's up the listeners.
3083
        */
3084
        _initEditor: function(raw) {
3085
            if (this._editorInit) {
3086
                return;
3087
            }
3088
            this._editorInit = true;
3089
            if (this.browser.ie) {
3090
                this._getDoc().body.style.margin = '0';
3091
            }
3092
            if (!this.get('disabled')) {
3093
                this._setDesignMode('on');
3094
                this._contentTimerCounter = 0;
3095
            }
3096
            if (!this._getDoc().body) {
3097
                this._contentTimerCounter = 0;
3098
                this._editorInit = false;
3099
                this._checkLoaded();
3100
                return false;
3101
            }
3102
 
3103
            if (!raw) {
3104
                this.toolbar.on('buttonClick', this._handleToolbarClick, this, true);
3105
            }
3106
            if (!this.get('disabled')) {
3107
                this._initEditorEvents();
3108
                this.toolbar.set('disabled', false);
3109
            }
3110
 
3111
            if (raw) {
3112
                this.fireEvent('editorContentReloaded', { type: 'editorreloaded', target: this });
3113
            } else {
3114
                this.fireEvent('editorContentLoaded', { type: 'editorLoaded', target: this });
3115
            }
3116
            this._fixWebkitDivs();
3117
            if (this.get('dompath')) {
3118
                var self = this;
3119
                setTimeout(function() {
3120
                    self._writeDomPath.call(self);
3121
                    self._setupResize.call(self);
3122
                }, 150);
3123
            }
3124
            var br = [];
3125
            for (var i in this.browser) {
3126
                if (this.browser[i]) {
3127
                    br.push(i);
3128
                }
3129
            }
3130
            if (this.get('ptags')) {
3131
                br.push('ptags');
3132
            }
3133
            Dom.addClass(this._getDoc().body, br.join(' '));
3134
            this.nodeChange(true);
3135
        },
3136
        /**
3137
        * @private
3138
        * @method _checkLoaded
3139
        * @param {Boolean} raw Don't add events.
3140
        * @description Called from a setTimeout loop to check if the iframes body.onload event has fired, then it will init the editor.
3141
        */
3142
        _checkLoaded: function(raw) {
3143
            this._editorInit = false;
3144
            this._contentTimerCounter++;
3145
            if (this._contentTimer) {
3146
                clearTimeout(this._contentTimer);
3147
            }
3148
            if (this._contentTimerCounter > this._contentTimerMax) {
3149
                return false;
3150
            }
3151
            var init = false;
3152
            try {
3153
                if (this._getDoc() && this._getDoc().body) {
3154
                    if (this.browser.ie) {
3155
                        if (this._getDoc().body.readyState == 'complete') {
3156
                            init = true;
3157
                        }
3158
                    } else {
3159
                        if (this._getDoc().body._rteLoaded === true) {
3160
                            init = true;
3161
                        }
3162
                    }
3163
                }
3164
            } catch (e) {
3165
                init = false;
3166
            }
3167
 
3168
            if (init === true) {
3169
                //The onload event has fired, clean up after ourselves and fire the _initEditor method
3170
                this._initEditor(raw);
3171
            } else {
3172
                var self = this;
3173
                this._contentTimer = setTimeout(function() {
3174
                    self._checkLoaded.call(self, raw);
3175
                }, 20);
3176
            }
3177
        },
3178
        /**
3179
        * @private
3180
        * @method _setInitialContent
3181
        * @param {Boolean} raw Don't add events.
3182
        * @description This method will open the iframes content document and write the textareas value into it, then start the body.onload checking.
3183
        */
3184
        _setInitialContent: function(raw) {
3185
 
3186
            var value = ((this._textarea) ? this.get('element').value : this.get('element').innerHTML),
3187
                doc = null;
3188
 
3189
            if (value === '') {
3190
                value = '<br>';
3191
            }
3192
 
3193
            var html = Lang.substitute(this.get('html'), {
3194
                TITLE: this.STR_TITLE,
3195
                CONTENT: this._cleanIncomingHTML(value),
3196
                CSS: this.get('css'),
3197
                HIDDEN_CSS: ((this.get('hiddencss')) ? this.get('hiddencss') : '/* No Hidden CSS */'),
3198
                EXTRA_CSS: ((this.get('extracss')) ? this.get('extracss') : '/* No Extra CSS */')
3199
            }),
3200
            check = true;
3201
 
3202
            html = html.replace(/RIGHT_BRACKET/gi, '{');
3203
            html = html.replace(/LEFT_BRACKET/gi, '}');
3204
 
3205
            if (document.compatMode != 'BackCompat') {
3206
                html = this._docType + "\n" + html;
3207
            } else {
3208
            }
3209
 
3210
            if (this.browser.ie || this.browser.webkit || this.browser.opera || (navigator.userAgent.indexOf('Firefox/1.5') != -1)) {
3211
                //Firefox 1.5 doesn't like setting designMode on an document created with a data url
3212
                try {
3213
                    //Adobe AIR Code
3214
                    if (this.browser.air) {
3215
                        doc = this._getDoc().implementation.createHTMLDocument();
3216
                        var origDoc = this._getDoc();
3217
                        origDoc.open();
3218
                        origDoc.close();
3219
                        doc.open();
3220
                        doc.write(html);
3221
                        doc.close();
3222
                        var node = origDoc.importNode(doc.getElementsByTagName("html")[0], true);
3223
                        origDoc.replaceChild(node, origDoc.getElementsByTagName("html")[0]);
3224
                        origDoc.body._rteLoaded = true;
3225
                    } else {
3226
                        doc = this._getDoc();
3227
                        doc.open();
3228
                        doc.write(html);
3229
                        doc.close();
3230
                    }
3231
                } catch (e) {
3232
                    //Safari will only be here if we are hidden
3233
                    check = false;
3234
                }
3235
            } else {
3236
                //This keeps Firefox 2 from writing the iframe to history preserving the back buttons functionality
3237
                this.get('iframe').get('element').src = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
3238
            }
3239
            this.get('iframe').setStyle('visibility', '');
3240
            if (check) {
3241
                this._checkLoaded(raw);
3242
            }
3243
        },
3244
        /**
3245
        * @private
3246
        * @method _setMarkupType
3247
        * @param {String} action The action to take. Possible values are: css, default or semantic
3248
        * @description This method will turn on/off the useCSS execCommand.
3249
        */
3250
        _setMarkupType: function(action) {
3251
            switch (this.get('markup')) {
3252
                case 'css':
3253
                    this._setEditorStyle(true);
3254
                    break;
3255
                case 'default':
3256
                    this._setEditorStyle(false);
3257
                    break;
3258
                case 'semantic':
3259
                case 'xhtml':
3260
                    if (this._semantic[action]) {
3261
                        this._setEditorStyle(false);
3262
                    } else {
3263
                        this._setEditorStyle(true);
3264
                    }
3265
                    break;
3266
            }
3267
        },
3268
        /**
3269
        * Set the editor to use CSS instead of HTML
3270
        * @param {Booleen} stat True/False
3271
        */
3272
        _setEditorStyle: function(stat) {
3273
            try {
3274
                this._getDoc().execCommand('useCSS', false, !stat);
3275
            } catch (ex) {
3276
            }
3277
        },
3278
        /**
3279
        * @private
3280
        * @method _getSelectedElement
3281
        * @description This method will attempt to locate the element that was last interacted with, either via selection, location or event.
3282
        * @return {HTMLElement} The currently selected element.
3283
        */
3284
        _getSelectedElement: function() {
3285
            var doc = this._getDoc(),
3286
                range = null,
3287
                sel = null,
3288
                elm = null,
3289
                check = true;
3290
 
3291
            if (this.browser.ie) {
3292
                this.currentEvent = this._getWindow().event; //Event utility assumes window.event, so we need to reset it to this._getWindow().event;
3293
                range = this._getRange();
3294
                if (range) {
3295
                    elm = range.item ? range.item(0) : range.parentElement();
3296
                    if (this._hasSelection()) {
3297
                        //TODO
3298
                        //WTF.. Why can't I get an element reference here?!??!
3299
                    }
3300
                    if (elm === doc.body) {
3301
                        elm = null;
3302
                    }
3303
                }
3304
                if ((this.currentEvent !== null) && (this.currentEvent.keyCode === 0)) {
3305
                    elm = Event.getTarget(this.currentEvent);
3306
                }
3307
            } else {
3308
                sel = this._getSelection();
3309
                range = this._getRange();
3310
 
3311
                if (!sel || !range) {
3312
                    return null;
3313
                }
3314
                //TODO
3315
                if (!this._hasSelection() && this.browser.webkit3) {
3316
                    //check = false;
3317
                }
3318
                if (this.browser.gecko) {
3319
                    //Added in 2.6.0
3320
                    if (range.startContainer) {
3321
                        if (range.startContainer.nodeType === 3) {
3322
                            elm = range.startContainer.parentNode;
3323
                        } else if (range.startContainer.nodeType === 1) {
3324
                            elm = range.startContainer;
3325
                        }
3326
                        //Added in 2.7.0
3327
                        if (this.currentEvent) {
3328
                            var tar = Event.getTarget(this.currentEvent);
3329
                            if (!this._isElement(tar, 'html')) {
3330
                                if (elm !== tar) {
3331
                                    elm = tar;
3332
                                }
3333
                            }
3334
                        }
3335
                    }
3336
                }
3337
 
3338
                if (check) {
3339
                    if (sel.anchorNode && (sel.anchorNode.nodeType == 3)) {
3340
                        if (sel.anchorNode.parentNode) { //next check parentNode
3341
                            elm = sel.anchorNode.parentNode;
3342
                        }
3343
                        if (sel.anchorNode.nextSibling != sel.focusNode.nextSibling) {
3344
                            elm = sel.anchorNode.nextSibling;
3345
                        }
3346
                    }
3347
                    if (this._isElement(elm, 'br')) {
3348
                        elm = null;
3349
                    }
3350
                    if (!elm) {
3351
                        elm = range.commonAncestorContainer;
3352
                        if (!range.collapsed) {
3353
                            if (range.startContainer == range.endContainer) {
3354
                                if (range.startOffset - range.endOffset < 2) {
3355
                                    if (range.startContainer.hasChildNodes()) {
3356
                                        elm = range.startContainer.childNodes[range.startOffset];
3357
                                    }
3358
                                }
3359
                            }
3360
                        }
3361
                    }
3362
               }
3363
            }
3364
 
3365
            if (this.currentEvent !== null) {
3366
                try {
3367
                    switch (this.currentEvent.type) {
3368
                        case 'click':
3369
                        case 'mousedown':
3370
                        case 'mouseup':
3371
                            if (this.browser.webkit) {
3372
                                elm = Event.getTarget(this.currentEvent);
3373
                            }
3374
                            break;
3375
                        default:
3376
                            //Do nothing
3377
                            break;
3378
                    }
3379
                } catch (e) {
3380
                }
3381
            } else if ((this.currentElement && this.currentElement[0]) && (!this.browser.ie)) {
3382
                //TODO is this still needed?
3383
                //elm = this.currentElement[0];
3384
            }
3385
 
3386
 
3387
            if (this.browser.opera || this.browser.webkit) {
3388
                if (this.currentEvent && !elm) {
3389
                    elm = YAHOO.util.Event.getTarget(this.currentEvent);
3390
                }
3391
            }
3392
            if (!elm || !elm.tagName) {
3393
                elm = doc.body;
3394
            }
3395
            if (this._isElement(elm, 'html')) {
3396
                //Safari sometimes gives us the HTML node back..
3397
                elm = doc.body;
3398
            }
3399
            if (this._isElement(elm, 'body')) {
3400
                //make sure that body means this body not the parent..
3401
                elm = doc.body;
3402
            }
3403
            if (elm && !elm.parentNode) { //Not in document
3404
                elm = doc.body;
3405
            }
3406
            if (elm === undefined) {
3407
                elm = null;
3408
            }
3409
            return elm;
3410
        },
3411
        /**
3412
        * @private
3413
        * @method _getDomPath
3414
        * @description This method will attempt to build the DOM path from the currently selected element.
3415
        * @param HTMLElement el The element to start with, if not provided _getSelectedElement is used
3416
        * @return {Array} An array of node references that will create the DOM Path.
3417
        */
3418
        _getDomPath: function(el) {
3419
            if (!el) {
3420
			    el = this._getSelectedElement();
3421
            }
3422
			var domPath = [];
3423
            while (el !== null) {
3424
                if (el.ownerDocument != this._getDoc()) {
3425
                    el = null;
3426
                    break;
3427
                }
3428
                //Check to see if we get el.nodeName and nodeType
3429
                if (el.nodeName && el.nodeType && (el.nodeType == 1)) {
3430
                    domPath[domPath.length] = el;
3431
                }
3432
 
3433
                if (this._isElement(el, 'body')) {
3434
                    break;
3435
                }
3436
 
3437
                el = el.parentNode;
3438
            }
3439
            if (domPath.length === 0) {
3440
                if (this._getDoc() && this._getDoc().body) {
3441
                    domPath[0] = this._getDoc().body;
3442
                }
3443
            }
3444
            return domPath.reverse();
3445
        },
3446
        /**
3447
        * @private
3448
        * @method _writeDomPath
3449
        * @description Write the current DOM path out to the dompath container below the editor.
3450
        */
3451
        _writeDomPath: function() {
3452
            var path = this._getDomPath(),
3453
                pathArr = [],
3454
                classPath = '',
3455
                pathStr = '';
3456
 
3457
            for (var i = 0; i < path.length; i++) {
3458
                var tag = path[i].tagName.toLowerCase();
3459
                if ((tag == 'ol') && (path[i].type)) {
3460
                    tag += ':' + path[i].type;
3461
                }
3462
                if (Dom.hasClass(path[i], 'yui-tag')) {
3463
                    tag = path[i].getAttribute('tag');
3464
                }
3465
                if ((this.get('markup') == 'semantic') || (this.get('markup') == 'xhtml')) {
3466
                    switch (tag) {
3467
                        case 'b': tag = 'strong'; break;
3468
                        case 'i': tag = 'em'; break;
3469
                    }
3470
                }
3471
                if (!Dom.hasClass(path[i], 'yui-non')) {
3472
                    if (Dom.hasClass(path[i], 'yui-tag')) {
3473
                        pathStr = tag;
3474
                    } else {
3475
                        classPath = ((path[i].className !== '') ? '.' + path[i].className.replace(/ /g, '.') : '');
3476
                        if ((classPath.indexOf('yui') != -1) || (classPath.toLowerCase().indexOf('apple-style-span') != -1)) {
3477
                            classPath = '';
3478
                        }
3479
                        pathStr = tag + ((path[i].id) ? '#' + path[i].id : '') + classPath;
3480
                    }
3481
                    switch (tag) {
3482
                        case 'body':
3483
                            pathStr = 'body';
3484
                            break;
3485
                        case 'a':
3486
                            if (path[i].getAttribute('href', 2)) {
3487
                                pathStr += ':' + path[i].getAttribute('href', 2).replace('mailto:', '').replace('http:/'+'/', '').replace('https:/'+'/', ''); //May need to add others here ftp
3488
                            }
3489
                            break;
3490
                        case 'img':
3491
                            var h = path[i].height;
3492
                            var w = path[i].width;
3493
                            if (path[i].style.height) {
3494
                                h = parseInt(path[i].style.height, 10);
3495
                            }
3496
                            if (path[i].style.width) {
3497
                                w = parseInt(path[i].style.width, 10);
3498
                            }
3499
                            pathStr += '(' + w + 'x' + h + ')';
3500
                        break;
3501
                    }
3502
 
3503
                    if (pathStr.length > 10) {
3504
                        pathStr = '<span title="' + pathStr + '">' + pathStr.substring(0, 10) + '...' + '</span>';
3505
                    } else {
3506
                        pathStr = '<span title="' + pathStr + '">' + pathStr + '</span>';
3507
                    }
3508
                    pathArr[pathArr.length] = pathStr;
3509
                }
3510
            }
3511
            var str = pathArr.join(' ' + this.SEP_DOMPATH + ' ');
3512
            //Prevent flickering
3513
            if (this.dompath.innerHTML != str) {
3514
                this.dompath.innerHTML = str;
3515
            }
3516
        },
3517
        /**
3518
        * @private
3519
        * @method _fixNodes
3520
        * @description Fix href and imgs as well as remove invalid HTML.
3521
        */
3522
        _fixNodes: function() {
3523
            try {
3524
                var doc = this._getDoc(),
3525
                    els = [];
3526
 
3527
                for (var v in this.invalidHTML) {
3528
                    if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
3529
                        if (v.toLowerCase() != 'span') {
3530
                            var tags = doc.body.getElementsByTagName(v);
3531
                            if (tags.length) {
3532
                                for (var i = 0; i < tags.length; i++) {
3533
                                    els.push(tags[i]);
3534
                                }
3535
                            }
3536
                        }
3537
                    }
3538
                }
3539
                for (var h = 0; h < els.length; h++) {
3540
                    if (els[h].parentNode) {
3541
                        if (Lang.isObject(this.invalidHTML[els[h].tagName.toLowerCase()]) && this.invalidHTML[els[h].tagName.toLowerCase()].keepContents) {
3542
                            this._swapEl(els[h], 'span', function(el) {
3543
                                el.className = 'yui-non';
3544
                            });
3545
                        } else {
3546
                            els[h].parentNode.removeChild(els[h]);
3547
                        }
3548
                    }
3549
                }
3550
                var imgs = this._getDoc().getElementsByTagName('img');
3551
                Dom.addClass(imgs, 'yui-img');
3552
            } catch(e) {}
3553
        },
3554
        /**
3555
        * @private
3556
        * @method _isNonEditable
3557
        * @param Event ev The Dom event being checked
3558
        * @description Method is called at the beginning of all event handlers to check if this element or a parent element has the class yui-noedit (this.CLASS_NOEDIT) applied.
3559
        * If it does, then this method will stop the event and return true. The event handlers will then return false and stop the nodeChange from occuring. This method will also
3560
        * disable and enable the Editor's toolbar based on the noedit state.
3561
        * @return Boolean
3562
        */
3563
        _isNonEditable: function(ev) {
3564
            if (this.get('allowNoEdit')) {
3565
                var el = Event.getTarget(ev);
3566
                if (this._isElement(el, 'html')) {
3567
                    el = null;
3568
                }
3569
                var path = this._getDomPath(el);
3570
                for (var i = (path.length - 1); i > -1; i--) {
3571
                    if (Dom.hasClass(path[i], this.CLASS_NOEDIT)) {
3572
                        //if (this.toolbar.get('disabled') === false) {
3573
                        //    this.toolbar.set('disabled', true);
3574
                        //}
3575
                        try {
3576
                             this._getDoc().execCommand('enableObjectResizing', false, 'false');
3577
                        } catch (e) {}
3578
                        this.nodeChange();
3579
                        Event.stopEvent(ev);
3580
                        return true;
3581
                    }
3582
                }
3583
                //if (this.toolbar.get('disabled') === true) {
3584
                    //Should only happen once..
3585
                    //this.toolbar.set('disabled', false);
3586
                    try {
3587
                         this._getDoc().execCommand('enableObjectResizing', false, 'true');
3588
                    } catch (e2) {}
3589
                //}
3590
            }
3591
            return false;
3592
        },
3593
        /**
3594
        * @private
3595
        * @method _setCurrentEvent
3596
        * @param {Event} ev The event to cache
3597
        * @description Sets the current event property
3598
        */
3599
        _setCurrentEvent: function(ev) {
3600
            this.currentEvent = ev;
3601
        },
3602
        /**
3603
        * @private
3604
        * @method _handleClick
3605
        * @param {Event} ev The event we are working on.
3606
        * @description Handles all click events inside the iFrame document.
3607
        */
3608
        _handleClick: function(ev) {
3609
            var ret = this.fireEvent('beforeEditorClick', { type: 'beforeEditorClick', target: this, ev: ev });
3610
            if (ret === false) {
3611
                return false;
3612
            }
3613
            if (this._isNonEditable(ev)) {
3614
                return false;
3615
            }
3616
            this._setCurrentEvent(ev);
3617
            if (this.currentWindow) {
3618
                this.closeWindow();
3619
            }
3620
            if (this.currentWindow) {
3621
                this.closeWindow();
3622
            }
3623
            if (this.browser.webkit) {
3624
                var tar =Event.getTarget(ev);
3625
                if (this._isElement(tar, 'a') || this._isElement(tar.parentNode, 'a')) {
3626
                    Event.stopEvent(ev);
3627
                    this.nodeChange();
3628
                }
3629
            } else {
3630
                this.nodeChange();
3631
            }
3632
            this.fireEvent('editorClick', { type: 'editorClick', target: this, ev: ev });
3633
        },
3634
        /**
3635
        * @private
3636
        * @method _handleMouseUp
3637
        * @param {Event} ev The event we are working on.
3638
        * @description Handles all mouseup events inside the iFrame document.
3639
        */
3640
        _handleMouseUp: function(ev) {
3641
            var ret = this.fireEvent('beforeEditorMouseUp', { type: 'beforeEditorMouseUp', target: this, ev: ev });
3642
            if (ret === false) {
3643
                return false;
3644
            }
3645
            if (this._isNonEditable(ev)) {
3646
                return false;
3647
            }
3648
            //Don't set current event for mouseup.
3649
            //It get's fired after a menu is closed and gives up a bogus event to work with
3650
            //this._setCurrentEvent(ev);
3651
            var self = this;
3652
            if (this.browser.opera) {
3653
                /*
3654
                * @knownissue Opera appears to stop the MouseDown, Click and DoubleClick events on an image inside of a document with designMode on..
3655
                * @browser Opera
3656
                * @description This work around traps the MouseUp event and sets a timer to check if another MouseUp event fires in so many seconds. If another event is fired, they we internally fire the DoubleClick event.
3657
                */
3658
                var sel = Event.getTarget(ev);
3659
                if (this._isElement(sel, 'img')) {
3660
                    this.nodeChange();
3661
                    if (this.operaEvent) {
3662
                        clearTimeout(this.operaEvent);
3663
                        this.operaEvent = null;
3664
                        this._handleDoubleClick(ev);
3665
                    } else {
3666
                        this.operaEvent = window.setTimeout(function() {
3667
                            self.operaEvent = false;
3668
                        }, 700);
3669
                    }
3670
                }
3671
            }
3672
            //This will stop Safari from selecting the entire document if you select all the text in the editor
3673
            if (this.browser.webkit || this.browser.opera) {
3674
                if (this.browser.webkit) {
3675
                    Event.stopEvent(ev);
3676
                }
3677
            }
3678
            this.nodeChange();
3679
            this.fireEvent('editorMouseUp', { type: 'editorMouseUp', target: this, ev: ev });
3680
        },
3681
        /**
3682
        * @private
3683
        * @method _handleMouseDown
3684
        * @param {Event} ev The event we are working on.
3685
        * @description Handles all mousedown events inside the iFrame document.
3686
        */
3687
        _handleMouseDown: function(ev) {
3688
            var ret = this.fireEvent('beforeEditorMouseDown', { type: 'beforeEditorMouseDown', target: this, ev: ev });
3689
            if (ret === false) {
3690
                return false;
3691
            }
3692
            if (this._isNonEditable(ev)) {
3693
                return false;
3694
            }
3695
            this._setCurrentEvent(ev);
3696
            var sel = Event.getTarget(ev);
3697
            if (this.browser.webkit && this._hasSelection()) {
3698
                var _sel = this._getSelection();
3699
                if (!this.browser.webkit3) {
3700
                    _sel.collapse(true);
3701
                } else {
3702
                    _sel.collapseToStart();
3703
                }
3704
            }
3705
            if (this.browser.webkit && this._lastImage) {
3706
                Dom.removeClass(this._lastImage, 'selected');
3707
                this._lastImage = null;
3708
            }
3709
            if (this._isElement(sel, 'img') || this._isElement(sel, 'a')) {
3710
                if (this.browser.webkit) {
3711
                    Event.stopEvent(ev);
3712
                    if (this._isElement(sel, 'img')) {
3713
                        Dom.addClass(sel, 'selected');
3714
                        this._lastImage = sel;
3715
                    }
3716
                }
3717
                if (this.currentWindow) {
3718
                    this.closeWindow();
3719
                }
3720
                this.nodeChange();
3721
            }
3722
            this.fireEvent('editorMouseDown', { type: 'editorMouseDown', target: this, ev: ev });
3723
        },
3724
        /**
3725
        * @private
3726
        * @method _handleDoubleClick
3727
        * @param {Event} ev The event we are working on.
3728
        * @description Handles all doubleclick events inside the iFrame document.
3729
        */
3730
        _handleDoubleClick: function(ev) {
3731
            var ret = this.fireEvent('beforeEditorDoubleClick', { type: 'beforeEditorDoubleClick', target: this, ev: ev });
3732
            if (ret === false) {
3733
                return false;
3734
            }
3735
            if (this._isNonEditable(ev)) {
3736
                return false;
3737
            }
3738
            this._setCurrentEvent(ev);
3739
            var sel = Event.getTarget(ev);
3740
            if (this._isElement(sel, 'img')) {
3741
                this.currentElement[0] = sel;
3742
                this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
3743
                this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3744
            } else if (this._hasParent(sel, 'a')) { //Handle elements inside an a
3745
                this.currentElement[0] = this._hasParent(sel, 'a');
3746
                this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
3747
                this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3748
            }
3749
            this.nodeChange();
3750
            this.fireEvent('editorDoubleClick', { type: 'editorDoubleClick', target: this, ev: ev });
3751
        },
3752
        /**
3753
        * @private
3754
        * @method _handleKeyUp
3755
        * @param {Event} ev The event we are working on.
3756
        * @description Handles all keyup events inside the iFrame document.
3757
        */
3758
        _handleKeyUp: function(ev) {
3759
            var ret = this.fireEvent('beforeEditorKeyUp', { type: 'beforeEditorKeyUp', target: this, ev: ev });
3760
            if (ret === false) {
3761
                return false;
3762
            }
3763
            if (this._isNonEditable(ev)) {
3764
                return false;
3765
            }
3766
            this._storeUndo();
3767
            this._setCurrentEvent(ev);
3768
            switch (ev.keyCode) {
3769
                case this._keyMap.SELECT_ALL.key:
3770
                    if (this._checkKey(this._keyMap.SELECT_ALL, ev)) {
3771
                        this.nodeChange();
3772
                    }
3773
                    break;
3774
                case 32: //Space Bar
3775
                case 35: //End
3776
                case 36: //Home
3777
                case 37: //Left Arrow
3778
                case 38: //Up Arrow
3779
                case 39: //Right Arrow
3780
                case 40: //Down Arrow
3781
                case 46: //Forward Delete
3782
                case 8: //Delete
3783
                case this._keyMap.CLOSE_WINDOW.key: //W key if window is open
3784
                    if ((ev.keyCode == this._keyMap.CLOSE_WINDOW.key) && this.currentWindow) {
3785
                        if (this._checkKey(this._keyMap.CLOSE_WINDOW, ev)) {
3786
                            this.closeWindow();
3787
                        }
3788
                    } else {
3789
                        if (!this.browser.ie) {
3790
                            if (this._nodeChangeTimer) {
3791
                                clearTimeout(this._nodeChangeTimer);
3792
                            }
3793
                            var self = this;
3794
                            this._nodeChangeTimer = setTimeout(function() {
3795
                                self._nodeChangeTimer = null;
3796
                                self.nodeChange.call(self);
3797
                            }, 100);
3798
                        } else {
3799
                            this.nodeChange();
3800
                        }
3801
                        this.editorDirty = true;
3802
                    }
3803
                    break;
3804
            }
3805
            this.fireEvent('editorKeyUp', { type: 'editorKeyUp', target: this, ev: ev });
3806
        },
3807
        /**
3808
        * @private
3809
        * @method _handleKeyPress
3810
        * @param {Event} ev The event we are working on.
3811
        * @description Handles all keypress events inside the iFrame document.
3812
        */
3813
        _handleKeyPress: function(ev) {
3814
            var ret = this.fireEvent('beforeEditorKeyPress', { type: 'beforeEditorKeyPress', target: this, ev: ev });
3815
            if (ret === false) {
3816
                return false;
3817
            }
3818
 
3819
            if (this.get('allowNoEdit')) {
3820
                //if (ev && ev.keyCode && ((ev.keyCode == 46) || ev.keyCode == 63272)) {
3821
                if (ev && ev.keyCode && (ev.keyCode == 63272)) {
3822
                    //Forward delete key
3823
                    Event.stopEvent(ev);
3824
                }
3825
            }
3826
            if (this._isNonEditable(ev)) {
3827
                return false;
3828
            }
3829
            this._setCurrentEvent(ev);
3830
            this._storeUndo();
3831
            if (this.browser.opera) {
3832
                if (ev.keyCode === 13) {
3833
                    var tar = this._getSelectedElement();
3834
                    if (!this._isElement(tar, 'li')) {
3835
                        this.execCommand('inserthtml', '<br>');
3836
                        Event.stopEvent(ev);
3837
                    }
3838
                }
3839
            }
3840
            if (this.browser.webkit) {
3841
                if (!this.browser.webkit3) {
3842
                    if (ev.keyCode && (ev.keyCode == 122) && (ev.metaKey)) {
3843
                        //This is CMD + z (for undo)
3844
                        if (this._hasParent(this._getSelectedElement(), 'li')) {
3845
                            Event.stopEvent(ev);
3846
                        }
3847
                    }
3848
                }
3849
                this._listFix(ev);
3850
            }
3851
            this._fixListDupIds();
3852
            this.fireEvent('editorKeyPress', { type: 'editorKeyPress', target: this, ev: ev });
3853
        },
3854
        /**
3855
        * @private
3856
        * @method _handleKeyDown
3857
        * @param {Event} ev The event we are working on.
3858
        * @description Handles all keydown events inside the iFrame document.
3859
        */
3860
        _handleKeyDown: function(ev) {
3861
            var ret = this.fireEvent('beforeEditorKeyDown', { type: 'beforeEditorKeyDown', target: this, ev: ev });
3862
            if (ret === false) {
3863
                return false;
3864
            }
3865
            var tar = null, _range = null;
3866
            if (this._isNonEditable(ev)) {
3867
                return false;
3868
            }
3869
            this._setCurrentEvent(ev);
3870
            if (this.currentWindow) {
3871
                this.closeWindow();
3872
            }
3873
            if (this.currentWindow) {
3874
                this.closeWindow();
3875
            }
3876
            var doExec = false,
3877
                action = null,
3878
                value = null,
3879
                exec = false;
3880
 
3881
 
3882
            switch (ev.keyCode) {
3883
                case this._keyMap.FOCUS_TOOLBAR.key:
3884
                    if (this._checkKey(this._keyMap.FOCUS_TOOLBAR, ev)) {
3885
                        var h = this.toolbar.getElementsByTagName('h2')[0];
3886
                        if (h && h.firstChild) {
3887
                            h.firstChild.focus();
3888
                        }
3889
                    } else if (this._checkKey(this._keyMap.FOCUS_AFTER, ev)) {
3890
                        //Focus After Element - Esc
3891
                        this.afterElement.focus();
3892
                    }
3893
                    Event.stopEvent(ev);
3894
                    doExec = false;
3895
                    break;
3896
                //case 76: //L
3897
                case this._keyMap.CREATE_LINK.key: //L
3898
                    if (this._hasSelection()) {
3899
                        if (this._checkKey(this._keyMap.CREATE_LINK, ev)) {
3900
                            var makeLink = true;
3901
                            if (this.get('limitCommands')) {
3902
                                if (!this.toolbar.getButtonByValue('createlink')) {
3903
                                    makeLink = false;
3904
                                }
3905
                            }
3906
                            if (makeLink) {
3907
                                this.execCommand('createlink', '');
3908
                                this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
3909
                                this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3910
                                doExec = false;
3911
                            }
3912
                        }
3913
                    }
3914
                    break;
3915
                //case 90: //Z
3916
                case this._keyMap.UNDO.key:
3917
                case this._keyMap.REDO.key:
3918
                    if (this._checkKey(this._keyMap.REDO, ev)) {
3919
                        action = 'redo';
3920
                        doExec = true;
3921
                    } else if (this._checkKey(this._keyMap.UNDO, ev)) {
3922
                        action = 'undo';
3923
                        doExec = true;
3924
                    }
3925
                    break;
3926
                //case 66: //B
3927
                case this._keyMap.BOLD.key:
3928
                    if (this._checkKey(this._keyMap.BOLD, ev)) {
3929
                        action = 'bold';
3930
                        doExec = true;
3931
                    }
3932
                    break;
3933
                case this._keyMap.FONT_SIZE_UP.key:
3934
                case this._keyMap.FONT_SIZE_DOWN.key:
3935
                    var uk = false, dk = false;
3936
                    if (this._checkKey(this._keyMap.FONT_SIZE_UP, ev)) {
3937
                        uk = true;
3938
                    }
3939
                    if (this._checkKey(this._keyMap.FONT_SIZE_DOWN, ev)) {
3940
                        dk = true;
3941
                    }
3942
                    if (uk || dk) {
3943
                        var fs_button = this.toolbar.getButtonByValue('fontsize'),
3944
                            label = parseInt(fs_button.get('label'), 10),
3945
                            newValue = (label + 1);
3946
 
3947
                        if (dk) {
3948
                            newValue = (label - 1);
3949
                        }
3950
 
3951
                        action = 'fontsize';
3952
                        value = newValue + 'px';
3953
                        doExec = true;
3954
                    }
3955
                    break;
3956
                //case 73: //I
3957
                case this._keyMap.ITALIC.key:
3958
                    if (this._checkKey(this._keyMap.ITALIC, ev)) {
3959
                        action = 'italic';
3960
                        doExec = true;
3961
                    }
3962
                    break;
3963
                //case 85: //U
3964
                case this._keyMap.UNDERLINE.key:
3965
                    if (this._checkKey(this._keyMap.UNDERLINE, ev)) {
3966
                        action = 'underline';
3967
                        doExec = true;
3968
                    }
3969
                    break;
3970
                case 9:
3971
                    if (this.browser.ie) {
3972
                        //Insert a tab in Internet Explorer
3973
                        _range = this._getRange();
3974
                        tar = this._getSelectedElement();
3975
                        if (!this._isElement(tar, 'li')) {
3976
                            if (_range) {
3977
                                _range.pasteHTML('&nbsp;&nbsp;&nbsp;&nbsp;');
3978
                                _range.collapse(false);
3979
                                _range.select();
3980
                            }
3981
                            Event.stopEvent(ev);
3982
                        }
3983
                    }
3984
                    //Firefox 3 code
3985
                    if (this.browser.gecko > 1.8) {
3986
                        tar = this._getSelectedElement();
3987
                        if (this._isElement(tar, 'li')) {
3988
                            if (ev.shiftKey) {
3989
                                this._getDoc().execCommand('outdent', null, '');
3990
                            } else {
3991
                                this._getDoc().execCommand('indent', null, '');
3992
                            }
3993
 
3994
                        } else if (!this._hasSelection()) {
3995
                            this.execCommand('inserthtml', '&nbsp;&nbsp;&nbsp;&nbsp;');
3996
                        }
3997
                        Event.stopEvent(ev);
3998
                    }
3999
                    break;
4000
                case 13:
4001
                    var p = null, i = 0;
4002
                    if (this.get('ptags') && !ev.shiftKey) {
4003
                        if (this.browser.gecko) {
4004
                            tar = this._getSelectedElement();
4005
                            if (!this._hasParent(tar, 'li')) {
4006
                                if (this._hasParent(tar, 'p')) {
4007
                                    p = this._getDoc().createElement('p');
4008
                                    p.innerHTML = '&nbsp;';
4009
                                    Dom.insertAfter(p, tar);
4010
                                    this._selectNode(p.firstChild);
4011
                                } else if (this._isElement(tar, 'body')) {
4012
                                    this.execCommand('insertparagraph', null);
4013
                                    var ps = this._getDoc().body.getElementsByTagName('p');
4014
                                    for (i = 0; i < ps.length; i++) {
4015
                                        if (ps[i].getAttribute('_moz_dirty') !== null) {
4016
                                            p = this._getDoc().createElement('p');
4017
                                            p.innerHTML = '&nbsp;';
4018
                                            Dom.insertAfter(p, ps[i]);
4019
                                            this._selectNode(p.firstChild);
4020
                                            ps[i].removeAttribute('_moz_dirty');
4021
                                        }
4022
                                    }
4023
                                } else {
4024
                                    doExec = true;
4025
                                    action = 'insertparagraph';
4026
                                }
4027
                                Event.stopEvent(ev);
4028
                            }
4029
                        }
4030
                        if (this.browser.webkit) {
4031
                            tar = this._getSelectedElement();
4032
                            if (!this._hasParent(tar, 'li')) {
4033
                                this.execCommand('insertparagraph', null);
4034
                                var divs = this._getDoc().body.getElementsByTagName('div');
4035
                                for (i = 0; i < divs.length; i++) {
4036
                                    if (!Dom.hasClass(divs[i], 'yui-wk-div')) {
4037
                                        Dom.addClass(divs[i], 'yui-wk-p');
4038
                                    }
4039
                                }
4040
                                Event.stopEvent(ev);
4041
                            }
4042
                        }
4043
                    } else {
4044
                        if (this.browser.webkit) {
4045
                            tar = this._getSelectedElement();
4046
                            if (!this._hasParent(tar, 'li')) {
4047
                                if (this.browser.webkit4) {
4048
                                    this.execCommand('insertlinebreak');
4049
                                } else {
4050
                                    this.execCommand('inserthtml', '<var id="yui-br"></var>');
4051
                                    var holder = this._getDoc().getElementById('yui-br'),
4052
                                        br = this._getDoc().createElement('br'),
4053
                                        caret = this._getDoc().createElement('span');
4054
 
4055
                                    holder.parentNode.replaceChild(br, holder);
4056
                                    caret.className = 'yui-non';
4057
                                    caret.innerHTML = '&nbsp;';
4058
                                    Dom.insertAfter(caret, br);
4059
                                    this._selectNode(caret);
4060
                                }
4061
                                Event.stopEvent(ev);
4062
                            }
4063
                        }
4064
                        if (this.browser.ie) {
4065
                            //Insert a <br> instead of a <p></p> in Internet Explorer
4066
                            _range = this._getRange();
4067
                            tar = this._getSelectedElement();
4068
                            if (!this._isElement(tar, 'li')) {
4069
                                if (_range) {
4070
                                    _range.pasteHTML('<br>');
4071
                                    _range.collapse(false);
4072
                                    _range.select();
4073
                                }
4074
                                Event.stopEvent(ev);
4075
                            }
4076
                        }
4077
                    }
4078
                    break;
4079
            }
4080
            if (this.browser.ie) {
4081
                this._listFix(ev);
4082
            }
4083
            if (doExec && action) {
4084
                this.execCommand(action, value);
4085
                Event.stopEvent(ev);
4086
                this.nodeChange();
4087
            }
4088
            this._storeUndo();
4089
            this.fireEvent('editorKeyDown', { type: 'editorKeyDown', target: this, ev: ev });
4090
        },
4091
        /**
4092
        * @private
4093
        * @property _fixListRunning
4094
        * @type Boolean
4095
        * @description Keeps more than one _fixListDupIds from running at the same time.
4096
        */
4097
        _fixListRunning: null,
4098
        /**
4099
        * @private
4100
        * @method _fixListDupIds
4101
        * @description Some browsers will duplicate the id of an LI when created in designMode.
4102
        * This method will fix the duplicate id issue. However it will only preserve the first element
4103
        * in the document list with the unique id.
4104
        */
4105
        _fixListDupIds: function() {
4106
            if (this._fixListRunning) {
4107
                return false;
4108
            }
4109
            if (this._getDoc()) {
4110
                this._fixListRunning = true;
4111
                var lis = this._getDoc().body.getElementsByTagName('li'),
4112
                    i = 0, ids = {};
4113
                for (i = 0; i < lis.length; i++) {
4114
                    if (lis[i].id) {
4115
                        if (ids[lis[i].id]) {
4116
                            lis[i].id = '';
4117
                        }
4118
                        ids[lis[i].id] = true;
4119
                    }
4120
                }
4121
                this._fixListRunning = false;
4122
            }
4123
        },
4124
        /**
4125
        * @private
4126
        * @method _listFix
4127
        * @param {Event} ev The event we are working on.
4128
        * @description Handles the Enter key, Tab Key and Shift + Tab keys for List Items.
4129
        */
4130
        _listFix: function(ev) {
4131
            var testLi = null, par = null, preContent = false, range = null;
4132
            //Enter Key
4133
            if (this.browser.webkit) {
4134
                if (ev.keyCode && (ev.keyCode == 13)) {
4135
                    if (this._hasParent(this._getSelectedElement(), 'li')) {
4136
                        var tar = this._hasParent(this._getSelectedElement(), 'li');
4137
                        if (tar.previousSibling) {
4138
                            if (tar.firstChild && (tar.firstChild.length == 1)) {
4139
                                this._selectNode(tar);
4140
                            }
4141
                        }
4142
                    }
4143
                }
4144
            }
4145
            //Shift + Tab Key
4146
            if (ev.keyCode && ((!this.browser.webkit3 && (ev.keyCode == 25)) || ((this.browser.webkit3 || !this.browser.webkit) && ((ev.keyCode == 9) && ev.shiftKey)))) {
4147
                testLi = this._getSelectedElement();
4148
                if (this._hasParent(testLi, 'li')) {
4149
                    testLi = this._hasParent(testLi, 'li');
4150
                    if (this._hasParent(testLi, 'ul') || this._hasParent(testLi, 'ol')) {
4151
                        par = this._hasParent(testLi, 'ul');
4152
                        if (!par) {
4153
                            par = this._hasParent(testLi, 'ol');
4154
                        }
4155
                        if (this._isElement(par.previousSibling, 'li')) {
4156
                            par.removeChild(testLi);
4157
                            par.parentNode.insertBefore(testLi, par.nextSibling);
4158
                            if (this.browser.ie) {
4159
                                range = this._getDoc().body.createTextRange();
4160
                                range.moveToElementText(testLi);
4161
                                range.collapse(false);
4162
                                range.select();
4163
                            }
4164
                            if (this.browser.webkit) {
4165
                                this._selectNode(testLi.firstChild);
4166
                            }
4167
                            Event.stopEvent(ev);
4168
                        }
4169
                    }
4170
                }
4171
            }
4172
            //Tab Key
4173
            if (ev.keyCode && ((ev.keyCode == 9) && (!ev.shiftKey))) {
4174
                var preLi = this._getSelectedElement();
4175
                if (this._hasParent(preLi, 'li')) {
4176
                    preContent = this._hasParent(preLi, 'li').innerHTML;
4177
                }
4178
                if (this.browser.webkit) {
4179
                    this._getDoc().execCommand('inserttext', false, '\t');
4180
                }
4181
                testLi = this._getSelectedElement();
4182
                if (this._hasParent(testLi, 'li')) {
4183
                    par = this._hasParent(testLi, 'li');
4184
                    var newUl = this._getDoc().createElement(par.parentNode.tagName.toLowerCase());
4185
                    if (this.browser.webkit) {
4186
                        var span = Dom.getElementsByClassName('Apple-tab-span', 'span', par);
4187
                        //Remove the span element that Safari puts in
4188
                        if (span[0]) {
4189
                            par.removeChild(span[0]);
4190
                            par.innerHTML = Lang.trim(par.innerHTML);
4191
                            //Put the HTML from the LI into this new LI
4192
                            if (preContent) {
4193
                                par.innerHTML = '<span class="yui-non">' + preContent + '</span>&nbsp;';
4194
                            } else {
4195
                                par.innerHTML = '<span class="yui-non">&nbsp;</span>&nbsp;';
4196
                            }
4197
                        }
4198
                    } else {
4199
                        if (preContent) {
4200
                            par.innerHTML = preContent + '&nbsp;';
4201
                        } else {
4202
                            par.innerHTML = '&nbsp;';
4203
                        }
4204
                    }
4205
 
4206
                    par.parentNode.replaceChild(newUl, par);
4207
                    newUl.appendChild(par);
4208
                    if (this.browser.webkit) {
4209
                        this._getSelection().setBaseAndExtent(par.firstChild, 1, par.firstChild, par.firstChild.innerText.length);
4210
                        if (!this.browser.webkit3) {
4211
                            par.parentNode.parentNode.style.display = 'list-item';
4212
                            setTimeout(function() {
4213
                                par.parentNode.parentNode.style.display = 'block';
4214
                            }, 1);
4215
                        }
4216
                    } else if (this.browser.ie) {
4217
                        range = this._getDoc().body.createTextRange();
4218
                        range.moveToElementText(par);
4219
                        range.collapse(false);
4220
                        range.select();
4221
                    } else {
4222
                        this._selectNode(par);
4223
                    }
4224
                    Event.stopEvent(ev);
4225
                }
4226
                if (this.browser.webkit) {
4227
                    Event.stopEvent(ev);
4228
                }
4229
                this.nodeChange();
4230
            }
4231
        },
4232
        /**
4233
        * @method nodeChange
4234
        * @param {Boolean} force Optional paramenter to skip the threshold counter
4235
        * @description Handles setting up the toolbar buttons, getting the Dom path, fixing nodes.
4236
        */
4237
        nodeChange: function(force) {
4238
            var NCself = this;
4239
            this._storeUndo();
4240
            if (this.get('nodeChangeDelay')) {
4241
                this._nodeChangeDelayTimer = window.setTimeout(function() {
4242
                    NCself._nodeChangeDelayTimer = null;
4243
                    NCself._nodeChange.apply(NCself, arguments);
4244
                }, 0);
4245
            } else {
4246
                this._nodeChange();
4247
            }
4248
        },
4249
        /**
4250
        * @private
4251
        * @method _nodeChange
4252
        * @param {Boolean} force Optional paramenter to skip the threshold counter
4253
        * @description Fired from nodeChange in a setTimeout.
4254
        */
4255
        _nodeChange: function(force) {
4256
            var threshold = parseInt(this.get('nodeChangeThreshold'), 10),
4257
                thisNodeChange = Math.round(new Date().getTime() / 1000),
4258
                self = this;
4259
 
4260
            if (force === true) {
4261
                this._lastNodeChange = 0;
4262
            }
4263
 
4264
            if ((this._lastNodeChange + threshold) < thisNodeChange) {
4265
                if (this._fixNodesTimer === null) {
4266
                    this._fixNodesTimer = window.setTimeout(function() {
4267
                        self._fixNodes.call(self);
4268
                        self._fixNodesTimer = null;
4269
                    }, 0);
4270
                }
4271
            }
4272
            this._lastNodeChange = thisNodeChange;
4273
            if (this.currentEvent) {
4274
                try {
4275
                    this._lastNodeChangeEvent = this.currentEvent.type;
4276
                } catch (e) {}
4277
            }
4278
 
4279
            var beforeNodeChange = this.fireEvent('beforeNodeChange', { type: 'beforeNodeChange', target: this });
4280
            if (beforeNodeChange === false) {
4281
                return false;
4282
            }
4283
            if (this.get('dompath')) {
4284
                window.setTimeout(function() {
4285
                    self._writeDomPath.call(self);
4286
                }, 0);
4287
            }
4288
            //Check to see if we are disabled before continuing
4289
            if (!this.get('disabled')) {
4290
                if (this.STOP_NODE_CHANGE) {
4291
                    //Reset this var for next action
4292
                    this.STOP_NODE_CHANGE = false;
4293
                    return false;
4294
                } else {
4295
                    var sel = this._getSelection(),
4296
                        range = this._getRange(),
4297
                        el = this._getSelectedElement(),
4298
                        fn_button = this.toolbar.getButtonByValue('fontname'),
4299
                        fs_button = this.toolbar.getButtonByValue('fontsize'),
4300
                        undo_button = this.toolbar.getButtonByValue('undo'),
4301
                        redo_button = this.toolbar.getButtonByValue('redo');
4302
 
4303
                    //Handle updating the toolbar with active buttons
4304
                    var _ex = {};
4305
                    if (this._lastButton) {
4306
                        _ex[this._lastButton.id] = true;
4307
                        //this._lastButton = null;
4308
                    }
4309
                    if (!this._isElement(el, 'body')) {
4310
                        if (fn_button) {
4311
                            _ex[fn_button.get('id')] = true;
4312
                        }
4313
                        if (fs_button) {
4314
                            _ex[fs_button.get('id')] = true;
4315
                        }
4316
                    }
4317
                    if (redo_button) {
4318
                        delete _ex[redo_button.get('id')];
4319
                    }
4320
                    this.toolbar.resetAllButtons(_ex);
4321
 
4322
                    //Handle disabled buttons
4323
                    for (var d = 0; d < this._disabled.length; d++) {
4324
                        var _button = this.toolbar.getButtonByValue(this._disabled[d]);
4325
                        if (_button && _button.get) {
4326
                            if (this._lastButton && (_button.get('id') === this._lastButton.id)) {
4327
                                //Skip
4328
                            } else {
4329
                                if (!this._hasSelection() && !this.get('insert')) {
4330
                                    switch (this._disabled[d]) {
4331
                                        case 'fontname':
4332
                                        case 'fontsize':
4333
                                            break;
4334
                                        default:
4335
                                            //No Selection - disable
4336
                                            this.toolbar.disableButton(_button);
4337
                                    }
4338
                                } else {
4339
                                    if (!this._alwaysDisabled[this._disabled[d]]) {
4340
                                        this.toolbar.enableButton(_button);
4341
                                    }
4342
                                }
4343
                                if (!this._alwaysEnabled[this._disabled[d]]) {
4344
                                    this.toolbar.deselectButton(_button);
4345
                                }
4346
                            }
4347
                        }
4348
                    }
4349
                    var path = this._getDomPath();
4350
                    var tag = null, cmd = null;
4351
                    for (var i = 0; i < path.length; i++) {
4352
                        tag = path[i].tagName.toLowerCase();
4353
                        if (path[i].getAttribute('tag')) {
4354
                            tag = path[i].getAttribute('tag').toLowerCase();
4355
                        }
4356
                        cmd = this._tag2cmd[tag];
4357
                        if (cmd === undefined) {
4358
                            cmd = [];
4359
                        }
4360
                        if (!Lang.isArray(cmd)) {
4361
                            cmd = [cmd];
4362
                        }
4363
 
4364
                        //Bold and Italic styles
4365
                        if (path[i].style.fontWeight.toLowerCase() == 'bold') {
4366
                            cmd[cmd.length] = 'bold';
4367
                        }
4368
                        if (path[i].style.fontStyle.toLowerCase() == 'italic') {
4369
                            cmd[cmd.length] = 'italic';
4370
                        }
4371
                        if (path[i].style.textDecoration.toLowerCase() == 'underline') {
4372
                            cmd[cmd.length] = 'underline';
4373
                        }
4374
                        if (path[i].style.textDecoration.toLowerCase() == 'line-through') {
4375
                            cmd[cmd.length] = 'strikethrough';
4376
                        }
4377
                        if (cmd.length > 0) {
4378
                            for (var j = 0; j < cmd.length; j++) {
4379
                                this.toolbar.selectButton(cmd[j]);
4380
                                this.toolbar.enableButton(cmd[j]);
4381
                            }
4382
                        }
4383
                        //Handle Alignment
4384
                        switch (path[i].style.textAlign.toLowerCase()) {
4385
                            case 'left':
4386
                            case 'right':
4387
                            case 'center':
4388
                            case 'justify':
4389
                                var alignType = path[i].style.textAlign.toLowerCase();
4390
                                if (path[i].style.textAlign.toLowerCase() == 'justify') {
4391
                                    alignType = 'full';
4392
                                }
4393
                                this.toolbar.selectButton('justify' + alignType);
4394
                                this.toolbar.enableButton('justify' + alignType);
4395
                                break;
4396
                        }
4397
                    }
4398
                    //After for loop
4399
 
4400
                    //Reset Font Family and Size to the inital configs
4401
                    if (fn_button) {
4402
                        var family = fn_button._configs.label._initialConfig.value;
4403
                        fn_button.set('label', '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>');
4404
                        this._updateMenuChecked('fontname', family);
4405
                    }
4406
 
4407
                    if (fs_button) {
4408
                        fs_button.set('label', fs_button._configs.label._initialConfig.value);
4409
                    }
4410
 
4411
                    var hd_button = this.toolbar.getButtonByValue('heading');
4412
                    if (hd_button) {
4413
                        hd_button.set('label', hd_button._configs.label._initialConfig.value);
4414
                        this._updateMenuChecked('heading', 'none');
4415
                    }
4416
                    var img_button = this.toolbar.getButtonByValue('insertimage');
4417
                    if (img_button && this.currentWindow && (this.currentWindow.name == 'insertimage')) {
4418
                        this.toolbar.disableButton(img_button);
4419
                    }
4420
                    if (this._lastButton && this._lastButton.isSelected) {
4421
                        this.toolbar.deselectButton(this._lastButton.id);
4422
                    }
4423
                    this._undoNodeChange();
4424
                }
4425
            }
4426
 
4427
            this.fireEvent('afterNodeChange', { type: 'afterNodeChange', target: this });
4428
        },
4429
        /**
4430
        * @private
4431
        * @method _updateMenuChecked
4432
        * @param {Object} button The command identifier of the button you want to check
4433
        * @param {String} value The value of the menu item you want to check
4434
        * @param {<a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>} The Toolbar instance the button belongs to (defaults to this.toolbar)
4435
        * @description Gets the menu from a button instance, if the menu is not rendered it will render it. It will then search the menu for the specified value, unchecking all other items and checking the specified on.
4436
        */
4437
        _updateMenuChecked: function(button, value, tbar) {
4438
            if (!tbar) {
4439
                tbar = this.toolbar;
4440
            }
4441
            var _button = tbar.getButtonByValue(button);
4442
            _button.checkValue(value);
4443
        },
4444
        /**
4445
        * @private
4446
        * @method _handleToolbarClick
4447
        * @param {Event} ev The event that triggered the button click
4448
        * @description This is an event handler attached to the Toolbar's buttonClick event. It will fire execCommand with the command identifier from the Toolbar Button.
4449
        */
4450
        _handleToolbarClick: function(ev) {
4451
            var value = '';
4452
            var str = '';
4453
            var cmd = ev.button.value;
4454
            if (ev.button.menucmd) {
4455
                value = cmd;
4456
                cmd = ev.button.menucmd;
4457
            }
4458
            this._lastButton = ev.button;
4459
            if (this.STOP_EXEC_COMMAND) {
4460
                this.STOP_EXEC_COMMAND = false;
4461
                return false;
4462
            } else {
4463
                this.execCommand(cmd, value);
4464
                if (!this.browser.webkit) {
4465
                     var Fself = this;
4466
                     setTimeout(function() {
4467
                         Fself.focus.call(Fself);
4468
                     }, 5);
4469
                 }
4470
            }
4471
            Event.stopEvent(ev);
4472
        },
4473
        /**
4474
        * @private
4475
        * @method _setupAfterElement
4476
        * @description Creates the accessibility h2 header and places it after the iframe in the Dom for navigation.
4477
        */
4478
        _setupAfterElement: function() {
4479
            if (!this.beforeElement) {
4480
                this.beforeElement = document.createElement('h2');
4481
                this.beforeElement.className = 'yui-editor-skipheader';
4482
                this.beforeElement.tabIndex = '-1';
4483
                this.beforeElement.innerHTML = this.STR_BEFORE_EDITOR;
4484
                this.get('element_cont').get('firstChild').insertBefore(this.beforeElement, this.toolbar.get('nextSibling'));
4485
            }
4486
            if (!this.afterElement) {
4487
                this.afterElement = document.createElement('h2');
4488
                this.afterElement.className = 'yui-editor-skipheader';
4489
                this.afterElement.tabIndex = '-1';
4490
                this.afterElement.innerHTML = this.STR_LEAVE_EDITOR;
4491
                this.get('element_cont').get('firstChild').appendChild(this.afterElement);
4492
            }
4493
        },
4494
        /**
4495
        * @private
4496
        * @method _disableEditor
4497
        * @param {Boolean} disabled Pass true to disable, false to enable
4498
        * @description Creates a mask to place over the Editor.
4499
        */
4500
        _disableEditor: function(disabled) {
4501
            var iframe, par, html, height;
4502
            if (!this.get('disabled_iframe')) {
4503
                iframe = this._createIframe();
4504
                iframe.set('id', 'disabled_' + this.get('iframe').get('id'));
4505
                iframe.setStyle('height', '100%');
4506
                iframe.setStyle('display', 'none');
4507
                iframe.setStyle('visibility', 'visible');
4508
                this.set('disabled_iframe', iframe);
4509
                par = this.get('iframe').get('parentNode');
4510
                par.appendChild(iframe.get('element'));
4511
            }
4512
            if (!iframe) {
4513
                iframe = this.get('disabled_iframe');
4514
            }
4515
            if (disabled) {
4516
                this._orgIframe = this.get('iframe');
4517
 
4518
                if (this.toolbar) {
4519
                    this.toolbar.set('disabled', true);
4520
                }
4521
 
4522
                html = this.getEditorHTML();
4523
                height = this.get('iframe').get('offsetHeight');
4524
                iframe.setStyle('visibility', '');
4525
                iframe.setStyle('position', '');
4526
                iframe.setStyle('top', '');
4527
                iframe.setStyle('left', '');
4528
                this._orgIframe.setStyle('visibility', 'hidden');
4529
                this._orgIframe.setStyle('position', 'absolute');
4530
                this._orgIframe.setStyle('top', '-99999px');
4531
                this._orgIframe.setStyle('left', '-99999px');
4532
                this.set('iframe', iframe);
4533
                this._setInitialContent(true);
4534
 
4535
                if (!this._mask) {
4536
                    this._mask = document.createElement('DIV');
4537
                    Dom.addClass(this._mask, 'yui-editor-masked');
4538
                    if (this.browser.ie) {
4539
                        this._mask.style.height = height + 'px';
4540
                    }
4541
                    this.get('iframe').get('parentNode').appendChild(this._mask);
4542
                }
4543
                this.on('editorContentReloaded', function() {
4544
                    this._getDoc().body._rteLoaded = false;
4545
                    this.setEditorHTML(html);
4546
                    iframe.setStyle('display', 'block');
4547
                    this.unsubscribeAll('editorContentReloaded');
4548
                });
4549
            } else {
4550
                if (this._mask) {
4551
                    this._mask.parentNode.removeChild(this._mask);
4552
                    this._mask = null;
4553
                    if (this.toolbar) {
4554
                        this.toolbar.set('disabled', false);
4555
                    }
4556
                    iframe.setStyle('visibility', 'hidden');
4557
                    iframe.setStyle('position', 'absolute');
4558
                    iframe.setStyle('top', '-99999px');
4559
                    iframe.setStyle('left', '-99999px');
4560
                    this._orgIframe.setStyle('visibility', '');
4561
                    this._orgIframe.setStyle('position', '');
4562
                    this._orgIframe.setStyle('top', '');
4563
                    this._orgIframe.setStyle('left', '');
4564
                    this.set('iframe', this._orgIframe);
4565
 
4566
                    this.focus();
4567
                    var self = this;
4568
                    window.setTimeout(function() {
4569
                        self.nodeChange.call(self);
4570
                    }, 100);
4571
                }
4572
            }
4573
        },
4574
        /**
4575
        * @property SEP_DOMPATH
4576
        * @description The value to place in between the Dom path items
4577
        * @type String
4578
        */
4579
        SEP_DOMPATH: '<',
4580
        /**
4581
        * @property STR_LEAVE_EDITOR
4582
        * @description The accessibility string for the element after the iFrame
4583
        * @type String
4584
        */
4585
        STR_LEAVE_EDITOR: 'You have left the Rich Text Editor.',
4586
        /**
4587
        * @property STR_BEFORE_EDITOR
4588
        * @description The accessibility string for the element before the iFrame
4589
        * @type String
4590
        */
4591
        STR_BEFORE_EDITOR: 'This text field can contain stylized text and graphics. To cycle through all formatting options, use the keyboard shortcut Shift + Escape to place focus on the toolbar and navigate between options with your arrow keys. To exit this text editor use the Escape key and continue tabbing. <h4>Common formatting keyboard shortcuts:</h4><ul><li>Control Shift B sets text to bold</li> <li>Control Shift I sets text to italic</li> <li>Control Shift U underlines text</li> <li>Control Shift L adds an HTML link</li></ul>',
4592
        /**
4593
        * @property STR_TITLE
4594
        * @description The Title of the HTML document that is created in the iFrame
4595
        * @type String
4596
        */
4597
        STR_TITLE: 'Rich Text Area.',
4598
        /**
4599
        * @property STR_IMAGE_HERE
4600
        * @description The text to place in the URL textbox when using the blankimage.
4601
        * @type String
4602
        */
4603
        STR_IMAGE_HERE: 'Image URL Here',
4604
        /**
4605
        * @property STR_IMAGE_URL
4606
        * @description The label string for Image URL
4607
        * @type String
4608
        */
4609
        STR_IMAGE_URL: 'Image URL',
4610
        /**
4611
        * @property STR_LINK_URL
4612
        * @description The label string for the Link URL.
4613
        * @type String
4614
        */
4615
        STR_LINK_URL: 'Link URL',
4616
        /**
4617
        * @protected
4618
        * @property STOP_EXEC_COMMAND
4619
        * @description Set to true when you want the default execCommand function to not process anything
4620
        * @type Boolean
4621
        */
4622
        STOP_EXEC_COMMAND: false,
4623
        /**
4624
        * @protected
4625
        * @property STOP_NODE_CHANGE
4626
        * @description Set to true when you want the default nodeChange function to not process anything
4627
        * @type Boolean
4628
        */
4629
        STOP_NODE_CHANGE: false,
4630
        /**
4631
        * @protected
4632
        * @property CLASS_NOEDIT
4633
        * @description CSS class applied to elements that are not editable.
4634
        * @type String
4635
        */
4636
        CLASS_NOEDIT: 'yui-noedit',
4637
        /**
4638
        * @protected
4639
        * @property CLASS_CONTAINER
4640
        * @description Default CSS class to apply to the editors container element
4641
        * @type String
4642
        */
4643
        CLASS_CONTAINER: 'yui-editor-container',
4644
        /**
4645
        * @protected
4646
        * @property CLASS_EDITABLE
4647
        * @description Default CSS class to apply to the editors iframe element
4648
        * @type String
4649
        */
4650
        CLASS_EDITABLE: 'yui-editor-editable',
4651
        /**
4652
        * @protected
4653
        * @property CLASS_EDITABLE_CONT
4654
        * @description Default CSS class to apply to the editors iframe's parent element
4655
        * @type String
4656
        */
4657
        CLASS_EDITABLE_CONT: 'yui-editor-editable-container',
4658
        /**
4659
        * @protected
4660
        * @property CLASS_PREFIX
4661
        * @description Default prefix for dynamically created class names
4662
        * @type String
4663
        */
4664
        CLASS_PREFIX: 'yui-editor',
4665
        /**
4666
        * @property browser
4667
        * @description Standard browser detection
4668
        * @type Object
4669
        */
4670
        browser: function() {
4671
            var br = YAHOO.env.ua;
4672
            //Check for webkit3
4673
            if (br.webkit >= 420) {
4674
                br.webkit3 = br.webkit;
4675
            } else {
4676
                br.webkit3 = 0;
4677
            }
4678
            if (br.webkit >= 530) {
4679
                br.webkit4 = br.webkit;
4680
            } else {
4681
                br.webkit4 = 0;
4682
            }
4683
            br.mac = false;
4684
            //Check for Mac
4685
            if (navigator.userAgent.indexOf('Macintosh') !== -1) {
4686
                br.mac = true;
4687
            }
4688
 
4689
            return br;
4690
        }(),
4691
        /**
4692
        * @method init
4693
        * @description The Editor class' initialization method
4694
        */
4695
        init: function(p_oElement, p_oAttributes) {
4696
 
4697
            if (!this._defaultToolbar) {
4698
                this._defaultToolbar = {
4699
                    collapse: true,
4700
                    titlebar: 'Text Editing Tools',
4701
                    draggable: false,
4702
                    buttons: [
4703
                        { group: 'fontstyle', label: 'Font Name and Size',
4704
                            buttons: [
4705
                                { type: 'select', label: 'Arial', value: 'fontname', disabled: true,
4706
                                    menu: [
4707
                                        { text: 'Arial', checked: true },
4708
                                        { text: 'Arial Black' },
4709
                                        { text: 'Comic Sans MS' },
4710
                                        { text: 'Courier New' },
4711
                                        { text: 'Lucida Console' },
4712
                                        { text: 'Tahoma' },
4713
                                        { text: 'Times New Roman' },
4714
                                        { text: 'Trebuchet MS' },
4715
                                        { text: 'Verdana' }
4716
                                    ]
4717
                                },
4718
                                { type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
4719
                            ]
4720
                        },
4721
                        { type: 'separator' },
4722
                        { group: 'textstyle', label: 'Font Style',
4723
                            buttons: [
4724
                                { type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
4725
                                { type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
4726
                                { type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
4727
                                { type: 'push', label: 'Strike Through', value: 'strikethrough' },
4728
                                { type: 'separator' },
4729
                                { type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
4730
                                { type: 'color', label: 'Background Color', value: 'backcolor', disabled: true }
4731
 
4732
                            ]
4733
                        },
4734
                        { type: 'separator' },
4735
                        { group: 'indentlist', label: 'Lists',
4736
                            buttons: [
4737
                                { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
4738
                                { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
4739
                            ]
4740
                        },
4741
                        { type: 'separator' },
4742
                        { group: 'insertitem', label: 'Insert Item',
4743
                            buttons: [
4744
                                { type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
4745
                                { type: 'push', label: 'Insert Image', value: 'insertimage' }
4746
                            ]
4747
                        }
4748
                    ]
4749
                };
4750
            }
4751
 
4752
            YAHOO.widget.SimpleEditor.superclass.init.call(this, p_oElement, p_oAttributes);
4753
            YAHOO.widget.EditorInfo._instances[this.get('id')] = this;
4754
 
4755
 
4756
            this.currentElement = [];
4757
            this.on('contentReady', function() {
4758
                this.DOMReady = true;
4759
                this.fireQueue();
4760
            }, this, true);
4761
 
4762
        },
4763
        /**
4764
        * @method initAttributes
4765
        * @description Initializes all of the configuration attributes used to create
4766
        * the editor.
4767
        * @param {Object} attr Object literal specifying a set of
4768
        * configuration attributes used to create the editor.
4769
        */
4770
        initAttributes: function(attr) {
4771
            YAHOO.widget.SimpleEditor.superclass.initAttributes.call(this, attr);
4772
            var self = this;
4773
 
4774
            /**
4775
            * @config setDesignMode
4776
            * @description Should the Editor set designMode on the document. Default: true.
4777
            * @default true
4778
            * @type Boolean
4779
            */
4780
            this.setAttributeConfig('setDesignMode', {
4781
                value: ((attr.setDesignMode === false) ? false : true)
4782
            });
4783
            /**
4784
            * @config nodeChangeDelay
4785
            * @description Do we wrap the nodeChange method in a timeout for performance. Default: true.
4786
            * @default true
4787
            * @type Boolean
4788
            */
4789
            this.setAttributeConfig('nodeChangeDelay', {
4790
                value: ((attr.nodeChangeDelay === false) ? false : true)
4791
            });
4792
            /**
4793
            * @config maxUndo
4794
            * @description The max number of undo levels to store.
4795
            * @default 30
4796
            * @type Number
4797
            */
4798
            this.setAttributeConfig('maxUndo', {
4799
                writeOnce: true,
4800
                value: attr.maxUndo || 30
4801
            });
4802
 
4803
            /**
4804
            * @config ptags
4805
            * @description If true, the editor uses &lt;P&gt; tags instead of &lt;br&gt; tags. (Use Shift + Enter to get a &lt;br&gt;)
4806
            * @default false
4807
            * @type Boolean
4808
            */
4809
            this.setAttributeConfig('ptags', {
4810
                writeOnce: true,
4811
                value: attr.ptags || false
4812
            });
4813
            /**
4814
            * @config insert
4815
            * @description If true, selection is not required for: fontname, fontsize, forecolor, backcolor.
4816
            * @default false
4817
            * @type Boolean
4818
            */
4819
            this.setAttributeConfig('insert', {
4820
                writeOnce: true,
4821
                value: attr.insert || false,
4822
                method: function(insert) {
4823
                    if (insert) {
4824
                        var buttons = {
4825
                            fontname: true,
4826
                            fontsize: true,
4827
                            forecolor: true,
4828
                            backcolor: true
4829
                        };
4830
                        var tmp = this._defaultToolbar.buttons;
4831
                        for (var i = 0; i < tmp.length; i++) {
4832
                            if (tmp[i].buttons) {
4833
                                for (var a = 0; a < tmp[i].buttons.length; a++) {
4834
                                    if (tmp[i].buttons[a].value) {
4835
                                        if (buttons[tmp[i].buttons[a].value]) {
4836
                                            delete tmp[i].buttons[a].disabled;
4837
                                        }
4838
                                    }
4839
                                }
4840
                            }
4841
                        }
4842
                    }
4843
                }
4844
            });
4845
            /**
4846
            * @config container
4847
            * @description Used when dynamically creating the Editor from Javascript with no default textarea.
4848
            * We will create one and place it in this container. If no container is passed we will append to document.body.
4849
            * @default false
4850
            * @type HTMLElement
4851
            */
4852
            this.setAttributeConfig('container', {
4853
                writeOnce: true,
4854
                value: attr.container || false
4855
            });
4856
            /**
4857
            * @config plainText
4858
            * @description Process the inital textarea data as if it was plain text. Accounting for spaces, tabs and line feeds.
4859
            * @default false
4860
            * @type Boolean
4861
            */
4862
            this.setAttributeConfig('plainText', {
4863
                writeOnce: true,
4864
                value: attr.plainText || false
4865
            });
4866
            /**
4867
            * @private
4868
            * @config iframe
4869
            * @description Internal config for holding the iframe element.
4870
            * @default null
4871
            * @type HTMLElement
4872
            */
4873
            this.setAttributeConfig('iframe', {
4874
                value: null
4875
            });
4876
            /**
4877
            * @private
4878
            * @config disabled_iframe
4879
            * @description Internal config for holding the iframe element used when disabling the Editor.
4880
            * @default null
4881
            * @type HTMLElement
4882
            */
4883
            this.setAttributeConfig('disabled_iframe', {
4884
                value: null
4885
            });
4886
            /**
4887
            * @private
4888
            * @depreciated - No longer used, should use this.get('element')
4889
            * @config textarea
4890
            * @description Internal config for holding the textarea element (replaced with element).
4891
            * @default null
4892
            * @type HTMLElement
4893
            */
4894
            this.setAttributeConfig('textarea', {
4895
                value: null,
4896
                writeOnce: true
4897
            });
4898
            /**
4899
            * @config nodeChangeThreshold
4900
            * @description The number of seconds that need to be in between nodeChange processing
4901
            * @default 3
4902
            * @type Number
4903
            */
4904
            this.setAttributeConfig('nodeChangeThreshold', {
4905
                value: attr.nodeChangeThreshold || 3,
4906
                validator: YAHOO.lang.isNumber
4907
            });
4908
            /**
4909
            * @config allowNoEdit
4910
            * @description Should the editor check for non-edit fields. It should be noted that this technique is not perfect. If the user does the right things, they will still be able to make changes.
4911
            * Such as highlighting an element below and above the content and hitting a toolbar button or a shortcut key.
4912
            * @default false
4913
            * @type Boolean
4914
            */
4915
            this.setAttributeConfig('allowNoEdit', {
4916
                value: attr.allowNoEdit || false,
4917
                validator: YAHOO.lang.isBoolean
4918
            });
4919
            /**
4920
            * @config limitCommands
4921
            * @description Should the Editor limit the allowed execCommands to the ones available in the toolbar. If true, then execCommand and keyboard shortcuts will fail if they are not defined in the toolbar.
4922
            * @default false
4923
            * @type Boolean
4924
            */
4925
            this.setAttributeConfig('limitCommands', {
4926
                value: attr.limitCommands || false,
4927
                validator: YAHOO.lang.isBoolean
4928
            });
4929
            /**
4930
            * @config element_cont
4931
            * @description Internal config for the editors container
4932
            * @default false
4933
            * @type HTMLElement
4934
            */
4935
            this.setAttributeConfig('element_cont', {
4936
                value: attr.element_cont
4937
            });
4938
            /**
4939
            * @private
4940
            * @config editor_wrapper
4941
            * @description The outter wrapper for the entire editor.
4942
            * @default null
4943
            * @type HTMLElement
4944
            */
4945
            this.setAttributeConfig('editor_wrapper', {
4946
                value: attr.editor_wrapper || null,
4947
                writeOnce: true
4948
            });
4949
            /**
4950
            * @attribute height
4951
            * @description The height of the editor iframe container, not including the toolbar..
4952
            * @default Best guessed size of the textarea, for best results use CSS to style the height of the textarea or pass it in as an argument
4953
            * @type String
4954
            */
4955
            this.setAttributeConfig('height', {
4956
                value: attr.height || Dom.getStyle(self.get('element'), 'height'),
4957
                method: function(height) {
4958
                    if (this._rendered) {
4959
                        //We have been rendered, change the height
4960
                        if (this.get('animate')) {
4961
                            var anim = new YAHOO.util.Anim(this.get('iframe').get('parentNode'), {
4962
                                height: {
4963
                                    to: parseInt(height, 10)
4964
                                }
4965
                            }, 0.5);
4966
                            anim.animate();
4967
                        } else {
4968
                            Dom.setStyle(this.get('iframe').get('parentNode'), 'height', height);
4969
                        }
4970
                    }
4971
                }
4972
            });
4973
            /**
4974
            * @config autoHeight
4975
            * @description Remove the scrollbars from the edit area and resize it to fit the content. It will not go any lower than the current config height.
4976
            * @default false
4977
            * @type Boolean || Number
4978
            */
4979
            this.setAttributeConfig('autoHeight', {
4980
                value: attr.autoHeight || false,
4981
                method: function(a) {
4982
                    if (a) {
4983
                        if (this.get('iframe')) {
4984
                            this.get('iframe').get('element').setAttribute('scrolling', 'no');
4985
                        }
4986
                        this.on('afterNodeChange', this._handleAutoHeight, this, true);
4987
                        this.on('editorKeyDown', this._handleAutoHeight, this, true);
4988
                        this.on('editorKeyPress', this._handleAutoHeight, this, true);
4989
                    } else {
4990
                        if (this.get('iframe')) {
4991
                            this.get('iframe').get('element').setAttribute('scrolling', 'auto');
4992
                        }
4993
                        this.unsubscribe('afterNodeChange', this._handleAutoHeight);
4994
                        this.unsubscribe('editorKeyDown', this._handleAutoHeight);
4995
                        this.unsubscribe('editorKeyPress', this._handleAutoHeight);
4996
                    }
4997
                }
4998
            });
4999
            /**
5000
            * @attribute width
5001
            * @description The width of the editor container.
5002
            * @default Best guessed size of the textarea, for best results use CSS to style the width of the textarea or pass it in as an argument
5003
            * @type String
5004
            */
5005
            this.setAttributeConfig('width', {
5006
                value: attr.width || Dom.getStyle(this.get('element'), 'width'),
5007
                method: function(width) {
5008
                    if (this._rendered) {
5009
                        //We have been rendered, change the width
5010
                        if (this.get('animate')) {
5011
                            var anim = new YAHOO.util.Anim(this.get('element_cont').get('element'), {
5012
                                width: {
5013
                                    to: parseInt(width, 10)
5014
                                }
5015
                            }, 0.5);
5016
                            anim.animate();
5017
                        } else {
5018
                            this.get('element_cont').setStyle('width', width);
5019
                        }
5020
                    }
5021
                }
5022
            });
5023
 
5024
            /**
5025
            * @attribute blankimage
5026
            * @description The URL for the image placeholder to put in when inserting an image.
5027
            * @default The yahooapis.com address for the current release + 'assets/blankimage.png'
5028
            * @type String
5029
            */
5030
            this.setAttributeConfig('blankimage', {
5031
                value: attr.blankimage || this._getBlankImage()
5032
            });
5033
            /**
5034
            * @attribute css
5035
            * @description The Base CSS used to format the content of the editor
5036
            * @default <code><pre>html {
5037
                height: 95%;
5038
            }
5039
            body {
5040
                height: 100%;
5041
                padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;
5042
            }
5043
            a {
5044
                color: blue;
5045
                text-decoration: underline;
5046
                cursor: pointer;
5047
            }
5048
            .warning-localfile {
5049
                border-bottom: 1px dashed red !important;
5050
            }
5051
            .yui-busy {
5052
                cursor: wait !important;
5053
            }
5054
            img.selected { //Safari image selection
5055
                border: 2px dotted #808080;
5056
            }
5057
            img {
5058
                cursor: pointer !important;
5059
                border: none;
5060
            }
5061
            </pre></code>
5062
            * @type String
5063
            */
5064
            this.setAttributeConfig('css', {
5065
                value: attr.css || this._defaultCSS,
5066
                writeOnce: true
5067
            });
5068
            /**
5069
            * @attribute html
5070
            * @description The default HTML to be written to the iframe document before the contents are loaded (Note that the DOCTYPE attr will be added at render item)
5071
            * @default This HTML requires a few things if you are to override:
5072
                <p><code>{TITLE}, {CSS}, {HIDDEN_CSS}, {EXTRA_CSS}</code> and <code>{CONTENT}</code> need to be there, they are passed to YAHOO.lang.substitute to be replace with other strings.<p>
5073
                <p><code>onload="document.body._rteLoaded = true;"</code> : the onload statement must be there or the editor will not finish loading.</p>
5074
                <code>
5075
                <pre>
5076
                &lt;html&gt;
5077
                    &lt;head&gt;
5078
                        &lt;title&gt;{TITLE}&lt;/title&gt;
5079
                        &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
5080
                        &lt;style&gt;
5081
                        {CSS}
5082
                        &lt;/style&gt;
5083
                        &lt;style&gt;
5084
                        {HIDDEN_CSS}
5085
                        &lt;/style&gt;
5086
                        &lt;style&gt;
5087
                        {EXTRA_CSS}
5088
                        &lt;/style&gt;
5089
                    &lt;/head&gt;
5090
                &lt;body onload="document.body._rteLoaded = true;"&gt;
5091
                {CONTENT}
5092
                &lt;/body&gt;
5093
                &lt;/html&gt;
5094
                </pre>
5095
                </code>
5096
            * @type String
5097
            */
5098
            this.setAttributeConfig('html', {
5099
                value: attr.html || '<html><head><title>{TITLE}</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><base href="' + this._baseHREF + '"><style>{CSS}</style><style>{HIDDEN_CSS}</style><style>{EXTRA_CSS}</style></head><body onload="document.body._rteLoaded = true;">{CONTENT}</body></html>',
5100
                writeOnce: true
5101
            });
5102
 
5103
            /**
5104
            * @attribute extracss
5105
            * @description Extra user defined css to load after the default SimpleEditor CSS
5106
            * @default ''
5107
            * @type String
5108
            */
5109
            this.setAttributeConfig('extracss', {
5110
                value: attr.extracss || '',
5111
                writeOnce: true
5112
            });
5113
 
5114
            /**
5115
            * @attribute handleSubmit
5116
            * @description Config handles if the editor will attach itself to the textareas parent form's submit handler.
5117
            If it is set to true, the editor will attempt to attach a submit listener to the textareas parent form.
5118
            Then it will trigger the editors save handler and place the new content back into the text area before the form is submitted.
5119
            * @default false
5120
            * @type Boolean
5121
            */
5122
            this.setAttributeConfig('handleSubmit', {
5123
                value: attr.handleSubmit || false,
5124
                method: function(exec) {
5125
                    if (this.get('element').form) {
5126
                        if (!this._formButtons) {
5127
                            this._formButtons = [];
5128
                        }
5129
                        if (exec) {
5130
                            Event.on(this.get('element').form, 'submit', this._handleFormSubmit, this, true);
5131
                            var i = this.get('element').form.getElementsByTagName('input');
5132
                            for (var s = 0; s < i.length; s++) {
5133
                                var type = i[s].getAttribute('type');
5134
                                if (type && (type.toLowerCase() == 'submit')) {
5135
                                    Event.on(i[s], 'click', this._handleFormButtonClick, this, true);
5136
                                    this._formButtons[this._formButtons.length] = i[s];
5137
                                }
5138
                            }
5139
                        } else {
5140
                            Event.removeListener(this.get('element').form, 'submit', this._handleFormSubmit);
5141
                            if (this._formButtons) {
5142
                                Event.removeListener(this._formButtons, 'click', this._handleFormButtonClick);
5143
                            }
5144
                        }
5145
                    }
5146
                }
5147
            });
5148
            /**
5149
            * @attribute disabled
5150
            * @description This will toggle the editor's disabled state. When the editor is disabled, designMode is turned off and a mask is placed over the iframe so no interaction can take place.
5151
            All Toolbar buttons are also disabled so they cannot be used.
5152
            * @default false
5153
            * @type Boolean
5154
            */
5155
 
5156
            this.setAttributeConfig('disabled', {
5157
                value: false,
5158
                method: function(disabled) {
5159
                    if (this._rendered) {
5160
                        this._disableEditor(disabled);
5161
                    }
5162
                }
5163
            });
5164
            /**
5165
            * @config saveEl
5166
            * @description When save HTML is called, this element will be updated as well as the source of data.
5167
            * @default element
5168
            * @type HTMLElement
5169
            */
5170
            this.setAttributeConfig('saveEl', {
5171
                value: this.get('element')
5172
            });
5173
            /**
5174
            * @config toolbar_cont
5175
            * @description Internal config for the toolbars container
5176
            * @default false
5177
            * @type Boolean
5178
            */
5179
            this.setAttributeConfig('toolbar_cont', {
5180
                value: null,
5181
                writeOnce: true
5182
            });
5183
            /**
5184
            * @attribute toolbar
5185
            * @description The default toolbar config.
5186
            * @type Object
5187
            */
5188
            this.setAttributeConfig('toolbar', {
5189
                value: attr.toolbar || this._defaultToolbar,
5190
                writeOnce: true,
5191
                method: function(toolbar) {
5192
                    if (!toolbar.buttonType) {
5193
                        toolbar.buttonType = this._defaultToolbar.buttonType;
5194
                    }
5195
                    this._defaultToolbar = toolbar;
5196
                }
5197
            });
5198
            /**
5199
            * @attribute animate
5200
            * @description Should the editor animate window movements
5201
            * @default false unless Animation is found, then true
5202
            * @type Boolean
5203
            */
5204
            this.setAttributeConfig('animate', {
5205
                value: ((attr.animate) ? ((YAHOO.util.Anim) ? true : false) : false),
5206
                validator: function(value) {
5207
                    var ret = true;
5208
                    if (!YAHOO.util.Anim) {
5209
                        ret = false;
5210
                    }
5211
                    return ret;
5212
                }
5213
            });
5214
            /**
5215
            * @config panel
5216
            * @description A reference to the panel we are using for windows.
5217
            * @default false
5218
            * @type Boolean
5219
            */
5220
            this.setAttributeConfig('panel', {
5221
                value: null,
5222
                writeOnce: true,
5223
                validator: function(value) {
5224
                    var ret = true;
5225
                    if (!YAHOO.widget.Overlay) {
5226
                        ret = false;
5227
                    }
5228
                    return ret;
5229
                }
5230
            });
5231
            /**
5232
            * @attribute focusAtStart
5233
            * @description Should we focus the window when the content is ready?
5234
            * @default false
5235
            * @type Boolean
5236
            */
5237
            this.setAttributeConfig('focusAtStart', {
5238
                value: attr.focusAtStart || false,
5239
                writeOnce: true,
5240
                method: function(fs) {
5241
                    if (fs) {
5242
                        this.on('editorContentLoaded', function() {
5243
                            var self = this;
5244
                            setTimeout(function() {
5245
                                self.focus.call(self);
5246
                                self.editorDirty = false;
5247
                            }, 400);
5248
                        }, this, true);
5249
                    }
5250
                }
5251
            });
5252
            /**
5253
            * @attribute dompath
5254
            * @description Toggle the display of the current Dom path below the editor
5255
            * @default false
5256
            * @type Boolean
5257
            */
5258
            this.setAttributeConfig('dompath', {
5259
                value: attr.dompath || false,
5260
                method: function(dompath) {
5261
                    if (dompath && !this.dompath) {
5262
                        this.dompath = document.createElement('DIV');
5263
                        this.dompath.id = this.get('id') + '_dompath';
5264
                        Dom.addClass(this.dompath, 'dompath');
5265
                        this.get('element_cont').get('firstChild').appendChild(this.dompath);
5266
                        if (this.get('iframe')) {
5267
                            this._writeDomPath();
5268
                        }
5269
                    } else if (!dompath && this.dompath) {
5270
                        this.dompath.parentNode.removeChild(this.dompath);
5271
                        this.dompath = null;
5272
                    }
5273
                }
5274
            });
5275
            /**
5276
            * @attribute markup
5277
            * @description Should we try to adjust the markup for the following types: semantic, css, default or xhtml
5278
            * @default "semantic"
5279
            * @type String
5280
            */
5281
            this.setAttributeConfig('markup', {
5282
                value: attr.markup || 'semantic',
5283
                validator: function(markup) {
5284
                    switch (markup.toLowerCase()) {
5285
                        case 'semantic':
5286
                        case 'css':
5287
                        case 'default':
5288
                        case 'xhtml':
5289
                        return true;
5290
                    }
5291
                    return false;
5292
                }
5293
            });
5294
            /**
5295
            * @attribute removeLineBreaks
5296
            * @description Should we remove linebreaks and extra spaces on cleanup
5297
            * @default false
5298
            * @type Boolean
5299
            */
5300
            this.setAttributeConfig('removeLineBreaks', {
5301
                value: attr.removeLineBreaks || false,
5302
                validator: YAHOO.lang.isBoolean
5303
            });
5304
 
5305
            /**
5306
            * @config drag
5307
            * @description Set this config to make the Editor draggable, pass 'proxy' to make use YAHOO.util.DDProxy.
5308
            * @type {Boolean/String}
5309
            */
5310
            this.setAttributeConfig('drag', {
5311
                writeOnce: true,
5312
                value: attr.drag || false
5313
            });
5314
 
5315
            /**
5316
            * @config resize
5317
            * @description Set this to true to make the Editor Resizable with YAHOO.util.Resize. The default config is available: myEditor._resizeConfig
5318
            * Animation will be ignored while performing this resize to allow for the dynamic change in size of the toolbar.
5319
            * @type Boolean
5320
            */
5321
            this.setAttributeConfig('resize', {
5322
                writeOnce: true,
5323
                value: attr.resize || false
5324
            });
5325
 
5326
            /**
5327
            * @config filterWord
5328
            * @description Attempt to filter out MS Word HTML from the Editor's output.
5329
            * @type Boolean
5330
            */
5331
            this.setAttributeConfig('filterWord', {
5332
                value: attr.filterWord || false,
5333
                validator: YAHOO.lang.isBoolean
5334
            });
5335
 
5336
        },
5337
        /**
5338
        * @private
5339
        * @method _getBlankImage
5340
        * @description Retrieves the full url of the image to use as the blank image.
5341
        * @return {String} The URL to the blank image
5342
        */
5343
        _getBlankImage: function() {
5344
            if (!this.DOMReady) {
5345
                this._queue[this._queue.length] = ['_getBlankImage', arguments];
5346
                return '';
5347
            }
5348
            var img = '';
5349
            if (!this._blankImageLoaded) {
5350
                if (YAHOO.widget.EditorInfo.blankImage) {
5351
                    this.set('blankimage', YAHOO.widget.EditorInfo.blankImage);
5352
                    this._blankImageLoaded = true;
5353
                } else {
5354
                    var div = document.createElement('div');
5355
                    div.style.position = 'absolute';
5356
                    div.style.top = '-9999px';
5357
                    div.style.left = '-9999px';
5358
                    div.className = this.CLASS_PREFIX + '-blankimage';
5359
                    document.body.appendChild(div);
5360
                    img = YAHOO.util.Dom.getStyle(div, 'background-image');
5361
                    img = img.replace('url(', '').replace(')', '').replace(/"/g, '');
5362
                    //Adobe AIR Code
5363
                    img = img.replace('app:/', '');
5364
                    this.set('blankimage', img);
5365
                    this._blankImageLoaded = true;
5366
                    div.parentNode.removeChild(div);
5367
                    YAHOO.widget.EditorInfo.blankImage = img;
5368
                }
5369
            } else {
5370
                img = this.get('blankimage');
5371
            }
5372
            return img;
5373
        },
5374
        /**
5375
        * @private
5376
        * @method _handleAutoHeight
5377
        * @description Handles resizing the editor's height based on the content
5378
        */
5379
        _handleAutoHeight: function() {
5380
            var doc = this._getDoc(),
5381
                body = doc.body,
5382
                docEl = doc.documentElement;
5383
 
5384
            var height = parseInt(Dom.getStyle(this.get('editor_wrapper'), 'height'), 10);
5385
            var newHeight = body.scrollHeight;
5386
            if (this.browser.webkit) {
5387
                newHeight = docEl.scrollHeight;
5388
            }
5389
            if (newHeight < parseInt(this.get('height'), 10)) {
5390
                newHeight = parseInt(this.get('height'), 10);
5391
            }
5392
            if ((height != newHeight) && (newHeight >= parseInt(this.get('height'), 10))) {
5393
                var anim = this.get('animate');
5394
                this.set('animate', false);
5395
                this.set('height', newHeight + 'px');
5396
                this.set('animate', anim);
5397
                if (this.browser.ie) {
5398
                    //Internet Explorer needs this
5399
                    this.get('iframe').setStyle('height', '99%');
5400
                    this.get('iframe').setStyle('zoom', '1');
5401
                    var self = this;
5402
                    window.setTimeout(function() {
5403
                        self.get('iframe').setStyle('height', '100%');
5404
                    }, 1);
5405
                }
5406
            }
5407
        },
5408
        /**
5409
        * @private
5410
        * @property _formButtons
5411
        * @description Array of buttons that are in the Editor's parent form (for handleSubmit)
5412
        * @type Array
5413
        */
5414
        _formButtons: null,
5415
        /**
5416
        * @private
5417
        * @property _formButtonClicked
5418
        * @description The form button that was clicked to submit the form.
5419
        * @type HTMLElement
5420
        */
5421
        _formButtonClicked: null,
5422
        /**
5423
        * @private
5424
        * @method _handleFormButtonClick
5425
        * @description The click listener assigned to each submit button in the Editor's parent form.
5426
        * @param {Event} ev The click event
5427
        */
5428
        _handleFormButtonClick: function(ev) {
5429
            var tar = Event.getTarget(ev);
5430
            this._formButtonClicked = tar;
5431
        },
5432
        /**
5433
        * @private
5434
        * @method _handleFormSubmit
5435
        * @description Handles the form submission.
5436
        * @param {Object} ev The Form Submit Event
5437
        */
5438
        _handleFormSubmit: function(ev) {
5439
            this.saveHTML();
5440
 
5441
            var form = this.get('element').form,
5442
                tar = this._formButtonClicked || false;
5443
 
5444
            Event.removeListener(form, 'submit', this._handleFormSubmit);
5445
            if (YAHOO.env.ua.ie) {
5446
                //form.fireEvent("onsubmit");
5447
                if (tar && !tar.disabled) {
5448
                    tar.click();
5449
                }
5450
            } else {  // Gecko, Opera, and Safari
5451
                if (tar && !tar.disabled) {
5452
                    tar.click();
5453
                }
5454
                var oEvent = document.createEvent("HTMLEvents");
5455
                oEvent.initEvent("submit", true, true);
5456
                form.dispatchEvent(oEvent);
5457
                if (YAHOO.env.ua.webkit) {
5458
                    if (YAHOO.lang.isFunction(form.submit)) {
5459
                        form.submit();
5460
                    }
5461
                }
5462
            }
5463
            //2.6.0
5464
            //Removed this, not need since removing Safari 2.x
5465
            //Event.stopEvent(ev);
5466
        },
5467
        /**
5468
        * @private
5469
        * @method _handleFontSize
5470
        * @description Handles the font size button in the toolbar.
5471
        * @param {Object} o Object returned from Toolbar's buttonClick Event
5472
        */
5473
        _handleFontSize: function(o) {
5474
            var button = this.toolbar.getButtonById(o.button.id);
5475
            var value = button.get('label') + 'px';
5476
            this.execCommand('fontsize', value);
5477
            return false;
5478
        },
5479
        /**
5480
        * @private
5481
        * @description Handles the colorpicker buttons in the toolbar.
5482
        * @param {Object} o Object returned from Toolbar's buttonClick Event
5483
        */
5484
        _handleColorPicker: function(o) {
5485
            var cmd = o.button;
5486
            var value = '#' + o.color;
5487
            if ((cmd == 'forecolor') || (cmd == 'backcolor')) {
5488
                this.execCommand(cmd, value);
5489
            }
5490
        },
5491
        /**
5492
        * @private
5493
        * @method _handleAlign
5494
        * @description Handles the alignment buttons in the toolbar.
5495
        * @param {Object} o Object returned from Toolbar's buttonClick Event
5496
        */
5497
        _handleAlign: function(o) {
5498
            var cmd = null;
5499
            for (var i = 0; i < o.button.menu.length; i++) {
5500
                if (o.button.menu[i].value == o.button.value) {
5501
                    cmd = o.button.menu[i].value;
5502
                }
5503
            }
5504
            var value = this._getSelection();
5505
 
5506
            this.execCommand(cmd, value);
5507
            return false;
5508
        },
5509
        /**
5510
        * @private
5511
        * @method _handleAfterNodeChange
5512
        * @description Fires after a nodeChange happens to setup the things that where reset on the node change (button state).
5513
        */
5514
        _handleAfterNodeChange: function() {
5515
            var path = this._getDomPath(),
5516
                elm = null,
5517
                family = null,
5518
                fontsize = null,
5519
                validFont = false,
5520
                fn_button = this.toolbar.getButtonByValue('fontname'),
5521
                fs_button = this.toolbar.getButtonByValue('fontsize'),
5522
                hd_button = this.toolbar.getButtonByValue('heading');
5523
 
5524
            for (var i = 0; i < path.length; i++) {
5525
                elm = path[i];
5526
 
5527
                var tag = elm.tagName.toLowerCase();
5528
 
5529
 
5530
                if (elm.getAttribute('tag')) {
5531
                    tag = elm.getAttribute('tag');
5532
                }
5533
 
5534
                family = elm.getAttribute('face');
5535
                if (Dom.getStyle(elm, 'font-family')) {
5536
                    family = Dom.getStyle(elm, 'font-family');
5537
                    //Adobe AIR Code
5538
                    family = family.replace(/'/g, '');
5539
                }
5540
 
5541
                if (tag.substring(0, 1) == 'h') {
5542
                    if (hd_button) {
5543
                        for (var h = 0; h < hd_button._configs.menu.value.length; h++) {
5544
                            if (hd_button._configs.menu.value[h].value.toLowerCase() == tag) {
5545
                                hd_button.set('label', hd_button._configs.menu.value[h].text);
5546
                            }
5547
                        }
5548
                        this._updateMenuChecked('heading', tag);
5549
                    }
5550
                }
5551
            }
5552
 
5553
            if (fn_button) {
5554
                for (var b = 0; b < fn_button._configs.menu.value.length; b++) {
5555
                    if (family && fn_button._configs.menu.value[b].text.toLowerCase() == family.toLowerCase()) {
5556
                        validFont = true;
5557
                        family = fn_button._configs.menu.value[b].text; //Put the proper menu name in the button
5558
                    }
5559
                }
5560
                if (!validFont) {
5561
                    family = fn_button._configs.label._initialConfig.value;
5562
                }
5563
                var familyLabel = '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>';
5564
                if (fn_button.get('label') != familyLabel) {
5565
                    fn_button.set('label', familyLabel);
5566
                    this._updateMenuChecked('fontname', family);
5567
                }
5568
            }
5569
 
5570
            if (fs_button) {
5571
                fontsize = parseInt(Dom.getStyle(elm, 'fontSize'), 10);
5572
                if ((fontsize === null) || isNaN(fontsize)) {
5573
                    fontsize = fs_button._configs.label._initialConfig.value;
5574
                }
5575
                fs_button.set('label', ''+fontsize);
5576
            }
5577
 
5578
            if (!this._isElement(elm, 'body') && !this._isElement(elm, 'img')) {
5579
                this.toolbar.enableButton(fn_button);
5580
                this.toolbar.enableButton(fs_button);
5581
                this.toolbar.enableButton('forecolor');
5582
                this.toolbar.enableButton('backcolor');
5583
            }
5584
            if (this._isElement(elm, 'img')) {
5585
                if (YAHOO.widget.Overlay) {
5586
                    this.toolbar.enableButton('createlink');
5587
                }
5588
            }
5589
            if (this._hasParent(elm, 'blockquote')) {
5590
                this.toolbar.selectButton('indent');
5591
                this.toolbar.disableButton('indent');
5592
                this.toolbar.enableButton('outdent');
5593
            }
5594
            if (this._hasParent(elm, 'ol') || this._hasParent(elm, 'ul')) {
5595
                this.toolbar.disableButton('indent');
5596
            }
5597
            this._lastButton = null;
5598
 
5599
        },
5600
        /**
5601
        * @private
5602
        * @method _handleInsertImageClick
5603
        * @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
5604
        */
5605
        _handleInsertImageClick: function() {
5606
            if (this.get('limitCommands')) {
5607
                if (!this.toolbar.getButtonByValue('insertimage')) {
5608
                    return false;
5609
                }
5610
            }
5611
 
5612
            this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
5613
            var _handleAEC = function() {
5614
                var el = this.currentElement[0],
5615
                    src = 'http://';
5616
                if (!el) {
5617
                    el = this._getSelectedElement();
5618
                }
5619
                if (el) {
5620
                    if (el.getAttribute('src')) {
5621
                        src = el.getAttribute('src', 2);
5622
                        if (src.indexOf(this.get('blankimage')) != -1) {
5623
                            src = this.STR_IMAGE_HERE;
5624
                        }
5625
                    }
5626
                }
5627
                var str = prompt(this.STR_IMAGE_URL + ': ', src);
5628
                if ((str !== '') && (str !== null)) {
5629
                    el.setAttribute('src', str);
5630
                } else if (str === '') {
5631
                    el.parentNode.removeChild(el);
5632
                    this.currentElement = [];
5633
                    this.nodeChange();
5634
                } else if ((str === null)) {
5635
                    src = el.getAttribute('src', 2);
5636
                    if (src.indexOf(this.get('blankimage')) != -1) {
5637
                        el.parentNode.removeChild(el);
5638
                        this.currentElement = [];
5639
                        this.nodeChange();
5640
                    }
5641
                }
5642
                this.closeWindow();
5643
                this.toolbar.set('disabled', false);
5644
                this.unsubscribe('afterExecCommand', _handleAEC, this, true);
5645
            };
5646
            this.on('afterExecCommand', _handleAEC, this, true);
5647
        },
5648
        /**
5649
        * @private
5650
        * @method _handleInsertImageWindowClose
5651
        * @description Handles the closing of the Image Properties Window.
5652
        */
5653
        _handleInsertImageWindowClose: function() {
5654
            this.nodeChange();
5655
        },
5656
        /**
5657
        * @private
5658
        * @method _isLocalFile
5659
        * @param {String} url THe url/string to check
5660
        * @description Checks to see if a string (href or img src) is possibly a local file reference..
5661
        */
5662
        _isLocalFile: function(url) {
5663
            if ((url) && (url !== '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
5664
                return true;
5665
            }
5666
            return false;
5667
        },
5668
        /**
5669
        * @private
5670
        * @method _handleCreateLinkClick
5671
        * @description Handles the opening of the Link Properties Window when the Create Link button is clicked or an href is doubleclicked.
5672
        */
5673
        _handleCreateLinkClick: function() {
5674
            if (this.get('limitCommands')) {
5675
                if (!this.toolbar.getButtonByValue('createlink')) {
5676
                    return false;
5677
                }
5678
            }
5679
 
5680
            this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
5681
 
5682
            var _handleAEC = function() {
5683
                var el = this.currentElement[0],
5684
                    url = '';
5685
 
5686
                if (el) {
5687
                    if (el.getAttribute('href', 2) !== null) {
5688
                        url = el.getAttribute('href', 2);
5689
                    }
5690
                }
5691
                var str = prompt(this.STR_LINK_URL + ': ', url);
5692
                if ((str !== '') && (str !== null)) {
5693
                    var urlValue = str;
5694
                    if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
5695
                        if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
5696
                            //Found an @ sign, prefix with mailto:
5697
                            urlValue = 'mailto:' + urlValue;
5698
                        } else {
5699
                            /* :// not found adding */
5700
                            if (urlValue.substring(0, 1) != '#') {
5701
                                //urlValue = 'http:/'+'/' + urlValue;
5702
                            }
5703
                        }
5704
                    }
5705
                    el.setAttribute('href', urlValue);
5706
                } else if (str !== null) {
5707
                    var _span = this._getDoc().createElement('span');
5708
                    _span.innerHTML = el.innerHTML;
5709
                    Dom.addClass(_span, 'yui-non');
5710
                    el.parentNode.replaceChild(_span, el);
5711
                }
5712
                this.closeWindow();
5713
                this.toolbar.set('disabled', false);
5714
                this.unsubscribe('afterExecCommand', _handleAEC, this, true);
5715
            };
5716
            this.on('afterExecCommand', _handleAEC, this);
5717
 
5718
        },
5719
        /**
5720
        * @private
5721
        * @method _handleCreateLinkWindowClose
5722
        * @description Handles the closing of the Link Properties Window.
5723
        */
5724
        _handleCreateLinkWindowClose: function() {
5725
            this.nodeChange();
5726
            this.currentElement = [];
5727
        },
5728
        /**
5729
        * @method render
5730
        * @description Calls the private method _render in a setTimeout to allow for other things on the page to continue to load.
5731
        */
5732
        render: function() {
5733
            if (this._rendered) {
5734
                return false;
5735
            }
5736
            if (!this.DOMReady) {
5737
                this._queue[this._queue.length] = ['render', arguments];
5738
                return false;
5739
            }
5740
            if (this.get('element')) {
5741
                if (this.get('element').tagName) {
5742
                    this._textarea = true;
5743
                    if (this.get('element').tagName.toLowerCase() !== 'textarea') {
5744
                        this._textarea = false;
5745
                    }
5746
                } else {
5747
                    return false;
5748
                }
5749
            } else {
5750
                return false;
5751
            }
5752
            this._rendered = true;
5753
            var self = this;
5754
            window.setTimeout(function() {
5755
                self._render.call(self);
5756
            }, 4);
5757
        },
5758
        /**
5759
        * @private
5760
        * @method _render
5761
        * @description Causes the toolbar and the editor to render and replace the textarea.
5762
        */
5763
        _render: function() {
5764
            var self = this;
5765
            this.set('textarea', this.get('element'));
5766
 
5767
            this.get('element_cont').setStyle('display', 'none');
5768
            this.get('element_cont').addClass(this.CLASS_CONTAINER);
5769
 
5770
            this.set('iframe', this._createIframe());
5771
 
5772
            window.setTimeout(function() {
5773
                self._setInitialContent.call(self);
5774
            }, 10);
5775
 
5776
            this.get('editor_wrapper').appendChild(this.get('iframe').get('element'));
5777
 
5778
            if (this.get('disabled')) {
5779
                this._disableEditor(true);
5780
            }
5781
 
5782
            var tbarConf = this.get('toolbar');
5783
            //Create Toolbar instance
5784
            if (tbarConf instanceof Toolbar) {
5785
                this.toolbar = tbarConf;
5786
                //Set the toolbar to disabled until content is loaded
5787
                this.toolbar.set('disabled', true);
5788
            } else {
5789
                //Set the toolbar to disabled until content is loaded
5790
                tbarConf.disabled = true;
5791
                this.toolbar = new Toolbar(this.get('toolbar_cont'), tbarConf);
5792
            }
5793
 
5794
            this.fireEvent('toolbarLoaded', { type: 'toolbarLoaded', target: this.toolbar });
5795
 
5796
 
5797
            this.toolbar.on('toolbarCollapsed', function() {
5798
                if (this.currentWindow) {
5799
                    this.moveWindow();
5800
                }
5801
            }, this, true);
5802
            this.toolbar.on('toolbarExpanded', function() {
5803
                if (this.currentWindow) {
5804
                    this.moveWindow();
5805
                }
5806
            }, this, true);
5807
            this.toolbar.on('fontsizeClick', this._handleFontSize, this, true);
5808
 
5809
            this.toolbar.on('colorPickerClicked', function(o) {
5810
                this._handleColorPicker(o);
5811
                return false; //Stop the buttonClick event
5812
            }, this, true);
5813
 
5814
            this.toolbar.on('alignClick', this._handleAlign, this, true);
5815
            this.on('afterNodeChange', this._handleAfterNodeChange, this, true);
5816
            this.toolbar.on('insertimageClick', this._handleInsertImageClick, this, true);
5817
            this.on('windowinsertimageClose', this._handleInsertImageWindowClose, this, true);
5818
            this.toolbar.on('createlinkClick', this._handleCreateLinkClick, this, true);
5819
            this.on('windowcreatelinkClose', this._handleCreateLinkWindowClose, this, true);
5820
 
5821
 
5822
            //Replace Textarea with editable area
5823
            this.get('parentNode').replaceChild(this.get('element_cont').get('element'), this.get('element'));
5824
 
5825
 
5826
            this.setStyle('visibility', 'hidden');
5827
            this.setStyle('position', 'absolute');
5828
            this.setStyle('top', '-9999px');
5829
            this.setStyle('left', '-9999px');
5830
            this.get('element_cont').appendChild(this.get('element'));
5831
            this.get('element_cont').setStyle('display', 'block');
5832
 
5833
 
5834
            Dom.addClass(this.get('iframe').get('parentNode'), this.CLASS_EDITABLE_CONT);
5835
            this.get('iframe').addClass(this.CLASS_EDITABLE);
5836
 
5837
            //Set height and width of editor container
5838
            this.get('element_cont').setStyle('width', this.get('width'));
5839
            Dom.setStyle(this.get('iframe').get('parentNode'), 'height', this.get('height'));
5840
 
5841
            this.get('iframe').setStyle('width', '100%'); //WIDTH
5842
            this.get('iframe').setStyle('height', '100%');
5843
 
5844
            this._setupDD();
5845
 
5846
            window.setTimeout(function() {
5847
                self._setupAfterElement.call(self);
5848
            }, 0);
5849
            this.fireEvent('afterRender', { type: 'afterRender', target: this });
5850
        },
5851
        /**
5852
        * @method execCommand
5853
        * @param {String} action The "execCommand" action to try to execute (Example: bold, insertimage, inserthtml)
5854
        * @param {String} value (optional) The value for a given action such as action: fontname value: 'Verdana'
5855
        * @description This method attempts to try and level the differences in the various browsers and their support for execCommand actions
5856
        */
5857
        execCommand: function(action, value) {
5858
            var beforeExec = this.fireEvent('beforeExecCommand', { type: 'beforeExecCommand', target: this, args: arguments });
5859
            if ((beforeExec === false) || (this.STOP_EXEC_COMMAND)) {
5860
                this.STOP_EXEC_COMMAND = false;
5861
                return false;
5862
            }
5863
            this._lastCommand = action;
5864
            this._setMarkupType(action);
5865
            if (this.browser.ie) {
5866
                this._getWindow().focus();
5867
            }
5868
            var exec = true;
5869
 
5870
            if (this.get('limitCommands')) {
5871
                if (!this.toolbar.getButtonByValue(action)) {
5872
                    exec = false;
5873
                }
5874
            }
5875
 
5876
            this.editorDirty = true;
5877
 
5878
            if ((typeof this['cmd_' + action.toLowerCase()] == 'function') && exec) {
5879
                var retValue = this['cmd_' + action.toLowerCase()](value);
5880
                exec = retValue[0];
5881
                if (retValue[1]) {
5882
                    action = retValue[1];
5883
                }
5884
                if (retValue[2]) {
5885
                    value = retValue[2];
5886
                }
5887
            }
5888
            if (exec) {
5889
                try {
5890
                    this._getDoc().execCommand(action, false, value);
5891
                } catch(e) {
5892
                }
5893
            } else {
5894
            }
5895
            this.on('afterExecCommand', function() {
5896
                this.unsubscribeAll('afterExecCommand');
5897
                this.nodeChange();
5898
            }, this, true);
5899
            this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
5900
 
5901
        },
5902
    /* {{{  Command Overrides */
5903
 
5904
        /**
5905
        * @method cmd_bold
5906
        * @param value Value passed from the execCommand method
5907
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('bold') is used.
5908
        */
5909
        cmd_bold: function(value) {
5910
            if (!this.browser.webkit) {
5911
                var el = this._getSelectedElement();
5912
                if (el && this._isElement(el, 'span') && this._hasSelection()) {
5913
                    if (el.style.fontWeight == 'bold') {
5914
                        el.style.fontWeight = '';
5915
                        var b = this._getDoc().createElement('b'),
5916
                        par = el.parentNode;
5917
                        par.replaceChild(b, el);
5918
                        b.appendChild(el);
5919
                    }
5920
                }
5921
            }
5922
            return [true];
5923
        },
5924
        /**
5925
        * @method cmd_italic
5926
        * @param value Value passed from the execCommand method
5927
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('italic') is used.
5928
        */
5929
 
5930
        cmd_italic: function(value) {
5931
            if (!this.browser.webkit) {
5932
                var el = this._getSelectedElement();
5933
                if (el && this._isElement(el, 'span') && this._hasSelection()) {
5934
                    if (el.style.fontStyle == 'italic') {
5935
                        el.style.fontStyle = '';
5936
                        var i = this._getDoc().createElement('i'),
5937
                        par = el.parentNode;
5938
                        par.replaceChild(i, el);
5939
                        i.appendChild(el);
5940
                    }
5941
                }
5942
            }
5943
            return [true];
5944
        },
5945
 
5946
 
5947
        /**
5948
        * @method cmd_underline
5949
        * @param value Value passed from the execCommand method
5950
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('underline') is used.
5951
        */
5952
        cmd_underline: function(value) {
5953
            if (!this.browser.webkit) {
5954
                var el = this._getSelectedElement();
5955
                if (el && this._isElement(el, 'span')) {
5956
                    if (el.style.textDecoration == 'underline') {
5957
                        el.style.textDecoration = 'none';
5958
                    } else {
5959
                        el.style.textDecoration = 'underline';
5960
                    }
5961
                    return [false];
5962
                }
5963
            }
5964
            return [true];
5965
        },
5966
        /**
5967
        * @method cmd_backcolor
5968
        * @param value Value passed from the execCommand method
5969
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('backcolor') is used.
5970
        */
5971
        cmd_backcolor: function(value) {
5972
            var exec = true,
5973
                el = this._getSelectedElement(),
5974
                action = 'backcolor';
5975
 
5976
            if (this.browser.gecko || this.browser.opera) {
5977
                this._setEditorStyle(true);
5978
                action = 'hilitecolor';
5979
            }
5980
 
5981
            if (!this._isElement(el, 'body') && !this._hasSelection()) {
5982
                el.style.backgroundColor = value;
5983
                this._selectNode(el);
5984
                exec = false;
5985
            } else {
5986
                if (this.get('insert')) {
5987
                    el = this._createInsertElement({ backgroundColor: value });
5988
                } else {
5989
                    this._createCurrentElement('span', { backgroundColor: value, color: el.style.color, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily });
5990
                    this._selectNode(this.currentElement[0]);
5991
                }
5992
                exec = false;
5993
            }
5994
 
5995
            return [exec, action];
5996
        },
5997
        /**
5998
        * @method cmd_forecolor
5999
        * @param value Value passed from the execCommand method
6000
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('forecolor') is used.
6001
        */
6002
        cmd_forecolor: function(value) {
6003
            var exec = true,
6004
                el = this._getSelectedElement();
6005
 
6006
                if (!this._isElement(el, 'body') && !this._hasSelection()) {
6007
                    Dom.setStyle(el, 'color', value);
6008
                    this._selectNode(el);
6009
                    exec = false;
6010
                } else {
6011
                    if (this.get('insert')) {
6012
                        el = this._createInsertElement({ color: value });
6013
                    } else {
6014
                        this._createCurrentElement('span', { color: value, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily, backgroundColor: el.style.backgroundColor });
6015
                        this._selectNode(this.currentElement[0]);
6016
                    }
6017
                    exec = false;
6018
                }
6019
                return [exec];
6020
        },
6021
        /**
6022
        * @method cmd_unlink
6023
        * @param value Value passed from the execCommand method
6024
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('unlink') is used.
6025
        */
6026
        cmd_unlink: function(value) {
6027
            this._swapEl(this.currentElement[0], 'span', function(el) {
6028
                el.className = 'yui-non';
6029
            });
6030
            return [false];
6031
        },
6032
        /**
6033
        * @method cmd_createlink
6034
        * @param value Value passed from the execCommand method
6035
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('createlink') is used.
6036
        */
6037
        cmd_createlink: function(value) {
6038
            var el = this._getSelectedElement(), _a = null;
6039
            if (this._hasParent(el, 'a')) {
6040
                this.currentElement[0] = this._hasParent(el, 'a');
6041
            } else if (this._isElement(el, 'li')) {
6042
                _a = this._getDoc().createElement('a');
6043
                _a.innerHTML = el.innerHTML;
6044
                el.innerHTML = '';
6045
                el.appendChild(_a);
6046
                this.currentElement[0] = _a;
6047
            } else if (!this._isElement(el, 'a')) {
6048
                this._createCurrentElement('a');
6049
                _a = this._swapEl(this.currentElement[0], 'a');
6050
                this.currentElement[0] = _a;
6051
            } else {
6052
                this.currentElement[0] = el;
6053
            }
6054
            return [false];
6055
        },
6056
        /**
6057
        * @method cmd_insertimage
6058
        * @param value Value passed from the execCommand method
6059
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertimage') is used.
6060
        */
6061
        cmd_insertimage: function(value) {
6062
            var exec = true, _img = null, action = 'insertimage',
6063
                el = this._getSelectedElement();
6064
 
6065
            if (value === '') {
6066
                value = this.get('blankimage');
6067
            }
6068
 
6069
            /*
6070
            * @knownissue Safari Cursor Position
6071
            * @browser Safari 2.x
6072
            * @description The issue here is that we have no way of knowing where the cursor position is
6073
            * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
6074
            */
6075
 
6076
            if (this._isElement(el, 'img')) {
6077
                this.currentElement[0] = el;
6078
                exec = false;
6079
            } else {
6080
                if (this._getDoc().queryCommandEnabled(action)) {
6081
                    this._getDoc().execCommand(action, false, value);
6082
                    var imgs = this._getDoc().getElementsByTagName('img');
6083
                    for (var i = 0; i < imgs.length; i++) {
6084
                        if (!YAHOO.util.Dom.hasClass(imgs[i], 'yui-img')) {
6085
                            YAHOO.util.Dom.addClass(imgs[i], 'yui-img');
6086
                            this.currentElement[0] = imgs[i];
6087
                        }
6088
                    }
6089
                    exec = false;
6090
                } else {
6091
                    if (el == this._getDoc().body) {
6092
                        _img = this._getDoc().createElement('img');
6093
                        _img.setAttribute('src', value);
6094
                        YAHOO.util.Dom.addClass(_img, 'yui-img');
6095
                        this._getDoc().body.appendChild(_img);
6096
                    } else {
6097
                        this._createCurrentElement('img');
6098
                        _img = this._getDoc().createElement('img');
6099
                        _img.setAttribute('src', value);
6100
                        YAHOO.util.Dom.addClass(_img, 'yui-img');
6101
                        this.currentElement[0].parentNode.replaceChild(_img, this.currentElement[0]);
6102
                    }
6103
                    this.currentElement[0] = _img;
6104
                    exec = false;
6105
                }
6106
            }
6107
            return [exec];
6108
        },
6109
        /**
6110
        * @method cmd_inserthtml
6111
        * @param value Value passed from the execCommand method
6112
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('inserthtml') is used.
6113
        */
6114
        cmd_inserthtml: function(value) {
6115
            var exec = true, action = 'inserthtml', _span = null, _range = null;
6116
            /*
6117
            * @knownissue Safari cursor position
6118
            * @browser Safari 2.x
6119
            * @description The issue here is that we have no way of knowing where the cursor position is
6120
            * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
6121
            */
6122
            if (this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) {
6123
                this._createCurrentElement('img');
6124
                _span = this._getDoc().createElement('span');
6125
                _span.innerHTML = value;
6126
                this.currentElement[0].parentNode.replaceChild(_span, this.currentElement[0]);
6127
                exec = false;
6128
            } else if (this.browser.ie) {
6129
                _range = this._getRange();
6130
                if (_range.item) {
6131
                    _range.item(0).outerHTML = value;
6132
                } else {
6133
                    _range.pasteHTML(value);
6134
                }
6135
                exec = false;
6136
            }
6137
            return [exec];
6138
        },
6139
        /**
6140
        * @method cmd_list
6141
        * @param tag The tag of the list you want to create (eg, ul or ol)
6142
        * @description This is a combined execCommand override method. It is called from the cmd_insertorderedlist and cmd_insertunorderedlist methods.
6143
        */
6144
        cmd_list: function(tag) {
6145
            var exec = true, list = null, li = 0, el = null, str = '',
6146
                selEl = this._getSelectedElement(), action = 'insertorderedlist';
6147
                if (tag == 'ul') {
6148
                    action = 'insertunorderedlist';
6149
                }
6150
            /*
6151
            * @knownissue Safari 2.+ doesn't support ordered and unordered lists
6152
            * @browser Safari 2.x
6153
            * The issue with this workaround is that when applied to a set of text
6154
            * that has BR's in it, Safari may or may not pick up the individual items as
6155
            * list items. This is fixed in WebKit (Safari 3)
6156
            * 2.6.0: Seems there are still some issues with List Creation and Safari 3, reverting to previously working Safari 2.x code
6157
            */
6158
            //if ((this.browser.webkit && !this._getDoc().queryCommandEnabled(action))) {
6159
            if ((this.browser.webkit && !this.browser.webkit4) || (this.browser.opera)) {
6160
                if (this._isElement(selEl, 'li') && this._isElement(selEl.parentNode, tag)) {
6161
                    el = selEl.parentNode;
6162
                    list = this._getDoc().createElement('span');
6163
                    YAHOO.util.Dom.addClass(list, 'yui-non');
6164
                    str = '';
6165
                    var lis = el.getElementsByTagName('li'), p_tag = ((this.browser.opera && this.get('ptags')) ? 'p' : 'div');
6166
                    for (li = 0; li < lis.length; li++) {
6167
                        str += '<' + p_tag + '>' + lis[li].innerHTML + '</' + p_tag + '>';
6168
                    }
6169
                    list.innerHTML = str;
6170
                    this.currentElement[0] = el;
6171
                    this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
6172
                } else {
6173
                    this._createCurrentElement(tag.toLowerCase());
6174
                    list = this._getDoc().createElement(tag);
6175
                    for (li = 0; li < this.currentElement.length; li++) {
6176
                        var newli = this._getDoc().createElement('li');
6177
                        newli.innerHTML = this.currentElement[li].innerHTML + '<span class="yui-non">&nbsp;</span>&nbsp;';
6178
                        list.appendChild(newli);
6179
                        if (li > 0) {
6180
                            this.currentElement[li].parentNode.removeChild(this.currentElement[li]);
6181
                        }
6182
                    }
6183
                    var b_tag = ((this.browser.opera) ? '<BR>' : '<br>'),
6184
                    items = list.firstChild.innerHTML.split(b_tag), i, item;
6185
                    if (items.length > 0) {
6186
                        list.innerHTML = '';
6187
                        for (i = 0; i < items.length; i++) {
6188
                            item = this._getDoc().createElement('li');
6189
                            item.innerHTML = items[i];
6190
                            list.appendChild(item);
6191
                        }
6192
                    }
6193
 
6194
                    this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
6195
                    this.currentElement[0] = list;
6196
                    var _h = this.currentElement[0].firstChild;
6197
                    _h = Dom.getElementsByClassName('yui-non', 'span', _h)[0];
6198
                    if (this.browser.webkit) {
6199
                        this._getSelection().setBaseAndExtent(_h, 1, _h, _h.innerText.length);
6200
                    }
6201
                }
6202
                exec = false;
6203
            } else {
6204
                el = this._getSelectedElement();
6205
                if (this._isElement(el, 'li') && this._isElement(el.parentNode, tag) || (this.browser.ie && this._isElement(this._getRange().parentElement, 'li')) || (this.browser.ie && this._isElement(el, 'ul')) || (this.browser.ie && this._isElement(el, 'ol'))) { //we are in a list..
6206
                    if (this.browser.ie) {
6207
                        if ((this.browser.ie && this._isElement(el, 'ul')) || (this.browser.ie && this._isElement(el, 'ol'))) {
6208
                            el = el.getElementsByTagName('li')[0];
6209
                        }
6210
                        str = '';
6211
                        var lis2 = el.parentNode.getElementsByTagName('li');
6212
                        for (var j = 0; j < lis2.length; j++) {
6213
                            str += lis2[j].innerHTML + '<br>';
6214
                        }
6215
                        var newEl = this._getDoc().createElement('span');
6216
                        newEl.innerHTML = str;
6217
                        el.parentNode.parentNode.replaceChild(newEl, el.parentNode);
6218
                    } else {
6219
                        this.nodeChange();
6220
                        this._getDoc().execCommand(action, '', el.parentNode);
6221
                        this.nodeChange();
6222
                    }
6223
                    exec = false;
6224
                }
6225
                if (this.browser.opera) {
6226
                    var self = this;
6227
                    window.setTimeout(function() {
6228
                        var liso = self._getDoc().getElementsByTagName('li');
6229
                        for (var i = 0; i < liso.length; i++) {
6230
                            if (liso[i].innerHTML.toLowerCase() == '<br>') {
6231
                                liso[i].parentNode.parentNode.removeChild(liso[i].parentNode);
6232
                            }
6233
                        }
6234
                    },30);
6235
                }
6236
                if (this.browser.ie && exec) {
6237
                    var html = '';
6238
                    if (this._getRange().html) {
6239
                        html = '<li>' + this._getRange().html+ '</li>';
6240
                    } else {
6241
                        var t = this._getRange().text.split('\n');
6242
                        if (t.length > 1) {
6243
                            html = '';
6244
                            for (var ie = 0; ie < t.length; ie++) {
6245
                                html += '<li>' + t[ie] + '</li>';
6246
                            }
6247
                        } else {
6248
                            var txt = this._getRange().text;
6249
                            if (txt === '') {
6250
                                html = '<li id="new_list_item">' + txt + '</li>';
6251
                            } else {
6252
                                html = '<li>' + txt + '</li>';
6253
                            }
6254
                        }
6255
                    }
6256
                    this._getRange().pasteHTML('<' + tag + '>' + html + '</' + tag + '>');
6257
                    var new_item = this._getDoc().getElementById('new_list_item');
6258
                    if (new_item) {
6259
                        var range = this._getDoc().body.createTextRange();
6260
                        range.moveToElementText(new_item);
6261
                        range.collapse(false);
6262
                        range.select();
6263
                        new_item.id = '';
6264
                    }
6265
                    exec = false;
6266
                }
6267
            }
6268
            return exec;
6269
        },
6270
        /**
6271
        * @method cmd_insertorderedlist
6272
        * @param value Value passed from the execCommand method
6273
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertorderedlist ') is used.
6274
        */
6275
        cmd_insertorderedlist: function(value) {
6276
            return [this.cmd_list('ol')];
6277
        },
6278
        /**
6279
        * @method cmd_insertunorderedlist
6280
        * @param value Value passed from the execCommand method
6281
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('insertunorderedlist') is used.
6282
        */
6283
        cmd_insertunorderedlist: function(value) {
6284
            return [this.cmd_list('ul')];
6285
        },
6286
        /**
6287
        * @method cmd_fontname
6288
        * @param value Value passed from the execCommand method
6289
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('fontname') is used.
6290
        */
6291
        cmd_fontname: function(value) {
6292
            var exec = true,
6293
                selEl = this._getSelectedElement();
6294
 
6295
            this.currentFont = value;
6296
            if (selEl && selEl.tagName && !this._hasSelection() && !this._isElement(selEl, 'body') && !this.get('insert')) {
6297
                YAHOO.util.Dom.setStyle(selEl, 'font-family', value);
6298
                exec = false;
6299
            } else if (this.get('insert') && !this._hasSelection()) {
6300
                var el = this._createInsertElement({ fontFamily: value });
6301
                exec = false;
6302
            }
6303
            return [exec];
6304
        },
6305
        /**
6306
        * @method cmd_fontsize
6307
        * @param value Value passed from the execCommand method
6308
        * @description This is an execCommand override method. It is called from execCommand when the execCommand('fontsize') is used.
6309
        */
6310
        cmd_fontsize: function(value) {
6311
            var el = null, go = true;
6312
            el = this._getSelectedElement();
6313
            if (this.browser.webkit) {
6314
                if (this.currentElement[0]) {
6315
                    if (el == this.currentElement[0]) {
6316
                        go = false;
6317
                        YAHOO.util.Dom.setStyle(el, 'fontSize', value);
6318
                        this._selectNode(el);
6319
                        this.currentElement[0] = el;
6320
                    }
6321
                }
6322
            }
6323
            if (go) {
6324
                if (!this._isElement(this._getSelectedElement(), 'body') && (!this._hasSelection())) {
6325
                    el = this._getSelectedElement();
6326
                    YAHOO.util.Dom.setStyle(el, 'fontSize', value);
6327
                    if (this.get('insert') && this.browser.ie) {
6328
                        var r = this._getRange();
6329
                        r.collapse(false);
6330
                        r.select();
6331
                    } else {
6332
                        this._selectNode(el);
6333
                    }
6334
                } else if (this.currentElement && (this.currentElement.length > 0) && (!this._hasSelection()) && (!this.get('insert'))) {
6335
                    YAHOO.util.Dom.setStyle(this.currentElement, 'fontSize', value);
6336
                } else {
6337
                    if (this.get('insert') && !this._hasSelection()) {
6338
                        el = this._createInsertElement({ fontSize: value });
6339
                        this.currentElement[0] = el;
6340
                        this._selectNode(this.currentElement[0]);
6341
                    } else {
6342
                        this._createCurrentElement('span', {'fontSize': value, fontFamily: el.style.fontFamily, color: el.style.color, backgroundColor: el.style.backgroundColor });
6343
                        this._selectNode(this.currentElement[0]);
6344
                    }
6345
                }
6346
            }
6347
            return [false];
6348
        },
6349
    /* }}} */
6350
        /**
6351
        * @private
6352
        * @method _swapEl
6353
        * @param {HTMLElement} el The element to swap with
6354
        * @param {String} tagName The tagname of the element that you wish to create
6355
        * @param {Function} callback (optional) A function to run on the element after it is created, but before it is replaced. An element reference is passed to this function.
6356
        * @description This function will create a new element in the DOM and populate it with the contents of another element. Then it will assume it's place.
6357
        */
6358
        _swapEl: function(el, tagName, callback) {
6359
            var _el = this._getDoc().createElement(tagName);
6360
            if (el) {
6361
                _el.innerHTML = el.innerHTML;
6362
            }
6363
            if (typeof callback == 'function') {
6364
                callback.call(this, _el);
6365
            }
6366
            if (el) {
6367
                el.parentNode.replaceChild(_el, el);
6368
            }
6369
            return _el;
6370
        },
6371
        /**
6372
        * @private
6373
        * @method _createInsertElement
6374
        * @description Creates a new "currentElement" then adds some text (and other things) to make it selectable and stylable. Then the user can continue typing.
6375
        * @param {Object} css (optional) Object literal containing styles to apply to the new element.
6376
        * @return {HTMLElement}
6377
        */
6378
        _createInsertElement: function(css) {
6379
            this._createCurrentElement('span', css);
6380
            var el = this.currentElement[0];
6381
            if (this.browser.webkit) {
6382
                //Little Safari Hackery here..
6383
                el.innerHTML = '<span class="yui-non">&nbsp;</span>';
6384
                el = el.firstChild;
6385
                this._getSelection().setBaseAndExtent(el, 1, el, el.innerText.length);
6386
            } else if (this.browser.ie || this.browser.opera) {
6387
                el.innerHTML = '&nbsp;';
6388
            }
6389
            this.focus();
6390
            this._selectNode(el, true);
6391
            return el;
6392
        },
6393
        /**
6394
        * @private
6395
        * @method _createCurrentElement
6396
        * @param {String} tagName (optional defaults to a) The tagname of the element that you wish to create
6397
        * @param {Object} tagStyle (optional) Object literal containing styles to apply to the new element.
6398
        * @description This is a work around for the various browser issues with execCommand. This method will run <code>execCommand('fontname', false, 'yui-tmp')</code> on the given selection.
6399
        * It will then search the document for an element with the font-family set to <strong>yui-tmp</strong> and replace that with another span that has other information in it, then assign the new span to the
6400
        * <code>this.currentElement</code> array, so we now have element references to the elements that were just modified. At this point we can use standard DOM manipulation to change them as we see fit.
6401
        */
6402
        _createCurrentElement: function(tagName, tagStyle) {
6403
            tagName = ((tagName) ? tagName : 'a');
6404
            var tar = null,
6405
                el = [],
6406
                _doc = this._getDoc();
6407
 
6408
            if (this.currentFont) {
6409
                if (!tagStyle) {
6410
                    tagStyle = {};
6411
                }
6412
                tagStyle.fontFamily = this.currentFont;
6413
                this.currentFont = null;
6414
            }
6415
            this.currentElement = [];
6416
 
6417
            var _elCreate = function(tagName, tagStyle) {
6418
                var el = null;
6419
                tagName = ((tagName) ? tagName : 'span');
6420
                tagName = tagName.toLowerCase();
6421
                switch (tagName) {
6422
                    case 'h1':
6423
                    case 'h2':
6424
                    case 'h3':
6425
                    case 'h4':
6426
                    case 'h5':
6427
                    case 'h6':
6428
                        el = _doc.createElement(tagName);
6429
                        break;
6430
                    default:
6431
                        el = _doc.createElement(tagName);
6432
                        if (tagName === 'span') {
6433
                            YAHOO.util.Dom.addClass(el, 'yui-tag-' + tagName);
6434
                            YAHOO.util.Dom.addClass(el, 'yui-tag');
6435
                            el.setAttribute('tag', tagName);
6436
                        }
6437
 
6438
                        for (var k in tagStyle) {
6439
                            if (YAHOO.lang.hasOwnProperty(tagStyle, k)) {
6440
                                el.style[k] = tagStyle[k];
6441
                            }
6442
                        }
6443
                        break;
6444
                }
6445
                return el;
6446
            };
6447
 
6448
            if (!this._hasSelection()) {
6449
                if (this._getDoc().queryCommandEnabled('insertimage')) {
6450
                    this._getDoc().execCommand('insertimage', false, 'yui-tmp-img');
6451
                    var imgs = this._getDoc().getElementsByTagName('img');
6452
                    for (var j = 0; j < imgs.length; j++) {
6453
                        if (imgs[j].getAttribute('src', 2) == 'yui-tmp-img') {
6454
                            el = _elCreate(tagName, tagStyle);
6455
                            imgs[j].parentNode.replaceChild(el, imgs[j]);
6456
                            this.currentElement[this.currentElement.length] = el;
6457
                        }
6458
                    }
6459
                } else {
6460
                    if (this.currentEvent) {
6461
                        tar = YAHOO.util.Event.getTarget(this.currentEvent);
6462
                    } else {
6463
                        //For Safari..
6464
                        tar = this._getDoc().body;
6465
                    }
6466
                }
6467
                if (tar) {
6468
                    /*
6469
                    * @knownissue Safari Cursor Position
6470
                    * @browser Safari 2.x
6471
                    * @description The issue here is that we have no way of knowing where the cursor position is
6472
                    * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
6473
                    */
6474
                    el = _elCreate(tagName, tagStyle);
6475
                    if (this._isElement(tar, 'body') || this._isElement(tar, 'html')) {
6476
                        if (this._isElement(tar, 'html')) {
6477
                            tar = this._getDoc().body;
6478
                        }
6479
                        tar.appendChild(el);
6480
                    } else if (tar.nextSibling) {
6481
                        tar.parentNode.insertBefore(el, tar.nextSibling);
6482
                    } else {
6483
                        tar.parentNode.appendChild(el);
6484
                    }
6485
                    //this.currentElement = el;
6486
                    this.currentElement[this.currentElement.length] = el;
6487
                    this.currentEvent = null;
6488
                    if (this.browser.webkit) {
6489
                        //Force Safari to focus the new element
6490
                        this._getSelection().setBaseAndExtent(el, 0, el, 0);
6491
                        if (this.browser.webkit3) {
6492
                            this._getSelection().collapseToStart();
6493
                        } else {
6494
                            this._getSelection().collapse(true);
6495
                        }
6496
                    }
6497
                }
6498
            } else {
6499
                //Force CSS Styling for this action...
6500
                this._setEditorStyle(true);
6501
                this._getDoc().execCommand('fontname', false, 'yui-tmp');
6502
                var _tmp = [], __tmp, __els = ['font', 'span', 'i', 'b', 'u'];
6503
 
6504
                if (!this._isElement(this._getSelectedElement(), 'body')) {
6505
                    __els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().tagName);
6506
                    __els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().parentNode.tagName);
6507
                }
6508
                for (var _els = 0; _els < __els.length; _els++) {
6509
                    var _tmp1 = this._getDoc().getElementsByTagName(__els[_els]);
6510
                    for (var e = 0; e < _tmp1.length; e++) {
6511
                        _tmp[_tmp.length] = _tmp1[e];
6512
                    }
6513
                }
6514
 
6515
 
6516
                for (var i = 0; i < _tmp.length; i++) {
6517
                    if ((YAHOO.util.Dom.getStyle(_tmp[i], 'font-family') == 'yui-tmp') || (_tmp[i].face && (_tmp[i].face == 'yui-tmp'))) {
6518
                        if (tagName !== 'span') {
6519
                            el = _elCreate(tagName, tagStyle);
6520
                        } else {
6521
                            el = _elCreate(_tmp[i].tagName, tagStyle);
6522
                        }
6523
                        el.innerHTML = _tmp[i].innerHTML;
6524
                        if (this._isElement(_tmp[i], 'ol') || (this._isElement(_tmp[i], 'ul'))) {
6525
                            var fc = _tmp[i].getElementsByTagName('li')[0];
6526
                            _tmp[i].style.fontFamily = 'inherit';
6527
                            fc.style.fontFamily = 'inherit';
6528
                            el.innerHTML = fc.innerHTML;
6529
                            fc.innerHTML = '';
6530
                            fc.appendChild(el);
6531
                            this.currentElement[this.currentElement.length] = el;
6532
                        } else if (this._isElement(_tmp[i], 'li')) {
6533
                            _tmp[i].innerHTML = '';
6534
                            _tmp[i].appendChild(el);
6535
                            _tmp[i].style.fontFamily = 'inherit';
6536
                            this.currentElement[this.currentElement.length] = el;
6537
                        } else {
6538
                            if (_tmp[i].parentNode) {
6539
                                _tmp[i].parentNode.replaceChild(el, _tmp[i]);
6540
                                this.currentElement[this.currentElement.length] = el;
6541
                                this.currentEvent = null;
6542
                                if (this.browser.webkit) {
6543
                                    //Force Safari to focus the new element
6544
                                    this._getSelection().setBaseAndExtent(el, 0, el, 0);
6545
                                    if (this.browser.webkit3) {
6546
                                        this._getSelection().collapseToStart();
6547
                                    } else {
6548
                                        this._getSelection().collapse(true);
6549
                                    }
6550
                                }
6551
                                if (this.browser.ie && tagStyle && tagStyle.fontSize) {
6552
                                    this._getSelection().empty();
6553
                                }
6554
                                if (this.browser.gecko) {
6555
                                    this._getSelection().collapseToStart();
6556
                                }
6557
                            }
6558
                        }
6559
                    }
6560
                }
6561
                var len = this.currentElement.length;
6562
                for (var o = 0; o < len; o++) {
6563
                    if ((o + 1) != len) { //Skip the last one in the list
6564
                        if (this.currentElement[o] && this.currentElement[o].nextSibling) {
6565
                            if (this._isElement(this.currentElement[o], 'br')) {
6566
                                this.currentElement[this.currentElement.length] = this.currentElement[o].nextSibling;
6567
                            }
6568
                        }
6569
                    }
6570
                }
6571
            }
6572
        },
6573
        /**
6574
        * @method saveHTML
6575
        * @description Cleans the HTML with the cleanHTML method then places that string back into the textarea.
6576
        * @return String
6577
        */
6578
        saveHTML: function() {
6579
            var html = this.cleanHTML();
6580
            if (this._textarea) {
6581
                this.get('element').value = html;
6582
            } else {
6583
                this.get('element').innerHTML = html;
6584
            }
6585
            if (this.get('saveEl') !== this.get('element')) {
6586
                var out = this.get('saveEl');
6587
                if (Lang.isString(out)) {
6588
                    out = Dom.get(out);
6589
                }
6590
                if (out) {
6591
                    if (out.tagName.toLowerCase() === 'textarea') {
6592
                        out.value = html;
6593
                    } else {
6594
                        out.innerHTML = html;
6595
                    }
6596
                }
6597
            }
6598
            return html;
6599
        },
6600
        /**
6601
        * @method setEditorHTML
6602
        * @param {String} incomingHTML The html content to load into the editor
6603
        * @description Loads HTML into the editors body
6604
        */
6605
        setEditorHTML: function(incomingHTML) {
6606
            var html = this._cleanIncomingHTML(incomingHTML);
6607
            html = html.replace(/RIGHT_BRACKET/gi, '{');
6608
            html = html.replace(/LEFT_BRACKET/gi, '}');
6609
            this._getDoc().body.innerHTML = html;
6610
            this.nodeChange();
6611
        },
6612
        /**
6613
        * @method getEditorHTML
6614
        * @description Gets the unprocessed/unfiltered HTML from the editor
6615
        */
6616
        getEditorHTML: function() {
6617
            try {
6618
                var b = this._getDoc().body;
6619
                if (b === null) {
6620
                    return null;
6621
                }
6622
                return this._getDoc().body.innerHTML;
6623
            } catch (e) {
6624
                return '';
6625
            }
6626
        },
6627
        /**
6628
        * @method show
6629
        * @description This method needs to be called if the Editor was hidden (like in a TabView or Panel). It is used to reset the editor after being in a container that was set to display none.
6630
        */
6631
        show: function() {
6632
            if (this.browser.gecko) {
6633
                this._setDesignMode('on');
6634
                this.focus();
6635
            }
6636
            if (this.browser.webkit) {
6637
                var self = this;
6638
                window.setTimeout(function() {
6639
                    self._setInitialContent.call(self);
6640
                }, 10);
6641
            }
6642
            //Adding this will close all other Editor window's when showing this one.
6643
            if (this.currentWindow) {
6644
                this.closeWindow();
6645
            }
6646
            //Put the iframe back in place
6647
            this.get('iframe').setStyle('position', 'static');
6648
            this.get('iframe').setStyle('left', '');
6649
        },
6650
        /**
6651
        * @method hide
6652
        * @description This method needs to be called if the Editor is to be hidden (like in a TabView or Panel). It should be called to clear timeouts and close open editor windows.
6653
        */
6654
        hide: function() {
6655
            //Adding this will close all other Editor window's.
6656
            if (this.currentWindow) {
6657
                this.closeWindow();
6658
            }
6659
            if (this._fixNodesTimer) {
6660
                clearTimeout(this._fixNodesTimer);
6661
                this._fixNodesTimer = null;
6662
            }
6663
            if (this._nodeChangeTimer) {
6664
                clearTimeout(this._nodeChangeTimer);
6665
                this._nodeChangeTimer = null;
6666
            }
6667
            this._lastNodeChange = 0;
6668
            //Move the iframe off of the screen, so that in containers with visiblity hidden, IE will not cover other elements.
6669
            this.get('iframe').setStyle('position', 'absolute');
6670
            this.get('iframe').setStyle('left', '-9999px');
6671
        },
6672
        /**
6673
        * @method _cleanIncomingHTML
6674
        * @param {String} html The unfiltered HTML
6675
        * @description Process the HTML with a few regexes to clean it up and stabilize the input
6676
        * @return {String} The filtered HTML
6677
        */
6678
        _cleanIncomingHTML: function(html) {
6679
            html = html.replace(/{/gi, 'RIGHT_BRACKET');
6680
            html = html.replace(/}/gi, 'LEFT_BRACKET');
6681
 
6682
            html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
6683
            html = html.replace(/<\/strong>/gi, '</b>');
6684
 
6685
            //replace embed before em check
6686
            html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
6687
            html = html.replace(/<\/embed>/gi, '</YUI_EMBED>');
6688
 
6689
            html = html.replace(/<em([^>]*)>/gi, '<i$1>');
6690
            html = html.replace(/<\/em>/gi, '</i>');
6691
            html = html.replace(/_moz_dirty=""/gi, '');
6692
 
6693
            //Put embed tags back in..
6694
            html = html.replace(/<YUI_EMBED([^>]*)>/gi, '<embed$1>');
6695
            html = html.replace(/<\/YUI_EMBED>/gi, '</embed>');
6696
            if (this.get('plainText')) {
6697
                html = html.replace(/\n/g, '<br>').replace(/\r/g, '<br>');
6698
                html = html.replace(/  /gi, '&nbsp;&nbsp;'); //Replace all double spaces
6699
                html = html.replace(/\t/gi, '&nbsp;&nbsp;&nbsp;&nbsp;'); //Replace all tabs
6700
            }
6701
            //Removing Script Tags from the Editor
6702
            html = html.replace(/<script([^>]*)>/gi, '<bad>');
6703
            html = html.replace(/<\/script([^>]*)>/gi, '</bad>');
6704
            html = html.replace(/&lt;script([^>]*)&gt;/gi, '<bad>');
6705
            html = html.replace(/&lt;\/script([^>]*)&gt;/gi, '</bad>');
6706
            //Replace the line feeds
6707
            html = html.replace(/\r\n/g, '<YUI_LF>').replace(/\n/g, '<YUI_LF>').replace(/\r/g, '<YUI_LF>');
6708
 
6709
            //Remove Bad HTML elements (used to be script nodes)
6710
            html = html.replace(new RegExp('<bad([^>]*)>(.*?)<\/bad>', 'gi'), '');
6711
            //Replace the lines feeds
6712
            html = html.replace(/<YUI_LF>/g, '\n');
6713
            return html;
6714
        },
6715
        /**
6716
        * @method cleanHTML
6717
        * @param {String} html The unfiltered HTML
6718
        * @description Process the HTML with a few regexes to clean it up and stabilize the output
6719
        * @return {String} The filtered HTML
6720
        */
6721
        cleanHTML: function(html) {
6722
            //Start Filtering Output
6723
            //Begin RegExs..
6724
            if (!html) {
6725
                html = this.getEditorHTML();
6726
            }
6727
            var markup = this.get('markup');
6728
            //Make some backups...
6729
            html = this.pre_filter_linebreaks(html, markup);
6730
 
6731
            //Filter MS Word
6732
            html = this.filter_msword(html);
6733
 
6734
		    html = html.replace(/<img([^>]*)\/>/gi, '<YUI_IMG$1>');
6735
		    html = html.replace(/<img([^>]*)>/gi, '<YUI_IMG$1>');
6736
 
6737
		    html = html.replace(/<input([^>]*)\/>/gi, '<YUI_INPUT$1>');
6738
		    html = html.replace(/<input([^>]*)>/gi, '<YUI_INPUT$1>');
6739
 
6740
		    html = html.replace(/<ul([^>]*)>/gi, '<YUI_UL$1>');
6741
		    html = html.replace(/<\/ul>/gi, '<\/YUI_UL>');
6742
		    html = html.replace(/<blockquote([^>]*)>/gi, '<YUI_BQ$1>');
6743
		    html = html.replace(/<\/blockquote>/gi, '<\/YUI_BQ>');
6744
 
6745
		    html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
6746
		    html = html.replace(/<\/embed>/gi, '<\/YUI_EMBED>');
6747
 
6748
            //Convert b and i tags to strong and em tags
6749
            if ((markup == 'semantic') || (markup == 'xhtml')) {
6750
                //html = html.replace(/<i(\s+[^>]*)?>/gi, "<em$1>");
6751
                html = html.replace(/<i([^>]*)>/gi, "<em$1>");
6752
                html = html.replace(/<\/i>/gi, '</em>');
6753
                //html = html.replace(/<b(\s+[^>]*)?>/gi, "<strong$1>");
6754
                html = html.replace(/<b([^>]*)>/gi, "<strong$1>");
6755
                html = html.replace(/<\/b>/gi, '</strong>');
6756
            }
6757
 
6758
            html = html.replace(/_moz_dirty=""/gi, '');
6759
 
6760
            //normalize strikethrough
6761
            html = html.replace(/<strike/gi, '<span style="text-decoration: line-through;"');
6762
            html = html.replace(/\/strike>/gi, '/span>');
6763
 
6764
 
6765
            //Case Changing
6766
            if (this.browser.ie) {
6767
                html = html.replace(/text-decoration/gi, 'text-decoration');
6768
                html = html.replace(/font-weight/gi, 'font-weight');
6769
                html = html.replace(/_width="([^>]*)"/gi, '');
6770
                html = html.replace(/_height="([^>]*)"/gi, '');
6771
                //Cleanup Image URL's
6772
                var url = this._baseHREF.replace(/\//gi, '\\/'),
6773
                    re = new RegExp('src="' + url, 'gi');
6774
                html = html.replace(re, 'src="');
6775
            }
6776
		    html = html.replace(/<font/gi, '<font');
6777
		    html = html.replace(/<\/font>/gi, '</font>');
6778
		    html = html.replace(/<span/gi, '<span');
6779
		    html = html.replace(/<\/span>/gi, '</span>');
6780
            if ((markup == 'semantic') || (markup == 'xhtml') || (markup == 'css')) {
6781
                html = html.replace(new RegExp('<font([^>]*)face="([^>]*)">(.*?)<\/font>', 'gi'), '<span $1 style="font-family: $2;">$3</span>');
6782
                html = html.replace(/<u/gi, '<span style="text-decoration: underline;"');
6783
                if (this.browser.webkit) {
6784
                    html = html.replace(new RegExp('<span class="Apple-style-span" style="font-weight: bold;">([^>]*)<\/span>', 'gi'), '<strong>$1</strong>');
6785
                    html = html.replace(new RegExp('<span class="Apple-style-span" style="font-style: italic;">([^>]*)<\/span>', 'gi'), '<em>$1</em>');
6786
                }
6787
                html = html.replace(/\/u>/gi, '/span>');
6788
                if (markup == 'css') {
6789
                    html = html.replace(/<em([^>]*)>/gi, '<i$1>');
6790
                    html = html.replace(/<\/em>/gi, '</i>');
6791
                    html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
6792
                    html = html.replace(/<\/strong>/gi, '</b>');
6793
                    html = html.replace(/<b/gi, '<span style="font-weight: bold;"');
6794
                    html = html.replace(/\/b>/gi, '/span>');
6795
                    html = html.replace(/<i/gi, '<span style="font-style: italic;"');
6796
                    html = html.replace(/\/i>/gi, '/span>');
6797
                }
6798
                html = html.replace(/  /gi, ' '); //Replace all double spaces and replace with a single
6799
            } else {
6800
		        html = html.replace(/<u/gi, '<u');
6801
		        html = html.replace(/\/u>/gi, '/u>');
6802
            }
6803
		    html = html.replace(/<ol([^>]*)>/gi, '<ol$1>');
6804
		    html = html.replace(/\/ol>/gi, '/ol>');
6805
		    html = html.replace(/<li/gi, '<li');
6806
		    html = html.replace(/\/li>/gi, '/li>');
6807
            html = this.filter_safari(html);
6808
 
6809
            html = this.filter_internals(html);
6810
 
6811
            html = this.filter_all_rgb(html);
6812
 
6813
            //Replace our backups with the real thing
6814
            html = this.post_filter_linebreaks(html, markup);
6815
 
6816
            if (markup == 'xhtml') {
6817
		        html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1 />');
6818
		        html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1 />');
6819
            } else {
6820
		        html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1>');
6821
		        html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1>');
6822
            }
6823
		    html = html.replace(/<YUI_UL([^>]*)>/g, '<ul$1>');
6824
		    html = html.replace(/<\/YUI_UL>/g, '<\/ul>');
6825
 
6826
            html = this.filter_invalid_lists(html);
6827
 
6828
		    html = html.replace(/<YUI_BQ([^>]*)>/g, '<blockquote$1>');
6829
		    html = html.replace(/<\/YUI_BQ>/g, '<\/blockquote>');
6830
 
6831
		    html = html.replace(/<YUI_EMBED([^>]*)>/g, '<embed$1>');
6832
		    html = html.replace(/<\/YUI_EMBED>/g, '<\/embed>');
6833
 
6834
            //This should fix &amp;'s in URL's
6835
            html = html.replace(/ &amp; /gi, ' YUI_AMP ');
6836
            html = html.replace(/ &amp;/gi, ' YUI_AMP_F ');
6837
            html = html.replace(/&amp; /gi, ' YUI_AMP_R ');
6838
            html = html.replace(/&amp;/gi, '&');
6839
            html = html.replace(/ YUI_AMP /gi, ' &amp; ');
6840
            html = html.replace(/ YUI_AMP_F /gi, ' &amp;');
6841
            html = html.replace(/ YUI_AMP_R /gi, '&amp; ');
6842
 
6843
            //Trim the output, removing whitespace from the beginning and end
6844
            html = YAHOO.lang.trim(html);
6845
 
6846
            if (this.get('removeLineBreaks')) {
6847
                html = html.replace(/\n/g, '').replace(/\r/g, '');
6848
                html = html.replace(/  /gi, ' '); //Replace all double spaces and replace with a single
6849
            }
6850
 
6851
            for (var v in this.invalidHTML) {
6852
                if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
6853
                    if (Lang.isObject(v) && v.keepContents) {
6854
                        html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '$1');
6855
                    } else {
6856
                        html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '');
6857
                    }
6858
                }
6859
            }
6860
 
6861
            /* LATER -- Add DOM manipulation
6862
            console.log(html);
6863
            var frag = document.createDocumentFragment();
6864
            frag.innerHTML = html;
6865
 
6866
            var ps = frag.getElementsByTagName('p'),
6867
                len = ps.length;
6868
            for (var i = 0; i < len; i++) {
6869
                var ps2 = ps[i].getElementsByTagName('p');
6870
                if (ps2.length) {
6871
 
6872
                }
6873
 
6874
            }
6875
            html = frag.innerHTML;
6876
            console.log(html);
6877
            */
6878
 
6879
            this.fireEvent('cleanHTML', { type: 'cleanHTML', target: this, html: html });
6880
 
6881
            return html;
6882
        },
6883
        /**
6884
        * @method filter_msword
6885
        * @param String html The HTML string to filter
6886
        * @description Filters out msword html attributes and other junk. Activate with filterWord: true in config
6887
        */
6888
        filter_msword: function(html) {
6889
            if (!this.get('filterWord')) {
6890
                return html;
6891
            }
6892
            //Remove the ms o: tags
6893
            html = html.replace(/<o:p>\s*<\/o:p>/g, '');
6894
            html = html.replace(/<o:p>[\s\S]*?<\/o:p>/g, '&nbsp;');
6895
 
6896
            //Remove the ms w: tags
6897
            html = html.replace( /<w:[^>]*>[\s\S]*?<\/w:[^>]*>/gi, '');
6898
 
6899
            //Remove mso-? styles.
6900
            html = html.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '');
6901
 
6902
            //Remove more bogus MS styles.
6903
            html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '');
6904
            html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"");
6905
            html = html.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '');
6906
            html = html.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"");
6907
            html = html.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");
6908
            html = html.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" );
6909
            html = html.replace( /\s*tab-stops:[^;"]*;?/gi, '');
6910
            html = html.replace( /\s*tab-stops:[^"]*/gi, '');
6911
 
6912
            //Remove XML declarations
6913
            html = html.replace(/<\\?\?xml[^>]*>/gi, '');
6914
 
6915
            //Remove lang
6916
            html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");
6917
 
6918
            //Remove language tags
6919
            html = html.replace( /<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3");
6920
 
6921
            //Remove onmouseover and onmouseout events (from MS Word comments effect)
6922
            html = html.replace( /<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3");
6923
            html = html.replace( /<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3");
6924
 
6925
            return html;
6926
        },
6927
        /**
6928
        * @method filter_invalid_lists
6929
        * @param String html The HTML string to filter
6930
        * @description Filters invalid ol and ul list markup, converts this: <li></li><ol>..</ol> to this: <li></li><li><ol>..</ol></li>
6931
        */
6932
        filter_invalid_lists: function(html) {
6933
            html = html.replace(/<\/li>\n/gi, '</li>');
6934
 
6935
            html = html.replace(/<\/li><ol>/gi, '</li><li><ol>');
6936
            html = html.replace(/<\/ol>/gi, '</ol></li>');
6937
            html = html.replace(/<\/ol><\/li>\n/gi, "</ol>");
6938
 
6939
            html = html.replace(/<\/li><ul>/gi, '</li><li><ul>');
6940
            html = html.replace(/<\/ul>/gi, '</ul></li>');
6941
            html = html.replace(/<\/ul><\/li>\n?/gi, "</ul>");
6942
 
6943
            html = html.replace(/<\/li>/gi, "</li>");
6944
            html = html.replace(/<\/ol>/gi, "</ol>");
6945
            html = html.replace(/<ol>/gi, "<ol>");
6946
            html = html.replace(/<ul>/gi, "<ul>");
6947
            return html;
6948
        },
6949
        /**
6950
        * @method filter_safari
6951
        * @param String html The HTML string to filter
6952
        * @description Filters strings specific to Safari
6953
        * @return String
6954
        */
6955
        filter_safari: function(html) {
6956
            if (this.browser.webkit) {
6957
                //<span class="Apple-tab-span" style="white-space:pre">	</span>
6958
                html = html.replace(/<span class="Apple-tab-span" style="white-space:pre">([^>])<\/span>/gi, '&nbsp;&nbsp;&nbsp;&nbsp;');
6959
                html = html.replace(/Apple-style-span/gi, '');
6960
                html = html.replace(/style="line-height: normal;"/gi, '');
6961
                html = html.replace(/yui-wk-div/gi, '');
6962
                html = html.replace(/yui-wk-p/gi, '');
6963
 
6964
 
6965
                //Remove bogus LI's
6966
                html = html.replace(/<li><\/li>/gi, '');
6967
                html = html.replace(/<li> <\/li>/gi, '');
6968
                html = html.replace(/<li>  <\/li>/gi, '');
6969
                //Remove bogus DIV's - updated from just removing the div's to replacing /div with a break
6970
                if (this.get('ptags')) {
6971
		            html = html.replace(/<div([^>]*)>/g, '<p$1>');
6972
				    html = html.replace(/<\/div>/gi, '</p>');
6973
                } else {
6974
                    //html = html.replace(/<div>/gi, '<br>');
6975
                    html = html.replace(/<div([^>]*)>([ tnr]*)<\/div>/gi, '<br>');
6976
				    html = html.replace(/<\/div>/gi, '');
6977
                }
6978
            }
6979
            return html;
6980
        },
6981
        /**
6982
        * @method filter_internals
6983
        * @param String html The HTML string to filter
6984
        * @description Filters internal RTE strings and bogus attrs we don't want
6985
        * @return String
6986
        */
6987
        filter_internals: function(html) {
6988
		    html = html.replace(/\r/g, '');
6989
            //Fix stuff we don't want
6990
	        html = html.replace(/<\/?(body|head|html)[^>]*>/gi, '');
6991
            //Fix last BR in LI
6992
		    html = html.replace(/<YUI_BR><\/li>/gi, '</li>');
6993
 
6994
		    html = html.replace(/yui-tag-span/gi, '');
6995
		    html = html.replace(/yui-tag/gi, '');
6996
		    html = html.replace(/yui-non/gi, '');
6997
		    html = html.replace(/yui-img/gi, '');
6998
		    html = html.replace(/ tag="span"/gi, '');
6999
		    html = html.replace(/ class=""/gi, '');
7000
		    html = html.replace(/ style=""/gi, '');
7001
		    html = html.replace(/ class=" "/gi, '');
7002
		    html = html.replace(/ class="  "/gi, '');
7003
		    html = html.replace(/ target=""/gi, '');
7004
		    html = html.replace(/ title=""/gi, '');
7005
 
7006
            if (this.browser.ie) {
7007
		        html = html.replace(/ class= /gi, '');
7008
		        html = html.replace(/ class= >/gi, '');
7009
            }
7010
 
7011
            return html;
7012
        },
7013
        /**
7014
        * @method filter_all_rgb
7015
        * @param String str The HTML string to filter
7016
        * @description Converts all RGB color strings found in passed string to a hex color, example: style="color: rgb(0, 255, 0)" converts to style="color: #00ff00"
7017
        * @return String
7018
        */
7019
        filter_all_rgb: function(str) {
7020
            var exp = new RegExp("rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)", "gi");
7021
            var arr = str.match(exp);
7022
            if (Lang.isArray(arr)) {
7023
                for (var i = 0; i < arr.length; i++) {
7024
                    var color = this.filter_rgb(arr[i]);
7025
                    str = str.replace(arr[i].toString(), color);
7026
                }
7027
            }
7028
 
7029
            return str;
7030
        },
7031
        /**
7032
        * @method filter_rgb
7033
        * @param String css The CSS string containing rgb(#,#,#);
7034
        * @description Converts an RGB color string to a hex color, example: rgb(0, 255, 0) converts to #00ff00
7035
        * @return String
7036
        */
7037
        filter_rgb: function(css) {
7038
            if (css.toLowerCase().indexOf('rgb') != -1) {
7039
                var exp = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi");
7040
                var rgb = css.replace(exp, "$1,$2,$3,$4,$5").split(',');
7041
 
7042
                if (rgb.length == 5) {
7043
                    var r = parseInt(rgb[1], 10).toString(16);
7044
                    var g = parseInt(rgb[2], 10).toString(16);
7045
                    var b = parseInt(rgb[3], 10).toString(16);
7046
 
7047
                    r = r.length == 1 ? '0' + r : r;
7048
                    g = g.length == 1 ? '0' + g : g;
7049
                    b = b.length == 1 ? '0' + b : b;
7050
 
7051
                    css = "#" + r + g + b;
7052
                }
7053
            }
7054
            return css;
7055
        },
7056
        /**
7057
        * @method pre_filter_linebreaks
7058
        * @param String html The HTML to filter
7059
        * @param String markup The markup type to filter to
7060
        * @description HTML Pre Filter
7061
        * @return String
7062
        */
7063
        pre_filter_linebreaks: function(html, markup) {
7064
            if (this.browser.webkit) {
7065
		        html = html.replace(/<br class="khtml-block-placeholder">/gi, '<YUI_BR>');
7066
		        html = html.replace(/<br class="webkit-block-placeholder">/gi, '<YUI_BR>');
7067
            }
7068
		    html = html.replace(/<br>/gi, '<YUI_BR>');
7069
		    html = html.replace(/<br (.*?)>/gi, '<YUI_BR>');
7070
		    html = html.replace(/<br\/>/gi, '<YUI_BR>');
7071
		    html = html.replace(/<br \/>/gi, '<YUI_BR>');
7072
		    html = html.replace(/<div><YUI_BR><\/div>/gi, '<YUI_BR>');
7073
		    html = html.replace(/<p>(&nbsp;|&#160;)<\/p>/g, '<YUI_BR>');
7074
		    html = html.replace(/<p><br>&nbsp;<\/p>/gi, '<YUI_BR>');
7075
		    html = html.replace(/<p>&nbsp;<\/p>/gi, '<YUI_BR>');
7076
            //Fix last BR
7077
	        html = html.replace(/<YUI_BR>$/, '');
7078
            //Fix last BR in P
7079
	        html = html.replace(/<YUI_BR><\/p>/g, '</p>');
7080
            if (this.browser.ie) {
7081
	            html = html.replace(/&nbsp;&nbsp;&nbsp;&nbsp;/g, '\t');
7082
            }
7083
            return html;
7084
        },
7085
        /**
7086
        * @method post_filter_linebreaks
7087
        * @param String html The HTML to filter
7088
        * @param String markup The markup type to filter to
7089
        * @description HTML Pre Filter
7090
        * @return String
7091
        */
7092
        post_filter_linebreaks: function(html, markup) {
7093
            if (markup == 'xhtml') {
7094
		        html = html.replace(/<YUI_BR>/g, '<br />');
7095
            } else {
7096
		        html = html.replace(/<YUI_BR>/g, '<br>');
7097
            }
7098
            return html;
7099
        },
7100
        /**
7101
        * @method clearEditorDoc
7102
        * @description Clear the doc of the Editor
7103
        */
7104
        clearEditorDoc: function() {
7105
            this._getDoc().body.innerHTML = '&nbsp;';
7106
        },
7107
        /**
7108
        * @method openWindow
7109
        * @description Override Method for Advanced Editor
7110
        */
7111
        openWindow: function(win) {
7112
        },
7113
        /**
7114
        * @method moveWindow
7115
        * @description Override Method for Advanced Editor
7116
        */
7117
        moveWindow: function() {
7118
        },
7119
        /**
7120
        * @private
7121
        * @method _closeWindow
7122
        * @description Override Method for Advanced Editor
7123
        */
7124
        _closeWindow: function() {
7125
        },
7126
        /**
7127
        * @method closeWindow
7128
        * @description Override Method for Advanced Editor
7129
        */
7130
        closeWindow: function() {
7131
            //this.unsubscribeAll('afterExecCommand');
7132
            this.toolbar.resetAllButtons();
7133
            this.focus();
7134
        },
7135
        /**
7136
        * @method destroy
7137
        * @description Destroys the editor, all of it's elements and objects.
7138
        * @return {Boolean}
7139
        */
7140
        destroy: function() {
7141
            if (this._nodeChangeDelayTimer) {
7142
                clearTimeout(this._nodeChangeDelayTimer);
7143
            }
7144
            this.hide();
7145
 
7146
            if (this.resize) {
7147
                this.resize.destroy();
7148
            }
7149
            if (this.dd) {
7150
                this.dd.unreg();
7151
            }
7152
            if (this.get('panel')) {
7153
                this.get('panel').destroy();
7154
            }
7155
            this.saveHTML();
7156
            this.toolbar.destroy();
7157
            this.setStyle('visibility', 'visible');
7158
            this.setStyle('position', 'static');
7159
            this.setStyle('top', '');
7160
            this.setStyle('left', '');
7161
            var textArea = this.get('element');
7162
            this.get('element_cont').get('parentNode').replaceChild(textArea, this.get('element_cont').get('element'));
7163
            this.get('element_cont').get('element').innerHTML = '';
7164
            this.set('handleSubmit', false); //Remove the submit handler
7165
            return true;
7166
        },
7167
        /**
7168
        * @method toString
7169
        * @description Returns a string representing the editor.
7170
        * @return {String}
7171
        */
7172
        toString: function() {
7173
            var str = 'SimpleEditor';
7174
            if (this.get && this.get('element_cont')) {
7175
                str = 'SimpleEditor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
7176
            }
7177
            return str;
7178
        }
7179
    });
7180
 
7181
/**
7182
* @event toolbarLoaded
7183
* @description Event is fired during the render process directly after the Toolbar is loaded. Allowing you to attach events to the toolbar. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7184
* @type YAHOO.util.CustomEvent
7185
*/
7186
/**
7187
* @event cleanHTML
7188
* @description Event is fired after the cleanHTML method is called.
7189
* @type YAHOO.util.CustomEvent
7190
*/
7191
/**
7192
* @event afterRender
7193
* @description Event is fired after the render process finishes. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7194
* @type YAHOO.util.CustomEvent
7195
*/
7196
/**
7197
* @event editorContentLoaded
7198
* @description Event is fired after the editor iframe's document fully loads and fires it's onload event. From here you can start injecting your own things into the document. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7199
* @type YAHOO.util.CustomEvent
7200
*/
7201
/**
7202
* @event beforeNodeChange
7203
* @description Event fires at the beginning of the nodeChange process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7204
* @type YAHOO.util.CustomEvent
7205
*/
7206
/**
7207
* @event afterNodeChange
7208
* @description Event fires at the end of the nodeChange process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7209
* @type YAHOO.util.CustomEvent
7210
*/
7211
/**
7212
* @event beforeExecCommand
7213
* @description Event fires at the beginning of the execCommand process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7214
* @type YAHOO.util.CustomEvent
7215
*/
7216
/**
7217
* @event afterExecCommand
7218
* @description Event fires at the end of the execCommand process. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7219
* @type YAHOO.util.CustomEvent
7220
*/
7221
/**
7222
* @event editorMouseUp
7223
* @param {Event} ev The DOM Event that occured
7224
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7225
* @type YAHOO.util.CustomEvent
7226
*/
7227
/**
7228
* @event editorMouseDown
7229
* @param {Event} ev The DOM Event that occured
7230
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7231
* @type YAHOO.util.CustomEvent
7232
*/
7233
/**
7234
* @event editorDoubleClick
7235
* @param {Event} ev The DOM Event that occured
7236
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7237
* @type YAHOO.util.CustomEvent
7238
*/
7239
/**
7240
* @event editorClick
7241
* @param {Event} ev The DOM Event that occured
7242
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7243
* @type YAHOO.util.CustomEvent
7244
*/
7245
/**
7246
* @event editorKeyUp
7247
* @param {Event} ev The DOM Event that occured
7248
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7249
* @type YAHOO.util.CustomEvent
7250
*/
7251
/**
7252
* @event editorKeyPress
7253
* @param {Event} ev The DOM Event that occured
7254
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7255
* @type YAHOO.util.CustomEvent
7256
*/
7257
/**
7258
* @event editorKeyDown
7259
* @param {Event} ev The DOM Event that occured
7260
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7261
* @type YAHOO.util.CustomEvent
7262
*/
7263
/**
7264
* @event beforeEditorMouseUp
7265
* @param {Event} ev The DOM Event that occured
7266
* @description Fires before editor event, returning false will stop the internal processing.
7267
* @type YAHOO.util.CustomEvent
7268
*/
7269
/**
7270
* @event beforeEditorMouseDown
7271
* @param {Event} ev The DOM Event that occured
7272
* @description Fires before editor event, returning false will stop the internal processing.
7273
* @type YAHOO.util.CustomEvent
7274
*/
7275
/**
7276
* @event beforeEditorDoubleClick
7277
* @param {Event} ev The DOM Event that occured
7278
* @description Fires before editor event, returning false will stop the internal processing.
7279
* @type YAHOO.util.CustomEvent
7280
*/
7281
/**
7282
* @event beforeEditorClick
7283
* @param {Event} ev The DOM Event that occured
7284
* @description Fires before editor event, returning false will stop the internal processing.
7285
* @type YAHOO.util.CustomEvent
7286
*/
7287
/**
7288
* @event beforeEditorKeyUp
7289
* @param {Event} ev The DOM Event that occured
7290
* @description Fires before editor event, returning false will stop the internal processing.
7291
* @type YAHOO.util.CustomEvent
7292
*/
7293
/**
7294
* @event beforeEditorKeyPress
7295
* @param {Event} ev The DOM Event that occured
7296
* @description Fires before editor event, returning false will stop the internal processing.
7297
* @type YAHOO.util.CustomEvent
7298
*/
7299
/**
7300
* @event beforeEditorKeyDown
7301
* @param {Event} ev The DOM Event that occured
7302
* @description Fires before editor event, returning false will stop the internal processing.
7303
* @type YAHOO.util.CustomEvent
7304
*/
7305
 
7306
/**
7307
* @event editorWindowFocus
7308
* @description Fires when the iframe is focused. Note, this is window focus event, not an Editor focus event.
7309
* @type YAHOO.util.CustomEvent
7310
*/
7311
/**
7312
* @event editorWindowBlur
7313
* @description Fires when the iframe is blurred. Note, this is window blur event, not an Editor blur event.
7314
* @type YAHOO.util.CustomEvent
7315
*/
7316
 
7317
 
7318
/**
7319
 * @description Singleton object used to track the open window objects and panels across the various open editors
7320
 * @class EditorInfo
7321
 * @static
7322
*/
7323
YAHOO.widget.EditorInfo = {
7324
    /**
7325
    * @private
7326
    * @property _instances
7327
    * @description A reference to all editors on the page.
7328
    * @type Object
7329
    */
7330
    _instances: {},
7331
    /**
7332
    * @private
7333
    * @property blankImage
7334
    * @description A reference to the blankImage url
7335
    * @type String
7336
    */
7337
    blankImage: '',
7338
    /**
7339
    * @private
7340
    * @property window
7341
    * @description A reference to the currently open window object in any editor on the page.
7342
    * @type Object <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>
7343
    */
7344
    window: {},
7345
    /**
7346
    * @private
7347
    * @property panel
7348
    * @description A reference to the currently open panel in any editor on the page.
7349
    * @type Object <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>
7350
    */
7351
    panel: null,
7352
    /**
7353
    * @method getEditorById
7354
    * @description Returns a reference to the Editor object associated with the given textarea
7355
    * @param {String/HTMLElement} id The id or reference of the textarea to return the Editor instance of
7356
    * @return Object <a href="YAHOO.widget.Editor.html">YAHOO.widget.Editor</a>
7357
    */
7358
    getEditorById: function(id) {
7359
        if (!YAHOO.lang.isString(id)) {
7360
            //Not a string, assume a node Reference
7361
            id = id.id;
7362
        }
7363
        if (this._instances[id]) {
7364
            return this._instances[id];
7365
        }
7366
        return false;
7367
    },
7368
    /**
7369
    * @method saveAll
7370
    * @description Saves all Editor instances on the page. If a form reference is passed, only Editor's bound to this form will be saved.
7371
    * @param {HTMLElement} form The form to check if this Editor instance belongs to
7372
    */
7373
    saveAll: function(form) {
7374
        var i, e, items = YAHOO.widget.EditorInfo._instances;
7375
        if (form) {
7376
            for (i in items) {
7377
                if (Lang.hasOwnProperty(items, i)) {
7378
                    e = items[i];
7379
                    if (e.get('element').form && (e.get('element').form == form)) {
7380
                        e.saveHTML();
7381
                    }
7382
                }
7383
            }
7384
        } else {
7385
            for (i in items) {
7386
                if (Lang.hasOwnProperty(items, i)) {
7387
                    items[i].saveHTML();
7388
                }
7389
            }
7390
        }
7391
    },
7392
    /**
7393
    * @method toString
7394
    * @description Returns a string representing the EditorInfo.
7395
    * @return {String}
7396
    */
7397
    toString: function() {
7398
        var len = 0;
7399
        for (var i in this._instances) {
7400
            if (Lang.hasOwnProperty(this._instances, i)) {
7401
                len++;
7402
            }
7403
        }
7404
        return 'Editor Info (' + len + ' registered intance' + ((len > 1) ? 's' : '') + ')';
7405
    }
7406
};
7407
 
7408
 
7409
 
7410
 
7411
})();
7412
YAHOO.register("simpleeditor", YAHOO.widget.SimpleEditor, {version: "2.9.0", build: "2800"});
7413
 
7414
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-skin-sam-simpleeditor", "yui2-event", "yui2-element"], "optional": ["yui2-containercore", "yui2-dragdrop", "yui2-skin-sam-button", "yui2-skin-sam-menu", "yui2-menu", "yui2-button", "yui2-animation"]});