]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blob - wradmin/wradmin/public/yui/editor/editor-debug.js
032dfc16cda1fe03c2663e50a7f270c1fc45022a
[philipp/winterrodeln/wradmin.git] / wradmin / wradmin / public / yui / editor / editor-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 /**
7257  * @module editor
7258  * @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>
7259  * @namespace YAHOO.widget
7260  * @requires yahoo, dom, element, event, container_core, simpleeditor
7261  * @optional dragdrop, animation, menu, button, resize
7262  */
7263
7264 (function() {
7265 var Dom = YAHOO.util.Dom,
7266     Event = YAHOO.util.Event,
7267     Lang = YAHOO.lang,
7268     Toolbar = YAHOO.widget.Toolbar;
7269
7270     /**
7271      * 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.
7272      * @constructor
7273      * @class Editor
7274      * @extends YAHOO.widget.SimpleEditor
7275      * @param {String/HTMLElement} el The textarea element to turn into an editor.
7276      * @param {Object} attrs Object liternal containing configuration parameters.
7277     */
7278     
7279     YAHOO.widget.Editor = function(el, attrs) {
7280         YAHOO.log('Editor Initalizing', 'info', 'Editor');
7281         YAHOO.widget.Editor.superclass.constructor.call(this, el, attrs);
7282     };
7283
7284     YAHOO.extend(YAHOO.widget.Editor, YAHOO.widget.SimpleEditor, {
7285         /**
7286         * @private
7287         * @property _undoCache
7288         * @description An Array hash of the Undo Levels.
7289         * @type Array
7290         */
7291         _undoCache: null,
7292         /**
7293         * @private
7294         * @property _undoLevel
7295         * @description The index of the current undo state.
7296         * @type Number
7297         */
7298         _undoLevel: null,    
7299         /**
7300         * @private
7301         * @method _hasUndoLevel
7302         * @description Checks to see if we have an undo level available
7303         * @return Boolean
7304         */
7305         _hasUndoLevel: function() {
7306             return ((this._undoCache.length > 1) && this._undoLevel);
7307         },
7308         /**
7309         * @private
7310         * @method _undoNodeChange
7311         * @description nodeChange listener for undo processing
7312         */
7313         _undoNodeChange: function() {
7314             var undo_button = this.toolbar.getButtonByValue('undo'),
7315                 redo_button = this.toolbar.getButtonByValue('redo');
7316             if (undo_button && redo_button) {
7317                 if (this._hasUndoLevel()) {
7318                     this.toolbar.enableButton(undo_button);
7319                 }
7320                 if (this._undoLevel < this._undoCache.length) {
7321                     this.toolbar.enableButton(redo_button);
7322                 }
7323             }
7324             this._lastCommand = null;
7325         },
7326         /**
7327         * @private
7328         * @method _checkUndo
7329         * @description Prunes the undo cache when it reaches the maxUndo config
7330         */
7331         _checkUndo: function() {
7332             var len = this._undoCache.length,
7333             tmp = [];
7334             if (len >= this.get('maxUndo')) {
7335                 //YAHOO.log('Undo cache too large (' + len + '), pruning..', 'info', 'SimpleEditor');
7336                 for (var i = (len - this.get('maxUndo')); i < len; i++) {
7337                     tmp.push(this._undoCache[i]);
7338                 }
7339                 this._undoCache = tmp;
7340             }
7341         },
7342         /**
7343         * @private
7344         * @method _putUndo
7345         * @description Puts the content of the Editor into the _undoCache.
7346         * //TODO Convert the hash to a series of TEXTAREAS to store state in.
7347         * @param {String} str The content of the Editor
7348         */
7349         _putUndo: function(str) {
7350             this._undoCache.push(str);
7351         },
7352         /**
7353         * @private
7354         * @method _getUndo
7355         * @description Get's a level from the undo cache.
7356         * @param {Number} index The index of the undo level we want to get.
7357         * @return {String}
7358         */
7359         _getUndo: function(index) {
7360             return this._undoCache[index];
7361         },
7362         /**
7363         * @private
7364         * @method _storeUndo
7365         * @description Method to call when you want to store an undo state. Currently called from nodeChange and _handleKeyUp
7366         */
7367         _storeUndo: function() {
7368             if (this._lastCommand === 'undo' || this._lastCommand === 'redo') {
7369                 return false;
7370             }
7371             if (!this._undoCache) {
7372                 this._undoCache = [];
7373             }
7374             this._checkUndo();
7375             var str = this.getEditorHTML();
7376             var last = this._undoCache[this._undoCache.length - 1];
7377             if (last) {
7378                 if (str !== last) {
7379                     //YAHOO.log('Storing Undo', 'info', 'SimpleEditor');
7380                     this._putUndo(str);
7381                 }
7382             } else {
7383                 //YAHOO.log('Storing Undo', 'info', 'SimpleEditor');
7384                 this._putUndo(str);
7385             }
7386             this._undoLevel = this._undoCache.length;
7387             this._undoNodeChange();
7388         },    
7389         /**
7390         * @property STR_BEFORE_EDITOR
7391         * @description The accessibility string for the element before the iFrame
7392         * @type String
7393         */
7394         STR_BEFORE_EDITOR: 'This text field can contain stylized text and graphics. To cycle through all formatting options, use the keyboard shortcut Control + Shift + T to place focus on the toolbar and navigate between option heading names. <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 [ aligns text left</li> <li>Control Shift | centers text</li> <li>Control Shift ] aligns text right</li> <li>Control Shift L adds an HTML link</li> <li>To exit this text editor use the keyboard shortcut Control + Shift + ESC.</li></ul>',    
7395         /**
7396         * @property STR_CLOSE_WINDOW
7397         * @description The Title of the close button in the Editor Window
7398         * @type String
7399         */
7400         STR_CLOSE_WINDOW: 'Close Window',
7401         /**
7402         * @property STR_CLOSE_WINDOW_NOTE
7403         * @description A note appearing in the Editor Window to tell the user that the Escape key will close the window
7404         * @type String
7405         */
7406         STR_CLOSE_WINDOW_NOTE: 'To close this window use the Control + Shift + W key',
7407         /**
7408         * @property STR_IMAGE_PROP_TITLE
7409         * @description The title for the Image Property Editor Window
7410         * @type String
7411         */
7412         STR_IMAGE_PROP_TITLE: 'Image Options',
7413         /**
7414         * @property STR_IMAGE_TITLE
7415         * @description The label string for Image Description
7416         * @type String
7417         */
7418         STR_IMAGE_TITLE: 'Description',
7419         /**
7420         * @property STR_IMAGE_SIZE
7421         * @description The label string for Image Size
7422         * @type String
7423         */
7424         STR_IMAGE_SIZE: 'Size',
7425         /**
7426         * @property STR_IMAGE_ORIG_SIZE
7427         * @description The label string for Original Image Size
7428         * @type String
7429         */
7430         STR_IMAGE_ORIG_SIZE: 'Original Size',
7431         /**
7432         * @property STR_IMAGE_COPY
7433         * @description The label string for the image copy and paste message for Opera and Safari
7434         * @type String
7435         */
7436         STR_IMAGE_COPY: '<span class="tip"><span class="icon icon-info"></span><strong>Note:</strong>To move this image just highlight it, cut, and paste where ever you\'d like.</span>',
7437         /**
7438         * @property STR_IMAGE_PADDING
7439         * @description The label string for the image padding.
7440         * @type String
7441         */
7442         STR_IMAGE_PADDING: 'Padding',
7443         /**
7444         * @property STR_IMAGE_BORDER
7445         * @description The label string for the image border.
7446         * @type String
7447         */
7448         STR_IMAGE_BORDER: 'Border',
7449         /**
7450         * @property STR_IMAGE_BORDER_SIZE
7451         * @description The label string for the image border size.
7452         * @type String
7453         */
7454         STR_IMAGE_BORDER_SIZE: 'Border Size',
7455         /**
7456         * @property STR_IMAGE_BORDER_TYPE
7457         * @description The label string for the image border type.
7458         * @type String
7459         */
7460         STR_IMAGE_BORDER_TYPE: 'Border Type',
7461         /**
7462         * @property STR_IMAGE_TEXTFLOW
7463         * @description The label string for the image text flow.
7464         * @type String
7465         */
7466         STR_IMAGE_TEXTFLOW: 'Text Flow',
7467         /**
7468         * @property STR_LOCAL_FILE_WARNING
7469         * @description The label string for the local file warning.
7470         * @type String
7471         */
7472         STR_LOCAL_FILE_WARNING: '<span class="tip"><span class="icon icon-warn"></span><strong>Note:</strong>This image/link points to a file on your computer and will not be accessible to others on the internet.</span>',
7473         /**
7474         * @property STR_LINK_PROP_TITLE
7475         * @description The label string for the Link Property Editor Window.
7476         * @type String
7477         */
7478         STR_LINK_PROP_TITLE: 'Link Options',
7479         /**
7480         * @property STR_LINK_PROP_REMOVE
7481         * @description The label string for the Remove link from text link inside the property editor.
7482         * @type String
7483         */
7484         STR_LINK_PROP_REMOVE: 'Remove link from text',
7485         /**
7486         * @property STR_LINK_NEW_WINDOW
7487         * @description The string for the open in a new window label.
7488         * @type String
7489         */
7490         STR_LINK_NEW_WINDOW: 'Open in a new window.',
7491         /**
7492         * @property STR_LINK_TITLE
7493         * @description The string for the link description.
7494         * @type String
7495         */
7496         STR_LINK_TITLE: 'Description',
7497         /**
7498         * @property STR_NONE
7499         * @description The string for the word none.
7500         * @type String
7501         */
7502         STR_NONE: 'none',
7503         /**
7504         * @protected
7505         * @property CLASS_LOCAL_FILE
7506         * @description CSS class applied to an element when it's found to have a local url.
7507         * @type String
7508         */
7509         CLASS_LOCAL_FILE: 'warning-localfile',
7510         /**
7511         * @protected
7512         * @property CLASS_HIDDEN
7513         * @description CSS class applied to the body when the hiddenelements button is pressed.
7514         * @type String
7515         */
7516         CLASS_HIDDEN: 'yui-hidden',
7517         /** 
7518         * @method init
7519         * @description The Editor class' initialization method
7520         */
7521         init: function(p_oElement, p_oAttributes) {
7522             YAHOO.log('init', 'info', 'Editor');
7523             
7524             this._windows = {};
7525             this._defaultToolbar = {
7526                 collapse: true,
7527                 titlebar: 'Text Editing Tools',
7528                 draggable: false,
7529                 buttonType: 'advanced',
7530                 buttons: [
7531                     { group: 'fontstyle', label: 'Font Name and Size',
7532                         buttons: [
7533                             { type: 'select', label: 'Arial', value: 'fontname', disabled: true,
7534                                 menu: [
7535                                     { text: 'Arial', checked: true },
7536                                     { text: 'Arial Black' },
7537                                     { text: 'Comic Sans MS' },
7538                                     { text: 'Courier New' },
7539                                     { text: 'Lucida Console' },
7540                                     { text: 'Tahoma' },
7541                                     { text: 'Times New Roman' },
7542                                     { text: 'Trebuchet MS' },
7543                                     { text: 'Verdana' }
7544                                 ]
7545                             },
7546                             { type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
7547                         ]
7548                     },
7549                     { type: 'separator' },
7550                     { group: 'textstyle', label: 'Font Style',
7551                         buttons: [
7552                             { type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
7553                             { type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
7554                             { type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
7555                             { type: 'separator' },
7556                             { type: 'push', label: 'Subscript', value: 'subscript', disabled: true },
7557                             { type: 'push', label: 'Superscript', value: 'superscript', disabled: true }
7558                         ]
7559                     },
7560                     { type: 'separator' },
7561                     { group: 'textstyle2', label: '&nbsp;',
7562                         buttons: [
7563                             { type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
7564                             { type: 'color', label: 'Background Color', value: 'backcolor', disabled: true },
7565                             { type: 'separator' },
7566                             { type: 'push', label: 'Remove Formatting', value: 'removeformat', disabled: true },
7567                             { type: 'push', label: 'Show/Hide Hidden Elements', value: 'hiddenelements' }
7568                         ]
7569                     },
7570                     { type: 'separator' },
7571                     { group: 'undoredo', label: 'Undo/Redo',
7572                         buttons: [
7573                             { type: 'push', label: 'Undo', value: 'undo', disabled: true },
7574                             { type: 'push', label: 'Redo', value: 'redo', disabled: true }
7575                             
7576                         ]
7577                     },
7578                     { type: 'separator' },
7579                     { group: 'alignment', label: 'Alignment',
7580                         buttons: [
7581                             { type: 'push', label: 'Align Left CTRL + SHIFT + [', value: 'justifyleft' },
7582                             { type: 'push', label: 'Align Center CTRL + SHIFT + |', value: 'justifycenter' },
7583                             { type: 'push', label: 'Align Right CTRL + SHIFT + ]', value: 'justifyright' },
7584                             { type: 'push', label: 'Justify', value: 'justifyfull' }
7585                         ]
7586                     },
7587                     { type: 'separator' },
7588                     { group: 'parastyle', label: 'Paragraph Style',
7589                         buttons: [
7590                         { type: 'select', label: 'Normal', value: 'heading', disabled: true,
7591                             menu: [
7592                                 { text: 'Normal', value: 'none', checked: true },
7593                                 { text: 'Header 1', value: 'h1' },
7594                                 { text: 'Header 2', value: 'h2' },
7595                                 { text: 'Header 3', value: 'h3' },
7596                                 { text: 'Header 4', value: 'h4' },
7597                                 { text: 'Header 5', value: 'h5' },
7598                                 { text: 'Header 6', value: 'h6' }
7599                             ]
7600                         }
7601                         ]
7602                     },
7603                     { type: 'separator' },
7604                     
7605                     { group: 'indentlist2', label: 'Indenting and Lists',
7606                         buttons: [
7607                             { type: 'push', label: 'Indent', value: 'indent', disabled: true },
7608                             { type: 'push', label: 'Outdent', value: 'outdent', disabled: true },
7609                             { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
7610                             { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
7611                         ]
7612                     },
7613                     { type: 'separator' },
7614                     { group: 'insertitem', label: 'Insert Item',
7615                         buttons: [
7616                             { type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
7617                             { type: 'push', label: 'Insert Image', value: 'insertimage' }
7618                         ]
7619                     }
7620                 ]
7621             };
7622
7623             this._defaultImageToolbarConfig = {
7624                 buttonType: this._defaultToolbar.buttonType,
7625                 buttons: [
7626                     { group: 'textflow', label: this.STR_IMAGE_TEXTFLOW + ':',
7627                         buttons: [
7628                             { type: 'push', label: 'Left', value: 'left' },
7629                             { type: 'push', label: 'Inline', value: 'inline' },
7630                             { type: 'push', label: 'Block', value: 'block' },
7631                             { type: 'push', label: 'Right', value: 'right' }
7632                         ]
7633                     },
7634                     { type: 'separator' },
7635                     { group: 'padding', label: this.STR_IMAGE_PADDING + ':',
7636                         buttons: [
7637                             { type: 'spin', label: '0', value: 'padding', range: [0, 50] }
7638                         ]
7639                     },
7640                     { type: 'separator' },
7641                     { group: 'border', label: this.STR_IMAGE_BORDER + ':',
7642                         buttons: [
7643                             { type: 'select', label: this.STR_IMAGE_BORDER_SIZE, value: 'bordersize',
7644                                 menu: [
7645                                     { text: 'none', value: '0', checked: true },
7646                                     { text: '1px', value: '1' },
7647                                     { text: '2px', value: '2' },
7648                                     { text: '3px', value: '3' },
7649                                     { text: '4px', value: '4' },
7650                                     { text: '5px', value: '5' }
7651                                 ]
7652                             },
7653                             { type: 'select', label: this.STR_IMAGE_BORDER_TYPE, value: 'bordertype', disabled: true,
7654                                 menu: [
7655                                     { text: 'Solid', value: 'solid', checked: true },
7656                                     { text: 'Dashed', value: 'dashed' },
7657                                     { text: 'Dotted', value: 'dotted' }
7658                                 ]
7659                             },
7660                             { type: 'color', label: 'Border Color', value: 'bordercolor', disabled: true }
7661                         ]
7662                     }
7663                 ]
7664             };
7665
7666             YAHOO.widget.Editor.superclass.init.call(this, p_oElement, p_oAttributes);
7667         },
7668         _render: function() {
7669             YAHOO.widget.Editor.superclass._render.apply(this, arguments);
7670             var self = this;
7671             //Render the panel in another thread and delay it a little..
7672             window.setTimeout(function() {
7673                 self._renderPanel.call(self);
7674             }, 800);
7675         },
7676         /**
7677         * @method initAttributes
7678         * @description Initializes all of the configuration attributes used to create 
7679         * the editor.
7680         * @param {Object} attr Object literal specifying a set of 
7681         * configuration attributes used to create the editor.
7682         */
7683         initAttributes: function(attr) {
7684             YAHOO.widget.Editor.superclass.initAttributes.call(this, attr);
7685
7686             /**
7687             * @attribute localFileWarning
7688             * @description Should we throw the warning if we detect a file that is local to their machine?
7689             * @default true
7690             * @type Boolean
7691             */            
7692             this.setAttributeConfig('localFileWarning', {
7693                 value: attr.locaFileWarning || true
7694             });
7695
7696             /**
7697             * @attribute hiddencss
7698             * @description The CSS used to show/hide hidden elements on the page, these rules must be prefixed with the class provided in <code>this.CLASS_HIDDEN</code>
7699             * @default <code><pre>
7700             .yui-hidden font, .yui-hidden strong, .yui-hidden b, .yui-hidden em, .yui-hidden i, .yui-hidden u,
7701             .yui-hidden div, .yui-hidden p, .yui-hidden span, .yui-hidden img, .yui-hidden ul, .yui-hidden ol,
7702             .yui-hidden li, .yui-hidden table {
7703                 border: 1px dotted #ccc;
7704             }
7705             .yui-hidden .yui-non {
7706                 border: none;
7707             }
7708             .yui-hidden img {
7709                 padding: 2px;
7710             }</pre></code>
7711             * @type String
7712             */            
7713             this.setAttributeConfig('hiddencss', {
7714                 value: attr.hiddencss || '.yui-hidden font, .yui-hidden strong, .yui-hidden b, .yui-hidden em, .yui-hidden i, .yui-hidden u, .yui-hidden div,.yui-hidden p,.yui-hidden span,.yui-hidden img, .yui-hidden ul, .yui-hidden ol, .yui-hidden li, .yui-hidden table { border: 1px dotted #ccc; } .yui-hidden .yui-non { border: none; } .yui-hidden img { padding: 2px; }',
7715                 writeOnce: true
7716             });
7717            
7718         },
7719         /**
7720         * @private
7721         * @method _windows
7722         * @description A reference to the HTML elements used for the body of Editor Windows.
7723         */
7724         _windows: null,
7725         /**
7726         * @private
7727         * @method _defaultImageToolbar
7728         * @description A reference to the Toolbar Object inside Image Editor Window.
7729         */
7730         _defaultImageToolbar: null,
7731         /**
7732         * @private
7733         * @method _defaultImageToolbarConfig
7734         * @description Config to be used for the default Image Editor Window.
7735         */
7736         _defaultImageToolbarConfig: null,
7737         /**
7738         * @private
7739         * @method _fixNodes
7740         * @description Fix href and imgs as well as remove invalid HTML.
7741         */
7742         _fixNodes: function() {
7743             YAHOO.widget.Editor.superclass._fixNodes.call(this);
7744             var url = '';
7745
7746             var imgs = this._getDoc().getElementsByTagName('img');
7747             for (var im = 0; im < imgs.length; im++) {
7748                 if (imgs[im].getAttribute('href', 2)) {
7749                     url = imgs[im].getAttribute('src', 2);
7750                     if (this._isLocalFile(url)) {
7751                         Dom.addClass(imgs[im], this.CLASS_LOCAL_FILE);
7752                     } else {
7753                         Dom.removeClass(imgs[im], this.CLASS_LOCAL_FILE);
7754                     }
7755                 }
7756             }
7757             var fakeAs = this._getDoc().body.getElementsByTagName('a');
7758             for (var a = 0; a < fakeAs.length; a++) {
7759                 if (fakeAs[a].getAttribute('href', 2)) {
7760                     url = fakeAs[a].getAttribute('href', 2);
7761                     if (this._isLocalFile(url)) {
7762                         Dom.addClass(fakeAs[a], this.CLASS_LOCAL_FILE);
7763                     } else {
7764                         Dom.removeClass(fakeAs[a], this.CLASS_LOCAL_FILE);
7765                     }
7766                 }
7767             }
7768         },
7769         /**
7770         * @private
7771         * @property _disabled
7772         * @description The Toolbar items that should be disabled if there is no selection present in the editor.
7773         * @type Array
7774         */
7775         _disabled: [ 'createlink', 'forecolor', 'backcolor', 'fontname', 'fontsize', 'superscript', 'subscript', 'removeformat', 'heading', 'indent' ],
7776         /**
7777         * @private
7778         * @property _alwaysDisabled
7779         * @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
7780         * @type Object
7781         */
7782         _alwaysDisabled: { 'outdent': true },
7783         /**
7784         * @private
7785         * @property _alwaysEnabled
7786         * @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
7787         * @type Object
7788         */
7789         _alwaysEnabled: { hiddenelements: true },
7790         /**
7791         * @private
7792         * @method _handleKeyDown
7793         * @param {Event} ev The event we are working on.
7794         * @description Override method that handles some new keydown events inside the iFrame document.
7795         */
7796         _handleKeyDown: function(ev) {
7797             YAHOO.widget.Editor.superclass._handleKeyDown.call(this, ev);
7798             var doExec = false,
7799                 action = null,
7800                 exec = false;
7801
7802             switch (ev.keyCode) {
7803                 //case 219: //Left
7804                 case this._keyMap.JUSTIFY_LEFT.key: //Left
7805                     if (this._checkKey(this._keyMap.JUSTIFY_LEFT, ev)) {
7806                         action = 'justifyleft';
7807                         doExec = true;
7808                     }
7809                     break;
7810                 //case 220: //Center
7811                 case this._keyMap.JUSTIFY_CENTER.key:
7812                     if (this._checkKey(this._keyMap.JUSTIFY_CENTER, ev)) {
7813                         action = 'justifycenter';
7814                         doExec = true;
7815                     }
7816                     break;
7817                 case 221: //Right
7818                 case this._keyMap.JUSTIFY_RIGHT.key:
7819                     if (this._checkKey(this._keyMap.JUSTIFY_RIGHT, ev)) {
7820                         action = 'justifyright';
7821                         doExec = true;
7822                     }
7823                     break;
7824             }
7825             if (doExec && action) {
7826                 this.execCommand(action, null);
7827                 Event.stopEvent(ev);
7828                 this.nodeChange();
7829             }
7830         },        
7831         /**
7832         * @private
7833         * @method _renderCreateLinkWindow
7834         * @description Pre renders the CreateLink window so we get faster window opening.
7835         */
7836         _renderCreateLinkWindow: function() {
7837                 var str = '<label for="' + this.get('id') + '_createlink_url"><strong>' + this.STR_LINK_URL + ':</strong> <input type="text" name="' + this.get('id') + '_createlink_url" id="' + this.get('id') + '_createlink_url" value=""></label>';
7838                 str += '<label for="' + this.get('id') + '_createlink_target"><strong>&nbsp;</strong><input type="checkbox" name="' + this.get('id') + '_createlink_target" id="' + this.get('id') + '_createlink_target" value="_blank" class="createlink_target"> ' + this.STR_LINK_NEW_WINDOW + '</label>';
7839                 str += '<label for="' + this.get('id') + '_createlink_title"><strong>' + this.STR_LINK_TITLE + ':</strong> <input type="text" name="' + this.get('id') + '_createlink_title" id="' + this.get('id') + '_createlink_title" value=""></label>';
7840                 
7841                 var body = document.createElement('div');
7842                 body.innerHTML = str;
7843
7844                 var unlinkCont = document.createElement('div');
7845                 unlinkCont.className = 'removeLink';
7846                 var unlink = document.createElement('a');
7847                 unlink.href = '#';
7848                 unlink.innerHTML = this.STR_LINK_PROP_REMOVE;
7849                 unlink.title = this.STR_LINK_PROP_REMOVE;
7850                 Event.on(unlink, 'click', function(ev) {
7851                     Event.stopEvent(ev);
7852                     this.unsubscribeAll('afterExecCommand');
7853                     this.execCommand('unlink');
7854                     this.closeWindow();
7855                 }, this, true);
7856                 unlinkCont.appendChild(unlink);
7857                 body.appendChild(unlinkCont);
7858                 
7859                 this._windows.createlink = {};
7860                 this._windows.createlink.body = body;
7861                 //body.style.display = 'none';
7862                 Event.on(body, 'keyup', function(e) {
7863                     Event.stopPropagation(e);
7864                 });
7865                 this.get('panel').editor_form.appendChild(body);
7866                 this.fireEvent('windowCreateLinkRender', { type: 'windowCreateLinkRender', panel: this.get('panel'), body: body });
7867                 return body;
7868         },
7869         _handleCreateLinkClick: function() {
7870             var el = this._getSelectedElement();
7871             if (this._isElement(el, 'img')) {
7872                 this.STOP_EXEC_COMMAND = true;
7873                 this.currentElement[0] = el;
7874                 this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
7875                 this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
7876                 return false;
7877             }
7878             if (this.get('limitCommands')) {
7879                 if (!this.toolbar.getButtonByValue('createlink')) {
7880                     YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'Editor');
7881                     return false;
7882                 }
7883             }
7884             
7885             this.on('afterExecCommand', function() {
7886                 var win = new YAHOO.widget.EditorWindow('createlink', {
7887                     width: '350px'
7888                 });
7889                 
7890                 var el = this.currentElement[0],
7891                     url = '',
7892                     title = '',
7893                     target = '',
7894                     localFile = false;
7895                 if (el) {
7896                     win.el = el;
7897                     if (el.getAttribute('href', 2) !== null) {
7898                         url = el.getAttribute('href', 2);
7899                         if (this._isLocalFile(url)) {
7900                             //Local File throw Warning
7901                             YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7902                             win.setFooter(this.STR_LOCAL_FILE_WARNING);
7903                             localFile = true;
7904                         } else {
7905                             win.setFooter(' ');
7906                         }
7907                     }
7908                     if (el.getAttribute('title') !== null) {
7909                         title = el.getAttribute('title');
7910                     }
7911                     if (el.getAttribute('target') !== null) {
7912                         target = el.getAttribute('target');
7913                     }
7914                 }
7915                 var body = null;
7916                 if (this._windows.createlink && this._windows.createlink.body) {
7917                     body = this._windows.createlink.body;
7918                 } else {
7919                     body = this._renderCreateLinkWindow();
7920                 }
7921
7922                 win.setHeader(this.STR_LINK_PROP_TITLE);
7923                 win.setBody(body);
7924
7925                 Event.purgeElement(this.get('id') + '_createlink_url');
7926
7927                 Dom.get(this.get('id') + '_createlink_url').value = url;
7928                 Dom.get(this.get('id') + '_createlink_title').value = title;
7929                 Dom.get(this.get('id') + '_createlink_target').checked = ((target) ? true : false);
7930                 
7931
7932                 Event.onAvailable(this.get('id') + '_createlink_url', function() {
7933                     var id = this.get('id');
7934                     window.setTimeout(function() {
7935                         try {
7936                             YAHOO.util.Dom.get(id + '_createlink_url').focus();
7937                         } catch (e) {}
7938                     }, 50);
7939
7940                     if (this._isLocalFile(url)) {
7941                         //Local File throw Warning
7942                         Dom.addClass(this.get('id') + '_createlink_url', 'warning');
7943                         YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7944                         this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
7945                     } else {
7946                         Dom.removeClass(this.get('id') + '_createlink_url', 'warning');
7947                         this.get('panel').setFooter(' ');
7948                     }
7949                     Event.on(this.get('id') + '_createlink_url', 'blur', function() {
7950                         var url = Dom.get(this.get('id') + '_createlink_url');
7951                         if (this._isLocalFile(url.value)) {
7952                             //Local File throw Warning
7953                             Dom.addClass(url, 'warning');
7954                             YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7955                             this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
7956                         } else {
7957                             Dom.removeClass(url, 'warning');
7958                             this.get('panel').setFooter(' ');
7959                         }
7960                     }, this, true);
7961                 }, this, true);
7962                 
7963                 this.openWindow(win);
7964
7965             });
7966         },
7967         /**
7968         * @private
7969         * @method _handleCreateLinkWindowClose
7970         * @description Handles the closing of the Link Properties Window.
7971         */
7972         _handleCreateLinkWindowClose: function() {
7973             
7974             var url = Dom.get(this.get('id') + '_createlink_url'),
7975                 target = Dom.get(this.get('id') + '_createlink_target'),
7976                 title = Dom.get(this.get('id') + '_createlink_title'),
7977                 el = arguments[0].win.el,
7978                 a = el;
7979
7980             if (url && url.value) {
7981                 var urlValue = url.value;
7982                 if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
7983                     if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
7984                         //Found an @ sign, prefix with mailto:
7985                         urlValue = 'mailto:' + urlValue;
7986                     } else {
7987                         // :// not found adding
7988                         if (urlValue.substring(0, 1) != '#') {
7989                             urlValue = 'http:/'+'/' + urlValue;
7990                         }
7991                         
7992                     }
7993                 }
7994                 el.setAttribute('href', urlValue);
7995                 if (target.checked) {
7996                     el.setAttribute('target', target.value);
7997                 } else {
7998                     el.setAttribute('target', '');
7999                 }
8000                 el.setAttribute('title', ((title.value) ? title.value : ''));
8001
8002             } else {
8003                 var _span = this._getDoc().createElement('span');
8004                 _span.innerHTML = el.innerHTML;
8005                 Dom.addClass(_span, 'yui-non');
8006                 el.parentNode.replaceChild(_span, el);
8007             }
8008             Dom.removeClass(url, 'warning');
8009             Dom.get(this.get('id') + '_createlink_url').value = '';
8010             Dom.get(this.get('id') + '_createlink_title').value = '';
8011             Dom.get(this.get('id') + '_createlink_target').checked = false;
8012             this.nodeChange();
8013             this.currentElement = [];
8014             
8015         },
8016         /**
8017         * @private
8018         * @method _renderInsertImageWindow
8019         * @description Pre renders the InsertImage window so we get faster window opening.
8020         */
8021         _renderInsertImageWindow: function() {
8022                 var el = this.currentElement[0];
8023                 var str = '<label for="' + this.get('id') + '_insertimage_url"><strong>' + this.STR_IMAGE_URL + ':</strong> <input type="text" id="' + this.get('id') + '_insertimage_url" value="" size="40"></label>';
8024                 var body = document.createElement('div');
8025                 body.innerHTML = str;
8026
8027                 var tbarCont = document.createElement('div');
8028                 tbarCont.id = this.get('id') + '_img_toolbar';
8029                 body.appendChild(tbarCont);
8030
8031                 var str2 = '<label for="' + this.get('id') + '_insertimage_title"><strong>' + this.STR_IMAGE_TITLE + ':</strong> <input type="text" id="' + this.get('id') + '_insertimage_title" value="" size="40"></label>';
8032                 str2 += '<label for="' + this.get('id') + '_insertimage_link"><strong>' + this.STR_LINK_URL + ':</strong> <input type="text" name="' + this.get('id') + '_insertimage_link" id="' + this.get('id') + '_insertimage_link" value=""></label>';
8033                 str2 += '<label for="' + this.get('id') + '_insertimage_target"><strong>&nbsp;</strong><input type="checkbox" name="' + this.get('id') + '_insertimage_target_" id="' + this.get('id') + '_insertimage_target" value="_blank" class="insertimage_target"> ' + this.STR_LINK_NEW_WINDOW + '</label>';
8034                 var div = document.createElement('div');
8035                 div.innerHTML = str2;
8036                 body.appendChild(div);
8037
8038                 var o = {};
8039                 Lang.augmentObject(o, this._defaultImageToolbarConfig); //Break the config reference
8040
8041                 var tbar = new YAHOO.widget.Toolbar(tbarCont, o);
8042                 tbar.editor_el = el;
8043                 this._defaultImageToolbar = tbar;
8044                 
8045                 var cont = tbar.get('cont');
8046                 var hw = document.createElement('div');
8047                 hw.className = 'yui-toolbar-group yui-toolbar-group-height-width height-width';
8048                 hw.innerHTML = '<h3>' + this.STR_IMAGE_SIZE + ':</h3>';
8049                 /*
8050                 var orgSize = '';
8051                 if ((height != oheight) || (width != owidth)) {
8052                     orgSize = '<span class="info">' + this.STR_IMAGE_ORIG_SIZE + '<br>'+ owidth +' x ' + oheight + '</span>';
8053                 }
8054                 */
8055                 hw.innerHTML += '<span tabIndex="-1"><input type="text" size="3" value="" id="' + this.get('id') + '_insertimage_width"> x <input type="text" size="3" value="" id="' + this.get('id') + '_insertimage_height"></span>';
8056                 cont.insertBefore(hw, cont.firstChild);
8057
8058                 Event.onAvailable(this.get('id') + '_insertimage_width', function() {
8059                     Event.on(this.get('id') + '_insertimage_width', 'blur', function() {
8060                         var value = parseInt(Dom.get(this.get('id') + '_insertimage_width').value, 10);
8061                         if (value > 5) {
8062                            this._defaultImageToolbar.editor_el.style.width = value + 'px';
8063                             //Removed moveWindow call so the window doesn't jump
8064                             //this.moveWindow();
8065                         }
8066                     }, this, true);
8067                 }, this, true);
8068                 Event.onAvailable(this.get('id') + '_insertimage_height', function() {
8069                     Event.on(this.get('id') + '_insertimage_height', 'blur', function() {
8070                         var value = parseInt(Dom.get(this.get('id') + '_insertimage_height').value, 10);
8071                         if (value > 5) {
8072                             this._defaultImageToolbar.editor_el.style.height = value + 'px';
8073                             //Removed moveWindow call so the window doesn't jump
8074                             //this.moveWindow();
8075                         }
8076                     }, this, true);
8077                 }, this, true);
8078
8079
8080                 tbar.on('colorPickerClicked', function(o) {
8081                     var size = '1', type = 'solid', color = 'black', el = this._defaultImageToolbar.editor_el;
8082
8083                     if (el.style.borderLeftWidth) {
8084                         size = parseInt(el.style.borderLeftWidth, 10);
8085                     }
8086                     if (el.style.borderLeftStyle) {
8087                         type = el.style.borderLeftStyle;
8088                     }
8089                     if (el.style.borderLeftColor) {
8090                         color = el.style.borderLeftColor;
8091                     }
8092                     var borderString = size + 'px ' + type + ' #' + o.color;
8093                     el.style.border = borderString;
8094                 }, this, true);
8095
8096                 tbar.on('buttonClick', function(o) {
8097                     var value = o.button.value,
8098                         el = this._defaultImageToolbar.editor_el,
8099                         borderString = '';
8100                     if (o.button.menucmd) {
8101                         value = o.button.menucmd;
8102                     }
8103                     var size = '1', type = 'solid', color = 'black';
8104
8105                     /* All border calcs are done on the left border
8106                         since our default interface only supports
8107                         one border size/type and color */
8108                     if (el.style.borderLeftWidth) {
8109                         size = parseInt(el.style.borderLeftWidth, 10);
8110                     }
8111                     if (el.style.borderLeftStyle) {
8112                         type = el.style.borderLeftStyle;
8113                     }
8114                     if (el.style.borderLeftColor) {
8115                         color = el.style.borderLeftColor;
8116                     }
8117                     switch(value) {
8118                         case 'bordersize':
8119                             if (this.browser.webkit && this._lastImage) {
8120                                 Dom.removeClass(this._lastImage, 'selected');
8121                                 this._lastImage = null;
8122                             }
8123
8124                             borderString = parseInt(o.button.value, 10) + 'px ' + type + ' ' + color;
8125                             el.style.border = borderString;
8126                             if (parseInt(o.button.value, 10) > 0) {
8127                                 tbar.enableButton('bordertype');
8128                                 tbar.enableButton('bordercolor');
8129                             } else {
8130                                 tbar.disableButton('bordertype');
8131                                 tbar.disableButton('bordercolor');
8132                             }
8133                             break;
8134                         case 'bordertype':
8135                             if (this.browser.webkit && this._lastImage) {
8136                                 Dom.removeClass(this._lastImage, 'selected');
8137                                 this._lastImage = null;
8138                             }
8139                             borderString = size + 'px ' + o.button.value + ' ' + color;
8140                             el.style.border = borderString;
8141                             break;
8142                         case 'right':
8143                         case 'left':
8144                             tbar.deselectAllButtons();
8145                             el.style.display = '';
8146                             el.align = o.button.value;
8147                             break;
8148                         case 'inline':
8149                             tbar.deselectAllButtons();
8150                             el.style.display = '';
8151                             el.align = '';
8152                             break;
8153                         case 'block':
8154                             tbar.deselectAllButtons();
8155                             el.style.display = 'block';
8156                             el.align = 'center';
8157                             break;
8158                         case 'padding':
8159                             var _button = tbar.getButtonById(o.button.id);
8160                             el.style.margin = _button.get('label') + 'px';
8161                             break;
8162                     }
8163                     tbar.selectButton(o.button.value);
8164                     if (value !== 'padding') {
8165                         this.moveWindow();
8166                     }
8167                 }, this, true);
8168
8169
8170
8171                 if (this.get('localFileWarning')) {
8172                     Event.on(this.get('id') + '_insertimage_link', 'blur', function() {
8173                         var url = Dom.get(this.get('id') + '_insertimage_link');
8174                         if (this._isLocalFile(url.value)) {
8175                             //Local File throw Warning
8176                             Dom.addClass(url, 'warning');
8177                             YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
8178                             this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
8179                         } else {
8180                             Dom.removeClass(url, 'warning');
8181                             this.get('panel').setFooter(' ');
8182                             //Adobe AIR Code
8183                             if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {                
8184                                 this.get('panel').setFooter(this.STR_IMAGE_COPY);
8185                             }
8186                         }
8187                     }, this, true);
8188                 }
8189
8190                 Event.on(this.get('id') + '_insertimage_url', 'blur', function() {
8191                     var url = Dom.get(this.get('id') + '_insertimage_url');
8192                     if (url.value && el) {
8193                         if (url.value == el.getAttribute('src', 2)) {
8194                             YAHOO.log('Images are the same, bail on blur handler', 'info', 'Editor');
8195                             return false;
8196                         }
8197                     }
8198                     YAHOO.log('Images are different, process blur handler', 'info', 'Editor');
8199                     if (this._isLocalFile(url.value)) {
8200                         //Local File throw Warning
8201                         Dom.addClass(url, 'warning');
8202                         YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
8203                         this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
8204                     } else if (this.currentElement[0]) {
8205                         Dom.removeClass(url, 'warning');
8206                         this.get('panel').setFooter(' ');
8207                         //Adobe AIR Code
8208                         if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {                
8209                             this.get('panel').setFooter(this.STR_IMAGE_COPY);
8210                         }
8211                         
8212                         if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
8213                             this.currentElement[0].setAttribute('src', url.value);
8214                             var self = this,
8215                                 img = new Image();
8216
8217                             img.onerror = function() {
8218                                 url.value = self.STR_IMAGE_HERE;
8219                                 img.setAttribute('src', self.get('blankimage'));
8220                                 self.currentElement[0].setAttribute('src', self.get('blankimage'));
8221                                 YAHOO.util.Dom.get(self.get('id') + '_insertimage_height').value = img.height;
8222                                 YAHOO.util.Dom.get(self.get('id') + '_insertimage_width').value = img.width;
8223                             };
8224                             var id = this.get('id');
8225                             window.setTimeout(function() {
8226                                 YAHOO.util.Dom.get(id + '_insertimage_height').value = img.height;
8227                                 YAHOO.util.Dom.get(id + '_insertimage_width').value = img.width;
8228                                 if (self.currentElement && self.currentElement[0]) {
8229                                     if (!self.currentElement[0]._height) {
8230                                         self.currentElement[0]._height = img.height;
8231                                     }
8232                                     if (!self.currentElement[0]._width) {
8233                                         self.currentElement[0]._width = img.width;
8234                                     }
8235                                 }
8236                                 //Removed moveWindow call so the window doesn't jump
8237                                 //self.moveWindow();
8238                             }, 800); //Bumped the timeout up to account for larger images..
8239
8240                             if (url.value != this.STR_IMAGE_HERE) {
8241                                 img.src = url.value;
8242                             }
8243                         }
8244                     }
8245                     }, this, true);
8246
8247
8248
8249                 this._windows.insertimage = {};
8250                 this._windows.insertimage.body = body;
8251                 //body.style.display = 'none';
8252                 this.get('panel').editor_form.appendChild(body);
8253                 this.fireEvent('windowInsertImageRender', { type: 'windowInsertImageRender', panel: this.get('panel'), body: body, toolbar: tbar });
8254                 return body;
8255         },
8256         /**
8257         * @private
8258         * @method _handleInsertImageClick
8259         * @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
8260         */
8261         _handleInsertImageClick: function() {
8262             if (this.get('limitCommands')) {
8263                 if (!this.toolbar.getButtonByValue('insertimage')) {
8264                     YAHOO.log('Toolbar Button for (insertimage) was not found, skipping exec.', 'info', 'Editor');
8265                     return false;
8266                 }
8267             }
8268             this.on('afterExecCommand', function() {
8269                 YAHOO.log('afterExecCommand :: _handleInsertImageClick', 'info', 'Editor');
8270                 var el = this.currentElement[0],
8271                     body = null,
8272                     link = '',
8273                     target = '',
8274                     tbar = null,
8275                     title = '',
8276                     src = '',
8277                     align = '',
8278                     height = 75,
8279                     width = 75,
8280                     padding = 0,
8281                     oheight = 0,
8282                     owidth = 0,
8283                     blankimage = false,
8284                     win = new YAHOO.widget.EditorWindow('insertimage', {
8285                         width: '415px'
8286                     });
8287
8288                 if (!el) {
8289                     el = this._getSelectedElement();
8290                 }
8291                 if (el) {
8292                     win.el = el;
8293                     if (el.getAttribute('src')) {
8294                         src = el.getAttribute('src', 2);
8295                         if (src.indexOf(this.get('blankimage')) != -1) {
8296                             src = this.STR_IMAGE_HERE;
8297                             blankimage = true;
8298                         }
8299                     }
8300                     if (el.getAttribute('alt', 2)) {
8301                         title = el.getAttribute('alt', 2);
8302                     }
8303                     if (el.getAttribute('title', 2)) {
8304                         title = el.getAttribute('title', 2);
8305                     }
8306
8307                     if (el.parentNode && this._isElement(el.parentNode, 'a')) {
8308                         link = el.parentNode.getAttribute('href', 2);
8309                         if (el.parentNode.getAttribute('target') !== null) {
8310                             target = el.parentNode.getAttribute('target');
8311                         }
8312                     }
8313                     height = parseInt(el.height, 10);
8314                     width = parseInt(el.width, 10);
8315                     if (el.style.height) {
8316                         height = parseInt(el.style.height, 10);
8317                     }
8318                     if (el.style.width) {
8319                         width = parseInt(el.style.width, 10);
8320                     }
8321                     if (el.style.margin) {
8322                         padding = parseInt(el.style.margin, 10);
8323                     }
8324                     if (!el._height) {
8325                         el._height = height;
8326                     }
8327                     if (!el._width) {
8328                         el._width = width;
8329                     }
8330                     oheight = el._height;
8331                     owidth = el._width;
8332                 }
8333                 if (this._windows.insertimage && this._windows.insertimage.body) {
8334                     body = this._windows.insertimage.body;
8335                     this._defaultImageToolbar.resetAllButtons();
8336                 } else {
8337                     body = this._renderInsertImageWindow();
8338                 }
8339
8340                 tbar = this._defaultImageToolbar;
8341                 tbar.editor_el = el;
8342                 
8343
8344                 var bsize = '0',
8345                     btype = 'solid';
8346
8347                 if (el.style.borderLeftWidth) {
8348                     bsize = parseInt(el.style.borderLeftWidth, 10);
8349                 }
8350                 if (el.style.borderLeftStyle) {
8351                     btype = el.style.borderLeftStyle;
8352                 }
8353                 var bs_button = tbar.getButtonByValue('bordersize'),
8354                     bSizeStr = ((parseInt(bsize, 10) > 0) ? '' : this.STR_NONE);
8355                 bs_button.set('label', '<span class="yui-toolbar-bordersize-' + bsize + '">' + bSizeStr + '</span>');
8356                 this._updateMenuChecked('bordersize', bsize, tbar);
8357
8358                 var bt_button = tbar.getButtonByValue('bordertype');
8359                 bt_button.set('label', '<span class="yui-toolbar-bordertype-' + btype + '">asdfa</span>');
8360                 this._updateMenuChecked('bordertype', btype, tbar);
8361                 if (parseInt(bsize, 10) > 0) {
8362                     tbar.enableButton(bt_button);
8363                     tbar.enableButton(bs_button);
8364                     tbar.enableButton('bordercolor');
8365                 }
8366
8367                 if ((el.align == 'right') || (el.align == 'left')) {
8368                     tbar.selectButton(el.align);
8369                 } else if (el.style.display == 'block') {
8370                     tbar.selectButton('block');
8371                 } else {
8372                     tbar.selectButton('inline');
8373                 }
8374                 if (parseInt(el.style.marginLeft, 10) > 0) {
8375                     tbar.getButtonByValue('padding').set('label', ''+parseInt(el.style.marginLeft, 10));
8376                 }
8377                 if (el.style.borderSize) {
8378                     tbar.selectButton('bordersize');
8379                     tbar.selectButton(parseInt(el.style.borderSize, 10));
8380                 }
8381                 tbar.getButtonByValue('padding').set('label', ''+padding);
8382
8383
8384
8385                 win.setHeader(this.STR_IMAGE_PROP_TITLE);
8386                 win.setBody(body);
8387                 //Adobe AIR Code
8388                 if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {                
8389                     win.setFooter(this.STR_IMAGE_COPY);
8390                 }
8391                 this.openWindow(win);
8392                 Dom.get(this.get('id') + '_insertimage_url').value = src;
8393                 Dom.get(this.get('id') + '_insertimage_title').value = title;
8394                 Dom.get(this.get('id') + '_insertimage_link').value = link;
8395                 Dom.get(this.get('id') + '_insertimage_target').checked = ((target) ? true : false);
8396                 Dom.get(this.get('id') + '_insertimage_width').value = width;
8397                 Dom.get(this.get('id') + '_insertimage_height').value = height;
8398
8399
8400                 var orgSize = '';
8401                 if ((height != oheight) || (width != owidth)) {
8402                     var s = document.createElement('span');
8403                     s.className = 'info';
8404                     s.innerHTML = this.STR_IMAGE_ORIG_SIZE + ': ('+ owidth +' x ' + oheight + ')';
8405                     if (Dom.get(this.get('id') + '_insertimage_height').nextSibling) {
8406                         var old = Dom.get(this.get('id') + '_insertimage_height').nextSibling;
8407                         old.parentNode.removeChild(old);
8408                     }
8409                     Dom.get(this.get('id') + '_insertimage_height').parentNode.appendChild(s);
8410                 }
8411
8412                 this.toolbar.selectButton('insertimage');
8413                 var id = this.get('id');
8414                 window.setTimeout(function() {
8415                     try {
8416                         YAHOO.util.Dom.get(id + '_insertimage_url').focus();
8417                         if (blankimage) {
8418                             YAHOO.util.Dom.get(id + '_insertimage_url').select();
8419                         }
8420                     } catch (e) {}
8421                 }, 50);
8422
8423             });
8424         },
8425         /**
8426         * @private
8427         * @method _handleInsertImageWindowClose
8428         * @description Handles the closing of the Image Properties Window.
8429         */
8430         _handleInsertImageWindowClose: function() {
8431             var url = Dom.get(this.get('id') + '_insertimage_url'),
8432                 title = Dom.get(this.get('id') + '_insertimage_title'),
8433                 link = Dom.get(this.get('id') + '_insertimage_link'),
8434                 target = Dom.get(this.get('id') + '_insertimage_target'),
8435                 el = arguments[0].win.el;
8436
8437             if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
8438                 el.setAttribute('src', url.value);
8439                 el.setAttribute('title', title.value);
8440                 el.setAttribute('alt', title.value);
8441                 var par = el.parentNode;
8442                 if (link.value) {
8443                     var urlValue = link.value;
8444                     if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
8445                         if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
8446                             //Found an @ sign, prefix with mailto:
8447                             urlValue = 'mailto:' + urlValue;
8448                         } else {
8449                             // :// not found adding
8450                             urlValue = 'http:/'+'/' + urlValue;
8451                         }
8452                     }
8453                     if (par && this._isElement(par, 'a')) {
8454                         par.setAttribute('href', urlValue);
8455                         if (target.checked) {
8456                             par.setAttribute('target', target.value);
8457                         } else {
8458                             par.setAttribute('target', '');
8459                         }
8460                     } else {
8461                         var _a = this._getDoc().createElement('a');
8462                         _a.setAttribute('href', urlValue);
8463                         if (target.checked) {
8464                             _a.setAttribute('target', target.value);
8465                         } else {
8466                             _a.setAttribute('target', '');
8467                         }
8468                         el.parentNode.replaceChild(_a, el);
8469                         _a.appendChild(el);
8470                     }
8471                 } else {
8472                     if (par && this._isElement(par, 'a')) {
8473                         par.parentNode.replaceChild(el, par);
8474                     }
8475                 }
8476             } else {
8477                 //No url/src given, remove the node from the document
8478                 el.parentNode.removeChild(el);
8479             }
8480             Dom.get(this.get('id') + '_insertimage_url').value = '';
8481             Dom.get(this.get('id') + '_insertimage_title').value = '';
8482             Dom.get(this.get('id') + '_insertimage_link').value = '';
8483             Dom.get(this.get('id') + '_insertimage_target').checked = false;
8484             Dom.get(this.get('id') + '_insertimage_width').value = 0;
8485             Dom.get(this.get('id') + '_insertimage_height').value = 0;
8486             this._defaultImageToolbar.resetAllButtons();
8487             this.currentElement = [];
8488             this.nodeChange();
8489         },
8490         /**
8491         * @property EDITOR_PANEL_ID
8492         * @description HTML id to give the properties window in the DOM.
8493         * @type String
8494         */
8495         EDITOR_PANEL_ID: '-panel',
8496         /**
8497         * @private
8498         * @method _renderPanel
8499         * @description Renders the panel used for Editor Windows to the document so we can start using it..
8500         * @return {<a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>}
8501         */
8502         _renderPanel: function() {
8503             var panelEl = document.createElement('div');
8504             Dom.addClass(panelEl, 'yui-editor-panel');
8505             panelEl.id = this.get('id') + this.EDITOR_PANEL_ID;
8506             panelEl.style.position = 'absolute';
8507             panelEl.style.top = '-9999px';
8508             panelEl.style.left = '-9999px';
8509             document.body.appendChild(panelEl);
8510             this.get('element_cont').insertBefore(panelEl, this.get('element_cont').get('firstChild'));
8511
8512                 
8513
8514             var panel = new YAHOO.widget.Overlay(this.get('id') + this.EDITOR_PANEL_ID, {
8515                     width: '300px',
8516                     iframe: true,
8517                     visible: false,
8518                     underlay: 'none',
8519                     draggable: false,
8520                     close: false
8521                 });
8522             this.set('panel', panel);
8523
8524             panel.setBody('---');
8525             panel.setHeader(' ');
8526             panel.setFooter(' ');
8527
8528
8529             var body = document.createElement('div');
8530             body.className = this.CLASS_PREFIX + '-body-cont';
8531             for (var b in this.browser) {
8532                 if (this.browser[b]) {
8533                     Dom.addClass(body, b);
8534                     break;
8535                 }
8536             }
8537             Dom.addClass(body, ((YAHOO.widget.Button && (this._defaultToolbar.buttonType == 'advanced')) ? 'good-button' : 'no-button'));
8538
8539             var _note = document.createElement('h3');
8540             _note.className = 'yui-editor-skipheader';
8541             _note.innerHTML = this.STR_CLOSE_WINDOW_NOTE;
8542             body.appendChild(_note);
8543             var form = document.createElement('fieldset');
8544             panel.editor_form = form;
8545
8546             body.appendChild(form);
8547             var _close = document.createElement('span');
8548             _close.innerHTML = 'X';
8549             _close.title = this.STR_CLOSE_WINDOW;
8550             _close.className = 'close';
8551             
8552             Event.on(_close, 'click', this.closeWindow, this, true);
8553
8554             var _knob = document.createElement('span');
8555             _knob.innerHTML = '^';
8556             _knob.className = 'knob';
8557             panel.editor_knob = _knob;
8558
8559             var _header = document.createElement('h3');
8560             panel.editor_header = _header;
8561             _header.innerHTML = '<span></span>';
8562
8563             panel.setHeader(' '); //Clear the current header
8564             panel.appendToHeader(_header);
8565             _header.appendChild(_close);
8566             _header.appendChild(_knob);
8567             panel.setBody(' '); //Clear the current body
8568             panel.setFooter(' '); //Clear the current footer
8569             panel.appendToBody(body); //Append the new DOM node to it
8570
8571             Event.on(panel.element, 'click', function(ev) {
8572                 Event.stopPropagation(ev);
8573             });
8574
8575             var fireShowEvent = function() {
8576                 panel.bringToTop();
8577                 YAHOO.util.Dom.setStyle(this.element, 'display', 'block');
8578                 this._handleWindowInputs(false);
8579             };
8580             panel.showEvent.subscribe(fireShowEvent, this, true);
8581             panel.hideEvent.subscribe(function() {
8582                 this._handleWindowInputs(true);
8583             }, this, true);
8584             panel.renderEvent.subscribe(function() {
8585                 this._renderInsertImageWindow();
8586                 this._renderCreateLinkWindow();
8587                 this.fireEvent('windowRender', { type: 'windowRender', panel: panel });
8588                 this._handleWindowInputs(true);
8589             }, this, true);
8590
8591             if (this.DOMReady) {
8592                 this.get('panel').render();
8593             } else {
8594                 Event.onDOMReady(function() {
8595                     this.get('panel').render();
8596                 }, this, true);
8597             }
8598             return this.get('panel');
8599         },
8600         /**
8601         * @method _handleWindowInputs
8602         * @param {Boolean} disable The state to set all inputs in all Editor windows to. Defaults to: false.
8603         * @description Disables/Enables all fields inside Editor windows. Used in show/hide events to keep window fields from submitting when the parent form is submitted.
8604         */
8605         _handleWindowInputs: function(disable) {
8606             if (!Lang.isBoolean(disable)) {
8607                 disable = false;
8608             }
8609             var inputs = this.get('panel').element.getElementsByTagName('input');
8610             for (var i = 0; i < inputs.length; i++) {
8611                 try {
8612                     inputs[i].disabled = disable;
8613                 } catch (e) {}
8614             }
8615         },
8616         /**
8617         * @method openWindow
8618         * @param {<a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>} win A <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a> instance
8619         * @description Opens a new "window/panel"
8620         */
8621         openWindow: function(win) {
8622             
8623             YAHOO.log('openWindow: ' + win.name, 'info', 'Editor');
8624             var self = this;
8625             window.setTimeout(function() {
8626                 self.toolbar.set('disabled', true); //Disable the toolbar when an editor window is open..
8627             }, 10);
8628             Event.on(document, 'keydown', this._closeWindow, this, true);
8629             
8630             if (this.currentWindow) {
8631                 this.closeWindow();
8632             }
8633             
8634             var xy = Dom.getXY(this.currentElement[0]),
8635             elXY = Dom.getXY(this.get('iframe').get('element')),
8636             panel = this.get('panel'),
8637             newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
8638             wWidth = (parseInt(win.attrs.width, 10) / 2),
8639             align = 'center',
8640             body = null;
8641
8642             this.fireEvent('beforeOpenWindow', { type: 'beforeOpenWindow', win: win, panel: panel });
8643
8644             var form = panel.editor_form;
8645             
8646             var wins = this._windows;
8647             for (var b in wins) {
8648                 if (Lang.hasOwnProperty(wins, b)) {
8649                     if (wins[b] && wins[b].body) {
8650                         if (b == win.name) {
8651                             Dom.setStyle(wins[b].body, 'display', 'block');
8652                         } else {
8653                             Dom.setStyle(wins[b].body, 'display', 'none');
8654                         }
8655                     }
8656                 }
8657             }
8658             
8659             if (this._windows[win.name].body) {
8660                 Dom.setStyle(this._windows[win.name].body, 'display', 'block');
8661                 form.appendChild(this._windows[win.name].body);
8662             } else {
8663                 if (Lang.isObject(win.body)) { //Assume it's a reference
8664                     form.appendChild(win.body);
8665                 } else { //Assume it's a string
8666                     var _tmp = document.createElement('div');
8667                     _tmp.innerHTML = win.body;
8668                     form.appendChild(_tmp);
8669                 }
8670             }
8671             panel.editor_header.firstChild.innerHTML = win.header;
8672             if (win.footer !== null) {
8673                 panel.setFooter(win.footer);
8674                 Dom.addClass(panel.footer, 'open');
8675             } else {
8676                 Dom.removeClass(panel.footer, 'open');
8677             }
8678             panel.cfg.setProperty('width', win.attrs.width);
8679
8680             this.currentWindow = win;
8681             this.moveWindow(true);
8682             panel.show();
8683             this.fireEvent('afterOpenWindow', { type: 'afterOpenWindow', win: win, panel: panel });
8684         },
8685         /**
8686         * @method moveWindow
8687         * @param {Boolean} force Boolean to tell it to move but not use any animation (Usually done the first time the window is loaded.)
8688         * @description Realign the window with the currentElement and reposition the knob above the panel.
8689         */
8690         moveWindow: function(force) {
8691             if (!this.currentWindow) {
8692                 return false;
8693             }
8694             var win = this.currentWindow,
8695                 xy = Dom.getXY(this.currentElement[0]),
8696                 elXY = Dom.getXY(this.get('iframe').get('element')),
8697                 panel = this.get('panel'),
8698                 //newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
8699                 newXY = [(xy[0] + elXY[0]), (xy[1] + elXY[1])],
8700                 wWidth = (parseInt(win.attrs.width, 10) / 2),
8701                 align = 'center',
8702                 orgXY = panel.cfg.getProperty('xy') || [0,0],
8703                 _knob = panel.editor_knob,
8704                 xDiff = 0,
8705                 yDiff = 0,
8706                 anim = false;
8707
8708
8709             newXY[0] = ((newXY[0] - wWidth) + 20);
8710             //Account for the Scroll bars in a scrolled editor window.
8711             newXY[0] = newXY[0] - Dom.getDocumentScrollLeft(this._getDoc());
8712             newXY[1] = newXY[1] - Dom.getDocumentScrollTop(this._getDoc());
8713             
8714             if (this._isElement(this.currentElement[0], 'img')) {
8715                 if (this.currentElement[0].src.indexOf(this.get('blankimage')) != -1) {
8716                     newXY[0] = (newXY[0] + (75 / 2)); //Placeholder size
8717                     newXY[1] = (newXY[1] + 75); //Placeholder sizea
8718                 } else {
8719                     var w = parseInt(this.currentElement[0].width, 10);
8720                     var h = parseInt(this.currentElement[0].height, 10);
8721                     newXY[0] = (newXY[0] + (w / 2));
8722                     newXY[1] = (newXY[1] + h);
8723                 }
8724                 newXY[1] = newXY[1] + 15;
8725             } else {
8726                 var fs = Dom.getStyle(this.currentElement[0], 'fontSize');
8727                 if (fs && fs.indexOf && fs.indexOf('px') != -1) {
8728                     newXY[1] = newXY[1] + parseInt(Dom.getStyle(this.currentElement[0], 'fontSize'), 10) + 5;
8729                 } else {
8730                     newXY[1] = newXY[1] + 20;
8731                 }
8732             }
8733             if (newXY[0] < elXY[0]) {
8734                 newXY[0] = elXY[0] + 5;
8735                 align = 'left';
8736             }
8737
8738             if ((newXY[0] + (wWidth * 2)) > (elXY[0] + parseInt(this.get('iframe').get('element').clientWidth, 10))) {
8739                 newXY[0] = ((elXY[0] + parseInt(this.get('iframe').get('element').clientWidth, 10)) - (wWidth * 2) - 5);
8740                 align = 'right';
8741             }
8742             
8743             try {
8744                 xDiff = (newXY[0] - orgXY[0]);
8745                 yDiff = (newXY[1] - orgXY[1]);
8746             } catch (e) {}
8747
8748
8749             var iTop = elXY[1] + parseInt(this.get('height'), 10);
8750             var iLeft = elXY[0] + parseInt(this.get('width'), 10);
8751             if (newXY[1] > iTop) {
8752                 newXY[1] = iTop;
8753             }
8754             if (newXY[0] > iLeft) {
8755                 newXY[0] = (iLeft / 2);
8756             }
8757             
8758             //Convert negative numbers to positive so we can get the difference in distance
8759             xDiff = ((xDiff < 0) ? (xDiff * -1) : xDiff);
8760             yDiff = ((yDiff < 0) ? (yDiff * -1) : yDiff);
8761
8762             if (((xDiff > 10) || (yDiff > 10)) || force) { //Only move the window if it's supposed to move more than 10px or force was passed (new window)
8763                 var _knobLeft = 0,
8764                     elW = 0;
8765
8766                 if (this.currentElement[0].width) {
8767                     elW = (parseInt(this.currentElement[0].width, 10) / 2);
8768                 }
8769
8770                 var leftOffset = xy[0] + elXY[0] + elW;
8771                 _knobLeft = leftOffset - newXY[0];
8772                 //Check to see if the knob will go off either side & reposition it
8773                 if (_knobLeft > (parseInt(win.attrs.width, 10) - 1)) {
8774                     _knobLeft = ((parseInt(win.attrs.width, 10) - 30) - 1);
8775                 } else if (_knobLeft < 40) {
8776                     _knobLeft = 1;
8777                 }
8778                 if (isNaN(_knobLeft)) {
8779                     _knobLeft = 1;
8780                 }
8781                 if (force) {
8782                     if (_knob) {
8783                         _knob.style.left = _knobLeft + 'px';
8784                     }
8785                     //Removed Animation from a forced move..
8786                     panel.cfg.setProperty('xy', newXY);
8787                 } else {
8788                     if (this.get('animate')) {
8789                         anim = new YAHOO.util.Anim(panel.element, {}, 0.5, YAHOO.util.Easing.easeOut);
8790                         anim.attributes = {
8791                             top: {
8792                                 to: newXY[1]
8793                             },
8794                             left: {
8795                                 to: newXY[0]
8796                             }
8797                         };
8798                         anim.onComplete.subscribe(function() {
8799                             panel.cfg.setProperty('xy', newXY);
8800                         });
8801                         //We have to animate the iframe shim at the same time as the panel or we get scrollbar bleed ..
8802                         var iframeAnim = new YAHOO.util.Anim(panel.iframe, anim.attributes, 0.5, YAHOO.util.Easing.easeOut);
8803
8804                         var _knobAnim = new YAHOO.util.Anim(_knob, {
8805                             left: {
8806                                 to: _knobLeft
8807                             }
8808                         }, 0.6, YAHOO.util.Easing.easeOut);
8809                         anim.animate();
8810                         iframeAnim.animate();
8811                         _knobAnim.animate();
8812                     } else {
8813                         _knob.style.left = _knobLeft + 'px';
8814                         panel.cfg.setProperty('xy', newXY);
8815                     }
8816                 }
8817             }
8818         },
8819         /**
8820         * @private
8821         * @method _closeWindow
8822         * @description Close the currently open EditorWindow with the Escape key.
8823         * @param {Event} ev The keypress Event that we are trapping
8824         */
8825         _closeWindow: function(ev) {
8826             //if ((ev.charCode == 87) && ev.shiftKey && ev.ctrlKey) {
8827             if (this._checkKey(this._keyMap.CLOSE_WINDOW, ev)) {            
8828                 if (this.currentWindow) {
8829                     this.closeWindow();
8830                 }
8831             }
8832         },
8833         /**
8834         * @method closeWindow
8835         * @description Close the currently open EditorWindow.
8836         */
8837         closeWindow: function(keepOpen) {
8838             YAHOO.log('closeWindow: ' + this.currentWindow.name, 'info', 'Editor');
8839             this.fireEvent('window' + this.currentWindow.name + 'Close', { type: 'window' + this.currentWindow.name + 'Close', win: this.currentWindow, el: this.currentElement[0] });
8840             this.fireEvent('closeWindow', { type: 'closeWindow', win: this.currentWindow });
8841             this.currentWindow = null;
8842             this.get('panel').hide();
8843             this.get('panel').cfg.setProperty('xy', [-900,-900]);
8844             this.get('panel').syncIframe(); //Needed to move the iframe with the hidden panel
8845             this.unsubscribeAll('afterExecCommand');
8846             this.toolbar.set('disabled', false); //enable the toolbar now that the window is closed
8847             this.toolbar.resetAllButtons();
8848             this.focus();
8849             Event.removeListener(document, 'keydown', this._closeWindow);
8850         },
8851
8852         /* {{{  Command Overrides - These commands are only over written when we are using the advanced version */
8853         
8854         /**
8855         * @method cmd_undo
8856         * @description Pulls an item from the Undo stack and updates the Editor
8857         * @param value Value passed from the execCommand method
8858         */
8859         cmd_undo: function(value) {
8860             if (this._hasUndoLevel()) {
8861                 if (!this._undoLevel) {
8862                     this._undoLevel = this._undoCache.length;
8863                 }
8864                 this._undoLevel = (this._undoLevel - 1);
8865                 if (this._undoCache[this._undoLevel]) {
8866                     var html = this._getUndo(this._undoLevel);
8867                     this.setEditorHTML(html);
8868                 } else {
8869                     this._undoLevel = null;
8870                     this.toolbar.disableButton('undo');
8871                 }
8872             }
8873             return [false];
8874         },
8875
8876         /**
8877         * @method cmd_redo
8878         * @description Pulls an item from the Undo stack and updates the Editor
8879         * @param value Value passed from the execCommand method
8880         */
8881         cmd_redo: function(value) {
8882             this._undoLevel = this._undoLevel + 1;
8883             if (this._undoLevel >= this._undoCache.length) {
8884                 this._undoLevel = this._undoCache.length;
8885             }
8886             YAHOO.log(this._undoLevel + ' :: ' + this._undoCache.length, 'warn', 'SimpleEditor');
8887             if (this._undoCache[this._undoLevel]) {
8888                 var html = this._getUndo(this._undoLevel);
8889                 this.setEditorHTML(html);
8890             } else {
8891                 this.toolbar.disableButton('redo');
8892             }
8893             return [false];
8894         },       
8895         
8896         /**
8897         * @method cmd_heading
8898         * @param value Value passed from the execCommand method
8899         * @description This is an execCommand override method. It is called from execCommand when the execCommand('heading') is used.
8900         */
8901         cmd_heading: function(value) {
8902             var exec = true,
8903                 el = null,
8904                 action = 'heading',
8905                 _sel = this._getSelection(),
8906                 _selEl = this._getSelectedElement();
8907
8908             if (_selEl) {
8909                 _sel = _selEl;
8910             }
8911             
8912             if (this.browser.ie) {
8913                 action = 'formatblock';
8914             }
8915             if (value == this.STR_NONE) {
8916                 if ((_sel && _sel.tagName && (_sel.tagName.toLowerCase().substring(0,1) == 'h')) || (_sel && _sel.parentNode && _sel.parentNode.tagName && (_sel.parentNode.tagName.toLowerCase().substring(0,1) == 'h'))) {
8917                     if (_sel.parentNode.tagName.toLowerCase().substring(0,1) == 'h') {
8918                         _sel = _sel.parentNode;
8919                     }
8920                     if (this._isElement(_sel, 'html')) {
8921                         return [false];
8922                     }
8923                     el = this._swapEl(_selEl, 'span', function(el) {
8924                         el.className = 'yui-non';
8925                     });
8926                     this._selectNode(el);
8927                     this.currentElement[0] = el;
8928                 }
8929                 exec = false;
8930             } else {
8931                 if (this._isElement(_selEl, 'h1') || this._isElement(_selEl, 'h2') || this._isElement(_selEl, 'h3') || this._isElement(_selEl, 'h4') || this._isElement(_selEl, 'h5') || this._isElement(_selEl, 'h6')) {
8932                     el = this._swapEl(_selEl, value);
8933                     this._selectNode(el);
8934                     this.currentElement[0] = el;
8935                 } else {
8936                     this._createCurrentElement(value);
8937                     this._selectNode(this.currentElement[0]);
8938                 }
8939                 exec = false;
8940             }
8941             return [exec, action];
8942         },
8943         /**
8944         * @method cmd_hiddenelements
8945         * @param value Value passed from the execCommand method
8946         * @description This is an execCommand override method. It is called from execCommand when the execCommand('hiddenelements') is used.
8947         */
8948         cmd_hiddenelements: function(value) {
8949             if (this._showingHiddenElements) {
8950                 //Don't auto highlight the hidden button
8951                 this._lastButton = null;
8952                 YAHOO.log('Enabling hidden CSS File', 'info', 'SimpleEditor');
8953                 this._showingHiddenElements = false;
8954                 this.toolbar.deselectButton('hiddenelements');
8955                 Dom.removeClass(this._getDoc().body, this.CLASS_HIDDEN);
8956             } else {
8957                 YAHOO.log('Disabling hidden CSS File', 'info', 'SimpleEditor');
8958                 this._showingHiddenElements = true;
8959                 Dom.addClass(this._getDoc().body, this.CLASS_HIDDEN);
8960                 this.toolbar.selectButton('hiddenelements');
8961             }
8962             return [false];
8963         },
8964         /**
8965         * @method cmd_removeformat
8966         * @param value Value passed from the execCommand method
8967         * @description This is an execCommand override method. It is called from execCommand when the execCommand('removeformat') is used.
8968         */
8969         cmd_removeformat: function(value) {
8970             var exec = true;
8971             /*
8972             * @knownissue Remove Format issue
8973             * @browser Safari 2.x
8974             * @description There is an issue here with Safari, that it may not always remove the format of the item that is selected.
8975             * Due to the way that Safari 2.x handles ranges, it is very difficult to determine what the selection holds.
8976             * So here we are making the best possible guess and acting on it.
8977             */
8978             if (this.browser.webkit && !this._getDoc().queryCommandEnabled('removeformat')) {
8979                 var _txt = this._getSelection()+'';
8980                 this._createCurrentElement('span');
8981                 this.currentElement[0].className = 'yui-non';
8982                 this.currentElement[0].innerHTML = _txt;
8983                 for (var i = 1; i < this.currentElement.length; i++) {
8984                     this.currentElement[i].parentNode.removeChild(this.currentElement[i]);
8985                 }
8986                 
8987                 exec = false;
8988             }
8989             return [exec];
8990         },
8991         /**
8992         * @method cmd_script
8993         * @param action action passed from the execCommand method
8994         * @param value Value passed from the execCommand method
8995         * @description This is a combined execCommand override method. It is called from the cmd_superscript and cmd_subscript methods.
8996         */
8997         cmd_script: function(action, value) {
8998             var exec = true, tag = action.toLowerCase().substring(0, 3),
8999                 _span = null, _selEl = this._getSelectedElement();
9000
9001             if (this.browser.webkit) {
9002                 YAHOO.log('Safari dom fun again (' + action + ')..', 'info', 'EditorSafari');
9003                 if (this._isElement(_selEl, tag)) {
9004                     YAHOO.log('we are a child of tag (' + tag + '), reverse process', 'info', 'EditorSafari');
9005                     _span = this._swapEl(this.currentElement[0], 'span', function(el) {
9006                         el.className = 'yui-non';
9007                     });
9008                     this._selectNode(_span);
9009                 } else {
9010                     this._createCurrentElement(tag);
9011                     var _sub = this._swapEl(this.currentElement[0], tag);
9012                     this._selectNode(_sub);
9013                     this.currentElement[0] = _sub;
9014                 }
9015                 exec = false;
9016             }
9017             return exec;
9018         },
9019         /**
9020         * @method cmd_superscript
9021         * @param value Value passed from the execCommand method
9022         * @description This is an execCommand override method. It is called from execCommand when the execCommand('superscript') is used.
9023         */
9024         cmd_superscript: function(value) {
9025             return [this.cmd_script('superscript', value)];
9026         },
9027         /**
9028         * @method cmd_subscript
9029         * @param value Value passed from the execCommand method
9030         * @description This is an execCommand override method. It is called from execCommand when the execCommand('subscript') is used.
9031         */
9032         cmd_subscript: function(value) {
9033             return [this.cmd_script('subscript', value)];
9034         },
9035         /**
9036         * @method cmd_indent
9037         * @param value Value passed from the execCommand method
9038         * @description This is an execCommand override method. It is called from execCommand when the execCommand('indent') is used.
9039         */
9040         cmd_indent: function(value) {
9041             var exec = true, selEl = this._getSelectedElement(), _bq = null;
9042
9043             //if (this.browser.webkit || this.browser.ie || this.browser.gecko) {
9044             //if (this.browser.webkit || this.browser.ie) {
9045             if (this.browser.ie) {
9046                 if (this._isElement(selEl, 'blockquote')) {
9047                     _bq = this._getDoc().createElement('blockquote');
9048                     _bq.innerHTML = selEl.innerHTML;
9049                     selEl.innerHTML = '';
9050                     selEl.appendChild(_bq);
9051                     this._selectNode(_bq);
9052                 } else {
9053                     _bq = this._getDoc().createElement('blockquote');
9054                     var html = this._getRange().htmlText;
9055                     _bq.innerHTML = html;
9056                     this._createCurrentElement('blockquote');
9057                     /*
9058                     for (var i = 0; i < this.currentElement.length; i++) {
9059                         _bq = this._getDoc().createElement('blockquote');
9060                         _bq.innerHTML = this.currentElement[i].innerHTML;
9061                         this.currentElement[i].parentNode.replaceChild(_bq, this.currentElement[i]);
9062                         this.currentElement[i] = _bq;
9063                     }
9064                     */
9065                     this.currentElement[0].parentNode.replaceChild(_bq, this.currentElement[0]);
9066                     this.currentElement[0] = _bq;
9067                     this._selectNode(this.currentElement[0]);
9068                 }
9069                 exec = false;
9070             } else {
9071                 value = 'blockquote';
9072             }
9073             return [exec, 'formatblock', value];
9074         },
9075         /**
9076         * @method cmd_outdent
9077         * @param value Value passed from the execCommand method
9078         * @description This is an execCommand override method. It is called from execCommand when the execCommand('outdent') is used.
9079         */
9080         cmd_outdent: function(value) {
9081             var exec = true, selEl = this._getSelectedElement(), _bq = null, _span = null;
9082             //if (this.browser.webkit || this.browser.ie || this.browser.gecko) {
9083             if (this.browser.webkit || this.browser.ie) {
9084             //if (this.browser.ie) {
9085                 selEl = this._getSelectedElement();
9086                 if (this._isElement(selEl, 'blockquote')) {
9087                     var par = selEl.parentNode;
9088                     if (this._isElement(selEl.parentNode, 'blockquote')) {
9089                         par.innerHTML = selEl.innerHTML;
9090                         this._selectNode(par);
9091                     } else {
9092                         _span = this._getDoc().createElement('span');
9093                         _span.innerHTML = selEl.innerHTML;
9094                         YAHOO.util.Dom.addClass(_span, 'yui-non');
9095                         par.replaceChild(_span, selEl);
9096                         this._selectNode(_span);
9097                     }
9098                 } else {
9099                     YAHOO.log('Can not outdent, we are not inside a blockquote', 'warn', 'Editor');
9100                 }
9101                 exec = false;
9102             } else {
9103                 value = false;
9104             }
9105             return [exec, 'outdent', value];
9106         },
9107         /**
9108         * @method cmd_justify
9109         * @param dir The direction to justify
9110         * @description This is a factory method for the justify family of commands.
9111         */
9112         cmd_justify: function(dir) {
9113             if (this.browser.ie) {
9114                 if (this._hasSelection()) {
9115                     this._createCurrentElement('span');
9116                     this._swapEl(this.currentElement[0], 'div', function(el) {
9117                         el.style.textAlign = dir;
9118                     });
9119                     
9120                     return [false];
9121                 }
9122             }
9123             return [true, 'justify' + dir, ''];
9124         },
9125         /**
9126         * @method cmd_justifycenter
9127         * @param value Value passed from the execCommand method
9128         * @description This is an execCommand override method. It is called from execCommand when the execCommand('justifycenter') is used.
9129         */
9130         cmd_justifycenter: function() {
9131             return [this.cmd_justify('center')];
9132         },
9133         /**
9134         * @method cmd_justifyleft
9135         * @param value Value passed from the execCommand method
9136         * @description This is an execCommand override method. It is called from execCommand when the execCommand('justifyleft') is used.
9137         */
9138         cmd_justifyleft: function() {
9139             return [this.cmd_justify('left')];
9140         },
9141         /**
9142         * @method cmd_justifyright
9143         * @param value Value passed from the execCommand method
9144         * @description This is an execCommand override method. It is called from execCommand when the execCommand('justifyright') is used.
9145         */
9146         cmd_justifyright: function() {
9147             return [this.cmd_justify('right')];
9148         },
9149         /* }}}*/        
9150         /**
9151         * @method toString
9152         * @description Returns a string representing the editor.
9153         * @return {String}
9154         */
9155         toString: function() {
9156             var str = 'Editor';
9157             if (this.get && this.get('element_cont')) {
9158                 str = 'Editor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
9159             }
9160             return str;
9161         }
9162     });
9163 /**
9164 * @event beforeOpenWindow
9165 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9166 * @param {Overlay} panel The Overlay object that is used to create the window.
9167 * @description Event fires before an Editor Window is opened. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
9168 * @type YAHOO.util.CustomEvent
9169 */
9170 /**
9171 * @event afterOpenWindow
9172 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9173 * @param {Overlay} panel The Overlay object that is used to create the window.
9174 * @description Event fires after an Editor Window is opened. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
9175 * @type YAHOO.util.CustomEvent
9176 */
9177 /**
9178 * @event closeWindow
9179 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9180 * @description Event fires after an Editor Window is closed. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
9181 * @type YAHOO.util.CustomEvent
9182 */
9183 /**
9184 * @event windowCMDOpen
9185 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9186 * @param {Overlay} panel The Overlay object that is used to create the window.
9187 * @description Dynamic event fired when an <a href="YAHOO.widget.EditorWindow.html">EditorWindow</a> is opened.. The dynamic event is based on the name of the window. Example Window: createlink, opening this window would fire the windowcreatelinkOpen event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
9188 * @type YAHOO.util.CustomEvent
9189 */
9190 /**
9191 * @event windowCMDClose
9192 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9193 * @param {Overlay} panel The Overlay object that is used to create the window.
9194 * @description Dynamic event fired when an <a href="YAHOO.widget.EditorWindow.html">EditorWindow</a> is closed.. The dynamic event is based on the name of the window. Example Window: createlink, opening this window would fire the windowcreatelinkClose event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
9195 * @type YAHOO.util.CustomEvent
9196 */
9197 /**
9198 * @event windowRender
9199 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9200 * @param {Overlay} panel The Overlay object that is used to create the window.
9201 * @description Event fired when the initial Overlay is rendered. Can be used to manipulate the content of the panel.
9202 * @type YAHOO.util.CustomEvent
9203 */
9204 /**
9205 * @event windowInsertImageRender
9206 * @param {Overlay} panel The Overlay object that is used to create the window.
9207 * @param {HTMLElement} body The HTML element used as the body of the window..
9208 * @param {Toolbar} toolbar A reference to the toolbar object used inside this window.
9209 * @description Event fired when the pre render of the Insert Image window has finished.
9210 * @type YAHOO.util.CustomEvent
9211 */
9212 /**
9213 * @event windowCreateLinkRender
9214 * @param {Overlay} panel The Overlay object that is used to create the window.
9215 * @param {HTMLElement} body The HTML element used as the body of the window..
9216 * @description Event fired when the pre render of the Create Link window has finished.
9217 * @type YAHOO.util.CustomEvent
9218 */
9219
9220
9221
9222     /**
9223      * @description Class to hold Window information between uses. We use the same panel to show the windows, so using this will allow you to configure a window before it is shown.
9224      * This is what you pass to Editor.openWindow();. These parameters will not take effect until the openWindow() is called in the editor.
9225      * @class EditorWindow
9226      * @param {String} name The name of the window.
9227      * @param {Object} attrs Attributes for the window. Current attributes used are : height and width
9228     */
9229     YAHOO.widget.EditorWindow = function(name, attrs) {
9230         /**
9231         * @private
9232         * @property name
9233         * @description A unique name for the window
9234         */
9235         this.name = name.replace(' ', '_');
9236         /**
9237         * @private
9238         * @property attrs
9239         * @description The window attributes
9240         */
9241         this.attrs = attrs;
9242     };
9243
9244     YAHOO.widget.EditorWindow.prototype = {
9245         /**
9246         * @private
9247         * @property header
9248         * @description Holder for the header of the window, used in Editor.openWindow
9249         */
9250         header: null,
9251         /**
9252         * @private
9253         * @property body
9254         * @description Holder for the body of the window, used in Editor.openWindow
9255         */
9256         body: null,
9257         /**
9258         * @private
9259         * @property footer
9260         * @description Holder for the footer of the window, used in Editor.openWindow
9261         */
9262         footer: null,
9263         /**
9264         * @method setHeader
9265         * @description Sets the header for the window.
9266         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows header.
9267         */
9268         setHeader: function(str) {
9269             this.header = str;
9270         },
9271         /**
9272         * @method setBody
9273         * @description Sets the body for the window.
9274         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows body.
9275         */
9276         setBody: function(str) {
9277             this.body = str;
9278         },
9279         /**
9280         * @method setFooter
9281         * @description Sets the footer for the window.
9282         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows footer.
9283         */
9284         setFooter: function(str) {
9285             this.footer = str;
9286         },
9287         /**
9288         * @method toString
9289         * @description Returns a string representing the EditorWindow.
9290         * @return {String}
9291         */
9292         toString: function() {
9293             return 'Editor Window (' + this.name + ')';
9294         }
9295     };
9296 })();
9297 YAHOO.register("editor", YAHOO.widget.Editor, {version: "2.7.0", build: "1799"});