[ 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 168 e.clipboardData = null; 169 } 170 171 this.fire('dom:paste', e); 172 }, 173 174 /** 175 * Binds DOM events and fires the ready event 176 * @private 177 * @method _defReadyFn 178 */ 179 _defReadyFn: function() { 180 var inst = this.getInstance(), 181 container = this.get(CONTAINER); 182 183 Y.each( 184 ContentEditable.DOM_EVENTS, 185 function(value, key) { 186 var fn = Y.bind(this._onDomEvent, this), 187 kfn = ((Y.UA.ie && ContentEditable.THROTTLE_TIME > 0) ? Y.throttle(fn, ContentEditable.THROTTLE_TIME) : fn); 188 189 if (!inst.Node.DOM_EVENTS[key]) { 190 inst.Node.DOM_EVENTS[key] = 1; 191 } 192 193 if (value === 1) { 194 if (key !== FOCUS && key !== BLUR && key !== PASTE) { 195 if (key.substring(0, 3) === KEY) { 196 //Throttle key events in IE 197 this._eventHandles.push(container.on(key, kfn, container)); 198 } else { 199 this._eventHandles.push(container.on(key, fn, container)); 200 } 201 } 202 } 203 }, 204 this 205 ); 206 207 inst.Node.DOM_EVENTS.paste = 1; 208 209 this._eventHandles.push( 210 container.on(PASTE, Y.bind(this._DOMPaste, this), container), 211 container.on(FOCUS, Y.bind(this._onDomEvent, this), container), 212 container.on(BLUR, Y.bind(this._onDomEvent, this), container) 213 ); 214 215 inst.__use = inst.use; 216 217 inst.use = Y.bind(this.use, this); 218 }, 219 220 /** 221 * Called once the content is available in the ContentEditable element and calls the final use call 222 * @private 223 * @method _onContentReady 224 * on the internal instance so that the modules are loaded properly. 225 */ 226 _onContentReady: function(event) { 227 if (!this._ready) { 228 this._ready = true; 229 230 var inst = this.getInstance(), 231 args = Y.clone(this.get(USE)); 232 233 this.fire(EVENT_CONTENT_READY); 234 235 236 if (event) { 237 inst.config.doc = YNode.getDOMNode(event.target); 238 } 239 240 args.push(Y.bind(function() { 241 242 if (inst.EditorSelection) { 243 inst.EditorSelection.DEFAULT_BLOCK_TAG = this.get('defaultblock'); 244 245 inst.EditorSelection.ROOT = this.get(CONTAINER); 246 } 247 248 this.fire(EVENT_READY); 249 }, this)); 250 251 252 inst.use.apply(inst, args); 253 } 254 }, 255 256 /** 257 * Retrieves defaultblock value from host attribute 258 * @private 259 * @method _getDefaultBlock 260 * @return {String} 261 */ 262 _getDefaultBlock: function() { 263 return this._getHostValue('defaultblock'); 264 }, 265 266 /** 267 * Retrieves dir value from host attribute 268 * @private 269 * @method _getDir 270 * @return {String} 271 */ 272 _getDir: function() { 273 return this._getHostValue('dir'); 274 }, 275 276 /** 277 * Retrieves extracss value from host attribute 278 * @private 279 * @method _getExtraCSS 280 * @return {String} 281 */ 282 _getExtraCSS: function() { 283 return this._getHostValue('extracss'); 284 }, 285 286 /** 287 * Get the content from the container 288 * @private 289 * @method _getHTML 290 * @param {String} html The raw HTML from the container. 291 * @return {String} 292 */ 293 _getHTML: function() { 294 var html, container; 295 296 if (this._ready) { 297 container = this.get(CONTAINER); 298 299 html = container.get(INNER_HTML); 300 } 301 302 return html; 303 }, 304 305 /** 306 * Retrieves a value from host attribute 307 * @private 308 * @method _getHostValue 309 * @param {attr} The attribute which value should be returned from the host 310 * @return {String|Object} 311 */ 312 _getHostValue: function(attr) { 313 var host = this.get(HOST); 314 315 if (host) { 316 return host.get(attr); 317 } 318 }, 319 320 /** 321 * Set the content of the container 322 * @private 323 * @method _setHTML 324 * @param {String} html The raw HTML to set to the container. 325 * @return {String} 326 */ 327 _setHTML: function(html) { 328 if (this._ready) { 329 var container = this.get(CONTAINER); 330 331 container.set(INNER_HTML, html); 332 } else { 333 //This needs to be wrapped in a contentready callback for the !_ready state 334 this.once(EVENT_CONTENT_READY, Y.bind(this._setHTML, this, html)); 335 } 336 337 return html; 338 }, 339 340 /** 341 * Sets the linked CSS on the instance. 342 * @private 343 * @method _setLinkedCSS 344 * @param {String} css The linkedcss value 345 * @return {String} 346 */ 347 _setLinkedCSS: function(css) { 348 if (this._ready) { 349 var inst = this.getInstance(); 350 inst.Get.css(css); 351 } else { 352 //This needs to be wrapped in a contentready callback for the !_ready state 353 this.once(EVENT_CONTENT_READY, Y.bind(this._setLinkedCSS, this, css)); 354 } 355 356 return css; 357 }, 358 359 /** 360 * Sets the dir (language direction) attribute on the container. 361 * @private 362 * @method _setDir 363 * @param {String} value The language direction 364 * @return {String} 365 */ 366 _setDir: function(value) { 367 var container; 368 369 if (this._ready) { 370 container = this.get(CONTAINER); 371 372 container.setAttribute('dir', value); 373 } else { 374 //This needs to be wrapped in a contentready callback for the !_ready state 375 this.once(EVENT_CONTENT_READY, Y.bind(this._setDir, this, value)); 376 } 377 378 return value; 379 }, 380 381 /** 382 * Set's the extra CSS on the instance. 383 * @private 384 * @method _setExtraCSS 385 * @param {String} css The CSS style to be set as extra css 386 * @return {String} 387 */ 388 _setExtraCSS: function(css) { 389 if (this._ready) { 390 if (css) { 391 var inst = this.getInstance(), 392 head = inst.one('head'); 393 394 if (this._extraCSSNode) { 395 this._extraCSSNode.remove(); 396 } 397 398 this._extraCSSNode = YNode.create('<style>' + css + '</style>'); 399 400 head.append(this._extraCSSNode); 401 } 402 } else { 403 //This needs to be wrapped in a contentready callback for the !_ready state 404 this.once(EVENT_CONTENT_READY, Y.bind(this._setExtraCSS, this, css)); 405 } 406 407 return css; 408 }, 409 410 /** 411 * Sets the language value on the instance. 412 * @private 413 * @method _setLang 414 * @param {String} value The language to be set 415 * @return {String} 416 */ 417 _setLang: function(value) { 418 var container; 419 420 if (this._ready) { 421 container = this.get(CONTAINER); 422 423 container.setAttribute('lang', value); 424 } else { 425 //This needs to be wrapped in a contentready callback for the !_ready state 426 this.once(EVENT_CONTENT_READY, Y.bind(this._setLang, this, value)); 427 } 428 429 return value; 430 }, 431 432 /** 433 * Called from the first YUI instance that sets up the internal instance. 434 * This loads the content into the ContentEditable element and attaches the contentready event. 435 * @private 436 * @method _instanceLoaded 437 * @param {YUI} inst The internal YUI instance bound to the ContentEditable element 438 */ 439 _instanceLoaded: function(inst) { 440 this._instance = inst; 441 442 this._onContentReady(); 443 444 var doc = this._instance.config.doc; 445 446 if (!Y.UA.ie) { 447 try { 448 //Force other browsers into non CSS styling 449 doc.execCommand('styleWithCSS', false, false); 450 doc.execCommand('insertbronreturn', false, false); 451 } catch (err) {} 452 } 453 }, 454 455 456 /** 457 * Validates linkedcss property 458 * 459 * @method _validateLinkedCSS 460 * @private 461 */ 462 _validateLinkedCSS: function(value) { 463 return Lang.isString(value) || Lang.isArray(value); 464 }, 465 466 //BEGIN PUBLIC METHODS 467 /** 468 * This is a scoped version of the normal YUI.use method & is bound to the ContentEditable element 469 * At setup, the inst.use method is mapped to this method. 470 * @method use 471 */ 472 use: function() { 473 474 var inst = this.getInstance(), 475 args = Y.Array(arguments), 476 callback = false; 477 478 if (Lang.isFunction(args[args.length - 1])) { 479 callback = args.pop(); 480 } 481 482 if (callback) { 483 args.push(function() { 484 485 callback.apply(inst, arguments); 486 }); 487 } 488 489 return inst.__use.apply(inst, args); 490 }, 491 492 /** 493 * A delegate method passed to the instance's delegate method 494 * @method delegate 495 * @param {String} type The type of event to listen for 496 * @param {Function} fn The method to attach 497 * @param {String, Node} cont The container to act as a delegate, if no "sel" passed, the container is assumed. 498 * @param {String} sel The selector to match in the event (optional) 499 * @return {EventHandle} The Event handle returned from Y.delegate 500 */ 501 delegate: function(type, fn, cont, sel) { 502 var inst = this.getInstance(); 503 504 if (!inst) { 505 506 return false; 507 } 508 509 if (!sel) { 510 sel = cont; 511 512 cont = this.get(CONTAINER); 513 } 514 515 return inst.delegate(type, fn, cont, sel); 516 }, 517 518 /** 519 * Get a reference to the internal YUI instance. 520 * @method getInstance 521 * @return {YUI} The internal YUI instance 522 */ 523 getInstance: function() { 524 return this._instance; 525 }, 526 527 /** 528 * @method render 529 * @param {String/HTMLElement/Node} node The node to render to 530 * @return {ContentEditable} 531 * @chainable 532 */ 533 render: function(node) { 534 var args, inst, fn; 535 536 if (this._rendered) { 537 538 return this; 539 } 540 541 if (node) { 542 this.set(CONTAINER, node); 543 } 544 545 container = this.get(CONTAINER); 546 547 if (!container) { 548 container = YNode.create(ContentEditable.HTML); 549 550 Y.one('body').prepend(container); 551 552 this.set(CONTAINER, container); 553 } 554 555 this._rendered = true; 556 557 this._container.setAttribute(CONTENT_EDITABLE, true); 558 559 args = Y.clone(this.get(USE)); 560 561 fn = Y.bind(function() { 562 inst = YUI(); 563 564 inst.host = this.get(HOST); //Cross reference to Editor 565 566 567 inst.use('node-base', Y.bind(this._instanceLoaded, this)); 568 }, this); 569 570 args.push(fn); 571 572 Y.use.apply(Y, args); 573 574 return this; 575 }, 576 577 /** 578 * Set the focus to the container 579 * @method focus 580 * @param {Function} fn Callback function to execute after focus happens 581 * @return {ContentEditable} 582 * @chainable 583 */ 584 focus: function() { 585 this._container.focus(); 586 587 return this; 588 }, 589 /** 590 * Show the iframe instance 591 * @method show 592 * @return {ContentEditable} 593 * @chainable 594 */ 595 show: function() { 596 this._container.show(); 597 598 this.focus(); 599 600 return this; 601 }, 602 603 /** 604 * Hide the iframe instance 605 * @method hide 606 * @return {ContentEditable} 607 * @chainable 608 */ 609 hide: function() { 610 this._container.hide(); 611 612 return this; 613 } 614 }, 615 { 616 /** 617 * The throttle time for key events in IE 618 * @static 619 * @property THROTTLE_TIME 620 * @type Number 621 * @default 100 622 */ 623 THROTTLE_TIME: 100, 624 625 /** 626 * The DomEvents that the frame automatically attaches and bubbles 627 * @static 628 * @property DOM_EVENTS 629 * @type Object 630 */ 631 DOM_EVENTS: { 632 click: 1, 633 dblclick: 1, 634 focusin: 1, 635 focusout: 1, 636 keydown: 1, 637 keypress: 1, 638 keyup: 1, 639 mousedown: 1, 640 mouseup: 1, 641 paste: 1 642 }, 643 644 /** 645 * The template string used to create the ContentEditable element 646 * @static 647 * @property HTML 648 * @type String 649 */ 650 HTML: '<div></div>', 651 652 /** 653 * The name of the class (contentEditable) 654 * @static 655 * @property NAME 656 * @type String 657 */ 658 NAME: 'contentEditable', 659 660 /** 661 * The namespace on which ContentEditable plugin will reside. 662 * 663 * @property NS 664 * @type String 665 * @default 'contentEditable' 666 * @static 667 */ 668 NS: CONTENT_EDITABLE, 669 670 ATTRS: { 671 /** 672 * The default text direction for this ContentEditable element. Default: ltr 673 * @attribute dir 674 * @type String 675 */ 676 dir: { 677 lazyAdd: false, 678 validator: Lang.isString, 679 setter: '_setDir', 680 valueFn: '_getDir' 681 }, 682 683 /** 684 * The container to set contentEditable=true or to create on render. 685 * @attribute container 686 * @type String/HTMLElement/Node 687 */ 688 container: { 689 setter: function(n) { 690 this._container = Y.one(n); 691 692 return this._container; 693 } 694 }, 695 696 /** 697 * The string to inject as Editor content. Default '<br>' 698 * @attribute content 699 * @type String 700 */ 701 content: { 702 getter: '_getHTML', 703 lazyAdd: false, 704 setter: '_setHTML', 705 validator: Lang.isString, 706 value: '<br>' 707 }, 708 709 /** 710 * The default tag to use for block level items, defaults to: p 711 * @attribute defaultblock 712 * @type String 713 */ 714 defaultblock: { 715 validator: Lang.isString, 716 value: TAG_PARAGRAPH, 717 valueFn: '_getDefaultBlock' 718 }, 719 720 /** 721 * A string of CSS to add to the Head of the Editor 722 * @attribute extracss 723 * @type String 724 */ 725 extracss: { 726 lazyAdd: false, 727 setter: '_setExtraCSS', 728 validator: Lang.isString, 729 valueFn: '_getExtraCSS' 730 }, 731 732 /** 733 * Set the id of the new Node. (optional) 734 * @attribute id 735 * @type String 736 * @writeonce 737 */ 738 id: { 739 writeOnce: true, 740 getter: function(id) { 741 if (!id) { 742 id = 'inlineedit-' + Y.guid(); 743 } 744 745 return id; 746 } 747 }, 748 749 /** 750 * The default language. Default: en-US 751 * @attribute lang 752 * @type String 753 */ 754 lang: { 755 validator: Lang.isString, 756 setter: '_setLang', 757 lazyAdd: false, 758 value: 'en-US' 759 }, 760 761 /** 762 * An array of url's to external linked style sheets 763 * @attribute linkedcss 764 * @type String|Array 765 */ 766 linkedcss: { 767 setter: '_setLinkedCSS', 768 validator: '_validateLinkedCSS' 769 //value: '' 770 }, 771 772 /** 773 * The Node instance of the container. 774 * @attribute node 775 * @type Node 776 */ 777 node: { 778 readOnly: true, 779 value: null, 780 getter: function() { 781 return this._container; 782 } 783 }, 784 785 /** 786 * Array of modules to include in the scoped YUI instance at render time. Default: ['node-base', 'editor-selection', 'stylesheet'] 787 * @attribute use 788 * @writeonce 789 * @type Array 790 */ 791 use: { 792 validator: Lang.isArray, 793 writeOnce: true, 794 value: ['node-base', 'editor-selection', 'stylesheet'] 795 } 796 } 797 }); 798 799 Y.namespace('Plugin'); 800 801 Y.Plugin.ContentEditable = ContentEditable; 802 803 }, '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 |