[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('editor-base', function (Y, NAME) { 9 10 11 /** 12 * Base class for Editor. Handles the business logic of Editor, no GUI involved only utility methods and events. 13 * 14 * var editor = new Y.EditorBase({ 15 * content: 'Foo' 16 * }); 17 * editor.render('#demo'); 18 * 19 * @class EditorBase 20 * @extends Base 21 * @module editor 22 * @main editor 23 * @submodule editor-base 24 * @constructor 25 */ 26 27 var Lang = Y.Lang, 28 29 EditorBase = function() { 30 EditorBase.superclass.constructor.apply(this, arguments); 31 }, LAST_CHILD = ':last-child'; 32 33 Y.extend(EditorBase, Y.Base, { 34 /** 35 * Internal reference to the Y.ContentEditable instance 36 * @property frame 37 */ 38 frame: null, 39 40 initializer: function() { 41 this.publish('nodeChange', { 42 emitFacade: true, 43 bubbles: true, 44 defaultFn: this._defNodeChangeFn 45 }); 46 47 //this.plug(Y.Plugin.EditorPara); 48 }, 49 destructor: function() { 50 this.detachAll(); 51 }, 52 /** 53 * Copy certain styles from one node instance to another (used for new paragraph creation mainly) 54 * @method copyStyles 55 * @param {Node} from The Node instance to copy the styles from 56 * @param {Node} to The Node instance to copy the styles to 57 */ 58 copyStyles: function(from, to) { 59 if (from.test('a')) { 60 //Don't carry the A styles 61 return; 62 } 63 var styles = ['color', 'fontSize', 'fontFamily', 'backgroundColor', 'fontStyle' ], 64 newStyles = {}; 65 66 Y.each(styles, function(v) { 67 newStyles[v] = from.getStyle(v); 68 }); 69 if (from.ancestor('b,strong')) { 70 newStyles.fontWeight = 'bold'; 71 } 72 if (from.ancestor('u')) { 73 if (!newStyles.textDecoration) { 74 newStyles.textDecoration = 'underline'; 75 } 76 } 77 to.setStyles(newStyles); 78 }, 79 /** 80 * Holder for the selection bookmark in IE. 81 * @property _lastBookmark 82 * @private 83 */ 84 _lastBookmark: null, 85 /** 86 * Resolves the e.changedNode in the nodeChange event if it comes from the document. If 87 * the event came from the document, it will get the last child of the last child of the document 88 * and return that instead. 89 * @method _resolveChangedNode 90 * @param {Node} n The node to resolve 91 * @private 92 */ 93 _resolveChangedNode: function(n) { 94 var inst = this.getInstance(), lc, lc2, found, root = this._getRoot(), sel; 95 96 if (n && n.compareTo(root)) { 97 sel = new inst.EditorSelection(); 98 if (sel && sel.anchorNode) { 99 n = sel.anchorNode; 100 } 101 } 102 if (inst && n && n.test('html')) { 103 lc = root.one(LAST_CHILD); 104 while (!found) { 105 if (lc) { 106 lc2 = lc.one(LAST_CHILD); 107 if (lc2) { 108 lc = lc2; 109 } else { 110 found = true; 111 } 112 } else { 113 found = true; 114 } 115 } 116 if (lc) { 117 if (lc.test('br')) { 118 if (lc.previous()) { 119 lc = lc.previous(); 120 } else { 121 lc = lc.get('parentNode'); 122 } 123 } 124 if (lc) { 125 n = lc; 126 } 127 } 128 } 129 if (!n) { 130 //Fallback to make sure a node is attached to the event 131 n = root; 132 } 133 return n; 134 }, 135 /** 136 * Resolves the ROOT editor element. 137 * @method _getRoot 138 * @private 139 */ 140 _getRoot: function() { 141 return this.getInstance().EditorSelection.ROOT; 142 }, 143 /** 144 * The default handler for the nodeChange event. 145 * @method _defNodeChangeFn 146 * @param {Event} e The event 147 * @private 148 */ 149 _defNodeChangeFn: function(e) { 150 var startTime = (new Date()).getTime(), 151 inst = this.getInstance(), sel, 152 changed, endTime, 153 cmds = {}, family, fsize, classes = [], 154 fColor = '', bColor = '', bq, 155 normal = false, 156 root = this._getRoot(); 157 158 if (Y.UA.ie && Y.UA.ie < 11) { 159 try { 160 sel = inst.config.doc.selection.createRange(); 161 if (sel.getBookmark) { 162 this._lastBookmark = sel.getBookmark(); 163 } 164 } catch (ie) {} 165 } 166 167 e.changedNode = this._resolveChangedNode(e.changedNode); 168 169 170 /* 171 * @TODO 172 * This whole method needs to be fixed and made more dynamic. 173 * Maybe static functions for the e.changeType and an object bag 174 * to walk through and filter to pass off the event to before firing.. 175 */ 176 177 switch (e.changedType) { 178 case 'tab': 179 if (!e.changedNode.test('li, li *') && !e.changedEvent.shiftKey) { 180 e.changedEvent.frameEvent.preventDefault(); 181 Y.log('Overriding TAB key to insert HTML: HALTING', 'info', 'editor'); 182 if (Y.UA.webkit) { 183 this.execCommand('inserttext', '\t'); 184 } else if (Y.UA.gecko) { 185 this.frame.exec._command('inserthtml', EditorBase.TABKEY); 186 } else if (Y.UA.ie) { 187 this.execCommand('inserthtml', EditorBase.TABKEY); 188 } 189 } 190 break; 191 case 'backspace-up': 192 // Fixes #2531090 - Joins text node strings so they become one for bidi 193 if (Y.UA.webkit && e.changedNode) { 194 e.changedNode.set('innerHTML', e.changedNode.get('innerHTML')); 195 } 196 break; 197 } 198 if (Y.UA.webkit && e.commands && (e.commands.indent || e.commands.outdent)) { 199 /* 200 * When executing execCommand 'indent or 'outdent' Webkit applies 201 * a class to the BLOCKQUOTE that adds left/right margin to it 202 * This strips that style so it is just a normal BLOCKQUOTE 203 */ 204 bq = root.all('.webkit-indent-blockquote, blockquote'); 205 if (bq.size()) { 206 bq.setStyle('margin', ''); 207 } 208 } 209 210 changed = this.getDomPath(e.changedNode, false); 211 212 if (e.commands) { 213 cmds = e.commands; 214 } 215 216 217 Y.each(changed, function(el) { 218 var tag = el.tagName.toLowerCase(), 219 cmd = EditorBase.TAG2CMD[tag], s, 220 n, family2, cls, bColor2; 221 222 if (cmd) { 223 cmds[cmd] = 1; 224 } 225 226 //Bold and Italic styles 227 s = el.currentStyle || el.style; 228 229 if ((''+s.fontWeight) === 'normal') { 230 normal = true; 231 } 232 if ((''+s.fontWeight) === 'bold') { //Cast this to a string 233 cmds.bold = 1; 234 } 235 if (Y.UA.ie) { 236 if (s.fontWeight > 400) { 237 cmds.bold = 1; 238 } 239 } 240 if (s.fontStyle === 'italic') { 241 cmds.italic = 1; 242 } 243 244 if (s.textDecoration.indexOf('underline') > -1) { 245 cmds.underline = 1; 246 } 247 if (s.textDecoration.indexOf('line-through') > -1) { 248 cmds.strikethrough = 1; 249 } 250 251 n = inst.one(el); 252 if (n.getStyle('fontFamily')) { 253 family2 = n.getStyle('fontFamily').split(',')[0].toLowerCase(); 254 if (family2) { 255 family = family2; 256 } 257 if (family) { 258 family = family.replace(/'/g, '').replace(/"/g, ''); 259 } 260 } 261 262 fsize = EditorBase.NORMALIZE_FONTSIZE(n); 263 264 265 cls = el.className.split(' '); 266 Y.each(cls, function(v) { 267 if (v !== '' && (v.substr(0, 4) !== 'yui_')) { 268 classes.push(v); 269 } 270 }); 271 272 fColor = EditorBase.FILTER_RGB(n.getStyle('color')); 273 bColor2 = EditorBase.FILTER_RGB(s.backgroundColor); 274 if (bColor2 !== 'transparent') { 275 if (bColor2 !== '') { 276 bColor = bColor2; 277 } 278 } 279 280 }); 281 282 if (normal) { 283 delete cmds.bold; 284 delete cmds.italic; 285 } 286 287 e.dompath = inst.all(changed); 288 e.classNames = classes; 289 e.commands = cmds; 290 291 //TODO Dont' like this, not dynamic enough.. 292 if (!e.fontFamily) { 293 e.fontFamily = family; 294 } 295 if (!e.fontSize) { 296 e.fontSize = fsize; 297 } 298 if (!e.fontColor) { 299 e.fontColor = fColor; 300 } 301 if (!e.backgroundColor) { 302 e.backgroundColor = bColor; 303 } 304 305 endTime = (new Date()).getTime(); 306 Y.log('_defNodeChangeTimer 2: ' + (endTime - startTime) + 'ms', 'info', 'selection'); 307 }, 308 /** 309 * Walk the dom tree from this node up to body, returning a reversed array of parents. 310 * @method getDomPath 311 * @param {Node} node The Node to start from 312 */ 313 getDomPath: function(node, nodeList) { 314 var domPath = [], domNode, rootNode, 315 root = this._getRoot(), 316 inst = this.frame.getInstance(); 317 318 domNode = inst.Node.getDOMNode(node); 319 rootNode = inst.Node.getDOMNode(root); 320 //return inst.all(domNode); 321 322 while (domNode !== null) { 323 324 if ((domNode === inst.config.doc.documentElement) || (domNode === inst.config.doc) || !domNode.tagName) { 325 domNode = null; 326 break; 327 } 328 329 if (!inst.DOM.inDoc(domNode)) { 330 domNode = null; 331 break; 332 } 333 334 //Check to see if we get el.nodeName and nodeType 335 if (domNode.nodeName && domNode.nodeType && (domNode.nodeType === 1)) { 336 domPath.push(domNode); 337 } 338 339 if (domNode === rootNode) { 340 domNode = null; 341 break; 342 } 343 344 domNode = domNode.parentNode; 345 } 346 347 /*{{{ Using Node 348 while (node !== null) { 349 if (node.test('html') || node.test('doc') || !node.get('tagName')) { 350 node = null; 351 break; 352 } 353 if (!node.inDoc()) { 354 node = null; 355 break; 356 } 357 //Check to see if we get el.nodeName and nodeType 358 if (node.get('nodeName') && node.get('nodeType') && (node.get('nodeType') == 1)) { 359 domPath.push(inst.Node.getDOMNode(node)); 360 } 361 362 if (node.test('body')) { 363 node = null; 364 break; 365 } 366 367 node = node.get('parentNode'); 368 } 369 }}}*/ 370 371 if (domPath.length === 0) { 372 domPath[0] = inst.config.doc.body; 373 } 374 375 if (nodeList) { 376 return inst.all(domPath.reverse()); 377 } else { 378 return domPath.reverse(); 379 } 380 381 }, 382 /** 383 * After frame ready, bind mousedown & keyup listeners 384 * @method _afterFrameReady 385 * @private 386 */ 387 _afterFrameReady: function() { 388 var inst = this.frame.getInstance(); 389 390 this.frame.on('dom:mouseup', Y.bind(this._onFrameMouseUp, this)); 391 this.frame.on('dom:mousedown', Y.bind(this._onFrameMouseDown, this)); 392 this.frame.on('dom:keydown', Y.bind(this._onFrameKeyDown, this)); 393 394 if (Y.UA.ie && Y.UA.ie < 11) { 395 this.frame.on('dom:activate', Y.bind(this._onFrameActivate, this)); 396 this.frame.on('dom:beforedeactivate', Y.bind(this._beforeFrameDeactivate, this)); 397 } 398 this.frame.on('dom:keyup', Y.bind(this._onFrameKeyUp, this)); 399 this.frame.on('dom:keypress', Y.bind(this._onFrameKeyPress, this)); 400 this.frame.on('dom:paste', Y.bind(this._onPaste, this)); 401 402 inst.EditorSelection.filter(); 403 this.fire('ready'); 404 }, 405 /** 406 * Caches the current cursor position in IE. 407 * @method _beforeFrameDeactivate 408 * @private 409 */ 410 _beforeFrameDeactivate: function(e) { 411 if (e.frameTarget.test('html')) { //Means it came from a scrollbar 412 return; 413 } 414 var inst = this.getInstance(), 415 sel = inst.config.doc.selection.createRange(); 416 417 if (sel.compareEndPoints && !sel.compareEndPoints('StartToEnd', sel)) { 418 sel.pasteHTML('<var id="yui-ie-cursor">'); 419 } 420 }, 421 /** 422 * Moves the cached selection bookmark back so IE can place the cursor in the right place. 423 * @method _onFrameActivate 424 * @private 425 */ 426 _onFrameActivate: function(e) { 427 if (e.frameTarget.test('html')) { //Means it came from a scrollbar 428 return; 429 } 430 var inst = this.getInstance(), 431 sel = new inst.EditorSelection(), 432 range = sel.createRange(), 433 root = this._getRoot(), 434 cur = root.all('#yui-ie-cursor'); 435 436 if (cur.size()) { 437 cur.each(function(n) { 438 n.set('id', ''); 439 if (range.moveToElementText) { 440 try { 441 range.moveToElementText(n._node); 442 var moved = range.move('character', -1); 443 if (moved === -1) { //Only move up if we actually moved back. 444 range.move('character', 1); 445 } 446 range.select(); 447 range.text = ''; 448 } catch (e) {} 449 } 450 n.remove(); 451 }); 452 } 453 }, 454 /** 455 * Fires nodeChange event 456 * @method _onPaste 457 * @private 458 */ 459 _onPaste: function(e) { 460 this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'paste', changedEvent: e.frameEvent }); 461 }, 462 /** 463 * Fires nodeChange event 464 * @method _onFrameMouseUp 465 * @private 466 */ 467 _onFrameMouseUp: function(e) { 468 this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'mouseup', changedEvent: e.frameEvent }); 469 }, 470 /** 471 * Fires nodeChange event 472 * @method _onFrameMouseDown 473 * @private 474 */ 475 _onFrameMouseDown: function(e) { 476 this.fire('nodeChange', { changedNode: e.frameTarget, changedType: 'mousedown', changedEvent: e.frameEvent }); 477 }, 478 /** 479 * Caches a copy of the selection for key events. Only creating the selection on keydown 480 * @property _currentSelection 481 * @private 482 */ 483 _currentSelection: null, 484 /** 485 * Holds the timer for selection clearing 486 * @property _currentSelectionTimer 487 * @private 488 */ 489 _currentSelectionTimer: null, 490 /** 491 * Flag to determine if we can clear the selection or not. 492 * @property _currentSelectionClear 493 * @private 494 */ 495 _currentSelectionClear: null, 496 /** 497 * Fires nodeChange event 498 * @method _onFrameKeyDown 499 * @private 500 */ 501 _onFrameKeyDown: function(e) { 502 var inst, sel; 503 if (!this._currentSelection) { 504 if (this._currentSelectionTimer) { 505 this._currentSelectionTimer.cancel(); 506 } 507 this._currentSelectionTimer = Y.later(850, this, function() { 508 this._currentSelectionClear = true; 509 }); 510 511 inst = this.frame.getInstance(); 512 sel = new inst.EditorSelection(e); 513 514 this._currentSelection = sel; 515 } else { 516 sel = this._currentSelection; 517 } 518 519 inst = this.frame.getInstance(); 520 sel = new inst.EditorSelection(); 521 522 this._currentSelection = sel; 523 524 if (sel && sel.anchorNode) { 525 this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keydown', changedEvent: e.frameEvent }); 526 if (EditorBase.NC_KEYS[e.keyCode]) { 527 this.fire('nodeChange', { 528 changedNode: sel.anchorNode, 529 changedType: EditorBase.NC_KEYS[e.keyCode], 530 changedEvent: e.frameEvent 531 }); 532 this.fire('nodeChange', { 533 changedNode: sel.anchorNode, 534 changedType: EditorBase.NC_KEYS[e.keyCode] + '-down', 535 changedEvent: e.frameEvent 536 }); 537 } 538 } 539 }, 540 /** 541 * Fires nodeChange event 542 * @method _onFrameKeyPress 543 * @private 544 */ 545 _onFrameKeyPress: function(e) { 546 var sel = this._currentSelection; 547 548 if (sel && sel.anchorNode) { 549 this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keypress', changedEvent: e.frameEvent }); 550 if (EditorBase.NC_KEYS[e.keyCode]) { 551 this.fire('nodeChange', { 552 changedNode: sel.anchorNode, 553 changedType: EditorBase.NC_KEYS[e.keyCode] + '-press', 554 changedEvent: e.frameEvent 555 }); 556 } 557 } 558 }, 559 /** 560 * Fires nodeChange event for keyup on specific keys 561 * @method _onFrameKeyUp 562 * @private 563 */ 564 _onFrameKeyUp: function(e) { 565 var inst = this.frame.getInstance(), 566 sel = new inst.EditorSelection(e); 567 568 if (sel && sel.anchorNode) { 569 this.fire('nodeChange', { changedNode: sel.anchorNode, changedType: 'keyup', selection: sel, changedEvent: e.frameEvent }); 570 if (EditorBase.NC_KEYS[e.keyCode]) { 571 this.fire('nodeChange', { 572 changedNode: sel.anchorNode, 573 changedType: EditorBase.NC_KEYS[e.keyCode] + '-up', 574 selection: sel, 575 changedEvent: e.frameEvent 576 }); 577 } 578 } 579 if (this._currentSelectionClear) { 580 this._currentSelectionClear = this._currentSelection = null; 581 } 582 }, 583 /** 584 * Validates linkedcss property 585 * 586 * @method _validateLinkedCSS 587 * @private 588 */ 589 _validateLinkedCSS: function(value) { 590 return Lang.isString(value) || Lang.isArray(value); 591 }, 592 /** 593 * Pass through to the frame.execCommand method 594 * @method execCommand 595 * @param {String} cmd The command to pass: inserthtml, insertimage, bold 596 * @param {String} val The optional value of the command: Helvetica 597 * @return {Node/NodeList} The Node or Nodelist affected by the command. Only returns on override commands, not browser defined commands. 598 */ 599 execCommand: function(cmd, val) { 600 var ret = this.frame.execCommand(cmd, val), 601 inst = this.frame.getInstance(), 602 sel = new inst.EditorSelection(), cmds = {}, 603 e = { changedNode: sel.anchorNode, changedType: 'execcommand', nodes: ret }; 604 605 switch (cmd) { 606 case 'forecolor': 607 e.fontColor = val; 608 break; 609 case 'backcolor': 610 e.backgroundColor = val; 611 break; 612 case 'fontsize': 613 e.fontSize = val; 614 break; 615 case 'fontname': 616 e.fontFamily = val; 617 break; 618 } 619 620 cmds[cmd] = 1; 621 e.commands = cmds; 622 623 this.fire('nodeChange', e); 624 625 return ret; 626 }, 627 /** 628 * Get the YUI instance of the frame 629 * @method getInstance 630 * @return {YUI} The YUI instance bound to the frame. 631 */ 632 getInstance: function() { 633 return this.frame.getInstance(); 634 }, 635 /** 636 * Renders the Y.ContentEditable to the passed node. 637 * @method render 638 * @param {Selector/HTMLElement/Node} node The node to append the Editor to 639 * @return {EditorBase} 640 * @chainable 641 */ 642 render: function(node) { 643 var frame = this.frame; 644 645 if (!frame) { 646 this.plug(Y.Plugin.Frame, { 647 designMode: true, 648 title: EditorBase.STRINGS.title, 649 use: EditorBase.USE, 650 dir: this.get('dir'), 651 extracss: this.get('extracss'), 652 linkedcss: this.get('linkedcss'), 653 defaultblock: this.get('defaultblock') 654 }); 655 656 frame = this.frame; 657 } 658 659 if (!frame.hasPlugin('exec')) { 660 frame.plug(Y.Plugin.ExecCommand); 661 } 662 663 frame.after('ready', Y.bind(this._afterFrameReady, this)); 664 665 frame.addTarget(this); 666 667 frame.set('content', this.get('content')); 668 669 frame.render(node); 670 671 return this; 672 }, 673 /** 674 * Focus the contentWindow of the iframe 675 * @method focus 676 * @param {Function} fn Callback function to execute after focus happens 677 * @return {EditorBase} 678 * @chainable 679 */ 680 focus: function(fn) { 681 this.frame.focus(fn); 682 return this; 683 }, 684 /** 685 * Handles the showing of the Editor instance. Currently only handles the iframe 686 * @method show 687 * @return {EditorBase} 688 * @chainable 689 */ 690 show: function() { 691 this.frame.show(); 692 return this; 693 }, 694 /** 695 * Handles the hiding of the Editor instance. Currently only handles the iframe 696 * @method hide 697 * @return {EditorBase} 698 * @chainable 699 */ 700 hide: function() { 701 this.frame.hide(); 702 return this; 703 }, 704 /** 705 * (Un)Filters the content of the Editor, cleaning YUI related code. //TODO better filtering 706 * @method getContent 707 * @return {String} The filtered content of the Editor 708 */ 709 getContent: function() { 710 var html = '', inst = this.getInstance(); 711 if (inst && inst.EditorSelection) { 712 html = inst.EditorSelection.unfilter(); 713 } 714 //Removing the _yuid from the objects in IE 715 html = html.replace(/ _yuid="([^>]*)"/g, ''); 716 return html; 717 } 718 }, { 719 /** 720 * @static 721 * @method NORMALIZE_FONTSIZE 722 * @description Pulls the fontSize from a node, then checks for string values (x-large, x-small) 723 * and converts them to pixel sizes. If the parsed size is different from the original, it calls 724 * node.setStyle to update the node with a pixel size for normalization. 725 */ 726 NORMALIZE_FONTSIZE: function(n) { 727 var size = n.getStyle('fontSize'), oSize = size; 728 729 switch (size) { 730 case '-webkit-xxx-large': 731 size = '48px'; 732 break; 733 case 'xx-large': 734 size = '32px'; 735 break; 736 case 'x-large': 737 size = '24px'; 738 break; 739 case 'large': 740 size = '18px'; 741 break; 742 case 'medium': 743 size = '16px'; 744 break; 745 case 'small': 746 size = '13px'; 747 break; 748 case 'x-small': 749 size = '10px'; 750 break; 751 } 752 if (oSize !== size) { 753 n.setStyle('fontSize', size); 754 } 755 return size; 756 }, 757 /** 758 * @static 759 * @property TABKEY 760 * @description The HTML markup to use for the tabkey 761 */ 762 TABKEY: '<span class="tab"> </span>', 763 /** 764 * @static 765 * @method FILTER_RGB 766 * @param String css The CSS string containing rgb(#,#,#); 767 * @description Converts an RGB color string to a hex color, example: rgb(0, 255, 0) converts to #00ff00 768 * @return String 769 */ 770 FILTER_RGB: function(css) { 771 if (css.toLowerCase().indexOf('rgb') !== -1) { 772 var exp = new RegExp("(.*?)rgb\\s*?\\(\\s*?([0-9]+).*?,\\s*?([0-9]+).*?,\\s*?([0-9]+).*?\\)(.*?)", "gi"), 773 rgb = css.replace(exp, "$1,$2,$3,$4,$5").split(','), 774 r, g, b; 775 776 if (rgb.length === 5) { 777 r = parseInt(rgb[1], 10).toString(16); 778 g = parseInt(rgb[2], 10).toString(16); 779 b = parseInt(rgb[3], 10).toString(16); 780 781 r = r.length === 1 ? '0' + r : r; 782 g = g.length === 1 ? '0' + g : g; 783 b = b.length === 1 ? '0' + b : b; 784 785 css = "#" + r + g + b; 786 } 787 } 788 return css; 789 }, 790 /** 791 * @static 792 * @property TAG2CMD 793 * @description A hash table of tags to their execcomand's 794 */ 795 TAG2CMD: { 796 'b': 'bold', 797 'strong': 'bold', 798 'i': 'italic', 799 'em': 'italic', 800 'u': 'underline', 801 'sup': 'superscript', 802 'sub': 'subscript', 803 'img': 'insertimage', 804 'a' : 'createlink', 805 'ul' : 'insertunorderedlist', 806 'ol' : 'insertorderedlist' 807 }, 808 /** 809 * Hash table of keys to fire a nodeChange event for. 810 * @static 811 * @property NC_KEYS 812 * @type Object 813 */ 814 NC_KEYS: { 815 8: 'backspace', 816 9: 'tab', 817 13: 'enter', 818 32: 'space', 819 33: 'pageup', 820 34: 'pagedown', 821 35: 'end', 822 36: 'home', 823 37: 'left', 824 38: 'up', 825 39: 'right', 826 40: 'down', 827 46: 'delete' 828 }, 829 /** 830 * The default modules to use inside the Frame 831 * @static 832 * @property USE 833 * @type Array 834 */ 835 USE: ['node', 'selector-css3', 'editor-selection', 'stylesheet'], 836 /** 837 * The Class Name: editorBase 838 * @static 839 * @property NAME 840 */ 841 NAME: 'editorBase', 842 /** 843 * Editor Strings. By default contains only the `title` property for the 844 * Title of frame document (default "Rich Text Editor"). 845 * 846 * @static 847 * @property STRINGS 848 */ 849 STRINGS: { 850 title: 'Rich Text Editor' 851 }, 852 ATTRS: { 853 /** 854 * The content to load into the Editor Frame 855 * @attribute content 856 */ 857 content: { 858 validator: Lang.isString, 859 value: '<br class="yui-cursor">', 860 setter: function(str) { 861 if (str.substr(0, 1) === "\n") { 862 Y.log('Stripping first carriage return from content before injecting', 'warn', 'editor'); 863 str = str.substr(1); 864 } 865 if (str === '') { 866 str = '<br class="yui-cursor">'; 867 } 868 if (str === ' ') { 869 if (Y.UA.gecko) { 870 str = '<br class="yui-cursor">'; 871 } 872 } 873 return this.frame.set('content', str); 874 }, 875 getter: function() { 876 return this.frame.get('content'); 877 } 878 }, 879 /** 880 * The value of the dir attribute on the HTML element of the frame. Default: ltr 881 * @attribute dir 882 */ 883 dir: { 884 validator: Lang.isString, 885 writeOnce: true, 886 value: 'ltr' 887 }, 888 /** 889 * @attribute linkedcss 890 * @description An array of url's to external linked style sheets 891 * @type String|Array 892 */ 893 linkedcss: { 894 validator: '_validateLinkedCSS', 895 value: '', 896 setter: function(css) { 897 if (this.frame) { 898 this.frame.set('linkedcss', css); 899 } 900 return css; 901 } 902 }, 903 /** 904 * @attribute extracss 905 * @description A string of CSS to add to the Head of the Editor 906 * @type String 907 */ 908 extracss: { 909 validator: Lang.isString, 910 value: '', 911 setter: function(css) { 912 if (this.frame) { 913 this.frame.set('extracss', css); 914 } 915 return css; 916 } 917 }, 918 /** 919 * @attribute defaultblock 920 * @description The default tag to use for block level items, defaults to: p 921 * @type String 922 */ 923 defaultblock: { 924 validator: Lang.isString, 925 value: 'p' 926 } 927 } 928 }); 929 930 Y.EditorBase = EditorBase; 931 932 /** 933 * @event nodeChange 934 * @description Fired from several mouse/key/paste event points. 935 * @param {EventFacade} event An Event Facade object with the following specific properties added: 936 * <dl> 937 * <dt>changedEvent</dt><dd>The event that caused the nodeChange</dd> 938 * <dt>changedNode</dt><dd>The node that was interacted with</dd> 939 * <dt>changedType</dt><dd>The type of change: mousedown, mouseup, right, left, backspace, tab, enter, etc..</dd> 940 * <dt>commands</dt><dd>The list of execCommands that belong to this change and the dompath that's associated with the changedNode</dd> 941 * <dt>classNames</dt><dd>An array of classNames that are applied to the changedNode and all of it's parents</dd> 942 * <dt>dompath</dt><dd>A sorted array of node instances that make up the DOM path from the changedNode to body.</dd> 943 * <dt>backgroundColor</dt><dd>The cascaded backgroundColor of the changedNode</dd> 944 * <dt>fontColor</dt><dd>The cascaded fontColor of the changedNode</dd> 945 * <dt>fontFamily</dt><dd>The cascaded fontFamily of the changedNode</dd> 946 * <dt>fontSize</dt><dd>The cascaded fontSize of the changedNode</dd> 947 * </dl> 948 */ 949 950 /** 951 * @event ready 952 * @description Fired after the frame is ready. 953 * @param {EventFacade} event An Event Facade object. 954 */ 955 956 957 958 959 960 }, '3.17.2', {"requires": ["base", "frame", "node", "exec-command", "editor-selection"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |