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