[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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

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


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