[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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