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