[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/2in3/2.9.0/build/yui2-editor/ -> yui2-editor.js (source)

   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"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1