Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

exec-command-debug.js 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. YUI.add('exec-command', function(Y) {
  9. /**
  10. * Plugin for the frame module to handle execCommands for Editor
  11. * @class Plugin.ExecCommand
  12. * @extends Base
  13. * @constructor
  14. * @module editor
  15. * @submodule exec-command
  16. */
  17. var ExecCommand = function() {
  18. ExecCommand.superclass.constructor.apply(this, arguments);
  19. };
  20. Y.extend(ExecCommand, Y.Base, {
  21. /**
  22. * An internal reference to the keyCode of the last key that was pressed.
  23. * @private
  24. * @property _lastKey
  25. */
  26. _lastKey: null,
  27. /**
  28. * An internal reference to the instance of the frame plugged into.
  29. * @private
  30. * @property _inst
  31. */
  32. _inst: null,
  33. /**
  34. * Execute a command on the frame's document.
  35. * @method command
  36. * @param {String} action The action to perform (bold, italic, fontname)
  37. * @param {String} value The optional value (helvetica)
  38. * @return {Node/NodeList} Should return the Node/Nodelist affected
  39. */
  40. command: function(action, value) {
  41. var fn = ExecCommand.COMMANDS[action];
  42. Y.log('execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
  43. if (fn) {
  44. Y.log('OVERIDE execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
  45. return fn.call(this, action, value);
  46. } else {
  47. return this._command(action, value);
  48. }
  49. },
  50. /**
  51. * The private version of execCommand that doesn't filter for overrides.
  52. * @private
  53. * @method _command
  54. * @param {String} action The action to perform (bold, italic, fontname)
  55. * @param {String} value The optional value (helvetica)
  56. */
  57. _command: function(action, value) {
  58. var inst = this.getInstance();
  59. try {
  60. try {
  61. inst.config.doc.execCommand('styleWithCSS', null, 1);
  62. } catch (e1) {
  63. try {
  64. inst.config.doc.execCommand('useCSS', null, 0);
  65. } catch (e2) {
  66. }
  67. }
  68. Y.log('Using default browser execCommand(' + action + '): "' + value + '"', 'info', 'exec-command');
  69. inst.config.doc.execCommand(action, null, value);
  70. } catch (e) {
  71. Y.log(e.message, 'error', 'exec-command');
  72. }
  73. },
  74. /**
  75. * Get's the instance of YUI bound to the parent frame
  76. * @method getInstance
  77. * @return {YUI} The YUI instance bound to the parent frame
  78. */
  79. getInstance: function() {
  80. if (!this._inst) {
  81. this._inst = this.get('host').getInstance();
  82. }
  83. return this._inst;
  84. },
  85. initializer: function() {
  86. Y.mix(this.get('host'), {
  87. execCommand: function(action, value) {
  88. return this.exec.command(action, value);
  89. },
  90. _execCommand: function(action, value) {
  91. return this.exec._command(action, value);
  92. }
  93. });
  94. this.get('host').on('dom:keypress', Y.bind(function(e) {
  95. this._lastKey = e.keyCode;
  96. }, this));
  97. }
  98. }, {
  99. /**
  100. * execCommand
  101. * @property NAME
  102. * @static
  103. */
  104. NAME: 'execCommand',
  105. /**
  106. * exec
  107. * @property NS
  108. * @static
  109. */
  110. NS: 'exec',
  111. ATTRS: {
  112. host: {
  113. value: false
  114. }
  115. },
  116. /**
  117. * Static object literal of execCommand overrides
  118. * @property COMMANDS
  119. * @static
  120. */
  121. COMMANDS: {
  122. /**
  123. * Wraps the content with a new element of type (tag)
  124. * @method COMMANDS.wrap
  125. * @static
  126. * @param {String} cmd The command executed: wrap
  127. * @param {String} tag The tag to wrap the selection with
  128. * @return {NodeList} NodeList of the items touched by this command.
  129. */
  130. wrap: function(cmd, tag) {
  131. var inst = this.getInstance();
  132. return (new inst.Selection()).wrapContent(tag);
  133. },
  134. /**
  135. * Inserts the provided HTML at the cursor, should be a single element.
  136. * @method COMMANDS.inserthtml
  137. * @static
  138. * @param {String} cmd The command executed: inserthtml
  139. * @param {String} html The html to insert
  140. * @return {Node} Node instance of the item touched by this command.
  141. */
  142. inserthtml: function(cmd, html) {
  143. var inst = this.getInstance();
  144. if (inst.Selection.hasCursor() || Y.UA.ie) {
  145. return (new inst.Selection()).insertContent(html);
  146. } else {
  147. this._command('inserthtml', html);
  148. }
  149. },
  150. /**
  151. * Inserts the provided HTML at the cursor, and focuses the cursor afterwards.
  152. * @method COMMANDS.insertandfocus
  153. * @static
  154. * @param {String} cmd The command executed: insertandfocus
  155. * @param {String} html The html to insert
  156. * @return {Node} Node instance of the item touched by this command.
  157. */
  158. insertandfocus: function(cmd, html) {
  159. var inst = this.getInstance(), out, sel;
  160. if (inst.Selection.hasCursor()) {
  161. html += inst.Selection.CURSOR;
  162. out = this.command('inserthtml', html);
  163. sel = new inst.Selection();
  164. sel.focusCursor(true, true);
  165. } else {
  166. this.command('inserthtml', html);
  167. }
  168. return out;
  169. },
  170. /**
  171. * Inserts a BR at the current cursor position
  172. * @method COMMANDS.insertbr
  173. * @static
  174. * @param {String} cmd The command executed: insertbr
  175. */
  176. insertbr: function(cmd) {
  177. var inst = this.getInstance(),
  178. sel = new inst.Selection(),
  179. html = '<var>|</var>', last = null,
  180. q = (Y.UA.webkit) ? 'span.Apple-style-span,var' : 'var';
  181. if (sel._selection.pasteHTML) {
  182. sel._selection.pasteHTML(html);
  183. } else {
  184. this._command('inserthtml', html);
  185. }
  186. var insert = function(n) {
  187. var c = inst.Node.create('<br>');
  188. n.insert(c, 'before');
  189. return c;
  190. };
  191. inst.all(q).each(function(n) {
  192. var g = true;
  193. if (Y.UA.webkit) {
  194. g = false;
  195. if (n.get('innerHTML') === '|') {
  196. g = true;
  197. }
  198. }
  199. if (g) {
  200. last = insert(n);
  201. if ((!last.previous() || !last.previous().test('br')) && Y.UA.gecko) {
  202. var s = last.cloneNode();
  203. last.insert(s, 'after');
  204. last = s;
  205. }
  206. n.remove();
  207. }
  208. });
  209. if (Y.UA.webkit && last) {
  210. insert(last);
  211. sel.selectNode(last);
  212. }
  213. },
  214. /**
  215. * Inserts an image at the cursor position
  216. * @method COMMANDS.insertimage
  217. * @static
  218. * @param {String} cmd The command executed: insertimage
  219. * @param {String} img The url of the image to be inserted
  220. * @return {Node} Node instance of the item touched by this command.
  221. */
  222. insertimage: function(cmd, img) {
  223. return this.command('inserthtml', '<img src="' + img + '">');
  224. },
  225. /**
  226. * Add a class to all of the elements in the selection
  227. * @method COMMANDS.addclass
  228. * @static
  229. * @param {String} cmd The command executed: addclass
  230. * @param {String} cls The className to add
  231. * @return {NodeList} NodeList of the items touched by this command.
  232. */
  233. addclass: function(cmd, cls) {
  234. var inst = this.getInstance();
  235. return (new inst.Selection()).getSelected().addClass(cls);
  236. },
  237. /**
  238. * Remove a class from all of the elements in the selection
  239. * @method COMMANDS.removeclass
  240. * @static
  241. * @param {String} cmd The command executed: removeclass
  242. * @param {String} cls The className to remove
  243. * @return {NodeList} NodeList of the items touched by this command.
  244. */
  245. removeclass: function(cmd, cls) {
  246. var inst = this.getInstance();
  247. return (new inst.Selection()).getSelected().removeClass(cls);
  248. },
  249. /**
  250. * Adds a forecolor to the current selection, or creates a new element and applies it
  251. * @method COMMANDS.forecolor
  252. * @static
  253. * @param {String} cmd The command executed: forecolor
  254. * @param {String} val The color value to apply
  255. * @return {NodeList} NodeList of the items touched by this command.
  256. */
  257. forecolor: function(cmd, val) {
  258. var inst = this.getInstance(),
  259. sel = new inst.Selection(), n;
  260. if (!Y.UA.ie) {
  261. this._command('useCSS', false);
  262. }
  263. if (inst.Selection.hasCursor()) {
  264. if (sel.isCollapsed) {
  265. if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
  266. sel.anchorNode.setStyle('color', val);
  267. n = sel.anchorNode;
  268. } else {
  269. n = this.command('inserthtml', '<span style="color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
  270. sel.focusCursor(true, true);
  271. }
  272. return n;
  273. } else {
  274. return this._command(cmd, val);
  275. }
  276. } else {
  277. this._command(cmd, val);
  278. }
  279. },
  280. /**
  281. * Adds a background color to the current selection, or creates a new element and applies it
  282. * @method COMMANDS.backcolor
  283. * @static
  284. * @param {String} cmd The command executed: backcolor
  285. * @param {String} val The color value to apply
  286. * @return {NodeList} NodeList of the items touched by this command.
  287. */
  288. backcolor: function(cmd, val) {
  289. var inst = this.getInstance(),
  290. sel = new inst.Selection(), n;
  291. if (Y.UA.gecko || Y.UA.opera) {
  292. cmd = 'hilitecolor';
  293. }
  294. if (!Y.UA.ie) {
  295. this._command('useCSS', false);
  296. }
  297. if (inst.Selection.hasCursor()) {
  298. if (sel.isCollapsed) {
  299. if (sel.anchorNode && (sel.anchorNode.get('innerHTML') === '&nbsp;')) {
  300. sel.anchorNode.setStyle('backgroundColor', val);
  301. n = sel.anchorNode;
  302. } else {
  303. n = this.command('inserthtml', '<span style="background-color: ' + val + '">' + inst.Selection.CURSOR + '</span>');
  304. sel.focusCursor(true, true);
  305. }
  306. return n;
  307. } else {
  308. return this._command(cmd, val);
  309. }
  310. } else {
  311. this._command(cmd, val);
  312. }
  313. },
  314. /**
  315. * Sugar method, calles backcolor
  316. * @method COMMANDS.hilitecolor
  317. * @static
  318. * @param {String} cmd The command executed: backcolor
  319. * @param {String} val The color value to apply
  320. * @return {NodeList} NodeList of the items touched by this command.
  321. */
  322. hilitecolor: function() {
  323. return ExecCommand.COMMANDS.backcolor.apply(this, arguments);
  324. },
  325. /**
  326. * Adds a font name to the current selection, or creates a new element and applies it
  327. * @method COMMANDS.fontname2
  328. * @deprecated
  329. * @static
  330. * @param {String} cmd The command executed: fontname
  331. * @param {String} val The font name to apply
  332. * @return {NodeList} NodeList of the items touched by this command.
  333. */
  334. fontname2: function(cmd, val) {
  335. this._command('fontname', val);
  336. var inst = this.getInstance(),
  337. sel = new inst.Selection();
  338. if (sel.isCollapsed && (this._lastKey != 32)) {
  339. if (sel.anchorNode.test('font')) {
  340. sel.anchorNode.set('face', val);
  341. }
  342. }
  343. },
  344. /**
  345. * Adds a fontsize to the current selection, or creates a new element and applies it
  346. * @method COMMANDS.fontsize2
  347. * @deprecated
  348. * @static
  349. * @param {String} cmd The command executed: fontsize
  350. * @param {String} val The font size to apply
  351. * @return {NodeList} NodeList of the items touched by this command.
  352. */
  353. fontsize2: function(cmd, val) {
  354. this._command('fontsize', val);
  355. var inst = this.getInstance(),
  356. sel = new inst.Selection();
  357. if (sel.isCollapsed && sel.anchorNode && (this._lastKey != 32)) {
  358. if (Y.UA.webkit) {
  359. if (sel.anchorNode.getStyle('lineHeight')) {
  360. sel.anchorNode.setStyle('lineHeight', '');
  361. }
  362. }
  363. if (sel.anchorNode.test('font')) {
  364. sel.anchorNode.set('size', val);
  365. } else if (Y.UA.gecko) {
  366. var p = sel.anchorNode.ancestor(inst.Selection.DEFAULT_BLOCK_TAG);
  367. if (p) {
  368. p.setStyle('fontSize', '');
  369. }
  370. }
  371. }
  372. },
  373. /**
  374. * Overload for COMMANDS.list
  375. * @method COMMANDS.insertorderedlist
  376. * @static
  377. * @param {String} cmd The command executed: list, ul
  378. */
  379. insertunorderedlist: function(cmd) {
  380. this.command('list', 'ul');
  381. },
  382. /**
  383. * Overload for COMMANDS.list
  384. * @method COMMANDS.insertunorderedlist
  385. * @static
  386. * @param {String} cmd The command executed: list, ol
  387. */
  388. insertorderedlist: function(cmd) {
  389. this.command('list', 'ol');
  390. },
  391. /**
  392. * Noramlizes lists creation/destruction for IE. All others pass through to native calls
  393. * @method COMMANDS.list
  394. * @static
  395. * @param {String} cmd The command executed: list (not used)
  396. * @param {String} tag The tag to deal with
  397. */
  398. list: function(cmd, tag) {
  399. var inst = this.getInstance(), html,
  400. DIR = 'dir', cls = 'yui3-touched',
  401. dir, range, div, elm, n, str, s, par, list, lis,
  402. useP = (inst.host.editorPara ? true : false),
  403. sel = new inst.Selection();
  404. cmd = 'insert' + ((tag === 'ul') ? 'un' : '') + 'orderedlist';
  405. if (Y.UA.ie && !sel.isCollapsed) {
  406. range = sel._selection;
  407. html = range.htmlText;
  408. div = inst.Node.create(html);
  409. if (div.test('li') || div.one('li')) {
  410. this._command(cmd, null);
  411. return;
  412. }
  413. if (div.test(tag)) {
  414. elm = range.item ? range.item(0) : range.parentElement();
  415. n = inst.one(elm);
  416. lis = n.all('li');
  417. str = '<div>';
  418. lis.each(function(l) {
  419. if (useP) {
  420. str += '<p>' + l.get('innerHTML') + '</p>';
  421. } else {
  422. str += l.get('innerHTML') + '<br>';
  423. }
  424. });
  425. str += '</div>';
  426. s = inst.Node.create(str);
  427. if (n.get('parentNode').test('div')) {
  428. n = n.get('parentNode');
  429. }
  430. if (n && n.hasAttribute(DIR)) {
  431. if (useP) {
  432. s.all('p').setAttribute(DIR, n.getAttribute(DIR));
  433. } else {
  434. s.setAttribute(DIR, n.getAttribute(DIR));
  435. }
  436. }
  437. if (useP) {
  438. n.replace(s.get('innerHTML'));
  439. } else {
  440. n.replace(s);
  441. }
  442. if (range.moveToElementText) {
  443. range.moveToElementText(s._node);
  444. }
  445. range.select();
  446. } else {
  447. par = Y.one(range.parentElement());
  448. if (!par.test(inst.Selection.BLOCKS)) {
  449. par = par.ancestor(inst.Selection.BLOCKS);
  450. }
  451. if (par) {
  452. if (par.hasAttribute(DIR)) {
  453. dir = par.getAttribute(DIR);
  454. }
  455. }
  456. if (html.indexOf('<br>') > -1) {
  457. html = html.split(/<br>/i);
  458. } else {
  459. var tmp = inst.Node.create(html),
  460. ps = tmp.all('p');
  461. if (ps.size()) {
  462. html = [];
  463. ps.each(function(n) {
  464. html.push(n.get('innerHTML'));
  465. });
  466. } else {
  467. html = [html];
  468. }
  469. }
  470. list = '<' + tag + ' id="ie-list">';
  471. Y.each(html, function(v) {
  472. var a = inst.Node.create(v);
  473. if (a.test('p')) {
  474. if (a.hasAttribute(DIR)) {
  475. dir = a.getAttribute(DIR);
  476. }
  477. v = a.get('innerHTML');
  478. }
  479. list += '<li>' + v + '</li>';
  480. });
  481. list += '</' + tag + '>';
  482. range.pasteHTML(list);
  483. elm = inst.config.doc.getElementById('ie-list');
  484. elm.id = '';
  485. if (dir) {
  486. elm.setAttribute(DIR, dir);
  487. }
  488. if (range.moveToElementText) {
  489. range.moveToElementText(elm);
  490. }
  491. range.select();
  492. }
  493. } else if (Y.UA.ie) {
  494. par = inst.one(sel._selection.parentElement());
  495. if (par.test('p')) {
  496. if (par && par.hasAttribute(DIR)) {
  497. dir = par.getAttribute(DIR);
  498. }
  499. html = Y.Selection.getText(par);
  500. if (html === '') {
  501. var sdir = '';
  502. if (dir) {
  503. sdir = ' dir="' + dir + '"';
  504. }
  505. list = inst.Node.create(Y.Lang.sub('<{tag}{dir}><li></li></{tag}>', { tag: tag, dir: sdir }));
  506. par.replace(list);
  507. sel.selectNode(list.one('li'));
  508. } else {
  509. this._command(cmd, null);
  510. }
  511. } else {
  512. this._command(cmd, null);
  513. }
  514. } else {
  515. inst.all(tag).addClass(cls);
  516. if (sel.anchorNode.test(inst.Selection.BLOCKS)) {
  517. par = sel.anchorNode;
  518. } else {
  519. par = sel.anchorNode.ancestor(inst.Selection.BLOCKS);
  520. }
  521. if (!par) { //No parent, find the first block under the anchorNode
  522. par = sel.anchorNode.one(inst.Selection.BLOCKS);
  523. }
  524. if (par && par.hasAttribute(DIR)) {
  525. dir = par.getAttribute(DIR);
  526. }
  527. if (par && par.test(tag)) {
  528. html = inst.Node.create('<div/>');
  529. elm = par.all('li');
  530. elm.each(function(h) {
  531. if (useP) {
  532. html.append('<p>' + h.get('innerHTML') + '</p>');
  533. } else {
  534. html.append(h.get('innerHTML') + '<br>');
  535. }
  536. });
  537. if (dir) {
  538. if (useP) {
  539. html.all('p').setAttribute(DIR, dir);
  540. } else {
  541. html.setAttribute(DIR, dir);
  542. }
  543. }
  544. if (useP) {
  545. par.replace(html.get('innerHTML'));
  546. } else {
  547. par.replace(html);
  548. }
  549. sel.selectNode(html.get('firstChild'));
  550. } else {
  551. this._command(cmd, null);
  552. }
  553. list = inst.all(tag);
  554. if (dir) {
  555. if (list.size()) {
  556. //Changed to a List
  557. list.each(function(n) {
  558. if (!n.hasClass(cls)) {
  559. n.setAttribute(DIR, dir);
  560. }
  561. });
  562. }
  563. }
  564. list.removeClass(cls);
  565. }
  566. },
  567. /**
  568. * Noramlizes alignment for Webkit Browsers
  569. * @method COMMANDS.justify
  570. * @static
  571. * @param {String} cmd The command executed: justify (not used)
  572. * @param {String} val The actual command from the justify{center,all,left,right} stubs
  573. */
  574. justify: function(cmd, val) {
  575. if (Y.UA.webkit) {
  576. var inst = this.getInstance(),
  577. sel = new inst.Selection(),
  578. aNode = sel.anchorNode;
  579. var bgColor = aNode.getStyle('backgroundColor');
  580. this._command(val);
  581. sel = new inst.Selection();
  582. if (sel.anchorNode.test('div')) {
  583. var html = '<span>' + sel.anchorNode.get('innerHTML') + '</span>';
  584. sel.anchorNode.set('innerHTML', html);
  585. sel.anchorNode.one('span').setStyle('backgroundColor', bgColor);
  586. sel.selectNode(sel.anchorNode.one('span'));
  587. }
  588. } else {
  589. this._command(val);
  590. }
  591. },
  592. /**
  593. * Override method for COMMANDS.justify
  594. * @method COMMANDS.justifycenter
  595. * @static
  596. */
  597. justifycenter: function(cmd) {
  598. this.command('justify', 'justifycenter');
  599. },
  600. /**
  601. * Override method for COMMANDS.justify
  602. * @method COMMANDS.justifyleft
  603. * @static
  604. */
  605. justifyleft: function(cmd) {
  606. this.command('justify', 'justifyleft');
  607. },
  608. /**
  609. * Override method for COMMANDS.justify
  610. * @method COMMANDS.justifyright
  611. * @static
  612. */
  613. justifyright: function(cmd) {
  614. this.command('justify', 'justifyright');
  615. },
  616. /**
  617. * Override method for COMMANDS.justify
  618. * @method COMMANDS.justifyfull
  619. * @static
  620. */
  621. justifyfull: function(cmd) {
  622. this.command('justify', 'justifyfull');
  623. }
  624. }
  625. });
  626. /**
  627. * This method is meant to normalize IE's in ability to exec the proper command on elements with CSS styling.
  628. * @method fixIETags
  629. * @protected
  630. * @param {String} cmd The command to execute
  631. * @param {String} tag The tag to create
  632. * @param {String} rule The rule that we are looking for.
  633. */
  634. var fixIETags = function(cmd, tag, rule) {
  635. var inst = this.getInstance(),
  636. doc = inst.config.doc,
  637. sel = doc.selection.createRange(),
  638. o = doc.queryCommandValue(cmd),
  639. html, reg, m, p, d, s, c;
  640. if (o) {
  641. html = sel.htmlText;
  642. reg = new RegExp(rule, 'g');
  643. m = html.match(reg);
  644. if (m) {
  645. html = html.replace(rule + ';', '').replace(rule, '');
  646. sel.pasteHTML('<var id="yui-ie-bs">');
  647. p = doc.getElementById('yui-ie-bs');
  648. d = doc.createElement('div');
  649. s = doc.createElement(tag);
  650. d.innerHTML = html;
  651. if (p.parentNode !== inst.config.doc.body) {
  652. p = p.parentNode;
  653. }
  654. c = d.childNodes;
  655. p.parentNode.replaceChild(s, p);
  656. Y.each(c, function(f) {
  657. s.appendChild(f);
  658. });
  659. sel.collapse();
  660. if (sel.moveToElementText) {
  661. sel.moveToElementText(s);
  662. }
  663. sel.select();
  664. }
  665. }
  666. this._command(cmd);
  667. };
  668. if (Y.UA.ie) {
  669. ExecCommand.COMMANDS.bold = function() {
  670. fixIETags.call(this, 'bold', 'b', 'FONT-WEIGHT: bold');
  671. };
  672. ExecCommand.COMMANDS.italic = function() {
  673. fixIETags.call(this, 'italic', 'i', 'FONT-STYLE: italic');
  674. };
  675. ExecCommand.COMMANDS.underline = function() {
  676. fixIETags.call(this, 'underline', 'u', 'TEXT-DECORATION: underline');
  677. };
  678. }
  679. Y.namespace('Plugin');
  680. Y.Plugin.ExecCommand = ExecCommand;
  681. }, '3.4.0' ,{skinnable:false, requires:['frame']});