MDL-11517 reserved word MOD used in table alias in questions backup code
[moodle-pu.git] / lib / yui / editor / editor-beta.js
blob658a32b4001af679c8021d765788fcd71709412f
1 /*
2 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.3.0
6 */
7 /*
8 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
9 Code licensed under the BSD License:
10 http://developer.yahoo.net/yui/license.txt
12 /**
13  * @description <p>Creates a rich Toolbar widget based on Button. Primarily used with the Rich Text Editor</p>
14  * @class Toolbar
15  * @namespace YAHOO.widget
16  * @requires yahoo, dom, element, event
17  * @optional container, menu, button, dragdrop
18  * @beta
19  */
20 (function() {
21     /**
22     * @private
23     **/
24 var Dom = YAHOO.util.Dom,
25     Event = YAHOO.util.Event,
26     Lang = YAHOO.lang;
28     /**
29      * Provides a rich toolbar widget based on the button and menu widgets
30      * @constructor
31      * @param {String/HTMLElement} el The element to turn into a toolbar.
32      * @param {Object} attrs Object liternal containing configuration parameters.
33     */
34     YAHOO.widget.Toolbar = function(el, attrs) {
35         
36         if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
37             var attrs = el;
38         }
39         var local_attrs = (attrs || {});
41         var oConfig = {
42             element: null,
43             attributes: local_attrs
44         }
45         
46         
47         if (Lang.isString(el) && Dom.get(el)) {
48             oConfig.element = Dom.get(el);
49         } else if (Lang.isObject(el) && Dom.get(el) && Dom.get(el).nodeType) {  
50             oConfig.element = Dom.get(el);
51         }
52         
54         if (!oConfig.element) {
55             oConfig.element = document.createElement('DIV');
56             oConfig.element.id = Dom.generateId();
57             
58             if (local_attrs.container && Dom.get(local_attrs.container)) {
59                 Dom.get(local_attrs.container).appendChild(oConfig.element);
60             }
61         }
62         
64         if (!oConfig.element.id) {
65             oConfig.element.id = ((Lang.isString(el)) ? el : Dom.generateId());
66         }
67         
68         var cont = document.createElement('DIV');
69         oConfig.attributes.cont = cont;
70         Dom.addClass(cont, 'yui-toolbar-subcont')
71         oConfig.element.appendChild(cont);
72         
73         oConfig.attributes.element = oConfig.element;
74         oConfig.attributes.id = oConfig.element.id;
76         YAHOO.widget.Toolbar.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
77         
78         
79     }
81     /**
82     * @method _addMenuClasses
83     * @private
84     * @description This method is called from Menu's renderEvent to add a few more classes to the menu items
85     * @param {String} ev The event that fired.
86     * @param {Array} na Array of event information.
87     * @param {Object} o Button config object. 
88     */
90     function _addMenuClasses(ev, na, o) {
91         Dom.addClass(this.element, 'yui-toolbar-' + o.get('value') + '-menu');
92         if (Dom.hasClass(o._button.parentNode.parentNode, 'yui-toolbar-select')) {
93             Dom.addClass(this.element, 'yui-toolbar-select-menu');
94         }
95         var items = this.getItems();
96         for (var i = 0; i < items.length; i++) {
97             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()));
98             Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-') : items[i]._oText.nodeValue.replace(/ /g, '-')));
99         }
100         this._setWidth();
101     };
103     YAHOO.extend(YAHOO.widget.Toolbar, YAHOO.util.Element, {
104         /** 
105         * @property dd
106         * @description The DragDrop instance associated with the Toolbar
107         * @type Object
108         */
109         dd: null,
110         /** 
111         * @property _colorData
112         * @description Object reference containing colors hex and text values.
113         * @type Object
114         */
115         _colorData: {
116 /* {{{ _colorData */
117     '#111111': 'Obsidian',
118     '#2D2D2D': 'Dark Gray',
119     '#434343': 'Shale',
120     '#5B5B5B': 'Flint',
121     '#737373': 'Gray',
122     '#8B8B8B': 'Concrete',
123     '#A2A2A2': 'Gray',
124     '#B9B9B9': 'Titanium',
125     '#000000': 'Black',
126     '#D0D0D0': 'Light Gray',
127     '#E6E6E6': 'Silver',
128     '#FFFFFF': 'White',
129     '#BFBF00': 'Pumpkin',
130     '#FFFF00': 'Yellow',
131     '#FFFF40': 'Banana',
132     '#FFFF80': 'Pale Yellow',
133     '#FFFFBF': 'Butter',
134     '#525330': 'Raw Siena',
135     '#898A49': 'Mildew',
136     '#AEA945': 'Olive',
137     '#7F7F00': 'Paprika',
138     '#C3BE71': 'Earth',
139     '#E0DCAA': 'Khaki',
140     '#FCFAE1': 'Cream',
141     '#60BF00': 'Cactus',
142     '#80FF00': 'Chartreuse',
143     '#A0FF40': 'Green',
144     '#C0FF80': 'Pale Lime',
145     '#DFFFBF': 'Light Mint',
146     '#3B5738': 'Green',
147     '#668F5A': 'Lime Gray',
148     '#7F9757': 'Yellow',
149     '#407F00': 'Clover',
150     '#8A9B55': 'Pistachio',
151     '#B7C296': 'Light Jade',
152     '#E6EBD5': 'Breakwater',
153     '#00BF00': 'Spring Frost',
154     '#00FF80': 'Pastel Green',
155     '#40FFA0': 'Light Emerald',
156     '#80FFC0': 'Sea Foam',
157     '#BFFFDF': 'Sea Mist',
158     '#033D21': 'Dark Forrest',
159     '#438059': 'Moss',
160     '#7FA37C': 'Medium Green',
161     '#007F40': 'Pine',
162     '#8DAE94': 'Yellow Gray Green',
163     '#ACC6B5': 'Aqua Lung',
164     '#DDEBE2': 'Sea Vapor',
165     '#00BFBF': 'Fog',
166     '#00FFFF': 'Cyan',
167     '#40FFFF': 'Turquoise Blue',
168     '#80FFFF': 'Light Aqua',
169     '#BFFFFF': 'Pale Cyan',
170     '#033D3D': 'Dark Teal',
171     '#347D7E': 'Gray Turquoise',
172     '#609A9F': 'Green Blue',
173     '#007F7F': 'Seaweed',
174     '#96BDC4': 'Green Gray',
175     '#B5D1D7': 'Soapstone',
176     '#E2F1F4': 'Light Turquoise',
177     '#0060BF': 'Summer Sky',
178     '#0080FF': 'Sky Blue',
179     '#40A0FF': 'Electric Blue',
180     '#80C0FF': 'Light Azure',
181     '#BFDFFF': 'Ice Blue',
182     '#1B2C48': 'Navy',
183     '#385376': 'Biscay',
184     '#57708F': 'Dusty Blue',
185     '#00407F': 'Sea Blue',
186     '#7792AC': 'Sky Blue Gray',
187     '#A8BED1': 'Morning Sky',
188     '#DEEBF6': 'Vapor',
189     '#0000BF': 'Deep Blue',
190     '#0000FF': 'Blue',
191     '#4040FF': 'Cerulean Blue',
192     '#8080FF': 'Evening Blue',
193     '#BFBFFF': 'Light Blue',
194     '#212143': 'Deep Indigo',
195     '#373E68': 'Sea Blue',
196     '#444F75': 'Night Blue',
197     '#00007F': 'Indigo Blue',
198     '#585E82': 'Dockside',
199     '#8687A4': 'Blue Gray',
200     '#D2D1E1': 'Light Blue Gray',
201     '#6000BF': 'Neon Violet',
202     '#8000FF': 'Blue Violet',
203     '#A040FF': 'Violet Purple',
204     '#C080FF': 'Violet Dusk',
205     '#DFBFFF': 'Pale Lavender',
206     '#302449': 'Cool Shale',
207     '#54466F': 'Dark Indigo',
208     '#655A7F': 'Dark Violet',
209     '#40007F': 'Violet',
210     '#726284': 'Smoky Violet',
211     '#9E8FA9': 'Slate Gray',
212     '#DCD1DF': 'Violet White',
213     '#BF00BF': 'Royal Violet',
214     '#FF00FF': 'Fuchsia',
215     '#FF40FF': 'Magenta',
216     '#FF80FF': 'Orchid',
217     '#FFBFFF': 'Pale Magenta',
218     '#4A234A': 'Dark Purple',
219     '#794A72': 'Medium Purple',
220     '#936386': 'Cool Granite',
221     '#7F007F': 'Purple',
222     '#9D7292': 'Purple Moon',
223     '#C0A0B6': 'Pale Purple',
224     '#ECDAE5': 'Pink Cloud',
225     '#BF005F': 'Hot Pink',
226     '#FF007F': 'Deep Pink',
227     '#FF409F': 'Grape',
228     '#FF80BF': 'Electric Pink',
229     '#FFBFDF': 'Pink',
230     '#451528': 'Purple Red',
231     '#823857': 'Purple Dino',
232     '#A94A76': 'Purple Gray',
233     '#7F003F': 'Rose',
234     '#BC6F95': 'Antique Mauve',
235     '#D8A5BB': 'Cool Marble',
236     '#F7DDE9': 'Pink Granite',
237     '#C00000': 'Apple',
238     '#FF0000': 'Fire Truck',
239     '#FF4040': 'Pale Red',
240     '#FF8080': 'Salmon',
241     '#FFC0C0': 'Warm Pink',
242     '#441415': 'Sepia',
243     '#82393C': 'Rust',
244     '#AA4D4E': 'Brick',
245     '#800000': 'Brick Red',
246     '#BC6E6E': 'Mauve',
247     '#D8A3A4': 'Shrimp Pink',
248     '#F8DDDD': 'Shell Pink',
249     '#BF5F00': 'Dark Orange',
250     '#FF7F00': 'Orange',
251     '#FF9F40': 'Grapefruit',
252     '#FFBF80': 'Canteloupe',
253     '#FFDFBF': 'Wax',
254     '#482C1B': 'Dark Brick',
255     '#855A40': 'Dirt',
256     '#B27C51': 'Tan',
257     '#7F3F00': 'Nutmeg',
258     '#C49B71': 'Mustard',
259     '#E1C4A8': 'Pale Tan',
260     '#FDEEE0': 'Marble'
261 /* }}} */
262         },
263         /** 
264         * @property _colorPicker
265         * @description The HTML Element containing the colorPicker
266         * @type HTMLElement
267         */
268         _colorPicker: null,
269         /** 
270         * @property STR_COLLAPSE
271         * @description String for Toolbar Collapse Button
272         * @type String
273         */
274         STR_COLLAPSE: 'Collapse Toolbar',
275         /** 
276         * @property STR_SPIN_LABEL
277         * @description String for spinbutton dynamic label. Note the {VALUE} will be replaced with YAHOO.lang.substitute
278         * @type String
279         */
280         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.',
281         /** 
282         * @property STR_SPIN_UP
283         * @description String for spinbutton up
284         * @type String
285         */
286         STR_SPIN_UP: 'Click to increase the value of this input',
287         /** 
288         * @property STR_SPIN_DOWN
289         * @description String for spinbutton down
290         * @type String
291         */
292         STR_SPIN_DOWN: 'Click to decrease the value of this input',
293         /** 
294         * @property _titlebar
295         * @description Object reference to the titlebar
296         * @type HTMLElement
297         */
298         _titlebar: null,
299         /** 
300         * @property _disabled
301         * @description Object to track button status when enabling/disabling the toolbar
302         * @type Object
303         */
304         _disabled: null,
305         /** 
306         * @property browser
307         * @description Standard browser detection
308         * @type Object
309         */
310         browser: YAHOO.env.ua,
311         /**
312         * @protected
313         * @property _buttonList
314         * @description Internal property list of current buttons in the toolbar
315         * @type Array
316         */
317         _buttonList: null,
318         /**
319         * @protected
320         * @property _buttonGroupList
321         * @description Internal property list of current button groups in the toolbar
322         * @type Array
323         */
324         _buttonGroupList: null,
325         /**
326         * @protected
327         * @property _sep
328         * @description Internal reference to the separator HTML Element for cloning
329         * @type HTMLElement
330         */
331         _sep: null,
332         /**
333         * @protected
334         * @property _sepCount
335         * @description Internal refernce for counting separators, so we can give them a useful class name for styling
336         * @type Number
337         */
338         _sepCount: null,
339         /**
340         * @protected
341         * @property draghandle
342         * @type HTMLElement
343         */
344         _dragHandle: null,
345         /**
346         * @protected
347         * @property _toolbarConfigs
348         * @type Object
349         */
350         _toolbarConfigs: {
351             renderer: true
352         },
353         /**
354         * @protected
355         * @property CLASS_CONTAINER
356         * @description Default CSS class to apply to the toolbar container element
357         * @type String
358         */
359         CLASS_CONTAINER: 'yui-toolbar-container',
360         /**
361         * @protected
362         * @property CLASS_DRAGHANDLE
363         * @description Default CSS class to apply to the toolbar's drag handle element
364         * @type String
365         */
366         CLASS_DRAGHANDLE: 'yui-toolbar-draghandle',
367         /**
368         * @protected
369         * @property CLASS_SEPARATOR
370         * @description Default CSS class to apply to all separators in the toolbar
371         * @type String
372         */
373         CLASS_SEPARATOR: 'yui-toolbar-separator',
374         /**
375         * @protected
376         * @property CLASS_DISABLED
377         * @description Default CSS class to apply when the toolbar is disabled
378         * @type String
379         */
380         CLASS_DISABLED: 'yui-toolbar-disabled',
381         /**
382         * @protected
383         * @property CLASS_PREFIX
384         * @description Default prefix for dynamically created class names
385         * @type String
386         */
387         CLASS_PREFIX: 'yui-toolbar',
388         /** 
389         * @method init
390         * @description The Toolbar class's initialization method
391         */
392         init: function(p_oElement, p_oAttributes) {
393             YAHOO.widget.Toolbar.superclass.init.call(this, p_oElement, p_oAttributes);
394         },
395         /**
396         * @method initAttributes
397         * @description Initializes all of the configuration attributes used to create 
398         * the toolbar.
399         * @param {Object} attr Object literal specifying a set of 
400         * configuration attributes used to create the toolbar.
401         */
402         initAttributes: function(attr) {
403             YAHOO.widget.Toolbar.superclass.initAttributes.call(this, attr);
404             var el = this.get('element');
405             this.addClass(this.CLASS_CONTAINER);
408             /**
409             * @config buttons
410             * @description Object specifying the buttons to include in the toolbar
411             * Example:
412             * <code><pre>
413             * {
414             *   { id: 'b3', type: 'button', label: 'Underline', value: 'underline' },
415             *   { type: 'separator' },
416             *   { id: 'b4', type: 'menu', label: 'Align', value: 'align',
417             *       menu: [
418             *           { text: "Left", value: 'alignleft' },
419             *           { text: "Center", value: 'aligncenter' },
420             *           { text: "Right", value: 'alignright' }
421             *       ]
422             *   }
423             * }
424             * </pre></code>
425             * @type Object
426             */
427             
428             this.setAttributeConfig('buttons', {
429                 value: [],
430                 writeOnce: true,
431                 method: function(data) {
432                     for (var i in data) {
433                         if (Lang.hasOwnProperty(data, i)) {
434                             if (data[i].type == 'separator') {
435                                 this.addSeparator();
436                             } else if (data[i].group != undefined) {
437                                 this.addButtonGroup(data[i]);
438                             } else {
439                                 this.addButton(data[i]);
440                             }
441                         }
442                     }
443                 }
444             });
446             /**
447             * @config disabled
448             * @description Boolean indicating if the toolbar should be disabled. It will also disable the draggable attribute if it is on.
449             * @default false
450             * @type Boolean
451             */
452             this.setAttributeConfig('disabled', {
453                 value: false,
454                 method: function(disabled) {
455                     if (!Lang.isObject(this._disabled)) {
456                         this._disabled = {};
457                     }
458                     if (disabled) {
459                         this.addClass(this.CLASS_DISABLED);
460                         this.set('draggable', false);
461                     } else {
462                         this.removeClass(this.CLASS_DISABLED);
463                         if (this._configs.draggable._initialConfig.value) {
464                             //Draggable by default, set it back
465                             this.set('draggable', true);
466                         }
467                     }
468                     var len = this._buttonList.length;
469                     for (var i = 0; i < len; i++) {
470                         if (disabled) {
471                             //If it's already disabled, flag it
472                             if (this._buttonList[i].get('disabled')) {
473                                 this._disabled[i] = true;
474                             } else {
475                                 this._disabled[i] = null;
476                             }
477                             this.disableButton(this._buttonList[i].get('id'));
478                         } else {
479                             //Check to see if it was disabled by default and skip it
480                             var _button = this._buttonList[i];
481                             var _check = _button._configs.disabled._initialConfig.value;
482                             if (this._disabled[i]) {
483                                 _check = true;
484                             }
485                             if (!_check) {
486                                 this.enableButton(_button.get('id'));
487                             }
488                         }
489                     }
490                 }
491             });
493             /**
494             * @config grouplabels
495             * @description Boolean indicating if the toolbar should show the group label's text string.
496             * @default true
497             * @type Boolean
498             */
499             this.setAttributeConfig('grouplabels', {
500                 value: true,
501                 writeOnce: true
502             });
504             /**
505             * @config cont
506             * @description Boolean indicating if the toolbar should show the group label's text string.
507             * @default true
508             * @type Boolean
509             */
510             this.setAttributeConfig('cont', {
511                 value: attr.cont,
512                 readOnly: true
513             });
515             /**
516             * @config collapse
517             * @description Boolean indicating if the the titlebar should have a collapse button.
518             * The collapse button will not remove the toolbar, it will minimize it to the titlebar
519             * @default false
520             * @type Boolean
521             */
522             this.setAttributeConfig('collapse', {
523                 value: false
524             });
525             /**
526             * @config titlebar
527             * @description Boolean indicating if the toolbar should have a titlebar. If
528             * passed a string, it will use that as the titlebar text
529             * @default false
530             * @type Boolean or String
531             */
532             this.setAttributeConfig('titlebar', {
533                 value: false,
534                 method: function(titlebar) {
535                     if (titlebar) {
536                         if (this._titlebar && this._titlebar.parentNode) {
537                             this._titlebar.parentNode.removeChild(this._titlebar);
538                         }
539                         this._titlebar = document.createElement('DIV');
540                         Dom.addClass(this._titlebar, this.CLASS_PREFIX + '-titlebar');
541                         if (Lang.isString(titlebar)) {
542                             var h2 = document.createElement('h2');
543                             h2.tabIndex = '-1';
544                             h2.innerHTML = titlebar;
545                             this._titlebar.appendChild(h2);
546                         }
547                         if (this.get('collapse')) {
548                             var collapse = document.createElement('SPAN');
549                             collapse.innerHTML = 'X';
550                             collapse.title = this.STR_COLLAPSE;
552                             Dom.addClass(collapse, 'collapse');
553                             this._titlebar.appendChild(collapse);
554                             Event.addListener(collapse, 'click', function() {
555                                 if (Dom.getStyle(this.get('cont'), 'display') == 'none') {
556                                     Dom.setStyle(this.get('cont'), 'display', 'block');
557                                     Dom.removeClass(collapse, 'collapsed');
558                                     this.fireEvent('toolbarExpanded', { type: 'toolbarExpanded', target: this });
559                                 } else {
560                                     Dom.setStyle(this.get('cont'), 'display', 'none');
561                                     Dom.addClass(collapse, 'collapsed');
562                                     this.fireEvent('toolbarCollapsed', { type: 'toolbarCollapsed', target: this });
563                                 }
564                             }, this, true);
565                         }
566                         if (this.get('draggable')) {
567                             this.dd = new YAHOO.util.DD(this.get('id'));
568                             this.dd.setHandleElId(this._titlebar);
569                             Dom.addClass(this._titlebar, 'draggable');
570                         }
571                         if (this.get('firstChild')) {
572                             this.insertBefore(this._titlebar, this.get('firstChild'));
573                         } else {
574                             this.appendChild(this._titlebar);
575                         }
576                     } else if (this._titlebar) {
577                         if (this._titlebar && this._titlebar.parentNode) {
578                             this._titlebar.parentNode.removeChild(this._titlebar);
579                         }
580                     }
581                 }
582             });
585             /**
586             * @config draggable
587             * @description Boolean indicating if the toolbar should be draggable.  
588             * @default false
589             * @type Boolean
590             */
592             this.setAttributeConfig('draggable', {
593                 value: (attr.draggable || false),
594                 method: function(draggable) {
595                     var el = this.get('element');
597                     if (draggable && !this.get('titlebar')) {
598                         if (!this._dragHandle) {
599                             this._dragHandle = document.createElement('SPAN');
600                             this._dragHandle.innerHTML = '|';
601                             this._dragHandle.setAttribute('title', 'Click to drag the toolbar');
602                             this._dragHandle.id = this.get('id') + '_draghandle';
603                             Dom.addClass(this._dragHandle, this.CLASS_DRAGHANDLE);
604                             if (this.get('cont').hasChildNodes()) {
605                                 this.get('cont').insertBefore(this._dragHandle, this.get('cont').firstChild);
606                             } else {
607                                 this.get('cont').appendChild(this._dragHandle);
608                             }
609                             /**
610                             * @property dd
611                             * @description The DragDrop instance associated with the Toolbar
612                             * @type Object
613                             */
614                             this.dd = new YAHOO.util.DD(this.get('id'));
615                             this.dd.setHandleElId(this._dragHandle.id);
616                             
617                         }
618                     } else {
619                         if (this._dragHandle) {
620                             this._dragHandle.parentNode.removeChild(this._dragHandle);
621                             this._dragHandle = null;
622                             this.dd = null;
623                         }
624                     }
625                     if (this._titlebar) {
626                         if (draggable) {
627                             this.dd = new YAHOO.util.DD(this.get('id'));
628                             this.dd.setHandleElId(this._titlebar);
629                             Dom.addClass(this._titlebar, 'draggable');
630                         } else {
631                             Dom.removeClass(this._titlebar, 'draggable');
632                             if (this.dd) {
633                                 this.dd.unreg();
634                                 this.dd = null;
635                             }
636                         }
637                     }
638                 },
639                 validator: function(value) {
640                     var ret = true;
641                     if (!YAHOO.util.DD) {
642                         ret = false;
643                     }
644                     return ret;
645                 }
646             });
648         },
649         /**
650         * @method addButtonGroup
651         * @description Add a new button group to the toolbar. (uses addButton)
652         * @param {Object} oGroup Object literal reference to the Groups Config (contains an array of button configs)
653         */
654         addButtonGroup: function(oGroup) {
655             if (!this.get('element')) {
656                 this._queue[this._queue.length] = ['addButtonGroup', arguments];
657                 return false;
658             }
659             
660             if (!this.hasClass(this.CLASS_PREFIX + '-grouped')) {
661                 this.addClass(this.CLASS_PREFIX + '-grouped');
662             }
663             var div = document.createElement('DIV');
664             Dom.addClass(div, this.CLASS_PREFIX + '-group');
665             Dom.addClass(div, this.CLASS_PREFIX + '-group-' + oGroup.group);
666             if (oGroup.label && this.get('grouplabels')) {
667                 var label = document.createElement('h3');
668                 label.innerHTML = oGroup.label;
669                 div.appendChild(label);
670             }
672             this.get('cont').appendChild(div);
674             //For accessibility, let's put all of the group buttons in an Unordered List
675             var ul = document.createElement('ul');
676             div.appendChild(ul);
678             if (!this._buttonGroupList) {
679                 this._buttonGroupList = {};
680             }
681             
682             this._buttonGroupList[oGroup.group] = ul;
684             for (var i = 0; i < oGroup.buttons.length; i++) {
685                 var li = document.createElement('li');
686                 ul.appendChild(li);
687                 if ((oGroup.buttons[i].type != undefined) && oGroup.buttons[i].type == 'separator') {
688                     this.addSeparator(li);
689                 } else {
690                     oGroup.buttons[i].container = li;
691                     this.addButton(oGroup.buttons[i]);
692                 }
693             }
694         },
695         /**
696         * @method addButtonToGroup
697         * @description Add a new button to a toolbar group. Buttons supported:
698         *   push, split, menu, select, color, spin
699         * @param {Object} oButton Object literal reference to the Button's Config
700         * @param {String} group The Group identifier passed into the initial config
701         * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
702         */
703         addButtonToGroup: function(oButton, group, after) {
704             var groupCont = this._buttonGroupList[group];
705             var li = document.createElement('li');
706             oButton.container = li;
707             this.addButton(oButton, after);
708             groupCont.appendChild(li);
709         },
710         /**
711         * @method addButton
712         * @description Add a new button to the toolbar. Buttons supported:
713         *   push, split, menu, select, color, spin
714         * @param {Object} oButton Object literal reference to the Button's Config
715         * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
716         */
717         addButton: function(oButton, after) {
718             if (!this.get('element')) {
719                 this._queue[this._queue.length] = ['addButton', arguments];
720                 return false;
721             }
722             if (!this._buttonList) {
723                 this._buttonList = [];
724             }
725             //Add to .get('buttons') manually
726             this._configs.buttons.value[this._configs.buttons.value.length] = oButton;
727             if (!oButton.container) {
728                 oButton.container = this.get('cont');
729             }
731             if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
732                 if (Lang.isArray(oButton.menu)) {
733                     for (var i in oButton.menu) {
734                         if (Lang.hasOwnProperty(oButton.menu, i)) {
735                             var funcObject = {
736                                 fn: function(ev, x, oMenu) {
737                                     if (!oButton.menucmd) {
738                                         oButton.menucmd = oButton.value;
739                                     }
740                                     oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
741                                     //This line made Opera fire the click event and the mousedown,
742                                     //  so events for menus where firing twice.
743                                     //this._buttonClick('click', oButton);
744                                 },
745                                 scope: this
746                             }
747                             oButton.menu[i].onclick = funcObject;
748                         }
749                     }
750                 }
751             }
752             var _oButton = {};
753             for (var i in oButton) {
754                 if (Lang.hasOwnProperty(oButton, i)) {
755                     if (!this._toolbarConfigs[i]) {
756                         _oButton[i] = oButton[i];
757                     }
758                 }
759             }
760             if (oButton.type == 'select') {
761                 _oButton.type = 'menu';
762             }
763             if (oButton.type == 'spin') {
764                 _oButton.type = 'push';
765             }
766             if (_oButton.type == 'color') {
767                 _oButton = this._makeColorButton(_oButton);
768             }
769             if (_oButton.menu) {
770                 if (oButton.menu instanceof YAHOO.widget.Overlay) {
771                     oButton.menu.showEvent.subscribe(function() {
772                         this._button = _oButton;
773                     });
774                 } else {
775                     for (var i = 0; i < _oButton.menu.length; i++) {
776                         if (!_oButton.menu[i].value) {
777                             _oButton.menu[i].value = _oButton.menu[i].text;
778                         }
779                     }
780                     if (this.browser.webkit) {
781                         _oButton.focusmenu = false;
782                     }
783                 }
784             }
785             var tmp = new YAHOO.widget.Button(_oButton);
786             if (this.get('disabled')) {
787                 //Toolbar is disabled, disable the new button too!
788                 tmp.set('disabled', true);
789             }
790             if (!oButton.id) {
791                 oButton.id = tmp.get('id');
792             }
793             
794             if (after) {
795                 var el = tmp.get('element');
796                 var nextSib = null;
797                 if (after.get) {
798                     nextSib = after.get('element').nextSibling;
799                 } else if (after.nextSibling) {
800                     nextSib = after.nextSibling;
801                 }
802                 if (nextSib) {
803                     nextSib.parentNode.insertBefore(el, nextSib);
804                 }
805             }
806             tmp.addClass(this.CLASS_PREFIX + '-' + tmp.get('value'));
807             var icon = document.createElement('span');
808             icon.className = this.CLASS_PREFIX + '-icon';
809             tmp.get('element').insertBefore(icon, tmp.get('firstChild'));
810             //Replace the Button HTML Element with an a href
811             var a = document.createElement('a');
812             a.innerHTML = tmp._button.innerHTML;
813             a.href = '#';
814             Event.on(a, 'click', function(ev) {
815                 Event.stopEvent(ev);
816             });
817             tmp._button.parentNode.replaceChild(a, tmp._button);
818             tmp._button = a;
820             if (oButton.type == 'select') {
821                 tmp.addClass(this.CLASS_PREFIX + '-select');
822             }
823             if (oButton.type == 'spin') {
824                 if (!Lang.isArray(oButton.range)) {
825                     oButton.range = [ 10, 100 ];
826                 }
827                 this._makeSpinButton(tmp, oButton);
828             }
830             tmp.get('element').setAttribute('title', tmp.get('label'));
832             if (oButton.type != 'spin') {
833                 if (_oButton.menu instanceof YAHOO.widget.Overlay) {
834                     var showPicker = function(ev) {
835                         var exec = true;
836                         if (ev.keyCode && (ev.keyCode == 9)) {
837                             exec = false;
838                         }
839                         if (exec) {
840                             this._colorPicker._button = oButton.value;
841                             var menuEL = tmp.getMenu().element;
842                             if (menuEL.style.visibility == 'hidden') {
843                                 tmp.getMenu().show();
844                             } else {
845                                 tmp.getMenu().hide();
846                             }
847                         }
848                         YAHOO.util.Event.stopEvent(ev);
849                     };
850                     tmp.on('mousedown', showPicker, oButton, this);
851                     tmp.on('keydown', showPicker, oButton, this);
852                     
853                 } else if ((oButton.type != 'menu') && (oButton.type != 'select')) {
854                     tmp.on('keypress', this._buttonClick, oButton, this);
855                     tmp.on('mousedown', function(ev) {
856                         this._buttonClick(ev, oButton);
857                         YAHOO.util.Event.stopEvent(ev);
858                     }, oButton, this);
859                 } else {
860                     //Stop the mousedown event so we can trap the selection in the editor!
861                     tmp.on('mousedown', function(ev) {
862                         YAHOO.util.Event.stopEvent(ev);
863                     });
864                     tmp.on('click', function(ev) {
865                         YAHOO.util.Event.stopEvent(ev);
866                     });
867                     var self = this;
868                     //Hijack the mousedown event in the menu and make it fire a button click..
869                     tmp.getMenu().mouseDownEvent.subscribe(function(ev, args) {
870                         var oMenu = args[1];
871                         YAHOO.util.Event.stopEvent(args[0]);
872                         tmp._onMenuClick(args[0], tmp);
873                         if (!oButton.menucmd) {
874                             oButton.menucmd = oButton.value;
875                         }
876                         oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
877                         self._buttonClick.call(self, args[1], oButton);
878                         tmp._hideMenu();
879                         return false;
880                     });
881                     tmp.getMenu().clickEvent.subscribe(function(ev, args) {
882                         YAHOO.util.Event.stopEvent(args[0]);
883                     });
884                 }
885             } else {
886                 //Stop the mousedown event so we can trap the selection in the editor!
887                 tmp.on('mousedown', function(ev) {
888                     YAHOO.util.Event.stopEvent(ev);
889                 });
890                 tmp.on('click', function(ev) {
891                     YAHOO.util.Event.stopEvent(ev);
892                 });
893             }
894             if (this.browser.ie) {
895                 //Add a couple of new events for IE
896                 tmp.DOM_EVENTS.focusin = true;
897                 tmp.DOM_EVENTS.focusout = true;
898                 
899                 //Stop them so we don't loose focus in the Editor
900                 tmp.on('focusin', function(ev) {
901                     YAHOO.util.Event.stopEvent(ev);
902                 }, oButton, this);
903                 
904                 tmp.on('focusout', function(ev) {
905                     YAHOO.util.Event.stopEvent(ev);
906                 }, oButton, this);
907                 tmp.on('click', function(ev) {
908                     YAHOO.util.Event.stopEvent(ev);
909                 }, oButton, this);
910             }
911             if (this.browser.webkit) {
912                 //This will keep the document from gaining focus and the editor from loosing it..
913                 //Forcefully remove the focus calls in button!
914                 tmp.hasFocus = function() {
915                     return true;
916                 }
917             }
918             this._buttonList[this._buttonList.length] = tmp;
919             if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
920                 if (Lang.isArray(oButton.menu)) {
921                     var menu = tmp.getMenu();
922                     menu.renderEvent.subscribe(_addMenuClasses, tmp);
923                     if (oButton.renderer) {
924                         menu.renderEvent.subscribe(oButton.renderer, tmp);
925                     }
926                 }
927             }
928             return oButton;
929         },
930         /**
931         * @method addSeparator
932         * @description Add a new button separator to the toolbar.
933         * @param {HTMLElement} cont Optional HTML element to insert this button into.
934         * @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
935         */
936         addSeparator: function(cont, after) {
937             if (!this.get('element')) {
938                 this._queue[this._queue.length] = ['addSeparator', arguments];
939                 return false;
940             }
941             var sepCont = ((cont) ? cont : this.get('cont'));
942             if (!this.get('element')) {
943                 this._queue[this._queue.length] = ['addSeparator', arguments];
944                 return false;
945             }
946             if (this._sepCount == null) {
947                 this._sepCount = 0;
948             }
949             if (!this._sep) {
950                 this._sep = document.createElement('SPAN');
951                 Dom.addClass(this._sep, this.CLASS_SEPARATOR);
952                 this._sep.innerHTML = '|';
953             }
954             var _sep = this._sep.cloneNode(true);
955             this._sepCount++;
956             Dom.addClass(_sep, this.CLASS_SEPARATOR + '-' + this._sepCount);
957             if (after) {
958                 var nextSib = null;
959                 if (after.get) {
960                     nextSib = after.get('element').nextSibling;
961                 } else if (after.nextSibling) {
962                     nextSib = after.nextSibling;
963                 } else {
964                     nextSib = after;
965                 }
966                 if (nextSib) {
967                     if (nextSib == after) {
968                         nextSib.parentNode.appendChild(_sep);
969                     } else {
970                         nextSib.parentNode.insertBefore(_sep, nextSib);
971                     }
972                 }
973             } else {
974                 sepCont.appendChild(_sep);
975             }
976             return _sep;
977         },
978         /**
979         * @method _createColorPicker
980         * @private
981         * @description Creates the core DOM reference to the color picker menu item.
982         * @param {String} id the id of the toolbar to prefix this DOM container with.
983         */
984         _createColorPicker: function(id) {
985             if (Dom.get(id + '_colors')) {
986                Dom.get(id + '_colors').parentNode.removeChild(Dom.get(id + '_colors'));
987             }
988             var picker = document.createElement('div');
989             picker.className = 'yui-toolbar-colors';
990             picker.id = id + '_colors';
991             picker.style.display = 'none';
992             Event.on(window, 'load', function() {
993                 document.body.appendChild(picker);
994             }, this, true);
996             this._colorPicker = picker;
998             var html = '';
999             for (var i in this._colorData) {
1000                 if (Lang.hasOwnProperty(this._colorData, i)) {
1001                     html += '<a style="background-color: ' + i + '" href="#">' + i.replace('#', '') + '</a>';
1002                 }
1003             }
1004             html += '<span><em>X</em><strong></strong></span>';
1005             picker.innerHTML = html;
1006             var em = picker.getElementsByTagName('em')[0];
1007             var strong = picker.getElementsByTagName('strong')[0];
1009             Event.on(picker, 'mouseover', function(ev) {
1010                 var tar = Event.getTarget(ev);
1011                 if (tar.tagName.toLowerCase() == 'a') {
1012                     em.style.backgroundColor = tar.style.backgroundColor;
1013                     strong.innerHTML = this._colorData['#' + tar.innerHTML] + '<br>' + tar.innerHTML;
1014                 }
1015             }, this, true);
1016             Event.on(picker, 'focus', function(ev) {
1017                 Event.stopEvent(ev);
1018             });
1019             Event.on(picker, 'click', function(ev) {
1020                 Event.stopEvent(ev);
1021             });
1022             Event.on(picker, 'mousedown', function(ev) {
1023                 Event.stopEvent(ev);
1024                 var tar = Event.getTarget(ev);
1025                 if (tar.tagName.toLowerCase() == 'a') {
1026                     this.fireEvent('colorPickerClicked', { type: 'colorPickerClicked', target: this, button: this._colorPicker._button, color: tar.innerHTML, colorName: this._colorData['#' + tar.innerHTML] } );
1027                     this.getButtonByValue(this._colorPicker._button).getMenu().hide();
1028                 }
1029             }, this, true);
1030         },
1031         /**
1032         * @method _resetColorPicker
1033         * @private
1034         * @description Clears the currently selected color or mouseover color in the color picker.
1035         */
1036         _resetColorPicker: function() {
1037             var em = this._colorPicker.getElementsByTagName('em')[0];
1038             var strong = this._colorPicker.getElementsByTagName('strong')[0];
1039             em.style.backgroundColor = 'transparent';
1040             strong.innerHTML = '';
1041         },
1042         /**
1043         * @method _makeColorButton
1044         * @private
1045         * @description Called to turn a "color" button into a menu button with an Overlay for the menu.
1046         * @param {Object} _oButton <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> reference
1047         */
1048         _makeColorButton: function(_oButton) {
1049             if (!this._colorPicker) {   
1050                 this._createColorPicker(this.get('id'));
1051             }
1052             _oButton.type = 'color';
1053             _oButton.menu = new YAHOO.widget.Overlay(this.get('id') + '_' + _oButton.value + '_menu', { visbile: false, position: 'absolute' });
1054             _oButton.menu.setBody('');
1055             _oButton.menu.render(this.get('cont'));
1056             _oButton.menu.beforeShowEvent.subscribe(function() {
1057                 _oButton.menu.cfg.setProperty('zindex', 5); //Re Adjust the overlays zIndex.. not sure why.
1058                 _oButton.menu.cfg.setProperty('context', [this.getButtonById(_oButton.id).get('element'), 'tl', 'bl']); //Re Adjust the overlay.. not sure why.
1059                 //Move the DOM reference of the color picker to the Overlay that we are about to show.
1060                 this._resetColorPicker();
1061                 var _p = this._colorPicker;
1062                 if (_p.parentNode) {
1063                     //if (_p.parentNode != _oButton.menu.body) {
1064                         _p.parentNode.removeChild(_p);
1065                     //}
1066                 }
1067                 _oButton.menu.setBody('');
1068                 _oButton.menu.appendToBody(_p);
1069                 this._colorPicker.style.display = 'block';
1070             }, this, true);
1071             return _oButton;
1072         },
1073         /**
1074         * @private
1075         * @method _makeSpinButton
1076         * @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.
1077         * @param {Object} _button <a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> reference
1078         * @param {Object} oButton Object literal containing the buttons initial config
1079         */
1080         _makeSpinButton: function(_button, oButton) {
1081             _button.addClass(this.CLASS_PREFIX + '-spinbutton');
1082             var self = this,
1083                 _par = _button._button.parentNode.parentNode, //parentNode of Button Element for appending child
1084                 range = oButton.range,
1085                 _b1 = document.createElement('a'),
1086                 _b2 = document.createElement('a');
1087                 _b1.href = '#';
1088                 _b2.href = '#';
1089             
1090             //Setup the up and down arrows
1091             _b1.className = 'up';
1092             _b1.title = this.STR_SPIN_UP;
1093             _b1.innerHTML = this.STR_SPIN_UP;
1094             _b2.className = 'down';
1095             _b2.title = this.STR_SPIN_DOWN;
1096             _b2.innerHTML = this.STR_SPIN_DOWN;
1098             //Append them to the container
1099             _par.appendChild(_b1);
1100             _par.appendChild(_b2);
1101             
1102             var label = YAHOO.lang.substitute(this.STR_SPIN_LABEL, { VALUE: _button.get('label') });
1103             _button.set('title', label);
1105             var cleanVal = function(value) {
1106                 value = ((value < range[0]) ? range[0] : value);
1107                 value = ((value > range[1]) ? range[1] : value);
1108                 return value;
1109             };
1111             var br = this.browser;
1112             var tbar = false;
1113             var strLabel = this.STR_SPIN_LABEL;
1114             if (this._titlebar && this._titlebar.firstChild) {
1115                 tbar = this._titlebar.firstChild;
1116             }
1117             
1118             var _intUp = function(ev) {
1119                 YAHOO.util.Event.stopEvent(ev);
1120                 if (!_button.get('disabled') && (ev.keyCode != 9)) {
1121                     var value = parseInt(_button.get('label'));
1122                     value++;
1123                     value = cleanVal(value);
1124                     _button.set('label', ''+value);
1125                     var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1126                     _button.set('title', label);
1127                     if (!br.webkit && tbar) {
1128                         //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
1129                         //_button.focus();
1130                     }
1131                     self._buttonClick(ev, oButton);
1132                 }
1133             };
1135             var _intDown = function(ev) {
1136                 YAHOO.util.Event.stopEvent(ev);
1137                 if (!_button.get('disabled') && (ev.keyCode != 9)) {
1138                     var value = parseInt(_button.get('label'));
1139                     value--;
1140                     value = cleanVal(value);
1142                     _button.set('label', ''+value);
1143                     var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1144                     _button.set('title', label);
1145                     if (!br.webkit && tbar) {
1146                         //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
1147                         //_button.focus();
1148                     }
1149                     self._buttonClick(ev, oButton);
1150                 }
1151             };
1153             var _intKeyUp = function(ev) {
1154                 if (ev.keyCode == 38) {
1155                     _intUp(ev);
1156                 } else if (ev.keyCode == 40) {
1157                     _intDown(ev);
1158                 } else if (ev.keyCode == 107 && ev.shiftKey) {  //Plus Key
1159                     _intUp(ev);
1160                 } else if (ev.keyCode == 109 && ev.shiftKey) {  //Minus Key
1161                     _intDown(ev);
1162                 }
1163             };
1165             //Handle arrow keys..
1166             _button.on('keydown', _intKeyUp, this, true);
1168             //Listen for the click on the up button and act on it
1169             //Listen for the click on the down button and act on it
1170             Event.on(_b1, 'mousedown',function(ev) {
1171                 Event.stopEvent(ev);
1172             }, this, true);
1173             Event.on(_b2, 'mousedown', function(ev) {
1174                 Event.stopEvent(ev);
1175             }, this, true);
1176             Event.on(_b1, 'click', _intUp, this, true);
1177             Event.on(_b2, 'click', _intDown, this, true);
1178         },
1179         /**
1180         * @protected
1181         * @method _buttonClick
1182         * @description Click handler for all buttons in the toolbar.
1183         * @param {String} ev The event that was passed in.
1184         * @param {Object} info Object literal of information about the button that was clicked.
1185         */
1186         _buttonClick: function(ev, info) {
1187             var doEvent = true;
1188             
1189             if (ev && ev.type == 'keypress') {
1190                 if (ev.keyCode == 9) {
1191                     doEvent = false;
1192                 } else if ((ev.keyCode == 13) || (ev.keyCode == 0) || (ev.keyCode == 32)) {
1193                 } else {
1194                     doEvent = false;
1195                 }
1196             }
1198             if (doEvent) {
1199                 if (info.value) {
1200                     this.fireEvent(info.value + 'Click', { type: info.value + 'Click', target: this.get('element'), button: info });
1201                 }
1202                 
1203                 if (info.menucmd) {
1204                     this.fireEvent(info.menucmd + 'Click', { type: info.menucmd + 'Click', target: this.get('element'), button: info });
1205                 }
1206                 
1207                 this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
1209                 if (info.type == 'select') {
1210                     var button = this.getButtonById(info.id);
1211                     var txt = info.value;
1212                     for (var i = 0; i < info.menu.length; i++) {
1213                         if (info.menu[i].value == info.value) {
1214                             txt = info.menu[i].text;
1215                             break;
1216                         }
1217                     }
1218                     button.set('label', '<span class="yui-toolbar-' + info.menucmd + '-' + (info.value).replace(/ /g, '-').toLowerCase() + '">' + txt + '</span>');
1219                     var _items = button.getMenu().getItems();
1220                     for (var m = 0; m < _items.length; m++) {
1221                         if (_items[m].value.toLowerCase() == info.value.toLowerCase()) {
1222                             _items[m].cfg.setProperty('checked', true);
1223                         } else {
1224                             _items[m].cfg.setProperty('checked', false);
1225                         }
1226                     }
1227                 }
1228             }
1229             if (ev) {
1230                 Event.stopEvent(ev);
1231             }
1232         },
1233         /**
1234         * @method getButtonById
1235         * @description Gets a button instance from the toolbar by is Dom id.
1236         * @param {String} id The Dom id to query for.
1237         * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}
1238         */
1239         getButtonById: function(id) {
1240             var len = this._buttonList.length;
1241             for (var i = 0; i < len; i++) {
1242                 if (this._buttonList[i].get('id') == id) {
1243                     return this._buttonList[i];
1244                 }
1245             }
1246             return false;
1247         },
1248         /**
1249         * @method getButtonByValue
1250         * @description Gets a button instance or a menuitem instance from the toolbar by it's value.
1251         * @param {String} value The button value to query for.
1252         * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a> or <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a>}
1253         */
1254         getButtonByValue: function(value) {
1255             var _buttons = this.get('buttons');
1256             var len = _buttons.length;
1257             for (var i = 0; i < len; i++) {
1258                 if (_buttons[i].group != undefined) {
1259                     for (var m = 0; m < _buttons[i].buttons.length; m++) {
1260                         if ((_buttons[i].buttons[m].value == value) || (_buttons[i].buttons[m].menucmd == value)) {
1261                             return this.getButtonById(_buttons[i].buttons[m].id);
1262                         }
1263                         if (_buttons[i].buttons[m].menu) { //Menu Button, loop through the values
1264                             for (var s = 0; s < _buttons[i].buttons[m].menu.length; s++) {
1265                                 if (_buttons[i].buttons[m].menu[s].value == value) {
1266                                     return this.getButtonById(_buttons[i].buttons[m].id);
1267                                 }
1268                             }
1269                         }
1270                     }
1271                 } else {
1272                     if ((_buttons[i].value == value) || (_buttons[i].menucmd == value)) {
1273                         return this.getButtonById(_buttons[i].id);
1274                     }
1275                     if (_buttons[i].menu) { //Menu Button, loop through the values
1276                         for (var s = 0; s < _buttons[i].menu.length; s++) {
1277                             if (_buttons[i].menu[s].value == value) {
1278                                 return this.getButtonById(_buttons[i].id);
1279                             }
1280                         }
1281                     }
1282                 }
1283             }
1284             return false;
1285         },
1286         /**
1287         * @method getButtonByIndex
1288         * @description Gets a button instance from the toolbar by is index in _buttonList.
1289         * @param {Number} index The index of the button in _buttonList.
1290         * @return {<a href="YAHOO.widget.Button.html">YAHOO.widget.Button</a>}
1291         */
1292         getButtonByIndex: function(index) {
1293             if (this._buttonList[index]) {
1294                 return this._buttonList[index];
1295             } else {
1296                 return false;
1297             }
1298         },
1299         /**
1300         * @method getButtons
1301         * @description Returns an array of buttons in the current toolbar
1302         * @return {Array}
1303         */
1304         getButtons: function() {
1305             return this._buttonList;
1306         },
1307         /**
1308         * @method disableButton
1309         * @description Disables a button in the toolbar.
1310         * @param {String/Number} button Disable a button by it's id or index.
1311         * @return {Boolean}
1312         */
1313         disableButton: function(button) {
1314             if (Lang.isString(button)) {
1315                 var button = this.getButtonById(button);
1316             }
1317             if (Lang.isNumber(button)) {
1318                 var button = this.getButtonByIndex(button);
1319             }
1320             if (button instanceof YAHOO.widget.Button) {
1321                 button.set('disabled', true);
1322             } else {
1323                 return false;
1324             }
1325         },
1326         /**
1327         * @method enableButton
1328         * @description Enables a button in the toolbar.
1329         * @param {String/Number} button Enable a button by it's id or index.
1330         * @return {Boolean}
1331         */
1332         enableButton: function(button) {
1333             if (Lang.isString(button)) {
1334                 var button = this.getButtonById(button);
1335             }
1336             if (Lang.isNumber(button)) {
1337                 var button = this.getButtonByIndex(button);
1338             }
1339             if (button instanceof YAHOO.widget.Button) {
1340                 button.set('disabled', false);
1341             } else {
1342                 return false;
1343             }
1344         },
1345         /**
1346         * @method selectButton
1347         * @description Selects a button in the toolbar.
1348         * @param {String/Number} button select a button by it's id or index.
1349         * @return {Boolean}
1350         */
1351         selectButton: function(button, value) {
1352             if (button) {
1353                 if (Lang.isString(button)) {
1354                     var button = this.getButtonById(button);
1355                 }
1356                 if (Lang.isNumber(button)) {
1357                     var button = this.getButtonByIndex(button);
1358                 }
1359                 if (button instanceof YAHOO.widget.Button) {
1360                     button.addClass('yui-button-selected');
1361                     button.addClass('yui-button-' + button.get('value') + '-selected');
1362                     if (value) {
1363                         var _items = button.getMenu().getItems();
1364                         for (var m = 0; m < _items.length; m++) {
1365                             if (_items[m].value == value) {
1366                                 _items[m].cfg.setProperty('checked', true);
1367                                 button.set('label', '<span class="yui-toolbar-' + button.get('value') + '-' + (value).replace(/ /g, '-').toLowerCase() + '">' + _items[m]._oText.nodeValue + '</span>');
1368                             } else {
1369                                 _items[m].cfg.setProperty('checked', false);
1370                             }
1371                         }
1372                     }
1373                 } else {
1374                     return false;
1375                 }
1376             }
1377         },
1378         /**
1379         * @method deselectButton
1380         * @description Deselects a button in the toolbar.
1381         * @param {String/Number} button Deselect a button by it's id or index.
1382         * @return {Boolean}
1383         */
1384         deselectButton: function(button) {
1385             if (Lang.isString(button)) {
1386                 var button = this.getButtonById(button);
1387             }
1388             if (Lang.isNumber(button)) {
1389                 var button = this.getButtonByIndex(button);
1390             }
1391             if (button instanceof YAHOO.widget.Button) {
1392                 button.removeClass('yui-button-selected');
1393                 button.removeClass('yui-button-' + button.get('value') + '-selected');
1394                 button.removeClass('yui-button-hover');
1395             } else {
1396                 return false;
1397             }
1398         },
1399         /**
1400         * @method deselectAllButtons
1401         * @description Deselects all buttons in the toolbar.
1402         * @return {Boolean}
1403         */
1404         deselectAllButtons: function() {
1405             var len = this._buttonList.length;
1406             for (var i = 0; i < len; i++) {
1407                 this.deselectButton(this._buttonList[i]);
1408             }
1409         },
1410         /**
1411         * @method destroyButton
1412         * @description Destroy a button in the toolbar.
1413         * @param {String/Number} button Destroy a button by it's id or index.
1414         * @return {Boolean}
1415         */
1416         destroyButton: function(button) {
1417             if (Lang.isString(button)) {
1418                 var button = this.getButtonById(button);
1419             }
1420             if (Lang.isNumber(button)) {
1421                 var button = this.getButtonByIndex(button);
1422             }
1423             if (button instanceof YAHOO.widget.Button) {
1424                 var id = button.get('id');
1425                 button.destroy();
1427                 var len = this._buttonList.length;
1428                 for (var i = 0; i < len; i++) {
1429                     if (this._buttonList[i].get('id') == id) {
1430                         this._buttonList[i] = null;
1431                     }
1432                 }
1433             } else {
1434                 return false;
1435             }
1437         },
1438         /**
1439         * @method destroy
1440         * @description Destroys the toolbar, all of it's elements and objects.
1441         * @return {Boolean}
1442         */
1443         destroy: function() {
1444             this.get('element').innerHTML = '';
1445             this.get('element').className = '';
1446             //Brutal Object Destroy
1447             for (var i in this) {
1448                 if (Lang.hasOwnProperty(this, i)) {
1449                     this[i] = null;
1450                 }
1451             }
1452             return true;
1453         },
1454         /**
1455         * @method toString
1456         * @description Returns a string representing the toolbar.
1457         * @return {String}
1458         */
1459         toString: function() {
1460             return 'Toolbar (#' + this.get('element').id + ') with ' + this._buttonList.length + ' buttons.';
1461         }
1462     });
1464 * @event buttonClick
1465 * @param {Object} o The object passed to this handler is the button config used to create the button.
1466 * @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.
1467 * @type YAHOO.util.CustomEvent
1470 * @event valueClick
1471 * @param {Object} o The object passed to this handler is the button config used to create the button.
1472 * @description This is a special dynamic event that is created and dispatched based on the value property
1473 * of the button config. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
1474 * Example:
1475 * <code><pre>
1476 * buttons : [
1477 *   { type: 'button', value: 'test', value: 'testButton' }
1478 * ]</pre>
1479 * </code>
1480 * With the valueClick event you could subscribe to this buttons click event with this:
1481 * tbar.in('testButtonClick', function() { alert('test button clicked'); })
1482 * @type YAHOO.util.CustomEvent
1485 * @event toolbarExpanded
1486 * @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.
1487 * @type YAHOO.util.CustomEvent
1490 * @event toolbarCollapsed
1491 * @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.
1492 * @type YAHOO.util.CustomEvent
1494 })();
1496 Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1497 Code licensed under the BSD License:
1498 http://developer.yahoo.net/yui/license.txt
1501  * @module editor
1502  * @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>
1503  * @namespace YAHOO.widget
1504  * @requires yahoo, dom, element, event, toolbar, container, menu, button
1505  * @optional dragdrop, animation
1506  * @beta
1507  */
1509 (function() {
1510 var Dom = YAHOO.util.Dom,
1511     Event = YAHOO.util.Event,
1512     Lang = YAHOO.lang,
1513     DD = YAHOO.util.DD,
1514     Toolbar = YAHOO.widget.Toolbar;
1517     /**
1518      * 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.
1519      * @constructor
1520      * @class Editor
1521      * @extends YAHOO.util.Element
1522      * @param {String/HTMLElement} el The textarea element to turn into an editor.
1523      * @param {Object} attrs Object liternal containing configuration parameters.
1524     */
1525     
1526     YAHOO.widget.Editor = function(el, attrs) {
1528         var oConfig = {
1529             element: null,
1530             attributes: (attrs || {})
1531         }
1533         if (Lang.isString(el)) {
1534             oConfig.attributes.textarea = Dom.get(el);
1535         }
1536         
1537         var element_cont = document.createElement('DIV');
1538         oConfig.attributes.element_cont = new YAHOO.util.Element(element_cont, {
1539             id: oConfig.attributes.textarea.id + '_container'
1540         });
1541         oConfig.attributes.element_cont.setStyle('display', 'none');
1543         oConfig.element = oConfig.attributes.textarea;
1545         var div = document.createElement('div');
1546         oConfig.attributes.element_cont.appendChild(div);
1547         
1548         if (!oConfig.attributes.toolbar_cont) {
1549             oConfig.attributes.toolbar_cont = document.createElement('DIV');
1550             oConfig.attributes.toolbar_cont.id = oConfig.attributes.textarea.id + '_toolbar';
1551             div.appendChild(oConfig.attributes.toolbar_cont);
1552         }
1553         
1554         if (!oConfig.attributes.iframe) {
1555             oConfig.attributes.iframe = _createIframe(oConfig.attributes.textarea.id);
1556             var editorWrapper = document.createElement('DIV');
1557             editorWrapper.appendChild(oConfig.attributes.iframe.get('element'));
1558             div.appendChild(editorWrapper);
1559         }
1561         Event.onDOMReady(function() {
1562             this.DOMReady = true;
1563             this.fireQueue();
1564         }, this, true);
1566         YAHOO.widget.Editor.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
1567     }
1569     /**
1570     * @private _cleanClassName
1571     * @description Makes a useable classname from dynamic data, by dropping it to lowercase and replacing spaces with -'s.
1572     * @param {String} str The classname to clean up
1573     * @returns {String}
1574     */
1575     function _cleanClassName(str) {
1576         return str.replace(/ /g, '-').toLowerCase();
1577     }
1579     /**
1580     * @private _createIframe
1581     * @description Creates the DOM and YUI Element for the iFrame editor area.
1582     * @param {String} id The string ID to prefix the iframe with
1583     * @returns {Object} iFrame object
1584     */
1585     function _createIframe(id) {
1586         var ifrmID = id + '_editor';
1587         var ifrmDom = document.createElement('iframe');
1588         ifrmDom.id = ifrmID;
1589         var config = {
1590             border: '0',
1591             frameBorder: '0',
1592             marginWidth: '0',
1593             marginHeight: '0',
1594             leftMargin: '0',
1595             topMargin: '0',
1596             allowTransparency: 'true',
1597             width: '100%',
1598             src: 'javascript:false'
1599         }
1600         for (var i in config) {
1601             if (Lang.hasOwnProperty(config, i)) {
1602                 ifrmDom.setAttribute(i, config[i]);
1603             }
1604         }
1606         var ifrm = new YAHOO.util.Element(ifrmDom);
1607         ifrm.setStyle('zIndex', '-1');
1608         return ifrm;
1609     }
1611     YAHOO.extend(YAHOO.widget.Editor, YAHOO.util.Element, {
1612         /**
1613         * @property DOMReady
1614         * @private
1615         * @description Flag to determine if DOM is ready or not
1616         * @type Boolean
1617         */
1618         DOMReady: null,
1619         /**
1620         * @property _selection
1621         * @private
1622         * @description Holder for caching iframe selections
1623         * @type Object
1624         */
1625         _selection: null,
1626         /**
1627         * @property _mask
1628         * @private
1629         * @description DOM Element holder for the editor Mask when disabled
1630         * @type Object
1631         */
1632         _mask: null,
1633         /**
1634         * @property _showingHiddenElements
1635         * @private
1636         * @description Status of the hidden elements button
1637         * @type Boolean
1638         */
1639         _showingHiddenElements: null,
1640         /**
1641         * @property currentWindow
1642         * @description A reference to the currently open EditorWindow
1643         * @type Object
1644         */
1645         currentWindow: null,
1646         /**
1647         * @property currentEvent
1648         * @description A reference to the current editor event
1649         * @type Event
1650         */
1651         currentEvent: null,
1652         /**
1653         * @property operaEvent
1654         * @private
1655         * @description setTimeout holder for Opera and Image DoubleClick event..
1656         * @type Object
1657         */
1658         operaEvent: null,
1659         /**
1660         * @property currentFont
1661         * @description A reference to the last font selected from the Toolbar
1662         * @type HTMLElement
1663         */
1664         currentFont: null,
1665         /**
1666         * @property currentElement
1667         * @description A reference to the current working element in the editor
1668         * @type Array
1669         */
1670         currentElement: [],
1671         /**
1672         * @property dompath
1673         * @description A reference to the dompath container for writing the current working dom path to.
1674         * @type HTMLElement
1675         */
1676         dompath: null,
1677         /**
1678         * @property beforeElement
1679         * @description A reference to the H2 placed before the editor for Accessibilty.
1680         * @type HTMLElement
1681         */
1682         beforeElement: null,
1683         /**
1684         * @property afterElement
1685         * @description A reference to the H2 placed after the editor for Accessibilty.
1686         * @type HTMLElement
1687         */
1688         afterElement: null,
1689         /**
1690         * @property invalidHTML
1691         * @description Contains a list of HTML elements that are invalid inside the editor. They will be removed when they are found.
1692         * @type Object
1693         */
1694         invalidHTML: {
1695             form: true,
1696             input: true,
1697             button: true,
1698             select: true,
1699             link: true,
1700             html: true,
1701             body: true,
1702             script: true,
1703             style: true,
1704             textarea: true
1705         },
1706         /**
1707         * @property toolbar
1708         * @description Local property containing the <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a> instance
1709         * @type <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>
1710         */
1711         toolbar: null,
1712         /**
1713         * @private
1714         * @property _contentTimer
1715         * @description setTimeout holder for documentReady check
1716         */
1717         _contentTimer: null,
1718         /**
1719         * @private
1720         * @property _contentTimerCounter
1721         * @description Counter to check the number of times the body is polled for before giving up
1722         * @type Number
1723         */
1724         _contentTimerCounter: 0,
1725         /**
1726         * @private
1727         * @property _disabled
1728         * @description The Toolbar items that should be disabled if there is no selection present in the editor.
1729         * @type Array
1730         */
1731         _disabled: [ 'createlink', 'forecolor', 'backcolor', 'fontname', 'fontsize', 'superscript', 'subscript', 'removeformat', 'heading', 'indent', 'outdent' ],
1732         /**
1733         * @private
1734         * @property _alwaysDisabled
1735         * @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
1736         * @type Object
1737         */
1738         _alwaysDisabled: { },
1739         /**
1740         * @private
1741         * @property _alwaysEnabled
1742         * @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
1743         * @type Object
1744         */
1745         _alwaysEnabled: { hiddenelements: true },
1746         /**
1747         * @private
1748         * @property _semantic
1749         * @description The Toolbar commands that we should attempt to make tags out of instead of using styles.
1750         * @type Object
1751         */
1752         _semantic: { 'bold': true, 'italic' : true, 'underline' : true },
1753         /**
1754         * @private
1755         * @property _tag2cmd
1756         * @description A tag map of HTML tags to convert to the different types of commands so we can select the proper toolbar button.
1757         * @type Object
1758         */
1759         _tag2cmd: {
1760             'b': 'bold',
1761             'strong': 'bold',
1762             'i': 'italic',
1763             'em': 'italic',
1764             'u': 'underline',
1765             'blockquote': 'formatblock',
1766             'sup': 'superscript',
1767             'sub': 'subscript',
1768             'img': 'insertimage',
1769             'a' : 'createlink',
1770             'ul' : 'insertunorderedlist',
1771             'ol' : 'insertorderedlist',
1772             'indent' : 'indent',
1773             'outdent' : 'outdent'
1774         },
1775         /**
1776         * @private
1777         * @method _getDoc
1778         * @description Get the Document of the IFRAME
1779         * @return {Object}
1780         */
1781         _getDoc: function() {
1782             //if (this.get && this.get('iframe') && this.get('iframe').get && this.get('iframe').get('element') && this.get('iframe').get('element').contentWindow && this.get('iframe').get('element').contentWindow.document) {
1783             var value = false;
1784             if (this.get) {
1785                 if (this.get('iframe')) {
1786                     if (this.get('iframe').get) {
1787                         if (this.get('iframe').get('element')) {
1788                             try {
1789                                 if (this.get('iframe').get('element').contentWindow) {
1790                                     if (this.get('iframe').get('element').contentWindow.document) {
1791                                         value = this.get('iframe').get('element').contentWindow.document;
1792                                     }
1793                                 }
1794                             } catch (e) {}
1795                         }
1796                     }
1797                 }
1798             }
1799             return value;
1800         },
1801         /**
1802         * @private
1803         * @method _getWindow
1804         * @description Get the Window of the IFRAME
1805         * @return {Object}
1806         */
1807         _getWindow: function() {
1808             return this.get('iframe').get('element').contentWindow;
1809         },
1810         /**
1811         * @private
1812         * @method _focusWindow
1813         * @description Attempt to set the focus of the iframes window.
1814         * @param {Boolean} onLoad Safari needs some special care to set the cursor in the iframe
1815         */
1816         _focusWindow: function(onLoad) {
1817             if (this.browser.webkit) {
1818                 if (onLoad) {
1819                     /**
1820                     * @knownissue Safari Cursor Position
1821                     * @browser Safari 2.x
1822                     * @description Can't get Safari to place the cursor at the beginning of the text..
1823                     * This workaround at least set's the toolbar into the proper state.
1824                     */
1825                     this._getSelection().setBaseAndExtent(this._getDoc().body, 0, this._getDoc().body, 1);
1826                     this._getSelection().collapse(false);   
1827                 } else {
1828                     this._getSelection().setBaseAndExtent(this._getDoc().body, 1, this._getDoc().body, 1);
1829                     this._getSelection().collapse(false);   
1830                 }
1831                 this._getWindow().focus();
1832                 //Check for.webkit3
1833                 if (this._getDoc().queryCommandEnabled('insertimage')) {
1834                     this.browser.webkit3 = true;
1835                 }
1836             } else {
1837                 this._getWindow().focus();
1838             }
1839         },
1840         /**
1841         * @private
1842         * @method _hasSelection
1843         * @description Determines if there is a selection in the editor document.
1844         * @returns {Boolean}
1845         */
1846         _hasSelection: function() {
1847             var sel = this._getSelection();
1848             var range = this._getRange();
1849             var hasSel = false;
1851             //Internet Explorer
1852             if (this.browser.ie || this.browser.opera) {
1853                 if (range.text) {
1854                     hasSel = true;
1855                 }
1856                 if (range.html) {
1857                     hasSel = true;
1858                 }
1859             } else {
1860                 if ((sel != '') && (sel != undefined)) {
1861                     hasSel = true;
1862                 }
1863             }
1864             return hasSel;
1865         },
1866         /**
1867         * @private
1868         * @method _getSelection
1869         * @description Handles the different selection objects across the A-Grade list.
1870         * @returns {Object} Selection Object
1871         */
1872         _getSelection: function() {
1873             var _sel = null;
1874             if (this._getDoc() && this._getWindow()) {
1875                 if (this._getDoc().selection) {
1876                     _sel = this._getDoc().selection;
1877                 } else {
1878                     _sel = this._getWindow().getSelection();
1879                 }
1880                 //Handle Safari's lack of Selection Object
1881                 if (this.browser.webkit) {
1882                     if (_sel.baseNode) {
1883                             this._selection = new Object();
1884                             this._selection.baseNode = _sel.baseNode;
1885                             this._selection.baseOffset = _sel.baseOffset;
1886                             this._selection.extentNode = _sel.extentNode;
1887                             this._selection.extentOffset = _sel.extentOffset;
1888                     } else if (this._selection != null) {
1889                         _sel = this._getWindow().getSelection();
1890                         _sel.setBaseAndExtent(
1891                             this._selection.baseNode,
1892                             this._selection.baseOffset,
1893                             this._selection.extentNode,
1894                             this._selection.extentOffset
1895                         );
1896                         this._selection = null;
1897                     }
1898                 }
1899             }
1900             return _sel;
1901         },
1902         /**
1903         * @private
1904         * @method _getRange
1905         * @description Handles the different range objects across the A-Grade list.
1906         * @returns {Object} Range Object
1907         */
1908         _getRange: function(sel) {
1909             var sel = this._getSelection();
1911             if (sel == null) {
1912                 return null;
1913             }
1915             if (this.browser.webkit && !sel.getRangeAt) {
1916                 var _range = this._getDoc().createRange();
1917                 try {
1918                     _range.setStart(sel.anchorNode, sel.anchorOffset);
1919                     _range.setEnd(sel.focusNode, sel.focusOffset);
1920                 } catch (e) {
1921                     _range = this._getWindow().getSelection()+'';
1922                 }
1923                 return _range;
1924             }
1926             if (this.browser.ie || this.browser.opera) {
1927                 return sel.createRange();
1928             }
1930             if (sel.rangeCount > 0) {
1931                 return sel.getRangeAt(0);
1932             }
1933             return null;
1934         },
1935         /**
1936         * @private
1937         * @method _setDesignMode
1938         * @description Sets the designMode of the iFrame document.
1939         * @param {String} state This should be either on or off
1940         */
1941         _setDesignMode: function(state) {
1942             try {
1943                 this._getDoc().designMode = state;
1944             } catch(e) { }
1945         },
1946         /**
1947         * @private
1948         * @method _toggleDesignMode
1949         * @description Toggles the designMode of the iFrame document on and off.
1950         * @returns {String} The state that it was set to.
1951         */
1952         _toggleDesignMode: function() {
1953             var _dMode = this._getDoc().designMode,
1954                 _state = 'on';
1955             if (_dMode == 'on') {
1956                 _state = 'off';
1957             }
1958             this._setDesignMode(_state);
1959             return _state;
1960         },
1961         /**
1962         * @private
1963         * @method _initEditor
1964         * @description This method is fired from _checkLoaded when the document is ready. It turns on designMode and set's up the listeners.
1965         */
1966         _initEditor: function() {
1967             if (this.browser.ie) {
1968                 this._getDoc().body.style.margin = '0';
1969             }
1970             this._setDesignMode('on');
1971             
1972             this.toolbar.on('buttonClick', this._handleToolbarClick, this, true);
1973             //Setup Listeners on iFrame
1974             Event.addListener(this._getDoc(), 'mouseup', this._handleMouseUp, this, true);
1975             Event.addListener(this._getDoc(), 'mousedown', this._handleMouseDown, this, true);
1976             Event.addListener(this._getDoc(), 'click', this._handleClick, this, true);
1977             Event.addListener(this._getDoc(), 'dblclick', this._handleDoubleClick, this, true);
1978             Event.addListener(this._getDoc(), 'keypress', this._handleKeyPress, this, true);
1979             Event.addListener(this._getDoc(), 'keyup', this._handleKeyUp, this, true);
1980             Event.addListener(this._getDoc(), 'keydown', this._handleKeyDown, this, true);
1981             this.toolbar.set('disabled', false);
1982             this.fireEvent('editorContentLoaded', { type: 'editorLoaded', target: this });
1983             if (this.get('dompath')) {
1984                 var self = this;
1985                 window.setTimeout(function() {
1986                     self._writeDomPath.call(self);
1987                 }, 150);
1988             }
1989         },
1990         /**
1991         * @private
1992         * @method _checkLoaded
1993         * @description Called from a setTimeout loop to check if the iframes body.onload event has fired, then it will init the editor.
1994         */
1995         _checkLoaded: function() {
1996             this._contentTimerCounter++;
1997             if (this._contentTimer) {
1998                 window.clearTimeout(this._contentTimer);
1999             }
2000             if (this._contentTimerCounter > 250) {
2001                 alert('ERROR: Body Did Not load');
2002                 return false;
2003             }
2004             if (this._getDoc() && this._getDoc().body && (this._getDoc().body._rteLoaded == true)) {
2005                 //The onload event has fired, clean up after ourselves and fire the _initEditor method
2006                 /*
2007                 if (!this.browser.ie) {
2008                     //IE Doesn't like this..
2009                     delete this._getDoc().body._rteLoaded;
2010                     this._getDoc().body.removeAttribute('onload');
2011                 }
2012                 */
2013                 this._initEditor();
2014             } else {
2015                 var self = this;
2016                 this._contentTimer = window.setTimeout(function() {
2017                     self._checkLoaded.call(self);
2018                 }, 20);
2019             }
2020         },
2021         /**
2022         * @private
2023         * @method _setInitialContent
2024         * @description This method will open the iframes content document and write the textareas value into it, then start the body.onload checking.
2025         */
2026         _setInitialContent: function() {
2027             var title = this.STR_TITLE;
2028             var html = this.get('html');
2029             html = html.replace('{TITLE}', title);
2030             html = html.replace('{CONTENT}', this.get('textarea').value);
2031             html = html.replace('{CSS}', this.get('css'));
2032             html = html.replace('{HIDDEN_CSS}', this.get('hiddencss'));
2034             this._getDoc().open();
2035             this._getDoc().write(html);
2036             this._getDoc().close();
2037             
2038             this._checkLoaded();   
2039         },
2040         /**
2041         * @private
2042         * @method _setMarkupType
2043         * @param {String} action The action to take. Possible values are: css, default or semantic
2044         * @description This method will turn on/off the useCSS execCommand.
2045         */
2046         _setMarkupType: function(action) {
2047             switch (this.get('markup')) {
2048                 case 'css':
2049                     this._setEditorStyle(true);
2050                     break;
2051                 case 'default':
2052                     this._setEditorStyle(false);
2053                     break;
2054                 case 'semantic':
2055                     if (this._semantic[action]) {
2056                         this._setEditorStyle(false);
2057                     } else {
2058                         this._setEditorStyle(true);
2059                     }
2060                     break;
2061             }
2062         },
2063         /**
2064         * Set the editor to use CSS instead of HTML
2065         * @param {Booleen} stat True/False
2066         */
2067         _setEditorStyle: function(stat) {
2068             try {
2069                 this._getDoc().execCommand('useCSS', false, !stat);
2070             } catch (ex) {
2071             }
2072         },
2073         /**
2074         * @private
2075         * @method _getSelectedElement
2076         * @description This method will attempt to locate the element that was last interacted with, either via selection, location or event.
2077         * @returns {HTMLElement} The currently selected element.
2078         */
2079         _getSelectedElement: function() {
2080             var doc = this._getDoc();
2081             if (this.browser.ie) {
2082                 var range = this._getRange(), elm = null;
2083                 if (range) {
2084                     elm = range.item ? range.item(0) : range.parentElement();
2085                     if (elm == doc.body) {
2086                         elm = null;
2087                     }
2088                 }
2089             } else {
2090                 var sel = this._getSelection(),
2091                     range = this._getRange(),
2092                     elm = null;
2093                 if (!sel || !range) {
2094                     return null;
2095                 }
2096                 if (sel != '') {
2097                     if (sel.anchorNode && (sel.anchorNode.nodeType == 3)) {
2098                         if (sel.anchorNode.parentNode) { //next check parentNode
2099                             elm = sel.anchorNode.parentNode;
2100                         }
2101                         if (sel.anchorNode.nextSibling != sel.focusNode.nextSibling) {
2102                             elm = sel.anchorNode.nextSibling;
2103                         }
2104                     }
2105                     
2106                     if (elm && elm.tagName && (elm.tagName.toLowerCase() == 'br')) {
2107                         elm = null;
2108                     }
2109                 
2110                     if (!elm) {
2111                         elm = range.commonAncestorContainer;
2112                         if (!range.collapsed) {
2113                             if (range.startContainer == range.endContainer) {
2114                                 if (range.startOffset - range.endOffset < 2) {
2115                                     if (range.startContainer.hasChildNodes()) {
2116                                         elm = range.startContainer.childNodes[range.startOffset];
2117                                     }
2118                                 }
2119                             }
2120                         }
2121                     }
2122                     //Safari Fix
2123                     if (!elm) {
2124                         if (this.currentEvent) {
2125                             //elm = Event.getTarget(this.currentEvent);
2126                         }
2127                     }
2128                 }
2129             }
2130             if (!elm && (this.currentElement[0] || this.currentEvent)) {
2131                 if (this.currentEvent && (this.currentEvent.keyCode == undefined) && Event.getTarget(this.currentEvent)) {
2132                     elm = Event.getTarget(this.currentEvent);
2133                 } else if (this.currentEvent && (this.currentEvent.keyCode != undefined) && Event.getTarget(this.currentEvent)) {
2134                 } else {
2135                     elm = this.currentElement[0];
2136                 }
2137             } else if ((elm == this._getDoc().body) && this.currentElement[0] && !this._hasSelection()) {
2138                 elm = this.currentElement[0];
2139             }
2141             if (this.browser.opera || this.browser.webkit) {
2142                 if (this.currentEvent && !elm) {
2143                     elm = Event.getTarget(this.currentEvent);
2144                 }
2145             }
2147             if (!elm || !elm.tagName) {
2148                 elm = doc.body;
2149             }
2150             if (elm && elm.tagName && elm.tagName.toLowerCase() == 'html') {
2151                 //Safari sometimes gives us the HTML node back..
2152                 elm = doc.body;
2153             }
2154             
2155             return elm;
2156         },
2157         /**
2158         * @private
2159         * @method _getDomPath
2160         * @description This method will attempt to build the DOM path from the currently selected element.
2161         * @returns {Array} An array of node references that will create the DOM Path.
2162         */
2163         _getDomPath: function() {
2164                         var el = this._getSelectedElement();
2165                         var domPath = [];
2166                         
2167                         while (el!= null) {
2168                 if (el.ownerDocument != this._getDoc()) {
2169                     return false;
2170                 }
2171                 //Check to see if we get el.nodeName and nodeType
2172                 if (el.nodeName && (el.nodeType == 1)) {
2173                     domPath[domPath.length] = el;
2174                 }
2176                 if (el.nodeName.toUpperCase() == "BODY") {
2177                                     break;
2178                 }
2180                                 el = el.parentNode;
2181                         }
2182             if (domPath.length == 0) {
2183                 if (this._getDoc() && this._getDoc().body) {
2184                     domPath[0] = this._getDoc().body;
2185                 }
2186             }
2187             return domPath.reverse();
2188         },
2189         /**
2190         * @private
2191         * @method _writeDomPath
2192         * @description Write the current DOM path out to the dompath container below the editor.
2193         */
2194         _writeDomPath: function() { 
2195             var path = this._getDomPath(),
2196                 pathArr = [];
2197             for (var i = 0; i < path.length; i++) {
2198                 var tag = path[i].tagName.toLowerCase();
2199                 if ((tag == 'ol') && (path[i].type)) {
2200                     tag += ':' + path[i].type;
2201                 }
2202                 if (Dom.hasClass(path[i], 'yui-tag')) {
2203                     tag = path[i].getAttribute('tag');
2204                 }
2205                 if ((this.get('markup') == 'semantic')) {
2206                     switch (tag) {
2207                         case 'b': tag = 'strong'; break;
2208                         case 'i': tag = 'em'; break;
2209                     }
2210                 }
2211                 if (!Dom.hasClass(path[i], 'yui-non')) {
2212                     if (Dom.hasClass(path[i], 'yui-tag')) {
2213                         var pathStr = tag;
2214                         if (tag == 'a') {
2215                             if (path[i].getAttribute('href')) {
2216                                 pathStr += ':' + path[i].getAttribute('href').replace('mailto:', '').replace('http:/'+'/', '').replace('https:/'+'/', ''); //May need to add others here ftp
2217                             }
2218                         }
2219                     } else {
2220                         var classPath = ((path[i].className != '') ? '.' + path[i].className.replace(/ /g, '.') : '');
2221                         if ((classPath.indexOf('yui') != -1) || (classPath.toLowerCase().indexOf('apple-style-span') != -1)) {
2222                             classPath = '';
2223                         }
2224                         var pathStr = tag + ((path[i].id) ? '#' + path[i].id : '') + classPath;
2225                     }
2226                     if (pathStr.length > 10) {
2227                         pathStr = pathStr.substring(0, 10) + '...';
2228                     }
2229                     pathArr[pathArr.length] = pathStr;
2230                 }
2231             }
2232             var str = pathArr.join(' ' + this.SEP_DOMPATH + ' ');
2233             //Prevent flickering
2234             if (this.dompath.innerHTML != str) {
2235                 this.dompath.innerHTML = str;
2236             }
2237         },
2238         /**
2239         * @private
2240         * @method _fixNodes
2241         * @description Fix href and imgs as well as remove invalid HTML.
2242         */
2243         _fixNodes: function() {
2244             for (var i in this.invalidHTML) {
2245                 if (Lang.hasOwnProperty(this.invalidHTML, i)) {
2246                     var tags = this._getDoc().body.getElementsByTagName(i);
2247                     for (var h = 0; h < tags.length; h++) {
2248                         if (tags[h].parentNode) {
2249                             tags[h].parentNode.removeChild(tags[h]);
2250                         }
2251                     }
2252                 }
2253             }
2254             var as = this._getDoc().body.getElementsByTagName('a');
2255             if (as.length) {
2256                 for (var i = 0; i < as.length; i++) {
2257                     var el = this._getDoc().createElement('span');
2258                     Dom.addClass(el, 'yui-tag-a');
2259                     Dom.addClass(el, 'yui-tag');
2260                     el.innerHTML = as[i].innerHTML;
2261                     el.setAttribute('tag', 'a');
2262                     el.setAttribute('href', as[i].getAttribute('href'));
2263                     if (as[i].getAttribute('target') != null) {
2264                         el.setAttribute('target', as[i].getAttribute('target'));
2265                     }
2266                     as[i].parentNode.replaceChild(el, as[i]);
2267                     as[i] = null;
2268                 }
2269             }
2270             var imgs = this._getDoc().getElementsByTagName('img');
2271             Dom.addClass(imgs, 'yui-img');
2273             for (var i = 0; i < imgs.length; i++) {
2274                 if (imgs[i].getAttribute('href', 2)) {
2275                     var url = imgs[i].getAttribute('src', 2);
2276                     if ((url != '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
2277                         Dom.addClass(imgs[i], this.CLASS_LOCAL_FILE);
2278                     } else {
2279                         Dom.removeClass(imgs[i], this.CLASS_LOCAL_FILE);
2280                     }
2281                 }
2282             }
2284             var fakeAs = this._getDoc().body.getElementsByTagName('span');
2285             for (var i = 0; i < fakeAs.length; i++) {
2286                 if (fakeAs[i].getAttribute('href', 2)) {
2287                     var url = fakeAs[i].getAttribute('href', 2);
2288                     if ((url != '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
2289                         Dom.addClass(fakeAs[i], this.CLASS_LOCAL_FILE);
2290                     } else {
2291                         Dom.removeClass(fakeAs[i], this.CLASS_LOCAL_FILE);
2292                     }
2293                 }
2294             }
2295         },
2296         /**
2297         * @private
2298         * @method _showHidden
2299         * @description Toggle on/off the hidden.css file.
2300         */
2301         _showHidden: function() {
2302             if (this._showingHiddenElements) {
2303                 this._showingHiddenElements = false;
2304                 this.toolbar.deselectButton(this.toolbar.getButtonByValue('hiddenelements'));
2305                 Dom.removeClass(this._getDoc().body, this.CLASS_HIDDEN);
2306             } else {
2307                 this._showingHiddenElements = true;
2308                 Dom.addClass(this._getDoc().body, this.CLASS_HIDDEN);
2309                 this.toolbar.selectButton(this.toolbar.getButtonByValue('hiddenelements'));
2310             }
2311         },
2312         /**
2313         * @private
2314         * @method _setCurrentEvent
2315         * @param {Event} ev The event to cache
2316         * @description Sets the current event property
2317         */
2318         _setCurrentEvent: function(ev) {
2319             if (ev && ev.type) {
2320             }
2321             this.currentEvent = ev;
2322         },
2323         /**
2324         * @private
2325         * @method _handleClick
2326         * @param {Event} ev The event we are working on.
2327         * @description Handles all click events inside the iFrame document.
2328         */
2329         _handleClick: function(ev) {
2330             this._setCurrentEvent(ev);
2331             if (this.currentWindow) {
2332                 this.closeWindow();
2333             }
2334             if (!this.browser.webkit) {
2335                 this.nodeChange();
2336             }
2337         },
2338         /**
2339         * @private
2340         * @method _handleMouseUp
2341         * @param {Event} ev The event we are working on.
2342         * @description Handles all mouseup events inside the iFrame document.
2343         */
2344         _handleMouseUp: function(ev) {
2345             this._setCurrentEvent(ev);
2346             if (this.browser.opera) {
2347                 /**
2348                 * @knownissue Opera appears to stop the MouseDown, Click and DoubleClick events on an image inside of a document with designMode on..
2349                 * @browser Opera
2350                 * @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.
2351                 */
2352                 var sel = Event.getTarget(ev);
2353                 if (sel && sel.tagName && (sel.tagName.toLowerCase() == 'img')) {
2354                     this.nodeChange();
2355                     var self = this;
2356                     if (this.operaEvent) {
2357                         clearTimeout(this.operaEvent);
2358                         this.operaEvent = null;
2359                         this._handleDoubleClick(ev);
2360                     } else {
2361                         this.operaEvent = window.setTimeout(function() {
2362                             self.operaEvent = false;
2363                         }, 200);
2364                     }
2365                 }
2366             }
2367             //This will stop Safari from selecting the entire document if you select all the text in the editor
2368             if (this.browser.webkit || this.browser.opera) {
2369                 if (this.browser.webkit) {
2370                     Event.stopEvent(ev);
2371                 }
2372             }            
2373             this.nodeChange();
2374             this.fireEvent('editorMouseUp', { type: 'editorMouseUp', target: this, ev: ev });
2375         },
2376         /**
2377         * @private
2378         * @method _handleMouseDown
2379         * @param {Event} ev The event we are working on.
2380         * @description Handles all mousedown events inside the iFrame document.
2381         */
2382         _handleMouseDown: function(ev) {
2383             this._setCurrentEvent(ev);
2384             var sel = Event.getTarget(ev);
2385             if (sel && sel.tagName && (sel.tagName.toLowerCase() == 'img')) {
2386                 if (this.browser.webkit) {
2387                     this.nodeChange();
2388                     Event.stopEvent(ev);
2389                 }
2390             }
2391             //this.nodeChange();
2392             this.fireEvent('editorMouseDown', { type: 'editorMouseDown', target: this, ev: ev });
2393         },
2394         /**
2395         * @private
2396         * @method _handleDoubleClick
2397         * @param {Event} ev The event we are working on.
2398         * @description Handles all doubleclick events inside the iFrame document.
2399         */
2400         _handleDoubleClick: function(ev) {
2401             this._setCurrentEvent(ev);
2402             var sel = Event.getTarget(ev);
2403             if (sel && sel.tagName && (sel.tagName.toLowerCase() == 'img')) {
2404                 this.currentElement[0] = sel;
2405                 this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
2406                 this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
2407             } else if (sel && sel.getAttribute && sel.getAttribute('tag') && (sel.getAttribute('tag').toLowerCase() == 'a')) {
2408                 this.currentElement[0] = sel;
2409                 this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
2410                 this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
2411             }
2412             this.nodeChange();
2413             this.fireEvent('editorDoubleClick', { type: 'editorDoubleClick', target: this, ev: ev });
2414         },
2415         /**
2416         * @private
2417         * @method _handleKeyUp
2418         * @param {Event} ev The event we are working on.
2419         * @description Handles all keyup events inside the iFrame document.
2420         */
2421         _handleKeyUp: function(ev) {
2422             this._setCurrentEvent(ev);
2423             switch (ev.keyCode) {
2424                 case 37: //Left Arrow
2425                 case 38: //Up Arrow
2426                 case 39: //Right Arrow
2427                 case 40: //Down Arrow
2428                 case 46: //Forward Delete
2429                 case 8: //Delete
2430                 case 65: //The letter a (for ctrl + a and cmd + a)
2431                 case 27: //Escape key if window is open
2432                     if ((ev.keyCode == 27) && this.currentWindow) {
2433                         this.closeWindow();
2434                     }
2435                     this.nodeChange();
2436                     break;
2437             }
2438             this.fireEvent('editorKeyUp', { type: 'editorKeyUp', target: this, ev: ev });
2439         },
2440         /**
2441         * @private
2442         * @method _handleKeyPress
2443         * @param {Event} ev The event we are working on.
2444         * @description Handles all keypress events inside the iFrame document.
2445         */
2446         _handleKeyPress: function(ev) {
2447             this._setCurrentEvent(ev);
2448             this.fireEvent('editorKeyPress', { type: 'editorKeyPress', target: this, ev: ev });
2449         },
2450         /**
2451         * @private
2452         * @method _handleKeyDown
2453         * @param {Event} ev The event we are working on.
2454         * @description Handles all keydown events inside the iFrame document.
2455         */
2456         _handleKeyDown: function(ev) {
2457             this._setCurrentEvent(ev);
2458             if (this.currentWindow) {
2459                 this.closeWindow();
2460             }
2461             var doExec = false;
2462             var action = null;
2463             //if (ev.ctrlKey) {
2464             if (ev.shiftKey && ev.ctrlKey) {
2465                 doExec = true;
2466             }
2467             switch (ev.keyCode) {
2468                 case 84: //Focus Toolbar Header -- Ctrl + Shift + T
2469                     if (ev.shiftKey && ev.ctrlKey) {
2470                         this.toolbar._titlebar.firstChild.focus();
2471                         Event.stopEvent(ev);
2472                         doExec = false;
2473                     }
2474                     break;
2475                 case 27: //Focus After Element - Ctrl + Shift + Esc
2476                     if (ev.shiftKey) {
2477                         this.afterElement.focus();
2478                         Event.stopEvent(ev);
2479                         exec = false;
2480                     }
2481                     break;
2482                 case 219: //Left
2483                     action = 'justifyleft';
2484                     break;
2485                 case 220: //Center
2486                     action = 'justifycenter';
2487                     break;
2488                 case 221: //Right
2489                     action = 'justifyright';
2490                     break;
2491                 case 76: //L
2492                     if (this._hasSelection()) {
2493                         this.execCommand('createlink', '');
2494                         this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
2495                         this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
2496                         doExec = false;
2497                     }
2498                     break;
2499                 case 66: //B
2500                     action = 'bold';
2501                     break;
2502                 case 73: //I
2503                     action = 'italic';
2504                     break;
2505                 case 85: //U
2506                     action = 'underline';
2507                     break;
2508                 case 9: //Tab Key
2509                     if (this.browser.safari) {
2510                         this._getDoc().execCommand('inserttext', false, '\t');
2511                         Event.stopEvent(ev);
2512                     }
2513                     break;
2514                 case 13:
2515                     if (this.browser.ie) {
2516                         //Insert a <br> instead of a <p></p> in Internet Explorer
2517                         var _range = this._getRange();
2518                         var tar = this._getSelectedElement();
2519                         if (tar && tar.tagName && (tar.tagName.toLowerCase() != 'li')) {
2520                             if (_range) {
2521                                 _range.pasteHTML('<br>');
2522                                 _range.collapse(false);
2523                                 _range.select();
2524                             }
2525                             Event.stopEvent(ev);
2526                         }
2527                     }
2528             }
2529             if (doExec && action) {
2530                 this.execCommand(action, null);
2531                 Event.stopEvent(ev);
2532                 this.nodeChange();
2533             }
2534             this.fireEvent('editorKeyDown', { type: 'editorKeyDown', target: this, ev: ev });
2535         },
2536         /**
2537         * @method nodeChange
2538         * @description Handles setting up the toolbar buttons, getting the Dom path, fixing nodes.
2539         */
2540         nodeChange: function() {
2541             this._fixNodes();
2543             this.fireEvent('beforeNodeChange', { type: 'beforeNodeChange', target: this });
2544             if (this.get('dompath')) {
2545                 this._writeDomPath();
2546             }
2547             //Check to see if we are disabled before continuing
2548             if (!this.get('disabled')) {
2549                 if (this.STOP_NODE_CHANGE) {
2550                     //Reset this var for next action
2551                     this.STOP_NODE_CHANGE = false;
2552                     return false;
2553                 } else {
2554                     var sel = this._getSelection();
2555                     var range = this._getRange();
2557                     //Handle disabled buttons
2558                     for (var i = 0; i < this._disabled.length; i++) {
2559                         var _button = this.toolbar.getButtonByValue(this._disabled[i]);
2560                         if (_button && _button.get) {
2561                             if (!this._hasSelection()) {
2562                                 //No Selection - disable
2563                                 this.toolbar.disableButton(_button.get('id'));
2564                             } else {
2565                                 if (!this._alwaysDisabled[this._disabled[i]]) {
2566                                     this.toolbar.enableButton(_button.get('id'));
2567                                 }
2568                             }
2569                             if (!this._alwaysEnabled[this._disabled[i]]) {
2570                                 this.toolbar.deselectButton(_button);
2571                             }
2572                         }
2573                     }
2574                     //Handle updating the toolbar with active buttons
2575                     for (var i = 0; i < this.toolbar._buttonList.length; i++) {
2576                         if (!this._alwaysEnabled[this.toolbar._buttonList[i].get('value')]) {
2577                             this.toolbar.deselectButton(this.toolbar._buttonList[i]);
2578                         }
2579                     }
2580                     var path = this._getDomPath();
2581                     var olType = null;
2582                     for (var i = 0; i < path.length; i++) {
2583                         var tag = path[i].tagName.toLowerCase();
2584                         if (path[i].getAttribute('tag')) {
2585                             var tag = path[i].getAttribute('tag').toLowerCase();
2586                         }
2587                         var cmd = this._tag2cmd[tag];
2589                         //Bold and Italic styles
2590                         if (path[i].style.fontWeight.toLowerCase() == 'bold') {
2591                             cmd = 'bold';
2592                         }
2593                         if (path[i].style.fontStyle.toLowerCase() == 'italic') {
2594                             cmd = 'italic';
2595                         }
2596                         if (path[i].style.textDecoration.toLowerCase() == 'underline') {
2597                             cmd = 'underline';
2598                         }
2599                         if (tag == 'ol') {
2600                             if (path[i].type) {
2601                                 olType = path[i].type;
2602                             } else {
2603                                 olType = 'A';
2604                             }
2605                         }
2606                         if (cmd) {
2607                             if (!Lang.isArray(cmd)) {
2608                                 cmd = [cmd];
2609                             }
2610                             for (var j = 0; j < cmd.length; j++) {
2611                                 var button = this.toolbar.getButtonByValue(cmd[j]);
2612                                 this.toolbar.selectButton(button);
2613                                 this.toolbar.enableButton(button);
2614                             }
2615                         }
2616                         //Handle Alignment
2617                         switch (path[i].style.textAlign.toLowerCase()) {
2618                             case 'left':
2619                             case 'right':
2620                             case 'center':
2621                             case 'justify':
2622                                 var alignType = path[i].style.textAlign.toLowerCase();
2623                                 if (path[i].style.textAlign.toLowerCase() == 'justify') {
2624                                     alignType = 'full';
2625                                 }
2626                                 var button = this.toolbar.getButtonByValue('justify' + alignType);
2627                                 this.toolbar.selectButton(button);
2628                                 this.toolbar.enableButton(button);
2629                                 break;
2630                         }
2631                         //Handle Ordered List Drop Down - it will reset if olType is null
2632                         //this._updateMenuChecked('insertorderedlist', olType);
2633                     }
2634                     //After for loop
2636                     //Reset Font Family and Size to the inital configs
2637                     var fn_button = this.toolbar.getButtonByValue('fontname');
2638                     if (fn_button) {
2639                         var family = fn_button._configs.label._initialConfig.value;
2640                         fn_button.set('label', '<span class="yui-toolbar-fontname-' + _cleanClassName(family) + '">' + family + '</span>');
2641                         this._updateMenuChecked('fontname', family);
2642                     }
2644                     var fs_button = this.toolbar.getButtonByValue('fontsize');
2645                     if (fs_button) {
2646                         fs_button.set('label', fs_button._configs.label._initialConfig.value);
2647                     }
2649                     var hd_button = this.toolbar.getButtonByValue('heading');
2650                     if (hd_button) {
2651                         hd_button.set('label', hd_button._configs.label._initialConfig.value);
2652                         this._updateMenuChecked('heading', 'none');
2653                     }
2654                 }
2655             }
2657             this.fireEvent('afterNodeChange', { type: 'afterNodeChange', target: this });
2658         },
2659         /**
2660         * @private
2661         * @method _updateMenuChecked
2662         * @param {Object} button The command identifier of the button you want to check
2663         * @param {String} value The value of the menu item you want to check
2664         * @param {<a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>} The Toolbar instance the button belongs to (defaults to this.toolbar) 
2665         * @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.
2666         */
2667         _updateMenuChecked: function(button, value, tbar) {
2668             if (!tbar) {
2669                 tbar = this.toolbar;
2670             }
2671             var _button = tbar.getButtonByValue(button);
2672             var _menuItems = _button.getMenu().getItems();
2673             if (_menuItems.length == 0) {
2674                 _button.getMenu()._onBeforeShow();
2675                 _menuItems = _button.getMenu().getItems();
2676             }
2677             for (var i = 0; i < _menuItems.length; i++) {
2678                 _menuItems[i].cfg.setProperty('checked', false);
2679                 if (_menuItems[i].value == value) {
2680                     _menuItems[i].cfg.setProperty('checked', true);
2681                 }
2682             }
2683         },
2684         /**
2685         * @private
2686         * @method _handleToolbarClick
2687         * @param {Event} ev The event that triggered the button click
2688         * @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.
2689         */
2690         _handleToolbarClick: function(ev) {
2691             var value = '';
2692             var str = '';
2693             var cmd = ev.button.value;
2694             if (ev.button.menucmd) {
2695                 value = cmd;
2696                 cmd = ev.button.menucmd;
2697             }
2698             if (this.STOP_EXEC_COMMAND) {
2699                 this.STOP_EXEC_COMMAND = false;
2700                 return false;
2701             } else {
2702                 this.execCommand(cmd, value);
2703             }
2704             Event.stopEvent(ev);
2705         },
2706         /**
2707         * @private
2708         * @method _setupAfterElement
2709         * @description Creates the accessibility h2 header and places it after the iframe in the Dom for navigation.
2710         */
2711         _setupAfterElement: function() {
2712             if (!this.afterElement) {
2713                 this.afterElement = document.createElement('h2');
2714                 this.afterElement.className = 'yui-editor-skipheader';
2715                 this.afterElement.tabIndex = '-1';
2716                 this.afterElement.innerHTML = this.STR_LEAVE_EDITOR;
2717                 this.get('element_cont').get('firstChild').appendChild(this.afterElement);
2718             }
2719         },
2720         /**
2721         * @property EDITOR_PANEL_ID
2722         * @description HTML id to give the properties window in the DOM.
2723         * @type String
2724         */
2725         EDITOR_PANEL_ID: 'yui-editor-panel',
2726         /**
2727         * @property SEP_DOMPATH
2728         * @description The value to place in between the Dom path items
2729         * @type String
2730         */
2731         SEP_DOMPATH: '<',
2732         /**
2733         * @property STR_LEAVE_EDITOR
2734         * @description The accessibility string for the element after the iFrame
2735         * @type String
2736         */
2737         STR_LEAVE_EDITOR: 'You have left the Rich Text Editor.',
2738         /**
2739         * @property STR_BEFORE_EDITOR
2740         * @description The accessibility string for the element before the iFrame
2741         * @type String
2742         */
2743         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>',
2744         /**
2745         * @property STR_CLOSE_WINDOW
2746         * @description The Title of the close button in the Editor Window
2747         * @type String
2748         */
2749         STR_CLOSE_WINDOW: 'Close Window',
2750         /**
2751         * @property STR_CLOSE_WINDOW_NOTE
2752         * @description A note appearing in the Editor Window to tell the user that the Escape key will close the window
2753         * @type String
2754         */
2755         STR_CLOSE_WINDOW_NOTE: 'To close this window use the Escape key',
2756         /**
2757         * @property STR_TITLE
2758         * @description The Title of the HTML document that is created in the iFrame
2759         * @type String
2760         */
2761         STR_TITLE: 'Rich Text Area.',
2762         /**
2763         * @property STR_IMAGE_HERE
2764         * @description The text to place in the URL textbox when using the blankimage.
2765         * @type String
2766         */
2767         STR_IMAGE_HERE: 'Image Url Here',
2768         /**
2769         * @property STR_IMAGE_PROP_TITLE
2770         * @description The title for the Image Property Editor Window
2771         * @type String
2772         */
2773         STR_IMAGE_PROP_TITLE: 'Image Options',
2774         /**
2775         * @property STR_IMAGE_URL
2776         * @description The label string for Image URL
2777         * @type String
2778         */
2779         STR_IMAGE_URL: 'Image Url',
2780         /**
2781         * @property STR_IMAGE_TITLE
2782         * @description The label string for Image Description
2783         * @type String
2784         */
2785         STR_IMAGE_TITLE: 'Description',
2786         /**
2787         * @property STR_IMAGE_SIZE
2788         * @description The label string for Image Size
2789         * @type String
2790         */
2791         STR_IMAGE_SIZE: 'Size',
2792         /**
2793         * @property STR_IMAGE_ORIG_SIZE
2794         * @description The label string for Original Image Size
2795         * @type String
2796         */
2797         STR_IMAGE_ORIG_SIZE: 'Original Size',
2798         /**
2799         * @property STR_IMAGE_COPY
2800         * @description The label string for the image copy and paste message for Opera and Safari
2801         * @type String
2802         */
2803         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>',
2804         /**
2805         * @property STR_IMAGE_PADDING
2806         * @description The label string for the image padding.
2807         * @type String
2808         */
2809         STR_IMAGE_PADDING: 'Padding',
2810         /**
2811         * @property STR_IMAGE_BORDER
2812         * @description The label string for the image border.
2813         * @type String
2814         */
2815         STR_IMAGE_BORDER: 'Border',
2816         /**
2817         * @property STR_IMAGE_TEXTFLOW
2818         * @description The label string for the image text flow.
2819         * @type String
2820         */
2821         STR_IMAGE_TEXTFLOW: 'Text Flow',
2822         /**
2823         * @property STR_LOCAL_FILE_WARNING
2824         * @description The label string for the local file warning.
2825         * @type String
2826         */
2827         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>',
2828         /**
2829         * @property STR_LINK_PROP_TITLE
2830         * @description The label string for the Link Property Editor Window.
2831         * @type String
2832         */
2833         STR_LINK_PROP_TITLE: 'Link Options',
2834         /**
2835         * @property STR_LINK_PROP_REMOVE
2836         * @description The label string for the Remove link from text link inside the property editor.
2837         * @type String
2838         */
2839         STR_LINK_PROP_REMOVE: 'Remove link from text',
2840         /**
2841         * @property STR_LINK_URL
2842         * @description The label string for the Link URL.
2843         * @type String
2844         */
2845         STR_LINK_URL: 'Link URL',
2846         /**
2847         * @property STR_LINK_NEW_WINDOW
2848         * @description The string for the open in a new window label.
2849         * @type String
2850         */
2851         STR_LINK_NEW_WINDOW: 'Open in a new window.',
2852         /**
2853         * @property STR_LINK_TITLE
2854         * @description The string for the link description.
2855         * @type String
2856         */
2857         STR_LINK_TITLE: 'Description',
2858         /**
2859         * @protected
2860         * @property STOP_EXEC_COMMAND
2861         * @description Set to true when you want the default execCommand function to not process anything
2862         * @type Boolean
2863         */
2864         STOP_EXEC_COMMAND: false,
2865         /**
2866         * @protected
2867         * @property STOP_NODE_CHANGE
2868         * @description Set to true when you want the default nodeChange function to not process anything
2869         * @type Boolean
2870         */
2871         STOP_NODE_CHANGE: false,
2872         /**
2873         * @protected
2874         * @property CLASS_HIDDEN
2875         * @description CSS class applied to the body when the hiddenelements button is pressed.
2876         * @type String
2877         */
2878         CLASS_HIDDEN: 'hidden',
2879         /**
2880         * @protected
2881         * @property CLASS_LOCAL_FILE
2882         * @description CSS class applied to an element when it's found to have a local url.
2883         * @type String
2884         */
2885         CLASS_LOCAL_FILE: 'warning-localfile',
2886         /**
2887         * @protected
2888         * @property CLASS_CONTAINER
2889         * @description Default CSS class to apply to the editors container element
2890         * @type String
2891         */
2892         CLASS_CONTAINER: 'yui-editor-container',
2893         /**
2894         * @protected
2895         * @property CLASS_EDITABLE
2896         * @description Default CSS class to apply to the editors iframe element
2897         * @type String
2898         */
2899         CLASS_EDITABLE: 'yui-editor-editable',
2900         /**
2901         * @protected
2902         * @property CLASS_EDITABLE_CONT
2903         * @description Default CSS class to apply to the editors iframe's parent element
2904         * @type String
2905         */
2906         CLASS_EDITABLE_CONT: 'yui-editor-editable-container',
2907         /**
2908         * @protected
2909         * @property CLASS_PREFIX
2910         * @description Default prefix for dynamically created class names
2911         * @type String
2912         */
2913         CLASS_PREFIX: 'yui-editor',
2914         /** 
2915         * @property browser
2916         * @description Standard browser detection
2917         * @type Object
2918         */
2919         browser: YAHOO.env.ua,
2920         /** 
2921         * @method init
2922         * @description The Editor class' initialization method
2923         */
2924         init: function(p_oElement, p_oAttributes) {
2925             YAHOO.widget.Editor.superclass.init.call(this, p_oElement, p_oAttributes);
2926             this.get('element_cont').addClass(this.CLASS_CONTAINER);
2927             Dom.addClass(this.get('iframe').get('parentNode'), this.CLASS_EDITABLE_CONT);
2928             this.get('iframe').addClass(this.CLASS_EDITABLE);
2929         },
2930         /**
2931         * @method initAttributes
2932         * @description Initializes all of the configuration attributes used to create 
2933         * the editor.
2934         * @param {Object} attr Object literal specifying a set of 
2935         * configuration attributes used to create the editor.
2936         */
2937         initAttributes: function(attr) {
2938             YAHOO.widget.Editor.superclass.initAttributes.call(this, attr);
2939             var self = this;
2941             /**
2942             * @private
2943             * @config textarea
2944             * @description A reference to the textarea element that we are replacing
2945             * @default null
2946             * @type Boolean
2947             */            
2948             this.setAttributeConfig('textarea', {
2949                 value: attr.textarea,
2950                 writeOnce: true
2951             });
2952             /**
2953             * @config height
2954             * @description The height of the editor iframe container, not including the toolbar..
2955             * @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
2956             * @type String
2957             */
2958             this.setAttributeConfig('height', {
2959                 value: attr.height || Dom.getStyle(self.get('textarea'), 'height'),
2960                 writeOnce: true
2961             });
2962             /**
2963             * @config width
2964             * @description The width of the editor container.
2965             * @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
2966             * @type String
2967             */            
2968             this.setAttributeConfig('width', {
2969                 value: attr.width || Dom.getStyle(this.get('textarea'), 'width'),
2970                 writeOnce: true
2971             });
2972                         
2973             /**
2974             * @config blankimage
2975             * @description The CSS used to show/hide hidden elements on the page
2976             * @default 'assets/blankimage.png'
2977             * @type String
2978             */            
2979             this.setAttributeConfig('blankimage', {
2980                 value: attr.blankimage || this._getBlankImage()
2981             });
2982             /**
2983             * @config hiddencss
2984             * @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>
2985             * @default <code><pre>
2986             .hidden div, .hidden p, .hidden span, .hidden img {
2987                 border: 1px dotted #ccc;
2988             }
2989             .hidden .yui-non {
2990                 border: none;
2991             }
2992             .hidden img {
2993                 padding: 2px;
2994             }</pre></code>
2995             * @type String
2996             */            
2997             this.setAttributeConfig('hiddencss', {
2998                 value: attr.hiddencss || '.hidden div,.hidden p,.hidden span,.hidden img { border: 1px dotted #ccc; } .hidden .yui-non { border: none; } .hidden img { padding: 2px; }',
2999                 writeOnce: true
3000             });
3001             /**
3002             * @config css
3003             * @description The Base CSS used to format the content of the editor
3004             * @default <code><pre>body {
3005                 padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;
3006             }
3007             span.yui-tag-a {
3008                 color: blue; text-decoration: underline;
3009             }
3010             span.yui-tag-blockquote {
3011                 margin: 1em; display: block;
3012             }
3013             span.yui-tag-indent {
3014                 margin-left: 1em; display: block;
3015             }
3016             .warning-localfile {
3017                 border-bottom: 1px dashed red !important;
3018             }</pre></code>
3019             * @type String
3020             */            
3021             this.setAttributeConfig('css', {
3022                 value: attr.css || 'body { padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } span.yui-tag-a { color: blue; text-decoration: underline; } span.yui-tag-blockquote { margin: 1em; display: block; } span.yui-tag-indent { margin-left: 1em; display: block; } .warning-localfile { border-bottom: 1px dashed red !important; }',
3023                 writeOnce: true
3024             });
3025             /**
3026             * @config html
3027             * @description The default HTML to be written to the iframe document before the contents are loaded
3028             * @default This HTML requires a few things if you are to override:
3029                 <p><code>{TITLE}, {CSS}, {HIDDEN_CSS}</code> and <code>{CONTENT}</code> need to be there, they are passed to YAHOO.lang.substitute to be replace with other strings.<p>
3030                 <p><code>onload="document.body._rteLoaded = true;"</code> : the onload statement must be there or the editor will not finish loading.</p>
3031                 <code>
3032                 <pre>
3033                 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"&gt;
3034                 &lt;html&gt;
3035                     &lt;head&gt;
3036                         &lt;title&gt;{TITLE}&lt;/title&gt;
3037                         &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /&gt;
3038                         &lt;style&gt;
3039                         {CSS}
3040                         &lt;/style&gt;
3041                         &lt;style&gt;
3042                         {HIDDEN_CSS}
3043                         &lt;/style&gt;
3044                     &lt;/head&gt;
3045                 &lt;body onload="document.body._rteLoaded = true;"&gt;
3046                 {CONTENT}
3047                 &lt;/body&gt;
3048                 &lt;/html&gt;
3049                 </pre>
3050                 </code>
3051             * @type String
3052             */            
3053             this.setAttributeConfig('html', {
3054                 value: attr.html || '<!DOCTYPE HTML PUBLIC "-/'+'/W3C/'+'/DTD HTML 4.01/'+'/EN" "http:/'+'/www.w3.org/TR/html4/strict.dtd"><html><head><title>{TITLE}</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><style>{CSS}</style><style>{HIDDEN_CSS}</style></head><body onload="document.body._rteLoaded = true;">{CONTENT}</body></html>',
3055                 writeOnce: true
3056             });
3058             /**
3059             * @config handleSubmit
3060             * @description Config handles if the editor will attach itself to the textareas parent form's submit handler.
3061             If it is set to true, the editor will attempt to attach a submit listener to the textareas parent form.
3062             Then it will trigger the editors save handler and place the new content back into the text area before the form is submitted.
3063             * @default false
3064             * @type Boolean
3065             */            
3066             this.setAttributeConfig('handleSubmit', {
3067                 value: false,
3068                 writeOnce: true,
3069                 method: function(exec) {
3070                     if (exec) {
3071                         var ta = this.get('textarea');
3072                         if (ta.form) {
3073                             Event.addListener(ta.form, 'submit', function() {
3074                                 this.saveHTML();
3075                             }, this, true);
3076                         }
3077                     }
3078                 }
3079             });
3080             /**
3081             * @private
3082             * @config iframe
3083             * @description Internal config for holding the iframe element.
3084             * @default null
3085             * @type Boolean
3086             */
3087             this.setAttributeConfig('iframe', {
3088                 value: null,
3089                 writeOnce: true
3090             });
3091             /**
3092             * @config disabled
3093             * @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.
3094             All Toolbar buttons are also disabled so they cannot be used.
3095             * @default false
3096             * @type Boolean
3097             */
3099             this.setAttributeConfig('disabled', {
3100                 value: false,
3101                 method: function(disabled) {
3102                     if (disabled) {
3103                         if (!this._mask) {
3104                             this._setDesignMode('off');
3105                             this.toolbar.set('disabled', true);
3106                             this._mask = document.createElement('DIV');
3107                             Dom.setStyle(this._mask, 'height', '100%');
3108                             Dom.setStyle(this._mask, 'width', '100%');
3109                             Dom.setStyle(this._mask, 'position', 'absolute');
3110                             Dom.setStyle(this._mask, 'top', '0');
3111                             Dom.setStyle(this._mask, 'left', '0');
3112                             Dom.setStyle(this._mask, 'opacity', '.5');
3113                             Dom.addClass(this._mask, 'yui-editor-masked');
3114                             this.get('iframe').get('parentNode').appendChild(this._mask);
3115                         }
3116                     } else {
3117                         if (this._mask) {
3118                             this._mask.parentNode.removeChild(this._mask);
3119                             this._mask = null;
3120                             this.toolbar.set('disabled', false);
3121                             this._setDesignMode('on');
3122                             this._focusWindow();
3123                         }
3124                     }
3125                 }
3126             });
3127             /**
3128             * @config element_cont
3129             * @description Internal config for the editors container
3130             * @default false
3131             * @type Boolean
3132             */
3133             this.setAttributeConfig('element_cont', {
3134                 value: null,
3135                 writeOnce: true
3136             });
3137             /**
3138             * @config toolbar_cont
3139             * @description Internal config for the toolbars container
3140             * @default false
3141             * @type Boolean
3142             */
3143             this.setAttributeConfig('toolbar_cont', {
3144                 value: null,
3145                 writeOnce: true
3146             });
3147             /**
3148             * @config toolbar
3149             * @description The default toolbar config.
3150             * @default This config is too large to display here, view the code to see it: <a href="editor.js.html"></a>
3151             * @type Object
3152             */            
3153             this.setAttributeConfig('toolbar', {
3154                 value: attr.toolbar || {
3155                     /* {{{ Defaut Toolbar Config */
3156                     collapse: true,
3157                     titlebar: 'Text Editing Tools',
3158                     draggable: false,
3159                     buttons: [
3160                         { group: 'fontstyle', label: 'Font Name and Size',
3161                             buttons: [
3162                                 { type: 'select', label: 'Arial', value: 'fontname', disabled: true,
3163                                     menu: [
3164                                         { text: 'Arial', checked: true },
3165                                         { text: 'Arial Black' },
3166                                         { text: 'Comic Sans MS' },
3167                                         { text: 'Courier New' },
3168                                         { text: 'Lucida Console' },
3169                                         { text: 'Tahoma' },
3170                                         { text: 'Times New Roman' },
3171                                         { text: 'Trebuchet MS' },
3172                                         { text: 'Verdana' }
3173                                     ]
3174                                 },
3175                                 { type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
3176                             ]
3177                         },
3178                         { type: 'separator' },
3179                         { group: 'textstyle', label: 'Font Style',
3180                             buttons: [
3181                                 { type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
3182                                 { type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
3183                                 { type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
3184                                 { type: 'separator' },
3185                                 { type: 'push', label: 'Subscript', value: 'subscript', disabled: true },
3186                                 { type: 'push', label: 'Superscript', value: 'superscript', disabled: true },
3187                                 { type: 'separator' },
3188                                 { type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
3189                                 { type: 'color', label: 'Background Color', value: 'backcolor', disabled: true },
3190                                 { type: 'separator' },
3191                                 { type: 'push', label: 'Remove Formatting', value: 'removeformat', disabled: true },
3192                                 { type: 'push', label: 'Hidden Elements', value: 'hiddenelements' }
3193                             ]
3194                         },
3195                         { type: 'separator' },
3196                         { group: 'alignment', label: 'Alignment',
3197                             buttons: [
3198                                 { type: 'push', label: 'Align Left CTRL + SHIFT + [', value: 'justifyleft' },
3199                                 { type: 'push', label: 'Align Center CTRL + SHIFT + |', value: 'justifycenter' },
3200                                 { type: 'push', label: 'Align Right CTRL + SHIFT + ]', value: 'justifyright' },
3201                                 { type: 'push', label: 'Justify', value: 'justifyfull' }
3202                             ]
3203                         },
3204                         { type: 'separator' },
3205                         { group: 'parastyle', label: 'Paragraph Style',
3206                             buttons: [
3207                             { type: 'select', label: 'Normal', value: 'heading', disabled: true,
3208                                 menu: [
3209                                     { text: 'Normal', value: 'none', checked: true },
3210                                     { text: 'Header 1', value: 'h1' },
3211                                     { text: 'Header 2', value: 'h2' },
3212                                     { text: 'Header 3', value: 'h3' },
3213                                     { text: 'Header 4', value: 'h4' },
3214                                     { text: 'Header 5', value: 'h5' },
3215                                     { text: 'Header 6', value: 'h6' }
3216                                 ]
3217                             }
3218                             ]
3219                         },
3220                         { type: 'separator' },
3221                         { group: 'indentlist', label: 'Indenting and Lists',
3222                             buttons: [
3223                                 { type: 'push', label: 'Indent', value: 'indent', disabled: true },
3224                                 { type: 'push', label: 'Outdent', value: 'outdent', disabled: true },
3225                                 { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
3226                                 { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
3227                                 /*
3228                                 { type: 'menu', label: 'Create an Ordered List', value: 'insertorderedlist',
3229                                     menu: [
3230                                         { text: '1,2,3,4', value: '1', checked: true },
3231                                         { text: 'A,B,C,D', value: 'A' },
3232                                         { text: 'a,b,c,d', value: 'a' },
3233                                         { text: 'I,II,III,IV', value: 'I' },
3234                                         { text: 'i,ii,iii,iv', value: 'i' }
3235                                     ]
3236                                 }
3237                                 */
3238                             ]
3239                         },
3240                         { type: 'separator' },
3241                         { group: 'insertitem', label: 'Insert Item',
3242                             buttons: [
3243                                 { type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
3244                                 { type: 'push', label: 'Insert Image', value: 'insertimage' }
3245                             ]
3246                         }
3247                     ]
3248                     /* }}} */
3249                 },
3250                 writeOnce: true,
3251                 method: function(toolbar) {
3252                 }
3253             });
3254             /**
3255             * @config animate
3256             * @description Should the editor animate window movements
3257             * @default false unless Animation is found, then true
3258             * @type Boolean
3259             */            
3260             this.setAttributeConfig('animate', {
3261                 value: false,
3262                 validator: function(value) {
3263                     var ret = true;
3264                     if (!YAHOO.util.Anim) {
3265                         ret = false;
3266                     }
3267                     return ret;
3268                 }               
3269             });
3270             /**
3271             * @config panel
3272             * @description A reference to the panel we are using for windows.
3273             * @default false
3274             * @type Boolean
3275             */            
3276             this.setAttributeConfig('panel', {
3277                 value: null,
3278                 writeOnce: true,
3279                 validator: function(value) {
3280                     var ret = true;
3281                     if (!YAHOO.widget.Panel) {
3282                         ret = false;
3283                     }
3284                     return ret;
3285                 }               
3286             });
3287             /**
3288             * @config localFileWarning
3289             * @description Should we throw the warning if we detect a file that is local to their machine?
3290             * @default true
3291             * @type Boolean
3292             */            
3293             this.setAttributeConfig('localFileWarning', {
3294                 value: true
3295             });
3296             /**
3297             * @config dompath
3298             * @description Toggle the display of the current Dom path below the editor
3299             * @default false
3300             * @type Boolean
3301             */            
3302             this.setAttributeConfig('dompath', {
3303                 value: false,
3304                 method: function(dompath) {
3305                     if (dompath && !this.dompath) {
3306                         this.dompath = document.createElement('DIV');
3307                         this.dompath.id = this.get('id') + '_dompath';
3308                         Dom.addClass(this.dompath, 'dompath');
3309                         this.get('element_cont').get('firstChild').appendChild(this.dompath);
3310                         if (this.get('iframe')) {
3311                             this._writeDomPath();
3312                         }
3313                     } else if (!dompath && this.dompath) {
3314                         this.dompath.parentNode.removeChild(this.dompath);
3315                         this.dompath = null;
3316                     }
3317                     this._setupAfterElement();
3318                 }
3319             });
3320             /**
3321             * @config markup
3322             * @description Should we try to adjust the markup for the following types: semantic, css or default
3323             * @default "semantic"
3324             * @type Boolean
3325             */            
3326             this.setAttributeConfig('markup', {
3327                 value: 'semantic',
3328                 validator: function(markup) {
3329                     switch (markup.toLowerCase()) {
3330                         case 'semantic':
3331                         case 'css':
3332                         case 'default':
3333                         return true;
3334                         break;
3335                     }
3336                     return false;
3337                 }
3338             });
3340             this.on('afterRender', function() {
3341                 this._renderPanel();
3342             });
3343         },
3344         /**
3345         * @private
3346         * @method _getBlankImage
3347         * @description Retrieves the full url of the image to use as the blank image.
3348         * @returns {String} The URL to the blank image
3349         */
3350         _getBlankImage: function() {
3351             if (!this.DOMReady) {
3352                 this._queue[this._queue.length] = ['_getBlankImage', arguments];
3353                 return '';
3354             }      
3355             var div = document.createElement('div');
3356             div.style.position = 'absolute';
3357             div.style.top = '-9999px';
3358             div.style.left = '-9999px';
3359             div.className = this.CLASS_PREFIX + '-blankimage';
3360             document.body.appendChild(div);
3361             var img = YAHOO.util.Dom.getStyle(div, 'background-image');
3362             img = img.replace('url(', '').replace(')', '').replace(/"/g, '');
3363             this.set('blankimage', img);            
3364             return img;
3365         },
3366         /**
3367         * @private
3368         * @method _handleFontSize
3369         * @description Handles the font size button in the toolbar.
3370         * @param {Object} o Object returned from Toolbar's buttonClick Event
3371         */
3372         _handleFontSize: function(o) {
3373             var button = this.toolbar.getButtonById(o.button.id);
3374             var value = button.get('label') + 'px';
3375             this.execCommand('fontsize', value);
3376             this.STOP_EXEC_COMMAND = true;
3377         },
3378         /**
3379         * @private
3380         * @method _handleColorPicker
3381         * @description Handles the colorpicker buttons in the toolbar.
3382         * @param {Object} o Object returned from Toolbar's buttonClick Event
3383         */
3384         _handleColorPicker: function(o) {
3385             var cmd = o.button;
3386             var value = '#' + o.color;
3387             if ((cmd == 'forecolor') || (cmd == 'backcolor')) {
3388                 this.execCommand(cmd, value);
3389             }
3390         },
3391         /**
3392         * @private
3393         * @method _handleAlign
3394         * @description Handles the alignment buttons in the toolbar.
3395         * @param {Object} o Object returned from Toolbar's buttonClick Event
3396         */
3397         _handleAlign: function(o) {
3398             var button = this.toolbar.getButtonById(o.button.id);
3399             var cmd = null;
3400             for (var i = 0; i < o.button.menu.length; i++) {
3401                 if (o.button.menu[i].value == o.button.value) {
3402                     cmd = o.button.menu[i].value;
3403                 }
3404             }
3405             var value = this._getSelection();
3407             this.execCommand(cmd, value);
3408             this.STOP_EXEC_COMMAND = true;
3409         },
3410         /**
3411         * @private
3412         * @method _handleAfterNodeChange
3413         * @description Fires after a nodeChange happens to setup the things that where reset on the node change (button state).
3414         */
3415         _handleAfterNodeChange: function() {
3416             var path = this._getDomPath();
3417             for (var i = 0; i < path.length; i++) {
3418                 var elm = path[i],
3419                     tag = elm.tagName.toLowerCase(),
3420                     family = null,
3421                     fontsize = null,
3422                     validFont = false;
3424                 if (elm.getAttribute('tag')) {
3425                     tag = elm.getAttribute('tag');
3426                 }
3428                 family = elm.getAttribute('face');
3429                 if (Dom.getStyle(elm, 'font-family')) {
3430                     family = Dom.getStyle(elm, 'font-family');
3431                 }
3432                 var fn_button = this.toolbar.getButtonByValue('fontname');
3433                 if (fn_button) {
3434                     for (var b = 0; b < fn_button._configs.menu.value.length; b++) {
3435                         if (family && fn_button._configs.menu.value[b].text.toLowerCase() == family.toLowerCase()) {
3436                             validFont = true;
3437                             family = fn_button._configs.menu.value[b].text; //Put the proper menu name in the button
3438                         }
3439                     }
3440                     if (!validFont) {
3441                         family = fn_button._configs.label._initialConfig.value;
3442                     }
3443                     fn_button.set('label', '<span class="yui-toolbar-fontname-' + _cleanClassName(family) + '">' + family + '</span>');
3444                     this._updateMenuChecked('fontname', family);
3445                 }
3447                 var fs_button = this.toolbar.getButtonByValue('fontsize');
3448                 if (fs_button) {
3449                     fontsize = parseInt(Dom.getStyle(elm, 'fontSize'));
3450                     if ((fontsize == null) || isNaN(fontsize)) {
3451                         fontsize = fs_button._configs.label._initialConfig.value;
3452                     }
3453                     fs_button.set('label', ''+fontsize);
3454                 }
3456                 if (tag.substring(0, 1) == 'h') {
3457                     var hd_button = this.toolbar.getButtonByValue('heading');
3458                     if (hd_button) {
3459                         for (var b = 0; b < hd_button._configs.menu.value.length; b++) {
3460                             if (hd_button._configs.menu.value[b].value.toLowerCase() == tag) {
3461                                 hd_button.set('label', hd_button._configs.menu.value[b].text);
3462                             }
3463                         }
3464                         this._updateMenuChecked('heading', tag);
3465                     }
3466                 }
3467             }
3468             
3469             if (elm && elm.tagName && (elm.tagName.toLowerCase() != 'body')) {
3470                 this.toolbar.enableButton(fn_button);
3471                 this.toolbar.enableButton(fs_button);
3472             }
3473             
3474         },
3475         /**
3476         * @private
3477         * @method _handleInsertImageClick
3478         * @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
3479         */
3480         _handleInsertImageClick: function() {
3481             //this.toolbar.disableButton(this.toolbar.getButtonByValue('insertimage'));
3482             this.on('afterExecCommand', function() {
3483                 var el = this.currentElement[0],
3484                     title = '',
3485                     src = '',
3486                     align = '',
3487                     height = 75,
3488                     width = 75,
3489                     padding = 0,
3490                     blankimage = false,
3491                     win = new YAHOO.widget.EditorWindow('insertimage', {
3492                         width: '415px'
3493                     });
3495                 if (!el) {
3496                     el = this._getSelectedElement();
3497                 }
3498                 if (el) {
3499                     if (el.getAttribute('src')) {
3500                         src = el.getAttribute('src', 2);
3501                         if (src.indexOf(this.get('blankimage')) != -1) {
3502                             src = this.STR_IMAGE_HERE;
3503                             blankimage = true;
3504                         }
3505                     }
3506                     if (el.getAttribute('alt', 2)) {
3507                         title = el.getAttribute('alt', 2);
3508                     }
3509                     if (el.getAttribute('title', 2)) {
3510                         title = el.getAttribute('title', 2);
3511                     }
3512                     height = parseInt(el.height);
3513                     width = parseInt(el.width);
3514                     if (el.style.height) {
3515                         height = parseInt(el.style.height);
3516                     }
3517                     if (el.style.width) {
3518                         width = parseInt(el.style.width);
3519                     }
3520                     if (el.style.margin) {
3521                         padding = parseInt(el.style.margin);
3522                     }
3523                     if (!el._height) {
3524                         el._height = height;
3525                     }
3526                     if (!el._width) {
3527                         el._width = width;
3528                     }
3529                     var oheight = el._height;
3530                     var owidth = el._width;
3531                 }
3532                 if (!win.cache) {
3533                     var str = '<label for="insertimage_url"><strong>' + this.STR_IMAGE_URL + ':</strong> <input type="text" id="insertimage_url" value="' + src + '" size="40"></label>';
3534                     var body = document.createElement('div');
3535                     body.innerHTML = str;
3537                     var tbarCont = document.createElement('div');
3538                     tbarCont.id = 'img_toolbar';
3539                     body.appendChild(tbarCont);
3541                     var str2 = '<label for="insertimage_title"><strong>' + this.STR_IMAGE_TITLE + ':</strong> <input type="text" id="insertimage_title" value="' + title + '" size="40"></label>';
3542                     var div = document.createElement('div');
3543                     div.innerHTML = str2;
3544                     body.appendChild(div);
3545                     win.cache = body;
3546                 } else {
3547                     body = win.cache;
3548                 }
3550                 var tbar = new YAHOO.widget.Toolbar(tbarCont, {
3551                     /* {{{ */ 
3552                     buttons: [
3553                         { group: 'padding', label: this.STR_IMAGE_PADDING + ':',
3554                             buttons: [
3555                                 { type: 'spin', label: ''+padding, value: 'padding', range: [0, 50] }
3556                             ]
3557                         },
3558                         { type: 'separator' },
3559                         { group: 'border', label: this.STR_IMAGE_BORDER + ':',
3560                             buttons: [
3561                                 { type: 'select', label: 'Border Size', value: 'bordersize',
3562                                     menu: [
3563                                         { text: 'none', value: '0', checked: true },
3564                                         { text: '----', value: '1' },
3565                                         { text: '----', value: '2' },
3566                                         { text: '----', value: '3' },
3567                                         { text: '----', value: '4' },
3568                                         { text: '----', value: '5' }
3569                                     ]
3570                                 },
3571                                 { type: 'select', label: 'Border Type', value: 'bordertype', disabled: true,
3572                                     menu: [
3573                                         { text: '----', value: 'solid', checked: true },
3574                                         { text: '----', value: 'dashed' },
3575                                         { text: '----', value: 'dotted' }
3576                                     ]
3577                                 },
3578                                 { type: 'color', label: 'Border Color', value: 'bordercolor', disabled: true }
3579                             ]
3580                         },
3581                         { type: 'separator' },
3582                         { group: 'textflow', label: this.STR_IMAGE_TEXTFLOW + ':',
3583                             buttons: [
3584                                 { type: 'push', label: 'Left', value: 'left' },
3585                                 { type: 'push', label: 'Inline', value: 'inline' },
3586                                 { type: 'push', label: 'Block', value: 'block' },
3587                                 { type: 'push', label: 'Right', value: 'right' }
3588                             ]
3589                         }
3590                     ]
3591                     /* }}} */
3592                 });
3593                 
3594                 var bsize = '0';
3595                 var btype = 'solid';
3596                 if (el.style.borderLeftWidth) {
3597                     bsize = parseInt(el.style.borderLeftWidth);
3598                 }
3599                 if (el.style.borderLeftStyle) {
3600                     btype = el.style.borderLeftStyle;
3601                 }
3602                 var bs_button = tbar.getButtonByValue('bordersize');
3603                 var bSizeStr = ((parseInt(bsize) > 0) ? '----' : 'none');
3604                 bs_button.set('label', '<span class="yui-toolbar-bordersize-' + bsize + '">'+bSizeStr+'</span>');
3605                 this._updateMenuChecked('bordersize', bsize, tbar);
3607                 var bs_button = tbar.getButtonByValue('bordertype');
3608                 bs_button.set('label', '<span class="yui-toolbar-bordertype-' + btype + '">----</span>');
3609                 this._updateMenuChecked('bordertype', btype, tbar);
3610                 if (parseInt(bsize) > 0) {
3611                     tbar.enableButton(tbar.getButtonByValue('bordertype'));
3612                     tbar.enableButton(tbar.getButtonByValue('bordercolor'));
3613                 }
3615                 var cont = tbar.get('cont');
3616                 var hw = document.createElement('div');
3617                 hw.className = 'yui-toolbar-group yui-toolbar-group-padding height-width';
3618                 hw.innerHTML = '<h3>' + this.STR_IMAGE_SIZE + ':</h3>';
3619                 var orgSize = '';
3620                 if ((height != oheight) || (width != owidth)) {
3621                     orgSize = '<span class="info">' + this.STR_IMAGE_ORIG_SIZE + '<br>'+ owidth +' x ' + oheight + '</span>';
3622                 }
3623                 hw.innerHTML += '<span><input type="text" size="3" value="'+width+'" id="insertimage_width"> x <input type="text" size="3" value="'+height+'" id="insertimage_height"></span>' + orgSize;
3624                 cont.insertBefore(hw, cont.firstChild);
3626                 Event.onAvailable('insertimage_width', function() {
3627                     Event.on('insertimage_width', 'blur', function() {
3628                         var value = parseInt(Dom.get('insertimage_width').value);
3629                         if (value > 5) {
3630                             el.style.width = value + 'px';
3631                             this.moveWindow();
3632                         }
3633                     }, this, true);
3634                 }, this, true);
3635                 Event.onAvailable('insertimage_height', function() {
3636                     Event.on('insertimage_height', 'blur', function() {
3637                         var value = parseInt(Dom.get('insertimage_height').value);
3638                         if (value > 5) {
3639                             el.style.height = value + 'px';
3640                             this.moveWindow();
3641                         }
3642                     }, this, true);
3643                 }, this, true);
3645                 if (el.align == 'right') {
3646                     tbar.selectButton(tbar.getButtonByValue('right'));
3647                 } else if (el.align == 'left') {
3648                     tbar.selectButton(tbar.getButtonByValue('left'));
3649                 } else if (el.style.display == 'block') {
3650                     tbar.selectButton(tbar.getButtonByValue('block'));
3651                 } else {
3652                     tbar.selectButton(tbar.getButtonByValue('inline'));
3653                 }
3654                 if (parseInt(el.style.marginLeft) > 0) {
3655                      tbar.getButtonByValue('padding').set('label', ''+parseInt(el.style.marginLeft));
3656                 }
3657                 if (el.style.borderSize) {
3658                     tbar.selectButton(tbar.getButtonByValue('bordersize'));
3659                     tbar.selectButton(tbar.getButtonByValue(parseInt(el.style.borderSize)));
3660                 }
3662                 tbar.on('colorPickerClicked', function(o) {
3663                     var size = '1', type = 'solid', color = 'black';
3665                     if (el.style.borderLeftWidth) {
3666                         size = parseInt(el.style.borderLeftWidth);
3667                     }
3668                     if (el.style.borderLeftStyle) {
3669                         type = el.style.borderLeftStyle;
3670                     }
3671                     if (el.style.borderLeftColor) {
3672                         color = el.style.borderLeftColor;
3673                     }
3674                     var borderString = size + 'px ' + type + ' #' + o.color;
3675                     el.style.border = borderString;
3676                 }, this.toolbar, true);
3678                 tbar.on('buttonClick', function(o) {
3679                     var value = o.button.value;
3680                     if (o.button.menucmd) {
3681                         value = o.button.menucmd
3682                     }
3683                     var size = '1', type = 'solid', color = 'black';
3685                     /* All border calcs are done on the left border
3686                         since our default interface only supports
3687                         one border size/type and color */
3688                     if (el.style.borderLeftWidth) {
3689                         size = parseInt(el.style.borderLeftWidth);
3690                     }
3691                     if (el.style.borderLeftStyle) {
3692                         type = el.style.borderLeftStyle;
3693                     }
3694                     if (el.style.borderLeftColor) {
3695                         color = el.style.borderLeftColor;
3696                     }
3697                     switch(value) {
3698                         case 'bordersize':
3699                             var borderString = parseInt(o.button.value) + 'px ' + type + ' ' + color;
3700                             el.style.border = borderString;
3701                             if (parseInt(o.button.value) > 0) {
3702                                 tbar.enableButton(tbar.getButtonByValue('bordertype'));
3703                                 tbar.enableButton(tbar.getButtonByValue('bordercolor'));
3704                             } else {
3705                                 tbar.disableButton(tbar.getButtonByValue('bordertype'));
3706                                 tbar.disableButton(tbar.getButtonByValue('bordercolor'));
3707                             }
3708                             break;
3709                         case 'bordertype':
3710                             var borderString = size + 'px ' + o.button.value + ' ' + color;
3711                             el.style.border = borderString;
3712                             break;
3713                         case 'right':
3714                         case 'left':
3715                             tbar.deselectAllButtons();
3716                             el.style.display = '';
3717                             el.align = o.button.value;
3718                             break;
3719                         case 'inline':
3720                             tbar.deselectAllButtons();
3721                             el.style.display = '';
3722                             el.align = '';
3723                             break;
3724                         case 'block':
3725                             tbar.deselectAllButtons();
3726                             el.style.display = 'block';
3727                             el.align = 'center';
3728                             break;
3729                         case 'padding':
3730                             var _button = tbar.getButtonById(o.button.id);
3731                             el.style.margin = _button.get('label') + 'px';
3732                             break;
3733                     }
3734                     tbar.selectButton(tbar.getButtonByValue(o.button.value));
3735                     this.moveWindow();
3736                 }, this, true);
3738                 win.setHeader(this.STR_IMAGE_PROP_TITLE);
3739                 win.setBody(body);
3740                 if ((this.browser.webkit && !this.browser.webkit3) || this.browser.opera) {
3741                     var str = this.STR_IMAGE_COPY;
3742                     win.setFooter(str);
3743                 }
3744                 this.openWindow(win);
3747                 //Set event after openWindow..
3748                 Event.onAvailable('insertimage_url', function() {
3749                     window.setTimeout(function() {
3750                         YAHOO.util.Dom.get('insertimage_url').focus();
3751                         if (blankimage) {
3752                             YAHOO.util.Dom.get('insertimage_url').select();
3753                         }
3754                     }, 50);
3755                     
3756                     if (this.get('localFileWarning')) {
3757                         Event.on('insertimage_url', 'blur', function() {
3758                             var url = Dom.get('insertimage_url');
3759                             if ((url.value != '') && ((url.value.indexOf('file:/') != -1) || (url.value.indexOf(':\\') != -1))) {
3760                                 //Local File throw Warning
3761                                 Dom.addClass(url, 'warning');
3762                                 var str = this.STR_LOCAL_FILE_WARNING;
3763                                 this.get('panel').setFooter(str);
3764                             } else {
3765                                 Dom.removeClass(url, 'warning');
3766                                 this.get('panel').setFooter(' ');
3767                                 if ((this.browser.webkit && !this.browser.webkit3) || this.browser.opera) {
3768                                     var str = this.STR_IMAGE_COPY;
3769                                     this.get('panel').setFooter(str);
3770                                 }
3771                                 
3772                                 if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
3773                                     this.currentElement[0].setAttribute('src', url.value);
3774                                     var img = new Image();
3775                                     var self = this;
3776                                     window.setTimeout(function() {
3777                                         YAHOO.util.Dom.get('insertimage_height').value = img.height;
3778                                         YAHOO.util.Dom.get('insertimage_width').value = img.width;
3779                                         if (!self.currentElement[0]._height) {
3780                                             self.currentElement[0]._height = img.height;
3781                                         }
3782                                         if (!self.currentElement[0]._width) {
3783                                             self.currentElement[0]._width = img.width;
3784                                         }
3785                                         self.moveWindow();
3786                                     }, 200);
3788                                     img.src = url.value;
3789                                 }
3790                             }
3791                         }, this, true);
3792                     }
3793                 }, this, true);
3794             });
3795         },
3796         /**
3797         * @private
3798         * @method _handleInsertImageWindowClose
3799         * @description Handles the closing of the Image Properties Window.
3800         */
3801         _handleInsertImageWindowClose: function() {
3802             var url = Dom.get('insertimage_url');
3803             var title = Dom.get('insertimage_title');
3804             var el = this.currentElement[0];
3805             if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
3806                 el.setAttribute('src', url.value);
3807                 el.setAttribute('title', title.value);
3808                 el.setAttribute('alt', title.value);
3809                 //this.toolbar.enableButton(this.toolbar.getButtonByValue('insertimage'));
3810             } else {
3811                 //No url/src given, remove the node from the document
3812                 el.parentNode.removeChild(el);
3813             }
3814         },
3815         /**
3816         * @private
3817         * @method _handleCreateLinkClick
3818         * @description Handles the opening of the Link Properties Window when the Create Link button is clicked or an href is doubleclicked.
3819         */
3820         _handleCreateLinkClick: function() {
3821             this.on('afterExecCommand', function() {
3823                 var win = new YAHOO.widget.EditorWindow('createlink', {
3824                     width: '300px'
3825                 });
3826                 
3827                 var el = this.currentElement[0],
3828                     url = '',
3829                     title = '',
3830                     target = '',
3831                     localFile = false;
3832                 if (el) {
3833                     if (el.getAttribute('href') != null) {
3834                         url = el.getAttribute('href');
3835                         if ((url != '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
3836                             //Local File throw Warning
3837                             var str = this.STR_LOCAL_FILE_WARNING;
3838                             win.setFooter(str);
3839                             localFile = true;
3840                         } else {
3841                             win.setFooter(' ');
3842                         }
3843                     }
3844                     if (el.getAttribute('title') != null) {
3845                         title = el.getAttribute('title');
3846                     }
3847                     if (el.getAttribute('target') != null) {
3848                         target = el.getAttribute('target');
3849                     }
3850                 }
3851                 var str = '<label for="createlink_url"><strong>' + this.STR_LINK_URL + ':</strong> <input type="text" name="createlink_url" id="createlink_url" value="' + url + '"' + ((localFile) ? ' class="warning"' : '') + '></label>';
3852                 str += '<label for="createlink_target"><strong>&nbsp;</strong><input type="checkbox" name="createlink_target_" id="createlink_target" value="_blank"' + ((target) ? ' checked' : '') + '> ' + this.STR_LINK_NEW_WINDOW + '</label>';
3853                 str += '<label for="createlink_title"><strong>' + this.STR_LINK_TITLE + ':</strong> <input type="text" name="createlink_title" id="createlink_title" value="' + title + '"></label>';
3854                 
3855                 var body = document.createElement('div');
3856                 body.innerHTML = str;
3858                 var unlinkCont = document.createElement('div');
3859                 unlinkCont.className = 'removeLink';
3860                 var unlink = document.createElement('a');
3861                 unlink.href = '#';
3862                 unlink.innerHTML = this.STR_LINK_PROP_REMOVE;
3863                 unlink.title = this.STR_LINK_PROP_REMOVE;
3864                 Event.on(unlink, 'click', function(ev) {
3865                     Event.stopEvent(ev);
3866                     this.execCommand('unlink');
3867                     this.closeWindow();
3868                 }, this, true);
3869                 unlinkCont.appendChild(unlink);
3870                 body.appendChild(unlinkCont);
3872                 win.setHeader(this.STR_LINK_PROP_TITLE);
3873                 win.setBody(body);
3875                 Event.onAvailable('createlink_url', function() {
3876                     window.setTimeout(function() {
3877                         try {
3878                             YAHOO.util.Dom.get('createlink_url').focus();
3879                         } catch (e) {}
3880                     }, 50);
3881                     Event.on('createlink_url', 'blur', function() {
3882                         var url = Dom.get('createlink_url');
3883                         if ((url.value != '') && ((url.value.indexOf('file:/') != -1) || (url.value.indexOf(':\\') != -1))) {
3884                             //Local File throw Warning
3885                             Dom.addClass(url, 'warning');
3886                             var str = this.STR_LOCAL_FILE_WARNING;
3887                             this.get('panel').setFooter(str);
3888                         } else {
3889                             Dom.removeClass(url, 'warning');
3890                             this.get('panel').setFooter(' ');
3891                         }
3892                     }, this, true);
3893                 }, this, true);
3895                 this.openWindow(win);
3896             });
3897         },
3898         /**
3899         * @private
3900         * @method _handleCreateLinkWindowClose
3901         * @description Handles the closing of the Link Properties Window.
3902         */
3903         _handleCreateLinkWindowClose: function() {
3904             var url = Dom.get('createlink_url');
3905             var target = Dom.get('createlink_target');
3906             var title = Dom.get('createlink_title');
3907             var el = this.currentElement[0];
3908             if (url && url.value) {
3909                 var urlValue = url.value;
3910                 if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
3911                     if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
3912                         //Found an @ sign, prefix with mailto:
3913                         urlValue = 'mailto:' + urlValue;
3914                     } else {
3915                         /* :// not found adding */
3916                         urlValue = 'http:/'+'/' + urlValue;
3917                     }
3918                 }
3919                 el.setAttribute('href', urlValue);
3920                 if (target.checked) {
3921                     el.setAttribute('target', target.value);
3922                 } else {
3923                     el.setAttribute('target', '');
3924                 }
3925                 el.setAttribute('title', ((title.value) ? title.value : ''));
3927             } else {
3928                 el.removeAttribute('tag');
3929                 Dom.removeClass(el, 'yui-tag-a');
3930                 Dom.removeClass(el, 'yui-tag');
3931                 Dom.addClass(el, 'yui-non');
3932             }
3933             this.nodeChange();
3934         },
3935         /**
3936         * @method render
3937         * @description Causes the toolbar and the editor to render and replace the textarea.
3938         */
3939         render: function() {
3940             if (!this.DOMReady) {
3941                 this._queue[this._queue.length] = ['render', arguments];
3942                 return false;
3943             }
3944             var self = this;
3945             var tbarConf = this.get('toolbar');
3946             //Set the toolbar to disabled until content is loaded
3947             tbarConf.disabled = true;
3948             //Create Toolbar instance
3949             this.toolbar = new Toolbar(this.get('toolbar_cont'), tbarConf);
3950             this.fireEvent('toolbarLoaded', { type: 'toolbarLoaded', target: this.toolbar });
3952             
3953             this.toolbar.on('toolbarCollapsed', function() {
3954                 if (this.currentWindow) {
3955                     this.moveWindow();
3956                 }
3957             }, this, true);
3958             this.toolbar.on('toolbarExpanded', function() {
3959                 if (this.currentWindow) {
3960                     this.moveWindow();
3961                 }
3962             }, this, true);
3963             this.toolbar.on('fontsizeClick', function(o) {
3964                 this._handleFontSize(o);
3965             }, this, true);
3966             
3967             this.toolbar.on('colorPickerClicked', function(o) {
3968                 this._handleColorPicker(o);
3969             }, this, true);
3971             this.toolbar.on('alignClick', function(o) {
3972                 this._handleAlign(o);
3973             }, this, true);
3974             this.on('afterNodeChange', function() {
3975                 this._handleAfterNodeChange();
3976             }, this, true);
3977             this.toolbar.on('insertimageClick', function() {
3978                 this._handleInsertImageClick();
3979             }, this, true);
3980             this.on('windowinsertimageClose', function() {
3981                 this._handleInsertImageWindowClose();
3982             }, this, true);
3983             this.toolbar.on('createlinkClick', function() {
3984                 this._handleCreateLinkClick();
3985             }, this, true);
3986             this.on('windowcreatelinkClose', function() {
3987                 this._handleCreateLinkWindowClose();
3988             }, this, true);
3991             //Replace Textarea with editable area
3992             
3993             this.get('parentNode').replaceChild(this.get('element_cont').get('element'), this.get('element'));
3996             if (!this.beforeElement) {
3997                 this.beforeElement = document.createElement('h2');
3998                 this.beforeElement.className = 'yui-editor-skipheader';
3999                 this.beforeElement.tabIndex = '-1';
4000                 this.beforeElement.innerHTML = this.STR_BEFORE_EDITOR;
4001                 this.get('element_cont').get('firstChild').insertBefore(this.beforeElement, this.toolbar.get('nextSibling'));
4002             }
4003             
4004             Dom.setStyle(this.get('textarea'), 'display', 'none');
4005             this.get('element_cont').appendChild(this.get('element'));
4006             this.get('element_cont').setStyle('display', 'block');
4009             //Set height and width of editor container
4010             this.get('element_cont').setStyle('width', this.get('width'));
4011             Dom.setStyle(this.get('iframe').get('parentNode'), 'height', this.get('height'));
4013             this.get('iframe').setStyle('width', '100%'); //WIDTH
4014             //this.get('iframe').setStyle('_width', '99%'); //WIDTH
4015             this.get('iframe').setStyle('height', '100%');
4018             var self = this;
4019             window.setTimeout(function() {
4020                 self._setInitialContent.call(self);
4021             }, 10);
4022             
4023             this.fireEvent('afterRender', { type: 'afterRender', target: this });
4024         },
4025         /**
4026         * @method execCommand
4027         * @param {String} action The "execCommand" action to try to execute (Example: bold, insertimage, inserthtml)
4028         * @param {String} value (optional) The value for a given action such as action: fontname value: 'Verdana'
4029         * @description This method attempts to try and level the differences in the various browsers and their support for execCommand actions
4030         */
4031         execCommand: function(action, value) {
4032             this.fireEvent('beforeExecCommand', { type: 'beforeExecCommand', target: this, args: arguments });
4033             if (this.STOP_EXEC_COMMAND) {
4034                 this.STOP_EXEC_COMMAND = false;
4035                 return false;
4036             }
4037             this._setMarkupType(action);
4038             if (this.browser.ie) {
4039                 this._getWindow().focus();
4040             }
4041             var exec = true;
4042             var _sel = this._getSelection();
4043             var _range = this._getRange();
4044             var _selEl = this._getSelectedElement();
4045             if (_selEl) {
4046                 _sel = _selEl;
4047             }
4048             switch (action.toLowerCase()) {
4049                 case 'heading':
4050                     if (this.browser.ie) {
4051                         action = 'formatblock';
4052                     }
4053                     if (value == 'none') {
4054                         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'))) {
4055                             if (_sel.parentNode.tagName.toLowerCase().substring(0,1) == 'h') {
4056                                 _sel = _sel.parentNode;
4057                             }
4058                             var _span = this._getDoc().createElement('span');
4059                             _span.className = 'yui-non';
4060                             _span.innerHTML = _sel.innerHTML;
4061                             _sel.parentNode.replaceChild(_span, _sel);
4062                         }
4063                         exec = false;
4064                     } else {
4065                         if (this.browser.ie || this.browser.webkit || this.browser.opera) {
4066                             this._createCurrentElement(value);
4067                             exec = false;
4068                         }
4069                     }
4070                     break;
4071                 case 'backcolor':
4072                     if (this.browser.gecko || this.browser.opera) {
4073                         this._setEditorStyle(true);
4074                         action = 'hilitecolor';
4075                     }
4076                     break;
4077                 case 'hiddenelements':
4078                     this._showHidden();
4079                     exec = false;
4080                     break;
4081                 case 'unlink':
4082                     //var el = this._getSelectedElement();
4083                     var el = this.currentElement[0];
4084                     el.removeAttribute('title');
4085                     el.removeAttribute('tag');
4086                     el.removeAttribute('target');
4087                     el.removeAttribute('href');
4088                     YAHOO.util.Dom.addClass(el, 'yui-non');
4089                     YAHOO.util.Dom.removeClass(el, 'yui-tag-a');
4090                     YAHOO.util.Dom.removeClass(el, 'yui-tag');
4091                     exec = false;
4092                     break;
4093                 case 'createlink':
4094                     var el = this._getSelectedElement();
4095                     if (!el || (el.getAttribute('tag') != 'a')) {
4096                         this._createCurrentElement('a');
4097                     } else {
4098                         this.currentElement[0] = el;
4099                     }
4100                     exec = false;
4101                     break;
4102                 case 'insertimage':
4103                     if (value == '') {
4104                         value = this.get('blankimage');
4105                     }
4106                     /**
4107                     * @knownissue
4108                     * @browser Safari 2.x
4109                     * @description The issue here is that we have no way of knowing where the cursor position is
4110                     * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
4111                     */
4112                     
4113                     var el = this._getSelectedElement();
4115                     if (el && el.tagName && (el.tagName.toLowerCase() == 'img')) {
4116                         this.currentElement[0] = el;
4117                         exec = false;
4118                     } else {
4119                         if (this._getDoc().queryCommandEnabled(action)) {
4120                             this._getDoc().execCommand('insertimage', false, value);
4121                             var imgs = this._getDoc().getElementsByTagName('img');
4122                             for (var i = 0; i < imgs.length; i++) {
4123                                 if (!YAHOO.util.Dom.hasClass(imgs[i], 'yui-img')) {
4124                                     YAHOO.util.Dom.addClass(imgs[i], 'yui-img');
4125                                     this.currentElement[0] = imgs[i];
4126                                 }
4127                             }
4128                             exec = false;
4129                         } else {
4130                             this._createCurrentElement('img');
4131                             var _img = this._getDoc().createElement('img');
4132                             _img.setAttribute('src', value);
4133                             YAHOO.util.Dom.addClass(_img, 'yui-img');
4134                             this.currentElement[0].parentNode.replaceChild(_img, this.currentElement[0]);
4135                             this.currentElement[0] = _img;
4136                             exec = false;
4137                         }
4138                     }
4139                     
4140                     break;
4141                 case 'inserthtml':
4142                     /**
4143                     * @knownissue
4144                     * @browser Safari 2.x
4145                     * @description The issue here is that we have no way of knowing where the cursor position is
4146                     * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
4147                     */
4148                     if (this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) {
4149                         this._createCurrentElement('img');
4150                         var _span = this._getDoc().createElement('span');
4151                         _span.innerHTML = value;
4152                         this.currentElement[0].parentNode.replaceChild(_span, this.currentElement[0]);
4153                         exec = false;
4154                     } else if (this.browser.ie) {
4155                         var _range = this._getRange();
4156                         if (_range.item) {
4157                             _range.item(0).outerHTML = value;
4158                         } else {
4159                             _range.pasteHTML(value);
4160                         }
4161                         exec = false;                    
4162                     }
4163                     break;
4164                 case 'removeformat':
4165                     /**
4166                     * @knownissue Remove Format issue
4167                     * @browser Safari 2.x
4168                     * @description There is an issue here with Safari, that it may not always remove the format of the item that is selected.
4169                     * Due to the way that Safari 2.x handles ranges, it is very difficult to determine what the selection holds.
4170                     * So here we are making the best possible guess and acting on it.
4171                     */
4172                     if (this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) {
4173                         this._createCurrentElement('span');
4174                         YAHOO.util.Dom.addClass(this.currentElement[0], 'yui-non');
4175                         var re= /<\S[^><]*>/g;
4176                         var str = this.currentElement[0].innerHTML.replace(re, '');
4177                         var _txt = this._getDoc().createTextNode(str);
4178                         this.currentElement[0].parentNode.parentNode.replaceChild(_txt, this.currentElement[0].parentNode);
4179                         
4180                         exec = false;
4181                     }
4182                     break;
4183                 case 'superscript':
4184                 case 'subscript':
4185                     if (this.browser.webkit) {
4186                         var tag = action.toLowerCase().substring(0, 3);
4187                         this._createCurrentElement(tag);
4188                         if (this.currentElement[0].parentNode.tagName.toLowerCase() == tag) {
4189                             var span = this._getDoc().createElement('span');
4190                             span.innerHTML = this.currentElement[0].innerHTML;
4191                             YAHOO.util.Dom.addClass(span, 'yui-non');
4192                             this.currentElement[0].parentNode.parentNode.replaceChild(span, this.currentElement[0].parentNode);
4194                         } else {
4195                             var _sub = this._getDoc().createElement(tag);
4196                             _sub.innerHTML = this.currentElement[0].innerHTML;
4197                             this.currentElement[0].parentNode.replaceChild(_sub, this.currentElement[0]);
4198                         }
4199                         exec = false;
4200                     }
4201                     break;
4202                 case 'formatblock':
4203                     value = 'blockquote';
4204                     if (this.browser.webkit) {
4205                         this._createCurrentElement('blockquote');
4206                         if (YAHOO.util.Dom.hasClass(this.currentElement[0].parentNode, 'yui-tag-blockquote')) {
4207                             var span = this._getDoc().createElement('span');
4208                             span.innerHTML = this.currentElement[0].innerHTML;
4209                             YAHOO.util.Dom.addClass(span, 'yui-non');
4210                             this.currentElement[0].parentNode.parentNode.replaceChild(span, this.currentElement[0].parentNode);
4211                         }
4212                         exec = false;
4213                     } else {
4214                         var tar = Event.getTarget(this.currentEvent);
4215                         if (tar && tar.tagName && (tar.tagName.toLowerCase() == 'blockquote')) {
4216                             var span = this._getDoc().createElement('span');
4217                             span.innerHTML = tar.innerHTML;
4218                             YAHOO.util.Dom.addClass(span, 'yui-non');
4219                             tar.parentNode.replaceChild(span, tar);
4220                             exec = false;
4221                         }
4222                     }
4223                     break;
4224                 case 'indent':
4225                 case 'outdent':
4226                     this._createCurrentElement(action.toLowerCase());
4227                     if (this.currentElement[0].parentNode) {
4228                         if (action.toLowerCase() == 'outdent') {
4229                             if (YAHOO.util.Dom.hasClass(this.currentElement[0].parentNode, 'yui-tag-indent')) {
4230                                 var span = this._getDoc().createElement('span');
4231                                 span.innerHTML = this.currentElement[0].innerHTML;
4232                                 YAHOO.util.Dom.addClass(span, 'yui-non');
4233                                 this.currentElement[0].parentNode.parentNode.replaceChild(span, this.currentElement[0].parentNode);
4234                             }
4235                         }
4236                     }
4237                     exec = false;
4238                     break;
4239                 case 'insertorderedlist':
4240                 case 'insertunorderedlist':
4241                     /**
4242                     * @knownissue Safari 2.+ doesn't support ordered and unordered lists
4243                     * @browser Safari 2.x
4244                     * The issue with this workaround is that when applied to a set of text
4245                     * that has BR's in it, Safari may or may not pick up the individual items as
4246                     * list items. This is fixed in WebKit (Safari 3)
4247                     */
4248                     var tag = ((action.toLowerCase() == 'insertorderedlist') ? 'ol' : 'ul');
4249                     //if ((this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) || this.browser.opera) {
4250                     if ((this.browser.webkit && !this._getDoc().queryCommandEnabled(action))) {
4251                         var selEl = this._getSelectedElement();
4252                         if ((selEl.tagName.toLowerCase() == 'li') && (selEl.parentNode.tagName.toLowerCase() == tag)) {
4253                             var el = selEl.parentNode;
4254                             var list = this._getDoc().createElement('span');
4255                             YAHOO.util.Dom.addClass(list, 'yui-non');
4256                             var str = '';
4257                             var lis = el.getElementsByTagName('li');
4258                             for (var i = 0; i < lis.length; i++) {
4259                                 str += lis[i].innerHTML + '<br>';
4260                             }
4261                             list.innerHTML = str;
4263                         } else {
4264                             this._createCurrentElement(tag.toLowerCase());
4265                             var el = this.currentElement[0];
4266                             var list = this._getDoc().createElement(tag);
4267                             if (tag == 'ol') {
4268                                 list.type = value;
4269                             }
4270                             var li = this._getDoc().createElement('li');
4271                             li.innerHTML = el.innerHTML + '&nbsp;';
4272                             list.appendChild(li);
4273                         }
4274                         el.parentNode.replaceChild(list, el);
4275                         exec = false;
4276                     } else {
4277                         var el = this._getSelectedElement();
4278                         if ((el.tagName.toLowerCase() == 'li') && (el.parentNode.tagName.toLowerCase() == tag) || (this.browser.ie && this._getRange().parentElement && this._getRange().parentElement.tagName && (this._getRange().parentElement.tagName.toLowerCase() == 'li'))) { //we are in a list..
4279                             if (this.browser.ie) {
4280                                 exec = false;
4281                                 var str = '';
4282                                 var lis = el.parentNode.getElementsByTagName('li');
4283                                 for (var i = 0; i < lis.length; i++) {
4284                                     str += lis[i].innerHTML + '<br>';
4285                                 }
4286                                 var newEl = this._getDoc().createElement('span');
4287                                 newEl.innerHTML = str;
4288                                 el.parentNode.parentNode.replaceChild(newEl, el.parentNode);
4289                             } else {
4290                                 this.nodeChange();
4291                                 this._getDoc().execCommand(action, '', el.parentNode);
4292                                 this.nodeChange();
4293                             }
4294                         }
4295                         if (this.browser.opera) {
4296                             var self = this;
4297                             window.setTimeout(function() {
4298                                 var lis = self._getDoc().getElementsByTagName('li');
4299                                 for (var i = 0; i < lis.length; i++) {
4300                                     if (lis[i].innerHTML.toLowerCase() == '<br>') {
4301                                         lis[i].parentNode.parentNode.removeChild(lis[i].parentNode);
4302                                     }
4303                                 }
4304                             },30);
4305                         }
4306                         if (this.browser.ie && exec) {
4307                             var html = '';
4308                             if (this._getRange().html) {
4309                                 html = '<li>' + this._getRange().html+ '</li>';
4310                             } else {
4311                                 html = '<li>' + this._getRange().text + '</li>';
4312                             }
4314                             this._getRange().pasteHTML('<' + tag + '>' + html + '</' + tag + '>');
4315                             exec = false;
4316                         }
4317                     }
4318                     break;
4319                 case 'fontname':
4320                     var selEl = this._getSelectedElement();
4321                     this.currentFont = value;
4322                     if (selEl && selEl.tagName && !this._hasSelection()) {
4323                         YAHOO.util.Dom.setStyle(selEl, 'font-family', value);
4324                         exec = false;
4325                     }
4326                     break;
4327                 case 'fontsize':
4328                     if ((this.currentElement.length > 0) && (!this._hasSelection())) {
4329                         YAHOO.util.Dom.setStyle(this.currentElement, 'fontSize', value);
4330                     } else {
4331                         this._createCurrentElement('span', {'fontSize': value });
4332                     }
4333                     exec = false;
4334                     break;
4335             }
4336             if (exec) {
4337                 try {
4338                     this._getDoc().execCommand(action, false, value);
4339                 } catch(e) {
4340                 }
4341             } else {
4342             }
4343             this.on('afterExecCommand', function() {
4344                 this.unsubscribeAll('afterExecCommand');
4345                 this.nodeChange();
4346             });
4347             this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
4348             
4349         },
4350         /**
4351         * @private
4352         * @method _createCurrentElement
4353         * @param {String} tagName (optional defaults to a) The tagname of the element that you wish to create
4354         * @param {Object} tagStyle (optional) Object literal containing styles to apply to the new element.
4355         * @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.
4356         * It will then search the document for a span 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
4357         * <code>this.currentElement</code>, so we now have an element reference to the element that was just modified. At this point we can use standard DOM manipulation to change it as we see fit.
4358         */
4359         _createCurrentElement: function(tagName, tagStyle) {
4360             var tagName = ((tagName) ? tagName : 'a'),
4361                 sel = this._getSelection(),
4362                 tar = null,
4363                 el = [],
4364                 _doc = this._getDoc();
4365             
4366             if (this.currentFont) {
4367                 if (!tagStyle) {
4368                     tagStyle = {};
4369                 }
4370                 tagStyle.fontFamily = this.currentFont;
4371                 this.currentFont = null;
4372             }
4373             this.currentElement = [];
4375             var _elCreate = function() {
4376                 switch (tagName) {
4377                     case 'h1':
4378                     case 'h2':
4379                     case 'h3':
4380                     case 'h4':
4381                     case 'h5':
4382                     case 'h6':
4383                         var el = _doc.createElement(tagName);
4384                         break;
4385                     default:
4386                         var el = _doc.createElement('span');
4387                         YAHOO.util.Dom.addClass(el, 'yui-tag-' + tagName);
4388                         YAHOO.util.Dom.addClass(el, 'yui-tag');
4389                         el.setAttribute('tag', tagName);
4390                         el.tabIndex = 1;
4392                         for (var i in tagStyle) {
4393                             if (YAHOO.util.Lang.hasOwnProperty(tagStyle, i)) {
4394                                 el.style[i] = tagStyle[i];
4395                             }
4396                         }
4397                         break;
4398                 }
4399                 return el;
4400             };
4402             if (!this._hasSelection()) {
4403                 if (this._getDoc().queryCommandEnabled('insertimage')) {
4404                     this._getDoc().execCommand('insertimage', false, 'yui-tmp-img');
4405                     var imgs = this._getDoc().getElementsByTagName('img');
4406                     for (var i = 0; i < imgs.length; i++) {
4407                         if (imgs[i].getAttribute('src', 2) == 'yui-tmp-img') {
4408                             el = _elCreate();
4409                             imgs[i].parentNode.replaceChild(el, imgs[i]);
4410                             this.currentElement[this.currentElement.length] = el;
4411                             //this.currentElement = el;
4412                         }
4413                     }
4414                 } else {
4415                     if (this.currentEvent) {
4416                         tar = YAHOO.util.Event.getTarget(this.currentEvent);
4417                     } else {
4418                         //For Safari..
4419                         tar = this._getDoc().body;                        
4420                     }
4421                 }
4422                 if (tar) {
4423                     /**
4424                     * @knownissue
4425                     * @browser Safari 2.x
4426                     * @description The issue here is that we have no way of knowing where the cursor position is
4427                     * inside of the iframe, so we have to place the newly inserted data in the best place that we can.
4428                     */
4429                     el = _elCreate();
4430                     if (tar.tagName.toLowerCase() == 'body') {
4431                         tar.appendChild(el);
4432                     } else if (tar.nextSibling) {
4433                         tar.parentNode.insertBefore(el, tar.nextSibling);
4434                     } else {
4435                         tar.parentNode.appendChild(el);
4436                     }
4437                     //this.currentElement = el;
4438                     this.currentElement[this.currentElement.length] = el;
4439                     this.currentEvent = null;
4440                     if (this.browser.webkit) {
4441                         //Force Safari to focus the new element
4442                         this._getSelection().setBaseAndExtent(el, 0, el, 0);
4443                         this._getSelection().collapse(true);   
4444                     }
4445                 }
4446             } else {
4447                 //Force CSS Styling for this action...
4448                 this._setEditorStyle(true);
4449                 this._getDoc().execCommand('fontname', false, 'yui-tmp');
4450                 var _tmp = [];
4451                 /* TODO: This needs to be cleaned up.. */
4452                 var _tmp1 = this._getDoc().getElementsByTagName('font');
4453                 var _tmp2 = this._getDoc().getElementsByTagName(this._getSelectedElement().tagName);
4454                 var _tmp3 = this._getDoc().getElementsByTagName('span');
4455                 var _tmp4 = this._getDoc().getElementsByTagName('i');
4456                 var _tmp5 = this._getDoc().getElementsByTagName('b');
4457                 for (var e = 0; e < _tmp1.length; e++) {
4458                     _tmp[_tmp.length] = _tmp1[e];
4459                 }
4460                 for (var e = 0; e < _tmp2.length; e++) {
4461                     _tmp[_tmp.length] = _tmp2[e];
4462                 }
4463                 for (var e = 0; e < _tmp3.length; e++) {
4464                     _tmp[_tmp.length] = _tmp3[e];
4465                 }
4466                 for (var e = 0; e < _tmp4.length; e++) {
4467                     _tmp[_tmp.length] = _tmp4[e];
4468                 }
4469                 for (var e = 0; e < _tmp5.length; e++) {
4470                     _tmp[_tmp.length] = _tmp5[e];
4471                 }
4472                 for (var i = 0; i < _tmp.length; i++) {
4473                     if ((YAHOO.util.Dom.getStyle(_tmp[i], 'font-family') == 'yui-tmp') || (_tmp[i].face && (_tmp[i].face == 'yui-tmp'))) {
4474                         var el = _elCreate();
4475                         el.innerHTML = _tmp[i].innerHTML;
4476                         if (_tmp[i].parentNode) {
4477                             _tmp[i].parentNode.replaceChild(el, _tmp[i]);
4478                             //this.currentElement = el;
4479                             this.currentElement[this.currentElement.length] = el;
4480                             this.currentEvent = null;
4481                             if (this.browser.webkit) {
4482                                 //Force Safari to focus the new element
4483                                 this._getSelection().setBaseAndExtent(el, 0, el, 0);
4484                                 this._getSelection().collapse(true);   
4485                             }
4486                             if (this.browser.ie && tagStyle && tagStyle.fontSize) {
4487                                 this._getSelection().empty();
4488                             }
4489                             if (this.browser.gecko) {
4490                                 this._getSelection().collapseToStart();
4491                             }
4492                         }
4493                     }
4494                 }
4495                 var len = this.currentElement.length;
4496                 for (var i = 0; i < len; i++) {
4497                     if ((i + 1) != len) { //Skip the last one in the list
4498                         if (this.currentElement[i] && this.currentElement[i].nextSibling) {
4499                             if (this.currentElement[i].tagName && (this.currentElement[i].tagName.toLowerCase() != 'br')) {
4500                                 this.currentElement[this.currentElement.length] = this.currentElement[i].nextSibling;
4501                             }
4502                         }
4503                     }
4504                 }
4505             }
4506         },
4507         /**
4508         * @method saveHTML
4509         * @description Cleans the HTML with the cleanHTML method then places that string back into the textarea.
4510         */
4511         saveHTML: function() {
4512             var html = this.cleanHTML();
4513             this.get('textarea').value = html;
4514             return html;
4515         },
4516         /**
4517         * @method setEditorHTML
4518         * @param {String} html The html content to load into the editor
4519         * @description Loads HTML into the editors body
4520         */
4521         setEditorHTML: function(html) {
4522             this._getDoc().body.innerHTML = html;
4523             this.nodeChange();
4524         },
4525         /**
4526         * @method getEditorHTML
4527         * @description Gets the unprocessed/unfiltered HTML from the editor
4528         */
4529         getEditorHTML: function() {
4530             return this._getDoc().body.innerHTML;
4531         },
4532         /**
4533         * @method cleanHTML
4534         * @param {String} html The unfiltered HTML
4535         * @description Process the HTML with a few regexes to clean it up and stabilize the output
4536         * @returns {String} The filtered HTML
4537         */
4538         cleanHTML: function(html) {
4539             //Start Filtering Output
4540             //Begin RegExs..
4541             if (!html) { 
4542                 var html = this.getEditorHTML();
4543             }
4544             //Make some backups...
4545                     html = html.replace(/<div><br><\/div>/gi, '<YUI_BR>');
4546                     html = html.replace(/<p>(&nbsp;|&#160;)<\/p>/g, '<YUI_BR>');            
4547                     html = html.replace(/<p><br>&nbsp;<\/p>/gi, '<YUI_BR>');
4548                     html = html.replace(/<p>&nbsp;<\/p>/gi, '<YUI_BR>');
4549                     html = html.replace(/<br class="khtml-block-placeholder">/gi, '<YUI_BR>');
4550                     html = html.replace(/<br>/gi, '<YUI_BR>');
4551                     html = html.replace(/<br\/>/gi, '<YUI_BR>');
4552                     html = html.replace(/<img([^>]*)>/gi, '<YUI_IMG$1>');
4553                     html = html.replace(/<ul([^>]*)>/gi, '<YUI_UL$1>');
4554                     html = html.replace(/<\/ul>/gi, '<\/YUI_UL>');
4556             //Convert b and i tags to strong and em tags
4557                     html = html.replace(/<i([^>]*)>/gi, '<em$1>');
4558                     html = html.replace(/<\/i>/gi, '</em>');
4559                     html = html.replace(/<b([^>]*)>/gi, '<strong$1>');
4560                     html = html.replace(/<\/b>/gi, '</strong>');
4562                     html = html.replace(/<font/gi, '<font');
4563                     html = html.replace(/<\/font>/gi, '</font>');
4564                     html = html.replace(/<span/gi, '<span');
4565                     html = html.replace(/<\/span>/gi, '</span>');
4566                     html = html.replace(/<u/gi, '<u');
4567                     html = html.replace(/\/u>/gi, '/u>');
4569                     html = html.replace(/<ol([^>]*)>/gi, '<ol$1>');
4570                     html = html.replace(/\/ol>/gi, '/ol>');
4571                     html = html.replace(/<li/gi, '<li');
4572                     html = html.replace(/\/li>/gi, '/li>');
4574             //Handle the sudo A tags
4575             html = html.replace(new RegExp('<span ([^>]*) tag="a" ([^>]*)>([^>]*)<\/span>', 'gi'), '<a $1 $2>$3</a>');
4577             //Safari only regexes
4578             if (this.browser.webkit) {
4579                 //<DIV><SPAN class="Apple-style-span" style="line-height: normal;">Test THis</SPAN></DIV>
4580                 html = html.replace(/Apple-style-span/gi, '');
4581                 html = html.replace(/style="line-height: normal;"/gi, '');
4582             }
4584             //yui-tag-a yui-tag yui-non yui-img
4585                     html = html.replace(/yui-tag-a/gi, '');
4586                     html = html.replace(/yui-tag-span/gi, '');
4587                     html = html.replace(/yui-tag/gi, '');
4588                     html = html.replace(/yui-non/gi, '');
4589                     html = html.replace(/yui-img/gi, '');
4590                     html = html.replace(/ tag="span"/gi, '');
4591                     html = html.replace(/ class=""/gi, '');
4592                     html = html.replace(/ class=" "/gi, '');
4593                     html = html.replace(/ class="  "/gi, '');
4594                     html = html.replace(/ target=""/gi, '');
4595                     html = html.replace(/ title=""/gi, '');
4597             //Other string cleanup
4598                     html = html.replace(/<br><li/gi, '<li');
4599             //<P><br>&nbsp;</P>
4600                     //html = html.replace(/<span >([^>]*)<\/span>/gi, '$1');
4601                     //html = html.replace(/<div>([^>]*)<\/div>/gi, '$1');
4602             
4603             
4604             //Replace our backups with the real thing
4605                     html = html.replace(/<YUI_BR>/g, '<br>');
4606                     html = html.replace(/<YUI_IMG([^>]*)>/g, '<img$1>');
4607                     html = html.replace(/<YUI_UL([^>]*)>/g, '<ul$1>');
4608                     html = html.replace(/<\/YUI_UL>/g, '<\/ul>');
4610             return html;
4611         },
4612         /**
4613         * @method clearEditorDoc
4614         * @description Clear the doc of the Editor
4615         */
4616         clearEditorDoc: function() {
4617             this._getDoc().body.innerHTML = '&nbsp;';
4618         },
4619         /**
4620         * @private
4621         * @method _renderPanel
4622         * @description Renders the panel used for Editor Windows to the document so we can start using it..
4623         * @returns {<a href="YAHOO.widget.Panel.html">YAHOO.widget.Panel</a>}
4624         */
4625         _renderPanel: function() {
4626             if (!YAHOO.widget.EditorInfo.panel) {
4627                 var panel = new YAHOO.widget.Panel(this.EDITOR_PANEL_ID, {
4628                     width: '300px',
4629                     iframe: true,
4630                     visible: false,
4631                     underlay: 'none',
4632                     draggable: false,
4633                     close: false
4634                 });
4635                 YAHOO.widget.EditorInfo.panel = panel;
4636             } else {
4637                 var panel = YAHOO.widget.EditorInfo.panel;
4638             }
4639             this.set('panel', panel);
4641             this.get('panel').setBody('---');
4642             this.get('panel').setHeader(' ');
4643             this.get('panel').setFooter(' ');
4644             if (this.DOMReady) {
4645                 this.get('panel').render(document.body);
4646                 Dom.addClass(this.get('panel').element, 'yui-editor-panel');
4647             } else {
4648                 Event.onDOMReady(function() {
4649                     this.get('panel').render(document.body);
4650                     Dom.addClass(this.get('panel').element, 'yui-editor-panel');
4651                 }, this, true);
4652             }
4653             this.get('panel').showEvent.subscribe(function() {
4654                 YAHOO.util.Dom.setStyle(this.element, 'display', 'block');
4655             });
4656             return this.get('panel');
4657         },
4658         /**
4659         * @method openWindow
4660         * @param {<a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>} win A <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a> instance
4661         * @description Opens a new "window/panel"
4662         */
4663         openWindow: function(win) {
4664             this.toolbar.set('disabled', true); //Disable the toolbar when an editor window is open..
4665             Event.addListener(document, 'keypress', this._closeWindow, this, true);
4666             if (YAHOO.widget.EditorInfo.window.win && YAHOO.widget.EditorInfo.window.scope) {
4667                 YAHOO.widget.EditorInfo.window.scope.closeWindow.call(YAHOO.widget.EditorInfo.window.scope);
4668             }
4669             YAHOO.widget.EditorInfo.window.win = win;
4670             YAHOO.widget.EditorInfo.window.scope = this;
4672             var self = this,
4673                 xy = Dom.getXY(this.currentElement[0]),
4674                 elXY = Dom.getXY(this.get('iframe').get('element')),
4675                 panel = this.get('panel'),
4676                 newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
4677                 wWidth = (parseInt(win.attrs.width) / 2),
4678                 align = 'center';
4680             this.fireEvent('beforeOpenWindow', { type: 'beforeOpenWindow', win: win, panel: panel });
4682             body = document.createElement('div');
4683             body.className = this.CLASS_PREFIX + '-body-cont';
4685             var _note = document.createElement('h3');
4686             _note.className = 'yui-editor-skipheader';
4687             _note.innerHTML = this.STR_CLOSE_WINDOW_NOTE;
4688             body.appendChild(_note);
4689             form = document.createElement('form');
4690             form.setAttribute('method', 'GET');
4691             var windowName = win.name;
4692             Event.addListener(form, 'submit', function(ev) {
4693                 var evName = 'window' + windowName + 'Submit';
4694                 self.fireEvent(evName, { type: evName, target: this });
4695                 Event.stopEvent(ev);
4696             }, this, true);
4697             body.appendChild(form);
4699             Dom.setStyle(panel.element.firstChild, 'width', win.attrs.width);
4700             if (Lang.isObject(win.body)) { //Assume it's a reference
4701                 form.appendChild(win.body);
4702             } else { //Assume it's a string
4703                 var _tmp = document.createElement('div');
4704                 _tmp.innerHTML = win.body;
4705                 form.appendChild(_tmp);
4706             }
4707             var _close = document.createElement('span');
4708             _close.innerHTML = 'X';
4709             _close.title = this.STR_CLOSE_WINDOW;
4710             _close.className = 'close';
4711             Event.addListener(_close, 'click', function() {
4712                 this.closeWindow();
4713             }, this, true);
4714             var _knob = document.createElement('span');
4715             _knob.innerHTML = '^';
4716             _knob.className = 'knob';
4717             win._knob = _knob;
4719             var _header = document.createElement('h3');
4720             _header.innerHTML = win.header;
4722             panel.cfg.setProperty('width', win.attrs.width);
4723             panel.setHeader(' '); //Clear the current header
4724             panel.appendToHeader(_header);
4725             _header.appendChild(_close);
4726             _header.appendChild(_knob);
4727             panel.setBody(' '); //Clear the current body
4728             panel.setFooter(' '); //Clear the current footer
4729             if (win.footer != null) {
4730                 panel.setFooter(win.footer);
4731             }
4732             panel.appendToBody(body); //Append the new DOM node to it
4733             panel.showEvent.subscribe(function() {
4734                 Event.addListener(panel.element, 'click', function(ev) {
4735                     Event.stopPropagation(ev);
4736                 });
4737             }, this, true);
4738             panel.hideEvent.subscribe(function() {
4739                 this.currentWindow = null;
4740                 var evName = 'window' + windowName + 'Close';
4741                 this.fireEvent(evName, { type: evName, target: this });
4743             }, this, true);
4744             this.currentWindow = win;
4745             this.moveWindow(true);
4746             panel.show();
4747             this.fireEvent('afterOpenWindow', { type: 'afterOpenWindow', win: win, panel: panel });
4748         },
4749         /**
4750         * @method moveWindow
4751         * @param {Boolean} force Boolean to tell it to move but not use any animation (Usually done the first time the window is loaded.)
4752         * @description Realign the window with the currentElement and reposition the knob above the panel.
4753         */
4754         moveWindow: function(force) {
4755             if (!this.currentWindow) {
4756                 return false;
4757             }
4758             var win = this.currentWindow,
4759                 xy = Dom.getXY(this.currentElement[0]),
4760                 elXY = Dom.getXY(this.get('iframe').get('element')),
4761                 panel = this.get('panel'),
4762                 //newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
4763                 newXY = [(xy[0] + elXY[0]), (xy[1] + elXY[1])],
4764                 wWidth = (parseInt(win.attrs.width) / 2),
4765                 align = 'center',
4766                 orgXY = panel.cfg.getProperty('xy'),
4767                 _knob = win._knob;
4769             newXY[0] = ((newXY[0] - wWidth) + 20);
4770             //Account for the Scroll bars in a scrolled editor window.
4771             newXY[0] = newXY[0] - Dom.getDocumentScrollLeft(this._getDoc());
4772             newXY[1] = newXY[1] - Dom.getDocumentScrollTop(this._getDoc());
4773             
4776             if (this.currentElement[0].tagName && (this.currentElement[0].tagName.toLowerCase() == 'img')) {
4777                 if (this.currentElement[0].src.indexOf(this.get('blankimage')) != -1) {
4778                     newXY[0] = (newXY[0] + (75 / 2)); //Placeholder size
4779                     newXY[1] = (newXY[1] + 75); //Placeholder sizea
4780                 } else {
4781                     var w = parseInt(this.currentElement[0].width);
4782                     var h = parseInt(this.currentElement[0].height);
4783                     newXY[0] = (newXY[0] + (w / 2));
4784                     newXY[1] = (newXY[1] + h);
4785                 }
4786                 newXY[1] = newXY[1] + 15;
4787             } else {
4788                 if (Dom.getStyle(this.currentElement[0], 'fontSize').indexOf('px') != -1) {
4789                     newXY[1] = newXY[1] + parseInt(Dom.getStyle(this.currentElement[0], 'fontSize')) + 5;
4790                 } else {
4791                     newXY[1] = newXY[1] + 20;
4792                 }
4793             }
4794             if (newXY[0] < elXY[0]) {
4795                 newXY[0] = elXY[0] + 5;
4796                 align = 'left';
4797             }
4799             if ((newXY[0] + (wWidth * 2)) > (elXY[0] + parseInt(this.get('iframe').get('element').clientWidth))) {
4800                 newXY[0] = ((elXY[0] + parseInt(this.get('iframe').get('element').clientWidth)) - (wWidth * 2) - 5);
4801                 align = 'right';
4802             }
4803             
4804             try {
4805                 var xDiff = (newXY[0] - orgXY[0]);
4806                 var yDiff = (newXY[1] - orgXY[1]);
4807             } catch (e) {
4808                 var xDiff = 0;
4809                 var yDiff = 0;
4810             }
4811             
4812             //Convert negative numbers to positive so we can get the difference in distance
4813             xDiff = ((xDiff < 0) ? (xDiff * -1) : xDiff);
4814             yDiff = ((yDiff < 0) ? (yDiff * -1) : yDiff);
4816             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)
4817                 var _knobLeft = 0,
4818                     elW = 0;
4820                 if (this.currentElement[0].width) {
4821                     elW = (parseInt(this.currentElement[0].width) / 2);
4822                 }
4824                 var leftOffset = xy[0] + elXY[0] + elW;
4825                 _knobLeft = leftOffset - newXY[0];
4826                 //Check to see if the knob will go off either side & reposition it
4827                 if (_knobLeft > (parseInt(win.attrs.width) - 40)) {
4828                     _knobLeft = parseInt(win.attrs.width) - 40;
4829                 } else if (_knobLeft < 40) {
4830                     _knobLeft = 40;
4831                 }
4832                 if (isNaN(_knobLeft)) {
4833                     _knobLeft = 40;
4834                 }
4835                 if (force) {
4836                     if (_knob) {
4837                         _knob.style.left = _knobLeft + 'px';
4838                     }
4839                     if (this.get('animate')) {
4840                         Dom.setStyle(panel.element, 'opacity', '0');
4841                         var anim = new YAHOO.util.Anim(panel.element, {
4842                             opacity: {
4843                                 from: 0,
4844                                 to: 1
4845                             }
4846                         }, .1, YAHOO.util.Easing.easeOut);
4847                         panel.cfg.setProperty('xy', newXY);
4848                         anim.onComplete.subscribe(function() {
4849                             if (this.browser.ie) {
4850                                 panel.element.style.filter = 'none';
4851                             }
4852                         }, this, true);
4853                         anim.animate();
4854                     } else {
4855                         panel.cfg.setProperty('xy', newXY);
4856                     }
4857                 } else {
4858                     if (this.get('animate')) {
4859                         var anim = new YAHOO.util.Anim(panel.element, {}, .5, YAHOO.util.Easing.easeOut);
4860                         anim.attributes = {
4861                             top: {
4862                                 to: newXY[1]
4863                             },
4864                             left: {
4865                                 to: newXY[0]
4866                             }
4867                         }
4868                         anim.onComplete.subscribe(function() {
4869                             panel.cfg.setProperty('xy', newXY);
4870                         });
4871                         //We have to animate the iframe shim at the same time as the panel or we get scrollbar bleed ..
4872                         var iframeAnim = new YAHOO.util.Anim(panel.iframe, anim.attributes, .5, YAHOO.util.Easing.easeOut)
4874                         var _knobAnim = new YAHOO.util.Anim(_knob, {
4875                             left: {
4876                                 to: _knobLeft
4877                             }
4878                         }, .75, YAHOO.util.Easing.easeOut);
4879                         anim.animate();
4880                         iframeAnim.animate();
4881                         _knobAnim.animate();
4882                     } else {
4883                         _knob.style.left = _knobLeft + 'px';
4884                         panel.cfg.setProperty('xy', newXY);
4885                     }
4886                 }
4887             }
4888         },
4889         /**
4890         * @private
4891         * @method _closeWindow
4892         * @description Close the currently open EditorWindow with the Escape key.
4893         * @param {Event} ev The keypress Event that we are trapping
4894         */
4895         _closeWindow: function(ev) {
4896             if (ev.keyCode == 27) {
4897                 if (this.currentWindow) {
4898                     this.closeWindow();
4899                 }
4900             }
4901         },
4902         /**
4903         * @method closeWindow
4904         * @description Close the currently open EditorWindow.
4905         */
4906         closeWindow: function() {
4907             YAHOO.widget.EditorInfo.window = {};
4908             this.fireEvent('closeWindow', { type: 'closeWindow', win: this.currentWindow });
4909             this.currentWindow = null;
4910             this.get('panel').hide();
4911             this.get('panel').cfg.setProperty('xy', [-900,-900]);
4912             this.get('panel').syncIframe(); //Needed to move the iframe with the hidden panel
4913             this.unsubscribeAll('afterExecCommand');
4914             this.toolbar.set('disabled', false); //enable the toolbar now that the window is closed
4915             this._focusWindow();
4916             Event.removeListener(document, 'keypress', this._closeWindow);
4917         },
4918         /**
4919         * @method destroy
4920         * @description Destroys the editor, all of it's elements and objects.
4921         * @return {Boolean}
4922         */
4923         destroy: function() {
4924             this.saveHTML();
4925             this.toolbar.destroy();
4926             Dom.setStyle(this.get('textarea'), 'display', 'block');
4927             var textArea = this.get('textarea');
4928             this.get('element_cont').get('parentNode').replaceChild(textArea, this.get('element_cont').get('element'));
4929             this.get('element_cont').get('element').innerHTML = '';
4930             //Brutal Object Destroy
4931             for (var i in this) {
4932                 if (Lang.hasOwnProperty(this, i)) {
4933                     this[i] = null;
4934                 }
4935             }
4936             return true;
4937         },        
4938         /**
4939         * @method toString
4940         * @description Returns a string representing the editor.
4941         * @return {String}
4942         */
4943         toString: function() {
4944             var str = 'Editor';
4945             if (this.get && this.get('element_cont')) {
4946                 str = 'Editor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
4947             }
4948             return str;
4949         }
4950     });
4953 * @event toolbarLoaded
4954 * @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.
4955 * @type YAHOO.util.CustomEvent
4958 * @event afterRender
4959 * @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.
4960 * @type YAHOO.util.CustomEvent
4963 * @event editorContentLoaded
4964 * @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.
4965 * @type YAHOO.util.CustomEvent
4968 * @event editorMouseUp
4969 * @param {Event} ev The DOM Event that occured
4970 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
4971 * @type YAHOO.util.CustomEvent
4974 * @event editorMouseDown
4975 * @param {Event} ev The DOM Event that occured
4976 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
4977 * @type YAHOO.util.CustomEvent
4980 * @event editorDoubleClick
4981 * @param {Event} ev The DOM Event that occured
4982 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
4983 * @type YAHOO.util.CustomEvent
4986 * @event editorKeyUp
4987 * @param {Event} ev The DOM Event that occured
4988 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
4989 * @type YAHOO.util.CustomEvent
4992 * @event editorKeyPress
4993 * @param {Event} ev The DOM Event that occured
4994 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
4995 * @type YAHOO.util.CustomEvent
4998 * @event editorKeyDown
4999 * @param {Event} ev The DOM Event that occured
5000 * @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
5001 * @type YAHOO.util.CustomEvent
5004 * @event beforeNodeChange
5005 * @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.
5006 * @type YAHOO.util.CustomEvent
5009 * @event afterNodeChange
5010 * @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.
5011 * @type YAHOO.util.CustomEvent
5014 * @event beforeExecCommand
5015 * @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.
5016 * @type YAHOO.util.CustomEvent
5019 * @event afterExecCommand
5020 * @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.
5021 * @type YAHOO.util.CustomEvent
5024 * @event beforeOpenWindow
5025 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
5026 * @param {Overlay} panel The Overlay object that is used to create the window.
5027 * @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.
5028 * @type YAHOO.util.CustomEvent
5031 * @event afterOpenWindow
5032 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
5033 * @param {Overlay} panel The Overlay object that is used to create the window.
5034 * @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.
5035 * @type YAHOO.util.CustomEvent
5038 * @event closeWindow
5039 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
5040 * @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.
5041 * @type YAHOO.util.CustomEvent
5044 * @event windowCMDOpen
5045 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
5046 * @param {Overlay} panel The Overlay object that is used to create the window.
5047 * @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.
5048 * @type YAHOO.util.CustomEvent
5051 * @event windowCMDClose
5052 * @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
5053 * @param {Overlay} panel The Overlay object that is used to create the window.
5054 * @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.
5055 * @type YAHOO.util.CustomEvent
5059      * @description Singleton object used to track the open window objects and panels across the various open editors
5060      * @class EditorInfo
5061      * @static
5062     */
5063     YAHOO.widget.EditorInfo = {
5064         /**
5065         * @private
5066         * @property window
5067         * @description A reference to the currently open window object in any editor on the page.
5068         * @type Object <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>
5069         */
5070         window: {},
5071         /**
5072         * @private
5073         * @property panel
5074         * @description A reference to the currently open panel in any editor on the page.
5075         * @type Object <a href="YAHOO.widget.Panel.html">YAHOO.widget.Panel</a>
5076         */
5077         panel: null
5078     }
5080     /**
5081      * @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.
5082      * This is what you pass to Editor.openWindow();. These parameters will not take effect until the openWindow() is called in the editor.
5083      * @class EditorWindow
5084      * @param {String} name The name of the window.
5085      * @param {Object} attrs Attributes for the window. Current attributes used are : height and width
5086     */
5087     YAHOO.widget.EditorWindow = function(name, attrs) {
5088         /**
5089         * @private
5090         * @property name
5091         * @description A unique name for the window
5092         */
5093         this.name = name.replace(' ', '_');
5094         /**
5095         * @private
5096         * @property attrs
5097         * @description The window attributes
5098         */
5099         this.attrs = attrs;
5100     }
5102     YAHOO.widget.EditorWindow.prototype = {
5103         /**
5104         * @private
5105         * @property _cache
5106         * @description Holds a cache of the DOM for the window so we only have to build it once..
5107         */
5108         _cache: null,
5109         /**
5110         * @private
5111         * @property header
5112         * @description Holder for the header of the window, used in Editor.openWindow
5113         */
5114         header: null,
5115         /**
5116         * @private
5117         * @property body
5118         * @description Holder for the body of the window, used in Editor.openWindow
5119         */
5120         body: null,
5121         /**
5122         * @private
5123         * @property footer
5124         * @description Holder for the footer of the window, used in Editor.openWindow
5125         */
5126         footer: null,
5127         /**
5128         * @method setHeader
5129         * @description Sets the header for the window.
5130         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows header.
5131         */
5132         setHeader: function(str) {
5133             this.header = str;
5134         },
5135         /**
5136         * @method setBody
5137         * @description Sets the body for the window.
5138         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows body.
5139         */
5140         setBody: function(str) {
5141             this.body = str;
5142         },
5143         /**
5144         * @method setFooter
5145         * @description Sets the footer for the window.
5146         * @param {String/HTMLElement} str The string or DOM reference to be used as the windows footer.
5147         */
5148         setFooter: function(str) {
5149             this.footer = str;
5150         },
5151         /**
5152         * @method toString
5153         * @description Returns a string representing the EditorWindow.
5154         * @return {String}
5155         */
5156         toString: function() {
5157             return 'Editor Window (' + this.name + ')';
5158         }
5159     };
5162     
5163 })();
5164 YAHOO.register("editor", YAHOO.widget.Editor, {version: "2.3.0", build: "442"});