/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.7.0 */ (function() { var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, Lang = YAHOO.lang; /** * @module editor * @description
Creates a rich custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar
* @class ToolbarButtonAdvanced * @namespace YAHOO.widget * @requires yahoo, dom, element, event, container_core, menu, button * * Provides a toolbar button based on the button and menu widgets. * @constructor * @class ToolbarButtonAdvanced * @param {String/HTMLElement} el The element to turn into a button. * @param {Object} attrs Object liternal containing configuration parameters. */ if (YAHOO.widget.Button) { YAHOO.widget.ToolbarButtonAdvanced = YAHOO.widget.Button; /** * @property buttonType * @private * @description Tells if the Button is a Rich Button or a Simple Button */ YAHOO.widget.ToolbarButtonAdvanced.prototype.buttonType = 'rich'; /** * @method checkValue * @param {String} value The value of the option that we want to mark as selected * @description Select an option by value */ YAHOO.widget.ToolbarButtonAdvanced.prototype.checkValue = function(value) { var _menuItems = this.getMenu().getItems(); if (_menuItems.length === 0) { this.getMenu()._onBeforeShow(); _menuItems = this.getMenu().getItems(); } for (var i = 0; i < _menuItems.length; i++) { _menuItems[i].cfg.setProperty('checked', false); if (_menuItems[i].value == value) { _menuItems[i].cfg.setProperty('checked', true); } } }; } else { YAHOO.widget.ToolbarButtonAdvanced = function() {}; } /** * @descriptionCreates a basic custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar
Provides a toolbar button based on the button and menu widgets, <select> elements are used in place of menu's.
* @class ToolbarButton * @namespace YAHOO.widget * @requires yahoo, dom, element, event * @extends YAHOO.util.Element * * * @constructor * @param {String/HTMLElement} el The element to turn into a button. * @param {Object} attrs Object liternal containing configuration parameters. */ YAHOO.widget.ToolbarButton = function(el, attrs) { YAHOO.log('ToolbarButton Initalizing', 'info', 'ToolbarButton'); YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar'); if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) { attrs = el; } var local_attrs = (attrs || {}); var oConfig = { element: null, attributes: local_attrs }; if (!oConfig.attributes.type) { oConfig.attributes.type = 'push'; } oConfig.element = document.createElement('span'); oConfig.element.setAttribute('unselectable', 'on'); oConfig.element.className = 'yui-button yui-' + oConfig.attributes.type + '-button'; oConfig.element.innerHTML = 'LABEL'; oConfig.element.firstChild.firstChild.tabIndex = '-1'; oConfig.attributes.id = (oConfig.attributes.id || Dom.generateId()); oConfig.element.id = oConfig.attributes.id; YAHOO.widget.ToolbarButton.superclass.constructor.call(this, oConfig.element, oConfig.attributes); }; YAHOO.extend(YAHOO.widget.ToolbarButton, YAHOO.util.Element, { /** * @property buttonType * @private * @description Tells if the Button is a Rich Button or a Simple Button */ buttonType: 'normal', /** * @method _handleMouseOver * @private * @description Adds classes to the button elements on mouseover (hover) */ _handleMouseOver: function() { if (!this.get('disabled')) { this.addClass('yui-button-hover'); this.addClass('yui-' + this.get('type') + '-button-hover'); } }, /** * @method _handleMouseOut * @private * @description Removes classes from the button elements on mouseout (hover) */ _handleMouseOut: function() { this.removeClass('yui-button-hover'); this.removeClass('yui-' + this.get('type') + '-button-hover'); }, /** * @method checkValue * @param {String} value The value of the option that we want to mark as selected * @description Select an option by value */ checkValue: function(value) { if (this.get('type') == 'menu') { var opts = this._button.options; for (var i = 0; i < opts.length; i++) { if (opts[i].value == value) { opts.selectedIndex = i; } } } }, /** * @method init * @description The ToolbarButton class's initialization method */ init: function(p_oElement, p_oAttributes) { YAHOO.widget.ToolbarButton.superclass.init.call(this, p_oElement, p_oAttributes); this.on('mouseover', this._handleMouseOver, this, true); this.on('mouseout', this._handleMouseOut, this, true); this.on('click', function(ev) { Event.stopEvent(ev); return false; }, this, true); }, /** * @method initAttributes * @description Initializes all of the configuration attributes used to create * the toolbar. * @param {Object} attr Object literal specifying a set of * configuration attributes used to create the toolbar. */ initAttributes: function(attr) { YAHOO.widget.ToolbarButton.superclass.initAttributes.call(this, attr); /** * @attribute value * @description The value of the button * @type String */ this.setAttributeConfig('value', { value: attr.value }); /** * @attribute menu * @description The menu attribute, see YAHOO.widget.Button * @type Object */ this.setAttributeConfig('menu', { value: attr.menu || false }); /** * @attribute type * @description The type of button to create: push, menu, color, select, spin * @type String */ this.setAttributeConfig('type', { value: attr.type, writeOnce: true, method: function(type) { var el, opt; if (!this._button) { this._button = this.get('element').getElementsByTagName('a')[0]; } switch (type) { case 'select': case 'menu': el = document.createElement('select'); var menu = this.get('menu'); for (var i = 0; i < menu.length; i++) { opt = document.createElement('option'); opt.innerHTML = menu[i].text; opt.value = menu[i].value; if (menu[i].checked) { opt.selected = true; } el.appendChild(opt); } this._button.parentNode.replaceChild(el, this._button); Event.on(el, 'change', this._handleSelect, this, true); this._button = el; break; } } }); /** * @attribute disabled * @description Set the button into a disabled state * @type String */ this.setAttributeConfig('disabled', { value: attr.disabled || false, method: function(disabled) { if (disabled) { this.addClass('yui-button-disabled'); this.addClass('yui-' + this.get('type') + '-button-disabled'); } else { this.removeClass('yui-button-disabled'); this.removeClass('yui-' + this.get('type') + '-button-disabled'); } if (this.get('type') == 'menu') { this._button.disabled = disabled; } } }); /** * @attribute label * @description The text label for the button * @type String */ this.setAttributeConfig('label', { value: attr.label, method: function(label) { if (!this._button) { this._button = this.get('element').getElementsByTagName('a')[0]; } if (this.get('type') == 'push') { this._button.innerHTML = label; } } }); /** * @attribute title * @description The title of the button * @type String */ this.setAttributeConfig('title', { value: attr.title }); /** * @config container * @description The container that the button is rendered to, handled by Toolbar * @type String */ this.setAttributeConfig('container', { value: null, writeOnce: true, method: function(cont) { this.appendTo(cont); } }); }, /** * @private * @method _handleSelect * @description The event fired when a change event gets fired on a select element * @param {Event} ev The change event. */ _handleSelect: function(ev) { var tar = Event.getTarget(ev); var value = tar.options[tar.selectedIndex].value; this.fireEvent('change', {type: 'change', value: value }); }, /** * @method getMenu * @description A stub function to mimic YAHOO.widget.Button's getMenu method */ getMenu: function() { return this.get('menu'); }, /** * @method destroy * @description Destroy the button */ destroy: function() { Event.purgeElement(this.get('element'), true); this.get('element').parentNode.removeChild(this.get('element')); //Brutal Object Destroy for (var i in this) { if (Lang.hasOwnProperty(this, i)) { this[i] = null; } } }, /** * @method fireEvent * @description Overridden fireEvent method to prevent DOM events from firing if the button is disabled. */ fireEvent: function(p_sType, p_aArgs) { // Disabled buttons should not respond to DOM events if (this.DOM_EVENTS[p_sType] && this.get('disabled')) { Event.stopEvent(p_aArgs); return; } YAHOO.widget.ToolbarButton.superclass.fireEvent.call(this, p_sType, p_aArgs); }, /** * @method toString * @description Returns a string representing the toolbar. * @return {String} */ toString: function() { return 'ToolbarButton (' + this.get('id') + ')'; } }); })(); /** * @module editor * @descriptionCreates a rich Toolbar widget based on Button. Primarily used with the Rich Text Editor
* @namespace YAHOO.widget * @requires yahoo, dom, element, event, toolbarbutton * @optional container_core, dragdrop */ (function() { var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, Lang = YAHOO.lang; var getButton = function(id) { var button = id; if (Lang.isString(id)) { button = this.getButtonById(id); } if (Lang.isNumber(id)) { button = this.getButtonByIndex(id); } if ((!(button instanceof YAHOO.widget.ToolbarButton)) && (!(button instanceof YAHOO.widget.ToolbarButtonAdvanced))) { button = this.getButtonByValue(id); } if ((button instanceof YAHOO.widget.ToolbarButton) || (button instanceof YAHOO.widget.ToolbarButtonAdvanced)) { return button; } return false; }; /** * Provides a rich toolbar widget based on the button and menu widgets * @constructor * @class Toolbar * @extends YAHOO.util.Element * @param {String/HTMLElement} el The element to turn into a toolbar. * @param {Object} attrs Object liternal containing configuration parameters. */ YAHOO.widget.Toolbar = function(el, attrs) { YAHOO.log('Toolbar Initalizing', 'info', 'Toolbar'); YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar'); if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) { attrs = el; } var local_attrs = {}; if (attrs) { Lang.augmentObject(local_attrs, attrs); //Break the config reference } var oConfig = { element: null, attributes: local_attrs }; if (Lang.isString(el) && Dom.get(el)) { oConfig.element = Dom.get(el); } else if (Lang.isObject(el) && Dom.get(el) && Dom.get(el).nodeType) { oConfig.element = Dom.get(el); } if (!oConfig.element) { YAHOO.log('No element defined, creating toolbar container', 'warn', 'Toolbar'); oConfig.element = document.createElement('DIV'); oConfig.element.id = Dom.generateId(); if (local_attrs.container && Dom.get(local_attrs.container)) { YAHOO.log('Container found in config appending to it (' + Dom.get(local_attrs.container).id + ')', 'info', 'Toolbar'); Dom.get(local_attrs.container).appendChild(oConfig.element); } } if (!oConfig.element.id) { oConfig.element.id = ((Lang.isString(el)) ? el : Dom.generateId()); YAHOO.log('No element ID defined for toolbar container, creating..', 'warn', 'Toolbar'); } YAHOO.log('Initing toolbar with id: ' + oConfig.element.id, 'info', 'Toolbar'); var fs = document.createElement('fieldset'); var lg = document.createElement('legend'); lg.innerHTML = 'Toolbar'; fs.appendChild(lg); var cont = document.createElement('DIV'); oConfig.attributes.cont = cont; Dom.addClass(cont, 'yui-toolbar-subcont'); fs.appendChild(cont); oConfig.element.appendChild(fs); oConfig.element.tabIndex = -1; oConfig.attributes.element = oConfig.element; oConfig.attributes.id = oConfig.element.id; YAHOO.widget.Toolbar.superclass.constructor.call(this, oConfig.element, oConfig.attributes); }; YAHOO.extend(YAHOO.widget.Toolbar, YAHOO.util.Element, { /** * @method _addMenuClasses * @private * @description This method is called from Menu's renderEvent to add a few more classes to the menu items * @param {String} ev The event that fired. * @param {Array} na Array of event information. * @param {Object} o Button config object. */ _addMenuClasses: function(ev, na, o) { Dom.addClass(this.element, 'yui-toolbar-' + o.get('value') + '-menu'); if (Dom.hasClass(o._button.parentNode.parentNode, 'yui-toolbar-select')) { Dom.addClass(this.element, 'yui-toolbar-select-menu'); } var items = this.getItems(); for (var i = 0; i < items.length; i++) { 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())); Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-') : items[i]._oText.nodeValue.replace(/ /g, '-'))); } }, /** * @property buttonType * @description The default button to use * @type Object */ buttonType: YAHOO.widget.ToolbarButton, /** * @property dd * @description The DragDrop instance associated with the Toolbar * @type Object */ dd: null, /** * @property _colorData * @description Object reference containing colors hex and text values. * @type Object */ _colorData: { /* {{{ _colorData */ '#111111': 'Obsidian', '#2D2D2D': 'Dark Gray', '#434343': 'Shale', '#5B5B5B': 'Flint', '#737373': 'Gray', '#8B8B8B': 'Concrete', '#A2A2A2': 'Gray', '#B9B9B9': 'Titanium', '#000000': 'Black', '#D0D0D0': 'Light Gray', '#E6E6E6': 'Silver', '#FFFFFF': 'White', '#BFBF00': 'Pumpkin', '#FFFF00': 'Yellow', '#FFFF40': 'Banana', '#FFFF80': 'Pale Yellow', '#FFFFBF': 'Butter', '#525330': 'Raw Siena', '#898A49': 'Mildew', '#AEA945': 'Olive', '#7F7F00': 'Paprika', '#C3BE71': 'Earth', '#E0DCAA': 'Khaki', '#FCFAE1': 'Cream', '#60BF00': 'Cactus', '#80FF00': 'Chartreuse', '#A0FF40': 'Green', '#C0FF80': 'Pale Lime', '#DFFFBF': 'Light Mint', '#3B5738': 'Green', '#668F5A': 'Lime Gray', '#7F9757': 'Yellow', '#407F00': 'Clover', '#8A9B55': 'Pistachio', '#B7C296': 'Light Jade', '#E6EBD5': 'Breakwater', '#00BF00': 'Spring Frost', '#00FF80': 'Pastel Green', '#40FFA0': 'Light Emerald', '#80FFC0': 'Sea Foam', '#BFFFDF': 'Sea Mist', '#033D21': 'Dark Forrest', '#438059': 'Moss', '#7FA37C': 'Medium Green', '#007F40': 'Pine', '#8DAE94': 'Yellow Gray Green', '#ACC6B5': 'Aqua Lung', '#DDEBE2': 'Sea Vapor', '#00BFBF': 'Fog', '#00FFFF': 'Cyan', '#40FFFF': 'Turquoise Blue', '#80FFFF': 'Light Aqua', '#BFFFFF': 'Pale Cyan', '#033D3D': 'Dark Teal', '#347D7E': 'Gray Turquoise', '#609A9F': 'Green Blue', '#007F7F': 'Seaweed', '#96BDC4': 'Green Gray', '#B5D1D7': 'Soapstone', '#E2F1F4': 'Light Turquoise', '#0060BF': 'Summer Sky', '#0080FF': 'Sky Blue', '#40A0FF': 'Electric Blue', '#80C0FF': 'Light Azure', '#BFDFFF': 'Ice Blue', '#1B2C48': 'Navy', '#385376': 'Biscay', '#57708F': 'Dusty Blue', '#00407F': 'Sea Blue', '#7792AC': 'Sky Blue Gray', '#A8BED1': 'Morning Sky', '#DEEBF6': 'Vapor', '#0000BF': 'Deep Blue', '#0000FF': 'Blue', '#4040FF': 'Cerulean Blue', '#8080FF': 'Evening Blue', '#BFBFFF': 'Light Blue', '#212143': 'Deep Indigo', '#373E68': 'Sea Blue', '#444F75': 'Night Blue', '#00007F': 'Indigo Blue', '#585E82': 'Dockside', '#8687A4': 'Blue Gray', '#D2D1E1': 'Light Blue Gray', '#6000BF': 'Neon Violet', '#8000FF': 'Blue Violet', '#A040FF': 'Violet Purple', '#C080FF': 'Violet Dusk', '#DFBFFF': 'Pale Lavender', '#302449': 'Cool Shale', '#54466F': 'Dark Indigo', '#655A7F': 'Dark Violet', '#40007F': 'Violet', '#726284': 'Smoky Violet', '#9E8FA9': 'Slate Gray', '#DCD1DF': 'Violet White', '#BF00BF': 'Royal Violet', '#FF00FF': 'Fuchsia', '#FF40FF': 'Magenta', '#FF80FF': 'Orchid', '#FFBFFF': 'Pale Magenta', '#4A234A': 'Dark Purple', '#794A72': 'Medium Purple', '#936386': 'Cool Granite', '#7F007F': 'Purple', '#9D7292': 'Purple Moon', '#C0A0B6': 'Pale Purple', '#ECDAE5': 'Pink Cloud', '#BF005F': 'Hot Pink', '#FF007F': 'Deep Pink', '#FF409F': 'Grape', '#FF80BF': 'Electric Pink', '#FFBFDF': 'Pink', '#451528': 'Purple Red', '#823857': 'Purple Dino', '#A94A76': 'Purple Gray', '#7F003F': 'Rose', '#BC6F95': 'Antique Mauve', '#D8A5BB': 'Cool Marble', '#F7DDE9': 'Pink Granite', '#C00000': 'Apple', '#FF0000': 'Fire Truck', '#FF4040': 'Pale Red', '#FF8080': 'Salmon', '#FFC0C0': 'Warm Pink', '#441415': 'Sepia', '#82393C': 'Rust', '#AA4D4E': 'Brick', '#800000': 'Brick Red', '#BC6E6E': 'Mauve', '#D8A3A4': 'Shrimp Pink', '#F8DDDD': 'Shell Pink', '#BF5F00': 'Dark Orange', '#FF7F00': 'Orange', '#FF9F40': 'Grapefruit', '#FFBF80': 'Canteloupe', '#FFDFBF': 'Wax', '#482C1B': 'Dark Brick', '#855A40': 'Dirt', '#B27C51': 'Tan', '#7F3F00': 'Nutmeg', '#C49B71': 'Mustard', '#E1C4A8': 'Pale Tan', '#FDEEE0': 'Marble' /* }}} */ }, /** * @property _colorPicker * @description The HTML Element containing the colorPicker * @type HTMLElement */ _colorPicker: null, /** * @property STR_COLLAPSE * @description String for Toolbar Collapse Button * @type String */ STR_COLLAPSE: 'Collapse Toolbar', /** * @property STR_SPIN_LABEL * @description String for spinbutton dynamic label. Note the {VALUE} will be replaced with YAHOO.lang.substitute * @type String */ 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.', /** * @property STR_SPIN_UP * @description String for spinbutton up * @type String */ STR_SPIN_UP: 'Click to increase the value of this input', /** * @property STR_SPIN_DOWN * @description String for spinbutton down * @type String */ STR_SPIN_DOWN: 'Click to decrease the value of this input', /** * @property _titlebar * @description Object reference to the titlebar * @type HTMLElement */ _titlebar: null, /** * @property browser * @description Standard browser detection * @type Object */ browser: YAHOO.env.ua, /** * @protected * @property _buttonList * @description Internal property list of current buttons in the toolbar * @type Array */ _buttonList: null, /** * @protected * @property _buttonGroupList * @description Internal property list of current button groups in the toolbar * @type Array */ _buttonGroupList: null, /** * @protected * @property _sep * @description Internal reference to the separator HTML Element for cloning * @type HTMLElement */ _sep: null, /** * @protected * @property _sepCount * @description Internal refernce for counting separators, so we can give them a useful class name for styling * @type Number */ _sepCount: null, /** * @protected * @property draghandle * @type HTMLElement */ _dragHandle: null, /** * @protected * @property _toolbarConfigs * @type Object */ _toolbarConfigs: { renderer: true }, /** * @protected * @property CLASS_CONTAINER * @description Default CSS class to apply to the toolbar container element * @type String */ CLASS_CONTAINER: 'yui-toolbar-container', /** * @protected * @property CLASS_DRAGHANDLE * @description Default CSS class to apply to the toolbar's drag handle element * @type String */ CLASS_DRAGHANDLE: 'yui-toolbar-draghandle', /** * @protected * @property CLASS_SEPARATOR * @description Default CSS class to apply to all separators in the toolbar * @type String */ CLASS_SEPARATOR: 'yui-toolbar-separator', /** * @protected * @property CLASS_DISABLED * @description Default CSS class to apply when the toolbar is disabled * @type String */ CLASS_DISABLED: 'yui-toolbar-disabled', /** * @protected * @property CLASS_PREFIX * @description Default prefix for dynamically created class names * @type String */ CLASS_PREFIX: 'yui-toolbar', /** * @method init * @description The Toolbar class's initialization method */ init: function(p_oElement, p_oAttributes) { YAHOO.widget.Toolbar.superclass.init.call(this, p_oElement, p_oAttributes); }, /** * @method initAttributes * @description Initializes all of the configuration attributes used to create * the toolbar. * @param {Object} attr Object literal specifying a set of * configuration attributes used to create the toolbar. */ initAttributes: function(attr) { YAHOO.widget.Toolbar.superclass.initAttributes.call(this, attr); this.addClass(this.CLASS_CONTAINER); /** * @attribute buttonType * @description The buttonType to use (advanced or basic) * @type String */ this.setAttributeConfig('buttonType', { value: attr.buttonType || 'basic', writeOnce: true, validator: function(type) { switch (type) { case 'advanced': case 'basic': return true; } return false; }, method: function(type) { if (type == 'advanced') { if (YAHOO.widget.Button) { this.buttonType = YAHOO.widget.ToolbarButtonAdvanced; } else { YAHOO.log('Can not find YAHOO.widget.Button', 'error', 'Toolbar'); this.buttonType = YAHOO.widget.ToolbarButton; } } else { this.buttonType = YAHOO.widget.ToolbarButton; } } }); /** * @attribute buttons * @description Object specifying the buttons to include in the toolbar * Example: *
* {
* { id: 'b3', type: 'button', label: 'Underline', value: 'underline' },
* { type: 'separator' },
* { id: 'b4', type: 'menu', label: 'Align', value: 'align',
* menu: [
* { text: "Left", value: 'alignleft' },
* { text: "Center", value: 'aligncenter' },
* { text: "Right", value: 'alignright' }
* ]
* }
* }
*
* @type Array
*/
this.setAttributeConfig('buttons', {
value: [],
writeOnce: true,
method: function(data) {
for (var i in data) {
if (Lang.hasOwnProperty(data, i)) {
if (data[i].type == 'separator') {
this.addSeparator();
} else if (data[i].group !== undefined) {
this.addButtonGroup(data[i]);
} else {
this.addButton(data[i]);
}
}
}
}
});
/**
* @attribute disabled
* @description Boolean indicating if the toolbar should be disabled. It will also disable the draggable attribute if it is on.
* @default false
* @type Boolean
*/
this.setAttributeConfig('disabled', {
value: false,
method: function(disabled) {
if (this.get('disabled') === disabled) {
return false;
}
if (disabled) {
this.addClass(this.CLASS_DISABLED);
this.set('draggable', false);
this.disableAllButtons();
} else {
this.removeClass(this.CLASS_DISABLED);
if (this._configs.draggable._initialConfig.value) {
//Draggable by default, set it back
this.set('draggable', true);
}
this.resetAllButtons();
}
}
});
/**
* @config cont
* @description The container for the toolbar.
* @type HTMLElement
*/
this.setAttributeConfig('cont', {
value: attr.cont,
readOnly: true
});
/**
* @attribute grouplabels
* @description Boolean indicating if the toolbar should show the group label's text string.
* @default true
* @type Boolean
*/
this.setAttributeConfig('grouplabels', {
value: ((attr.grouplabels === false) ? false : true),
method: function(grouplabels) {
if (grouplabels) {
Dom.removeClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
} else {
Dom.addClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
}
}
});
/**
* @attribute titlebar
* @description Boolean indicating if the toolbar should have a titlebar. If
* passed a string, it will use that as the titlebar text
* @default false
* @type Boolean or String
*/
this.setAttributeConfig('titlebar', {
value: false,
method: function(titlebar) {
if (titlebar) {
if (this._titlebar && this._titlebar.parentNode) {
this._titlebar.parentNode.removeChild(this._titlebar);
}
this._titlebar = document.createElement('DIV');
this._titlebar.tabIndex = '-1';
Event.on(this._titlebar, 'focus', function() {
this._handleFocus();
}, this, true);
Dom.addClass(this._titlebar, this.CLASS_PREFIX + '-titlebar');
if (Lang.isString(titlebar)) {
var h2 = document.createElement('h2');
h2.tabIndex = '-1';
h2.innerHTML = '' + titlebar + '';
this._titlebar.appendChild(h2);
Event.on(h2.firstChild, 'click', function(ev) {
Event.stopEvent(ev);
});
Event.on([h2, h2.firstChild], 'focus', function() {
this._handleFocus();
}, this, true);
}
if (this.get('firstChild')) {
this.insertBefore(this._titlebar, this.get('firstChild'));
} else {
this.appendChild(this._titlebar);
}
if (this.get('collapse')) {
this.set('collapse', true);
}
} else if (this._titlebar) {
if (this._titlebar && this._titlebar.parentNode) {
this._titlebar.parentNode.removeChild(this._titlebar);
}
}
}
});
/**
* @attribute collapse
* @description Boolean indicating if the the titlebar should have a collapse button.
* The collapse button will not remove the toolbar, it will minimize it to the titlebar
* @default false
* @type Boolean
*/
this.setAttributeConfig('collapse', {
value: false,
method: function(collapse) {
if (this._titlebar) {
var collapseEl = null;
var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
if (collapse) {
if (el.length > 0) {
//There is already a collapse button
return true;
}
collapseEl = document.createElement('SPAN');
collapseEl.innerHTML = 'X';
collapseEl.title = this.STR_COLLAPSE;
Dom.addClass(collapseEl, 'collapse');
this._titlebar.appendChild(collapseEl);
Event.addListener(collapseEl, 'click', function() {
if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
this.collapse(false); //Expand Toolbar
} else {
this.collapse(); //Collapse Toolbar
}
}, this, true);
} else {
collapseEl = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
if (collapseEl[0]) {
if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
//We are closed, reopen the titlebar..
this.collapse(false); //Expand Toolbar
}
collapseEl[0].parentNode.removeChild(collapseEl[0]);
}
}
}
}
});
/**
* @attribute draggable
* @description Boolean indicating if the toolbar should be draggable.
* @default false
* @type Boolean
*/
this.setAttributeConfig('draggable', {
value: (attr.draggable || false),
method: function(draggable) {
if (draggable && !this.get('titlebar')) {
YAHOO.log('Dragging enabled', 'info', 'Toolbar');
if (!this._dragHandle) {
this._dragHandle = document.createElement('SPAN');
this._dragHandle.innerHTML = '|';
this._dragHandle.setAttribute('title', 'Click to drag the toolbar');
this._dragHandle.id = this.get('id') + '_draghandle';
Dom.addClass(this._dragHandle, this.CLASS_DRAGHANDLE);
if (this.get('cont').hasChildNodes()) {
this.get('cont').insertBefore(this._dragHandle, this.get('cont').firstChild);
} else {
this.get('cont').appendChild(this._dragHandle);
}
this.dd = new YAHOO.util.DD(this.get('id'));
this.dd.setHandleElId(this._dragHandle.id);
}
} else {
YAHOO.log('Dragging disabled', 'info', 'Toolbar');
if (this._dragHandle) {
this._dragHandle.parentNode.removeChild(this._dragHandle);
this._dragHandle = null;
this.dd = null;
}
}
if (this._titlebar) {
if (draggable) {
this.dd = new YAHOO.util.DD(this.get('id'));
this.dd.setHandleElId(this._titlebar);
Dom.addClass(this._titlebar, 'draggable');
} else {
Dom.removeClass(this._titlebar, 'draggable');
if (this.dd) {
this.dd.unreg();
this.dd = null;
}
}
}
},
validator: function(value) {
var ret = true;
if (!YAHOO.util.DD) {
ret = false;
}
return ret;
}
});
},
/**
* @method addButtonGroup
* @description Add a new button group to the toolbar. (uses addButton)
* @param {Object} oGroup Object literal reference to the Groups Config (contains an array of button configs as well as the group label)
*/
addButtonGroup: function(oGroup) {
if (!this.get('element')) {
this._queue[this._queue.length] = ['addButtonGroup', arguments];
return false;
}
if (!this.hasClass(this.CLASS_PREFIX + '-grouped')) {
this.addClass(this.CLASS_PREFIX + '-grouped');
}
var div = document.createElement('DIV');
Dom.addClass(div, this.CLASS_PREFIX + '-group');
Dom.addClass(div, this.CLASS_PREFIX + '-group-' + oGroup.group);
if (oGroup.label) {
var label = document.createElement('h3');
label.innerHTML = oGroup.label;
div.appendChild(label);
}
if (!this.get('grouplabels')) {
Dom.addClass(this.get('cont'), this.CLASS_PREFIX, '-nogrouplabels');
}
this.get('cont').appendChild(div);
//For accessibility, let's put all of the group buttons in an Unordered List
var ul = document.createElement('ul');
div.appendChild(ul);
if (!this._buttonGroupList) {
this._buttonGroupList = {};
}
this._buttonGroupList[oGroup.group] = ul;
for (var i = 0; i < oGroup.buttons.length; i++) {
var li = document.createElement('li');
li.className = this.CLASS_PREFIX + '-groupitem';
ul.appendChild(li);
if ((oGroup.buttons[i].type !== undefined) && oGroup.buttons[i].type == 'separator') {
this.addSeparator(li);
} else {
oGroup.buttons[i].container = li;
this.addButton(oGroup.buttons[i]);
}
}
},
/**
* @method addButtonToGroup
* @description Add a new button to a toolbar group. Buttons supported:
* push, split, menu, select, color, spin
* @param {Object} oButton Object literal reference to the Button's Config
* @param {String} group The Group identifier passed into the initial config
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
*/
addButtonToGroup: function(oButton, group, after) {
var groupCont = this._buttonGroupList[group];
var li = document.createElement('li');
li.className = this.CLASS_PREFIX + '-groupitem';
oButton.container = li;
this.addButton(oButton, after);
groupCont.appendChild(li);
},
/**
* @method addButton
* @description Add a new button to the toolbar. Buttons supported:
* push, split, menu, select, color, spin
* @param {Object} oButton Object literal reference to the Button's Config
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
*/
addButton: function(oButton, after) {
if (!this.get('element')) {
this._queue[this._queue.length] = ['addButton', arguments];
return false;
}
if (!this._buttonList) {
this._buttonList = [];
}
YAHOO.log('Adding button of type: ' + oButton.type, 'info', 'Toolbar');
if (!oButton.container) {
oButton.container = this.get('cont');
}
if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
if (Lang.isArray(oButton.menu)) {
for (var i in oButton.menu) {
if (Lang.hasOwnProperty(oButton.menu, i)) {
var funcObject = {
fn: function(ev, x, oMenu) {
if (!oButton.menucmd) {
oButton.menucmd = oButton.value;
}
oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
},
scope: this
};
oButton.menu[i].onclick = funcObject;
}
}
}
}
var _oButton = {}, skip = false;
for (var o in oButton) {
if (Lang.hasOwnProperty(oButton, o)) {
if (!this._toolbarConfigs[o]) {
_oButton[o] = oButton[o];
}
}
}
if (oButton.type == 'select') {
_oButton.type = 'menu';
}
if (oButton.type == 'spin') {
_oButton.type = 'push';
}
if (_oButton.type == 'color') {
if (YAHOO.widget.Overlay) {
_oButton = this._makeColorButton(_oButton);
} else {
skip = true;
}
}
if (_oButton.menu) {
if ((YAHOO.widget.Overlay) && (oButton.menu instanceof YAHOO.widget.Overlay)) {
oButton.menu.showEvent.subscribe(function() {
this._button = _oButton;
});
} else {
for (var m = 0; m < _oButton.menu.length; m++) {
if (!_oButton.menu[m].value) {
_oButton.menu[m].value = _oButton.menu[m].text;
}
}
if (this.browser.webkit) {
_oButton.focusmenu = false;
}
}
}
if (skip) {
oButton = false;
} else {
//Add to .get('buttons') manually
this._configs.buttons.value[this._configs.buttons.value.length] = oButton;
var tmp = new this.buttonType(_oButton);
tmp.get('element').tabIndex = '-1';
tmp.get('element').setAttribute('role', 'button');
tmp._selected = true;
if (this.get('disabled')) {
//Toolbar is disabled, disable the new button too!
tmp.set('disabled', true);
}
if (!oButton.id) {
oButton.id = tmp.get('id');
}
YAHOO.log('Button created (' + oButton.type + ')', 'info', 'Toolbar');
if (after) {
var el = tmp.get('element');
var nextSib = null;
if (after.get) {
nextSib = after.get('element').nextSibling;
} else if (after.nextSibling) {
nextSib = after.nextSibling;
}
if (nextSib) {
nextSib.parentNode.insertBefore(el, nextSib);
}
}
tmp.addClass(this.CLASS_PREFIX + '-' + tmp.get('value'));
var icon = document.createElement('span');
icon.className = this.CLASS_PREFIX + '-icon';
tmp.get('element').insertBefore(icon, tmp.get('firstChild'));
if (tmp._button.tagName.toLowerCase() == 'button') {
tmp.get('element').setAttribute('unselectable', 'on');
//Replace the Button HTML Element with an a href if it exists
var a = document.createElement('a');
a.innerHTML = tmp._button.innerHTML;
a.href = '#';
a.tabIndex = '-1';
Event.on(a, 'click', function(ev) {
Event.stopEvent(ev);
});
tmp._button.parentNode.replaceChild(a, tmp._button);
tmp._button = a;
}
if (oButton.type == 'select') {
if (tmp._button.tagName.toLowerCase() == 'select') {
icon.parentNode.removeChild(icon);
var iel = tmp._button;
var parEl = tmp.get('element');
parEl.parentNode.replaceChild(iel, parEl);
} else {
//Don't put a class on it if it's a real select element
tmp.addClass(this.CLASS_PREFIX + '-select');
}
}
if (oButton.type == 'spin') {
if (!Lang.isArray(oButton.range)) {
oButton.range = [ 10, 100 ];
}
this._makeSpinButton(tmp, oButton);
}
tmp.get('element').setAttribute('title', tmp.get('label'));
if (oButton.type != 'spin') {
if ((YAHOO.widget.Overlay) && (_oButton.menu instanceof YAHOO.widget.Overlay)) {
var showPicker = function(ev) {
var exec = true;
if (ev.keyCode && (ev.keyCode == 9)) {
exec = false;
}
if (exec) {
if (this._colorPicker) {
this._colorPicker._button = oButton.value;
}
var menuEL = tmp.getMenu().element;
if (Dom.getStyle(menuEL, 'visibility') == 'hidden') {
tmp.getMenu().show();
} else {
tmp.getMenu().hide();
}
}
YAHOO.util.Event.stopEvent(ev);
};
tmp.on('mousedown', showPicker, oButton, this);
tmp.on('keydown', showPicker, oButton, this);
} else if ((oButton.type != 'menu') && (oButton.type != 'select')) {
tmp.on('keypress', this._buttonClick, oButton, this);
tmp.on('mousedown', function(ev) {
YAHOO.util.Event.stopEvent(ev);
this._buttonClick(ev, oButton);
}, oButton, this);
tmp.on('click', function(ev) {
YAHOO.util.Event.stopEvent(ev);
});
} else {
//Stop the mousedown event so we can trap the selection in the editor!
tmp.on('mousedown', function(ev) {
YAHOO.util.Event.stopEvent(ev);
});
tmp.on('click', function(ev) {
YAHOO.util.Event.stopEvent(ev);
});
tmp.on('change', function(ev) {
if (!oButton.menucmd) {
oButton.menucmd = oButton.value;
}
oButton.value = ev.value;
this._buttonClick(ev, oButton);
}, this, true);
var self = this;
//Hijack the mousedown event in the menu and make it fire a button click..
tmp.on('appendTo', function() {
var tmp = this;
if (tmp.getMenu() && tmp.getMenu().mouseDownEvent) {
tmp.getMenu().mouseDownEvent.subscribe(function(ev, args) {
YAHOO.log('mouseDownEvent', 'warn', 'Toolbar');
var oMenu = args[1];
YAHOO.util.Event.stopEvent(args[0]);
tmp._onMenuClick(args[0], tmp);
if (!oButton.menucmd) {
oButton.menucmd = oButton.value;
}
oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
self._buttonClick.call(self, args[1], oButton);
tmp._hideMenu();
return false;
});
tmp.getMenu().clickEvent.subscribe(function(ev, args) {
YAHOO.log('clickEvent', 'warn', 'Toolbar');
YAHOO.util.Event.stopEvent(args[0]);
});
tmp.getMenu().mouseUpEvent.subscribe(function(ev, args) {
YAHOO.log('mouseUpEvent', 'warn', 'Toolbar');
YAHOO.util.Event.stopEvent(args[0]);
});
}
});
}
} else {
//Stop the mousedown event so we can trap the selection in the editor!
tmp.on('mousedown', function(ev) {
YAHOO.util.Event.stopEvent(ev);
});
tmp.on('click', function(ev) {
YAHOO.util.Event.stopEvent(ev);
});
}
if (this.browser.ie) {
/*
//Add a couple of new events for IE
tmp.DOM_EVENTS.focusin = true;
tmp.DOM_EVENTS.focusout = true;
//Stop them so we don't loose focus in the Editor
tmp.on('focusin', function(ev) {
YAHOO.util.Event.stopEvent(ev);
}, oButton, this);
tmp.on('focusout', function(ev) {
YAHOO.util.Event.stopEvent(ev);
}, oButton, this);
tmp.on('click', function(ev) {
YAHOO.util.Event.stopEvent(ev);
}, oButton, this);
*/
}
if (this.browser.webkit) {
//This will keep the document from gaining focus and the editor from loosing it..
//Forcefully remove the focus calls in button!
tmp.hasFocus = function() {
return true;
};
}
this._buttonList[this._buttonList.length] = tmp;
if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
if (Lang.isArray(oButton.menu)) {
YAHOO.log('Button type is (' + oButton.type + '), doing extra renderer work.', 'info', 'Toolbar');
var menu = tmp.getMenu();
if (menu && menu.renderEvent) {
menu.renderEvent.subscribe(this._addMenuClasses, tmp);
if (oButton.renderer) {
menu.renderEvent.subscribe(oButton.renderer, tmp);
}
}
}
}
}
return oButton;
},
/**
* @method addSeparator
* @description Add a new button separator to the toolbar.
* @param {HTMLElement} cont Optional HTML element to insert this button into.
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
*/
addSeparator: function(cont, after) {
if (!this.get('element')) {
this._queue[this._queue.length] = ['addSeparator', arguments];
return false;
}
var sepCont = ((cont) ? cont : this.get('cont'));
if (!this.get('element')) {
this._queue[this._queue.length] = ['addSeparator', arguments];
return false;
}
if (this._sepCount === null) {
this._sepCount = 0;
}
if (!this._sep) {
YAHOO.log('Separator does not yet exist, creating', 'info', 'Toolbar');
this._sep = document.createElement('SPAN');
Dom.addClass(this._sep, this.CLASS_SEPARATOR);
this._sep.innerHTML = '|';
}
YAHOO.log('Separator does exist, cloning', 'info', 'Toolbar');
var _sep = this._sep.cloneNode(true);
this._sepCount++;
Dom.addClass(_sep, this.CLASS_SEPARATOR + '-' + this._sepCount);
if (after) {
var nextSib = null;
if (after.get) {
nextSib = after.get('element').nextSibling;
} else if (after.nextSibling) {
nextSib = after.nextSibling;
} else {
nextSib = after;
}
if (nextSib) {
if (nextSib == after) {
nextSib.parentNode.appendChild(_sep);
} else {
nextSib.parentNode.insertBefore(_sep, nextSib);
}
}
} else {
sepCont.appendChild(_sep);
}
return _sep;
},
/**
* @method _createColorPicker
* @private
* @description Creates the core DOM reference to the color picker menu item.
* @param {String} id the id of the toolbar to prefix this DOM container with.
*/
_createColorPicker: function(id) {
if (Dom.get(id + '_colors')) {
Dom.get(id + '_colors').parentNode.removeChild(Dom.get(id + '_colors'));
}
var picker = document.createElement('div');
picker.className = 'yui-toolbar-colors';
picker.id = id + '_colors';
picker.style.display = 'none';
Event.on(window, 'load', function() {
document.body.appendChild(picker);
}, this, true);
this._colorPicker = picker;
var html = '';
for (var i in this._colorData) {
if (Lang.hasOwnProperty(this._colorData, i)) {
html += '' + i.replace('#', '') + '';
}
}
html += 'X';
window.setTimeout(function() {
picker.innerHTML = html;
}, 0);
Event.on(picker, 'mouseover', function(ev) {
var picker = this._colorPicker;
var em = picker.getElementsByTagName('em')[0];
var strong = picker.getElementsByTagName('strong')[0];
var tar = Event.getTarget(ev);
if (tar.tagName.toLowerCase() == 'a') {
em.style.backgroundColor = tar.style.backgroundColor;
strong.innerHTML = this._colorData['#' + tar.innerHTML] + '
* buttons : [
* { type: 'button', value: 'test', value: 'testButton' }
* ]
*
* With the valueClick event you could subscribe to this buttons click event with this:
* tbar.in('testButtonClick', function() { alert('test button clicked'); })
* @type YAHOO.util.CustomEvent
*/
/**
* @event toolbarExpanded
* @description Fires when the toolbar is expanded via the collapse button. See Element.addListener for more information on listening for this event.
* @type YAHOO.util.CustomEvent
*/
/**
* @event toolbarCollapsed
* @description Fires when the toolbar is collapsed via the collapse button. See Element.addListener for more information on listening for this event.
* @type YAHOO.util.CustomEvent
*/
})();
/**
* @module editor
* @description 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.
* @namespace YAHOO.widget * @requires yahoo, dom, element, event, toolbar * @optional animation, container_core, resize, dragdrop */ (function() { var Dom = YAHOO.util.Dom, Event = YAHOO.util.Event, Lang = YAHOO.lang, Toolbar = YAHOO.widget.Toolbar; /** * 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. * @constructor * @class SimpleEditor * @extends YAHOO.util.Element * @param {String/HTMLElement} el The textarea element to turn into an editor. * @param {Object} attrs Object liternal containing configuration parameters. */ YAHOO.widget.SimpleEditor = function(el, attrs) { YAHOO.log('SimpleEditor Initalizing', 'info', 'SimpleEditor'); var o = {}; if (Lang.isObject(el) && (!el.tagName) && !attrs) { Lang.augmentObject(o, el); //Break the config reference el = document.createElement('textarea'); this.DOMReady = true; if (o.container) { var c = Dom.get(o.container); c.appendChild(el); } else { document.body.appendChild(el); } } else { if (attrs) { Lang.augmentObject(o, attrs); //Break the config reference } } var oConfig = { element: null, attributes: o }, id = null; if (Lang.isString(el)) { id = el; } else { if (oConfig.attributes.id) { id = oConfig.attributes.id; } else { this.DOMReady = true; id = Dom.generateId(el); } } oConfig.element = el; var element_cont = document.createElement('DIV'); oConfig.attributes.element_cont = new YAHOO.util.Element(element_cont, { id: id + '_container' }); var div = document.createElement('div'); Dom.addClass(div, 'first-child'); oConfig.attributes.element_cont.appendChild(div); if (!oConfig.attributes.toolbar_cont) { oConfig.attributes.toolbar_cont = document.createElement('DIV'); oConfig.attributes.toolbar_cont.id = id + '_toolbar'; div.appendChild(oConfig.attributes.toolbar_cont); } var editorWrapper = document.createElement('DIV'); div.appendChild(editorWrapper); oConfig.attributes.editor_wrapper = editorWrapper; YAHOO.widget.SimpleEditor.superclass.constructor.call(this, oConfig.element, oConfig.attributes); }; YAHOO.extend(YAHOO.widget.SimpleEditor, YAHOO.util.Element, { /** * @private * @property _resizeConfig * @description The default config for the Resize Utility */ _resizeConfig: { handles: ['br'], autoRatio: true, status: true, proxy: true, useShim: true, setSize: false }, /** * @private * @method _setupResize * @description Creates the Resize instance and binds its events. */ _setupResize: function() { if (!YAHOO.util.DD || !YAHOO.util.Resize) { return false; } if (this.get('resize')) { var config = {}; Lang.augmentObject(config, this._resizeConfig); //Break the config reference this.resize = new YAHOO.util.Resize(this.get('element_cont').get('element'), config); this.resize.on('resize', function(args) { var anim = this.get('animate'); this.set('animate', false); this.set('width', args.width + 'px'); var h = args.height, th = (this.toolbar.get('element').clientHeight + 2), dh = 0; if (this.dompath) { dh = (this.dompath.clientHeight + 1); //It has a 1px top border.. } var newH = (h - th - dh); this.set('height', newH + 'px'); this.get('element_cont').setStyle('height', ''); this.set('animate', anim); }, this, true); } }, /** * @property resize * @description A reference to the Resize object * @type YAHOO.util.Resize */ resize: null, /** * @private * @method _setupDD * @description Sets up the DD instance used from the 'drag' config option. */ _setupDD: function() { if (!YAHOO.util.DD) { return false; } if (this.get('drag')) { YAHOO.log('Attaching DD instance to Editor', 'info', 'SimpleEditor'); var d = this.get('drag'), dd = YAHOO.util.DD; if (d === 'proxy') { dd = YAHOO.util.DDProxy; } this.dd = new dd(this.get('element_cont').get('element')); this.toolbar.addClass('draggable'); this.dd.setHandleElId(this.toolbar._titlebar); } }, /** * @property dd * @description A reference to the DragDrop object. * @type YAHOO.util.DD/YAHOO.util.DDProxy */ dd: null, /** * @private * @property _lastCommand * @description A cache of the last execCommand (used for Undo/Redo so they don't mark an undo level) * @type String */ _lastCommand: null, _undoNodeChange: function() {}, _storeUndo: function() {}, /** * @private * @method _checkKey * @description Checks a keyMap entry against a key event * @param {Object} k The _keyMap object * @param {Event} e The Mouse Event * @return {Boolean} */ _checkKey: function(k, e) { var ret = false; if ((e.keyCode === k.key)) { if (k.mods && (k.mods.length > 0)) { var val = 0; for (var i = 0; i < k.mods.length; i++) { if (this.browser.mac) { if (k.mods[i] == 'ctrl') { k.mods[i] = 'meta'; } } if (e[k.mods[i] + 'Key'] === true) { val++; } } if (val === k.mods.length) { ret = true; } } else { ret = true; } } //YAHOO.log('Shortcut Key Check: (' + k.key + ') return: ' + ret, 'info', 'SimpleEditor'); return ret; }, /** * @private * @property _keyMap * @description Named key maps for various actions in the Editor. Example:CLOSE_WINDOW: { key: 87, mods: ['shift', 'ctrl'] }
.
* 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.
* @type {Object/Mixed}
*/
_keyMap: {
SELECT_ALL: {
key: 65, //A key
mods: ['ctrl']
},
CLOSE_WINDOW: {
key: 87, //W key
mods: ['shift', 'ctrl']
},
FOCUS_TOOLBAR: {
key: 27,
mods: ['shift']
},
FOCUS_AFTER: {
key: 27
},
FONT_SIZE_UP: {
key: 38,
mods: ['shift', 'ctrl']
},
FONT_SIZE_DOWN: {
key: 40,
mods: ['shift', 'ctrl']
},
CREATE_LINK: {
key: 76,
mods: ['shift', 'ctrl']
},
BOLD: {
key: 66,
mods: ['shift', 'ctrl']
},
ITALIC: {
key: 73,
mods: ['shift', 'ctrl']
},
UNDERLINE: {
key: 85,
mods: ['shift', 'ctrl']
},
UNDO: {
key: 90,
mods: ['ctrl']
},
REDO: {
key: 90,
mods: ['shift', 'ctrl']
},
JUSTIFY_LEFT: {
key: 219,
mods: ['shift', 'ctrl']
},
JUSTIFY_CENTER: {
key: 220,
mods: ['shift', 'ctrl']
},
JUSTIFY_RIGHT: {
key: 221,
mods: ['shift', 'ctrl']
}
},
/**
* @private
* @method _cleanClassName
* @description Makes a useable classname from dynamic data, by dropping it to lowercase and replacing spaces with -'s.
* @param {String} str The classname to clean up
* @return {String}
*/
_cleanClassName: function(str) {
return str.replace(/ /g, '-').toLowerCase();
},
/**
* @property _textarea
* @description Flag to determine if we are using a textarea or an HTML Node.
* @type Boolean
*/
_textarea: null,
/**
* @property _docType
* @description The DOCTYPE to use in the editable container.
* @type String
*/
_docType: '',
/**
* @property editorDirty
* @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.
* @type Boolean
*/
editorDirty: null,
/**
* @property _defaultCSS
* @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' }
* @type String
*/
_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; }',
/**
* @property _defaultToolbar
* @private
* @description Default toolbar config.
* @type Object
*/
_defaultToolbar: null,
/**
* @property _lastButton
* @private
* @description The last button pressed, so we don't disable it.
* @type Object
*/
_lastButton: null,
/**
* @property _baseHREF
* @private
* @description The base location of the editable page (this page) so that relative paths for image work.
* @type String
*/
_baseHREF: function() {
var href = document.location.href;
if (href.indexOf('?') !== -1) { //Remove the query string
href = href.substring(0, href.indexOf('?'));
}
href = href.substring(0, href.lastIndexOf('/')) + '/';
return href;
}(),
/**
* @property _lastImage
* @private
* @description Safari reference for the last image selected (for styling as selected).
* @type HTMLElement
*/
_lastImage: null,
/**
* @property _blankImageLoaded
* @private
* @description Don't load the blank image more than once..
* @type Boolean
*/
_blankImageLoaded: null,
/**
* @property _fixNodesTimer
* @private
* @description Holder for the fixNodes timer
* @type Date
*/
_fixNodesTimer: null,
/**
* @property _nodeChangeTimer
* @private
* @description Holds a reference to the nodeChange setTimeout call
* @type Number
*/
_nodeChangeTimer: null,
/**
* @property _lastNodeChangeEvent
* @private
* @description Flag to determine the last event that fired a node change
* @type Event
*/
_lastNodeChangeEvent: null,
/**
* @property _lastNodeChange
* @private
* @description Flag to determine when the last node change was fired
* @type Date
*/
_lastNodeChange: 0,
/**
* @property _rendered
* @private
* @description Flag to determine if editor has been rendered or not
* @type Boolean
*/
_rendered: null,
/**
* @property DOMReady
* @private
* @description Flag to determine if DOM is ready or not
* @type Boolean
*/
DOMReady: null,
/**
* @property _selection
* @private
* @description Holder for caching iframe selections
* @type Object
*/
_selection: null,
/**
* @property _mask
* @private
* @description DOM Element holder for the editor Mask when disabled
* @type Object
*/
_mask: null,
/**
* @property _showingHiddenElements
* @private
* @description Status of the hidden elements button
* @type Boolean
*/
_showingHiddenElements: null,
/**
* @property currentWindow
* @description A reference to the currently open EditorWindow
* @type Object
*/
currentWindow: null,
/**
* @property currentEvent
* @description A reference to the current editor event
* @type Event
*/
currentEvent: null,
/**
* @property operaEvent
* @private
* @description setTimeout holder for Opera and Image DoubleClick event..
* @type Object
*/
operaEvent: null,
/**
* @property currentFont
* @description A reference to the last font selected from the Toolbar
* @type HTMLElement
*/
currentFont: null,
/**
* @property currentElement
* @description A reference to the current working element in the editor
* @type Array
*/
currentElement: null,
/**
* @property dompath
* @description A reference to the dompath container for writing the current working dom path to.
* @type HTMLElement
*/
dompath: null,
/**
* @property beforeElement
* @description A reference to the H2 placed before the editor for Accessibilty.
* @type HTMLElement
*/
beforeElement: null,
/**
* @property afterElement
* @description A reference to the H2 placed after the editor for Accessibilty.
* @type HTMLElement
*/
afterElement: null,
/**
* @property invalidHTML
* @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.
* @type Object
*/
invalidHTML: {
form: true,
input: true,
button: true,
select: true,
link: true,
html: true,
body: true,
iframe: true,
script: true,
style: true,
textarea: true
},
/**
* @property toolbar
* @description Local property containing the YAHOO.widget.Toolbar instance
* @type YAHOO.widget.Toolbar
*/
toolbar: null,
/**
* @private
* @property _contentTimer
* @description setTimeout holder for documentReady check
*/
_contentTimer: null,
/**
* @private
* @property _contentTimerCounter
* @description Counter to check the number of times the body is polled for before giving up
* @type Number
*/
_contentTimerCounter: 0,
/**
* @private
* @property _disabled
* @description The Toolbar items that should be disabled if there is no selection present in the editor.
* @type Array
*/
_disabled: [ 'createlink', 'fontname', 'fontsize', 'forecolor', 'backcolor' ],
/**
* @private
* @property _alwaysDisabled
* @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
* @type Object
*/
_alwaysDisabled: { undo: true, redo: true },
/**
* @private
* @property _alwaysEnabled
* @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
* @type Object
*/
_alwaysEnabled: { },
/**
* @private
* @property _semantic
* @description The Toolbar commands that we should attempt to make tags out of instead of using styles.
* @type Object
*/
_semantic: { 'bold': true, 'italic' : true, 'underline' : true },
/**
* @private
* @property _tag2cmd
* @description A tag map of HTML tags to convert to the different types of commands so we can select the proper toolbar button.
* @type Object
*/
_tag2cmd: {
'b': 'bold',
'strong': 'bold',
'i': 'italic',
'em': 'italic',
'u': 'underline',
'sup': 'superscript',
'sub': 'subscript',
'img': 'insertimage',
'a' : 'createlink',
'ul' : 'insertunorderedlist',
'ol' : 'insertorderedlist'
},
/**
* @private _createIframe
* @description Creates the DOM and YUI Element for the iFrame editor area.
* @param {String} id The string ID to prefix the iframe with
* @return {Object} iFrame object
*/
_createIframe: function() {
var ifrmDom = document.createElement('iframe');
ifrmDom.id = this.get('id') + '_editor';
var config = {
border: '0',
frameBorder: '0',
marginWidth: '0',
marginHeight: '0',
leftMargin: '0',
topMargin: '0',
allowTransparency: 'true',
width: '100%'
};
if (this.get('autoHeight')) {
config.scrolling = 'no';
}
for (var i in config) {
if (Lang.hasOwnProperty(config, i)) {
ifrmDom.setAttribute(i, config[i]);
}
}
var isrc = 'javascript:;';
if (this.browser.ie) {
//isrc = 'about:blank';
//TODO - Check this, I have changed it before..
isrc = 'javascript:false;';
}
ifrmDom.setAttribute('src', isrc);
var ifrm = new YAHOO.util.Element(ifrmDom);
ifrm.setStyle('visibility', 'hidden');
return ifrm;
},
/**
* @private _isElement
* @description Checks to see if an Element reference is a valid one and has a certain tag type
* @param {HTMLElement} el The element to check
* @param {String} tag The tag that the element needs to be
* @return {Boolean}
*/
_isElement: function(el, tag) {
if (el && el.tagName && (el.tagName.toLowerCase() == tag)) {
return true;
}
if (el && el.getAttribute && (el.getAttribute('tag') == tag)) {
return true;
}
return false;
},
/**
* @private _hasParent
* @description Checks to see if an Element reference or one of it's parents is a valid one and has a certain tag type
* @param {HTMLElement} el The element to check
* @param {String} tag The tag that the element needs to be
* @return HTMLElement
*/
_hasParent: function(el, tag) {
if (!el || !el.parentNode) {
return false;
}
while (el.parentNode) {
if (this._isElement(el, tag)) {
return el;
}
if (el.parentNode) {
el = el.parentNode;
} else {
return false;
}
}
return false;
},
/**
* @private
* @method _getDoc
* @description Get the Document of the IFRAME
* @return {Object}
*/
_getDoc: function() {
var value = false;
if (this.get) {
if (this.get('iframe')) {
if (this.get('iframe').get) {
if (this.get('iframe').get('element')) {
try {
if (this.get('iframe').get('element').contentWindow) {
if (this.get('iframe').get('element').contentWindow.document) {
value = this.get('iframe').get('element').contentWindow.document;
return value;
}
}
} catch (e) {}
}
}
}
}
return false;
},
/**
* @private
* @method _getWindow
* @description Get the Window of the IFRAME
* @return {Object}
*/
_getWindow: function() {
return this.get('iframe').get('element').contentWindow;
},
/**
* @method focus
* @description Attempt to set the focus of the iframes window.
*/
focus: function() {
this._getWindow().focus();
},
/**
* @private
* @depreciated - This should not be used, moved to this.focus();
* @method _focusWindow
* @description Attempt to set the focus of the iframes window.
*/
_focusWindow: function() {
YAHOO.log('_focusWindow: depreciated in favor of this.focus()', 'warn', 'Editor');
this.focus();
},
/**
* @private
* @method _hasSelection
* @description Determines if there is a selection in the editor document.
* @return {Boolean}
*/
_hasSelection: function() {
var sel = this._getSelection();
var range = this._getRange();
var hasSel = false;
if (!sel || !range) {
return hasSel;
}
//Internet Explorer
if (this.browser.ie || this.browser.opera) {
if (range.text) {
hasSel = true;
}
if (range.html) {
hasSel = true;
}
} else {
if (this.browser.webkit) {
if (sel+'' !== '') {
hasSel = true;
}
} else {
if (sel && (sel.toString() !== '') && (sel !== undefined)) {
hasSel = true;
}
}
}
return hasSel;
},
/**
* @private
* @method _getSelection
* @description Handles the different selection objects across the A-Grade list.
* @return {Object} Selection Object
*/
_getSelection: function() {
var _sel = null;
if (this._getDoc() && this._getWindow()) {
if (this._getDoc().selection) {
_sel = this._getDoc().selection;
} else {
_sel = this._getWindow().getSelection();
}
//Handle Safari's lack of Selection Object
if (this.browser.webkit) {
if (_sel.baseNode) {
this._selection = {};
this._selection.baseNode = _sel.baseNode;
this._selection.baseOffset = _sel.baseOffset;
this._selection.extentNode = _sel.extentNode;
this._selection.extentOffset = _sel.extentOffset;
} else if (this._selection !== null) {
_sel = this._getWindow().getSelection();
_sel.setBaseAndExtent(
this._selection.baseNode,
this._selection.baseOffset,
this._selection.extentNode,
this._selection.extentOffset);
this._selection = null;
}
}
}
return _sel;
},
/**
* @private
* @method _selectNode
* @description Places the highlight around a given node
* @param {HTMLElement} node The node to select
*/
_selectNode: function(node, collapse) {
if (!node) {
return false;
}
var sel = this._getSelection(),
range = null;
if (this.browser.ie) {
try { //IE freaks out here sometimes..
range = this._getDoc().body.createTextRange();
range.moveToElementText(node);
range.select();
} catch (e) {
YAHOO.log('IE failed to select element: ' + node.tagName, 'warn', 'SimpleEditor');
}
} else if (this.browser.webkit) {
if (collapse) {
sel.setBaseAndExtent(node, 1, node, node.innerText.length);
} else {
sel.setBaseAndExtent(node, 0, node, node.innerText.length);
}
} else if (this.browser.opera) {
sel = this._getWindow().getSelection();
range = this._getDoc().createRange();
range.selectNode(node);
sel.removeAllRanges();
sel.addRange(range);
} else {
range = this._getDoc().createRange();
range.selectNodeContents(node);
sel.removeAllRanges();
sel.addRange(range);
}
//TODO - Check Performance
this.nodeChange();
},
/**
* @private
* @method _getRange
* @description Handles the different range objects across the A-Grade list.
* @return {Object} Range Object
*/
_getRange: function() {
var sel = this._getSelection();
if (sel === null) {
return null;
}
if (this.browser.webkit && !sel.getRangeAt) {
var _range = this._getDoc().createRange();
try {
_range.setStart(sel.anchorNode, sel.anchorOffset);
_range.setEnd(sel.focusNode, sel.focusOffset);
} catch (e) {
_range = this._getWindow().getSelection()+'';
}
return _range;
}
if (this.browser.ie || this.browser.opera) {
try {
return sel.createRange();
} catch (e2) {
return null;
}
}
if (sel.rangeCount > 0) {
return sel.getRangeAt(0);
}
return null;
},
/**
* @private
* @method _setDesignMode
* @description Sets the designMode of the iFrame document.
* @param {String} state This should be either on or off
*/
_setDesignMode: function(state) {
try {
var set = true;
//SourceForge Bug #1807057
if (this.browser.ie && (state.toLowerCase() == 'off')) {
set = false;
}
if (set) {
this._getDoc().designMode = state;
}
} catch(e) { }
},
/**
* @private
* @method _toggleDesignMode
* @description Toggles the designMode of the iFrame document on and off.
* @return {String} The state that it was set to.
*/
_toggleDesignMode: function() {
var _dMode = this._getDoc().designMode.toLowerCase(),
_state = 'on';
if (_dMode == 'on') {
_state = 'off';
}
this._setDesignMode(_state);
return _state;
},
/**
* @private
* @property _focused
* @description Holder for trapping focus/blur state and prevent double events
* @type Boolean
*/
_focused: null,
/**
* @private
* @method _handleFocus
* @description Handles the focus of the iframe. Note, this is window focus event, not an Editor focus event.
* @param {Event} e The DOM Event
*/
_handleFocus: function(e) {
if (!this._focused) {
YAHOO.log('Editor Window Focused', 'info', 'SimpleEditor');
this._focused = true;
this.fireEvent('editorWindowFocus', { type: 'editorWindowFocus', target: this });
}
},
/**
* @private
* @method _handleBlur
* @description Handles the blur of the iframe. Note, this is window blur event, not an Editor blur event.
* @param {Event} e The DOM Event
*/
_handleBlur: function(e) {
if (this._focused) {
YAHOO.log('Editor Window Blurred', 'info', 'SimpleEditor');
this._focused = false;
this.fireEvent('editorWindowBlur', { type: 'editorWindowBlur', target: this });
}
},
/**
* @private
* @method _initEditorEvents
* @description This method sets up the listeners on the Editors document.
*/
_initEditorEvents: function() {
//Setup Listeners on iFrame
var doc = this._getDoc(),
win = this._getWindow();
Event.on(doc, 'mouseup', this._handleMouseUp, this, true);
Event.on(doc, 'mousedown', this._handleMouseDown, this, true);
Event.on(doc, 'click', this._handleClick, this, true);
Event.on(doc, 'dblclick', this._handleDoubleClick, this, true);
Event.on(doc, 'keypress', this._handleKeyPress, this, true);
Event.on(doc, 'keyup', this._handleKeyUp, this, true);
Event.on(doc, 'keydown', this._handleKeyDown, this, true);
/* TODO -- Everyone but Opera works here..
Event.on(doc, 'paste', function() {
YAHOO.log('PASTE', 'info', 'SimpleEditor');
}, this, true);
*/
//Focus and blur..
Event.on(win, 'focus', this._handleFocus, this, true);
Event.on(win, 'blur', this._handleBlur, this, true);
},
/**
* @private
* @method _removeEditorEvents
* @description This method removes the listeners on the Editors document (for disabling).
*/
_removeEditorEvents: function() {
//Remove Listeners on iFrame
var doc = this._getDoc(),
win = this._getWindow();
Event.removeListener(doc, 'mouseup', this._handleMouseUp, this, true);
Event.removeListener(doc, 'mousedown', this._handleMouseDown, this, true);
Event.removeListener(doc, 'click', this._handleClick, this, true);
Event.removeListener(doc, 'dblclick', this._handleDoubleClick, this, true);
Event.removeListener(doc, 'keypress', this._handleKeyPress, this, true);
Event.removeListener(doc, 'keyup', this._handleKeyUp, this, true);
Event.removeListener(doc, 'keydown', this._handleKeyDown, this, true);
//Focus and blur..
Event.removeListener(win, 'focus', this._handleFocus, this, true);
Event.removeListener(win, 'blur', this._handleBlur, this, true);
},
_fixWebkitDivs: function() {
if (this.browser.webkit) {
var divs = this._getDoc().body.getElementsByTagName('div');
Dom.addClass(divs, 'yui-wk-div');
}
},
/**
* @private
* @method _initEditor
* @description This method is fired from _checkLoaded when the document is ready. It turns on designMode and set's up the listeners.
*/
_initEditor: function() {
if (this.browser.ie) {
this._getDoc().body.style.margin = '0';
}
if (!this.get('disabled')) {
if (this._getDoc().designMode.toLowerCase() != 'on') {
this._setDesignMode('on');
this._contentTimerCounter = 0;
}
}
if (!this._getDoc().body) {
YAHOO.log('Body is null, check again', 'error', 'SimpleEditor');
this._contentTimerCounter = 0;
this._checkLoaded();
return false;
}
YAHOO.log('editorLoaded', 'info', 'SimpleEditor');
this.toolbar.on('buttonClick', this._handleToolbarClick, this, true);
if (!this.get('disabled')) {
this._initEditorEvents();
this.toolbar.set('disabled', false);
}
this.fireEvent('editorContentLoaded', { type: 'editorLoaded', target: this });
this._fixWebkitDivs();
if (this.get('dompath')) {
YAHOO.log('Delayed DomPath write', 'info', 'SimpleEditor');
var self = this;
setTimeout(function() {
self._writeDomPath.call(self);
self._setupResize.call(self);
}, 150);
}
var br = [];
for (var i in this.browser) {
if (this.browser[i]) {
br.push(i);
}
}
if (this.get('ptags')) {
br.push('ptags');
}
Dom.addClass(this._getDoc().body, br.join(' '));
this.nodeChange(true);
},
/**
* @private
* @method _checkLoaded
* @description Called from a setTimeout loop to check if the iframes body.onload event has fired, then it will init the editor.
*/
_checkLoaded: function() {
this._contentTimerCounter++;
if (this._contentTimer) {
clearTimeout(this._contentTimer);
}
if (this._contentTimerCounter > 500) {
YAHOO.log('ERROR: Body Did Not load', 'error', 'SimpleEditor');
return false;
}
var init = false;
try {
if (this._getDoc() && this._getDoc().body) {
if (this.browser.ie) {
if (this._getDoc().body.readyState == 'complete') {
init = true;
}
} else {
if (this._getDoc().body._rteLoaded === true) {
init = true;
}
}
}
} catch (e) {
init = false;
YAHOO.log('checking body (e)' + e, 'error', 'SimpleEditor');
}
if (init === true) {
//The onload event has fired, clean up after ourselves and fire the _initEditor method
YAHOO.log('Firing _initEditor', 'info', 'SimpleEditor');
this._initEditor();
} else {
var self = this;
this._contentTimer = setTimeout(function() {
self._checkLoaded.call(self);
}, 20);
}
},
/**
* @private
* @method _setInitialContent
* @description This method will open the iframes content document and write the textareas value into it, then start the body.onload checking.
*/
_setInitialContent: function() {
YAHOO.log('Populating editor body with contents of the text area', 'info', 'SimpleEditor');
var value = ((this._textarea) ? this.get('element').value : this.get('element').innerHTML),
doc = null;
if ((value === '') && this.browser.gecko) {
//It seems that Gecko doesn't like an empty body so we have to give it something..
value = 'html {
height: 95%;
}
body {
height: 100%;
padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;
}
a {
color: blue;
text-decoration: underline;
cursor: pointer;
}
.warning-localfile {
border-bottom: 1px dashed red !important;
}
.yui-busy {
cursor: wait !important;
}
img.selected { //Safari image selection
border: 2px dotted #808080;
}
img {
cursor: pointer !important;
border: none;
}
* @type String
*/
this.setAttributeConfig('css', {
value: attr.css || this._defaultCSS,
writeOnce: true
});
/**
* @attribute html
* @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)
* @default This HTML requires a few things if you are to override:
{TITLE}, {CSS}, {HIDDEN_CSS}, {EXTRA_CSS}
and {CONTENT}
need to be there, they are passed to YAHOO.lang.substitute to be replace with other strings.
onload="document.body._rteLoaded = true;"
: the onload statement must be there or the editor will not finish loading.
<html>
<head>
<title>{TITLE}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
{CSS}
</style>
<style>
{HIDDEN_CSS}
</style>
<style>
{EXTRA_CSS}
</style>
</head>
<body onload="document.body._rteLoaded = true;">
{CONTENT}
</body>
</html>
* @type String
*/
this.setAttributeConfig('html', {
value: attr.html || 'execCommand('fontname', false, 'yui-tmp')
on the given selection.
* It will then search the document for an element with the font-family set to yui-tmp and replace that with another span that has other information in it, then assign the new span to the
* this.currentElement
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.
*/
_createCurrentElement: function(tagName, tagStyle) {
tagName = ((tagName) ? tagName : 'a');
var tar = null,
el = [],
_doc = this._getDoc();
if (this.currentFont) {
if (!tagStyle) {
tagStyle = {};
}
tagStyle.fontFamily = this.currentFont;
this.currentFont = null;
}
this.currentElement = [];
var _elCreate = function(tagName, tagStyle) {
var el = null;
tagName = ((tagName) ? tagName : 'span');
tagName = tagName.toLowerCase();
switch (tagName) {
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
el = _doc.createElement(tagName);
break;
default:
el = _doc.createElement(tagName);
if (tagName === 'span') {
YAHOO.util.Dom.addClass(el, 'yui-tag-' + tagName);
YAHOO.util.Dom.addClass(el, 'yui-tag');
el.setAttribute('tag', tagName);
}
for (var k in tagStyle) {
if (YAHOO.lang.hasOwnProperty(tagStyle, k)) {
el.style[k] = tagStyle[k];
}
}
break;
}
return el;
};
if (!this._hasSelection()) {
if (this._getDoc().queryCommandEnabled('insertimage')) {
this._getDoc().execCommand('insertimage', false, 'yui-tmp-img');
var imgs = this._getDoc().getElementsByTagName('img');
for (var j = 0; j < imgs.length; j++) {
if (imgs[j].getAttribute('src', 2) == 'yui-tmp-img') {
el = _elCreate(tagName, tagStyle);
imgs[j].parentNode.replaceChild(el, imgs[j]);
this.currentElement[this.currentElement.length] = el;
}
}
} else {
if (this.currentEvent) {
tar = YAHOO.util.Event.getTarget(this.currentEvent);
} else {
//For Safari..
tar = this._getDoc().body;
}
}
if (tar) {
/*
* @knownissue Safari Cursor Position
* @browser Safari 2.x
* @description The issue here is that we have no way of knowing where the cursor position is
* inside of the iframe, so we have to place the newly inserted data in the best place that we can.
*/
el = _elCreate(tagName, tagStyle);
if (this._isElement(tar, 'body') || this._isElement(tar, 'html')) {
if (this._isElement(tar, 'html')) {
tar = this._getDoc().body;
}
tar.appendChild(el);
} else if (tar.nextSibling) {
tar.parentNode.insertBefore(el, tar.nextSibling);
} else {
tar.parentNode.appendChild(el);
}
//this.currentElement = el;
this.currentElement[this.currentElement.length] = el;
this.currentEvent = null;
if (this.browser.webkit) {
//Force Safari to focus the new element
this._getSelection().setBaseAndExtent(el, 0, el, 0);
if (this.browser.webkit3) {
this._getSelection().collapseToStart();
} else {
this._getSelection().collapse(true);
}
}
}
} else {
//Force CSS Styling for this action...
this._setEditorStyle(true);
this._getDoc().execCommand('fontname', false, 'yui-tmp');
var _tmp = [], __tmp, __els = ['font', 'span', 'i', 'b', 'u'];
if (!this._isElement(this._getSelectedElement(), 'body')) {
__els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().tagName);
__els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().parentNode.tagName);
}
for (var _els = 0; _els < __els.length; _els++) {
var _tmp1 = this._getDoc().getElementsByTagName(__els[_els]);
for (var e = 0; e < _tmp1.length; e++) {
_tmp[_tmp.length] = _tmp1[e];
}
}
for (var i = 0; i < _tmp.length; i++) {
if ((YAHOO.util.Dom.getStyle(_tmp[i], 'font-family') == 'yui-tmp') || (_tmp[i].face && (_tmp[i].face == 'yui-tmp'))) {
if (tagName !== 'span') {
el = _elCreate(tagName, tagStyle);
} else {
el = _elCreate(_tmp[i].tagName, tagStyle);
}
el.innerHTML = _tmp[i].innerHTML;
if (this._isElement(_tmp[i], 'ol') || (this._isElement(_tmp[i], 'ul'))) {
var fc = _tmp[i].getElementsByTagName('li')[0];
_tmp[i].style.fontFamily = 'inherit';
fc.style.fontFamily = 'inherit';
el.innerHTML = fc.innerHTML;
fc.innerHTML = '';
fc.appendChild(el);
this.currentElement[this.currentElement.length] = el;
} else if (this._isElement(_tmp[i], 'li')) {
_tmp[i].innerHTML = '';
_tmp[i].appendChild(el);
_tmp[i].style.fontFamily = 'inherit';
this.currentElement[this.currentElement.length] = el;
} else {
if (_tmp[i].parentNode) {
_tmp[i].parentNode.replaceChild(el, _tmp[i]);
this.currentElement[this.currentElement.length] = el;
this.currentEvent = null;
if (this.browser.webkit) {
//Force Safari to focus the new element
this._getSelection().setBaseAndExtent(el, 0, el, 0);
if (this.browser.webkit3) {
this._getSelection().collapseToStart();
} else {
this._getSelection().collapse(true);
}
}
if (this.browser.ie && tagStyle && tagStyle.fontSize) {
this._getSelection().empty();
}
if (this.browser.gecko) {
this._getSelection().collapseToStart();
}
}
}
}
}
var len = this.currentElement.length;
for (var o = 0; o < len; o++) {
if ((o + 1) != len) { //Skip the last one in the list
if (this.currentElement[o] && this.currentElement[o].nextSibling) {
if (this._isElement(this.currentElement[o], 'br')) {
this.currentElement[this.currentElement.length] = this.currentElement[o].nextSibling;
}
}
}
}
}
},
/**
* @method saveHTML
* @description Cleans the HTML with the cleanHTML method then places that string back into the textarea.
* @return String
*/
saveHTML: function() {
var html = this.cleanHTML();
if (this._textarea) {
this.get('element').value = html;
} else {
this.get('element').innerHTML = html;
}
if (this.get('saveEl') !== this.get('element')) {
var out = this.get('saveEl');
if (Lang.isString(out)) {
out = Dom.get(out);
}
if (out) {
if (out.tagName.toLowerCase() === 'textarea') {
out.value = html;
} else {
out.innerHTML = html;
}
}
}
return html;
},
/**
* @method setEditorHTML
* @param {String} incomingHTML The html content to load into the editor
* @description Loads HTML into the editors body
*/
setEditorHTML: function(incomingHTML) {
var html = this._cleanIncomingHTML(incomingHTML);
this._getDoc().body.innerHTML = html;
this.nodeChange();
},
/**
* @method getEditorHTML
* @description Gets the unprocessed/unfiltered HTML from the editor
*/
getEditorHTML: function() {
var b = this._getDoc().body;
if (b === null) {
YAHOO.log('Body is null, returning null.', 'error', 'SimpleEditor');
return null;
}
return this._getDoc().body.innerHTML;
},
/**
* @method show
* @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.
*/
show: function() {
if (this.browser.gecko) {
this._setDesignMode('on');
this.focus();
}
if (this.browser.webkit) {
var self = this;
window.setTimeout(function() {
self._setInitialContent.call(self);
}, 10);
}
//Adding this will close all other Editor window's when showing this one.
if (this.currentWindow) {
this.closeWindow();
}
//Put the iframe back in place
this.get('iframe').setStyle('position', 'static');
this.get('iframe').setStyle('left', '');
},
/**
* @method hide
* @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.
*/
hide: function() {
//Adding this will close all other Editor window's.
if (this.currentWindow) {
this.closeWindow();
}
if (this._fixNodesTimer) {
clearTimeout(this._fixNodesTimer);
this._fixNodesTimer = null;
}
if (this._nodeChangeTimer) {
clearTimeout(this._nodeChangeTimer);
this._nodeChangeTimer = null;
}
this._lastNodeChange = 0;
//Move the iframe off of the screen, so that in containers with visiblity hidden, IE will not cover other elements.
this.get('iframe').setStyle('position', 'absolute');
this.get('iframe').setStyle('left', '-9999px');
},
/**
* @method _cleanIncomingHTML
* @param {String} html The unfiltered HTML
* @description Process the HTML with a few regexes to clean it up and stabilize the input
* @return {String} The filtered HTML
*/
_cleanIncomingHTML: function(html) {
html = html.replace(/]*)>/gi, '');
html = html.replace(/<\/strong>/gi, '');
//replace embed before em check
html = html.replace(/