2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
8
var Dom = YAHOO.util.Dom,
9
Event = YAHOO.util.Event,
13
* @description <p>Creates a rich custom Toolbar Button. Primarily used with the Rich Text Editor's Toolbar</p>
14
* @class ToolbarButtonAdvanced
15
* @namespace YAHOO.widget
16
* @requires yahoo, dom, element, event, container_core, menu, button
18
* Provides a toolbar button based on the button and menu widgets.
20
* @class ToolbarButtonAdvanced
21
* @param {String/HTMLElement} el The element to turn into a button.
22
* @param {Object} attrs Object liternal containing configuration parameters.
24
if (YAHOO.widget.Button) {
25
YAHOO.widget.ToolbarButtonAdvanced = YAHOO.widget.Button;
27
* @property buttonType
29
* @description Tells if the Button is a Rich Button or a Simple Button
31
YAHOO.widget.ToolbarButtonAdvanced.prototype.buttonType = 'rich';
34
* @param {String} value The value of the option that we want to mark as selected
35
* @description Select an option by value
37
YAHOO.widget.ToolbarButtonAdvanced.prototype.checkValue = function(value) {
38
var _menuItems = this.getMenu().getItems();
39
if (_menuItems.length === 0) {
40
this.getMenu()._onBeforeShow();
41
_menuItems = this.getMenu().getItems();
43
for (var i = 0; i < _menuItems.length; i++) {
44
_menuItems[i].cfg.setProperty('checked', false);
45
if (_menuItems[i].value == value) {
46
_menuItems[i].cfg.setProperty('checked', true);
51
YAHOO.widget.ToolbarButtonAdvanced = function() {};
56
* @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, <select> elements are used in place of menu's.</p>
57
* @class ToolbarButton
58
* @namespace YAHOO.widget
59
* @requires yahoo, dom, element, event
60
* @extends YAHOO.util.Element
64
* @param {String/HTMLElement} el The element to turn into a button.
65
* @param {Object} attrs Object liternal containing configuration parameters.
68
YAHOO.widget.ToolbarButton = function(el, attrs) {
69
YAHOO.log('ToolbarButton Initalizing', 'info', 'ToolbarButton');
70
YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar');
72
if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
75
var local_attrs = (attrs || {});
79
attributes: local_attrs
82
if (!oConfig.attributes.type) {
83
oConfig.attributes.type = 'push';
86
oConfig.element = document.createElement('span');
87
oConfig.element.setAttribute('unselectable', 'on');
88
oConfig.element.className = 'yui-button yui-' + oConfig.attributes.type + '-button';
89
oConfig.element.innerHTML = '<span class="first-child"><a href="#">LABEL</a></span>';
90
oConfig.element.firstChild.firstChild.tabIndex = '-1';
91
oConfig.attributes.id = (oConfig.attributes.id || Dom.generateId());
92
oConfig.element.id = oConfig.attributes.id;
94
YAHOO.widget.ToolbarButton.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
97
YAHOO.extend(YAHOO.widget.ToolbarButton, YAHOO.util.Element, {
99
* @property buttonType
101
* @description Tells if the Button is a Rich Button or a Simple Button
103
buttonType: 'normal',
105
* @method _handleMouseOver
107
* @description Adds classes to the button elements on mouseover (hover)
109
_handleMouseOver: function() {
110
if (!this.get('disabled')) {
111
this.addClass('yui-button-hover');
112
this.addClass('yui-' + this.get('type') + '-button-hover');
116
* @method _handleMouseOut
118
* @description Removes classes from the button elements on mouseout (hover)
120
_handleMouseOut: function() {
121
this.removeClass('yui-button-hover');
122
this.removeClass('yui-' + this.get('type') + '-button-hover');
126
* @param {String} value The value of the option that we want to mark as selected
127
* @description Select an option by value
129
checkValue: function(value) {
130
if (this.get('type') == 'menu') {
131
var opts = this._button.options;
132
for (var i = 0; i < opts.length; i++) {
133
if (opts[i].value == value) {
134
opts.selectedIndex = i;
141
* @description The ToolbarButton class's initialization method
143
init: function(p_oElement, p_oAttributes) {
144
YAHOO.widget.ToolbarButton.superclass.init.call(this, p_oElement, p_oAttributes);
146
this.on('mouseover', this._handleMouseOver, this, true);
147
this.on('mouseout', this._handleMouseOut, this, true);
148
this.on('click', function(ev) {
154
* @method initAttributes
155
* @description Initializes all of the configuration attributes used to create
157
* @param {Object} attr Object literal specifying a set of
158
* configuration attributes used to create the toolbar.
160
initAttributes: function(attr) {
161
YAHOO.widget.ToolbarButton.superclass.initAttributes.call(this, attr);
164
* @description The value of the button
167
this.setAttributeConfig('value', {
172
* @description The menu attribute, see YAHOO.widget.Button
175
this.setAttributeConfig('menu', {
176
value: attr.menu || false
180
* @description The type of button to create: push, menu, color, select, spin
183
this.setAttributeConfig('type', {
186
method: function(type) {
189
this._button = this.get('element').getElementsByTagName('a')[0];
194
el = document.createElement('select');
195
var menu = this.get('menu');
196
for (var i = 0; i < menu.length; i++) {
197
opt = document.createElement('option');
198
opt.innerHTML = menu[i].text;
199
opt.value = menu[i].value;
200
if (menu[i].checked) {
205
this._button.parentNode.replaceChild(el, this._button);
206
Event.on(el, 'change', this._handleSelect, this, true);
214
* @attribute disabled
215
* @description Set the button into a disabled state
218
this.setAttributeConfig('disabled', {
219
value: attr.disabled || false,
220
method: function(disabled) {
222
this.addClass('yui-button-disabled');
223
this.addClass('yui-' + this.get('type') + '-button-disabled');
225
this.removeClass('yui-button-disabled');
226
this.removeClass('yui-' + this.get('type') + '-button-disabled');
228
if (this.get('type') == 'menu') {
229
this._button.disabled = disabled;
236
* @description The text label for the button
239
this.setAttributeConfig('label', {
241
method: function(label) {
243
this._button = this.get('element').getElementsByTagName('a')[0];
245
if (this.get('type') == 'push') {
246
this._button.innerHTML = label;
253
* @description The title of the button
256
this.setAttributeConfig('title', {
262
* @description The container that the button is rendered to, handled by Toolbar
265
this.setAttributeConfig('container', {
268
method: function(cont) {
276
* @method _handleSelect
277
* @description The event fired when a change event gets fired on a select element
278
* @param {Event} ev The change event.
280
_handleSelect: function(ev) {
281
var tar = Event.getTarget(ev);
282
var value = tar.options[tar.selectedIndex].value;
283
this.fireEvent('change', {type: 'change', value: value });
287
* @description A stub function to mimic YAHOO.widget.Button's getMenu method
289
getMenu: function() {
290
return this.get('menu');
294
* @description Destroy the button
296
destroy: function() {
297
Event.purgeElement(this.get('element'), true);
298
this.get('element').parentNode.removeChild(this.get('element'));
299
//Brutal Object Destroy
300
for (var i in this) {
301
if (Lang.hasOwnProperty(this, i)) {
308
* @description Overridden fireEvent method to prevent DOM events from firing if the button is disabled.
310
fireEvent: function(p_sType, p_aArgs) {
311
// Disabled buttons should not respond to DOM events
312
if (this.DOM_EVENTS[p_sType] && this.get('disabled')) {
313
Event.stopEvent(p_aArgs);
317
YAHOO.widget.ToolbarButton.superclass.fireEvent.call(this, p_sType, p_aArgs);
321
* @description Returns a string representing the toolbar.
324
toString: function() {
325
return 'ToolbarButton (' + this.get('id') + ')';
332
* @description <p>Creates a rich Toolbar widget based on Button. Primarily used with the Rich Text Editor</p>
333
* @namespace YAHOO.widget
334
* @requires yahoo, dom, element, event, toolbarbutton
335
* @optional container_core, dragdrop
338
var Dom = YAHOO.util.Dom,
339
Event = YAHOO.util.Event,
342
var getButton = function(id) {
344
if (Lang.isString(id)) {
345
button = this.getButtonById(id);
347
if (Lang.isNumber(id)) {
348
button = this.getButtonByIndex(id);
350
if ((!(button instanceof YAHOO.widget.ToolbarButton)) && (!(button instanceof YAHOO.widget.ToolbarButtonAdvanced))) {
351
button = this.getButtonByValue(id);
353
if ((button instanceof YAHOO.widget.ToolbarButton) || (button instanceof YAHOO.widget.ToolbarButtonAdvanced)) {
360
* Provides a rich toolbar widget based on the button and menu widgets
363
* @extends YAHOO.util.Element
364
* @param {String/HTMLElement} el The element to turn into a toolbar.
365
* @param {Object} attrs Object liternal containing configuration parameters.
367
YAHOO.widget.Toolbar = function(el, attrs) {
368
YAHOO.log('Toolbar Initalizing', 'info', 'Toolbar');
369
YAHOO.log(arguments.length + ' arguments passed to constructor', 'info', 'Toolbar');
371
if (Lang.isObject(arguments[0]) && !Dom.get(el).nodeType) {
374
var local_attrs = {};
376
Lang.augmentObject(local_attrs, attrs); //Break the config reference
382
attributes: local_attrs
386
if (Lang.isString(el) && Dom.get(el)) {
387
oConfig.element = Dom.get(el);
388
} else if (Lang.isObject(el) && Dom.get(el) && Dom.get(el).nodeType) {
389
oConfig.element = Dom.get(el);
393
if (!oConfig.element) {
394
YAHOO.log('No element defined, creating toolbar container', 'warn', 'Toolbar');
395
oConfig.element = document.createElement('DIV');
396
oConfig.element.id = Dom.generateId();
398
if (local_attrs.container && Dom.get(local_attrs.container)) {
399
YAHOO.log('Container found in config appending to it (' + Dom.get(local_attrs.container).id + ')', 'info', 'Toolbar');
400
Dom.get(local_attrs.container).appendChild(oConfig.element);
405
if (!oConfig.element.id) {
406
oConfig.element.id = ((Lang.isString(el)) ? el : Dom.generateId());
407
YAHOO.log('No element ID defined for toolbar container, creating..', 'warn', 'Toolbar');
409
YAHOO.log('Initing toolbar with id: ' + oConfig.element.id, 'info', 'Toolbar');
411
var fs = document.createElement('fieldset');
412
var lg = document.createElement('legend');
413
lg.innerHTML = 'Toolbar';
416
var cont = document.createElement('DIV');
417
oConfig.attributes.cont = cont;
418
Dom.addClass(cont, 'yui-toolbar-subcont');
419
fs.appendChild(cont);
420
oConfig.element.appendChild(fs);
422
oConfig.element.tabIndex = -1;
425
oConfig.attributes.element = oConfig.element;
426
oConfig.attributes.id = oConfig.element.id;
428
YAHOO.widget.Toolbar.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
432
YAHOO.extend(YAHOO.widget.Toolbar, YAHOO.util.Element, {
434
* @method _addMenuClasses
436
* @description This method is called from Menu's renderEvent to add a few more classes to the menu items
437
* @param {String} ev The event that fired.
438
* @param {Array} na Array of event information.
439
* @param {Object} o Button config object.
441
_addMenuClasses: function(ev, na, o) {
442
Dom.addClass(this.element, 'yui-toolbar-' + o.get('value') + '-menu');
443
if (Dom.hasClass(o._button.parentNode.parentNode, 'yui-toolbar-select')) {
444
Dom.addClass(this.element, 'yui-toolbar-select-menu');
446
var items = this.getItems();
447
for (var i = 0; i < items.length; i++) {
448
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()));
449
Dom.addClass(items[i].element, 'yui-toolbar-' + o.get('value') + '-' + ((items[i].value) ? items[i].value.replace(/ /g, '-') : items[i]._oText.nodeValue.replace(/ /g, '-')));
453
* @property buttonType
454
* @description The default button to use
457
buttonType: YAHOO.widget.ToolbarButton,
460
* @description The DragDrop instance associated with the Toolbar
465
* @property _colorData
466
* @description Object reference containing colors hex and text values.
471
'#111111': 'Obsidian',
472
'#2D2D2D': 'Dark Gray',
476
'#8B8B8B': 'Concrete',
478
'#B9B9B9': 'Titanium',
480
'#D0D0D0': 'Light Gray',
483
'#BFBF00': 'Pumpkin',
486
'#FFFF80': 'Pale Yellow',
488
'#525330': 'Raw Siena',
491
'#7F7F00': 'Paprika',
496
'#80FF00': 'Chartreuse',
498
'#C0FF80': 'Pale Lime',
499
'#DFFFBF': 'Light Mint',
501
'#668F5A': 'Lime Gray',
504
'#8A9B55': 'Pistachio',
505
'#B7C296': 'Light Jade',
506
'#E6EBD5': 'Breakwater',
507
'#00BF00': 'Spring Frost',
508
'#00FF80': 'Pastel Green',
509
'#40FFA0': 'Light Emerald',
510
'#80FFC0': 'Sea Foam',
511
'#BFFFDF': 'Sea Mist',
512
'#033D21': 'Dark Forrest',
514
'#7FA37C': 'Medium Green',
516
'#8DAE94': 'Yellow Gray Green',
517
'#ACC6B5': 'Aqua Lung',
518
'#DDEBE2': 'Sea Vapor',
521
'#40FFFF': 'Turquoise Blue',
522
'#80FFFF': 'Light Aqua',
523
'#BFFFFF': 'Pale Cyan',
524
'#033D3D': 'Dark Teal',
525
'#347D7E': 'Gray Turquoise',
526
'#609A9F': 'Green Blue',
527
'#007F7F': 'Seaweed',
528
'#96BDC4': 'Green Gray',
529
'#B5D1D7': 'Soapstone',
530
'#E2F1F4': 'Light Turquoise',
531
'#0060BF': 'Summer Sky',
532
'#0080FF': 'Sky Blue',
533
'#40A0FF': 'Electric Blue',
534
'#80C0FF': 'Light Azure',
535
'#BFDFFF': 'Ice Blue',
538
'#57708F': 'Dusty Blue',
539
'#00407F': 'Sea Blue',
540
'#7792AC': 'Sky Blue Gray',
541
'#A8BED1': 'Morning Sky',
543
'#0000BF': 'Deep Blue',
545
'#4040FF': 'Cerulean Blue',
546
'#8080FF': 'Evening Blue',
547
'#BFBFFF': 'Light Blue',
548
'#212143': 'Deep Indigo',
549
'#373E68': 'Sea Blue',
550
'#444F75': 'Night Blue',
551
'#00007F': 'Indigo Blue',
552
'#585E82': 'Dockside',
553
'#8687A4': 'Blue Gray',
554
'#D2D1E1': 'Light Blue Gray',
555
'#6000BF': 'Neon Violet',
556
'#8000FF': 'Blue Violet',
557
'#A040FF': 'Violet Purple',
558
'#C080FF': 'Violet Dusk',
559
'#DFBFFF': 'Pale Lavender',
560
'#302449': 'Cool Shale',
561
'#54466F': 'Dark Indigo',
562
'#655A7F': 'Dark Violet',
564
'#726284': 'Smoky Violet',
565
'#9E8FA9': 'Slate Gray',
566
'#DCD1DF': 'Violet White',
567
'#BF00BF': 'Royal Violet',
568
'#FF00FF': 'Fuchsia',
569
'#FF40FF': 'Magenta',
571
'#FFBFFF': 'Pale Magenta',
572
'#4A234A': 'Dark Purple',
573
'#794A72': 'Medium Purple',
574
'#936386': 'Cool Granite',
576
'#9D7292': 'Purple Moon',
577
'#C0A0B6': 'Pale Purple',
578
'#ECDAE5': 'Pink Cloud',
579
'#BF005F': 'Hot Pink',
580
'#FF007F': 'Deep Pink',
582
'#FF80BF': 'Electric Pink',
584
'#451528': 'Purple Red',
585
'#823857': 'Purple Dino',
586
'#A94A76': 'Purple Gray',
588
'#BC6F95': 'Antique Mauve',
589
'#D8A5BB': 'Cool Marble',
590
'#F7DDE9': 'Pink Granite',
592
'#FF0000': 'Fire Truck',
593
'#FF4040': 'Pale Red',
595
'#FFC0C0': 'Warm Pink',
599
'#800000': 'Brick Red',
601
'#D8A3A4': 'Shrimp Pink',
602
'#F8DDDD': 'Shell Pink',
603
'#BF5F00': 'Dark Orange',
605
'#FF9F40': 'Grapefruit',
606
'#FFBF80': 'Canteloupe',
608
'#482C1B': 'Dark Brick',
612
'#C49B71': 'Mustard',
613
'#E1C4A8': 'Pale Tan',
618
* @property _colorPicker
619
* @description The HTML Element containing the colorPicker
624
* @property STR_COLLAPSE
625
* @description String for Toolbar Collapse Button
628
STR_COLLAPSE: 'Collapse Toolbar',
630
* @property STR_SPIN_LABEL
631
* @description String for spinbutton dynamic label. Note the {VALUE} will be replaced with YAHOO.lang.substitute
634
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.',
636
* @property STR_SPIN_UP
637
* @description String for spinbutton up
640
STR_SPIN_UP: 'Click to increase the value of this input',
642
* @property STR_SPIN_DOWN
643
* @description String for spinbutton down
646
STR_SPIN_DOWN: 'Click to decrease the value of this input',
648
* @property _titlebar
649
* @description Object reference to the titlebar
655
* @description Standard browser detection
658
browser: YAHOO.env.ua,
661
* @property _buttonList
662
* @description Internal property list of current buttons in the toolbar
668
* @property _buttonGroupList
669
* @description Internal property list of current button groups in the toolbar
672
_buttonGroupList: null,
676
* @description Internal reference to the separator HTML Element for cloning
682
* @property _sepCount
683
* @description Internal refernce for counting separators, so we can give them a useful class name for styling
689
* @property draghandle
695
* @property _toolbarConfigs
703
* @property CLASS_CONTAINER
704
* @description Default CSS class to apply to the toolbar container element
707
CLASS_CONTAINER: 'yui-toolbar-container',
710
* @property CLASS_DRAGHANDLE
711
* @description Default CSS class to apply to the toolbar's drag handle element
714
CLASS_DRAGHANDLE: 'yui-toolbar-draghandle',
717
* @property CLASS_SEPARATOR
718
* @description Default CSS class to apply to all separators in the toolbar
721
CLASS_SEPARATOR: 'yui-toolbar-separator',
724
* @property CLASS_DISABLED
725
* @description Default CSS class to apply when the toolbar is disabled
728
CLASS_DISABLED: 'yui-toolbar-disabled',
731
* @property CLASS_PREFIX
732
* @description Default prefix for dynamically created class names
735
CLASS_PREFIX: 'yui-toolbar',
738
* @description The Toolbar class's initialization method
740
init: function(p_oElement, p_oAttributes) {
741
YAHOO.widget.Toolbar.superclass.init.call(this, p_oElement, p_oAttributes);
745
* @method initAttributes
746
* @description Initializes all of the configuration attributes used to create
748
* @param {Object} attr Object literal specifying a set of
749
* configuration attributes used to create the toolbar.
751
initAttributes: function(attr) {
752
YAHOO.widget.Toolbar.superclass.initAttributes.call(this, attr);
753
this.addClass(this.CLASS_CONTAINER);
756
* @attribute buttonType
757
* @description The buttonType to use (advanced or basic)
760
this.setAttributeConfig('buttonType', {
761
value: attr.buttonType || 'basic',
763
validator: function(type) {
771
method: function(type) {
772
if (type == 'advanced') {
773
if (YAHOO.widget.Button) {
774
this.buttonType = YAHOO.widget.ToolbarButtonAdvanced;
776
YAHOO.log('Can not find YAHOO.widget.Button', 'error', 'Toolbar');
777
this.buttonType = YAHOO.widget.ToolbarButton;
780
this.buttonType = YAHOO.widget.ToolbarButton;
788
* @description Object specifying the buttons to include in the toolbar
792
* { id: 'b3', type: 'button', label: 'Underline', value: 'underline' },
793
* { type: 'separator' },
794
* { id: 'b4', type: 'menu', label: 'Align', value: 'align',
796
* { text: "Left", value: 'alignleft' },
797
* { text: "Center", value: 'aligncenter' },
798
* { text: "Right", value: 'alignright' }
806
this.setAttributeConfig('buttons', {
809
method: function(data) {
810
for (var i in data) {
811
if (Lang.hasOwnProperty(data, i)) {
812
if (data[i].type == 'separator') {
814
} else if (data[i].group !== undefined) {
815
this.addButtonGroup(data[i]);
817
this.addButton(data[i]);
825
* @attribute disabled
826
* @description Boolean indicating if the toolbar should be disabled. It will also disable the draggable attribute if it is on.
830
this.setAttributeConfig('disabled', {
832
method: function(disabled) {
833
if (this.get('disabled') === disabled) {
837
this.addClass(this.CLASS_DISABLED);
838
this.set('draggable', false);
839
this.disableAllButtons();
841
this.removeClass(this.CLASS_DISABLED);
842
if (this._configs.draggable._initialConfig.value) {
843
//Draggable by default, set it back
844
this.set('draggable', true);
846
this.resetAllButtons();
853
* @description The container for the toolbar.
856
this.setAttributeConfig('cont', {
863
* @attribute grouplabels
864
* @description Boolean indicating if the toolbar should show the group label's text string.
868
this.setAttributeConfig('grouplabels', {
869
value: ((attr.grouplabels === false) ? false : true),
870
method: function(grouplabels) {
872
Dom.removeClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
874
Dom.addClass(this.get('cont'), (this.CLASS_PREFIX + '-nogrouplabels'));
879
* @attribute titlebar
880
* @description Boolean indicating if the toolbar should have a titlebar. If
881
* passed a string, it will use that as the titlebar text
883
* @type Boolean or String
885
this.setAttributeConfig('titlebar', {
887
method: function(titlebar) {
889
if (this._titlebar && this._titlebar.parentNode) {
890
this._titlebar.parentNode.removeChild(this._titlebar);
892
this._titlebar = document.createElement('DIV');
893
this._titlebar.tabIndex = '-1';
894
Event.on(this._titlebar, 'focus', function() {
897
Dom.addClass(this._titlebar, this.CLASS_PREFIX + '-titlebar');
898
if (Lang.isString(titlebar)) {
899
var h2 = document.createElement('h2');
901
h2.innerHTML = '<a href="#" tabIndex="0">' + titlebar + '</a>';
902
this._titlebar.appendChild(h2);
903
Event.on(h2.firstChild, 'click', function(ev) {
906
Event.on([h2, h2.firstChild], 'focus', function() {
910
if (this.get('firstChild')) {
911
this.insertBefore(this._titlebar, this.get('firstChild'));
913
this.appendChild(this._titlebar);
915
if (this.get('collapse')) {
916
this.set('collapse', true);
918
} else if (this._titlebar) {
919
if (this._titlebar && this._titlebar.parentNode) {
920
this._titlebar.parentNode.removeChild(this._titlebar);
928
* @attribute collapse
929
* @description Boolean indicating if the the titlebar should have a collapse button.
930
* The collapse button will not remove the toolbar, it will minimize it to the titlebar
934
this.setAttributeConfig('collapse', {
936
method: function(collapse) {
937
if (this._titlebar) {
938
var collapseEl = null;
939
var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
942
//There is already a collapse button
945
collapseEl = document.createElement('SPAN');
946
collapseEl.innerHTML = 'X';
947
collapseEl.title = this.STR_COLLAPSE;
949
Dom.addClass(collapseEl, 'collapse');
950
this._titlebar.appendChild(collapseEl);
951
Event.addListener(collapseEl, 'click', function() {
952
if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
953
this.collapse(false); //Expand Toolbar
955
this.collapse(); //Collapse Toolbar
959
collapseEl = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
961
if (Dom.hasClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed')) {
962
//We are closed, reopen the titlebar..
963
this.collapse(false); //Expand Toolbar
965
collapseEl[0].parentNode.removeChild(collapseEl[0]);
973
* @attribute draggable
974
* @description Boolean indicating if the toolbar should be draggable.
979
this.setAttributeConfig('draggable', {
980
value: (attr.draggable || false),
981
method: function(draggable) {
982
if (draggable && !this.get('titlebar')) {
983
YAHOO.log('Dragging enabled', 'info', 'Toolbar');
984
if (!this._dragHandle) {
985
this._dragHandle = document.createElement('SPAN');
986
this._dragHandle.innerHTML = '|';
987
this._dragHandle.setAttribute('title', 'Click to drag the toolbar');
988
this._dragHandle.id = this.get('id') + '_draghandle';
989
Dom.addClass(this._dragHandle, this.CLASS_DRAGHANDLE);
990
if (this.get('cont').hasChildNodes()) {
991
this.get('cont').insertBefore(this._dragHandle, this.get('cont').firstChild);
993
this.get('cont').appendChild(this._dragHandle);
995
this.dd = new YAHOO.util.DD(this.get('id'));
996
this.dd.setHandleElId(this._dragHandle.id);
1000
YAHOO.log('Dragging disabled', 'info', 'Toolbar');
1001
if (this._dragHandle) {
1002
this._dragHandle.parentNode.removeChild(this._dragHandle);
1003
this._dragHandle = null;
1007
if (this._titlebar) {
1009
this.dd = new YAHOO.util.DD(this.get('id'));
1010
this.dd.setHandleElId(this._titlebar);
1011
Dom.addClass(this._titlebar, 'draggable');
1013
Dom.removeClass(this._titlebar, 'draggable');
1021
validator: function(value) {
1023
if (!YAHOO.util.DD) {
1032
* @method addButtonGroup
1033
* @description Add a new button group to the toolbar. (uses addButton)
1034
* @param {Object} oGroup Object literal reference to the Groups Config (contains an array of button configs as well as the group label)
1036
addButtonGroup: function(oGroup) {
1037
if (!this.get('element')) {
1038
this._queue[this._queue.length] = ['addButtonGroup', arguments];
1042
if (!this.hasClass(this.CLASS_PREFIX + '-grouped')) {
1043
this.addClass(this.CLASS_PREFIX + '-grouped');
1045
var div = document.createElement('DIV');
1046
Dom.addClass(div, this.CLASS_PREFIX + '-group');
1047
Dom.addClass(div, this.CLASS_PREFIX + '-group-' + oGroup.group);
1049
var label = document.createElement('h3');
1050
label.innerHTML = oGroup.label;
1051
div.appendChild(label);
1053
if (!this.get('grouplabels')) {
1054
Dom.addClass(this.get('cont'), this.CLASS_PREFIX, '-nogrouplabels');
1057
this.get('cont').appendChild(div);
1059
//For accessibility, let's put all of the group buttons in an Unordered List
1060
var ul = document.createElement('ul');
1061
div.appendChild(ul);
1063
if (!this._buttonGroupList) {
1064
this._buttonGroupList = {};
1067
this._buttonGroupList[oGroup.group] = ul;
1069
for (var i = 0; i < oGroup.buttons.length; i++) {
1070
var li = document.createElement('li');
1071
li.className = this.CLASS_PREFIX + '-groupitem';
1073
if ((oGroup.buttons[i].type !== undefined) && oGroup.buttons[i].type == 'separator') {
1074
this.addSeparator(li);
1076
oGroup.buttons[i].container = li;
1077
this.addButton(oGroup.buttons[i]);
1082
* @method addButtonToGroup
1083
* @description Add a new button to a toolbar group. Buttons supported:
1084
* push, split, menu, select, color, spin
1085
* @param {Object} oButton Object literal reference to the Button's Config
1086
* @param {String} group The Group identifier passed into the initial config
1087
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1089
addButtonToGroup: function(oButton, group, after) {
1090
var groupCont = this._buttonGroupList[group];
1091
var li = document.createElement('li');
1092
li.className = this.CLASS_PREFIX + '-groupitem';
1093
oButton.container = li;
1094
this.addButton(oButton, after);
1095
groupCont.appendChild(li);
1099
* @description Add a new button to the toolbar. Buttons supported:
1100
* push, split, menu, select, color, spin
1101
* @param {Object} oButton Object literal reference to the Button's Config
1102
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1104
addButton: function(oButton, after) {
1105
if (!this.get('element')) {
1106
this._queue[this._queue.length] = ['addButton', arguments];
1109
if (!this._buttonList) {
1110
this._buttonList = [];
1112
YAHOO.log('Adding button of type: ' + oButton.type, 'info', 'Toolbar');
1113
if (!oButton.container) {
1114
oButton.container = this.get('cont');
1117
if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
1118
if (Lang.isArray(oButton.menu)) {
1119
for (var i in oButton.menu) {
1120
if (Lang.hasOwnProperty(oButton.menu, i)) {
1122
fn: function(ev, x, oMenu) {
1123
if (!oButton.menucmd) {
1124
oButton.menucmd = oButton.value;
1126
oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
1130
oButton.menu[i].onclick = funcObject;
1135
var _oButton = {}, skip = false;
1136
for (var o in oButton) {
1137
if (Lang.hasOwnProperty(oButton, o)) {
1138
if (!this._toolbarConfigs[o]) {
1139
_oButton[o] = oButton[o];
1143
if (oButton.type == 'select') {
1144
_oButton.type = 'menu';
1146
if (oButton.type == 'spin') {
1147
_oButton.type = 'push';
1149
if (_oButton.type == 'color') {
1150
if (YAHOO.widget.Overlay) {
1151
_oButton = this._makeColorButton(_oButton);
1156
if (_oButton.menu) {
1157
if ((YAHOO.widget.Overlay) && (oButton.menu instanceof YAHOO.widget.Overlay)) {
1158
oButton.menu.showEvent.subscribe(function() {
1159
this._button = _oButton;
1162
for (var m = 0; m < _oButton.menu.length; m++) {
1163
if (!_oButton.menu[m].value) {
1164
_oButton.menu[m].value = _oButton.menu[m].text;
1167
if (this.browser.webkit) {
1168
_oButton.focusmenu = false;
1175
//Add to .get('buttons') manually
1176
this._configs.buttons.value[this._configs.buttons.value.length] = oButton;
1178
var tmp = new this.buttonType(_oButton);
1179
tmp.get('element').tabIndex = '-1';
1180
tmp.get('element').setAttribute('role', 'button');
1181
tmp._selected = true;
1183
if (this.get('disabled')) {
1184
//Toolbar is disabled, disable the new button too!
1185
tmp.set('disabled', true);
1188
oButton.id = tmp.get('id');
1190
YAHOO.log('Button created (' + oButton.type + ')', 'info', 'Toolbar');
1193
var el = tmp.get('element');
1196
nextSib = after.get('element').nextSibling;
1197
} else if (after.nextSibling) {
1198
nextSib = after.nextSibling;
1201
nextSib.parentNode.insertBefore(el, nextSib);
1204
tmp.addClass(this.CLASS_PREFIX + '-' + tmp.get('value'));
1206
var icon = document.createElement('span');
1207
icon.className = this.CLASS_PREFIX + '-icon';
1208
tmp.get('element').insertBefore(icon, tmp.get('firstChild'));
1209
if (tmp._button.tagName.toLowerCase() == 'button') {
1210
tmp.get('element').setAttribute('unselectable', 'on');
1211
//Replace the Button HTML Element with an a href if it exists
1212
var a = document.createElement('a');
1213
a.innerHTML = tmp._button.innerHTML;
1216
Event.on(a, 'click', function(ev) {
1217
Event.stopEvent(ev);
1219
tmp._button.parentNode.replaceChild(a, tmp._button);
1223
if (oButton.type == 'select') {
1224
if (tmp._button.tagName.toLowerCase() == 'select') {
1225
icon.parentNode.removeChild(icon);
1226
var iel = tmp._button;
1227
var parEl = tmp.get('element');
1228
parEl.parentNode.replaceChild(iel, parEl);
1230
//Don't put a class on it if it's a real select element
1231
tmp.addClass(this.CLASS_PREFIX + '-select');
1234
if (oButton.type == 'spin') {
1235
if (!Lang.isArray(oButton.range)) {
1236
oButton.range = [ 10, 100 ];
1238
this._makeSpinButton(tmp, oButton);
1240
tmp.get('element').setAttribute('title', tmp.get('label'));
1241
if (oButton.type != 'spin') {
1242
if ((YAHOO.widget.Overlay) && (_oButton.menu instanceof YAHOO.widget.Overlay)) {
1243
var showPicker = function(ev) {
1245
if (ev.keyCode && (ev.keyCode == 9)) {
1249
if (this._colorPicker) {
1250
this._colorPicker._button = oButton.value;
1252
var menuEL = tmp.getMenu().element;
1253
if (Dom.getStyle(menuEL, 'visibility') == 'hidden') {
1254
tmp.getMenu().show();
1256
tmp.getMenu().hide();
1259
YAHOO.util.Event.stopEvent(ev);
1261
tmp.on('mousedown', showPicker, oButton, this);
1262
tmp.on('keydown', showPicker, oButton, this);
1264
} else if ((oButton.type != 'menu') && (oButton.type != 'select')) {
1265
tmp.on('keypress', this._buttonClick, oButton, this);
1266
tmp.on('mousedown', function(ev) {
1267
YAHOO.util.Event.stopEvent(ev);
1268
this._buttonClick(ev, oButton);
1270
tmp.on('click', function(ev) {
1271
YAHOO.util.Event.stopEvent(ev);
1274
//Stop the mousedown event so we can trap the selection in the editor!
1275
tmp.on('mousedown', function(ev) {
1276
YAHOO.util.Event.stopEvent(ev);
1278
tmp.on('click', function(ev) {
1279
YAHOO.util.Event.stopEvent(ev);
1281
tmp.on('change', function(ev) {
1282
if (!oButton.menucmd) {
1283
oButton.menucmd = oButton.value;
1285
oButton.value = ev.value;
1286
this._buttonClick(ev, oButton);
1290
//Hijack the mousedown event in the menu and make it fire a button click..
1291
tmp.on('appendTo', function() {
1293
if (tmp.getMenu() && tmp.getMenu().mouseDownEvent) {
1294
tmp.getMenu().mouseDownEvent.subscribe(function(ev, args) {
1295
YAHOO.log('mouseDownEvent', 'warn', 'Toolbar');
1296
var oMenu = args[1];
1297
YAHOO.util.Event.stopEvent(args[0]);
1298
tmp._onMenuClick(args[0], tmp);
1299
if (!oButton.menucmd) {
1300
oButton.menucmd = oButton.value;
1302
oButton.value = ((oMenu.value) ? oMenu.value : oMenu._oText.nodeValue);
1303
self._buttonClick.call(self, args[1], oButton);
1307
tmp.getMenu().clickEvent.subscribe(function(ev, args) {
1308
YAHOO.log('clickEvent', 'warn', 'Toolbar');
1309
YAHOO.util.Event.stopEvent(args[0]);
1311
tmp.getMenu().mouseUpEvent.subscribe(function(ev, args) {
1312
YAHOO.log('mouseUpEvent', 'warn', 'Toolbar');
1313
YAHOO.util.Event.stopEvent(args[0]);
1320
//Stop the mousedown event so we can trap the selection in the editor!
1321
tmp.on('mousedown', function(ev) {
1322
YAHOO.util.Event.stopEvent(ev);
1324
tmp.on('click', function(ev) {
1325
YAHOO.util.Event.stopEvent(ev);
1328
if (this.browser.ie) {
1330
//Add a couple of new events for IE
1331
tmp.DOM_EVENTS.focusin = true;
1332
tmp.DOM_EVENTS.focusout = true;
1334
//Stop them so we don't loose focus in the Editor
1335
tmp.on('focusin', function(ev) {
1336
YAHOO.util.Event.stopEvent(ev);
1339
tmp.on('focusout', function(ev) {
1340
YAHOO.util.Event.stopEvent(ev);
1342
tmp.on('click', function(ev) {
1343
YAHOO.util.Event.stopEvent(ev);
1347
if (this.browser.webkit) {
1348
//This will keep the document from gaining focus and the editor from loosing it..
1349
//Forcefully remove the focus calls in button!
1350
tmp.hasFocus = function() {
1354
this._buttonList[this._buttonList.length] = tmp;
1355
if ((oButton.type == 'menu') || (oButton.type == 'split') || (oButton.type == 'select')) {
1356
if (Lang.isArray(oButton.menu)) {
1357
YAHOO.log('Button type is (' + oButton.type + '), doing extra renderer work.', 'info', 'Toolbar');
1358
var menu = tmp.getMenu();
1359
if (menu && menu.renderEvent) {
1360
menu.renderEvent.subscribe(this._addMenuClasses, tmp);
1361
if (oButton.renderer) {
1362
menu.renderEvent.subscribe(oButton.renderer, tmp);
1371
* @method addSeparator
1372
* @description Add a new button separator to the toolbar.
1373
* @param {HTMLElement} cont Optional HTML element to insert this button into.
1374
* @param {HTMLElement} after Optional HTML element to insert this button after in the DOM.
1376
addSeparator: function(cont, after) {
1377
if (!this.get('element')) {
1378
this._queue[this._queue.length] = ['addSeparator', arguments];
1381
var sepCont = ((cont) ? cont : this.get('cont'));
1382
if (!this.get('element')) {
1383
this._queue[this._queue.length] = ['addSeparator', arguments];
1386
if (this._sepCount === null) {
1390
YAHOO.log('Separator does not yet exist, creating', 'info', 'Toolbar');
1391
this._sep = document.createElement('SPAN');
1392
Dom.addClass(this._sep, this.CLASS_SEPARATOR);
1393
this._sep.innerHTML = '|';
1395
YAHOO.log('Separator does exist, cloning', 'info', 'Toolbar');
1396
var _sep = this._sep.cloneNode(true);
1398
Dom.addClass(_sep, this.CLASS_SEPARATOR + '-' + this._sepCount);
1402
nextSib = after.get('element').nextSibling;
1403
} else if (after.nextSibling) {
1404
nextSib = after.nextSibling;
1409
if (nextSib == after) {
1410
nextSib.parentNode.appendChild(_sep);
1412
nextSib.parentNode.insertBefore(_sep, nextSib);
1416
sepCont.appendChild(_sep);
1421
* @method _createColorPicker
1423
* @description Creates the core DOM reference to the color picker menu item.
1424
* @param {String} id the id of the toolbar to prefix this DOM container with.
1426
_createColorPicker: function(id) {
1427
if (Dom.get(id + '_colors')) {
1428
Dom.get(id + '_colors').parentNode.removeChild(Dom.get(id + '_colors'));
1430
var picker = document.createElement('div');
1431
picker.className = 'yui-toolbar-colors';
1432
picker.id = id + '_colors';
1433
picker.style.display = 'none';
1434
Event.on(window, 'load', function() {
1435
document.body.appendChild(picker);
1438
this._colorPicker = picker;
1441
for (var i in this._colorData) {
1442
if (Lang.hasOwnProperty(this._colorData, i)) {
1443
html += '<a style="background-color: ' + i + '" href="#">' + i.replace('#', '') + '</a>';
1446
html += '<span><em>X</em><strong></strong></span>';
1447
window.setTimeout(function() {
1448
picker.innerHTML = html;
1451
Event.on(picker, 'mouseover', function(ev) {
1452
var picker = this._colorPicker;
1453
var em = picker.getElementsByTagName('em')[0];
1454
var strong = picker.getElementsByTagName('strong')[0];
1455
var tar = Event.getTarget(ev);
1456
if (tar.tagName.toLowerCase() == 'a') {
1457
em.style.backgroundColor = tar.style.backgroundColor;
1458
strong.innerHTML = this._colorData['#' + tar.innerHTML] + '<br>' + tar.innerHTML;
1461
Event.on(picker, 'focus', function(ev) {
1462
Event.stopEvent(ev);
1464
Event.on(picker, 'click', function(ev) {
1465
Event.stopEvent(ev);
1467
Event.on(picker, 'mousedown', function(ev) {
1468
Event.stopEvent(ev);
1469
var tar = Event.getTarget(ev);
1470
if (tar.tagName.toLowerCase() == 'a') {
1471
var retVal = this.fireEvent('colorPickerClicked', { type: 'colorPickerClicked', target: this, button: this._colorPicker._button, color: tar.innerHTML, colorName: this._colorData['#' + tar.innerHTML] } );
1472
if (retVal !== false) {
1474
color: tar.innerHTML,
1475
colorName: this._colorData['#' + tar.innerHTML],
1476
value: this._colorPicker._button
1479
this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
1481
this.getButtonByValue(this._colorPicker._button).getMenu().hide();
1486
* @method _resetColorPicker
1488
* @description Clears the currently selected color or mouseover color in the color picker.
1490
_resetColorPicker: function() {
1491
var em = this._colorPicker.getElementsByTagName('em')[0];
1492
var strong = this._colorPicker.getElementsByTagName('strong')[0];
1493
em.style.backgroundColor = 'transparent';
1494
strong.innerHTML = '';
1497
* @method _makeColorButton
1499
* @description Called to turn a "color" button into a menu button with an Overlay for the menu.
1500
* @param {Object} _oButton <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
1502
_makeColorButton: function(_oButton) {
1503
if (!this._colorPicker) {
1504
this._createColorPicker(this.get('id'));
1506
_oButton.type = 'color';
1507
_oButton.menu = new YAHOO.widget.Overlay(this.get('id') + '_' + _oButton.value + '_menu', { visible: false, position: 'absolute', iframe: true });
1508
_oButton.menu.setBody('');
1509
_oButton.menu.render(this.get('cont'));
1510
Dom.addClass(_oButton.menu.element, 'yui-button-menu');
1511
Dom.addClass(_oButton.menu.element, 'yui-color-button-menu');
1512
_oButton.menu.beforeShowEvent.subscribe(function() {
1513
_oButton.menu.cfg.setProperty('zindex', 5); //Re Adjust the overlays zIndex.. not sure why.
1514
_oButton.menu.cfg.setProperty('context', [this.getButtonById(_oButton.id).get('element'), 'tl', 'bl']); //Re Adjust the overlay.. not sure why.
1515
//Move the DOM reference of the color picker to the Overlay that we are about to show.
1516
this._resetColorPicker();
1517
var _p = this._colorPicker;
1518
if (_p.parentNode) {
1519
_p.parentNode.removeChild(_p);
1521
_oButton.menu.setBody('');
1522
_oButton.menu.appendToBody(_p);
1523
this._colorPicker.style.display = 'block';
1529
* @method _makeSpinButton
1530
* @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.
1531
* @param {Object} _button <a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> reference
1532
* @param {Object} oButton Object literal containing the buttons initial config
1534
_makeSpinButton: function(_button, oButton) {
1535
_button.addClass(this.CLASS_PREFIX + '-spinbutton');
1537
_par = _button._button.parentNode.parentNode, //parentNode of Button Element for appending child
1538
range = oButton.range,
1539
_b1 = document.createElement('a'),
1540
_b2 = document.createElement('a');
1543
_b1.tabIndex = '-1';
1544
_b2.tabIndex = '-1';
1546
//Setup the up and down arrows
1547
_b1.className = 'up';
1548
_b1.title = this.STR_SPIN_UP;
1549
_b1.innerHTML = this.STR_SPIN_UP;
1550
_b2.className = 'down';
1551
_b2.title = this.STR_SPIN_DOWN;
1552
_b2.innerHTML = this.STR_SPIN_DOWN;
1554
//Append them to the container
1555
_par.appendChild(_b1);
1556
_par.appendChild(_b2);
1558
var label = YAHOO.lang.substitute(this.STR_SPIN_LABEL, { VALUE: _button.get('label') });
1559
_button.set('title', label);
1561
var cleanVal = function(value) {
1562
value = ((value < range[0]) ? range[0] : value);
1563
value = ((value > range[1]) ? range[1] : value);
1567
var br = this.browser;
1569
var strLabel = this.STR_SPIN_LABEL;
1570
if (this._titlebar && this._titlebar.firstChild) {
1571
tbar = this._titlebar.firstChild;
1574
var _intUp = function(ev) {
1575
YAHOO.util.Event.stopEvent(ev);
1576
if (!_button.get('disabled') && (ev.keyCode != 9)) {
1577
var value = parseInt(_button.get('label'), 10);
1579
value = cleanVal(value);
1580
_button.set('label', ''+value);
1581
var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1582
_button.set('title', label);
1583
if (!br.webkit && tbar) {
1584
//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
1587
self._buttonClick(ev, oButton);
1591
var _intDown = function(ev) {
1592
YAHOO.util.Event.stopEvent(ev);
1593
if (!_button.get('disabled') && (ev.keyCode != 9)) {
1594
var value = parseInt(_button.get('label'), 10);
1596
value = cleanVal(value);
1598
_button.set('label', ''+value);
1599
var label = YAHOO.lang.substitute(strLabel, { VALUE: _button.get('label') });
1600
_button.set('title', label);
1601
if (!br.webkit && tbar) {
1602
//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
1605
self._buttonClick(ev, oButton);
1609
var _intKeyUp = function(ev) {
1610
if (ev.keyCode == 38) {
1612
} else if (ev.keyCode == 40) {
1614
} else if (ev.keyCode == 107 && ev.shiftKey) { //Plus Key
1616
} else if (ev.keyCode == 109 && ev.shiftKey) { //Minus Key
1621
//Handle arrow keys..
1622
_button.on('keydown', _intKeyUp, this, true);
1624
//Listen for the click on the up button and act on it
1625
//Listen for the click on the down button and act on it
1626
Event.on(_b1, 'mousedown',function(ev) {
1627
Event.stopEvent(ev);
1629
Event.on(_b2, 'mousedown', function(ev) {
1630
Event.stopEvent(ev);
1632
Event.on(_b1, 'click', _intUp, this, true);
1633
Event.on(_b2, 'click', _intDown, this, true);
1637
* @method _buttonClick
1638
* @description Click handler for all buttons in the toolbar.
1639
* @param {String} ev The event that was passed in.
1640
* @param {Object} info Object literal of information about the button that was clicked.
1642
_buttonClick: function(ev, info) {
1645
if (ev && ev.type == 'keypress') {
1646
if (ev.keyCode == 9) {
1648
} else if ((ev.keyCode === 13) || (ev.keyCode === 0) || (ev.keyCode === 32)) {
1655
var fireNextEvent = true,
1658
info.isSelected = this.isSelected(info.id);
1661
YAHOO.log('fireEvent::' + info.value + 'Click', 'info', 'Toolbar');
1662
retValue = this.fireEvent(info.value + 'Click', { type: info.value + 'Click', target: this.get('element'), button: info });
1663
if (retValue === false) {
1664
fireNextEvent = false;
1668
if (info.menucmd && fireNextEvent) {
1669
YAHOO.log('fireEvent::' + info.menucmd + 'Click', 'info', 'Toolbar');
1670
retValue = this.fireEvent(info.menucmd + 'Click', { type: info.menucmd + 'Click', target: this.get('element'), button: info });
1671
if (retValue === false) {
1672
fireNextEvent = false;
1675
if (fireNextEvent) {
1676
YAHOO.log('fireEvent::buttonClick', 'info', 'Toolbar');
1677
this.fireEvent('buttonClick', { type: 'buttonClick', target: this.get('element'), button: info });
1680
if (info.type == 'select') {
1681
var button = this.getButtonById(info.id);
1682
if (button.buttonType == 'rich') {
1683
var txt = info.value;
1684
for (var i = 0; i < info.menu.length; i++) {
1685
if (info.menu[i].value == info.value) {
1686
txt = info.menu[i].text;
1690
button.set('label', '<span class="yui-toolbar-' + info.menucmd + '-' + (info.value).replace(/ /g, '-').toLowerCase() + '">' + txt + '</span>');
1691
var _items = button.getMenu().getItems();
1692
for (var m = 0; m < _items.length; m++) {
1693
if (_items[m].value.toLowerCase() == info.value.toLowerCase()) {
1694
_items[m].cfg.setProperty('checked', true);
1696
_items[m].cfg.setProperty('checked', false);
1702
Event.stopEvent(ev);
1709
* @description Flag to determine if the arrow nav listeners have been attached
1715
* @property _navCounter
1716
* @description Internal counter for walking the buttons in the toolbar with the arrow keys
1722
* @method _navigateButtons
1723
* @description Handles the navigation/focus of toolbar buttons with the Arrow Keys
1724
* @param {Event} ev The Key Event
1726
_navigateButtons: function(ev) {
1727
switch (ev.keyCode) {
1730
if (ev.keyCode == 37) {
1735
if (this._navCounter > (this._buttonList.length - 1)) {
1736
this._navCounter = 0;
1738
if (this._navCounter < 0) {
1739
this._navCounter = (this._buttonList.length - 1);
1741
if (this._buttonList[this._navCounter]) {
1742
var el = this._buttonList[this._navCounter].get('element');
1743
if (this.browser.ie) {
1744
el = this._buttonList[this._navCounter].get('element').getElementsByTagName('a')[0];
1746
if (this._buttonList[this._navCounter].get('disabled')) {
1747
this._navigateButtons(ev);
1757
* @method _handleFocus
1758
* @description Sets up the listeners for the arrow key navigation
1760
_handleFocus: function() {
1761
if (!this._keyNav) {
1762
var ev = 'keypress';
1763
if (this.browser.ie) {
1766
Event.on(this.get('element'), ev, this._navigateButtons, this, true);
1767
this._keyNav = true;
1768
this._navCounter = -1;
1772
* @method getButtonById
1773
* @description Gets a button instance from the toolbar by is Dom id.
1774
* @param {String} id The Dom id to query for.
1775
* @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
1777
getButtonById: function(id) {
1778
var len = this._buttonList.length;
1779
for (var i = 0; i < len; i++) {
1780
if (this._buttonList[i] && this._buttonList[i].get('id') == id) {
1781
return this._buttonList[i];
1787
* @method getButtonByValue
1788
* @description Gets a button instance or a menuitem instance from the toolbar by it's value.
1789
* @param {String} value The button value to query for.
1790
* @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a> or <a href="YAHOO.widget.MenuItem.html">YAHOO.widget.MenuItem</a>}
1792
getButtonByValue: function(value) {
1793
var _buttons = this.get('buttons');
1794
var len = _buttons.length;
1795
for (var i = 0; i < len; i++) {
1796
if (_buttons[i].group !== undefined) {
1797
for (var m = 0; m < _buttons[i].buttons.length; m++) {
1798
if ((_buttons[i].buttons[m].value == value) || (_buttons[i].buttons[m].menucmd == value)) {
1799
return this.getButtonById(_buttons[i].buttons[m].id);
1801
if (_buttons[i].buttons[m].menu) { //Menu Button, loop through the values
1802
for (var s = 0; s < _buttons[i].buttons[m].menu.length; s++) {
1803
if (_buttons[i].buttons[m].menu[s].value == value) {
1804
return this.getButtonById(_buttons[i].buttons[m].id);
1810
if ((_buttons[i].value == value) || (_buttons[i].menucmd == value)) {
1811
return this.getButtonById(_buttons[i].id);
1813
if (_buttons[i].menu) { //Menu Button, loop through the values
1814
for (var j = 0; j < _buttons[i].menu.length; j++) {
1815
if (_buttons[i].menu[j].value == value) {
1816
return this.getButtonById(_buttons[i].id);
1825
* @method getButtonByIndex
1826
* @description Gets a button instance from the toolbar by is index in _buttonList.
1827
* @param {Number} index The index of the button in _buttonList.
1828
* @return {<a href="YAHOO.widget.ToolbarButton.html">YAHOO.widget.ToolbarButton</a>}
1830
getButtonByIndex: function(index) {
1831
if (this._buttonList[index]) {
1832
return this._buttonList[index];
1838
* @method getButtons
1839
* @description Returns an array of buttons in the current toolbar
1842
getButtons: function() {
1843
return this._buttonList;
1846
* @method disableButton
1847
* @description Disables a button in the toolbar.
1848
* @param {String/Number} id Disable a button by it's id, index or value.
1851
disableButton: function(id) {
1852
var button = getButton.call(this, id);
1854
button.set('disabled', true);
1860
* @method enableButton
1861
* @description Enables a button in the toolbar.
1862
* @param {String/Number} id Enable a button by it's id, index or value.
1865
enableButton: function(id) {
1866
if (this.get('disabled')) {
1869
var button = getButton.call(this, id);
1871
if (button.get('disabled')) {
1872
button.set('disabled', false);
1879
* @method isSelected
1880
* @description Tells if a button is selected or not.
1881
* @param {String/Number} id A button by it's id, index or value.
1884
isSelected: function(id) {
1885
var button = getButton.call(this, id);
1887
return button._selected;
1892
* @method selectButton
1893
* @description Selects a button in the toolbar.
1894
* @param {String/Number} id Select a button by it's id, index or value.
1895
* @param {String} value If this is a Menu Button, check this item in the menu
1898
selectButton: function(id, value) {
1899
var button = getButton.call(this, id);
1901
button.addClass('yui-button-selected');
1902
button.addClass('yui-button-' + button.get('value') + '-selected');
1903
button._selected = true;
1905
if (button.buttonType == 'rich') {
1906
var _items = button.getMenu().getItems();
1907
for (var m = 0; m < _items.length; m++) {
1908
if (_items[m].value == value) {
1909
_items[m].cfg.setProperty('checked', true);
1910
button.set('label', '<span class="yui-toolbar-' + button.get('value') + '-' + (value).replace(/ /g, '-').toLowerCase() + '">' + _items[m]._oText.nodeValue + '</span>');
1912
_items[m].cfg.setProperty('checked', false);
1922
* @method deselectButton
1923
* @description Deselects a button in the toolbar.
1924
* @param {String/Number} id Deselect a button by it's id, index or value.
1927
deselectButton: function(id) {
1928
var button = getButton.call(this, id);
1930
button.removeClass('yui-button-selected');
1931
button.removeClass('yui-button-' + button.get('value') + '-selected');
1932
button.removeClass('yui-button-hover');
1933
button._selected = false;
1939
* @method deselectAllButtons
1940
* @description Deselects all buttons in the toolbar.
1943
deselectAllButtons: function() {
1944
var len = this._buttonList.length;
1945
for (var i = 0; i < len; i++) {
1946
this.deselectButton(this._buttonList[i]);
1950
* @method disableAllButtons
1951
* @description Disables all buttons in the toolbar.
1954
disableAllButtons: function() {
1955
if (this.get('disabled')) {
1958
var len = this._buttonList.length;
1959
for (var i = 0; i < len; i++) {
1960
this.disableButton(this._buttonList[i]);
1964
* @method enableAllButtons
1965
* @description Enables all buttons in the toolbar.
1968
enableAllButtons: function() {
1969
if (this.get('disabled')) {
1972
var len = this._buttonList.length;
1973
for (var i = 0; i < len; i++) {
1974
this.enableButton(this._buttonList[i]);
1978
* @method resetAllButtons
1979
* @description Resets all buttons to their initial state.
1980
* @param {Object} _ex Except these buttons
1983
resetAllButtons: function(_ex) {
1984
if (!Lang.isObject(_ex)) {
1987
if (this.get('disabled')) {
1990
var len = this._buttonList.length;
1991
for (var i = 0; i < len; i++) {
1992
var _button = this._buttonList[i];
1994
var disabled = _button._configs.disabled._initialConfig.value;
1995
if (_ex[_button.get('id')]) {
1996
this.enableButton(_button);
1997
this.selectButton(_button);
2000
this.disableButton(_button);
2002
this.enableButton(_button);
2004
this.deselectButton(_button);
2010
* @method destroyButton
2011
* @description Destroy a button in the toolbar.
2012
* @param {String/Number} id Destroy a button by it's id or index.
2015
destroyButton: function(id) {
2016
var button = getButton.call(this, id);
2018
var thisID = button.get('id');
2021
var len = this._buttonList.length;
2022
for (var i = 0; i < len; i++) {
2023
if (this._buttonList[i] && this._buttonList[i].get('id') == thisID) {
2024
this._buttonList[i] = null;
2033
* @description Destroys the toolbar, all of it's elements and objects.
2036
destroy: function() {
2037
this.get('element').innerHTML = '';
2038
this.get('element').className = '';
2039
//Brutal Object Destroy
2040
for (var i in this) {
2041
if (Lang.hasOwnProperty(this, i)) {
2049
* @description Programatically collapse the toolbar.
2050
* @param {Boolean} collapse True to collapse, false to expand.
2052
collapse: function(collapse) {
2053
var el = Dom.getElementsByClassName('collapse', 'span', this._titlebar);
2054
if (collapse === false) {
2055
Dom.removeClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
2057
Dom.removeClass(el[0], 'collapsed');
2059
this.fireEvent('toolbarExpanded', { type: 'toolbarExpanded', target: this });
2062
Dom.addClass(el[0], 'collapsed');
2064
Dom.addClass(this.get('cont').parentNode, 'yui-toolbar-container-collapsed');
2065
this.fireEvent('toolbarCollapsed', { type: 'toolbarCollapsed', target: this });
2070
* @description Returns a string representing the toolbar.
2073
toString: function() {
2074
return 'Toolbar (#' + this.get('element').id + ') with ' + this._buttonList.length + ' buttons.';
2078
* @event buttonClick
2079
* @param {Object} o The object passed to this handler is the button config used to create the button.
2080
* @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.
2081
* @type YAHOO.util.CustomEvent
2085
* @param {Object} o The object passed to this handler is the button config used to create the button.
2086
* @description This is a special dynamic event that is created and dispatched based on the value property
2087
* of the button config. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
2091
* { type: 'button', value: 'test', value: 'testButton' }
2094
* With the valueClick event you could subscribe to this buttons click event with this:
2095
* tbar.in('testButtonClick', function() { alert('test button clicked'); })
2096
* @type YAHOO.util.CustomEvent
2099
* @event toolbarExpanded
2100
* @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.
2101
* @type YAHOO.util.CustomEvent
2104
* @event toolbarCollapsed
2105
* @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.
2106
* @type YAHOO.util.CustomEvent
2111
* @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>
2112
* @namespace YAHOO.widget
2113
* @requires yahoo, dom, element, event, toolbar
2114
* @optional animation, container_core, resize, dragdrop
2118
var Dom = YAHOO.util.Dom,
2119
Event = YAHOO.util.Event,
2121
Toolbar = YAHOO.widget.Toolbar;
2124
* 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.
2126
* @class SimpleEditor
2127
* @extends YAHOO.util.Element
2128
* @param {String/HTMLElement} el The textarea element to turn into an editor.
2129
* @param {Object} attrs Object liternal containing configuration parameters.
2132
YAHOO.widget.SimpleEditor = function(el, attrs) {
2133
YAHOO.log('SimpleEditor Initalizing', 'info', 'SimpleEditor');
2136
if (Lang.isObject(el) && (!el.tagName) && !attrs) {
2137
Lang.augmentObject(o, el); //Break the config reference
2138
el = document.createElement('textarea');
2139
this.DOMReady = true;
2141
var c = Dom.get(o.container);
2144
document.body.appendChild(el);
2148
Lang.augmentObject(o, attrs); //Break the config reference
2157
if (Lang.isString(el)) {
2160
if (oConfig.attributes.id) {
2161
id = oConfig.attributes.id;
2163
this.DOMReady = true;
2164
id = Dom.generateId(el);
2167
oConfig.element = el;
2169
var element_cont = document.createElement('DIV');
2170
oConfig.attributes.element_cont = new YAHOO.util.Element(element_cont, {
2171
id: id + '_container'
2173
var div = document.createElement('div');
2174
Dom.addClass(div, 'first-child');
2175
oConfig.attributes.element_cont.appendChild(div);
2177
if (!oConfig.attributes.toolbar_cont) {
2178
oConfig.attributes.toolbar_cont = document.createElement('DIV');
2179
oConfig.attributes.toolbar_cont.id = id + '_toolbar';
2180
div.appendChild(oConfig.attributes.toolbar_cont);
2182
var editorWrapper = document.createElement('DIV');
2183
div.appendChild(editorWrapper);
2184
oConfig.attributes.editor_wrapper = editorWrapper;
2186
YAHOO.widget.SimpleEditor.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
2190
YAHOO.extend(YAHOO.widget.SimpleEditor, YAHOO.util.Element, {
2193
* @property _resizeConfig
2194
* @description The default config for the Resize Utility
2206
* @method _setupResize
2207
* @description Creates the Resize instance and binds its events.
2209
_setupResize: function() {
2210
if (!YAHOO.util.DD || !YAHOO.util.Resize) { return false; }
2211
if (this.get('resize')) {
2213
Lang.augmentObject(config, this._resizeConfig); //Break the config reference
2214
this.resize = new YAHOO.util.Resize(this.get('element_cont').get('element'), config);
2215
this.resize.on('resize', function(args) {
2216
var anim = this.get('animate');
2217
this.set('animate', false);
2218
this.set('width', args.width + 'px');
2219
var h = args.height,
2220
th = (this.toolbar.get('element').clientHeight + 2),
2223
dh = (this.dompath.clientHeight + 1); //It has a 1px top border..
2225
var newH = (h - th - dh);
2226
this.set('height', newH + 'px');
2227
this.get('element_cont').setStyle('height', '');
2228
this.set('animate', anim);
2234
* @description A reference to the Resize object
2235
* @type YAHOO.util.Resize
2241
* @description Sets up the DD instance used from the 'drag' config option.
2243
_setupDD: function() {
2244
if (!YAHOO.util.DD) { return false; }
2245
if (this.get('drag')) {
2246
YAHOO.log('Attaching DD instance to Editor', 'info', 'SimpleEditor');
2247
var d = this.get('drag'),
2249
if (d === 'proxy') {
2250
dd = YAHOO.util.DDProxy;
2253
this.dd = new dd(this.get('element_cont').get('element'));
2254
this.toolbar.addClass('draggable');
2255
this.dd.setHandleElId(this.toolbar._titlebar);
2260
* @description A reference to the DragDrop object.
2261
* @type YAHOO.util.DD/YAHOO.util.DDProxy
2266
* @property _lastCommand
2267
* @description A cache of the last execCommand (used for Undo/Redo so they don't mark an undo level)
2271
_undoNodeChange: function() {},
2272
_storeUndo: function() {},
2276
* @description Checks a keyMap entry against a key event
2277
* @param {Object} k The _keyMap object
2278
* @param {Event} e The Mouse Event
2281
_checkKey: function(k, e) {
2283
if ((e.keyCode === k.key)) {
2284
if (k.mods && (k.mods.length > 0)) {
2286
for (var i = 0; i < k.mods.length; i++) {
2287
if (this.browser.mac) {
2288
if (k.mods[i] == 'ctrl') {
2292
if (e[k.mods[i] + 'Key'] === true) {
2296
if (val === k.mods.length) {
2303
//YAHOO.log('Shortcut Key Check: (' + k.key + ') return: ' + ret, 'info', 'SimpleEditor');
2309
* @description Named key maps for various actions in the Editor. Example: <code>CLOSE_WINDOW: { key: 87, mods: ['shift', 'ctrl'] }</code>.
2310
* 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.
2311
* @type {Object/Mixed}
2320
mods: ['shift', 'ctrl']
2331
mods: ['shift', 'ctrl']
2335
mods: ['shift', 'ctrl']
2339
mods: ['shift', 'ctrl']
2343
mods: ['shift', 'ctrl']
2347
mods: ['shift', 'ctrl']
2351
mods: ['shift', 'ctrl']
2359
mods: ['shift', 'ctrl']
2363
mods: ['shift', 'ctrl']
2367
mods: ['shift', 'ctrl']
2371
mods: ['shift', 'ctrl']
2376
* @method _cleanClassName
2377
* @description Makes a useable classname from dynamic data, by dropping it to lowercase and replacing spaces with -'s.
2378
* @param {String} str The classname to clean up
2381
_cleanClassName: function(str) {
2382
return str.replace(/ /g, '-').toLowerCase();
2385
* @property _textarea
2386
* @description Flag to determine if we are using a textarea or an HTML Node.
2391
* @property _docType
2392
* @description The DOCTYPE to use in the editable container.
2395
_docType: '<!DOCTYPE HTML PUBLIC "-/'+'/W3C/'+'/DTD HTML 4.01/'+'/EN" "http:/'+'/www.w3.org/TR/html4/strict.dtd">',
2397
* @property editorDirty
2398
* @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.
2403
* @property _defaultCSS
2404
* @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' }
2407
_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; }',
2409
* @property _defaultToolbar
2411
* @description Default toolbar config.
2414
_defaultToolbar: null,
2416
* @property _lastButton
2418
* @description The last button pressed, so we don't disable it.
2423
* @property _baseHREF
2425
* @description The base location of the editable page (this page) so that relative paths for image work.
2428
_baseHREF: function() {
2429
var href = document.location.href;
2430
if (href.indexOf('?') !== -1) { //Remove the query string
2431
href = href.substring(0, href.indexOf('?'));
2433
href = href.substring(0, href.lastIndexOf('/')) + '/';
2437
* @property _lastImage
2439
* @description Safari reference for the last image selected (for styling as selected).
2444
* @property _blankImageLoaded
2446
* @description Don't load the blank image more than once..
2449
_blankImageLoaded: null,
2451
* @property _fixNodesTimer
2453
* @description Holder for the fixNodes timer
2456
_fixNodesTimer: null,
2458
* @property _nodeChangeTimer
2460
* @description Holds a reference to the nodeChange setTimeout call
2463
_nodeChangeTimer: null,
2465
* @property _lastNodeChangeEvent
2467
* @description Flag to determine the last event that fired a node change
2470
_lastNodeChangeEvent: null,
2472
* @property _lastNodeChange
2474
* @description Flag to determine when the last node change was fired
2479
* @property _rendered
2481
* @description Flag to determine if editor has been rendered or not
2486
* @property DOMReady
2488
* @description Flag to determine if DOM is ready or not
2493
* @property _selection
2495
* @description Holder for caching iframe selections
2502
* @description DOM Element holder for the editor Mask when disabled
2507
* @property _showingHiddenElements
2509
* @description Status of the hidden elements button
2512
_showingHiddenElements: null,
2514
* @property currentWindow
2515
* @description A reference to the currently open EditorWindow
2518
currentWindow: null,
2520
* @property currentEvent
2521
* @description A reference to the current editor event
2526
* @property operaEvent
2528
* @description setTimeout holder for Opera and Image DoubleClick event..
2533
* @property currentFont
2534
* @description A reference to the last font selected from the Toolbar
2539
* @property currentElement
2540
* @description A reference to the current working element in the editor
2543
currentElement: null,
2546
* @description A reference to the dompath container for writing the current working dom path to.
2551
* @property beforeElement
2552
* @description A reference to the H2 placed before the editor for Accessibilty.
2555
beforeElement: null,
2557
* @property afterElement
2558
* @description A reference to the H2 placed after the editor for Accessibilty.
2563
* @property invalidHTML
2564
* @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.
2582
* @description Local property containing the <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a> instance
2583
* @type <a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>
2588
* @property _contentTimer
2589
* @description setTimeout holder for documentReady check
2591
_contentTimer: null,
2594
* @property _contentTimerCounter
2595
* @description Counter to check the number of times the body is polled for before giving up
2598
_contentTimerCounter: 0,
2601
* @property _disabled
2602
* @description The Toolbar items that should be disabled if there is no selection present in the editor.
2605
_disabled: [ 'createlink', 'fontname', 'fontsize', 'forecolor', 'backcolor' ],
2608
* @property _alwaysDisabled
2609
* @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
2612
_alwaysDisabled: { undo: true, redo: true },
2615
* @property _alwaysEnabled
2616
* @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
2619
_alwaysEnabled: { },
2622
* @property _semantic
2623
* @description The Toolbar commands that we should attempt to make tags out of instead of using styles.
2626
_semantic: { 'bold': true, 'italic' : true, 'underline' : true },
2629
* @property _tag2cmd
2630
* @description A tag map of HTML tags to convert to the different types of commands so we can select the proper toolbar button.
2639
'sup': 'superscript',
2641
'img': 'insertimage',
2643
'ul' : 'insertunorderedlist',
2644
'ol' : 'insertorderedlist'
2648
* @private _createIframe
2649
* @description Creates the DOM and YUI Element for the iFrame editor area.
2650
* @param {String} id The string ID to prefix the iframe with
2651
* @return {Object} iFrame object
2653
_createIframe: function() {
2654
var ifrmDom = document.createElement('iframe');
2655
ifrmDom.id = this.get('id') + '_editor';
2663
allowTransparency: 'true',
2666
if (this.get('autoHeight')) {
2667
config.scrolling = 'no';
2669
for (var i in config) {
2670
if (Lang.hasOwnProperty(config, i)) {
2671
ifrmDom.setAttribute(i, config[i]);
2674
var isrc = 'javascript:;';
2675
if (this.browser.ie) {
2676
//isrc = 'about:blank';
2677
//TODO - Check this, I have changed it before..
2678
isrc = 'javascript:false;';
2680
ifrmDom.setAttribute('src', isrc);
2681
var ifrm = new YAHOO.util.Element(ifrmDom);
2682
ifrm.setStyle('visibility', 'hidden');
2686
* @private _isElement
2687
* @description Checks to see if an Element reference is a valid one and has a certain tag type
2688
* @param {HTMLElement} el The element to check
2689
* @param {String} tag The tag that the element needs to be
2692
_isElement: function(el, tag) {
2693
if (el && el.tagName && (el.tagName.toLowerCase() == tag)) {
2696
if (el && el.getAttribute && (el.getAttribute('tag') == tag)) {
2702
* @private _hasParent
2703
* @description Checks to see if an Element reference or one of it's parents is a valid one and has a certain tag type
2704
* @param {HTMLElement} el The element to check
2705
* @param {String} tag The tag that the element needs to be
2706
* @return HTMLElement
2708
_hasParent: function(el, tag) {
2709
if (!el || !el.parentNode) {
2713
while (el.parentNode) {
2714
if (this._isElement(el, tag)) {
2717
if (el.parentNode) {
2728
* @description Get the Document of the IFRAME
2731
_getDoc: function() {
2734
if (this.get('iframe')) {
2735
if (this.get('iframe').get) {
2736
if (this.get('iframe').get('element')) {
2738
if (this.get('iframe').get('element').contentWindow) {
2739
if (this.get('iframe').get('element').contentWindow.document) {
2740
value = this.get('iframe').get('element').contentWindow.document;
2753
* @method _getWindow
2754
* @description Get the Window of the IFRAME
2757
_getWindow: function() {
2758
return this.get('iframe').get('element').contentWindow;
2762
* @description Attempt to set the focus of the iframes window.
2765
this._getWindow().focus();
2769
* @depreciated - This should not be used, moved to this.focus();
2770
* @method _focusWindow
2771
* @description Attempt to set the focus of the iframes window.
2773
_focusWindow: function() {
2774
YAHOO.log('_focusWindow: depreciated in favor of this.focus()', 'warn', 'Editor');
2779
* @method _hasSelection
2780
* @description Determines if there is a selection in the editor document.
2783
_hasSelection: function() {
2784
var sel = this._getSelection();
2785
var range = this._getRange();
2788
if (!sel || !range) {
2793
if (this.browser.ie || this.browser.opera) {
2801
if (this.browser.webkit) {
2802
if (sel+'' !== '') {
2806
if (sel && (sel.toString() !== '') && (sel !== undefined)) {
2815
* @method _getSelection
2816
* @description Handles the different selection objects across the A-Grade list.
2817
* @return {Object} Selection Object
2819
_getSelection: function() {
2821
if (this._getDoc() && this._getWindow()) {
2822
if (this._getDoc().selection) {
2823
_sel = this._getDoc().selection;
2825
_sel = this._getWindow().getSelection();
2827
//Handle Safari's lack of Selection Object
2828
if (this.browser.webkit) {
2829
if (_sel.baseNode) {
2830
this._selection = {};
2831
this._selection.baseNode = _sel.baseNode;
2832
this._selection.baseOffset = _sel.baseOffset;
2833
this._selection.extentNode = _sel.extentNode;
2834
this._selection.extentOffset = _sel.extentOffset;
2835
} else if (this._selection !== null) {
2836
_sel = this._getWindow().getSelection();
2837
_sel.setBaseAndExtent(
2838
this._selection.baseNode,
2839
this._selection.baseOffset,
2840
this._selection.extentNode,
2841
this._selection.extentOffset);
2842
this._selection = null;
2850
* @method _selectNode
2851
* @description Places the highlight around a given node
2852
* @param {HTMLElement} node The node to select
2854
_selectNode: function(node, collapse) {
2858
var sel = this._getSelection(),
2861
if (this.browser.ie) {
2862
try { //IE freaks out here sometimes..
2863
range = this._getDoc().body.createTextRange();
2864
range.moveToElementText(node);
2867
YAHOO.log('IE failed to select element: ' + node.tagName, 'warn', 'SimpleEditor');
2869
} else if (this.browser.webkit) {
2871
sel.setBaseAndExtent(node, 1, node, node.innerText.length);
2873
sel.setBaseAndExtent(node, 0, node, node.innerText.length);
2875
} else if (this.browser.opera) {
2876
sel = this._getWindow().getSelection();
2877
range = this._getDoc().createRange();
2878
range.selectNode(node);
2879
sel.removeAllRanges();
2880
sel.addRange(range);
2882
range = this._getDoc().createRange();
2883
range.selectNodeContents(node);
2884
sel.removeAllRanges();
2885
sel.addRange(range);
2887
//TODO - Check Performance
2893
* @description Handles the different range objects across the A-Grade list.
2894
* @return {Object} Range Object
2896
_getRange: function() {
2897
var sel = this._getSelection();
2903
if (this.browser.webkit && !sel.getRangeAt) {
2904
var _range = this._getDoc().createRange();
2906
_range.setStart(sel.anchorNode, sel.anchorOffset);
2907
_range.setEnd(sel.focusNode, sel.focusOffset);
2909
_range = this._getWindow().getSelection()+'';
2914
if (this.browser.ie || this.browser.opera) {
2916
return sel.createRange();
2922
if (sel.rangeCount > 0) {
2923
return sel.getRangeAt(0);
2929
* @method _setDesignMode
2930
* @description Sets the designMode of the iFrame document.
2931
* @param {String} state This should be either on or off
2933
_setDesignMode: function(state) {
2936
//SourceForge Bug #1807057
2937
if (this.browser.ie && (state.toLowerCase() == 'off')) {
2941
this._getDoc().designMode = state;
2947
* @method _toggleDesignMode
2948
* @description Toggles the designMode of the iFrame document on and off.
2949
* @return {String} The state that it was set to.
2951
_toggleDesignMode: function() {
2952
var _dMode = this._getDoc().designMode.toLowerCase(),
2954
if (_dMode == 'on') {
2957
this._setDesignMode(_state);
2962
* @property _focused
2963
* @description Holder for trapping focus/blur state and prevent double events
2969
* @method _handleFocus
2970
* @description Handles the focus of the iframe. Note, this is window focus event, not an Editor focus event.
2971
* @param {Event} e The DOM Event
2973
_handleFocus: function(e) {
2974
if (!this._focused) {
2975
YAHOO.log('Editor Window Focused', 'info', 'SimpleEditor');
2976
this._focused = true;
2977
this.fireEvent('editorWindowFocus', { type: 'editorWindowFocus', target: this });
2982
* @method _handleBlur
2983
* @description Handles the blur of the iframe. Note, this is window blur event, not an Editor blur event.
2984
* @param {Event} e The DOM Event
2986
_handleBlur: function(e) {
2987
if (this._focused) {
2988
YAHOO.log('Editor Window Blurred', 'info', 'SimpleEditor');
2989
this._focused = false;
2990
this.fireEvent('editorWindowBlur', { type: 'editorWindowBlur', target: this });
2995
* @method _initEditorEvents
2996
* @description This method sets up the listeners on the Editors document.
2998
_initEditorEvents: function() {
2999
//Setup Listeners on iFrame
3000
var doc = this._getDoc(),
3001
win = this._getWindow();
3003
Event.on(doc, 'mouseup', this._handleMouseUp, this, true);
3004
Event.on(doc, 'mousedown', this._handleMouseDown, this, true);
3005
Event.on(doc, 'click', this._handleClick, this, true);
3006
Event.on(doc, 'dblclick', this._handleDoubleClick, this, true);
3007
Event.on(doc, 'keypress', this._handleKeyPress, this, true);
3008
Event.on(doc, 'keyup', this._handleKeyUp, this, true);
3009
Event.on(doc, 'keydown', this._handleKeyDown, this, true);
3010
/* TODO -- Everyone but Opera works here..
3011
Event.on(doc, 'paste', function() {
3012
YAHOO.log('PASTE', 'info', 'SimpleEditor');
3017
Event.on(win, 'focus', this._handleFocus, this, true);
3018
Event.on(win, 'blur', this._handleBlur, this, true);
3022
* @method _removeEditorEvents
3023
* @description This method removes the listeners on the Editors document (for disabling).
3025
_removeEditorEvents: function() {
3026
//Remove Listeners on iFrame
3027
var doc = this._getDoc(),
3028
win = this._getWindow();
3030
Event.removeListener(doc, 'mouseup', this._handleMouseUp, this, true);
3031
Event.removeListener(doc, 'mousedown', this._handleMouseDown, this, true);
3032
Event.removeListener(doc, 'click', this._handleClick, this, true);
3033
Event.removeListener(doc, 'dblclick', this._handleDoubleClick, this, true);
3034
Event.removeListener(doc, 'keypress', this._handleKeyPress, this, true);
3035
Event.removeListener(doc, 'keyup', this._handleKeyUp, this, true);
3036
Event.removeListener(doc, 'keydown', this._handleKeyDown, this, true);
3039
Event.removeListener(win, 'focus', this._handleFocus, this, true);
3040
Event.removeListener(win, 'blur', this._handleBlur, this, true);
3042
_fixWebkitDivs: function() {
3043
if (this.browser.webkit) {
3044
var divs = this._getDoc().body.getElementsByTagName('div');
3045
Dom.addClass(divs, 'yui-wk-div');
3050
* @method _initEditor
3051
* @description This method is fired from _checkLoaded when the document is ready. It turns on designMode and set's up the listeners.
3053
_initEditor: function() {
3054
if (this.browser.ie) {
3055
this._getDoc().body.style.margin = '0';
3057
if (!this.get('disabled')) {
3058
if (this._getDoc().designMode.toLowerCase() != 'on') {
3059
this._setDesignMode('on');
3060
this._contentTimerCounter = 0;
3063
if (!this._getDoc().body) {
3064
YAHOO.log('Body is null, check again', 'error', 'SimpleEditor');
3065
this._contentTimerCounter = 0;
3066
this._checkLoaded();
3070
YAHOO.log('editorLoaded', 'info', 'SimpleEditor');
3071
this.toolbar.on('buttonClick', this._handleToolbarClick, this, true);
3072
if (!this.get('disabled')) {
3073
this._initEditorEvents();
3074
this.toolbar.set('disabled', false);
3077
this.fireEvent('editorContentLoaded', { type: 'editorLoaded', target: this });
3078
this._fixWebkitDivs();
3079
if (this.get('dompath')) {
3080
YAHOO.log('Delayed DomPath write', 'info', 'SimpleEditor');
3082
setTimeout(function() {
3083
self._writeDomPath.call(self);
3084
self._setupResize.call(self);
3088
for (var i in this.browser) {
3089
if (this.browser[i]) {
3093
if (this.get('ptags')) {
3096
Dom.addClass(this._getDoc().body, br.join(' '));
3097
this.nodeChange(true);
3101
* @method _checkLoaded
3102
* @description Called from a setTimeout loop to check if the iframes body.onload event has fired, then it will init the editor.
3104
_checkLoaded: function() {
3105
this._contentTimerCounter++;
3106
if (this._contentTimer) {
3107
clearTimeout(this._contentTimer);
3109
if (this._contentTimerCounter > 500) {
3110
YAHOO.log('ERROR: Body Did Not load', 'error', 'SimpleEditor');
3115
if (this._getDoc() && this._getDoc().body) {
3116
if (this.browser.ie) {
3117
if (this._getDoc().body.readyState == 'complete') {
3121
if (this._getDoc().body._rteLoaded === true) {
3128
YAHOO.log('checking body (e)' + e, 'error', 'SimpleEditor');
3131
if (init === true) {
3132
//The onload event has fired, clean up after ourselves and fire the _initEditor method
3133
YAHOO.log('Firing _initEditor', 'info', 'SimpleEditor');
3137
this._contentTimer = setTimeout(function() {
3138
self._checkLoaded.call(self);
3144
* @method _setInitialContent
3145
* @description This method will open the iframes content document and write the textareas value into it, then start the body.onload checking.
3147
_setInitialContent: function() {
3148
YAHOO.log('Populating editor body with contents of the text area', 'info', 'SimpleEditor');
3150
var value = ((this._textarea) ? this.get('element').value : this.get('element').innerHTML),
3153
if ((value === '') && this.browser.gecko) {
3154
//It seems that Gecko doesn't like an empty body so we have to give it something..
3158
var html = Lang.substitute(this.get('html'), {
3159
TITLE: this.STR_TITLE,
3160
CONTENT: this._cleanIncomingHTML(value),
3161
CSS: this.get('css'),
3162
HIDDEN_CSS: ((this.get('hiddencss')) ? this.get('hiddencss') : '/* No Hidden CSS */'),
3163
EXTRA_CSS: ((this.get('extracss')) ? this.get('extracss') : '/* No Extra CSS */')
3166
if (document.compatMode != 'BackCompat') {
3167
YAHOO.log('Adding Doctype to editable area', 'info', 'SimpleEditor');
3168
html = this._docType + "\n" + html;
3170
YAHOO.log('DocType skipped because we are in BackCompat Mode.', 'warn', 'SimpleEditor');
3173
if (this.browser.ie || this.browser.webkit || this.browser.opera || (navigator.userAgent.indexOf('Firefox/1.5') != -1)) {
3174
//Firefox 1.5 doesn't like setting designMode on an document created with a data url
3177
if (this.browser.air) {
3178
doc = this._getDoc().implementation.createHTMLDocument();
3179
var origDoc = this._getDoc();
3185
var node = origDoc.importNode(doc.getElementsByTagName("html")[0], true);
3186
origDoc.replaceChild(node, origDoc.getElementsByTagName("html")[0]);
3187
origDoc.body._rteLoaded = true;
3189
doc = this._getDoc();
3195
YAHOO.log('Setting doc failed.. (_setInitialContent)', 'error', 'SimpleEditor');
3196
//Safari will only be here if we are hidden
3200
//This keeps Firefox 2 from writing the iframe to history preserving the back buttons functionality
3201
this.get('iframe').get('element').src = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
3203
this.get('iframe').setStyle('visibility', '');
3205
this._checkLoaded();
3210
* @method _setMarkupType
3211
* @param {String} action The action to take. Possible values are: css, default or semantic
3212
* @description This method will turn on/off the useCSS execCommand.
3214
_setMarkupType: function(action) {
3215
switch (this.get('markup')) {
3217
this._setEditorStyle(true);
3220
this._setEditorStyle(false);
3224
if (this._semantic[action]) {
3225
this._setEditorStyle(false);
3227
this._setEditorStyle(true);
3233
* Set the editor to use CSS instead of HTML
3234
* @param {Booleen} stat True/False
3236
_setEditorStyle: function(stat) {
3238
this._getDoc().execCommand('useCSS', false, !stat);
3244
* @method _getSelectedElement
3245
* @description This method will attempt to locate the element that was last interacted with, either via selection, location or event.
3246
* @return {HTMLElement} The currently selected element.
3248
_getSelectedElement: function() {
3249
var doc = this._getDoc(),
3255
if (this.browser.ie) {
3256
this.currentEvent = this._getWindow().event; //Event utility assumes window.event, so we need to reset it to this._getWindow().event;
3257
range = this._getRange();
3259
elm = range.item ? range.item(0) : range.parentElement();
3260
if (this._hasSelection()) {
3262
//WTF.. Why can't I get an element reference here?!??!
3264
if (elm === doc.body) {
3268
if ((this.currentEvent !== null) && (this.currentEvent.keyCode === 0)) {
3269
elm = Event.getTarget(this.currentEvent);
3272
sel = this._getSelection();
3273
range = this._getRange();
3275
if (!sel || !range) {
3279
if (!this._hasSelection() && this.browser.webkit3) {
3282
if (this.browser.gecko) {
3284
if (range.startContainer) {
3285
if (range.startContainer.nodeType === 3) {
3286
elm = range.startContainer.parentNode;
3287
} else if (range.startContainer.nodeType === 1) {
3288
elm = range.startContainer;
3291
if (this.currentEvent) {
3292
var tar = Event.getTarget(this.currentEvent);
3293
if (!this._isElement(tar, 'html')) {
3303
if (sel.anchorNode && (sel.anchorNode.nodeType == 3)) {
3304
if (sel.anchorNode.parentNode) { //next check parentNode
3305
elm = sel.anchorNode.parentNode;
3307
if (sel.anchorNode.nextSibling != sel.focusNode.nextSibling) {
3308
elm = sel.anchorNode.nextSibling;
3311
if (this._isElement(elm, 'br')) {
3315
elm = range.commonAncestorContainer;
3316
if (!range.collapsed) {
3317
if (range.startContainer == range.endContainer) {
3318
if (range.startOffset - range.endOffset < 2) {
3319
if (range.startContainer.hasChildNodes()) {
3320
elm = range.startContainer.childNodes[range.startOffset];
3329
if (this.currentEvent !== null) {
3331
switch (this.currentEvent.type) {
3335
if (this.browser.webkit) {
3336
elm = Event.getTarget(this.currentEvent);
3344
YAHOO.log('Firefox 1.5 errors here: ' + e, 'error', 'SimpleEditor');
3346
} else if ((this.currentElement && this.currentElement[0]) && (!this.browser.ie)) {
3347
//TODO is this still needed?
3348
//elm = this.currentElement[0];
3352
if (this.browser.opera || this.browser.webkit) {
3353
if (this.currentEvent && !elm) {
3354
elm = YAHOO.util.Event.getTarget(this.currentEvent);
3357
if (!elm || !elm.tagName) {
3360
if (this._isElement(elm, 'html')) {
3361
//Safari sometimes gives us the HTML node back..
3364
if (this._isElement(elm, 'body')) {
3365
//make sure that body means this body not the parent..
3368
if (elm && !elm.parentNode) { //Not in document
3371
if (elm === undefined) {
3378
* @method _getDomPath
3379
* @description This method will attempt to build the DOM path from the currently selected element.
3380
* @param HTMLElement el The element to start with, if not provided _getSelectedElement is used
3381
* @return {Array} An array of node references that will create the DOM Path.
3383
_getDomPath: function(el) {
3385
el = this._getSelectedElement();
3388
while (el !== null) {
3389
if (el.ownerDocument != this._getDoc()) {
3393
//Check to see if we get el.nodeName and nodeType
3394
if (el.nodeName && el.nodeType && (el.nodeType == 1)) {
3395
domPath[domPath.length] = el;
3398
if (this._isElement(el, 'body')) {
3404
if (domPath.length === 0) {
3405
if (this._getDoc() && this._getDoc().body) {
3406
domPath[0] = this._getDoc().body;
3409
return domPath.reverse();
3413
* @method _writeDomPath
3414
* @description Write the current DOM path out to the dompath container below the editor.
3416
_writeDomPath: function() {
3417
var path = this._getDomPath(),
3422
for (var i = 0; i < path.length; i++) {
3423
var tag = path[i].tagName.toLowerCase();
3424
if ((tag == 'ol') && (path[i].type)) {
3425
tag += ':' + path[i].type;
3427
if (Dom.hasClass(path[i], 'yui-tag')) {
3428
tag = path[i].getAttribute('tag');
3430
if ((this.get('markup') == 'semantic') || (this.get('markup') == 'xhtml')) {
3432
case 'b': tag = 'strong'; break;
3433
case 'i': tag = 'em'; break;
3436
if (!Dom.hasClass(path[i], 'yui-non')) {
3437
if (Dom.hasClass(path[i], 'yui-tag')) {
3440
classPath = ((path[i].className !== '') ? '.' + path[i].className.replace(/ /g, '.') : '');
3441
if ((classPath.indexOf('yui') != -1) || (classPath.toLowerCase().indexOf('apple-style-span') != -1)) {
3444
pathStr = tag + ((path[i].id) ? '#' + path[i].id : '') + classPath;
3451
if (path[i].getAttribute('href', 2)) {
3452
pathStr += ':' + path[i].getAttribute('href', 2).replace('mailto:', '').replace('http:/'+'/', '').replace('https:/'+'/', ''); //May need to add others here ftp
3456
var h = path[i].height;
3457
var w = path[i].width;
3458
if (path[i].style.height) {
3459
h = parseInt(path[i].style.height, 10);
3461
if (path[i].style.width) {
3462
w = parseInt(path[i].style.width, 10);
3464
pathStr += '(' + w + 'x' + h + ')';
3468
if (pathStr.length > 10) {
3469
pathStr = '<span title="' + pathStr + '">' + pathStr.substring(0, 10) + '...' + '</span>';
3471
pathStr = '<span title="' + pathStr + '">' + pathStr + '</span>';
3473
pathArr[pathArr.length] = pathStr;
3476
var str = pathArr.join(' ' + this.SEP_DOMPATH + ' ');
3477
//Prevent flickering
3478
if (this.dompath.innerHTML != str) {
3479
this.dompath.innerHTML = str;
3485
* @description Fix href and imgs as well as remove invalid HTML.
3487
_fixNodes: function() {
3488
var doc = this._getDoc(),
3491
for (var v in this.invalidHTML) {
3492
if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
3493
if (v.toLowerCase() != 'span') {
3494
var tags = doc.body.getElementsByTagName(v);
3496
for (var i = 0; i < tags.length; i++) {
3503
for (var h = 0; h < els.length; h++) {
3504
if (els[h].parentNode) {
3505
if (Lang.isObject(this.invalidHTML[els[h].tagName.toLowerCase()]) && this.invalidHTML[els[h].tagName.toLowerCase()].keepContents) {
3506
this._swapEl(els[h], 'span', function(el) {
3507
el.className = 'yui-non';
3510
els[h].parentNode.removeChild(els[h]);
3514
var imgs = this._getDoc().getElementsByTagName('img');
3515
Dom.addClass(imgs, 'yui-img');
3519
* @method _isNonEditable
3520
* @param Event ev The Dom event being checked
3521
* @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.
3522
* 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
3523
* disable and enable the Editor's toolbar based on the noedit state.
3526
_isNonEditable: function(ev) {
3527
if (this.get('allowNoEdit')) {
3528
var el = Event.getTarget(ev);
3529
if (this._isElement(el, 'html')) {
3532
var path = this._getDomPath(el);
3533
for (var i = (path.length - 1); i > -1; i--) {
3534
if (Dom.hasClass(path[i], this.CLASS_NOEDIT)) {
3535
//if (this.toolbar.get('disabled') === false) {
3536
// this.toolbar.set('disabled', true);
3539
this._getDoc().execCommand('enableObjectResizing', false, 'false');
3542
Event.stopEvent(ev);
3543
YAHOO.log('CLASS_NOEDIT found in DOM Path, stopping event', 'info', 'SimpleEditor');
3547
//if (this.toolbar.get('disabled') === true) {
3548
//Should only happen once..
3549
//this.toolbar.set('disabled', false);
3551
this._getDoc().execCommand('enableObjectResizing', false, 'true');
3559
* @method _setCurrentEvent
3560
* @param {Event} ev The event to cache
3561
* @description Sets the current event property
3563
_setCurrentEvent: function(ev) {
3564
this.currentEvent = ev;
3568
* @method _handleClick
3569
* @param {Event} ev The event we are working on.
3570
* @description Handles all click events inside the iFrame document.
3572
_handleClick: function(ev) {
3573
var ret = this.fireEvent('beforeEditorClick', { type: 'beforeEditorClick', target: this, ev: ev });
3574
if (ret === false) {
3577
if (this._isNonEditable(ev)) {
3580
this._setCurrentEvent(ev);
3581
if (this.currentWindow) {
3584
if (this.currentWindow) {
3587
if (this.browser.webkit) {
3588
var tar =Event.getTarget(ev);
3589
if (this._isElement(tar, 'a') || this._isElement(tar.parentNode, 'a')) {
3590
Event.stopEvent(ev);
3596
this.fireEvent('editorClick', { type: 'editorClick', target: this, ev: ev });
3600
* @method _handleMouseUp
3601
* @param {Event} ev The event we are working on.
3602
* @description Handles all mouseup events inside the iFrame document.
3604
_handleMouseUp: function(ev) {
3605
var ret = this.fireEvent('beforeEditorMouseUp', { type: 'beforeEditorMouseUp', target: this, ev: ev });
3606
if (ret === false) {
3609
if (this._isNonEditable(ev)) {
3612
//Don't set current event for mouseup.
3613
//It get's fired after a menu is closed and gives up a bogus event to work with
3614
//this._setCurrentEvent(ev);
3616
if (this.browser.opera) {
3618
* @knownissue Opera appears to stop the MouseDown, Click and DoubleClick events on an image inside of a document with designMode on..
3620
* @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.
3622
var sel = Event.getTarget(ev);
3623
if (this._isElement(sel, 'img')) {
3625
if (this.operaEvent) {
3626
clearTimeout(this.operaEvent);
3627
this.operaEvent = null;
3628
this._handleDoubleClick(ev);
3630
this.operaEvent = window.setTimeout(function() {
3631
self.operaEvent = false;
3636
//This will stop Safari from selecting the entire document if you select all the text in the editor
3637
if (this.browser.webkit || this.browser.opera) {
3638
if (this.browser.webkit) {
3639
Event.stopEvent(ev);
3643
this.fireEvent('editorMouseUp', { type: 'editorMouseUp', target: this, ev: ev });
3647
* @method _handleMouseDown
3648
* @param {Event} ev The event we are working on.
3649
* @description Handles all mousedown events inside the iFrame document.
3651
_handleMouseDown: function(ev) {
3652
var ret = this.fireEvent('beforeEditorMouseDown', { type: 'beforeEditorMouseDown', target: this, ev: ev });
3653
if (ret === false) {
3656
if (this._isNonEditable(ev)) {
3659
this._setCurrentEvent(ev);
3660
var sel = Event.getTarget(ev);
3661
if (this.browser.webkit && this._hasSelection()) {
3662
var _sel = this._getSelection();
3663
if (!this.browser.webkit3) {
3664
_sel.collapse(true);
3666
_sel.collapseToStart();
3669
if (this.browser.webkit && this._lastImage) {
3670
Dom.removeClass(this._lastImage, 'selected');
3671
this._lastImage = null;
3673
if (this._isElement(sel, 'img') || this._isElement(sel, 'a')) {
3674
if (this.browser.webkit) {
3675
Event.stopEvent(ev);
3676
if (this._isElement(sel, 'img')) {
3677
Dom.addClass(sel, 'selected');
3678
this._lastImage = sel;
3681
if (this.currentWindow) {
3686
this.fireEvent('editorMouseDown', { type: 'editorMouseDown', target: this, ev: ev });
3690
* @method _handleDoubleClick
3691
* @param {Event} ev The event we are working on.
3692
* @description Handles all doubleclick events inside the iFrame document.
3694
_handleDoubleClick: function(ev) {
3695
var ret = this.fireEvent('beforeEditorDoubleClick', { type: 'beforeEditorDoubleClick', target: this, ev: ev });
3696
if (ret === false) {
3699
if (this._isNonEditable(ev)) {
3702
this._setCurrentEvent(ev);
3703
var sel = Event.getTarget(ev);
3704
if (this._isElement(sel, 'img')) {
3705
this.currentElement[0] = sel;
3706
this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
3707
this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3708
} else if (this._hasParent(sel, 'a')) { //Handle elements inside an a
3709
this.currentElement[0] = this._hasParent(sel, 'a');
3710
this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
3711
this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3714
this.fireEvent('editorDoubleClick', { type: 'editorDoubleClick', target: this, ev: ev });
3718
* @method _handleKeyUp
3719
* @param {Event} ev The event we are working on.
3720
* @description Handles all keyup events inside the iFrame document.
3722
_handleKeyUp: function(ev) {
3723
var ret = this.fireEvent('beforeEditorKeyUp', { type: 'beforeEditorKeyUp', target: this, ev: ev });
3724
if (ret === false) {
3727
if (this._isNonEditable(ev)) {
3730
this._setCurrentEvent(ev);
3731
switch (ev.keyCode) {
3732
case this._keyMap.SELECT_ALL.key:
3733
if (this._checkKey(this._keyMap.SELECT_ALL, ev)) {
3737
case 32: //Space Bar
3740
case 37: //Left Arrow
3742
case 39: //Right Arrow
3743
case 40: //Down Arrow
3744
case 46: //Forward Delete
3746
case this._keyMap.CLOSE_WINDOW.key: //W key if window is open
3747
if ((ev.keyCode == this._keyMap.CLOSE_WINDOW.key) && this.currentWindow) {
3748
if (this._checkKey(this._keyMap.CLOSE_WINDOW, ev)) {
3752
if (!this.browser.ie) {
3753
if (this._nodeChangeTimer) {
3754
clearTimeout(this._nodeChangeTimer);
3757
this._nodeChangeTimer = setTimeout(function() {
3758
self._nodeChangeTimer = null;
3759
self.nodeChange.call(self);
3764
this.editorDirty = true;
3768
this.fireEvent('editorKeyUp', { type: 'editorKeyUp', target: this, ev: ev });
3773
* @method _handleKeyPress
3774
* @param {Event} ev The event we are working on.
3775
* @description Handles all keypress events inside the iFrame document.
3777
_handleKeyPress: function(ev) {
3778
var ret = this.fireEvent('beforeEditorKeyPress', { type: 'beforeEditorKeyPress', target: this, ev: ev });
3779
if (ret === false) {
3783
if (this.get('allowNoEdit')) {
3784
//if (ev && ev.keyCode && ((ev.keyCode == 46) || ev.keyCode == 63272)) {
3785
if (ev && ev.keyCode && (ev.keyCode == 63272)) {
3786
//Forward delete key
3787
YAHOO.log('allowNoEdit is set, forward delete key has been disabled', 'warn', 'SimpleEditor');
3788
Event.stopEvent(ev);
3791
if (this._isNonEditable(ev)) {
3794
this._setCurrentEvent(ev);
3795
if (this.browser.opera) {
3796
if (ev.keyCode === 13) {
3797
var tar = this._getSelectedElement();
3798
if (!this._isElement(tar, 'li')) {
3799
this.execCommand('inserthtml', '<br>');
3800
Event.stopEvent(ev);
3804
if (this.browser.webkit) {
3805
if (!this.browser.webkit3) {
3806
if (ev.keyCode && (ev.keyCode == 122) && (ev.metaKey)) {
3807
//This is CMD + z (for undo)
3808
if (this._hasParent(this._getSelectedElement(), 'li')) {
3809
YAHOO.log('We are in an LI and we found CMD + z, stopping the event', 'warn', 'SimpleEditor');
3810
Event.stopEvent(ev);
3816
this.fireEvent('editorKeyPress', { type: 'editorKeyPress', target: this, ev: ev });
3820
* @method _handleKeyDown
3821
* @param {Event} ev The event we are working on.
3822
* @description Handles all keydown events inside the iFrame document.
3824
_handleKeyDown: function(ev) {
3825
var ret = this.fireEvent('beforeEditorKeyDown', { type: 'beforeEditorKeyDown', target: this, ev: ev });
3826
if (ret === false) {
3829
var tar = null, _range = null;
3830
if (this._isNonEditable(ev)) {
3833
this._setCurrentEvent(ev);
3834
if (this.currentWindow) {
3837
if (this.currentWindow) {
3845
//YAHOO.log('keyCode: ' + ev.keyCode, 'info', 'SimpleEditor');
3847
switch (ev.keyCode) {
3848
case this._keyMap.FOCUS_TOOLBAR.key:
3849
if (this._checkKey(this._keyMap.FOCUS_TOOLBAR, ev)) {
3850
var h = this.toolbar.getElementsByTagName('h2')[0];
3851
if (h && h.firstChild) {
3852
h.firstChild.focus();
3854
} else if (this._checkKey(this._keyMap.FOCUS_AFTER, ev)) {
3855
//Focus After Element - Esc
3856
this.afterElement.focus();
3858
Event.stopEvent(ev);
3862
case this._keyMap.CREATE_LINK.key: //L
3863
if (this._hasSelection()) {
3864
if (this._checkKey(this._keyMap.CREATE_LINK, ev)) {
3865
var makeLink = true;
3866
if (this.get('limitCommands')) {
3867
if (!this.toolbar.getButtonByValue('createlink')) {
3868
YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'SimpleEditor');
3873
this.execCommand('createlink', '');
3874
this.toolbar.fireEvent('createlinkClick', { type: 'createlinkClick', target: this.toolbar });
3875
this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
3882
case this._keyMap.UNDO.key:
3883
case this._keyMap.REDO.key:
3884
if (this._checkKey(this._keyMap.REDO, ev)) {
3887
} else if (this._checkKey(this._keyMap.UNDO, ev)) {
3893
case this._keyMap.BOLD.key:
3894
if (this._checkKey(this._keyMap.BOLD, ev)) {
3899
case this._keyMap.FONT_SIZE_UP.key:
3900
case this._keyMap.FONT_SIZE_DOWN.key:
3901
var uk = false, dk = false;
3902
if (this._checkKey(this._keyMap.FONT_SIZE_UP, ev)) {
3905
if (this._checkKey(this._keyMap.FONT_SIZE_DOWN, ev)) {
3909
var fs_button = this.toolbar.getButtonByValue('fontsize'),
3910
label = parseInt(fs_button.get('label'), 10),
3911
newValue = (label + 1);
3914
newValue = (label - 1);
3917
action = 'fontsize';
3918
value = newValue + 'px';
3923
case this._keyMap.ITALIC.key:
3924
if (this._checkKey(this._keyMap.ITALIC, ev)) {
3930
case this._keyMap.UNDERLINE.key:
3931
if (this._checkKey(this._keyMap.UNDERLINE, ev)) {
3932
action = 'underline';
3937
if (this.browser.ie) {
3938
//Insert a tab in Internet Explorer
3939
_range = this._getRange();
3940
tar = this._getSelectedElement();
3941
if (!this._isElement(tar, 'li')) {
3943
_range.pasteHTML(' ');
3944
_range.collapse(false);
3947
Event.stopEvent(ev);
3951
if (this.browser.gecko > 1.8) {
3952
tar = this._getSelectedElement();
3953
if (this._isElement(tar, 'li')) {
3955
this._getDoc().execCommand('outdent', null, '');
3957
this._getDoc().execCommand('indent', null, '');
3960
} else if (!this._hasSelection()) {
3961
this.execCommand('inserthtml', ' ');
3963
Event.stopEvent(ev);
3967
var p = null, i = 0;
3968
if (this.get('ptags') && !ev.shiftKey) {
3969
if (this.browser.gecko) {
3970
tar = this._getSelectedElement();
3971
if (!this._hasParent(tar, 'li')) {
3972
if (this._hasParent(tar, 'p')) {
3973
p = this._getDoc().createElement('p');
3974
p.innerHTML = ' ';
3975
Dom.insertAfter(p, tar);
3976
this._selectNode(p.firstChild);
3977
} else if (this._isElement(tar, 'body')) {
3978
this.execCommand('insertparagraph', null);
3979
var ps = this._getDoc().body.getElementsByTagName('p');
3980
for (i = 0; i < ps.length; i++) {
3981
if (ps[i].getAttribute('_moz_dirty') !== null) {
3982
p = this._getDoc().createElement('p');
3983
p.innerHTML = ' ';
3984
Dom.insertAfter(p, ps[i]);
3985
this._selectNode(p.firstChild);
3986
ps[i].removeAttribute('_moz_dirty');
3990
YAHOO.log('Something went wrong with paragraphs, please file a bug!!', 'error', 'SimpleEditor');
3992
action = 'insertparagraph';
3994
Event.stopEvent(ev);
3997
if (this.browser.webkit) {
3998
tar = this._getSelectedElement();
3999
if (!this._hasParent(tar, 'li')) {
4000
this.execCommand('insertparagraph', null);
4001
var divs = this._getDoc().body.getElementsByTagName('div');
4002
for (i = 0; i < divs.length; i++) {
4003
if (!Dom.hasClass(divs[i], 'yui-wk-div')) {
4004
Dom.addClass(divs[i], 'yui-wk-p');
4007
Event.stopEvent(ev);
4011
if (this.browser.webkit) {
4012
tar = this._getSelectedElement();
4013
if (!this._hasParent(tar, 'li')) {
4014
this.execCommand('inserthtml', '<var id="yui-br"></var>');
4015
var holder = this._getDoc().getElementById('yui-br'),
4016
br = this._getDoc().createElement('br'),
4017
caret = this._getDoc().createElement('span');
4019
holder.parentNode.replaceChild(br, holder);
4020
caret.className = 'yui-non';
4021
caret.innerHTML = ' ';
4022
Dom.insertAfter(caret, br);
4023
this._selectNode(caret);
4024
Event.stopEvent(ev);
4027
if (this.browser.ie) {
4028
YAHOO.log('Stopping P tags', 'info', 'SimpleEditor');
4029
//Insert a <br> instead of a <p></p> in Internet Explorer
4030
_range = this._getRange();
4031
tar = this._getSelectedElement();
4032
if (!this._isElement(tar, 'li')) {
4034
_range.pasteHTML('<br>');
4035
_range.collapse(false);
4038
Event.stopEvent(ev);
4044
if (this.browser.ie) {
4047
if (doExec && action) {
4048
this.execCommand(action, value);
4049
Event.stopEvent(ev);
4052
this.fireEvent('editorKeyDown', { type: 'editorKeyDown', target: this, ev: ev });
4057
* @param {Event} ev The event we are working on.
4058
* @description Handles the Enter key, Tab Key and Shift + Tab keys for List Items.
4060
_listFix: function(ev) {
4061
//YAHOO.log('Lists Fix (' + ev.keyCode + ')', 'info', 'SimpleEditor');
4062
var testLi = null, par = null, preContent = false, range = null;
4064
if (this.browser.webkit) {
4065
if (ev.keyCode && (ev.keyCode == 13)) {
4066
if (this._hasParent(this._getSelectedElement(), 'li')) {
4067
var tar = this._hasParent(this._getSelectedElement(), 'li');
4068
if (tar.previousSibling) {
4069
if (tar.firstChild && (tar.firstChild.length == 1)) {
4070
this._selectNode(tar);
4077
if (ev.keyCode && ((!this.browser.webkit3 && (ev.keyCode == 25)) || ((this.browser.webkit3 || !this.browser.webkit) && ((ev.keyCode == 9) && ev.shiftKey)))) {
4078
testLi = this._getSelectedElement();
4079
if (this._hasParent(testLi, 'li')) {
4080
testLi = this._hasParent(testLi, 'li');
4081
YAHOO.log('We have a SHIFT tab in an LI, reverse it..', 'info', 'SimpleEditor');
4082
if (this._hasParent(testLi, 'ul') || this._hasParent(testLi, 'ol')) {
4083
YAHOO.log('We have a double parent, move up a level', 'info', 'SimpleEditor');
4084
par = this._hasParent(testLi, 'ul');
4086
par = this._hasParent(testLi, 'ol');
4088
//YAHOO.log(par.previousSibling + ' :: ' + par.previousSibling.innerHTML);
4089
if (this._isElement(par.previousSibling, 'li')) {
4090
par.removeChild(testLi);
4091
par.parentNode.insertBefore(testLi, par.nextSibling);
4092
if (this.browser.ie) {
4093
range = this._getDoc().body.createTextRange();
4094
range.moveToElementText(testLi);
4095
range.collapse(false);
4098
if (this.browser.webkit) {
4099
this._selectNode(testLi.firstChild);
4101
Event.stopEvent(ev);
4107
if (ev.keyCode && ((ev.keyCode == 9) && (!ev.shiftKey))) {
4108
YAHOO.log('List Fix - Tab', 'info', 'SimpleEditor');
4109
var preLi = this._getSelectedElement();
4110
if (this._hasParent(preLi, 'li')) {
4111
preContent = this._hasParent(preLi, 'li').innerHTML;
4113
//YAHOO.log('preLI: ' + preLi.tagName + ' :: ' + preLi.innerHTML);
4114
if (this.browser.webkit) {
4115
this._getDoc().execCommand('inserttext', false, '\t');
4117
testLi = this._getSelectedElement();
4118
if (this._hasParent(testLi, 'li')) {
4119
YAHOO.log('We have a tab in an LI', 'info', 'SimpleEditor');
4120
par = this._hasParent(testLi, 'li');
4121
YAHOO.log('parLI: ' + par.tagName + ' :: ' + par.innerHTML);
4122
var newUl = this._getDoc().createElement(par.parentNode.tagName.toLowerCase());
4123
if (this.browser.webkit) {
4124
var span = Dom.getElementsByClassName('Apple-tab-span', 'span', par);
4125
//Remove the span element that Safari puts in
4127
par.removeChild(span[0]);
4128
par.innerHTML = Lang.trim(par.innerHTML);
4129
//Put the HTML from the LI into this new LI
4131
par.innerHTML = '<span class="yui-non">' + preContent + '</span> ';
4133
par.innerHTML = '<span class="yui-non"> </span> ';
4138
par.innerHTML = preContent + ' ';
4140
par.innerHTML = ' ';
4144
par.parentNode.replaceChild(newUl, par);
4145
newUl.appendChild(par);
4146
if (this.browser.webkit) {
4147
this._getSelection().setBaseAndExtent(par.firstChild, 1, par.firstChild, par.firstChild.innerText.length);
4148
if (!this.browser.webkit3) {
4149
par.parentNode.parentNode.style.display = 'list-item';
4150
setTimeout(function() {
4151
par.parentNode.parentNode.style.display = 'block';
4154
} else if (this.browser.ie) {
4155
range = this._getDoc().body.createTextRange();
4156
range.moveToElementText(par);
4157
range.collapse(false);
4160
this._selectNode(par);
4162
Event.stopEvent(ev);
4164
if (this.browser.webkit) {
4165
Event.stopEvent(ev);
4171
* @method nodeChange
4172
* @param {Boolean} force Optional paramenter to skip the threshold counter
4173
* @description Handles setting up the toolbar buttons, getting the Dom path, fixing nodes.
4175
nodeChange: function(force) {
4178
if (this.get('nodeChangeDelay')) {
4179
window.setTimeout(function() {
4180
NCself._nodeChange.apply(NCself, arguments);
4188
* @method _nodeChange
4189
* @param {Boolean} force Optional paramenter to skip the threshold counter
4190
* @description Fired from nodeChange in a setTimeout.
4192
_nodeChange: function(force) {
4193
var threshold = parseInt(this.get('nodeChangeThreshold'), 10),
4194
thisNodeChange = Math.round(new Date().getTime() / 1000),
4197
if (force === true) {
4198
this._lastNodeChange = 0;
4201
if ((this._lastNodeChange + threshold) < thisNodeChange) {
4202
if (this._fixNodesTimer === null) {
4203
this._fixNodesTimer = window.setTimeout(function() {
4204
self._fixNodes.call(self);
4205
self._fixNodesTimer = null;
4209
this._lastNodeChange = thisNodeChange;
4210
if (this.currentEvent) {
4212
this._lastNodeChangeEvent = this.currentEvent.type;
4216
var beforeNodeChange = this.fireEvent('beforeNodeChange', { type: 'beforeNodeChange', target: this });
4217
if (beforeNodeChange === false) {
4220
if (this.get('dompath')) {
4221
window.setTimeout(function() {
4222
self._writeDomPath.call(self);
4225
//Check to see if we are disabled before continuing
4226
if (!this.get('disabled')) {
4227
if (this.STOP_NODE_CHANGE) {
4228
//Reset this var for next action
4229
this.STOP_NODE_CHANGE = false;
4232
var sel = this._getSelection(),
4233
range = this._getRange(),
4234
el = this._getSelectedElement(),
4235
fn_button = this.toolbar.getButtonByValue('fontname'),
4236
fs_button = this.toolbar.getButtonByValue('fontsize'),
4237
undo_button = this.toolbar.getButtonByValue('undo'),
4238
redo_button = this.toolbar.getButtonByValue('redo');
4240
//Handle updating the toolbar with active buttons
4242
if (this._lastButton) {
4243
_ex[this._lastButton.id] = true;
4244
//this._lastButton = null;
4246
if (!this._isElement(el, 'body')) {
4248
_ex[fn_button.get('id')] = true;
4251
_ex[fs_button.get('id')] = true;
4255
delete _ex[redo_button.get('id')];
4257
this.toolbar.resetAllButtons(_ex);
4259
//Handle disabled buttons
4260
for (var d = 0; d < this._disabled.length; d++) {
4261
var _button = this.toolbar.getButtonByValue(this._disabled[d]);
4262
if (_button && _button.get) {
4263
if (this._lastButton && (_button.get('id') === this._lastButton.id)) {
4266
if (!this._hasSelection() && !this.get('insert')) {
4267
switch (this._disabled[d]) {
4272
//No Selection - disable
4273
this.toolbar.disableButton(_button);
4276
if (!this._alwaysDisabled[this._disabled[d]]) {
4277
this.toolbar.enableButton(_button);
4280
if (!this._alwaysEnabled[this._disabled[d]]) {
4281
this.toolbar.deselectButton(_button);
4286
var path = this._getDomPath();
4287
var tag = null, cmd = null;
4288
for (var i = 0; i < path.length; i++) {
4289
tag = path[i].tagName.toLowerCase();
4290
if (path[i].getAttribute('tag')) {
4291
tag = path[i].getAttribute('tag').toLowerCase();
4293
cmd = this._tag2cmd[tag];
4294
if (cmd === undefined) {
4297
if (!Lang.isArray(cmd)) {
4301
//Bold and Italic styles
4302
if (path[i].style.fontWeight.toLowerCase() == 'bold') {
4303
cmd[cmd.length] = 'bold';
4305
if (path[i].style.fontStyle.toLowerCase() == 'italic') {
4306
cmd[cmd.length] = 'italic';
4308
if (path[i].style.textDecoration.toLowerCase() == 'underline') {
4309
cmd[cmd.length] = 'underline';
4311
if (path[i].style.textDecoration.toLowerCase() == 'line-through') {
4312
cmd[cmd.length] = 'strikethrough';
4314
if (cmd.length > 0) {
4315
for (var j = 0; j < cmd.length; j++) {
4316
this.toolbar.selectButton(cmd[j]);
4317
this.toolbar.enableButton(cmd[j]);
4321
switch (path[i].style.textAlign.toLowerCase()) {
4326
var alignType = path[i].style.textAlign.toLowerCase();
4327
if (path[i].style.textAlign.toLowerCase() == 'justify') {
4330
this.toolbar.selectButton('justify' + alignType);
4331
this.toolbar.enableButton('justify' + alignType);
4337
//Reset Font Family and Size to the inital configs
4339
var family = fn_button._configs.label._initialConfig.value;
4340
fn_button.set('label', '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>');
4341
this._updateMenuChecked('fontname', family);
4345
fs_button.set('label', fs_button._configs.label._initialConfig.value);
4348
var hd_button = this.toolbar.getButtonByValue('heading');
4350
hd_button.set('label', hd_button._configs.label._initialConfig.value);
4351
this._updateMenuChecked('heading', 'none');
4353
var img_button = this.toolbar.getButtonByValue('insertimage');
4354
if (img_button && this.currentWindow && (this.currentWindow.name == 'insertimage')) {
4355
this.toolbar.disableButton(img_button);
4357
if (this._lastButton && this._lastButton.isSelected) {
4358
this.toolbar.deselectButton(this._lastButton.id);
4360
this._undoNodeChange();
4364
this.fireEvent('afterNodeChange', { type: 'afterNodeChange', target: this });
4368
* @method _updateMenuChecked
4369
* @param {Object} button The command identifier of the button you want to check
4370
* @param {String} value The value of the menu item you want to check
4371
* @param {<a href="YAHOO.widget.Toolbar.html">YAHOO.widget.Toolbar</a>} The Toolbar instance the button belongs to (defaults to this.toolbar)
4372
* @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.
4374
_updateMenuChecked: function(button, value, tbar) {
4376
tbar = this.toolbar;
4378
var _button = tbar.getButtonByValue(button);
4379
_button.checkValue(value);
4383
* @method _handleToolbarClick
4384
* @param {Event} ev The event that triggered the button click
4385
* @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.
4387
_handleToolbarClick: function(ev) {
4390
var cmd = ev.button.value;
4391
if (ev.button.menucmd) {
4393
cmd = ev.button.menucmd;
4395
this._lastButton = ev.button;
4396
if (this.STOP_EXEC_COMMAND) {
4397
YAHOO.log('execCommand skipped because we found the STOP_EXEC_COMMAND flag set to true', 'warn', 'SimpleEditor');
4398
YAHOO.log('NOEXEC::execCommand::(' + cmd + '), (' + value + ')', 'warn', 'SimpleEditor');
4399
this.STOP_EXEC_COMMAND = false;
4402
this.execCommand(cmd, value);
4403
if (!this.browser.webkit) {
4405
setTimeout(function() {
4406
Fself.focus.call(Fself);
4410
Event.stopEvent(ev);
4414
* @method _setupAfterElement
4415
* @description Creates the accessibility h2 header and places it after the iframe in the Dom for navigation.
4417
_setupAfterElement: function() {
4418
if (!this.beforeElement) {
4419
this.beforeElement = document.createElement('h2');
4420
this.beforeElement.className = 'yui-editor-skipheader';
4421
this.beforeElement.tabIndex = '-1';
4422
this.beforeElement.innerHTML = this.STR_BEFORE_EDITOR;
4423
this.get('element_cont').get('firstChild').insertBefore(this.beforeElement, this.toolbar.get('nextSibling'));
4425
if (!this.afterElement) {
4426
this.afterElement = document.createElement('h2');
4427
this.afterElement.className = 'yui-editor-skipheader';
4428
this.afterElement.tabIndex = '-1';
4429
this.afterElement.innerHTML = this.STR_LEAVE_EDITOR;
4430
this.get('element_cont').get('firstChild').appendChild(this.afterElement);
4435
* @method _disableEditor
4436
* @param {Boolean} disabled Pass true to disable, false to enable
4437
* @description Creates a mask to place over the Editor.
4439
_disableEditor: function(disabled) {
4441
this._removeEditorEvents();
4443
if (!!this.browser.ie) {
4444
this._setDesignMode('off');
4447
this.toolbar.set('disabled', true);
4449
this._mask = document.createElement('DIV');
4450
Dom.addClass(this._mask, 'yui-editor-masked');
4451
this.get('iframe').get('parentNode').appendChild(this._mask);
4454
this._initEditorEvents();
4456
this._mask.parentNode.removeChild(this._mask);
4459
this.toolbar.set('disabled', false);
4461
this._setDesignMode('on');
4464
window.setTimeout(function() {
4465
self.nodeChange.call(self);
4471
* @property SEP_DOMPATH
4472
* @description The value to place in between the Dom path items
4477
* @property STR_LEAVE_EDITOR
4478
* @description The accessibility string for the element after the iFrame
4481
STR_LEAVE_EDITOR: 'You have left the Rich Text Editor.',
4483
* @property STR_BEFORE_EDITOR
4484
* @description The accessibility string for the element before the iFrame
4487
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>',
4489
* @property STR_TITLE
4490
* @description The Title of the HTML document that is created in the iFrame
4493
STR_TITLE: 'Rich Text Area.',
4495
* @property STR_IMAGE_HERE
4496
* @description The text to place in the URL textbox when using the blankimage.
4499
STR_IMAGE_HERE: 'Image URL Here',
4501
* @property STR_IMAGE_URL
4502
* @description The label string for Image URL
4505
STR_IMAGE_URL: 'Image URL',
4507
* @property STR_LINK_URL
4508
* @description The label string for the Link URL.
4511
STR_LINK_URL: 'Link URL',
4514
* @property STOP_EXEC_COMMAND
4515
* @description Set to true when you want the default execCommand function to not process anything
4518
STOP_EXEC_COMMAND: false,
4521
* @property STOP_NODE_CHANGE
4522
* @description Set to true when you want the default nodeChange function to not process anything
4525
STOP_NODE_CHANGE: false,
4528
* @property CLASS_NOEDIT
4529
* @description CSS class applied to elements that are not editable.
4532
CLASS_NOEDIT: 'yui-noedit',
4535
* @property CLASS_CONTAINER
4536
* @description Default CSS class to apply to the editors container element
4539
CLASS_CONTAINER: 'yui-editor-container',
4542
* @property CLASS_EDITABLE
4543
* @description Default CSS class to apply to the editors iframe element
4546
CLASS_EDITABLE: 'yui-editor-editable',
4549
* @property CLASS_EDITABLE_CONT
4550
* @description Default CSS class to apply to the editors iframe's parent element
4553
CLASS_EDITABLE_CONT: 'yui-editor-editable-container',
4556
* @property CLASS_PREFIX
4557
* @description Default prefix for dynamically created class names
4560
CLASS_PREFIX: 'yui-editor',
4563
* @description Standard browser detection
4566
browser: function() {
4567
var br = YAHOO.env.ua;
4569
if (br.webkit >= 420) {
4570
br.webkit3 = br.webkit;
4576
if (navigator.userAgent.indexOf('Macintosh') !== -1) {
4584
* @description The Editor class' initialization method
4586
init: function(p_oElement, p_oAttributes) {
4587
YAHOO.log('init', 'info', 'SimpleEditor');
4589
if (!this._defaultToolbar) {
4590
this._defaultToolbar = {
4592
titlebar: 'Text Editing Tools',
4595
{ group: 'fontstyle', label: 'Font Name and Size',
4597
{ type: 'select', label: 'Arial', value: 'fontname', disabled: true,
4599
{ text: 'Arial', checked: true },
4600
{ text: 'Arial Black' },
4601
{ text: 'Comic Sans MS' },
4602
{ text: 'Courier New' },
4603
{ text: 'Lucida Console' },
4605
{ text: 'Times New Roman' },
4606
{ text: 'Trebuchet MS' },
4610
{ type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
4613
{ type: 'separator' },
4614
{ group: 'textstyle', label: 'Font Style',
4616
{ type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
4617
{ type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
4618
{ type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
4619
{ type: 'push', label: 'Strike Through', value: 'strikethrough' },
4620
{ type: 'separator' },
4621
{ type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
4622
{ type: 'color', label: 'Background Color', value: 'backcolor', disabled: true }
4626
{ type: 'separator' },
4627
{ group: 'indentlist', label: 'Lists',
4629
{ type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
4630
{ type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
4633
{ type: 'separator' },
4634
{ group: 'insertitem', label: 'Insert Item',
4636
{ type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
4637
{ type: 'push', label: 'Insert Image', value: 'insertimage' }
4644
YAHOO.widget.SimpleEditor.superclass.init.call(this, p_oElement, p_oAttributes);
4645
YAHOO.widget.EditorInfo._instances[this.get('id')] = this;
4648
this.currentElement = [];
4649
this.on('contentReady', function() {
4650
this.DOMReady = true;
4656
* @method initAttributes
4657
* @description Initializes all of the configuration attributes used to create
4659
* @param {Object} attr Object literal specifying a set of
4660
* configuration attributes used to create the editor.
4662
initAttributes: function(attr) {
4663
YAHOO.widget.SimpleEditor.superclass.initAttributes.call(this, attr);
4667
* @config nodeChangeDelay
4668
* @description Do we wrap the nodeChange method in a timeout for performance, default: true.
4672
this.setAttributeConfig('nodeChangeDelay', {
4673
value: ((attr.nodeChangeDelay === false) ? false : true)
4677
* @description The max number of undo levels to store.
4681
this.setAttributeConfig('maxUndo', {
4683
value: attr.maxUndo || 30
4688
* @description If true, the editor uses <P> tags instead of <br> tags. (Use Shift + Enter to get a <br>)
4692
this.setAttributeConfig('ptags', {
4694
value: attr.ptags || false
4698
* @description If true, selection is not required for: fontname, fontsize, forecolor, backcolor.
4702
this.setAttributeConfig('insert', {
4704
value: attr.insert || false,
4705
method: function(insert) {
4713
var tmp = this._defaultToolbar.buttons;
4714
for (var i = 0; i < tmp.length; i++) {
4715
if (tmp[i].buttons) {
4716
for (var a = 0; a < tmp[i].buttons.length; a++) {
4717
if (tmp[i].buttons[a].value) {
4718
if (buttons[tmp[i].buttons[a].value]) {
4719
delete tmp[i].buttons[a].disabled;
4730
* @description Used when dynamically creating the Editor from Javascript with no default textarea.
4731
* We will create one and place it in this container. If no container is passed we will append to document.body.
4735
this.setAttributeConfig('container', {
4737
value: attr.container || false
4741
* @description Process the inital textarea data as if it was plain text. Accounting for spaces, tabs and line feeds.
4745
this.setAttributeConfig('plainText', {
4747
value: attr.plainText || false
4752
* @description Internal config for holding the iframe element.
4756
this.setAttributeConfig('iframe', {
4761
* @depreciated - No longer used, should use this.get('element')
4763
* @description Internal config for holding the textarea element (replaced with element).
4767
this.setAttributeConfig('textarea', {
4772
* @config nodeChangeThreshold
4773
* @description The number of seconds that need to be in between nodeChange processing
4777
this.setAttributeConfig('nodeChangeThreshold', {
4778
value: attr.nodeChangeThreshold || 3,
4779
validator: YAHOO.lang.isNumber
4782
* @config allowNoEdit
4783
* @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.
4784
* Such as highlighting an element below and above the content and hitting a toolbar button or a shortcut key.
4788
this.setAttributeConfig('allowNoEdit', {
4789
value: attr.allowNoEdit || false,
4790
validator: YAHOO.lang.isBoolean
4793
* @config limitCommands
4794
* @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.
4798
this.setAttributeConfig('limitCommands', {
4799
value: attr.limitCommands || false,
4800
validator: YAHOO.lang.isBoolean
4803
* @config element_cont
4804
* @description Internal config for the editors container
4808
this.setAttributeConfig('element_cont', {
4809
value: attr.element_cont
4813
* @config editor_wrapper
4814
* @description The outter wrapper for the entire editor.
4818
this.setAttributeConfig('editor_wrapper', {
4819
value: attr.editor_wrapper || null,
4824
* @description The height of the editor iframe container, not including the toolbar..
4825
* @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
4828
this.setAttributeConfig('height', {
4829
value: attr.height || Dom.getStyle(self.get('element'), 'height'),
4830
method: function(height) {
4831
if (this._rendered) {
4832
//We have been rendered, change the height
4833
if (this.get('animate')) {
4834
var anim = new YAHOO.util.Anim(this.get('iframe').get('parentNode'), {
4836
to: parseInt(height, 10)
4841
Dom.setStyle(this.get('iframe').get('parentNode'), 'height', height);
4847
* @config autoHeight
4848
* @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.
4850
* @type Boolean || Number
4852
this.setAttributeConfig('autoHeight', {
4853
value: attr.autoHeight || false,
4854
method: function(a) {
4856
if (this.get('iframe')) {
4857
this.get('iframe').get('element').setAttribute('scrolling', 'no');
4859
this.on('afterNodeChange', this._handleAutoHeight, this, true);
4860
this.on('editorKeyDown', this._handleAutoHeight, this, true);
4861
this.on('editorKeyPress', this._handleAutoHeight, this, true);
4863
if (this.get('iframe')) {
4864
this.get('iframe').get('element').setAttribute('scrolling', 'auto');
4866
this.unsubscribe('afterNodeChange', this._handleAutoHeight);
4867
this.unsubscribe('editorKeyDown', this._handleAutoHeight);
4868
this.unsubscribe('editorKeyPress', this._handleAutoHeight);
4874
* @description The width of the editor container.
4875
* @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
4878
this.setAttributeConfig('width', {
4879
value: attr.width || Dom.getStyle(this.get('element'), 'width'),
4880
method: function(width) {
4881
if (this._rendered) {
4882
//We have been rendered, change the width
4883
if (this.get('animate')) {
4884
var anim = new YAHOO.util.Anim(this.get('element_cont').get('element'), {
4886
to: parseInt(width, 10)
4891
this.get('element_cont').setStyle('width', width);
4898
* @attribute blankimage
4899
* @description The URL for the image placeholder to put in when inserting an image.
4900
* @default The yahooapis.com address for the current release + 'assets/blankimage.png'
4903
this.setAttributeConfig('blankimage', {
4904
value: attr.blankimage || this._getBlankImage()
4908
* @description The Base CSS used to format the content of the editor
4909
* @default <code><pre>html {
4914
padding: 7px; background-color: #fff; font:13px/1.22 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;
4918
text-decoration: underline;
4921
.warning-localfile {
4922
border-bottom: 1px dashed red !important;
4925
cursor: wait !important;
4927
img.selected { //Safari image selection
4928
border: 2px dotted #808080;
4931
cursor: pointer !important;
4937
this.setAttributeConfig('css', {
4938
value: attr.css || this._defaultCSS,
4943
* @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)
4944
* @default This HTML requires a few things if you are to override:
4945
<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>
4946
<p><code>onload="document.body._rteLoaded = true;"</code> : the onload statement must be there or the editor will not finish loading.</p>
4951
<title>{TITLE}</title>
4952
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
4963
<body onload="document.body._rteLoaded = true;">
4971
this.setAttributeConfig('html', {
4972
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>',
4977
* @attribute extracss
4978
* @description Extra user defined css to load after the default SimpleEditor CSS
4982
this.setAttributeConfig('extracss', {
4983
value: attr.extracss || '',
4988
* @attribute handleSubmit
4989
* @description Config handles if the editor will attach itself to the textareas parent form's submit handler.
4990
If it is set to true, the editor will attempt to attach a submit listener to the textareas parent form.
4991
Then it will trigger the editors save handler and place the new content back into the text area before the form is submitted.
4995
this.setAttributeConfig('handleSubmit', {
4996
value: attr.handleSubmit || false,
4997
method: function(exec) {
4998
if (this.get('element').form) {
4999
if (!this._formButtons) {
5000
this._formButtons = [];
5003
Event.on(this.get('element').form, 'submit', this._handleFormSubmit, this, true);
5004
var i = this.get('element').form.getElementsByTagName('input');
5005
for (var s = 0; s < i.length; s++) {
5006
var type = i[s].getAttribute('type');
5007
if (type && (type.toLowerCase() == 'submit')) {
5008
Event.on(i[s], 'click', this._handleFormButtonClick, this, true);
5009
this._formButtons[this._formButtons.length] = i[s];
5013
Event.removeListener(this.get('element').form, 'submit', this._handleFormSubmit);
5014
if (this._formButtons) {
5015
Event.removeListener(this._formButtons, 'click', this._handleFormButtonClick);
5022
* @attribute disabled
5023
* @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.
5024
All Toolbar buttons are also disabled so they cannot be used.
5029
this.setAttributeConfig('disabled', {
5031
method: function(disabled) {
5032
if (this._rendered) {
5033
this._disableEditor(disabled);
5039
* @description When save HTML is called, this element will be updated as well as the source of data.
5043
this.setAttributeConfig('saveEl', {
5044
value: this.get('element')
5047
* @config toolbar_cont
5048
* @description Internal config for the toolbars container
5052
this.setAttributeConfig('toolbar_cont', {
5057
* @attribute toolbar
5058
* @description The default toolbar config.
5061
this.setAttributeConfig('toolbar', {
5062
value: attr.toolbar || this._defaultToolbar,
5064
method: function(toolbar) {
5065
if (!toolbar.buttonType) {
5066
toolbar.buttonType = this._defaultToolbar.buttonType;
5068
this._defaultToolbar = toolbar;
5072
* @attribute animate
5073
* @description Should the editor animate window movements
5074
* @default false unless Animation is found, then true
5077
this.setAttributeConfig('animate', {
5078
value: ((attr.animate) ? ((YAHOO.util.Anim) ? true : false) : false),
5079
validator: function(value) {
5081
if (!YAHOO.util.Anim) {
5089
* @description A reference to the panel we are using for windows.
5093
this.setAttributeConfig('panel', {
5096
validator: function(value) {
5098
if (!YAHOO.widget.Overlay) {
5105
* @attribute focusAtStart
5106
* @description Should we focus the window when the content is ready?
5110
this.setAttributeConfig('focusAtStart', {
5111
value: attr.focusAtStart || false,
5113
method: function(fs) {
5115
this.on('editorContentLoaded', function() {
5117
setTimeout(function() {
5118
self.focus.call(self);
5119
self.editorDirty = false;
5126
* @attribute dompath
5127
* @description Toggle the display of the current Dom path below the editor
5131
this.setAttributeConfig('dompath', {
5132
value: attr.dompath || false,
5133
method: function(dompath) {
5134
if (dompath && !this.dompath) {
5135
this.dompath = document.createElement('DIV');
5136
this.dompath.id = this.get('id') + '_dompath';
5137
Dom.addClass(this.dompath, 'dompath');
5138
this.get('element_cont').get('firstChild').appendChild(this.dompath);
5139
if (this.get('iframe')) {
5140
this._writeDomPath();
5142
} else if (!dompath && this.dompath) {
5143
this.dompath.parentNode.removeChild(this.dompath);
5144
this.dompath = null;
5150
* @description Should we try to adjust the markup for the following types: semantic, css, default or xhtml
5151
* @default "semantic"
5154
this.setAttributeConfig('markup', {
5155
value: attr.markup || 'semantic',
5156
validator: function(markup) {
5157
switch (markup.toLowerCase()) {
5168
* @attribute removeLineBreaks
5169
* @description Should we remove linebreaks and extra spaces on cleanup
5173
this.setAttributeConfig('removeLineBreaks', {
5174
value: attr.removeLineBreaks || false,
5175
validator: YAHOO.lang.isBoolean
5180
* @description Set this config to make the Editor draggable, pass 'proxy' to make use YAHOO.util.DDProxy.
5181
* @type {Boolean/String}
5183
this.setAttributeConfig('drag', {
5185
value: attr.drag || false
5190
* @description Set this to true to make the Editor Resizable with YAHOO.util.Resize. The default config is available: myEditor._resizeConfig
5191
* Animation will be ignored while performing this resize to allow for the dynamic change in size of the toolbar.
5194
this.setAttributeConfig('resize', {
5196
value: attr.resize || false
5200
* @config filterWord
5201
* @description Attempt to filter out MS Word HTML from the Editor's output.
5204
this.setAttributeConfig('filterWord', {
5205
value: attr.filterWord || false,
5206
validator: YAHOO.lang.isBoolean
5212
* @method _getBlankImage
5213
* @description Retrieves the full url of the image to use as the blank image.
5214
* @return {String} The URL to the blank image
5216
_getBlankImage: function() {
5217
if (!this.DOMReady) {
5218
this._queue[this._queue.length] = ['_getBlankImage', arguments];
5222
if (!this._blankImageLoaded) {
5223
if (YAHOO.widget.EditorInfo.blankImage) {
5224
this.set('blankimage', YAHOO.widget.EditorInfo.blankImage);
5225
this._blankImageLoaded = true;
5227
var div = document.createElement('div');
5228
div.style.position = 'absolute';
5229
div.style.top = '-9999px';
5230
div.style.left = '-9999px';
5231
div.className = this.CLASS_PREFIX + '-blankimage';
5232
document.body.appendChild(div);
5233
img = YAHOO.util.Dom.getStyle(div, 'background-image');
5234
img = img.replace('url(', '').replace(')', '').replace(/"/g, '');
5236
img = img.replace('app:/', '');
5237
this.set('blankimage', img);
5238
this._blankImageLoaded = true;
5239
div.parentNode.removeChild(div);
5240
YAHOO.widget.EditorInfo.blankImage = img;
5243
img = this.get('blankimage');
5249
* @method _handleAutoHeight
5250
* @description Handles resizing the editor's height based on the content
5252
_handleAutoHeight: function() {
5253
var doc = this._getDoc(),
5255
docEl = doc.documentElement;
5257
var height = parseInt(Dom.getStyle(this.get('editor_wrapper'), 'height'), 10);
5258
var newHeight = body.scrollHeight;
5259
if (this.browser.webkit) {
5260
newHeight = docEl.scrollHeight;
5262
if (newHeight < parseInt(this.get('height'), 10)) {
5263
newHeight = parseInt(this.get('height'), 10);
5265
if ((height != newHeight) && (newHeight >= parseInt(this.get('height'), 10))) {
5266
var anim = this.get('animate');
5267
this.set('animate', false);
5268
this.set('height', newHeight + 'px');
5269
this.set('animate', anim);
5270
if (this.browser.ie) {
5271
//Internet Explorer needs this
5272
this.get('iframe').setStyle('height', '99%');
5273
this.get('iframe').setStyle('zoom', '1');
5275
window.setTimeout(function() {
5276
self.get('iframe').setStyle('height', '100%');
5283
* @property _formButtons
5284
* @description Array of buttons that are in the Editor's parent form (for handleSubmit)
5290
* @property _formButtonClicked
5291
* @description The form button that was clicked to submit the form.
5294
_formButtonClicked: null,
5297
* @method _handleFormButtonClick
5298
* @description The click listener assigned to each submit button in the Editor's parent form.
5299
* @param {Event} ev The click event
5301
_handleFormButtonClick: function(ev) {
5302
var tar = Event.getTarget(ev);
5303
this._formButtonClicked = tar;
5307
* @method _handleFormSubmit
5308
* @description Handles the form submission.
5309
* @param {Object} ev The Form Submit Event
5311
_handleFormSubmit: function(ev) {
5314
var form = this.get('element').form,
5315
tar = this._formButtonClicked || false;
5317
Event.removeListener(form, 'submit', this._handleFormSubmit);
5318
if (YAHOO.env.ua.ie) {
5319
//form.fireEvent("onsubmit");
5320
if (tar && !tar.disabled) {
5323
} else { // Gecko, Opera, and Safari
5324
if (tar && !tar.disabled) {
5327
var oEvent = document.createEvent("HTMLEvents");
5328
oEvent.initEvent("submit", true, true);
5329
form.dispatchEvent(oEvent);
5330
if (YAHOO.env.ua.webkit) {
5331
if (YAHOO.lang.isFunction(form.submit)) {
5337
//Removed this, not need since removing Safari 2.x
5338
//Event.stopEvent(ev);
5342
* @method _handleFontSize
5343
* @description Handles the font size button in the toolbar.
5344
* @param {Object} o Object returned from Toolbar's buttonClick Event
5346
_handleFontSize: function(o) {
5347
var button = this.toolbar.getButtonById(o.button.id);
5348
var value = button.get('label') + 'px';
5349
this.execCommand('fontsize', value);
5354
* @description Handles the colorpicker buttons in the toolbar.
5355
* @param {Object} o Object returned from Toolbar's buttonClick Event
5357
_handleColorPicker: function(o) {
5359
var value = '#' + o.color;
5360
if ((cmd == 'forecolor') || (cmd == 'backcolor')) {
5361
this.execCommand(cmd, value);
5366
* @method _handleAlign
5367
* @description Handles the alignment buttons in the toolbar.
5368
* @param {Object} o Object returned from Toolbar's buttonClick Event
5370
_handleAlign: function(o) {
5372
for (var i = 0; i < o.button.menu.length; i++) {
5373
if (o.button.menu[i].value == o.button.value) {
5374
cmd = o.button.menu[i].value;
5377
var value = this._getSelection();
5379
this.execCommand(cmd, value);
5384
* @method _handleAfterNodeChange
5385
* @description Fires after a nodeChange happens to setup the things that where reset on the node change (button state).
5387
_handleAfterNodeChange: function() {
5388
var path = this._getDomPath(),
5393
fn_button = this.toolbar.getButtonByValue('fontname'),
5394
fs_button = this.toolbar.getButtonByValue('fontsize'),
5395
hd_button = this.toolbar.getButtonByValue('heading');
5397
for (var i = 0; i < path.length; i++) {
5400
var tag = elm.tagName.toLowerCase();
5403
if (elm.getAttribute('tag')) {
5404
tag = elm.getAttribute('tag');
5407
family = elm.getAttribute('face');
5408
if (Dom.getStyle(elm, 'font-family')) {
5409
family = Dom.getStyle(elm, 'font-family');
5411
family = family.replace(/'/g, '');
5414
if (tag.substring(0, 1) == 'h') {
5416
for (var h = 0; h < hd_button._configs.menu.value.length; h++) {
5417
if (hd_button._configs.menu.value[h].value.toLowerCase() == tag) {
5418
hd_button.set('label', hd_button._configs.menu.value[h].text);
5421
this._updateMenuChecked('heading', tag);
5427
for (var b = 0; b < fn_button._configs.menu.value.length; b++) {
5428
if (family && fn_button._configs.menu.value[b].text.toLowerCase() == family.toLowerCase()) {
5430
family = fn_button._configs.menu.value[b].text; //Put the proper menu name in the button
5434
family = fn_button._configs.label._initialConfig.value;
5436
var familyLabel = '<span class="yui-toolbar-fontname-' + this._cleanClassName(family) + '">' + family + '</span>';
5437
if (fn_button.get('label') != familyLabel) {
5438
fn_button.set('label', familyLabel);
5439
this._updateMenuChecked('fontname', family);
5444
fontsize = parseInt(Dom.getStyle(elm, 'fontSize'), 10);
5445
if ((fontsize === null) || isNaN(fontsize)) {
5446
fontsize = fs_button._configs.label._initialConfig.value;
5448
fs_button.set('label', ''+fontsize);
5451
if (!this._isElement(elm, 'body') && !this._isElement(elm, 'img')) {
5452
this.toolbar.enableButton(fn_button);
5453
this.toolbar.enableButton(fs_button);
5454
this.toolbar.enableButton('forecolor');
5455
this.toolbar.enableButton('backcolor');
5457
if (this._isElement(elm, 'img')) {
5458
if (YAHOO.widget.Overlay) {
5459
this.toolbar.enableButton('createlink');
5462
if (this._hasParent(elm, 'blockquote')) {
5463
this.toolbar.selectButton('indent');
5464
this.toolbar.disableButton('indent');
5465
this.toolbar.enableButton('outdent');
5467
if (this._hasParent(elm, 'ol') || this._hasParent(elm, 'ul')) {
5468
this.toolbar.disableButton('indent');
5470
this._lastButton = null;
5475
* @method _handleInsertImageClick
5476
* @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
5478
_handleInsertImageClick: function() {
5479
if (this.get('limitCommands')) {
5480
if (!this.toolbar.getButtonByValue('insertimage')) {
5481
YAHOO.log('Toolbar Button for (insertimage) was not found, skipping exec.', 'info', 'SimpleEditor');
5486
this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
5487
var _handleAEC = function() {
5488
var el = this.currentElement[0],
5491
el = this._getSelectedElement();
5494
if (el.getAttribute('src')) {
5495
src = el.getAttribute('src', 2);
5496
if (src.indexOf(this.get('blankimage')) != -1) {
5497
src = this.STR_IMAGE_HERE;
5501
var str = prompt(this.STR_IMAGE_URL + ': ', src);
5502
if ((str !== '') && (str !== null)) {
5503
el.setAttribute('src', str);
5504
} else if (str === '') {
5505
el.parentNode.removeChild(el);
5506
this.currentElement = [];
5508
} else if ((str === null)) {
5509
src = el.getAttribute('src', 2);
5510
if (src.indexOf(this.get('blankimage')) != -1) {
5511
el.parentNode.removeChild(el);
5512
this.currentElement = [];
5517
this.toolbar.set('disabled', false);
5518
this.unsubscribe('afterExecCommand', _handleAEC, this, true);
5520
this.on('afterExecCommand', _handleAEC, this, true);
5524
* @method _handleInsertImageWindowClose
5525
* @description Handles the closing of the Image Properties Window.
5527
_handleInsertImageWindowClose: function() {
5532
* @method _isLocalFile
5533
* @param {String} url THe url/string to check
5534
* @description Checks to see if a string (href or img src) is possibly a local file reference..
5536
_isLocalFile: function(url) {
5537
if ((url) && (url !== '') && ((url.indexOf('file:/') != -1) || (url.indexOf(':\\') != -1))) {
5544
* @method _handleCreateLinkClick
5545
* @description Handles the opening of the Link Properties Window when the Create Link button is clicked or an href is doubleclicked.
5547
_handleCreateLinkClick: function() {
5548
if (this.get('limitCommands')) {
5549
if (!this.toolbar.getButtonByValue('createlink')) {
5550
YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'SimpleEditor');
5555
this.toolbar.set('disabled', true); //Disable the toolbar when the prompt is showing
5557
var _handleAEC = function() {
5558
var el = this.currentElement[0],
5562
if (el.getAttribute('href', 2) !== null) {
5563
url = el.getAttribute('href', 2);
5566
var str = prompt(this.STR_LINK_URL + ': ', url);
5567
if ((str !== '') && (str !== null)) {
5569
if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
5570
if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
5571
//Found an @ sign, prefix with mailto:
5572
urlValue = 'mailto:' + urlValue;
5574
/* :// not found adding */
5575
if (urlValue.substring(0, 1) != '#') {
5576
//urlValue = 'http:/'+'/' + urlValue;
5580
el.setAttribute('href', urlValue);
5581
} else if (str !== null) {
5582
var _span = this._getDoc().createElement('span');
5583
_span.innerHTML = el.innerHTML;
5584
Dom.addClass(_span, 'yui-non');
5585
el.parentNode.replaceChild(_span, el);
5588
this.toolbar.set('disabled', false);
5589
this.unsubscribe('afterExecCommand', _handleAEC, this, true);
5591
this.on('afterExecCommand', _handleAEC, this);
5596
* @method _handleCreateLinkWindowClose
5597
* @description Handles the closing of the Link Properties Window.
5599
_handleCreateLinkWindowClose: function() {
5601
this.currentElement = [];
5605
* @description Calls the private method _render in a setTimeout to allow for other things on the page to continue to load.
5607
render: function() {
5608
if (this._rendered) {
5611
YAHOO.log('Render', 'info', 'SimpleEditor');
5612
if (!this.DOMReady) {
5613
YAHOO.log('!DOMReady', 'info', 'SimpleEditor');
5614
this._queue[this._queue.length] = ['render', arguments];
5617
if (this.get('element')) {
5618
if (this.get('element').tagName) {
5619
this._textarea = true;
5620
if (this.get('element').tagName.toLowerCase() !== 'textarea') {
5621
this._textarea = false;
5624
YAHOO.log('No Valid Element', 'error', 'SimpleEditor');
5628
YAHOO.log('No Element', 'error', 'SimpleEditor');
5631
this._rendered = true;
5633
window.setTimeout(function() {
5634
self._render.call(self);
5640
* @description Causes the toolbar and the editor to render and replace the textarea.
5642
_render: function() {
5644
this.set('textarea', this.get('element'));
5646
this.get('element_cont').setStyle('display', 'none');
5647
this.get('element_cont').addClass(this.CLASS_CONTAINER);
5649
this.set('iframe', this._createIframe());
5651
window.setTimeout(function() {
5652
self._setInitialContent.call(self);
5655
this.get('editor_wrapper').appendChild(this.get('iframe').get('element'));
5657
if (this.get('disabled')) {
5658
this._disableEditor(true);
5661
var tbarConf = this.get('toolbar');
5662
//Create Toolbar instance
5663
if (tbarConf instanceof Toolbar) {
5664
this.toolbar = tbarConf;
5665
//Set the toolbar to disabled until content is loaded
5666
this.toolbar.set('disabled', true);
5668
//Set the toolbar to disabled until content is loaded
5669
tbarConf.disabled = true;
5670
this.toolbar = new Toolbar(this.get('toolbar_cont'), tbarConf);
5673
YAHOO.log('fireEvent::toolbarLoaded', 'info', 'SimpleEditor');
5674
this.fireEvent('toolbarLoaded', { type: 'toolbarLoaded', target: this.toolbar });
5677
this.toolbar.on('toolbarCollapsed', function() {
5678
if (this.currentWindow) {
5682
this.toolbar.on('toolbarExpanded', function() {
5683
if (this.currentWindow) {
5687
this.toolbar.on('fontsizeClick', this._handleFontSize, this, true);
5689
this.toolbar.on('colorPickerClicked', function(o) {
5690
this._handleColorPicker(o);
5691
return false; //Stop the buttonClick event
5694
this.toolbar.on('alignClick', this._handleAlign, this, true);
5695
this.on('afterNodeChange', this._handleAfterNodeChange, this, true);
5696
this.toolbar.on('insertimageClick', this._handleInsertImageClick, this, true);
5697
this.on('windowinsertimageClose', this._handleInsertImageWindowClose, this, true);
5698
this.toolbar.on('createlinkClick', this._handleCreateLinkClick, this, true);
5699
this.on('windowcreatelinkClose', this._handleCreateLinkWindowClose, this, true);
5702
//Replace Textarea with editable area
5703
this.get('parentNode').replaceChild(this.get('element_cont').get('element'), this.get('element'));
5706
this.setStyle('visibility', 'hidden');
5707
this.setStyle('position', 'absolute');
5708
this.setStyle('top', '-9999px');
5709
this.setStyle('left', '-9999px');
5710
this.get('element_cont').appendChild(this.get('element'));
5711
this.get('element_cont').setStyle('display', 'block');
5714
Dom.addClass(this.get('iframe').get('parentNode'), this.CLASS_EDITABLE_CONT);
5715
this.get('iframe').addClass(this.CLASS_EDITABLE);
5717
//Set height and width of editor container
5718
this.get('element_cont').setStyle('width', this.get('width'));
5719
Dom.setStyle(this.get('iframe').get('parentNode'), 'height', this.get('height'));
5721
this.get('iframe').setStyle('width', '100%'); //WIDTH
5722
this.get('iframe').setStyle('height', '100%');
5726
window.setTimeout(function() {
5727
self._setupAfterElement.call(self);
5729
this.fireEvent('afterRender', { type: 'afterRender', target: this });
5732
* @method execCommand
5733
* @param {String} action The "execCommand" action to try to execute (Example: bold, insertimage, inserthtml)
5734
* @param {String} value (optional) The value for a given action such as action: fontname value: 'Verdana'
5735
* @description This method attempts to try and level the differences in the various browsers and their support for execCommand actions
5737
execCommand: function(action, value) {
5738
var beforeExec = this.fireEvent('beforeExecCommand', { type: 'beforeExecCommand', target: this, args: arguments });
5739
if ((beforeExec === false) || (this.STOP_EXEC_COMMAND)) {
5740
this.STOP_EXEC_COMMAND = false;
5743
this._lastCommand = action;
5744
this._setMarkupType(action);
5745
if (this.browser.ie) {
5746
this._getWindow().focus();
5750
if (this.get('limitCommands')) {
5751
if (!this.toolbar.getButtonByValue(action)) {
5752
YAHOO.log('Toolbar Button for (' + action + ') was not found, skipping exec.', 'info', 'SimpleEditor');
5757
this.editorDirty = true;
5759
if ((typeof this['cmd_' + action.toLowerCase()] == 'function') && exec) {
5760
YAHOO.log('Found execCommand override method: (cmd_' + action.toLowerCase() + ')', 'info', 'SimpleEditor');
5761
var retValue = this['cmd_' + action.toLowerCase()](value);
5764
action = retValue[1];
5767
value = retValue[2];
5771
YAHOO.log('execCommand::(' + action + '), (' + value + ')', 'info', 'SimpleEditor');
5773
this._getDoc().execCommand(action, false, value);
5775
YAHOO.log('execCommand Failed', 'error', 'SimpleEditor');
5778
YAHOO.log('OVERRIDE::execCommand::(' + action + '),(' + value + ') skipped', 'warn', 'SimpleEditor');
5780
this.on('afterExecCommand', function() {
5781
this.unsubscribeAll('afterExecCommand');
5784
this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
5787
/* {{{ Command Overrides */
5791
* @param value Value passed from the execCommand method
5792
* @description This is an execCommand override method. It is called from execCommand when the execCommand('bold') is used.
5794
cmd_bold: function(value) {
5795
if (!this.browser.webkit) {
5796
var el = this._getSelectedElement();
5797
if (el && this._isElement(el, 'span') && this._hasSelection()) {
5798
if (el.style.fontWeight == 'bold') {
5799
el.style.fontWeight = '';
5800
var b = this._getDoc().createElement('b'),
5801
par = el.parentNode;
5802
par.replaceChild(b, el);
5810
* @method cmd_italic
5811
* @param value Value passed from the execCommand method
5812
* @description This is an execCommand override method. It is called from execCommand when the execCommand('italic') is used.
5815
cmd_italic: function(value) {
5816
if (!this.browser.webkit) {
5817
var el = this._getSelectedElement();
5818
if (el && this._isElement(el, 'span') && this._hasSelection()) {
5819
if (el.style.fontStyle == 'italic') {
5820
el.style.fontStyle = '';
5821
var i = this._getDoc().createElement('i'),
5822
par = el.parentNode;
5823
par.replaceChild(i, el);
5833
* @method cmd_underline
5834
* @param value Value passed from the execCommand method
5835
* @description This is an execCommand override method. It is called from execCommand when the execCommand('underline') is used.
5837
cmd_underline: function(value) {
5838
if (!this.browser.webkit) {
5839
var el = this._getSelectedElement();
5840
if (el && this._isElement(el, 'span')) {
5841
if (el.style.textDecoration == 'underline') {
5842
el.style.textDecoration = 'none';
5844
el.style.textDecoration = 'underline';
5852
* @method cmd_backcolor
5853
* @param value Value passed from the execCommand method
5854
* @description This is an execCommand override method. It is called from execCommand when the execCommand('backcolor') is used.
5856
cmd_backcolor: function(value) {
5858
el = this._getSelectedElement(),
5859
action = 'backcolor';
5861
if (this.browser.gecko || this.browser.opera) {
5862
this._setEditorStyle(true);
5863
action = 'hilitecolor';
5866
if (!this._isElement(el, 'body') && !this._hasSelection()) {
5867
el.style.backgroundColor = value;
5868
this._selectNode(el);
5871
if (this.get('insert')) {
5872
el = this._createInsertElement({ backgroundColor: value });
5874
this._createCurrentElement('span', { backgroundColor: value, color: el.style.color, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily });
5875
this._selectNode(this.currentElement[0]);
5880
return [exec, action];
5883
* @method cmd_forecolor
5884
* @param value Value passed from the execCommand method
5885
* @description This is an execCommand override method. It is called from execCommand when the execCommand('forecolor') is used.
5887
cmd_forecolor: function(value) {
5889
el = this._getSelectedElement();
5891
if (!this._isElement(el, 'body') && !this._hasSelection()) {
5892
Dom.setStyle(el, 'color', value);
5893
this._selectNode(el);
5896
if (this.get('insert')) {
5897
el = this._createInsertElement({ color: value });
5899
this._createCurrentElement('span', { color: value, fontSize: el.style.fontSize, fontFamily: el.style.fontFamily, backgroundColor: el.style.backgroundColor });
5900
this._selectNode(this.currentElement[0]);
5907
* @method cmd_unlink
5908
* @param value Value passed from the execCommand method
5909
* @description This is an execCommand override method. It is called from execCommand when the execCommand('unlink') is used.
5911
cmd_unlink: function(value) {
5912
this._swapEl(this.currentElement[0], 'span', function(el) {
5913
el.className = 'yui-non';
5918
* @method cmd_createlink
5919
* @param value Value passed from the execCommand method
5920
* @description This is an execCommand override method. It is called from execCommand when the execCommand('createlink') is used.
5922
cmd_createlink: function(value) {
5923
var el = this._getSelectedElement(), _a = null;
5924
if (this._hasParent(el, 'a')) {
5925
this.currentElement[0] = this._hasParent(el, 'a');
5926
} else if (this._isElement(el, 'li')) {
5927
_a = this._getDoc().createElement('a');
5928
_a.innerHTML = el.innerHTML;
5931
this.currentElement[0] = _a;
5932
} else if (!this._isElement(el, 'a')) {
5933
this._createCurrentElement('a');
5934
_a = this._swapEl(this.currentElement[0], 'a');
5935
this.currentElement[0] = _a;
5937
this.currentElement[0] = el;
5942
* @method cmd_insertimage
5943
* @param value Value passed from the execCommand method
5944
* @description This is an execCommand override method. It is called from execCommand when the execCommand('insertimage') is used.
5946
cmd_insertimage: function(value) {
5947
var exec = true, _img = null, action = 'insertimage',
5948
el = this._getSelectedElement();
5951
value = this.get('blankimage');
5955
* @knownissue Safari Cursor Position
5956
* @browser Safari 2.x
5957
* @description The issue here is that we have no way of knowing where the cursor position is
5958
* inside of the iframe, so we have to place the newly inserted data in the best place that we can.
5961
YAHOO.log('InsertImage: ' + el.tagName, 'info', 'SimpleEditor');
5962
if (this._isElement(el, 'img')) {
5963
this.currentElement[0] = el;
5966
if (this._getDoc().queryCommandEnabled(action)) {
5967
this._getDoc().execCommand('insertimage', false, value);
5968
var imgs = this._getDoc().getElementsByTagName('img');
5969
for (var i = 0; i < imgs.length; i++) {
5970
if (!YAHOO.util.Dom.hasClass(imgs[i], 'yui-img')) {
5971
YAHOO.util.Dom.addClass(imgs[i], 'yui-img');
5972
this.currentElement[0] = imgs[i];
5977
if (el == this._getDoc().body) {
5978
_img = this._getDoc().createElement('img');
5979
_img.setAttribute('src', value);
5980
YAHOO.util.Dom.addClass(_img, 'yui-img');
5981
this._getDoc().body.appendChild(_img);
5983
this._createCurrentElement('img');
5984
_img = this._getDoc().createElement('img');
5985
_img.setAttribute('src', value);
5986
YAHOO.util.Dom.addClass(_img, 'yui-img');
5987
this.currentElement[0].parentNode.replaceChild(_img, this.currentElement[0]);
5989
this.currentElement[0] = _img;
5996
* @method cmd_inserthtml
5997
* @param value Value passed from the execCommand method
5998
* @description This is an execCommand override method. It is called from execCommand when the execCommand('inserthtml') is used.
6000
cmd_inserthtml: function(value) {
6001
var exec = true, action = 'inserthtml', _span = null, _range = null;
6003
* @knownissue Safari cursor position
6004
* @browser Safari 2.x
6005
* @description The issue here is that we have no way of knowing where the cursor position is
6006
* inside of the iframe, so we have to place the newly inserted data in the best place that we can.
6008
if (this.browser.webkit && !this._getDoc().queryCommandEnabled(action)) {
6009
YAHOO.log('More Safari DOM tricks (inserthtml)', 'info', 'EditorSafari');
6010
this._createCurrentElement('img');
6011
_span = this._getDoc().createElement('span');
6012
_span.innerHTML = value;
6013
this.currentElement[0].parentNode.replaceChild(_span, this.currentElement[0]);
6015
} else if (this.browser.ie) {
6016
_range = this._getRange();
6018
_range.item(0).outerHTML = value;
6020
_range.pasteHTML(value);
6028
* @param tag The tag of the list you want to create (eg, ul or ol)
6029
* @description This is a combined execCommand override method. It is called from the cmd_insertorderedlist and cmd_insertunorderedlist methods.
6031
cmd_list: function(tag) {
6032
var exec = true, list = null, li = 0, el = null, str = '',
6033
selEl = this._getSelectedElement(), action = 'insertorderedlist';
6035
action = 'insertunorderedlist';
6038
* @knownissue Safari 2.+ doesn't support ordered and unordered lists
6039
* @browser Safari 2.x
6040
* The issue with this workaround is that when applied to a set of text
6041
* that has BR's in it, Safari may or may not pick up the individual items as
6042
* list items. This is fixed in WebKit (Safari 3)
6043
* 2.6.0: Seems there are still some issues with List Creation and Safari 3, reverting to previously working Safari 2.x code
6045
//if ((this.browser.webkit && !this._getDoc().queryCommandEnabled(action))) {
6046
if (this.browser.webkit) {
6047
if (this._isElement(selEl, 'li') && this._isElement(selEl.parentNode, tag)) {
6048
YAHOO.log('We already have a list, undo it', 'info', 'SimpleEditor');
6049
el = selEl.parentNode;
6050
list = this._getDoc().createElement('span');
6051
YAHOO.util.Dom.addClass(list, 'yui-non');
6053
var lis = el.getElementsByTagName('li');
6054
for (li = 0; li < lis.length; li++) {
6055
str += '<div>' + lis[li].innerHTML + '</div>';
6057
list.innerHTML = str;
6058
this.currentElement[0] = el;
6059
this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
6061
YAHOO.log('Create list item', 'info', 'SimpleEditor');
6062
this._createCurrentElement(tag.toLowerCase());
6063
list = this._getDoc().createElement(tag);
6064
for (li = 0; li < this.currentElement.length; li++) {
6065
var newli = this._getDoc().createElement('li');
6066
newli.innerHTML = this.currentElement[li].innerHTML + '<span class="yui-non"> </span> ';
6067
list.appendChild(newli);
6069
this.currentElement[li].parentNode.removeChild(this.currentElement[li]);
6073
var items = list.firstChild.innerHTML.split('<br>');
6074
if (items.length > 0) {
6075
list.innerHTML = '';
6076
for (var i = 0; i < items.length; i++) {
6077
var item = this._getDoc().createElement('li');
6078
item.innerHTML = items[i];
6079
list.appendChild(item);
6083
this.currentElement[0].parentNode.replaceChild(list, this.currentElement[0]);
6084
this.currentElement[0] = list;
6085
var _h = this.currentElement[0].firstChild;
6086
_h = Dom.getElementsByClassName('yui-non', 'span', _h)[0];
6087
this._getSelection().setBaseAndExtent(_h, 1, _h, _h.innerText.length);
6091
el = this._getSelectedElement();
6092
YAHOO.log(el.tagName);
6093
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..
6094
YAHOO.log('We already have a list, undo it', 'info', 'SimpleEditor');
6095
if (this.browser.ie) {
6096
if ((this.browser.ie && this._isElement(el, 'ul')) || (this.browser.ie && this._isElement(el, 'ol'))) {
6097
el = el.getElementsByTagName('li')[0];
6099
YAHOO.log('Undo IE', 'info', 'SimpleEditor');
6101
var lis2 = el.parentNode.getElementsByTagName('li');
6102
for (var j = 0; j < lis2.length; j++) {
6103
str += lis2[j].innerHTML + '<br>';
6105
var newEl = this._getDoc().createElement('span');
6106
newEl.innerHTML = str;
6107
el.parentNode.parentNode.replaceChild(newEl, el.parentNode);
6110
this._getDoc().execCommand(action, '', el.parentNode);
6115
if (this.browser.opera) {
6117
window.setTimeout(function() {
6118
var liso = self._getDoc().getElementsByTagName('li');
6119
for (var i = 0; i < liso.length; i++) {
6120
if (liso[i].innerHTML.toLowerCase() == '<br>') {
6121
liso[i].parentNode.parentNode.removeChild(liso[i].parentNode);
6126
if (this.browser.ie && exec) {
6128
if (this._getRange().html) {
6129
html = '<li>' + this._getRange().html+ '</li>';
6131
var t = this._getRange().text.split('\n');
6134
for (var ie = 0; ie < t.length; ie++) {
6135
html += '<li>' + t[ie] + '</li>';
6138
var txt = this._getRange().text;
6140
html = '<li id="new_list_item">' + txt + '</li>';
6142
html = '<li>' + txt + '</li>';
6146
this._getRange().pasteHTML('<' + tag + '>' + html + '</' + tag + '>');
6147
var new_item = this._getDoc().getElementById('new_list_item');
6149
var range = this._getDoc().body.createTextRange();
6150
range.moveToElementText(new_item);
6151
range.collapse(false);
6161
* @method cmd_insertorderedlist
6162
* @param value Value passed from the execCommand method
6163
* @description This is an execCommand override method. It is called from execCommand when the execCommand('insertorderedlist ') is used.
6165
cmd_insertorderedlist: function(value) {
6166
return [this.cmd_list('ol')];
6169
* @method cmd_insertunorderedlist
6170
* @param value Value passed from the execCommand method
6171
* @description This is an execCommand override method. It is called from execCommand when the execCommand('insertunorderedlist') is used.
6173
cmd_insertunorderedlist: function(value) {
6174
return [this.cmd_list('ul')];
6177
* @method cmd_fontname
6178
* @param value Value passed from the execCommand method
6179
* @description This is an execCommand override method. It is called from execCommand when the execCommand('fontname') is used.
6181
cmd_fontname: function(value) {
6183
selEl = this._getSelectedElement();
6185
this.currentFont = value;
6186
if (selEl && selEl.tagName && !this._hasSelection() && !this._isElement(selEl, 'body') && !this.get('insert')) {
6187
YAHOO.util.Dom.setStyle(selEl, 'font-family', value);
6189
} else if (this.get('insert') && !this._hasSelection()) {
6190
YAHOO.log('No selection and no selected element and we are in insert mode', 'info', 'SimpleEditor');
6191
var el = this._createInsertElement({ fontFamily: value });
6197
* @method cmd_fontsize
6198
* @param value Value passed from the execCommand method
6199
* @description This is an execCommand override method. It is called from execCommand when the execCommand('fontsize') is used.
6201
cmd_fontsize: function(value) {
6202
var el = null, go = true;
6203
el = this._getSelectedElement();
6204
if (this.browser.webkit) {
6205
if (this.currentElement[0]) {
6206
if (el == this.currentElement[0]) {
6208
YAHOO.util.Dom.setStyle(el, 'fontSize', value);
6209
this._selectNode(el);
6210
this.currentElement[0] = el;
6215
if (!this._isElement(this._getSelectedElement(), 'body') && (!this._hasSelection())) {
6216
el = this._getSelectedElement();
6217
YAHOO.util.Dom.setStyle(el, 'fontSize', value);
6218
if (this.get('insert') && this.browser.ie) {
6219
var r = this._getRange();
6223
this._selectNode(el);
6225
} else if (this.currentElement && (this.currentElement.length > 0) && (!this._hasSelection()) && (!this.get('insert'))) {
6226
YAHOO.util.Dom.setStyle(this.currentElement, 'fontSize', value);
6228
if (this.get('insert') && !this._hasSelection()) {
6229
el = this._createInsertElement({ fontSize: value });
6230
this.currentElement[0] = el;
6231
this._selectNode(this.currentElement[0]);
6233
this._createCurrentElement('span', {'fontSize': value, fontFamily: el.style.fontFamily, color: el.style.color, backgroundColor: el.style.backgroundColor });
6234
this._selectNode(this.currentElement[0]);
6244
* @param {HTMLElement} el The element to swap with
6245
* @param {String} tagName The tagname of the element that you wish to create
6246
* @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.
6247
* @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.
6249
_swapEl: function(el, tagName, callback) {
6250
var _el = this._getDoc().createElement(tagName);
6252
_el.innerHTML = el.innerHTML;
6254
if (typeof callback == 'function') {
6255
callback.call(this, _el);
6258
el.parentNode.replaceChild(_el, el);
6264
* @method _createInsertElement
6265
* @description Creates a new "currentElement" then adds some text (and other things) to make it selectable and stylable. Then the user can continue typing.
6266
* @param {Object} css (optional) Object literal containing styles to apply to the new element.
6267
* @return {HTMLElement}
6269
_createInsertElement: function(css) {
6270
this._createCurrentElement('span', css);
6271
var el = this.currentElement[0];
6272
if (this.browser.webkit) {
6273
//Little Safari Hackery here..
6274
el.innerHTML = '<span class="yui-non"> </span>';
6276
this._getSelection().setBaseAndExtent(el, 1, el, el.innerText.length);
6277
} else if (this.browser.ie || this.browser.opera) {
6278
el.innerHTML = ' ';
6281
this._selectNode(el, true);
6286
* @method _createCurrentElement
6287
* @param {String} tagName (optional defaults to a) The tagname of the element that you wish to create
6288
* @param {Object} tagStyle (optional) Object literal containing styles to apply to the new element.
6289
* @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.
6290
* 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
6291
* <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.
6293
_createCurrentElement: function(tagName, tagStyle) {
6294
tagName = ((tagName) ? tagName : 'a');
6297
_doc = this._getDoc();
6299
if (this.currentFont) {
6303
tagStyle.fontFamily = this.currentFont;
6304
this.currentFont = null;
6306
this.currentElement = [];
6308
var _elCreate = function(tagName, tagStyle) {
6310
tagName = ((tagName) ? tagName : 'span');
6311
tagName = tagName.toLowerCase();
6319
el = _doc.createElement(tagName);
6322
el = _doc.createElement(tagName);
6323
if (tagName === 'span') {
6324
YAHOO.util.Dom.addClass(el, 'yui-tag-' + tagName);
6325
YAHOO.util.Dom.addClass(el, 'yui-tag');
6326
el.setAttribute('tag', tagName);
6329
for (var k in tagStyle) {
6330
if (YAHOO.lang.hasOwnProperty(tagStyle, k)) {
6331
el.style[k] = tagStyle[k];
6339
if (!this._hasSelection()) {
6340
if (this._getDoc().queryCommandEnabled('insertimage')) {
6341
this._getDoc().execCommand('insertimage', false, 'yui-tmp-img');
6342
var imgs = this._getDoc().getElementsByTagName('img');
6343
for (var j = 0; j < imgs.length; j++) {
6344
if (imgs[j].getAttribute('src', 2) == 'yui-tmp-img') {
6345
el = _elCreate(tagName, tagStyle);
6346
imgs[j].parentNode.replaceChild(el, imgs[j]);
6347
this.currentElement[this.currentElement.length] = el;
6351
if (this.currentEvent) {
6352
tar = YAHOO.util.Event.getTarget(this.currentEvent);
6355
tar = this._getDoc().body;
6360
* @knownissue Safari Cursor Position
6361
* @browser Safari 2.x
6362
* @description The issue here is that we have no way of knowing where the cursor position is
6363
* inside of the iframe, so we have to place the newly inserted data in the best place that we can.
6365
el = _elCreate(tagName, tagStyle);
6366
if (this._isElement(tar, 'body') || this._isElement(tar, 'html')) {
6367
if (this._isElement(tar, 'html')) {
6368
tar = this._getDoc().body;
6370
tar.appendChild(el);
6371
} else if (tar.nextSibling) {
6372
tar.parentNode.insertBefore(el, tar.nextSibling);
6374
tar.parentNode.appendChild(el);
6376
//this.currentElement = el;
6377
this.currentElement[this.currentElement.length] = el;
6378
this.currentEvent = null;
6379
if (this.browser.webkit) {
6380
//Force Safari to focus the new element
6381
this._getSelection().setBaseAndExtent(el, 0, el, 0);
6382
if (this.browser.webkit3) {
6383
this._getSelection().collapseToStart();
6385
this._getSelection().collapse(true);
6390
//Force CSS Styling for this action...
6391
this._setEditorStyle(true);
6392
this._getDoc().execCommand('fontname', false, 'yui-tmp');
6393
var _tmp = [], __tmp, __els = ['font', 'span', 'i', 'b', 'u'];
6395
if (!this._isElement(this._getSelectedElement(), 'body')) {
6396
__els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().tagName);
6397
__els[__els.length] = this._getDoc().getElementsByTagName(this._getSelectedElement().parentNode.tagName);
6399
for (var _els = 0; _els < __els.length; _els++) {
6400
var _tmp1 = this._getDoc().getElementsByTagName(__els[_els]);
6401
for (var e = 0; e < _tmp1.length; e++) {
6402
_tmp[_tmp.length] = _tmp1[e];
6407
for (var i = 0; i < _tmp.length; i++) {
6408
if ((YAHOO.util.Dom.getStyle(_tmp[i], 'font-family') == 'yui-tmp') || (_tmp[i].face && (_tmp[i].face == 'yui-tmp'))) {
6409
if (tagName !== 'span') {
6410
el = _elCreate(tagName, tagStyle);
6412
el = _elCreate(_tmp[i].tagName, tagStyle);
6414
el.innerHTML = _tmp[i].innerHTML;
6415
if (this._isElement(_tmp[i], 'ol') || (this._isElement(_tmp[i], 'ul'))) {
6416
var fc = _tmp[i].getElementsByTagName('li')[0];
6417
_tmp[i].style.fontFamily = 'inherit';
6418
fc.style.fontFamily = 'inherit';
6419
el.innerHTML = fc.innerHTML;
6422
this.currentElement[this.currentElement.length] = el;
6423
} else if (this._isElement(_tmp[i], 'li')) {
6424
_tmp[i].innerHTML = '';
6425
_tmp[i].appendChild(el);
6426
_tmp[i].style.fontFamily = 'inherit';
6427
this.currentElement[this.currentElement.length] = el;
6429
if (_tmp[i].parentNode) {
6430
_tmp[i].parentNode.replaceChild(el, _tmp[i]);
6431
this.currentElement[this.currentElement.length] = el;
6432
this.currentEvent = null;
6433
if (this.browser.webkit) {
6434
//Force Safari to focus the new element
6435
this._getSelection().setBaseAndExtent(el, 0, el, 0);
6436
if (this.browser.webkit3) {
6437
this._getSelection().collapseToStart();
6439
this._getSelection().collapse(true);
6442
if (this.browser.ie && tagStyle && tagStyle.fontSize) {
6443
this._getSelection().empty();
6445
if (this.browser.gecko) {
6446
this._getSelection().collapseToStart();
6452
var len = this.currentElement.length;
6453
for (var o = 0; o < len; o++) {
6454
if ((o + 1) != len) { //Skip the last one in the list
6455
if (this.currentElement[o] && this.currentElement[o].nextSibling) {
6456
if (this._isElement(this.currentElement[o], 'br')) {
6457
this.currentElement[this.currentElement.length] = this.currentElement[o].nextSibling;
6466
* @description Cleans the HTML with the cleanHTML method then places that string back into the textarea.
6469
saveHTML: function() {
6470
var html = this.cleanHTML();
6471
if (this._textarea) {
6472
this.get('element').value = html;
6474
this.get('element').innerHTML = html;
6476
if (this.get('saveEl') !== this.get('element')) {
6477
var out = this.get('saveEl');
6478
if (Lang.isString(out)) {
6482
if (out.tagName.toLowerCase() === 'textarea') {
6485
out.innerHTML = html;
6492
* @method setEditorHTML
6493
* @param {String} incomingHTML The html content to load into the editor
6494
* @description Loads HTML into the editors body
6496
setEditorHTML: function(incomingHTML) {
6497
var html = this._cleanIncomingHTML(incomingHTML);
6498
this._getDoc().body.innerHTML = html;
6502
* @method getEditorHTML
6503
* @description Gets the unprocessed/unfiltered HTML from the editor
6505
getEditorHTML: function() {
6506
var b = this._getDoc().body;
6508
YAHOO.log('Body is null, returning null.', 'error', 'SimpleEditor');
6511
return this._getDoc().body.innerHTML;
6515
* @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.
6518
if (this.browser.gecko) {
6519
this._setDesignMode('on');
6522
if (this.browser.webkit) {
6524
window.setTimeout(function() {
6525
self._setInitialContent.call(self);
6528
//Adding this will close all other Editor window's when showing this one.
6529
if (this.currentWindow) {
6532
//Put the iframe back in place
6533
this.get('iframe').setStyle('position', 'static');
6534
this.get('iframe').setStyle('left', '');
6538
* @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.
6541
//Adding this will close all other Editor window's.
6542
if (this.currentWindow) {
6545
if (this._fixNodesTimer) {
6546
clearTimeout(this._fixNodesTimer);
6547
this._fixNodesTimer = null;
6549
if (this._nodeChangeTimer) {
6550
clearTimeout(this._nodeChangeTimer);
6551
this._nodeChangeTimer = null;
6553
this._lastNodeChange = 0;
6554
//Move the iframe off of the screen, so that in containers with visiblity hidden, IE will not cover other elements.
6555
this.get('iframe').setStyle('position', 'absolute');
6556
this.get('iframe').setStyle('left', '-9999px');
6559
* @method _cleanIncomingHTML
6560
* @param {String} html The unfiltered HTML
6561
* @description Process the HTML with a few regexes to clean it up and stabilize the input
6562
* @return {String} The filtered HTML
6564
_cleanIncomingHTML: function(html) {
6565
html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
6566
html = html.replace(/<\/strong>/gi, '</b>');
6568
//replace embed before em check
6569
html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
6570
html = html.replace(/<\/embed>/gi, '</YUI_EMBED>');
6572
html = html.replace(/<em([^>]*)>/gi, '<i$1>');
6573
html = html.replace(/<\/em>/gi, '</i>');
6574
html = html.replace(/_moz_dirty=""/gi, '');
6576
//Put embed tags back in..
6577
html = html.replace(/<YUI_EMBED([^>]*)>/gi, '<embed$1>');
6578
html = html.replace(/<\/YUI_EMBED>/gi, '</embed>');
6579
if (this.get('plainText')) {
6580
YAHOO.log('Filtering as plain text', 'info', 'SimpleEditor');
6581
html = html.replace(/\n/g, '<br>').replace(/\r/g, '<br>');
6582
html = html.replace(/ /gi, ' '); //Replace all double spaces
6583
html = html.replace(/\t/gi, ' '); //Replace all tabs
6585
//Removing Script Tags from the Editor
6586
html = html.replace(/<script([^>]*)>/gi, '<bad>');
6587
html = html.replace(/<\/script([^>]*)>/gi, '</bad>');
6588
html = html.replace(/<script([^>]*)>/gi, '<bad>');
6589
html = html.replace(/<\/script([^>]*)>/gi, '</bad>');
6590
//Replace the line feeds
6591
html = html.replace(/\r\n/g, '<YUI_LF>').replace(/\n/g, '<YUI_LF>').replace(/\r/g, '<YUI_LF>');
6593
//Remove Bad HTML elements (used to be script nodes)
6594
html = html.replace(new RegExp('<bad([^>]*)>(.*?)<\/bad>', 'gi'), '');
6595
//Replace the lines feeds
6596
html = html.replace(/<YUI_LF>/g, '\n');
6601
* @param {String} html The unfiltered HTML
6602
* @description Process the HTML with a few regexes to clean it up and stabilize the output
6603
* @return {String} The filtered HTML
6605
cleanHTML: function(html) {
6606
//Start Filtering Output
6609
html = this.getEditorHTML();
6611
var markup = this.get('markup');
6612
//Make some backups...
6613
html = this.pre_filter_linebreaks(html, markup);
6616
html = this.filter_msword(html);
6618
html = html.replace(/<img([^>]*)\/>/gi, '<YUI_IMG$1>');
6619
html = html.replace(/<img([^>]*)>/gi, '<YUI_IMG$1>');
6621
html = html.replace(/<input([^>]*)\/>/gi, '<YUI_INPUT$1>');
6622
html = html.replace(/<input([^>]*)>/gi, '<YUI_INPUT$1>');
6624
html = html.replace(/<ul([^>]*)>/gi, '<YUI_UL$1>');
6625
html = html.replace(/<\/ul>/gi, '<\/YUI_UL>');
6626
html = html.replace(/<blockquote([^>]*)>/gi, '<YUI_BQ$1>');
6627
html = html.replace(/<\/blockquote>/gi, '<\/YUI_BQ>');
6629
html = html.replace(/<embed([^>]*)>/gi, '<YUI_EMBED$1>');
6630
html = html.replace(/<\/embed>/gi, '<\/YUI_EMBED>');
6632
//Convert b and i tags to strong and em tags
6633
if ((markup == 'semantic') || (markup == 'xhtml')) {
6634
html = html.replace(/<i(\s+[^>]*)?>/gi, '<em$1>');
6635
html = html.replace(/<\/i>/gi, '</em>');
6636
html = html.replace(/<b(\s+[^>]*)?>/gi, '<strong$1>');
6637
html = html.replace(/<\/b>/gi, '</strong>');
6640
html = html.replace(/_moz_dirty=""/gi, '');
6642
//normalize strikethrough
6643
html = html.replace(/<strike/gi, '<span style="text-decoration: line-through;"');
6644
html = html.replace(/\/strike>/gi, '/span>');
6648
if (this.browser.ie) {
6649
html = html.replace(/text-decoration/gi, 'text-decoration');
6650
html = html.replace(/font-weight/gi, 'font-weight');
6651
html = html.replace(/_width="([^>]*)"/gi, '');
6652
html = html.replace(/_height="([^>]*)"/gi, '');
6653
//Cleanup Image URL's
6654
var url = this._baseHREF.replace(/\//gi, '\\/'),
6655
re = new RegExp('src="' + url, 'gi');
6656
html = html.replace(re, 'src="');
6658
html = html.replace(/<font/gi, '<font');
6659
html = html.replace(/<\/font>/gi, '</font>');
6660
html = html.replace(/<span/gi, '<span');
6661
html = html.replace(/<\/span>/gi, '</span>');
6662
if ((markup == 'semantic') || (markup == 'xhtml') || (markup == 'css')) {
6663
html = html.replace(new RegExp('<font([^>]*)face="([^>]*)">(.*?)<\/font>', 'gi'), '<span $1 style="font-family: $2;">$3</span>');
6664
html = html.replace(/<u/gi, '<span style="text-decoration: underline;"');
6665
if (this.browser.webkit) {
6666
html = html.replace(new RegExp('<span class="Apple-style-span" style="font-weight: bold;">([^>]*)<\/span>', 'gi'), '<strong>$1</strong>');
6667
html = html.replace(new RegExp('<span class="Apple-style-span" style="font-style: italic;">([^>]*)<\/span>', 'gi'), '<em>$1</em>');
6669
html = html.replace(/\/u>/gi, '/span>');
6670
if (markup == 'css') {
6671
html = html.replace(/<em([^>]*)>/gi, '<i$1>');
6672
html = html.replace(/<\/em>/gi, '</i>');
6673
html = html.replace(/<strong([^>]*)>/gi, '<b$1>');
6674
html = html.replace(/<\/strong>/gi, '</b>');
6675
html = html.replace(/<b/gi, '<span style="font-weight: bold;"');
6676
html = html.replace(/\/b>/gi, '/span>');
6677
html = html.replace(/<i/gi, '<span style="font-style: italic;"');
6678
html = html.replace(/\/i>/gi, '/span>');
6680
html = html.replace(/ /gi, ' '); //Replace all double spaces and replace with a single
6682
html = html.replace(/<u/gi, '<u');
6683
html = html.replace(/\/u>/gi, '/u>');
6685
html = html.replace(/<ol([^>]*)>/gi, '<ol$1>');
6686
html = html.replace(/\/ol>/gi, '/ol>');
6687
html = html.replace(/<li/gi, '<li');
6688
html = html.replace(/\/li>/gi, '/li>');
6689
html = this.filter_safari(html);
6691
html = this.filter_internals(html);
6693
html = this.filter_all_rgb(html);
6695
//Replace our backups with the real thing
6696
html = this.post_filter_linebreaks(html, markup);
6698
if (markup == 'xhtml') {
6699
html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1 />');
6700
html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1 />');
6702
html = html.replace(/<YUI_IMG([^>]*)>/g, '<img $1>');
6703
html = html.replace(/<YUI_INPUT([^>]*)>/g, '<input $1>');
6705
html = html.replace(/<YUI_UL([^>]*)>/g, '<ul$1>');
6706
html = html.replace(/<\/YUI_UL>/g, '<\/ul>');
6708
html = this.filter_invalid_lists(html);
6710
html = html.replace(/<YUI_BQ([^>]*)>/g, '<blockquote$1>');
6711
html = html.replace(/<\/YUI_BQ>/g, '<\/blockquote>');
6713
html = html.replace(/<YUI_EMBED([^>]*)>/g, '<embed$1>');
6714
html = html.replace(/<\/YUI_EMBED>/g, '<\/embed>');
6716
//This should fix &s in URL's
6717
html = html.replace(/ & /gi, 'YUI_AMP');
6718
html = html.replace(/&/gi, '&');
6719
html = html.replace(/YUI_AMP/gi, ' & ');
6721
//Trim the output, removing whitespace from the beginning and end
6722
html = YAHOO.lang.trim(html);
6724
if (this.get('removeLineBreaks')) {
6725
html = html.replace(/\n/g, '').replace(/\r/g, '');
6726
html = html.replace(/ /gi, ' '); //Replace all double spaces and replace with a single
6730
if (html.substring(0, 6).toLowerCase() == '<span>') {
6731
html = html.substring(6);
6733
if (html.substring(html.length - 7, html.length).toLowerCase() == '</span>') {
6734
html = html.substring(0, html.length - 7);
6738
for (var v in this.invalidHTML) {
6739
if (YAHOO.lang.hasOwnProperty(this.invalidHTML, v)) {
6740
if (Lang.isObject(v) && v.keepContents) {
6741
html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '$1');
6743
html = html.replace(new RegExp('<' + v + '([^>]*)>(.*?)<\/' + v + '>', 'gi'), '');
6748
this.fireEvent('cleanHTML', { type: 'cleanHTML', target: this, html: html });
6753
* @method filter_msword
6754
* @param String html The HTML string to filter
6755
* @description Filters out msword html attributes and other junk. Activate with filterWord: true in config
6757
filter_msword: function(html) {
6758
if (!this.get('filterWord')) {
6761
//Remove the ms o: tags
6762
html = html.replace(/<o:p>\s*<\/o:p>/g, '');
6763
html = html.replace(/<o:p>[\s\S]*?<\/o:p>/g, ' ');
6765
//Remove the ms w: tags
6766
html = html.replace( /<w:[^>]*>[\s\S]*?<\/w:[^>]*>/gi, '');
6768
//Remove mso-? styles.
6769
html = html.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '');
6771
//Remove more bogus MS styles.
6772
html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '');
6773
html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"");
6774
html = html.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '');
6775
html = html.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"");
6776
html = html.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");
6777
html = html.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" );
6778
html = html.replace( /\s*tab-stops:[^;"]*;?/gi, '');
6779
html = html.replace( /\s*tab-stops:[^"]*/gi, '');
6781
//Remove XML declarations
6782
html = html.replace(/<\\?\?xml[^>]*>/gi, '');
6785
html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");
6787
//Remove language tags
6788
html = html.replace( /<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3");
6790
//Remove onmouseover and onmouseout events (from MS Word comments effect)
6791
html = html.replace( /<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3");
6792
html = html.replace( /<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3");
6797
* @method filter_invalid_lists
6798
* @param String html The HTML string to filter
6799
* @description Filters invalid ol and ul list markup, converts this: <li></li><ol>..</ol> to this: <li></li><li><ol>..</ol></li>
6801
filter_invalid_lists: function(html) {
6802
html = html.replace(/<\/li>\n/gi, '</li>');
6804
html = html.replace(/<\/li><ol>/gi, '</li><li><ol>');
6805
html = html.replace(/<\/ol>/gi, '</ol></li>');
6806
html = html.replace(/<\/ol><\/li>\n/gi, "</ol>");
6808
html = html.replace(/<\/li><ul>/gi, '</li><li><ul>');
6809
html = html.replace(/<\/ul>/gi, '</ul></li>');
6810
html = html.replace(/<\/ul><\/li>\n?/gi, "</ul>");
6812
html = html.replace(/<\/li>/gi, "</li>");
6813
html = html.replace(/<\/ol>/gi, "</ol>");
6814
html = html.replace(/<ol>/gi, "<ol>");
6815
html = html.replace(/<ul>/gi, "<ul>");
6819
* @method filter_safari
6820
* @param String html The HTML string to filter
6821
* @description Filters strings specific to Safari
6824
filter_safari: function(html) {
6825
if (this.browser.webkit) {
6826
//<span class="Apple-tab-span" style="white-space:pre"> </span>
6827
html = html.replace(/<span class="Apple-tab-span" style="white-space:pre">([^>])<\/span>/gi, ' ');
6828
html = html.replace(/Apple-style-span/gi, '');
6829
html = html.replace(/style="line-height: normal;"/gi, '');
6830
html = html.replace(/yui-wk-div/gi, '');
6831
html = html.replace(/yui-wk-p/gi, '');
6835
html = html.replace(/<li><\/li>/gi, '');
6836
html = html.replace(/<li> <\/li>/gi, '');
6837
html = html.replace(/<li> <\/li>/gi, '');
6838
//Remove bogus DIV's - updated from just removing the div's to replacing /div with a break
6839
if (this.get('ptags')) {
6840
html = html.replace(/<div([^>]*)>/g, '<p$1>');
6841
html = html.replace(/<\/div>/gi, '</p>');
6843
html = html.replace(/<div>/gi, '<br>');
6844
html = html.replace(/<\/div>/gi, '');
6850
* @method filter_internals
6851
* @param String html The HTML string to filter
6852
* @description Filters internal RTE strings and bogus attrs we don't want
6855
filter_internals: function(html) {
6856
html = html.replace(/\r/g, '');
6857
//Fix stuff we don't want
6858
html = html.replace(/<\/?(body|head|html)[^>]*>/gi, '');
6860
html = html.replace(/<YUI_BR><\/li>/gi, '</li>');
6862
html = html.replace(/yui-tag-span/gi, '');
6863
html = html.replace(/yui-tag/gi, '');
6864
html = html.replace(/yui-non/gi, '');
6865
html = html.replace(/yui-img/gi, '');
6866
html = html.replace(/ tag="span"/gi, '');
6867
html = html.replace(/ class=""/gi, '');
6868
html = html.replace(/ style=""/gi, '');
6869
html = html.replace(/ class=" "/gi, '');
6870
html = html.replace(/ class=" "/gi, '');
6871
html = html.replace(/ target=""/gi, '');
6872
html = html.replace(/ title=""/gi, '');
6874
if (this.browser.ie) {
6875
html = html.replace(/ class= /gi, '');
6876
html = html.replace(/ class= >/gi, '');
6882
* @method filter_all_rgb
6883
* @param String str The HTML string to filter
6884
* @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"
6887
filter_all_rgb: function(str) {
6888
var exp = new RegExp("rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)", "gi");
6889
var arr = str.match(exp);
6890
if (Lang.isArray(arr)) {
6891
for (var i = 0; i < arr.length; i++) {
6892
var color = this.filter_rgb(arr[i]);
6893
str = str.replace(arr[i].toString(), color);
6900
* @method filter_rgb
6901
* @param String css The CSS string containing rgb(#,#,#);
6902
* @description Converts an RGB color string to a hex color, example: rgb(0, 255, 0) converts to #00ff00
6905
filter_rgb: function(css) {
6906
if (css.toLowerCase().indexOf('rgb') != -1) {
6907
var exp = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi");
6908
var rgb = css.replace(exp, "$1,$2,$3,$4,$5").split(',');
6910
if (rgb.length == 5) {
6911
var r = parseInt(rgb[1], 10).toString(16);
6912
var g = parseInt(rgb[2], 10).toString(16);
6913
var b = parseInt(rgb[3], 10).toString(16);
6915
r = r.length == 1 ? '0' + r : r;
6916
g = g.length == 1 ? '0' + g : g;
6917
b = b.length == 1 ? '0' + b : b;
6919
css = "#" + r + g + b;
6925
* @method pre_filter_linebreaks
6926
* @param String html The HTML to filter
6927
* @param String markup The markup type to filter to
6928
* @description HTML Pre Filter
6931
pre_filter_linebreaks: function(html, markup) {
6932
if (this.browser.webkit) {
6933
html = html.replace(/<br class="khtml-block-placeholder">/gi, '<YUI_BR>');
6934
html = html.replace(/<br class="webkit-block-placeholder">/gi, '<YUI_BR>');
6936
html = html.replace(/<br>/gi, '<YUI_BR>');
6937
html = html.replace(/<br (.*?)>/gi, '<YUI_BR>');
6938
html = html.replace(/<br\/>/gi, '<YUI_BR>');
6939
html = html.replace(/<br \/>/gi, '<YUI_BR>');
6940
html = html.replace(/<div><YUI_BR><\/div>/gi, '<YUI_BR>');
6941
html = html.replace(/<p>( | )<\/p>/g, '<YUI_BR>');
6942
html = html.replace(/<p><br> <\/p>/gi, '<YUI_BR>');
6943
html = html.replace(/<p> <\/p>/gi, '<YUI_BR>');
6945
html = html.replace(/<YUI_BR>$/, '');
6947
html = html.replace(/<YUI_BR><\/p>/g, '</p>');
6948
if (this.browser.ie) {
6949
html = html.replace(/ /g, '\t');
6954
* @method post_filter_linebreaks
6955
* @param String html The HTML to filter
6956
* @param String markup The markup type to filter to
6957
* @description HTML Pre Filter
6960
post_filter_linebreaks: function(html, markup) {
6961
if (markup == 'xhtml') {
6962
html = html.replace(/<YUI_BR>/g, '<br />');
6964
html = html.replace(/<YUI_BR>/g, '<br>');
6969
* @method clearEditorDoc
6970
* @description Clear the doc of the Editor
6972
clearEditorDoc: function() {
6973
this._getDoc().body.innerHTML = ' ';
6976
* @method openWindow
6977
* @description Override Method for Advanced Editor
6979
openWindow: function(win) {
6982
* @method moveWindow
6983
* @description Override Method for Advanced Editor
6985
moveWindow: function() {
6989
* @method _closeWindow
6990
* @description Override Method for Advanced Editor
6992
_closeWindow: function() {
6995
* @method closeWindow
6996
* @description Override Method for Advanced Editor
6998
closeWindow: function() {
6999
//this.unsubscribeAll('afterExecCommand');
7000
this.toolbar.resetAllButtons();
7005
* @description Destroys the editor, all of it's elements and objects.
7008
destroy: function() {
7009
YAHOO.log('Destroying Editor', 'warn', 'SimpleEditor');
7011
YAHOO.log('Destroying Resize', 'warn', 'SimpleEditor');
7012
this.resize.destroy();
7015
YAHOO.log('Unreg DragDrop Instance', 'warn', 'SimpleEditor');
7018
if (this.get('panel')) {
7019
YAHOO.log('Destroying Editor Panel', 'warn', 'SimpleEditor');
7020
this.get('panel').destroy();
7023
this.toolbar.destroy();
7024
YAHOO.log('Restoring TextArea', 'info', 'SimpleEditor');
7025
this.setStyle('visibility', 'visible');
7026
this.setStyle('position', 'static');
7027
this.setStyle('top', '');
7028
this.setStyle('left', '');
7029
var textArea = this.get('element');
7030
this.get('element_cont').get('parentNode').replaceChild(textArea, this.get('element_cont').get('element'));
7031
this.get('element_cont').get('element').innerHTML = '';
7032
this.set('handleSubmit', false); //Remove the submit handler
7037
* @description Returns a string representing the editor.
7040
toString: function() {
7041
var str = 'SimpleEditor';
7042
if (this.get && this.get('element_cont')) {
7043
str = 'SimpleEditor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
7050
* @event toolbarLoaded
7051
* @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.
7052
* @type YAHOO.util.CustomEvent
7056
* @description Event is fired after the cleanHTML method is called.
7057
* @type YAHOO.util.CustomEvent
7060
* @event afterRender
7061
* @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.
7062
* @type YAHOO.util.CustomEvent
7065
* @event editorContentLoaded
7066
* @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.
7067
* @type YAHOO.util.CustomEvent
7070
* @event beforeNodeChange
7071
* @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.
7072
* @type YAHOO.util.CustomEvent
7075
* @event afterNodeChange
7076
* @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.
7077
* @type YAHOO.util.CustomEvent
7080
* @event beforeExecCommand
7081
* @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.
7082
* @type YAHOO.util.CustomEvent
7085
* @event afterExecCommand
7086
* @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.
7087
* @type YAHOO.util.CustomEvent
7090
* @event editorMouseUp
7091
* @param {Event} ev The DOM Event that occured
7092
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7093
* @type YAHOO.util.CustomEvent
7096
* @event editorMouseDown
7097
* @param {Event} ev The DOM Event that occured
7098
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7099
* @type YAHOO.util.CustomEvent
7102
* @event editorDoubleClick
7103
* @param {Event} ev The DOM Event that occured
7104
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7105
* @type YAHOO.util.CustomEvent
7108
* @event editorClick
7109
* @param {Event} ev The DOM Event that occured
7110
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7111
* @type YAHOO.util.CustomEvent
7114
* @event editorKeyUp
7115
* @param {Event} ev The DOM Event that occured
7116
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7117
* @type YAHOO.util.CustomEvent
7120
* @event editorKeyPress
7121
* @param {Event} ev The DOM Event that occured
7122
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7123
* @type YAHOO.util.CustomEvent
7126
* @event editorKeyDown
7127
* @param {Event} ev The DOM Event that occured
7128
* @description Passed through HTML Event. See <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> for more information on listening for this event.
7129
* @type YAHOO.util.CustomEvent
7132
* @event beforeEditorMouseUp
7133
* @param {Event} ev The DOM Event that occured
7134
* @description Fires before editor event, returning false will stop the internal processing.
7135
* @type YAHOO.util.CustomEvent
7138
* @event beforeEditorMouseDown
7139
* @param {Event} ev The DOM Event that occured
7140
* @description Fires before editor event, returning false will stop the internal processing.
7141
* @type YAHOO.util.CustomEvent
7144
* @event beforeEditorDoubleClick
7145
* @param {Event} ev The DOM Event that occured
7146
* @description Fires before editor event, returning false will stop the internal processing.
7147
* @type YAHOO.util.CustomEvent
7150
* @event beforeEditorClick
7151
* @param {Event} ev The DOM Event that occured
7152
* @description Fires before editor event, returning false will stop the internal processing.
7153
* @type YAHOO.util.CustomEvent
7156
* @event beforeEditorKeyUp
7157
* @param {Event} ev The DOM Event that occured
7158
* @description Fires before editor event, returning false will stop the internal processing.
7159
* @type YAHOO.util.CustomEvent
7162
* @event beforeEditorKeyPress
7163
* @param {Event} ev The DOM Event that occured
7164
* @description Fires before editor event, returning false will stop the internal processing.
7165
* @type YAHOO.util.CustomEvent
7168
* @event beforeEditorKeyDown
7169
* @param {Event} ev The DOM Event that occured
7170
* @description Fires before editor event, returning false will stop the internal processing.
7171
* @type YAHOO.util.CustomEvent
7175
* @event editorWindowFocus
7176
* @description Fires when the iframe is focused. Note, this is window focus event, not an Editor focus event.
7177
* @type YAHOO.util.CustomEvent
7180
* @event editorWindowBlur
7181
* @description Fires when the iframe is blurred. Note, this is window blur event, not an Editor blur event.
7182
* @type YAHOO.util.CustomEvent
7187
* @description Singleton object used to track the open window objects and panels across the various open editors
7191
YAHOO.widget.EditorInfo = {
7194
* @property _instances
7195
* @description A reference to all editors on the page.
7201
* @property blankImage
7202
* @description A reference to the blankImage url
7209
* @description A reference to the currently open window object in any editor on the page.
7210
* @type Object <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>
7216
* @description A reference to the currently open panel in any editor on the page.
7217
* @type Object <a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>
7221
* @method getEditorById
7222
* @description Returns a reference to the Editor object associated with the given textarea
7223
* @param {String/HTMLElement} id The id or reference of the textarea to return the Editor instance of
7224
* @return Object <a href="YAHOO.widget.Editor.html">YAHOO.widget.Editor</a>
7226
getEditorById: function(id) {
7227
if (!YAHOO.lang.isString(id)) {
7228
//Not a string, assume a node Reference
7231
if (this._instances[id]) {
7232
return this._instances[id];
7238
* @description Returns a string representing the EditorInfo.
7241
toString: function() {
7243
for (var i in this._instances) {
7244
if (Lang.hasOwnProperty(this._instances, i)) {
7248
return 'Editor Info (' + len + ' registered intance' + ((len > 1) ? 's' : '') + ')';
7258
* @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>
7259
* @namespace YAHOO.widget
7260
* @requires yahoo, dom, element, event, container_core, simpleeditor
7261
* @optional dragdrop, animation, menu, button, resize
7265
var Dom = YAHOO.util.Dom,
7266
Event = YAHOO.util.Event,
7268
Toolbar = YAHOO.widget.Toolbar;
7271
* 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.
7274
* @extends YAHOO.widget.SimpleEditor
7275
* @param {String/HTMLElement} el The textarea element to turn into an editor.
7276
* @param {Object} attrs Object liternal containing configuration parameters.
7279
YAHOO.widget.Editor = function(el, attrs) {
7280
YAHOO.log('Editor Initalizing', 'info', 'Editor');
7281
YAHOO.widget.Editor.superclass.constructor.call(this, el, attrs);
7284
YAHOO.extend(YAHOO.widget.Editor, YAHOO.widget.SimpleEditor, {
7287
* @property _undoCache
7288
* @description An Array hash of the Undo Levels.
7294
* @property _undoLevel
7295
* @description The index of the current undo state.
7301
* @method _hasUndoLevel
7302
* @description Checks to see if we have an undo level available
7305
_hasUndoLevel: function() {
7306
return ((this._undoCache.length > 1) && this._undoLevel);
7310
* @method _undoNodeChange
7311
* @description nodeChange listener for undo processing
7313
_undoNodeChange: function() {
7314
var undo_button = this.toolbar.getButtonByValue('undo'),
7315
redo_button = this.toolbar.getButtonByValue('redo');
7316
if (undo_button && redo_button) {
7317
if (this._hasUndoLevel()) {
7318
this.toolbar.enableButton(undo_button);
7320
if (this._undoLevel < this._undoCache.length) {
7321
this.toolbar.enableButton(redo_button);
7324
this._lastCommand = null;
7328
* @method _checkUndo
7329
* @description Prunes the undo cache when it reaches the maxUndo config
7331
_checkUndo: function() {
7332
var len = this._undoCache.length,
7334
if (len >= this.get('maxUndo')) {
7335
//YAHOO.log('Undo cache too large (' + len + '), pruning..', 'info', 'SimpleEditor');
7336
for (var i = (len - this.get('maxUndo')); i < len; i++) {
7337
tmp.push(this._undoCache[i]);
7339
this._undoCache = tmp;
7345
* @description Puts the content of the Editor into the _undoCache.
7346
* //TODO Convert the hash to a series of TEXTAREAS to store state in.
7347
* @param {String} str The content of the Editor
7349
_putUndo: function(str) {
7350
this._undoCache.push(str);
7355
* @description Get's a level from the undo cache.
7356
* @param {Number} index The index of the undo level we want to get.
7359
_getUndo: function(index) {
7360
return this._undoCache[index];
7364
* @method _storeUndo
7365
* @description Method to call when you want to store an undo state. Currently called from nodeChange and _handleKeyUp
7367
_storeUndo: function() {
7368
if (this._lastCommand === 'undo' || this._lastCommand === 'redo') {
7371
if (!this._undoCache) {
7372
this._undoCache = [];
7375
var str = this.getEditorHTML();
7376
var last = this._undoCache[this._undoCache.length - 1];
7379
//YAHOO.log('Storing Undo', 'info', 'SimpleEditor');
7383
//YAHOO.log('Storing Undo', 'info', 'SimpleEditor');
7386
this._undoLevel = this._undoCache.length;
7387
this._undoNodeChange();
7390
* @property STR_BEFORE_EDITOR
7391
* @description The accessibility string for the element before the iFrame
7394
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>',
7396
* @property STR_CLOSE_WINDOW
7397
* @description The Title of the close button in the Editor Window
7400
STR_CLOSE_WINDOW: 'Close Window',
7402
* @property STR_CLOSE_WINDOW_NOTE
7403
* @description A note appearing in the Editor Window to tell the user that the Escape key will close the window
7406
STR_CLOSE_WINDOW_NOTE: 'To close this window use the Control + Shift + W key',
7408
* @property STR_IMAGE_PROP_TITLE
7409
* @description The title for the Image Property Editor Window
7412
STR_IMAGE_PROP_TITLE: 'Image Options',
7414
* @property STR_IMAGE_TITLE
7415
* @description The label string for Image Description
7418
STR_IMAGE_TITLE: 'Description',
7420
* @property STR_IMAGE_SIZE
7421
* @description The label string for Image Size
7424
STR_IMAGE_SIZE: 'Size',
7426
* @property STR_IMAGE_ORIG_SIZE
7427
* @description The label string for Original Image Size
7430
STR_IMAGE_ORIG_SIZE: 'Original Size',
7432
* @property STR_IMAGE_COPY
7433
* @description The label string for the image copy and paste message for Opera and Safari
7436
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>',
7438
* @property STR_IMAGE_PADDING
7439
* @description The label string for the image padding.
7442
STR_IMAGE_PADDING: 'Padding',
7444
* @property STR_IMAGE_BORDER
7445
* @description The label string for the image border.
7448
STR_IMAGE_BORDER: 'Border',
7450
* @property STR_IMAGE_BORDER_SIZE
7451
* @description The label string for the image border size.
7454
STR_IMAGE_BORDER_SIZE: 'Border Size',
7456
* @property STR_IMAGE_BORDER_TYPE
7457
* @description The label string for the image border type.
7460
STR_IMAGE_BORDER_TYPE: 'Border Type',
7462
* @property STR_IMAGE_TEXTFLOW
7463
* @description The label string for the image text flow.
7466
STR_IMAGE_TEXTFLOW: 'Text Flow',
7468
* @property STR_LOCAL_FILE_WARNING
7469
* @description The label string for the local file warning.
7472
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>',
7474
* @property STR_LINK_PROP_TITLE
7475
* @description The label string for the Link Property Editor Window.
7478
STR_LINK_PROP_TITLE: 'Link Options',
7480
* @property STR_LINK_PROP_REMOVE
7481
* @description The label string for the Remove link from text link inside the property editor.
7484
STR_LINK_PROP_REMOVE: 'Remove link from text',
7486
* @property STR_LINK_NEW_WINDOW
7487
* @description The string for the open in a new window label.
7490
STR_LINK_NEW_WINDOW: 'Open in a new window.',
7492
* @property STR_LINK_TITLE
7493
* @description The string for the link description.
7496
STR_LINK_TITLE: 'Description',
7498
* @property STR_NONE
7499
* @description The string for the word none.
7505
* @property CLASS_LOCAL_FILE
7506
* @description CSS class applied to an element when it's found to have a local url.
7509
CLASS_LOCAL_FILE: 'warning-localfile',
7512
* @property CLASS_HIDDEN
7513
* @description CSS class applied to the body when the hiddenelements button is pressed.
7516
CLASS_HIDDEN: 'yui-hidden',
7519
* @description The Editor class' initialization method
7521
init: function(p_oElement, p_oAttributes) {
7522
YAHOO.log('init', 'info', 'Editor');
7525
this._defaultToolbar = {
7527
titlebar: 'Text Editing Tools',
7529
buttonType: 'advanced',
7531
{ group: 'fontstyle', label: 'Font Name and Size',
7533
{ type: 'select', label: 'Arial', value: 'fontname', disabled: true,
7535
{ text: 'Arial', checked: true },
7536
{ text: 'Arial Black' },
7537
{ text: 'Comic Sans MS' },
7538
{ text: 'Courier New' },
7539
{ text: 'Lucida Console' },
7541
{ text: 'Times New Roman' },
7542
{ text: 'Trebuchet MS' },
7546
{ type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
7549
{ type: 'separator' },
7550
{ group: 'textstyle', label: 'Font Style',
7552
{ type: 'push', label: 'Bold CTRL + SHIFT + B', value: 'bold' },
7553
{ type: 'push', label: 'Italic CTRL + SHIFT + I', value: 'italic' },
7554
{ type: 'push', label: 'Underline CTRL + SHIFT + U', value: 'underline' },
7555
{ type: 'separator' },
7556
{ type: 'push', label: 'Subscript', value: 'subscript', disabled: true },
7557
{ type: 'push', label: 'Superscript', value: 'superscript', disabled: true }
7560
{ type: 'separator' },
7561
{ group: 'textstyle2', label: ' ',
7563
{ type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
7564
{ type: 'color', label: 'Background Color', value: 'backcolor', disabled: true },
7565
{ type: 'separator' },
7566
{ type: 'push', label: 'Remove Formatting', value: 'removeformat', disabled: true },
7567
{ type: 'push', label: 'Show/Hide Hidden Elements', value: 'hiddenelements' }
7570
{ type: 'separator' },
7571
{ group: 'undoredo', label: 'Undo/Redo',
7573
{ type: 'push', label: 'Undo', value: 'undo', disabled: true },
7574
{ type: 'push', label: 'Redo', value: 'redo', disabled: true }
7578
{ type: 'separator' },
7579
{ group: 'alignment', label: 'Alignment',
7581
{ type: 'push', label: 'Align Left CTRL + SHIFT + [', value: 'justifyleft' },
7582
{ type: 'push', label: 'Align Center CTRL + SHIFT + |', value: 'justifycenter' },
7583
{ type: 'push', label: 'Align Right CTRL + SHIFT + ]', value: 'justifyright' },
7584
{ type: 'push', label: 'Justify', value: 'justifyfull' }
7587
{ type: 'separator' },
7588
{ group: 'parastyle', label: 'Paragraph Style',
7590
{ type: 'select', label: 'Normal', value: 'heading', disabled: true,
7592
{ text: 'Normal', value: 'none', checked: true },
7593
{ text: 'Header 1', value: 'h1' },
7594
{ text: 'Header 2', value: 'h2' },
7595
{ text: 'Header 3', value: 'h3' },
7596
{ text: 'Header 4', value: 'h4' },
7597
{ text: 'Header 5', value: 'h5' },
7598
{ text: 'Header 6', value: 'h6' }
7603
{ type: 'separator' },
7605
{ group: 'indentlist2', label: 'Indenting and Lists',
7607
{ type: 'push', label: 'Indent', value: 'indent', disabled: true },
7608
{ type: 'push', label: 'Outdent', value: 'outdent', disabled: true },
7609
{ type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
7610
{ type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
7613
{ type: 'separator' },
7614
{ group: 'insertitem', label: 'Insert Item',
7616
{ type: 'push', label: 'HTML Link CTRL + SHIFT + L', value: 'createlink', disabled: true },
7617
{ type: 'push', label: 'Insert Image', value: 'insertimage' }
7623
this._defaultImageToolbarConfig = {
7624
buttonType: this._defaultToolbar.buttonType,
7626
{ group: 'textflow', label: this.STR_IMAGE_TEXTFLOW + ':',
7628
{ type: 'push', label: 'Left', value: 'left' },
7629
{ type: 'push', label: 'Inline', value: 'inline' },
7630
{ type: 'push', label: 'Block', value: 'block' },
7631
{ type: 'push', label: 'Right', value: 'right' }
7634
{ type: 'separator' },
7635
{ group: 'padding', label: this.STR_IMAGE_PADDING + ':',
7637
{ type: 'spin', label: '0', value: 'padding', range: [0, 50] }
7640
{ type: 'separator' },
7641
{ group: 'border', label: this.STR_IMAGE_BORDER + ':',
7643
{ type: 'select', label: this.STR_IMAGE_BORDER_SIZE, value: 'bordersize',
7645
{ text: 'none', value: '0', checked: true },
7646
{ text: '1px', value: '1' },
7647
{ text: '2px', value: '2' },
7648
{ text: '3px', value: '3' },
7649
{ text: '4px', value: '4' },
7650
{ text: '5px', value: '5' }
7653
{ type: 'select', label: this.STR_IMAGE_BORDER_TYPE, value: 'bordertype', disabled: true,
7655
{ text: 'Solid', value: 'solid', checked: true },
7656
{ text: 'Dashed', value: 'dashed' },
7657
{ text: 'Dotted', value: 'dotted' }
7660
{ type: 'color', label: 'Border Color', value: 'bordercolor', disabled: true }
7666
YAHOO.widget.Editor.superclass.init.call(this, p_oElement, p_oAttributes);
7668
_render: function() {
7669
YAHOO.widget.Editor.superclass._render.apply(this, arguments);
7671
//Render the panel in another thread and delay it a little..
7672
window.setTimeout(function() {
7673
self._renderPanel.call(self);
7677
* @method initAttributes
7678
* @description Initializes all of the configuration attributes used to create
7680
* @param {Object} attr Object literal specifying a set of
7681
* configuration attributes used to create the editor.
7683
initAttributes: function(attr) {
7684
YAHOO.widget.Editor.superclass.initAttributes.call(this, attr);
7687
* @attribute localFileWarning
7688
* @description Should we throw the warning if we detect a file that is local to their machine?
7692
this.setAttributeConfig('localFileWarning', {
7693
value: attr.locaFileWarning || true
7697
* @attribute hiddencss
7698
* @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>
7699
* @default <code><pre>
7700
.yui-hidden font, .yui-hidden strong, .yui-hidden b, .yui-hidden em, .yui-hidden i, .yui-hidden u,
7701
.yui-hidden div, .yui-hidden p, .yui-hidden span, .yui-hidden img, .yui-hidden ul, .yui-hidden ol,
7702
.yui-hidden li, .yui-hidden table {
7703
border: 1px dotted #ccc;
7705
.yui-hidden .yui-non {
7713
this.setAttributeConfig('hiddencss', {
7714
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; }',
7722
* @description A reference to the HTML elements used for the body of Editor Windows.
7727
* @method _defaultImageToolbar
7728
* @description A reference to the Toolbar Object inside Image Editor Window.
7730
_defaultImageToolbar: null,
7733
* @method _defaultImageToolbarConfig
7734
* @description Config to be used for the default Image Editor Window.
7736
_defaultImageToolbarConfig: null,
7740
* @description Fix href and imgs as well as remove invalid HTML.
7742
_fixNodes: function() {
7743
YAHOO.widget.Editor.superclass._fixNodes.call(this);
7746
var imgs = this._getDoc().getElementsByTagName('img');
7747
for (var im = 0; im < imgs.length; im++) {
7748
if (imgs[im].getAttribute('href', 2)) {
7749
url = imgs[im].getAttribute('src', 2);
7750
if (this._isLocalFile(url)) {
7751
Dom.addClass(imgs[im], this.CLASS_LOCAL_FILE);
7753
Dom.removeClass(imgs[im], this.CLASS_LOCAL_FILE);
7757
var fakeAs = this._getDoc().body.getElementsByTagName('a');
7758
for (var a = 0; a < fakeAs.length; a++) {
7759
if (fakeAs[a].getAttribute('href', 2)) {
7760
url = fakeAs[a].getAttribute('href', 2);
7761
if (this._isLocalFile(url)) {
7762
Dom.addClass(fakeAs[a], this.CLASS_LOCAL_FILE);
7764
Dom.removeClass(fakeAs[a], this.CLASS_LOCAL_FILE);
7771
* @property _disabled
7772
* @description The Toolbar items that should be disabled if there is no selection present in the editor.
7775
_disabled: [ 'createlink', 'forecolor', 'backcolor', 'fontname', 'fontsize', 'superscript', 'subscript', 'removeformat', 'heading', 'indent' ],
7778
* @property _alwaysDisabled
7779
* @description The Toolbar items that should ALWAYS be disabled event if there is a selection present in the editor.
7782
_alwaysDisabled: { 'outdent': true },
7785
* @property _alwaysEnabled
7786
* @description The Toolbar items that should ALWAYS be enabled event if there isn't a selection present in the editor.
7789
_alwaysEnabled: { hiddenelements: true },
7792
* @method _handleKeyDown
7793
* @param {Event} ev The event we are working on.
7794
* @description Override method that handles some new keydown events inside the iFrame document.
7796
_handleKeyDown: function(ev) {
7797
YAHOO.widget.Editor.superclass._handleKeyDown.call(this, ev);
7802
switch (ev.keyCode) {
7804
case this._keyMap.JUSTIFY_LEFT.key: //Left
7805
if (this._checkKey(this._keyMap.JUSTIFY_LEFT, ev)) {
7806
action = 'justifyleft';
7810
//case 220: //Center
7811
case this._keyMap.JUSTIFY_CENTER.key:
7812
if (this._checkKey(this._keyMap.JUSTIFY_CENTER, ev)) {
7813
action = 'justifycenter';
7818
case this._keyMap.JUSTIFY_RIGHT.key:
7819
if (this._checkKey(this._keyMap.JUSTIFY_RIGHT, ev)) {
7820
action = 'justifyright';
7825
if (doExec && action) {
7826
this.execCommand(action, null);
7827
Event.stopEvent(ev);
7833
* @method _renderCreateLinkWindow
7834
* @description Pre renders the CreateLink window so we get faster window opening.
7836
_renderCreateLinkWindow: function() {
7837
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>';
7838
str += '<label for="' + this.get('id') + '_createlink_target"><strong> </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>';
7839
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>';
7841
var body = document.createElement('div');
7842
body.innerHTML = str;
7844
var unlinkCont = document.createElement('div');
7845
unlinkCont.className = 'removeLink';
7846
var unlink = document.createElement('a');
7848
unlink.innerHTML = this.STR_LINK_PROP_REMOVE;
7849
unlink.title = this.STR_LINK_PROP_REMOVE;
7850
Event.on(unlink, 'click', function(ev) {
7851
Event.stopEvent(ev);
7852
this.unsubscribeAll('afterExecCommand');
7853
this.execCommand('unlink');
7856
unlinkCont.appendChild(unlink);
7857
body.appendChild(unlinkCont);
7859
this._windows.createlink = {};
7860
this._windows.createlink.body = body;
7861
//body.style.display = 'none';
7862
Event.on(body, 'keyup', function(e) {
7863
Event.stopPropagation(e);
7865
this.get('panel').editor_form.appendChild(body);
7866
this.fireEvent('windowCreateLinkRender', { type: 'windowCreateLinkRender', panel: this.get('panel'), body: body });
7869
_handleCreateLinkClick: function() {
7870
var el = this._getSelectedElement();
7871
if (this._isElement(el, 'img')) {
7872
this.STOP_EXEC_COMMAND = true;
7873
this.currentElement[0] = el;
7874
this.toolbar.fireEvent('insertimageClick', { type: 'insertimageClick', target: this.toolbar });
7875
this.fireEvent('afterExecCommand', { type: 'afterExecCommand', target: this });
7878
if (this.get('limitCommands')) {
7879
if (!this.toolbar.getButtonByValue('createlink')) {
7880
YAHOO.log('Toolbar Button for (createlink) was not found, skipping exec.', 'info', 'Editor');
7885
this.on('afterExecCommand', function() {
7886
var win = new YAHOO.widget.EditorWindow('createlink', {
7890
var el = this.currentElement[0],
7897
if (el.getAttribute('href', 2) !== null) {
7898
url = el.getAttribute('href', 2);
7899
if (this._isLocalFile(url)) {
7900
//Local File throw Warning
7901
YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7902
win.setFooter(this.STR_LOCAL_FILE_WARNING);
7908
if (el.getAttribute('title') !== null) {
7909
title = el.getAttribute('title');
7911
if (el.getAttribute('target') !== null) {
7912
target = el.getAttribute('target');
7916
if (this._windows.createlink && this._windows.createlink.body) {
7917
body = this._windows.createlink.body;
7919
body = this._renderCreateLinkWindow();
7922
win.setHeader(this.STR_LINK_PROP_TITLE);
7925
Event.purgeElement(this.get('id') + '_createlink_url');
7927
Dom.get(this.get('id') + '_createlink_url').value = url;
7928
Dom.get(this.get('id') + '_createlink_title').value = title;
7929
Dom.get(this.get('id') + '_createlink_target').checked = ((target) ? true : false);
7932
Event.onAvailable(this.get('id') + '_createlink_url', function() {
7933
var id = this.get('id');
7934
window.setTimeout(function() {
7936
YAHOO.util.Dom.get(id + '_createlink_url').focus();
7940
if (this._isLocalFile(url)) {
7941
//Local File throw Warning
7942
Dom.addClass(this.get('id') + '_createlink_url', 'warning');
7943
YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7944
this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
7946
Dom.removeClass(this.get('id') + '_createlink_url', 'warning');
7947
this.get('panel').setFooter(' ');
7949
Event.on(this.get('id') + '_createlink_url', 'blur', function() {
7950
var url = Dom.get(this.get('id') + '_createlink_url');
7951
if (this._isLocalFile(url.value)) {
7952
//Local File throw Warning
7953
Dom.addClass(url, 'warning');
7954
YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
7955
this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
7957
Dom.removeClass(url, 'warning');
7958
this.get('panel').setFooter(' ');
7963
this.openWindow(win);
7969
* @method _handleCreateLinkWindowClose
7970
* @description Handles the closing of the Link Properties Window.
7972
_handleCreateLinkWindowClose: function() {
7974
var url = Dom.get(this.get('id') + '_createlink_url'),
7975
target = Dom.get(this.get('id') + '_createlink_target'),
7976
title = Dom.get(this.get('id') + '_createlink_title'),
7977
el = arguments[0].win.el,
7980
if (url && url.value) {
7981
var urlValue = url.value;
7982
if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
7983
if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
7984
//Found an @ sign, prefix with mailto:
7985
urlValue = 'mailto:' + urlValue;
7987
// :// not found adding
7988
if (urlValue.substring(0, 1) != '#') {
7989
urlValue = 'http:/'+'/' + urlValue;
7994
el.setAttribute('href', urlValue);
7995
if (target.checked) {
7996
el.setAttribute('target', target.value);
7998
el.setAttribute('target', '');
8000
el.setAttribute('title', ((title.value) ? title.value : ''));
8003
var _span = this._getDoc().createElement('span');
8004
_span.innerHTML = el.innerHTML;
8005
Dom.addClass(_span, 'yui-non');
8006
el.parentNode.replaceChild(_span, el);
8008
Dom.removeClass(url, 'warning');
8009
Dom.get(this.get('id') + '_createlink_url').value = '';
8010
Dom.get(this.get('id') + '_createlink_title').value = '';
8011
Dom.get(this.get('id') + '_createlink_target').checked = false;
8013
this.currentElement = [];
8018
* @method _renderInsertImageWindow
8019
* @description Pre renders the InsertImage window so we get faster window opening.
8021
_renderInsertImageWindow: function() {
8022
var el = this.currentElement[0];
8023
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>';
8024
var body = document.createElement('div');
8025
body.innerHTML = str;
8027
var tbarCont = document.createElement('div');
8028
tbarCont.id = this.get('id') + '_img_toolbar';
8029
body.appendChild(tbarCont);
8031
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>';
8032
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>';
8033
str2 += '<label for="' + this.get('id') + '_insertimage_target"><strong> </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>';
8034
var div = document.createElement('div');
8035
div.innerHTML = str2;
8036
body.appendChild(div);
8039
Lang.augmentObject(o, this._defaultImageToolbarConfig); //Break the config reference
8041
var tbar = new YAHOO.widget.Toolbar(tbarCont, o);
8042
tbar.editor_el = el;
8043
this._defaultImageToolbar = tbar;
8045
var cont = tbar.get('cont');
8046
var hw = document.createElement('div');
8047
hw.className = 'yui-toolbar-group yui-toolbar-group-height-width height-width';
8048
hw.innerHTML = '<h3>' + this.STR_IMAGE_SIZE + ':</h3>';
8051
if ((height != oheight) || (width != owidth)) {
8052
orgSize = '<span class="info">' + this.STR_IMAGE_ORIG_SIZE + '<br>'+ owidth +' x ' + oheight + '</span>';
8055
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>';
8056
cont.insertBefore(hw, cont.firstChild);
8058
Event.onAvailable(this.get('id') + '_insertimage_width', function() {
8059
Event.on(this.get('id') + '_insertimage_width', 'blur', function() {
8060
var value = parseInt(Dom.get(this.get('id') + '_insertimage_width').value, 10);
8062
this._defaultImageToolbar.editor_el.style.width = value + 'px';
8063
//Removed moveWindow call so the window doesn't jump
8064
//this.moveWindow();
8068
Event.onAvailable(this.get('id') + '_insertimage_height', function() {
8069
Event.on(this.get('id') + '_insertimage_height', 'blur', function() {
8070
var value = parseInt(Dom.get(this.get('id') + '_insertimage_height').value, 10);
8072
this._defaultImageToolbar.editor_el.style.height = value + 'px';
8073
//Removed moveWindow call so the window doesn't jump
8074
//this.moveWindow();
8080
tbar.on('colorPickerClicked', function(o) {
8081
var size = '1', type = 'solid', color = 'black', el = this._defaultImageToolbar.editor_el;
8083
if (el.style.borderLeftWidth) {
8084
size = parseInt(el.style.borderLeftWidth, 10);
8086
if (el.style.borderLeftStyle) {
8087
type = el.style.borderLeftStyle;
8089
if (el.style.borderLeftColor) {
8090
color = el.style.borderLeftColor;
8092
var borderString = size + 'px ' + type + ' #' + o.color;
8093
el.style.border = borderString;
8096
tbar.on('buttonClick', function(o) {
8097
var value = o.button.value,
8098
el = this._defaultImageToolbar.editor_el,
8100
if (o.button.menucmd) {
8101
value = o.button.menucmd;
8103
var size = '1', type = 'solid', color = 'black';
8105
/* All border calcs are done on the left border
8106
since our default interface only supports
8107
one border size/type and color */
8108
if (el.style.borderLeftWidth) {
8109
size = parseInt(el.style.borderLeftWidth, 10);
8111
if (el.style.borderLeftStyle) {
8112
type = el.style.borderLeftStyle;
8114
if (el.style.borderLeftColor) {
8115
color = el.style.borderLeftColor;
8119
if (this.browser.webkit && this._lastImage) {
8120
Dom.removeClass(this._lastImage, 'selected');
8121
this._lastImage = null;
8124
borderString = parseInt(o.button.value, 10) + 'px ' + type + ' ' + color;
8125
el.style.border = borderString;
8126
if (parseInt(o.button.value, 10) > 0) {
8127
tbar.enableButton('bordertype');
8128
tbar.enableButton('bordercolor');
8130
tbar.disableButton('bordertype');
8131
tbar.disableButton('bordercolor');
8135
if (this.browser.webkit && this._lastImage) {
8136
Dom.removeClass(this._lastImage, 'selected');
8137
this._lastImage = null;
8139
borderString = size + 'px ' + o.button.value + ' ' + color;
8140
el.style.border = borderString;
8144
tbar.deselectAllButtons();
8145
el.style.display = '';
8146
el.align = o.button.value;
8149
tbar.deselectAllButtons();
8150
el.style.display = '';
8154
tbar.deselectAllButtons();
8155
el.style.display = 'block';
8156
el.align = 'center';
8159
var _button = tbar.getButtonById(o.button.id);
8160
el.style.margin = _button.get('label') + 'px';
8163
tbar.selectButton(o.button.value);
8164
if (value !== 'padding') {
8171
if (this.get('localFileWarning')) {
8172
Event.on(this.get('id') + '_insertimage_link', 'blur', function() {
8173
var url = Dom.get(this.get('id') + '_insertimage_link');
8174
if (this._isLocalFile(url.value)) {
8175
//Local File throw Warning
8176
Dom.addClass(url, 'warning');
8177
YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
8178
this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
8180
Dom.removeClass(url, 'warning');
8181
this.get('panel').setFooter(' ');
8183
if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {
8184
this.get('panel').setFooter(this.STR_IMAGE_COPY);
8190
Event.on(this.get('id') + '_insertimage_url', 'blur', function() {
8191
var url = Dom.get(this.get('id') + '_insertimage_url');
8192
if (url.value && el) {
8193
if (url.value == el.getAttribute('src', 2)) {
8194
YAHOO.log('Images are the same, bail on blur handler', 'info', 'Editor');
8198
YAHOO.log('Images are different, process blur handler', 'info', 'Editor');
8199
if (this._isLocalFile(url.value)) {
8200
//Local File throw Warning
8201
Dom.addClass(url, 'warning');
8202
YAHOO.log('Local file reference found, show local warning', 'warn', 'Editor');
8203
this.get('panel').setFooter(this.STR_LOCAL_FILE_WARNING);
8204
} else if (this.currentElement[0]) {
8205
Dom.removeClass(url, 'warning');
8206
this.get('panel').setFooter(' ');
8208
if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {
8209
this.get('panel').setFooter(this.STR_IMAGE_COPY);
8212
if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
8213
this.currentElement[0].setAttribute('src', url.value);
8217
img.onerror = function() {
8218
url.value = self.STR_IMAGE_HERE;
8219
img.setAttribute('src', self.get('blankimage'));
8220
self.currentElement[0].setAttribute('src', self.get('blankimage'));
8221
YAHOO.util.Dom.get(self.get('id') + '_insertimage_height').value = img.height;
8222
YAHOO.util.Dom.get(self.get('id') + '_insertimage_width').value = img.width;
8224
var id = this.get('id');
8225
window.setTimeout(function() {
8226
YAHOO.util.Dom.get(id + '_insertimage_height').value = img.height;
8227
YAHOO.util.Dom.get(id + '_insertimage_width').value = img.width;
8228
if (self.currentElement && self.currentElement[0]) {
8229
if (!self.currentElement[0]._height) {
8230
self.currentElement[0]._height = img.height;
8232
if (!self.currentElement[0]._width) {
8233
self.currentElement[0]._width = img.width;
8236
//Removed moveWindow call so the window doesn't jump
8237
//self.moveWindow();
8238
}, 800); //Bumped the timeout up to account for larger images..
8240
if (url.value != this.STR_IMAGE_HERE) {
8241
img.src = url.value;
8249
this._windows.insertimage = {};
8250
this._windows.insertimage.body = body;
8251
//body.style.display = 'none';
8252
this.get('panel').editor_form.appendChild(body);
8253
this.fireEvent('windowInsertImageRender', { type: 'windowInsertImageRender', panel: this.get('panel'), body: body, toolbar: tbar });
8258
* @method _handleInsertImageClick
8259
* @description Opens the Image Properties Window when the insert Image button is clicked or an Image is Double Clicked.
8261
_handleInsertImageClick: function() {
8262
if (this.get('limitCommands')) {
8263
if (!this.toolbar.getButtonByValue('insertimage')) {
8264
YAHOO.log('Toolbar Button for (insertimage) was not found, skipping exec.', 'info', 'Editor');
8268
this.on('afterExecCommand', function() {
8269
YAHOO.log('afterExecCommand :: _handleInsertImageClick', 'info', 'Editor');
8270
var el = this.currentElement[0],
8284
win = new YAHOO.widget.EditorWindow('insertimage', {
8289
el = this._getSelectedElement();
8293
if (el.getAttribute('src')) {
8294
src = el.getAttribute('src', 2);
8295
if (src.indexOf(this.get('blankimage')) != -1) {
8296
src = this.STR_IMAGE_HERE;
8300
if (el.getAttribute('alt', 2)) {
8301
title = el.getAttribute('alt', 2);
8303
if (el.getAttribute('title', 2)) {
8304
title = el.getAttribute('title', 2);
8307
if (el.parentNode && this._isElement(el.parentNode, 'a')) {
8308
link = el.parentNode.getAttribute('href', 2);
8309
if (el.parentNode.getAttribute('target') !== null) {
8310
target = el.parentNode.getAttribute('target');
8313
height = parseInt(el.height, 10);
8314
width = parseInt(el.width, 10);
8315
if (el.style.height) {
8316
height = parseInt(el.style.height, 10);
8318
if (el.style.width) {
8319
width = parseInt(el.style.width, 10);
8321
if (el.style.margin) {
8322
padding = parseInt(el.style.margin, 10);
8325
el._height = height;
8330
oheight = el._height;
8333
if (this._windows.insertimage && this._windows.insertimage.body) {
8334
body = this._windows.insertimage.body;
8335
this._defaultImageToolbar.resetAllButtons();
8337
body = this._renderInsertImageWindow();
8340
tbar = this._defaultImageToolbar;
8341
tbar.editor_el = el;
8347
if (el.style.borderLeftWidth) {
8348
bsize = parseInt(el.style.borderLeftWidth, 10);
8350
if (el.style.borderLeftStyle) {
8351
btype = el.style.borderLeftStyle;
8353
var bs_button = tbar.getButtonByValue('bordersize'),
8354
bSizeStr = ((parseInt(bsize, 10) > 0) ? '' : this.STR_NONE);
8355
bs_button.set('label', '<span class="yui-toolbar-bordersize-' + bsize + '">' + bSizeStr + '</span>');
8356
this._updateMenuChecked('bordersize', bsize, tbar);
8358
var bt_button = tbar.getButtonByValue('bordertype');
8359
bt_button.set('label', '<span class="yui-toolbar-bordertype-' + btype + '">asdfa</span>');
8360
this._updateMenuChecked('bordertype', btype, tbar);
8361
if (parseInt(bsize, 10) > 0) {
8362
tbar.enableButton(bt_button);
8363
tbar.enableButton(bs_button);
8364
tbar.enableButton('bordercolor');
8367
if ((el.align == 'right') || (el.align == 'left')) {
8368
tbar.selectButton(el.align);
8369
} else if (el.style.display == 'block') {
8370
tbar.selectButton('block');
8372
tbar.selectButton('inline');
8374
if (parseInt(el.style.marginLeft, 10) > 0) {
8375
tbar.getButtonByValue('padding').set('label', ''+parseInt(el.style.marginLeft, 10));
8377
if (el.style.borderSize) {
8378
tbar.selectButton('bordersize');
8379
tbar.selectButton(parseInt(el.style.borderSize, 10));
8381
tbar.getButtonByValue('padding').set('label', ''+padding);
8385
win.setHeader(this.STR_IMAGE_PROP_TITLE);
8388
if ((this.browser.webkit && !this.browser.webkit3 || this.browser.air) || this.browser.opera) {
8389
win.setFooter(this.STR_IMAGE_COPY);
8391
this.openWindow(win);
8392
Dom.get(this.get('id') + '_insertimage_url').value = src;
8393
Dom.get(this.get('id') + '_insertimage_title').value = title;
8394
Dom.get(this.get('id') + '_insertimage_link').value = link;
8395
Dom.get(this.get('id') + '_insertimage_target').checked = ((target) ? true : false);
8396
Dom.get(this.get('id') + '_insertimage_width').value = width;
8397
Dom.get(this.get('id') + '_insertimage_height').value = height;
8401
if ((height != oheight) || (width != owidth)) {
8402
var s = document.createElement('span');
8403
s.className = 'info';
8404
s.innerHTML = this.STR_IMAGE_ORIG_SIZE + ': ('+ owidth +' x ' + oheight + ')';
8405
if (Dom.get(this.get('id') + '_insertimage_height').nextSibling) {
8406
var old = Dom.get(this.get('id') + '_insertimage_height').nextSibling;
8407
old.parentNode.removeChild(old);
8409
Dom.get(this.get('id') + '_insertimage_height').parentNode.appendChild(s);
8412
this.toolbar.selectButton('insertimage');
8413
var id = this.get('id');
8414
window.setTimeout(function() {
8416
YAHOO.util.Dom.get(id + '_insertimage_url').focus();
8418
YAHOO.util.Dom.get(id + '_insertimage_url').select();
8427
* @method _handleInsertImageWindowClose
8428
* @description Handles the closing of the Image Properties Window.
8430
_handleInsertImageWindowClose: function() {
8431
var url = Dom.get(this.get('id') + '_insertimage_url'),
8432
title = Dom.get(this.get('id') + '_insertimage_title'),
8433
link = Dom.get(this.get('id') + '_insertimage_link'),
8434
target = Dom.get(this.get('id') + '_insertimage_target'),
8435
el = arguments[0].win.el;
8437
if (url && url.value && (url.value != this.STR_IMAGE_HERE)) {
8438
el.setAttribute('src', url.value);
8439
el.setAttribute('title', title.value);
8440
el.setAttribute('alt', title.value);
8441
var par = el.parentNode;
8443
var urlValue = link.value;
8444
if ((urlValue.indexOf(':/'+'/') == -1) && (urlValue.substring(0,1) != '/') && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
8445
if ((urlValue.indexOf('@') != -1) && (urlValue.substring(0, 6).toLowerCase() != 'mailto')) {
8446
//Found an @ sign, prefix with mailto:
8447
urlValue = 'mailto:' + urlValue;
8449
// :// not found adding
8450
urlValue = 'http:/'+'/' + urlValue;
8453
if (par && this._isElement(par, 'a')) {
8454
par.setAttribute('href', urlValue);
8455
if (target.checked) {
8456
par.setAttribute('target', target.value);
8458
par.setAttribute('target', '');
8461
var _a = this._getDoc().createElement('a');
8462
_a.setAttribute('href', urlValue);
8463
if (target.checked) {
8464
_a.setAttribute('target', target.value);
8466
_a.setAttribute('target', '');
8468
el.parentNode.replaceChild(_a, el);
8472
if (par && this._isElement(par, 'a')) {
8473
par.parentNode.replaceChild(el, par);
8477
//No url/src given, remove the node from the document
8478
el.parentNode.removeChild(el);
8480
Dom.get(this.get('id') + '_insertimage_url').value = '';
8481
Dom.get(this.get('id') + '_insertimage_title').value = '';
8482
Dom.get(this.get('id') + '_insertimage_link').value = '';
8483
Dom.get(this.get('id') + '_insertimage_target').checked = false;
8484
Dom.get(this.get('id') + '_insertimage_width').value = 0;
8485
Dom.get(this.get('id') + '_insertimage_height').value = 0;
8486
this._defaultImageToolbar.resetAllButtons();
8487
this.currentElement = [];
8491
* @property EDITOR_PANEL_ID
8492
* @description HTML id to give the properties window in the DOM.
8495
EDITOR_PANEL_ID: '-panel',
8498
* @method _renderPanel
8499
* @description Renders the panel used for Editor Windows to the document so we can start using it..
8500
* @return {<a href="YAHOO.widget.Overlay.html">YAHOO.widget.Overlay</a>}
8502
_renderPanel: function() {
8503
var panelEl = document.createElement('div');
8504
Dom.addClass(panelEl, 'yui-editor-panel');
8505
panelEl.id = this.get('id') + this.EDITOR_PANEL_ID;
8506
panelEl.style.position = 'absolute';
8507
panelEl.style.top = '-9999px';
8508
panelEl.style.left = '-9999px';
8509
document.body.appendChild(panelEl);
8510
this.get('element_cont').insertBefore(panelEl, this.get('element_cont').get('firstChild'));
8514
var panel = new YAHOO.widget.Overlay(this.get('id') + this.EDITOR_PANEL_ID, {
8522
this.set('panel', panel);
8524
panel.setBody('---');
8525
panel.setHeader(' ');
8526
panel.setFooter(' ');
8529
var body = document.createElement('div');
8530
body.className = this.CLASS_PREFIX + '-body-cont';
8531
for (var b in this.browser) {
8532
if (this.browser[b]) {
8533
Dom.addClass(body, b);
8537
Dom.addClass(body, ((YAHOO.widget.Button && (this._defaultToolbar.buttonType == 'advanced')) ? 'good-button' : 'no-button'));
8539
var _note = document.createElement('h3');
8540
_note.className = 'yui-editor-skipheader';
8541
_note.innerHTML = this.STR_CLOSE_WINDOW_NOTE;
8542
body.appendChild(_note);
8543
var form = document.createElement('fieldset');
8544
panel.editor_form = form;
8546
body.appendChild(form);
8547
var _close = document.createElement('span');
8548
_close.innerHTML = 'X';
8549
_close.title = this.STR_CLOSE_WINDOW;
8550
_close.className = 'close';
8552
Event.on(_close, 'click', this.closeWindow, this, true);
8554
var _knob = document.createElement('span');
8555
_knob.innerHTML = '^';
8556
_knob.className = 'knob';
8557
panel.editor_knob = _knob;
8559
var _header = document.createElement('h3');
8560
panel.editor_header = _header;
8561
_header.innerHTML = '<span></span>';
8563
panel.setHeader(' '); //Clear the current header
8564
panel.appendToHeader(_header);
8565
_header.appendChild(_close);
8566
_header.appendChild(_knob);
8567
panel.setBody(' '); //Clear the current body
8568
panel.setFooter(' '); //Clear the current footer
8569
panel.appendToBody(body); //Append the new DOM node to it
8571
Event.on(panel.element, 'click', function(ev) {
8572
Event.stopPropagation(ev);
8575
var fireShowEvent = function() {
8577
YAHOO.util.Dom.setStyle(this.element, 'display', 'block');
8578
this._handleWindowInputs(false);
8580
panel.showEvent.subscribe(fireShowEvent, this, true);
8581
panel.hideEvent.subscribe(function() {
8582
this._handleWindowInputs(true);
8584
panel.renderEvent.subscribe(function() {
8585
this._renderInsertImageWindow();
8586
this._renderCreateLinkWindow();
8587
this.fireEvent('windowRender', { type: 'windowRender', panel: panel });
8588
this._handleWindowInputs(true);
8591
if (this.DOMReady) {
8592
this.get('panel').render();
8594
Event.onDOMReady(function() {
8595
this.get('panel').render();
8598
return this.get('panel');
8601
* @method _handleWindowInputs
8602
* @param {Boolean} disable The state to set all inputs in all Editor windows to. Defaults to: false.
8603
* @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.
8605
_handleWindowInputs: function(disable) {
8606
if (!Lang.isBoolean(disable)) {
8609
var inputs = this.get('panel').element.getElementsByTagName('input');
8610
for (var i = 0; i < inputs.length; i++) {
8612
inputs[i].disabled = disable;
8617
* @method openWindow
8618
* @param {<a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a>} win A <a href="YAHOO.widget.EditorWindow.html">YAHOO.widget.EditorWindow</a> instance
8619
* @description Opens a new "window/panel"
8621
openWindow: function(win) {
8623
YAHOO.log('openWindow: ' + win.name, 'info', 'Editor');
8625
window.setTimeout(function() {
8626
self.toolbar.set('disabled', true); //Disable the toolbar when an editor window is open..
8628
Event.on(document, 'keydown', this._closeWindow, this, true);
8630
if (this.currentWindow) {
8634
var xy = Dom.getXY(this.currentElement[0]),
8635
elXY = Dom.getXY(this.get('iframe').get('element')),
8636
panel = this.get('panel'),
8637
newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
8638
wWidth = (parseInt(win.attrs.width, 10) / 2),
8642
this.fireEvent('beforeOpenWindow', { type: 'beforeOpenWindow', win: win, panel: panel });
8644
var form = panel.editor_form;
8646
var wins = this._windows;
8647
for (var b in wins) {
8648
if (Lang.hasOwnProperty(wins, b)) {
8649
if (wins[b] && wins[b].body) {
8650
if (b == win.name) {
8651
Dom.setStyle(wins[b].body, 'display', 'block');
8653
Dom.setStyle(wins[b].body, 'display', 'none');
8659
if (this._windows[win.name].body) {
8660
Dom.setStyle(this._windows[win.name].body, 'display', 'block');
8661
form.appendChild(this._windows[win.name].body);
8663
if (Lang.isObject(win.body)) { //Assume it's a reference
8664
form.appendChild(win.body);
8665
} else { //Assume it's a string
8666
var _tmp = document.createElement('div');
8667
_tmp.innerHTML = win.body;
8668
form.appendChild(_tmp);
8671
panel.editor_header.firstChild.innerHTML = win.header;
8672
if (win.footer !== null) {
8673
panel.setFooter(win.footer);
8674
Dom.addClass(panel.footer, 'open');
8676
Dom.removeClass(panel.footer, 'open');
8678
panel.cfg.setProperty('width', win.attrs.width);
8680
this.currentWindow = win;
8681
this.moveWindow(true);
8683
this.fireEvent('afterOpenWindow', { type: 'afterOpenWindow', win: win, panel: panel });
8686
* @method moveWindow
8687
* @param {Boolean} force Boolean to tell it to move but not use any animation (Usually done the first time the window is loaded.)
8688
* @description Realign the window with the currentElement and reposition the knob above the panel.
8690
moveWindow: function(force) {
8691
if (!this.currentWindow) {
8694
var win = this.currentWindow,
8695
xy = Dom.getXY(this.currentElement[0]),
8696
elXY = Dom.getXY(this.get('iframe').get('element')),
8697
panel = this.get('panel'),
8698
//newXY = [(xy[0] + elXY[0] - 20), (xy[1] + elXY[1] + 10)],
8699
newXY = [(xy[0] + elXY[0]), (xy[1] + elXY[1])],
8700
wWidth = (parseInt(win.attrs.width, 10) / 2),
8702
orgXY = panel.cfg.getProperty('xy') || [0,0],
8703
_knob = panel.editor_knob,
8709
newXY[0] = ((newXY[0] - wWidth) + 20);
8710
//Account for the Scroll bars in a scrolled editor window.
8711
newXY[0] = newXY[0] - Dom.getDocumentScrollLeft(this._getDoc());
8712
newXY[1] = newXY[1] - Dom.getDocumentScrollTop(this._getDoc());
8714
if (this._isElement(this.currentElement[0], 'img')) {
8715
if (this.currentElement[0].src.indexOf(this.get('blankimage')) != -1) {
8716
newXY[0] = (newXY[0] + (75 / 2)); //Placeholder size
8717
newXY[1] = (newXY[1] + 75); //Placeholder sizea
8719
var w = parseInt(this.currentElement[0].width, 10);
8720
var h = parseInt(this.currentElement[0].height, 10);
8721
newXY[0] = (newXY[0] + (w / 2));
8722
newXY[1] = (newXY[1] + h);
8724
newXY[1] = newXY[1] + 15;
8726
var fs = Dom.getStyle(this.currentElement[0], 'fontSize');
8727
if (fs && fs.indexOf && fs.indexOf('px') != -1) {
8728
newXY[1] = newXY[1] + parseInt(Dom.getStyle(this.currentElement[0], 'fontSize'), 10) + 5;
8730
newXY[1] = newXY[1] + 20;
8733
if (newXY[0] < elXY[0]) {
8734
newXY[0] = elXY[0] + 5;
8738
if ((newXY[0] + (wWidth * 2)) > (elXY[0] + parseInt(this.get('iframe').get('element').clientWidth, 10))) {
8739
newXY[0] = ((elXY[0] + parseInt(this.get('iframe').get('element').clientWidth, 10)) - (wWidth * 2) - 5);
8744
xDiff = (newXY[0] - orgXY[0]);
8745
yDiff = (newXY[1] - orgXY[1]);
8749
var iTop = elXY[1] + parseInt(this.get('height'), 10);
8750
var iLeft = elXY[0] + parseInt(this.get('width'), 10);
8751
if (newXY[1] > iTop) {
8754
if (newXY[0] > iLeft) {
8755
newXY[0] = (iLeft / 2);
8758
//Convert negative numbers to positive so we can get the difference in distance
8759
xDiff = ((xDiff < 0) ? (xDiff * -1) : xDiff);
8760
yDiff = ((yDiff < 0) ? (yDiff * -1) : yDiff);
8762
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)
8766
if (this.currentElement[0].width) {
8767
elW = (parseInt(this.currentElement[0].width, 10) / 2);
8770
var leftOffset = xy[0] + elXY[0] + elW;
8771
_knobLeft = leftOffset - newXY[0];
8772
//Check to see if the knob will go off either side & reposition it
8773
if (_knobLeft > (parseInt(win.attrs.width, 10) - 1)) {
8774
_knobLeft = ((parseInt(win.attrs.width, 10) - 30) - 1);
8775
} else if (_knobLeft < 40) {
8778
if (isNaN(_knobLeft)) {
8783
_knob.style.left = _knobLeft + 'px';
8785
//Removed Animation from a forced move..
8786
panel.cfg.setProperty('xy', newXY);
8788
if (this.get('animate')) {
8789
anim = new YAHOO.util.Anim(panel.element, {}, 0.5, YAHOO.util.Easing.easeOut);
8798
anim.onComplete.subscribe(function() {
8799
panel.cfg.setProperty('xy', newXY);
8801
//We have to animate the iframe shim at the same time as the panel or we get scrollbar bleed ..
8802
var iframeAnim = new YAHOO.util.Anim(panel.iframe, anim.attributes, 0.5, YAHOO.util.Easing.easeOut);
8804
var _knobAnim = new YAHOO.util.Anim(_knob, {
8808
}, 0.6, YAHOO.util.Easing.easeOut);
8810
iframeAnim.animate();
8811
_knobAnim.animate();
8813
_knob.style.left = _knobLeft + 'px';
8814
panel.cfg.setProperty('xy', newXY);
8821
* @method _closeWindow
8822
* @description Close the currently open EditorWindow with the Escape key.
8823
* @param {Event} ev The keypress Event that we are trapping
8825
_closeWindow: function(ev) {
8826
//if ((ev.charCode == 87) && ev.shiftKey && ev.ctrlKey) {
8827
if (this._checkKey(this._keyMap.CLOSE_WINDOW, ev)) {
8828
if (this.currentWindow) {
8834
* @method closeWindow
8835
* @description Close the currently open EditorWindow.
8837
closeWindow: function(keepOpen) {
8838
YAHOO.log('closeWindow: ' + this.currentWindow.name, 'info', 'Editor');
8839
this.fireEvent('window' + this.currentWindow.name + 'Close', { type: 'window' + this.currentWindow.name + 'Close', win: this.currentWindow, el: this.currentElement[0] });
8840
this.fireEvent('closeWindow', { type: 'closeWindow', win: this.currentWindow });
8841
this.currentWindow = null;
8842
this.get('panel').hide();
8843
this.get('panel').cfg.setProperty('xy', [-900,-900]);
8844
this.get('panel').syncIframe(); //Needed to move the iframe with the hidden panel
8845
this.unsubscribeAll('afterExecCommand');
8846
this.toolbar.set('disabled', false); //enable the toolbar now that the window is closed
8847
this.toolbar.resetAllButtons();
8849
Event.removeListener(document, 'keydown', this._closeWindow);
8852
/* {{{ Command Overrides - These commands are only over written when we are using the advanced version */
8856
* @description Pulls an item from the Undo stack and updates the Editor
8857
* @param value Value passed from the execCommand method
8859
cmd_undo: function(value) {
8860
if (this._hasUndoLevel()) {
8861
if (!this._undoLevel) {
8862
this._undoLevel = this._undoCache.length;
8864
this._undoLevel = (this._undoLevel - 1);
8865
if (this._undoCache[this._undoLevel]) {
8866
var html = this._getUndo(this._undoLevel);
8867
this.setEditorHTML(html);
8869
this._undoLevel = null;
8870
this.toolbar.disableButton('undo');
8878
* @description Pulls an item from the Undo stack and updates the Editor
8879
* @param value Value passed from the execCommand method
8881
cmd_redo: function(value) {
8882
this._undoLevel = this._undoLevel + 1;
8883
if (this._undoLevel >= this._undoCache.length) {
8884
this._undoLevel = this._undoCache.length;
8886
YAHOO.log(this._undoLevel + ' :: ' + this._undoCache.length, 'warn', 'SimpleEditor');
8887
if (this._undoCache[this._undoLevel]) {
8888
var html = this._getUndo(this._undoLevel);
8889
this.setEditorHTML(html);
8891
this.toolbar.disableButton('redo');
8897
* @method cmd_heading
8898
* @param value Value passed from the execCommand method
8899
* @description This is an execCommand override method. It is called from execCommand when the execCommand('heading') is used.
8901
cmd_heading: function(value) {
8905
_sel = this._getSelection(),
8906
_selEl = this._getSelectedElement();
8912
if (this.browser.ie) {
8913
action = 'formatblock';
8915
if (value == this.STR_NONE) {
8916
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'))) {
8917
if (_sel.parentNode.tagName.toLowerCase().substring(0,1) == 'h') {
8918
_sel = _sel.parentNode;
8920
if (this._isElement(_sel, 'html')) {
8923
el = this._swapEl(_selEl, 'span', function(el) {
8924
el.className = 'yui-non';
8926
this._selectNode(el);
8927
this.currentElement[0] = el;
8931
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')) {
8932
el = this._swapEl(_selEl, value);
8933
this._selectNode(el);
8934
this.currentElement[0] = el;
8936
this._createCurrentElement(value);
8937
this._selectNode(this.currentElement[0]);
8941
return [exec, action];
8944
* @method cmd_hiddenelements
8945
* @param value Value passed from the execCommand method
8946
* @description This is an execCommand override method. It is called from execCommand when the execCommand('hiddenelements') is used.
8948
cmd_hiddenelements: function(value) {
8949
if (this._showingHiddenElements) {
8950
//Don't auto highlight the hidden button
8951
this._lastButton = null;
8952
YAHOO.log('Enabling hidden CSS File', 'info', 'SimpleEditor');
8953
this._showingHiddenElements = false;
8954
this.toolbar.deselectButton('hiddenelements');
8955
Dom.removeClass(this._getDoc().body, this.CLASS_HIDDEN);
8957
YAHOO.log('Disabling hidden CSS File', 'info', 'SimpleEditor');
8958
this._showingHiddenElements = true;
8959
Dom.addClass(this._getDoc().body, this.CLASS_HIDDEN);
8960
this.toolbar.selectButton('hiddenelements');
8965
* @method cmd_removeformat
8966
* @param value Value passed from the execCommand method
8967
* @description This is an execCommand override method. It is called from execCommand when the execCommand('removeformat') is used.
8969
cmd_removeformat: function(value) {
8972
* @knownissue Remove Format issue
8973
* @browser Safari 2.x
8974
* @description There is an issue here with Safari, that it may not always remove the format of the item that is selected.
8975
* Due to the way that Safari 2.x handles ranges, it is very difficult to determine what the selection holds.
8976
* So here we are making the best possible guess and acting on it.
8978
if (this.browser.webkit && !this._getDoc().queryCommandEnabled('removeformat')) {
8979
var _txt = this._getSelection()+'';
8980
this._createCurrentElement('span');
8981
this.currentElement[0].className = 'yui-non';
8982
this.currentElement[0].innerHTML = _txt;
8983
for (var i = 1; i < this.currentElement.length; i++) {
8984
this.currentElement[i].parentNode.removeChild(this.currentElement[i]);
8992
* @method cmd_script
8993
* @param action action passed from the execCommand method
8994
* @param value Value passed from the execCommand method
8995
* @description This is a combined execCommand override method. It is called from the cmd_superscript and cmd_subscript methods.
8997
cmd_script: function(action, value) {
8998
var exec = true, tag = action.toLowerCase().substring(0, 3),
8999
_span = null, _selEl = this._getSelectedElement();
9001
if (this.browser.webkit) {
9002
YAHOO.log('Safari dom fun again (' + action + ')..', 'info', 'EditorSafari');
9003
if (this._isElement(_selEl, tag)) {
9004
YAHOO.log('we are a child of tag (' + tag + '), reverse process', 'info', 'EditorSafari');
9005
_span = this._swapEl(this.currentElement[0], 'span', function(el) {
9006
el.className = 'yui-non';
9008
this._selectNode(_span);
9010
this._createCurrentElement(tag);
9011
var _sub = this._swapEl(this.currentElement[0], tag);
9012
this._selectNode(_sub);
9013
this.currentElement[0] = _sub;
9020
* @method cmd_superscript
9021
* @param value Value passed from the execCommand method
9022
* @description This is an execCommand override method. It is called from execCommand when the execCommand('superscript') is used.
9024
cmd_superscript: function(value) {
9025
return [this.cmd_script('superscript', value)];
9028
* @method cmd_subscript
9029
* @param value Value passed from the execCommand method
9030
* @description This is an execCommand override method. It is called from execCommand when the execCommand('subscript') is used.
9032
cmd_subscript: function(value) {
9033
return [this.cmd_script('subscript', value)];
9036
* @method cmd_indent
9037
* @param value Value passed from the execCommand method
9038
* @description This is an execCommand override method. It is called from execCommand when the execCommand('indent') is used.
9040
cmd_indent: function(value) {
9041
var exec = true, selEl = this._getSelectedElement(), _bq = null;
9043
//if (this.browser.webkit || this.browser.ie || this.browser.gecko) {
9044
//if (this.browser.webkit || this.browser.ie) {
9045
if (this.browser.ie) {
9046
if (this._isElement(selEl, 'blockquote')) {
9047
_bq = this._getDoc().createElement('blockquote');
9048
_bq.innerHTML = selEl.innerHTML;
9049
selEl.innerHTML = '';
9050
selEl.appendChild(_bq);
9051
this._selectNode(_bq);
9053
_bq = this._getDoc().createElement('blockquote');
9054
var html = this._getRange().htmlText;
9055
_bq.innerHTML = html;
9056
this._createCurrentElement('blockquote');
9058
for (var i = 0; i < this.currentElement.length; i++) {
9059
_bq = this._getDoc().createElement('blockquote');
9060
_bq.innerHTML = this.currentElement[i].innerHTML;
9061
this.currentElement[i].parentNode.replaceChild(_bq, this.currentElement[i]);
9062
this.currentElement[i] = _bq;
9065
this.currentElement[0].parentNode.replaceChild(_bq, this.currentElement[0]);
9066
this.currentElement[0] = _bq;
9067
this._selectNode(this.currentElement[0]);
9071
value = 'blockquote';
9073
return [exec, 'formatblock', value];
9076
* @method cmd_outdent
9077
* @param value Value passed from the execCommand method
9078
* @description This is an execCommand override method. It is called from execCommand when the execCommand('outdent') is used.
9080
cmd_outdent: function(value) {
9081
var exec = true, selEl = this._getSelectedElement(), _bq = null, _span = null;
9082
//if (this.browser.webkit || this.browser.ie || this.browser.gecko) {
9083
if (this.browser.webkit || this.browser.ie) {
9084
//if (this.browser.ie) {
9085
selEl = this._getSelectedElement();
9086
if (this._isElement(selEl, 'blockquote')) {
9087
var par = selEl.parentNode;
9088
if (this._isElement(selEl.parentNode, 'blockquote')) {
9089
par.innerHTML = selEl.innerHTML;
9090
this._selectNode(par);
9092
_span = this._getDoc().createElement('span');
9093
_span.innerHTML = selEl.innerHTML;
9094
YAHOO.util.Dom.addClass(_span, 'yui-non');
9095
par.replaceChild(_span, selEl);
9096
this._selectNode(_span);
9099
YAHOO.log('Can not outdent, we are not inside a blockquote', 'warn', 'Editor');
9105
return [exec, 'outdent', value];
9108
* @method cmd_justify
9109
* @param dir The direction to justify
9110
* @description This is a factory method for the justify family of commands.
9112
cmd_justify: function(dir) {
9113
if (this.browser.ie) {
9114
if (this._hasSelection()) {
9115
this._createCurrentElement('span');
9116
this._swapEl(this.currentElement[0], 'div', function(el) {
9117
el.style.textAlign = dir;
9123
return [true, 'justify' + dir, ''];
9126
* @method cmd_justifycenter
9127
* @param value Value passed from the execCommand method
9128
* @description This is an execCommand override method. It is called from execCommand when the execCommand('justifycenter') is used.
9130
cmd_justifycenter: function() {
9131
return [this.cmd_justify('center')];
9134
* @method cmd_justifyleft
9135
* @param value Value passed from the execCommand method
9136
* @description This is an execCommand override method. It is called from execCommand when the execCommand('justifyleft') is used.
9138
cmd_justifyleft: function() {
9139
return [this.cmd_justify('left')];
9142
* @method cmd_justifyright
9143
* @param value Value passed from the execCommand method
9144
* @description This is an execCommand override method. It is called from execCommand when the execCommand('justifyright') is used.
9146
cmd_justifyright: function() {
9147
return [this.cmd_justify('right')];
9152
* @description Returns a string representing the editor.
9155
toString: function() {
9157
if (this.get && this.get('element_cont')) {
9158
str = 'Editor (#' + this.get('element_cont').get('id') + ')' + ((this.get('disabled') ? ' Disabled' : ''));
9164
* @event beforeOpenWindow
9165
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9166
* @param {Overlay} panel The Overlay object that is used to create the window.
9167
* @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.
9168
* @type YAHOO.util.CustomEvent
9171
* @event afterOpenWindow
9172
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9173
* @param {Overlay} panel The Overlay object that is used to create the window.
9174
* @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.
9175
* @type YAHOO.util.CustomEvent
9178
* @event closeWindow
9179
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9180
* @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.
9181
* @type YAHOO.util.CustomEvent
9184
* @event windowCMDOpen
9185
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9186
* @param {Overlay} panel The Overlay object that is used to create the window.
9187
* @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.
9188
* @type YAHOO.util.CustomEvent
9191
* @event windowCMDClose
9192
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9193
* @param {Overlay} panel The Overlay object that is used to create the window.
9194
* @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.
9195
* @type YAHOO.util.CustomEvent
9198
* @event windowRender
9199
* @param {<a href="YAHOO.widget.EditorWindow.html">EditorWindow</a>} win The EditorWindow object
9200
* @param {Overlay} panel The Overlay object that is used to create the window.
9201
* @description Event fired when the initial Overlay is rendered. Can be used to manipulate the content of the panel.
9202
* @type YAHOO.util.CustomEvent
9205
* @event windowInsertImageRender
9206
* @param {Overlay} panel The Overlay object that is used to create the window.
9207
* @param {HTMLElement} body The HTML element used as the body of the window..
9208
* @param {Toolbar} toolbar A reference to the toolbar object used inside this window.
9209
* @description Event fired when the pre render of the Insert Image window has finished.
9210
* @type YAHOO.util.CustomEvent
9213
* @event windowCreateLinkRender
9214
* @param {Overlay} panel The Overlay object that is used to create the window.
9215
* @param {HTMLElement} body The HTML element used as the body of the window..
9216
* @description Event fired when the pre render of the Create Link window has finished.
9217
* @type YAHOO.util.CustomEvent
9223
* @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.
9224
* This is what you pass to Editor.openWindow();. These parameters will not take effect until the openWindow() is called in the editor.
9225
* @class EditorWindow
9226
* @param {String} name The name of the window.
9227
* @param {Object} attrs Attributes for the window. Current attributes used are : height and width
9229
YAHOO.widget.EditorWindow = function(name, attrs) {
9233
* @description A unique name for the window
9235
this.name = name.replace(' ', '_');
9239
* @description The window attributes
9244
YAHOO.widget.EditorWindow.prototype = {
9248
* @description Holder for the header of the window, used in Editor.openWindow
9254
* @description Holder for the body of the window, used in Editor.openWindow
9260
* @description Holder for the footer of the window, used in Editor.openWindow
9265
* @description Sets the header for the window.
9266
* @param {String/HTMLElement} str The string or DOM reference to be used as the windows header.
9268
setHeader: function(str) {
9273
* @description Sets the body for the window.
9274
* @param {String/HTMLElement} str The string or DOM reference to be used as the windows body.
9276
setBody: function(str) {
9281
* @description Sets the footer for the window.
9282
* @param {String/HTMLElement} str The string or DOM reference to be used as the windows footer.
9284
setFooter: function(str) {
9289
* @description Returns a string representing the EditorWindow.
9292
toString: function() {
9293
return 'Editor Window (' + this.name + ')';
9297
YAHOO.register("editor", YAHOO.widget.Editor, {version: "2.7.0", build: "1799"});