[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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

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


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