2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('exec-command', function(Y) {
12 * Plugin for the frame module to handle execCommands for Editor
14 * @submodule exec-command
17 * Plugin for the frame module to handle execCommands for Editor
18 * @class Plugin.ExecCommand
22 var ExecCommand = function() {
23 ExecCommand.superclass.constructor.apply(this, arguments);
26 Y.extend(ExecCommand, Y.Base, {
28 * An internal reference to the keyCode of the last key that was pressed.
34 * An internal reference to the instance of the frame plugged into.
40 * Execute a command on the frame's document.
42 * @param {String} action The action to perform (bold, italic, fontname)
43 * @param {String} value The optional value (helvetica)
44 * @return {Node/NodeList} Should return the Node/Nodelist affected
46 command: function(action, value) {
47 var fn = ExecCommand.COMMANDS[action];
50 if (action !== 'insertbr') {
51 Y.later(0, this, function() {
52 var inst = this.getInstance();
53 if (inst && inst.Selection) {
54 inst.Selection.cleanCursor();
61 return fn.call(this, action, value);
63 return this._command(action, value);
67 * The private version of execCommand that doesn't filter for overrides.
70 * @param {String} action The action to perform (bold, italic, fontname)
71 * @param {String} value The optional value (helvetica)
73 _command: function(action, value) {
74 var inst = this.getInstance();
77 inst.config.doc.execCommand('styleWithCSS', null, 1);
80 inst.config.doc.execCommand('useCSS', null, 0);
84 inst.config.doc.execCommand(action, null, value);
89 * Get's the instance of YUI bound to the parent frame
91 * @return {YUI} The YUI instance bound to the parent frame
93 getInstance: function() {
95 this._inst = this.get('host').getInstance();
99 initializer: function() {
100 Y.mix(this.get('host'), {
101 execCommand: function(action, value) {
102 return this.exec.command(action, value);
104 _execCommand: function(action, value) {
105 return this.exec._command(action, value);
109 this.get('host').on('dom:keypress', Y.bind(function(e) {
110 this._lastKey = e.keyCode;
132 * Static object literal of execCommand overrides
138 * Wraps the content with a new element of type (tag)
139 * @method COMMANDS.wrap
141 * @param {String} cmd The command executed: wrap
142 * @param {String} tag The tag to wrap the selection with
143 * @return {NodeList} NodeList of the items touched by this command.
145 wrap: function(cmd, tag) {
146 var inst = this.getInstance();
147 return (new inst.Selection()).wrapContent(tag);
150 * Inserts the provided HTML at the cursor, should be a single element.
151 * @method COMMANDS.inserthtml
153 * @param {String} cmd The command executed: inserthtml
154 * @param {String} html The html to insert
155 * @return {Node} Node instance of the item touched by this command.
157 inserthtml: function(cmd, html) {
158 var inst = this.getInstance();
159 if (inst.Selection.hasCursor() || Y.UA.ie) {
160 return (new inst.Selection()).insertContent(html);
162 this._command('inserthtml', html);
166 * Inserts the provided HTML at the cursor, and focuses the cursor afterwards.
167 * @method COMMANDS.insertandfocus
169 * @param {String} cmd The command executed: insertandfocus
170 * @param {String} html The html to insert
171 * @return {Node} Node instance of the item touched by this command.
173 insertandfocus: function(cmd, html) {
174 var inst = this.getInstance(), out, sel;
175 if (inst.Selection.hasCursor()) {
176 html += inst.Selection.CURSOR;
177 out = this.command('inserthtml', html);
178 sel = new inst.Selection();
179 sel.focusCursor(true, true);
181 this.command('inserthtml', html);
186 * Inserts a BR at the current cursor position
187 * @method COMMANDS.insertbr
189 * @param {String} cmd The command executed: insertbr
191 insertbr: function(cmd) {
192 var inst = this.getInstance(), cur,
193 sel = new inst.Selection();
196 cur = sel.getCursor();
197 cur.insert('<br>', 'before');
198 sel.focusCursor(true, false);
199 return ((cur && cur.previous) ? cur.previous() : null);
202 * Inserts an image at the cursor position
203 * @method COMMANDS.insertimage
205 * @param {String} cmd The command executed: insertimage
206 * @param {String} img The url of the image to be inserted
207 * @return {Node} Node instance of the item touched by this command.
209 insertimage: function(cmd, img) {
210 return this.command('inserthtml', '<img src="' + img + '">');
213 * Add a class to all of the elements in the selection
214 * @method COMMANDS.addclass
216 * @param {String} cmd The command executed: addclass
217 * @param {String} cls The className to add
218 * @return {NodeList} NodeList of the items touched by this command.
220 addclass: function(cmd, cls) {
221 var inst = this.getInstance();
222 return (new inst.Selection()).getSelected().addClass(cls);
225 * Remove a class from all of the elements in the selection
226 * @method COMMANDS.removeclass
228 * @param {String} cmd The command executed: removeclass
229 * @param {String} cls The className to remove
230 * @return {NodeList} NodeList of the items touched by this command.
232 removeclass: function(cmd, cls) {
233 var inst = this.getInstance();
234 return (new inst.Selection()).getSelected().removeClass(cls);
237 * Adds a forecolor to the current selection, or creates a new element and applies it
238 * @method COMMANDS.forecolor
240 * @param {String} cmd The command executed: forecolor
241 * @param {String} val The color value to apply
242 * @return {NodeList} NodeList of the items touched by this command.
244 forecolor: function(cmd, val) {
245 var inst = this.getInstance(),
246 sel = new inst.Selection(), n;
249 this._command('useCSS', false);
251 if (inst.Selection.hasCursor()) {
252 if (sel.isCollapsed) {
253 if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === ' ')) {
254 sel.anchorNode.setStyle('color', val);
257 n = this.command('inserthtml', '<span style="color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
258 sel.focusCursor(true, true);
262 return this._command(cmd, val);
265 this._command(cmd, val);
269 * Adds a background color to the current selection, or creates a new element and applies it
270 * @method COMMANDS.backcolor
272 * @param {String} cmd The command executed: backcolor
273 * @param {String} val The color value to apply
274 * @return {NodeList} NodeList of the items touched by this command.
276 backcolor: function(cmd, val) {
277 var inst = this.getInstance(),
278 sel = new inst.Selection(), n;
280 if (Y.UA.gecko || Y.UA.opera) {
284 this._command('useCSS', false);
286 if (inst.Selection.hasCursor()) {
287 if (sel.isCollapsed) {
288 if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === ' ')) {
289 sel.anchorNode.setStyle('backgroundColor', val);
292 n = this.command('inserthtml', '<span style="background-color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
293 sel.focusCursor(true, true);
297 return this._command(cmd, val);
300 this._command(cmd, val);
304 * Sugar method, calles backcolor
305 * @method COMMANDS.hilitecolor
307 * @param {String} cmd The command executed: backcolor
308 * @param {String} val The color value to apply
309 * @return {NodeList} NodeList of the items touched by this command.
311 hilitecolor: function() {
312 return ExecCommand.COMMANDS.backcolor.apply(this, arguments);
315 * Adds a font name to the current selection, or creates a new element and applies it
316 * @method COMMANDS.fontname
318 * @param {String} cmd The command executed: fontname
319 * @param {String} val The font name to apply
320 * @return {NodeList} NodeList of the items touched by this command.
322 fontname: function(cmd, val) {
323 this._command('fontname', val);
324 var inst = this.getInstance(),
325 sel = new inst.Selection();
327 if (sel.isCollapsed && (this._lastKey != 32)) {
328 if (sel.anchorNode.test('font')) {
329 sel.anchorNode.set('face', val);
334 * Adds a fontsize to the current selection, or creates a new element and applies it
335 * @method COMMANDS.fontsize
337 * @param {String} cmd The command executed: fontsize
338 * @param {String} val The font size to apply
339 * @return {NodeList} NodeList of the items touched by this command.
341 fontsize: function(cmd, val) {
342 this._command('fontsize', val);
344 var inst = this.getInstance(),
345 sel = new inst.Selection();
347 if (sel.isCollapsed && sel.anchorNode && (this._lastKey != 32)) {
349 if (sel.anchorNode.getStyle('lineHeight')) {
350 sel.anchorNode.setStyle('lineHeight', '');
353 if (sel.anchorNode.test('font')) {
354 sel.anchorNode.set('size', val);
355 } else if (Y.UA.gecko) {
356 var p = sel.anchorNode.ancestor(inst.Selection.DEFAULT_BLOCK_TAG);
358 p.setStyle('fontSize', '');
367 * This method is meant to normalize IE's in ability to exec the proper command on elements with CSS styling.
370 * @param {String} cmd The command to execute
371 * @param {String} tag The tag to create
372 * @param {String} rule The rule that we are looking for.
374 var fixIETags = function(cmd, tag, rule) {
375 var inst = this.getInstance(),
376 doc = inst.config.doc,
377 sel = doc.selection.createRange(),
378 o = doc.queryCommandValue(cmd),
379 html, reg, m, p, d, s, c;
383 reg = new RegExp(rule, 'g');
387 html = html.replace(rule + ';', '').replace(rule, '');
389 sel.pasteHTML('<var id="yui-ie-bs">');
391 p = doc.getElementById('yui-ie-bs');
392 d = doc.createElement('div');
393 s = doc.createElement(tag);
396 if (p.parentNode !== inst.config.doc.body) {
402 p.parentNode.replaceChild(s, p);
404 Y.each(c, function(f) {
408 sel.moveToElementText(s);
416 ExecCommand.COMMANDS.bold = function() {
417 fixIETags.call(this, 'bold', 'b', 'FONT-WEIGHT: bold');
419 ExecCommand.COMMANDS.italic = function() {
420 fixIETags.call(this, 'italic', 'i', 'FONT-STYLE: italic');
422 ExecCommand.COMMANDS.underline = function() {
423 fixIETags.call(this, 'underline', 'u', 'TEXT-DECORATION: underline');
427 Y.namespace('Plugin');
428 Y.Plugin.ExecCommand = ExecCommand;
432 }, '3.3.0' ,{requires:['frame'], skinnable:false});