2
YUI 3.10.3 (build 2fb5187)
3
Copyright 2013 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
8
YUI.add('exec-command', function (Y, NAME) {
12
* Plugin for the frame module to handle execCommands for Editor
13
* @class Plugin.ExecCommand
17
* @submodule exec-command
19
var ExecCommand = function() {
20
ExecCommand.superclass.constructor.apply(this, arguments);
23
* This method is meant to normalize IE's in ability to exec the proper command on elements with CSS styling.
26
* @param {String} cmd The command to execute
27
* @param {String} tag The tag to create
28
* @param {String} rule The rule that we are looking for.
30
fixIETags = function(cmd, tag, rule) {
31
var inst = this.getInstance(),
32
doc = inst.config.doc,
33
sel = doc.selection.createRange(),
34
o = doc.queryCommandValue(cmd),
35
html, reg, m, p, d, s, c;
39
reg = new RegExp(rule, 'g');
43
html = html.replace(rule + ';', '').replace(rule, '');
45
sel.pasteHTML('<var id="yui-ie-bs">');
47
p = doc.getElementById('yui-ie-bs');
48
d = doc.createElement('div');
49
s = doc.createElement(tag);
52
if (p.parentNode !== inst.config.doc.body) {
58
p.parentNode.replaceChild(s, p);
60
Y.each(c, function(f) {
64
if (sel.moveToElementText) {
65
sel.moveToElementText(s);
73
Y.extend(ExecCommand, Y.Base, {
75
* An internal reference to the keyCode of the last key that was pressed.
81
* An internal reference to the instance of the frame plugged into.
87
* Execute a command on the frame's document.
89
* @param {String} action The action to perform (bold, italic, fontname)
90
* @param {String} value The optional value (helvetica)
91
* @return {Node/NodeList} Should return the Node/Nodelist affected
93
command: function(action, value) {
94
var fn = ExecCommand.COMMANDS[action];
96
Y.log('execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
98
Y.log('OVERIDE execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
99
return fn.call(this, action, value);
101
return this._command(action, value);
105
* The private version of execCommand that doesn't filter for overrides.
108
* @param {String} action The action to perform (bold, italic, fontname)
109
* @param {String} value The optional value (helvetica)
111
_command: function(action, value) {
112
var inst = this.getInstance();
115
inst.config.doc.execCommand('styleWithCSS', null, 1);
118
inst.config.doc.execCommand('useCSS', null, 0);
122
Y.log('Using default browser execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
123
inst.config.doc.execCommand(action, null, value);
125
Y.log(e.message, 'warn', 'exec-command');
129
* Get's the instance of YUI bound to the parent frame
130
* @method getInstance
131
* @return {YUI} The YUI instance bound to the parent frame
133
getInstance: function() {
135
this._inst = this.get('host').getInstance();
139
initializer: function() {
140
Y.mix(this.get('host'), {
141
execCommand: function(action, value) {
142
return this.exec.command(action, value);
144
_execCommand: function(action, value) {
145
return this.exec._command(action, value);
149
this.get('host').on('dom:keypress', Y.bind(function(e) {
150
this._lastKey = e.keyCode;
153
_wrapContent: function(str, override) {
154
var useP = (this.getInstance().host.editorPara && !override ? true : false);
157
str = '<p>' + str + '</p>';
182
* Static object literal of execCommand overrides
188
* Wraps the content with a new element of type (tag)
189
* @method COMMANDS.wrap
191
* @param {String} cmd The command executed: wrap
192
* @param {String} tag The tag to wrap the selection with
193
* @return {NodeList} NodeList of the items touched by this command.
195
wrap: function(cmd, tag) {
196
var inst = this.getInstance();
197
return (new inst.EditorSelection()).wrapContent(tag);
200
* Inserts the provided HTML at the cursor, should be a single element.
201
* @method COMMANDS.inserthtml
203
* @param {String} cmd The command executed: inserthtml
204
* @param {String} html The html to insert
205
* @return {Node} Node instance of the item touched by this command.
207
inserthtml: function(cmd, html) {
208
var inst = this.getInstance();
209
if (inst.EditorSelection.hasCursor() || Y.UA.ie) {
210
return (new inst.EditorSelection()).insertContent(html);
212
this._command('inserthtml', html);
216
* Inserts the provided HTML at the cursor, and focuses the cursor afterwards.
217
* @method COMMANDS.insertandfocus
219
* @param {String} cmd The command executed: insertandfocus
220
* @param {String} html The html to insert
221
* @return {Node} Node instance of the item touched by this command.
223
insertandfocus: function(cmd, html) {
224
var inst = this.getInstance(), out, sel;
225
if (inst.EditorSelection.hasCursor()) {
226
html += inst.EditorSelection.CURSOR;
227
out = this.command('inserthtml', html);
228
sel = new inst.EditorSelection();
229
sel.focusCursor(true, true);
231
this.command('inserthtml', html);
236
* Inserts a BR at the current cursor position
237
* @method COMMANDS.insertbr
239
* @param {String} cmd The command executed: insertbr
241
insertbr: function() {
242
var inst = this.getInstance(),
243
sel = new inst.EditorSelection(),
244
html = '<var>|</var>', last = null,
245
q = (Y.UA.webkit) ? 'span.Apple-style-span,var' : 'var',
246
insert = function(n) {
247
var c = inst.Node.create('<br>');
248
n.insert(c, 'before');
252
if (sel._selection.pasteHTML) {
253
sel._selection.pasteHTML(html);
255
this._command('inserthtml', html);
259
inst.all(q).each(function(n) {
263
if (n.get('innerHTML') === '|') {
269
if ((!last.previous() || !last.previous().test('br')) && Y.UA.gecko) {
270
s = last.cloneNode();
271
last.insert(s, 'after');
277
if (Y.UA.webkit && last) {
279
sel.selectNode(last);
283
* Inserts an image at the cursor position
284
* @method COMMANDS.insertimage
286
* @param {String} cmd The command executed: insertimage
287
* @param {String} img The url of the image to be inserted
288
* @return {Node} Node instance of the item touched by this command.
290
insertimage: function(cmd, img) {
291
return this.command('inserthtml', '<img src="' + img + '">');
294
* Add a class to all of the elements in the selection
295
* @method COMMANDS.addclass
297
* @param {String} cmd The command executed: addclass
298
* @param {String} cls The className to add
299
* @return {NodeList} NodeList of the items touched by this command.
301
addclass: function(cmd, cls) {
302
var inst = this.getInstance();
303
return (new inst.EditorSelection()).getSelected().addClass(cls);
306
* Remove a class from all of the elements in the selection
307
* @method COMMANDS.removeclass
309
* @param {String} cmd The command executed: removeclass
310
* @param {String} cls The className to remove
311
* @return {NodeList} NodeList of the items touched by this command.
313
removeclass: function(cmd, cls) {
314
var inst = this.getInstance();
315
return (new inst.EditorSelection()).getSelected().removeClass(cls);
318
* Adds a forecolor to the current selection, or creates a new element and applies it
319
* @method COMMANDS.forecolor
321
* @param {String} cmd The command executed: forecolor
322
* @param {String} val The color value to apply
323
* @return {NodeList} NodeList of the items touched by this command.
325
forecolor: function(cmd, val) {
326
var inst = this.getInstance(),
327
sel = new inst.EditorSelection(), n;
330
this._command('useCSS', false);
332
if (inst.EditorSelection.hasCursor()) {
333
if (sel.isCollapsed) {
334
if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === ' ')) {
335
sel.anchorNode.setStyle('color', val);
338
n = this.command('inserthtml', '<span style="color: ' + val + '">' + inst.EditorSelection.CURSOR + '</span>');
339
sel.focusCursor(true, true);
343
return this._command(cmd, val);
346
this._command(cmd, val);
350
* Adds a background color to the current selection, or creates a new element and applies it
351
* @method COMMANDS.backcolor
353
* @param {String} cmd The command executed: backcolor
354
* @param {String} val The color value to apply
355
* @return {NodeList} NodeList of the items touched by this command.
357
backcolor: function(cmd, val) {
358
var inst = this.getInstance(),
359
sel = new inst.EditorSelection(), n;
361
if (Y.UA.gecko || Y.UA.opera) {
365
this._command('useCSS', false);
367
if (inst.EditorSelection.hasCursor()) {
368
if (sel.isCollapsed) {
369
if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === ' ')) {
370
sel.anchorNode.setStyle('backgroundColor', val);
373
n = this.command('inserthtml',
374
'<span style="background-color: ' + val + '">' + inst.EditorSelection.CURSOR + '</span>');
375
sel.focusCursor(true, true);
379
return this._command(cmd, val);
382
this._command(cmd, val);
386
* Sugar method, calles backcolor
387
* @method COMMANDS.hilitecolor
389
* @param {String} cmd The command executed: backcolor
390
* @param {String} val The color value to apply
391
* @return {NodeList} NodeList of the items touched by this command.
393
hilitecolor: function() {
394
return ExecCommand.COMMANDS.backcolor.apply(this, arguments);
397
* Adds a font name to the current selection, or creates a new element and applies it
398
* @method COMMANDS.fontname2
401
* @param {String} cmd The command executed: fontname
402
* @param {String} val The font name to apply
403
* @return {NodeList} NodeList of the items touched by this command.
405
fontname2: function(cmd, val) {
406
this._command('fontname', val);
407
var inst = this.getInstance(),
408
sel = new inst.EditorSelection();
410
if (sel.isCollapsed && (this._lastKey !== 32)) {
411
if (sel.anchorNode.test('font')) {
412
sel.anchorNode.set('face', val);
417
* Adds a fontsize to the current selection, or creates a new element and applies it
418
* @method COMMANDS.fontsize2
421
* @param {String} cmd The command executed: fontsize
422
* @param {String} val The font size to apply
423
* @return {NodeList} NodeList of the items touched by this command.
425
fontsize2: function(cmd, val) {
426
this._command('fontsize', val);
428
var inst = this.getInstance(),
429
sel = new inst.EditorSelection(), p;
431
if (sel.isCollapsed && sel.anchorNode && (this._lastKey !== 32)) {
433
if (sel.anchorNode.getStyle('lineHeight')) {
434
sel.anchorNode.setStyle('lineHeight', '');
437
if (sel.anchorNode.test('font')) {
438
sel.anchorNode.set('size', val);
439
} else if (Y.UA.gecko) {
440
p = sel.anchorNode.ancestor(inst.EditorSelection.DEFAULT_BLOCK_TAG);
442
p.setStyle('fontSize', '');
448
* Overload for COMMANDS.list
449
* @method COMMANDS.insertorderedlist
451
* @param {String} cmd The command executed: list, ul
453
insertunorderedlist: function() {
454
this.command('list', 'ul');
457
* Overload for COMMANDS.list
458
* @method COMMANDS.insertunorderedlist
460
* @param {String} cmd The command executed: list, ol
462
insertorderedlist: function() {
463
this.command('list', 'ol');
466
* Noramlizes lists creation/destruction for IE. All others pass through to native calls
467
* @method COMMANDS.list
469
* @param {String} cmd The command executed: list (not used)
470
* @param {String} tag The tag to deal with
472
list: function(cmd, tag) {
473
var inst = this.getInstance(), html, self = this,
475
The yui3- class name below is not a skinnable class,
476
it's a utility class used internally by editor and
477
stripped when completed, calling getClassName on this
478
is a waste of resources.
480
DIR = 'dir', cls = 'yui3-touched',
481
dir, range, div, elm, n, str, s, par, list, lis,
482
useP = (inst.host.editorPara ? true : false), tmp,
483
sdir, hasPParent, fc,
484
sel = new inst.EditorSelection();
486
cmd = 'insert' + ((tag === 'ul') ? 'un' : '') + 'orderedlist';
488
if (Y.UA.ie && !sel.isCollapsed) {
489
range = sel._selection;
490
html = range.htmlText;
491
div = inst.Node.create(html) || inst.one('body');
493
if (div.test('li') || div.one('li')) {
494
this._command(cmd, null);
498
elm = range.item ? range.item(0) : range.parentElement();
503
lis.each(function(l) {
504
str = self._wrapContent(l.get('innerHTML'));
507
s = inst.Node.create(str);
508
if (n.get('parentNode').test('div')) {
509
n = n.get('parentNode');
511
if (n && n.hasAttribute(DIR)) {
513
s.all('p').setAttribute(DIR, n.getAttribute(DIR));
515
s.setAttribute(DIR, n.getAttribute(DIR));
519
n.replace(s.get('innerHTML'));
523
if (range.moveToElementText) {
524
range.moveToElementText(s._node);
528
par = Y.one(range.parentElement());
529
if (!par.test(inst.EditorSelection.BLOCKS)) {
530
par = par.ancestor(inst.EditorSelection.BLOCKS);
533
if (par.hasAttribute(DIR)) {
534
dir = par.getAttribute(DIR);
537
if (html.indexOf('<br>') > -1) {
538
html = html.split(/<br>/i);
540
tmp = inst.Node.create(html),
541
ps = tmp ? tmp.all('p') : null;
543
if (ps && ps.size()) {
545
ps.each(function(n) {
546
html.push(n.get('innerHTML'));
552
list = '<' + tag + ' id="ie-list">';
553
Y.each(html, function(v) {
554
var a = inst.Node.create(v);
555
if (a && a.test('p')) {
556
if (a.hasAttribute(DIR)) {
557
dir = a.getAttribute(DIR);
559
v = a.get('innerHTML');
561
list += '<li>' + v + '</li>';
563
list += '</' + tag + '>';
564
range.pasteHTML(list);
565
elm = inst.config.doc.getElementById('ie-list');
568
elm.setAttribute(DIR, dir);
570
if (range.moveToElementText) {
571
range.moveToElementText(elm);
575
} else if (Y.UA.ie) {
576
par = inst.one(sel._selection.parentElement());
578
if (par && par.hasAttribute(DIR)) {
579
dir = par.getAttribute(DIR);
581
html = Y.EditorSelection.getText(par);
585
sdir = ' dir="' + dir + '"';
587
list = inst.Node.create(Y.Lang.sub('<{tag}{dir}><li></li></{tag}>', { tag: tag, dir: sdir }));
589
sel.selectNode(list.one('li'));
591
this._command(cmd, null);
594
this._command(cmd, null);
597
inst.all(tag).addClass(cls);
598
if (sel.anchorNode.test(inst.EditorSelection.BLOCKS)) {
599
par = sel.anchorNode;
601
par = sel.anchorNode.ancestor(inst.EditorSelection.BLOCKS);
603
if (!par) { //No parent, find the first block under the anchorNode
604
par = sel.anchorNode.one(inst.EditorSelection.BLOCKS);
607
if (par && par.hasAttribute(DIR)) {
608
dir = par.getAttribute(DIR);
610
if (par && par.test(tag)) {
611
hasPParent = par.ancestor('p');
612
html = inst.Node.create('<div/>');
614
elm.each(function(h) {
615
html.append(self._wrapContent(h.get('innerHTML'), hasPParent));
619
html.all('p').setAttribute(DIR, dir);
621
html.setAttribute(DIR, dir);
625
html = inst.Node.create(html.get('innerHTML'));
627
fc = html.get('firstChild');
631
this._command(cmd, null);
633
list = inst.all(tag);
637
list.each(function(n) {
638
if (!n.hasClass(cls)) {
639
n.setAttribute(DIR, dir);
645
list.removeClass(cls);
649
* Noramlizes alignment for Webkit Browsers
650
* @method COMMANDS.justify
652
* @param {String} cmd The command executed: justify (not used)
653
* @param {String} val The actual command from the justify{center,all,left,right} stubs
655
justify: function(cmd, val) {
657
var inst = this.getInstance(),
658
sel = new inst.EditorSelection(),
659
aNode = sel.anchorNode, html,
660
bgColor = aNode.getStyle('backgroundColor');
663
sel = new inst.EditorSelection();
664
if (sel.anchorNode.test('div')) {
665
html = '<span>' + sel.anchorNode.get('innerHTML') + '</span>';
666
sel.anchorNode.set('innerHTML', html);
667
sel.anchorNode.one('span').setStyle('backgroundColor', bgColor);
668
sel.selectNode(sel.anchorNode.one('span'));
675
* Override method for COMMANDS.justify
676
* @method COMMANDS.justifycenter
679
justifycenter: function() {
680
this.command('justify', 'justifycenter');
683
* Override method for COMMANDS.justify
684
* @method COMMANDS.justifyleft
687
justifyleft: function() {
688
this.command('justify', 'justifyleft');
691
* Override method for COMMANDS.justify
692
* @method COMMANDS.justifyright
695
justifyright: function() {
696
this.command('justify', 'justifyright');
699
* Override method for COMMANDS.justify
700
* @method COMMANDS.justifyfull
703
justifyfull: function() {
704
this.command('justify', 'justifyfull');
710
ExecCommand.COMMANDS.bold = function() {
711
fixIETags.call(this, 'bold', 'b', 'FONT-WEIGHT: bold');
713
ExecCommand.COMMANDS.italic = function() {
714
fixIETags.call(this, 'italic', 'i', 'FONT-STYLE: italic');
716
ExecCommand.COMMANDS.underline = function() {
717
fixIETags.call(this, 'underline', 'u', 'TEXT-DECORATION: underline');
721
Y.namespace('Plugin');
722
Y.Plugin.ExecCommand = ExecCommand;
726
}, '3.10.3', {"requires": ["frame"]});