[ 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('content-editable', function (Y, NAME) { 9 10 /*jshint maxlen: 500 */ 11 /** 12 * Creates a component to work with an elemment. 13 * @class ContentEditable 14 * @for ContentEditable 15 * @extends Y.Plugin.Base 16 * @constructor 17 * @module editor 18 * @submodule content-editable 19 */ 20 21 var Lang = Y.Lang, 22 YNode = Y.Node, 23 24 EVENT_CONTENT_READY = 'contentready', 25 EVENT_READY = 'ready', 26 27 TAG_PARAGRAPH = 'p', 28 29 BLUR = 'blur', 30 CONTAINER = 'container', 31 CONTENT_EDITABLE = 'contentEditable', 32 EMPTY = '', 33 FOCUS = 'focus', 34 HOST = 'host', 35 INNER_HTML = 'innerHTML', 36 KEY = 'key', 37 PARENT_NODE = 'parentNode', 38 PASTE = 'paste', 39 TEXT = 'Text', 40 USE = 'use', 41 42 ContentEditable = function() { 43 ContentEditable.superclass.constructor.apply(this, arguments); 44 }; 45 46 Y.extend(ContentEditable, Y.Plugin.Base, { 47 48 /** 49 * Internal reference set when render is called. 50 * @private 51 * @property _rendered 52 * @type Boolean 53 */ 54 _rendered: null, 55 56 /** 57 * Internal reference to the YUI instance bound to the element 58 * @private 59 * @property _instance 60 * @type YUI 61 */ 62 _instance: null, 63 64 /** 65 * Initializes the ContentEditable instance 66 * @protected 67 * @method initializer 68 */ 69 initializer: function() { 70 var host = this.get(HOST); 71 72 if (host) { 73 host.frame = this; 74 } 75 76 this._eventHandles = []; 77 78 this.publish(EVENT_READY, { 79 emitFacade: true, 80 defaultFn: this._defReadyFn 81 }); 82 }, 83 84 /** 85 * Destroys the instance. 86 * @protected 87 * @method destructor 88 */ 89 destructor: function() { 90 new Y.EventHandle(this._eventHandles).detach(); 91 92 this._container.removeAttribute(CONTENT_EDITABLE); 93 }, 94 95 /** 96 * Generic handler for all DOM events fired by the Editor container. This handler 97 * takes the current EventFacade and augments it to fire on the ContentEditable host. It adds two new properties 98 * to the EventFacade called frameX and frameY which adds the scroll and xy position of the ContentEditable element 99 * to the original pageX and pageY of the event so external nodes can be positioned over the element. 100 * In case of ContentEditable element these will be equal to pageX and pageY of the container. 101 * @private 102 * @method _onDomEvent 103 * @param {EventFacade} e 104 */ 105 _onDomEvent: function(e) { 106 var xy; 107 108 e.frameX = e.frameY = 0; 109 110 if (e.pageX > 0 || e.pageY > 0) { 111 if (e.type.substring(0, 3) !== KEY) { 112 xy = this._container.getXY(); 113 114 e.frameX = xy[0]; 115 e.frameY = xy[1]; 116 } 117 } 118 119 e.frameTarget = e.target; 120 e.frameCurrentTarget = e.currentTarget; 121 e.frameEvent = e; 122 123 this.fire('dom:' + e.type, e); 124 }, 125 126 /** 127 * Simple pass thru handler for the paste event so we can do content cleanup 128 * @private 129 * @method _DOMPaste 130 * @param {EventFacade} e 131 */ 132 _DOMPaste: function(e) { 133 var inst = this.getInstance(), 134 data = EMPTY, win = inst.config.win; 135 136 if (e._event.originalTarget) { 137 data = e._event.originalTarget; 138 } 139 140 if (e._event.clipboardData) { 141 data = e._event.clipboardData.getData(TEXT); 142 } 143 144 if (win.clipboardData) { 145 data = win.clipboardData.getData(TEXT); 146 147 if (data === EMPTY) { // Could be empty, or failed 148 // Verify failure 149 if (!win.clipboardData.setData(TEXT, data)) { 150 data = null; 151 } 152 } 153 } 154 155 e.frameTarget = e.target; 156 e.frameCurrentTarget = e.currentTarget; 157 e.frameEvent = e; 158 159 if (data) { 160 e.clipboardData = { 161 data: data, 162 getData: function() { 163 return data; 164 } 165 }; 166 } else { 167 Y.log('Failed to collect clipboard data', 'warn', 'contenteditable'); 168 169 e.clipboardData = null; 170 } 171 172 this.fire('dom:paste', e); 173 }, 174 175 /** 176 * Binds DOM events and fires the ready event 177 * @private 178 * @method _defReadyFn 179 */ 180 _defReadyFn: function() { 181 var inst = this.getInstance(), 182 container = this.get(CONTAINER); 183 184 Y.each( 185 ContentEditable.DOM_EVENTS, 186 function(value, key) { 187 var fn = Y.bind(this._onDomEvent, this), 188 kfn = ((Y.UA.ie && ContentEditable.THROTTLE_TIME > 0) ? Y.throttle(fn, ContentEditable.THROTTLE_TIME) : fn); 189 190 if (!inst.Node.DOM_EVENTS[key]) { 191 inst.Node.DOM_EVENTS[key] = 1; 192 } 193 194 if (value === 1) { 195 if (key !== FOCUS && key !== BLUR && key !== PASTE) { 196 if (key.substring(0, 3) === KEY) { 197 //Throttle key events in IE 198 this._eventHandles.push(container.on(key, kfn, container)); 199 } else { 200 this._eventHandles.push(container.on(key, fn, container)); 201 } 202 } 203 } 204 }, 205 this 206 ); 207 208 inst.Node.DOM_EVENTS.paste = 1; 209 210 this._eventHandles.push( 211 container.on(PASTE, Y.bind(this._DOMPaste, this), container), 212 container.on(FOCUS, Y.bind(this._onDomEvent, this), container), 213 container.on(BLUR, Y.bind(this._onDomEvent, this), container) 214 ); 215 216 inst.__use = inst.use; 217 218 inst.use = Y.bind(this.use, this); 219 }, 220 221 /** 222 * Called once the content is available in the ContentEditable element and calls the final use call 223 * @private 224 * @method _onContentReady 225 * on the internal instance so that the modules are loaded properly. 226 */ 227 _onContentReady: function(event) { 228 if (!this._ready) { 229 this._ready = true; 230 231 var inst = this.getInstance(), 232 args = Y.clone(this.get(USE)); 233 234 this.fire(EVENT_CONTENT_READY); 235 236 Y.log('On content available', 'info', 'contenteditable'); 237 238 if (event) { 239 inst.config.doc = YNode.getDOMNode(event.target); 240 } 241 242 args.push(Y.bind(function() { 243 Y.log('Callback from final internal use call', 'info', 'contenteditable'); 244 245 if (inst.EditorSelection) { 246 inst.EditorSelection.DEFAULT_BLOCK_TAG = this.get('defaultblock'); 247 248 inst.EditorSelection.ROOT = this.get(CONTAINER); 249 } 250 251 this.fire(EVENT_READY); 252 }, this)); 253 254 Y.log('Calling use on internal instance: ' + args, 'info', 'contentEditable'); 255 256 inst.use.apply(inst, args); 257 } 258 }, 259 260 /** 261 * Retrieves defaultblock value from host attribute 262 * @private 263 * @method _getDefaultBlock 264 * @return {String} 265 */ 266 _getDefaultBlock: function() { 267 return this._getHostValue('defaultblock'); 268 }, 269 270 /** 271 * Retrieves dir value from host attribute 272 * @private 273 * @method _getDir 274 * @return {String} 275 */ 276 _getDir: function() { 277 return this._getHostValue('dir'); 278 }, 279 280 /** 281 * Retrieves extracss value from host attribute 282 * @private 283 * @method _getExtraCSS 284 * @return {String} 285 */ 286 _getExtraCSS: function() { 287 return this._getHostValue('extracss'); 288 }, 289 290 /** 291 * Get the content from the container 292 * @private 293 * @method _getHTML 294 * @param {String} html The raw HTML from the container. 295 * @return {String} 296 */ 297 _getHTML: function() { 298 var html, container; 299 300 if (this._ready) { 301 container = this.get(CONTAINER); 302 303 html = container.get(INNER_HTML); 304 } 305 306 return html; 307 }, 308 309 /** 310 * Retrieves a value from host attribute 311 * @private 312 * @method _getHostValue 313 * @param {attr} The attribute which value should be returned from the host 314 * @return {String|Object} 315 */ 316 _getHostValue: function(attr) { 317 var host = this.get(HOST); 318 319 if (host) { 320 return host.get(attr); 321 } 322 }, 323 324 /** 325 * Set the content of the container 326 * @private 327 * @method _setHTML 328 * @param {String} html The raw HTML to set to the container. 329 * @return {String} 330 */ 331 _setHTML: function(html) { 332 if (this._ready) { 333 var container = this.get(CONTAINER); 334 335 container.set(INNER_HTML, html); 336 } else { 337 //This needs to be wrapped in a contentready callback for the !_ready state 338 this.once(EVENT_CONTENT_READY, Y.bind(this._setHTML, this, html)); 339 } 340 341 return html; 342 }, 343 344 /** 345 * Sets the linked CSS on the instance. 346 * @private 347 * @method _setLinkedCSS 348 * @param {String} css The linkedcss value 349 * @return {String} 350 */ 351 _setLinkedCSS: function(css) { 352 if (this._ready) { 353 var inst = this.getInstance(); 354 inst.Get.css(css); 355 } else { 356 //This needs to be wrapped in a contentready callback for the !_ready state 357 this.once(EVENT_CONTENT_READY, Y.bind(this._setLinkedCSS, this, css)); 358 } 359 360 return css; 361 }, 362 363 /** 364 * Sets the dir (language direction) attribute on the container. 365 * @private 366 * @method _setDir 367 * @param {String} value The language direction 368 * @return {String} 369 */ 370 _setDir: function(value) { 371 var container; 372 373 if (this._ready) { 374 container = this.get(CONTAINER); 375 376 container.setAttribute('dir', value); 377 } else { 378 //This needs to be wrapped in a contentready callback for the !_ready state 379 this.once(EVENT_CONTENT_READY, Y.bind(this._setDir, this, value)); 380 } 381 382 return value; 383 }, 384 385 /** 386 * Set's the extra CSS on the instance. 387 * @private 388 * @method _setExtraCSS 389 * @param {String} css The CSS style to be set as extra css 390 * @return {String} 391 */ 392 _setExtraCSS: function(css) { 393 if (this._ready) { 394 if (css) { 395 var inst = this.getInstance(), 396 head = inst.one('head'); 397 398 if (this._extraCSSNode) { 399 this._extraCSSNode.remove(); 400 } 401 402 this._extraCSSNode = YNode.create('<style>' + css + '</style>'); 403 404 head.append(this._extraCSSNode); 405 } 406 } else { 407 //This needs to be wrapped in a contentready callback for the !_ready state 408 this.once(EVENT_CONTENT_READY, Y.bind(this._setExtraCSS, this, css)); 409 } 410 411 return css; 412 }, 413 414 /** 415 * Sets the language value on the instance. 416 * @private 417 * @method _setLang 418 * @param {String} value The language to be set 419 * @return {String} 420 */ 421 _setLang: function(value) { 422 var container; 423 424 if (this._ready) { 425 container = this.get(CONTAINER); 426 427 container.setAttribute('lang', value); 428 } else { 429 //This needs to be wrapped in a contentready callback for the !_ready state 430 this.once(EVENT_CONTENT_READY, Y.bind(this._setLang, this, value)); 431 } 432 433 return value; 434 }, 435 436 /** 437 * Called from the first YUI instance that sets up the internal instance. 438 * This loads the content into the ContentEditable element and attaches the contentready event. 439 * @private 440 * @method _instanceLoaded 441 * @param {YUI} inst The internal YUI instance bound to the ContentEditable element 442 */ 443 _instanceLoaded: function(inst) { 444 this._instance = inst; 445 446 this._onContentReady(); 447 448 var doc = this._instance.config.doc; 449 450 if (!Y.UA.ie) { 451 try { 452 //Force other browsers into non CSS styling 453 doc.execCommand('styleWithCSS', false, false); 454 doc.execCommand('insertbronreturn', false, false); 455 } catch (err) {} 456 } 457 }, 458 459 460 /** 461 * Validates linkedcss property 462 * 463 * @method _validateLinkedCSS 464 * @private 465 */ 466 _validateLinkedCSS: function(value) { 467 return Lang.isString(value) || Lang.isArray(value); 468 }, 469 470 //BEGIN PUBLIC METHODS 471 /** 472 * This is a scoped version of the normal YUI.use method & is bound to the ContentEditable element 473 * At setup, the inst.use method is mapped to this method. 474 * @method use 475 */ 476 use: function() { 477 Y.log('Calling augmented use after ready', 'info', 'contenteditable'); 478 479 var inst = this.getInstance(), 480 args = Y.Array(arguments), 481 callback = false; 482 483 if (Lang.isFunction(args[args.length - 1])) { 484 callback = args.pop(); 485 } 486 487 if (callback) { 488 args.push(function() { 489 Y.log('Internal callback from augmented use', 'info', 'contenteditable'); 490 491 callback.apply(inst, arguments); 492 }); 493 } 494 495 return inst.__use.apply(inst, args); 496 }, 497 498 /** 499 * A delegate method passed to the instance's delegate method 500 * @method delegate 501 * @param {String} type The type of event to listen for 502 * @param {Function} fn The method to attach 503 * @param {String, Node} cont The container to act as a delegate, if no "sel" passed, the container is assumed. 504 * @param {String} sel The selector to match in the event (optional) 505 * @return {EventHandle} The Event handle returned from Y.delegate 506 */ 507 delegate: function(type, fn, cont, sel) { 508 var inst = this.getInstance(); 509 510 if (!inst) { 511 Y.log('Delegate events can not be attached until after the ready event has fired.', 'error', 'contenteditable'); 512 513 return false; 514 } 515 516 if (!sel) { 517 sel = cont; 518 519 cont = this.get(CONTAINER); 520 } 521 522 return inst.delegate(type, fn, cont, sel); 523 }, 524 525 /** 526 * Get a reference to the internal YUI instance. 527 * @method getInstance 528 * @return {YUI} The internal YUI instance 529 */ 530 getInstance: function() { 531 return this._instance; 532 }, 533 534 /** 535 * @method render 536 * @param {String/HTMLElement/Node} node The node to render to 537 * @return {ContentEditable} 538 * @chainable 539 */ 540 render: function(node) { 541 var args, inst, fn; 542 543 if (this._rendered) { 544 Y.log('Container already rendered.', 'warn', 'contentEditable'); 545 546 return this; 547 } 548 549 if (node) { 550 this.set(CONTAINER, node); 551 } 552 553 container = this.get(CONTAINER); 554 555 if (!container) { 556 container = YNode.create(ContentEditable.HTML); 557 558 Y.one('body').prepend(container); 559 560 this.set(CONTAINER, container); 561 } 562 563 this._rendered = true; 564 565 this._container.setAttribute(CONTENT_EDITABLE, true); 566 567 args = Y.clone(this.get(USE)); 568 569 fn = Y.bind(function() { 570 inst = YUI(); 571 572 inst.host = this.get(HOST); //Cross reference to Editor 573 574 inst.log = Y.log; //Dump the instance logs to the parent instance. 575 576 Y.log('Creating new internal instance with node-base only', 'info', 'contenteditable'); 577 inst.use('node-base', Y.bind(this._instanceLoaded, this)); 578 }, this); 579 580 args.push(fn); 581 582 Y.log('Adding new modules to main instance: ' + args, 'info', 'contenteditable'); 583 Y.use.apply(Y, args); 584 585 return this; 586 }, 587 588 /** 589 * Set the focus to the container 590 * @method focus 591 * @param {Function} fn Callback function to execute after focus happens 592 * @return {ContentEditable} 593 * @chainable 594 */ 595 focus: function() { 596 this._container.focus(); 597 598 return this; 599 }, 600 /** 601 * Show the iframe instance 602 * @method show 603 * @return {ContentEditable} 604 * @chainable 605 */ 606 show: function() { 607 this._container.show(); 608 609 this.focus(); 610 611 return this; 612 }, 613 614 /** 615 * Hide the iframe instance 616 * @method hide 617 * @return {ContentEditable} 618 * @chainable 619 */ 620 hide: function() { 621 this._container.hide(); 622 623 return this; 624 } 625 }, 626 { 627 /** 628 * The throttle time for key events in IE 629 * @static 630 * @property THROTTLE_TIME 631 * @type Number 632 * @default 100 633 */ 634 THROTTLE_TIME: 100, 635 636 /** 637 * The DomEvents that the frame automatically attaches and bubbles 638 * @static 639 * @property DOM_EVENTS 640 * @type Object 641 */ 642 DOM_EVENTS: { 643 click: 1, 644 dblclick: 1, 645 focusin: 1, 646 focusout: 1, 647 keydown: 1, 648 keypress: 1, 649 keyup: 1, 650 mousedown: 1, 651 mouseup: 1, 652 paste: 1 653 }, 654 655 /** 656 * The template string used to create the ContentEditable element 657 * @static 658 * @property HTML 659 * @type String 660 */ 661 HTML: '<div></div>', 662 663 /** 664 * The name of the class (contentEditable) 665 * @static 666 * @property NAME 667 * @type String 668 */ 669 NAME: 'contentEditable', 670 671 /** 672 * The namespace on which ContentEditable plugin will reside. 673 * 674 * @property NS 675 * @type String 676 * @default 'contentEditable' 677 * @static 678 */ 679 NS: CONTENT_EDITABLE, 680 681 ATTRS: { 682 /** 683 * The default text direction for this ContentEditable element. Default: ltr 684 * @attribute dir 685 * @type String 686 */ 687 dir: { 688 lazyAdd: false, 689 validator: Lang.isString, 690 setter: '_setDir', 691 valueFn: '_getDir' 692 }, 693 694 /** 695 * The container to set contentEditable=true or to create on render. 696 * @attribute container 697 * @type String/HTMLElement/Node 698 */ 699 container: { 700 setter: function(n) { 701 this._container = Y.one(n); 702 703 return this._container; 704 } 705 }, 706 707 /** 708 * The string to inject as Editor content. Default '<br>' 709 * @attribute content 710 * @type String 711 */ 712 content: { 713 getter: '_getHTML', 714 lazyAdd: false, 715 setter: '_setHTML', 716 validator: Lang.isString, 717 value: '<br>' 718 }, 719 720 /** 721 * The default tag to use for block level items, defaults to: p 722 * @attribute defaultblock 723 * @type String 724 */ 725 defaultblock: { 726 validator: Lang.isString, 727 value: TAG_PARAGRAPH, 728 valueFn: '_getDefaultBlock' 729 }, 730 731 /** 732 * A string of CSS to add to the Head of the Editor 733 * @attribute extracss 734 * @type String 735 */ 736 extracss: { 737 lazyAdd: false, 738 setter: '_setExtraCSS', 739 validator: Lang.isString, 740 valueFn: '_getExtraCSS' 741 }, 742 743 /** 744 * Set the id of the new Node. (optional) 745 * @attribute id 746 * @type String 747 * @writeonce 748 */ 749 id: { 750 writeOnce: true, 751 getter: function(id) { 752 if (!id) { 753 id = 'inlineedit-' + Y.guid(); 754 } 755 756 return id; 757 } 758 }, 759 760 /** 761 * The default language. Default: en-US 762 * @attribute lang 763 * @type String 764 */ 765 lang: { 766 validator: Lang.isString, 767 setter: '_setLang', 768 lazyAdd: false, 769 value: 'en-US' 770 }, 771 772 /** 773 * An array of url's to external linked style sheets 774 * @attribute linkedcss 775 * @type String|Array 776 */ 777 linkedcss: { 778 setter: '_setLinkedCSS', 779 validator: '_validateLinkedCSS' 780 //value: '' 781 }, 782 783 /** 784 * The Node instance of the container. 785 * @attribute node 786 * @type Node 787 */ 788 node: { 789 readOnly: true, 790 value: null, 791 getter: function() { 792 return this._container; 793 } 794 }, 795 796 /** 797 * Array of modules to include in the scoped YUI instance at render time. Default: ['node-base', 'editor-selection', 'stylesheet'] 798 * @attribute use 799 * @writeonce 800 * @type Array 801 */ 802 use: { 803 validator: Lang.isArray, 804 writeOnce: true, 805 value: ['node-base', 'editor-selection', 'stylesheet'] 806 } 807 } 808 }); 809 810 Y.namespace('Plugin'); 811 812 Y.Plugin.ContentEditable = ContentEditable; 813 814 }, '3.17.2', {"requires": ["node-base", "editor-selection", "stylesheet", "plugin"]});
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 |