Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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