[ 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('widget-base', function (Y, NAME) { 9 10 /** 11 * Provides the base Widget class, with HTML Parser support 12 * 13 * @module widget 14 * @main widget 15 */ 16 17 /** 18 * Provides the base Widget class 19 * 20 * @module widget 21 * @submodule widget-base 22 */ 23 var L = Y.Lang, 24 Node = Y.Node, 25 26 ClassNameManager = Y.ClassNameManager, 27 28 _getClassName = ClassNameManager.getClassName, 29 _getWidgetClassName, 30 31 _toInitialCap = Y.cached(function(str) { 32 return str.substring(0, 1).toUpperCase() + str.substring(1); 33 }), 34 35 // K-Weight, IE GC optimizations 36 CONTENT = "content", 37 VISIBLE = "visible", 38 HIDDEN = "hidden", 39 DISABLED = "disabled", 40 FOCUSED = "focused", 41 WIDTH = "width", 42 HEIGHT = "height", 43 BOUNDING_BOX = "boundingBox", 44 CONTENT_BOX = "contentBox", 45 PARENT_NODE = "parentNode", 46 OWNER_DOCUMENT = "ownerDocument", 47 AUTO = "auto", 48 SRC_NODE = "srcNode", 49 BODY = "body", 50 TAB_INDEX = "tabIndex", 51 ID = "id", 52 RENDER = "render", 53 RENDERED = "rendered", 54 DESTROYED = "destroyed", 55 STRINGS = "strings", 56 DIV = "<div></div>", 57 CHANGE = "Change", 58 LOADING = "loading", 59 60 _UISET = "_uiSet", 61 62 EMPTY_STR = "", 63 EMPTY_FN = function() {}, 64 65 TRUE = true, 66 FALSE = false, 67 68 UI, 69 ATTRS = {}, 70 UI_ATTRS = [VISIBLE, DISABLED, HEIGHT, WIDTH, FOCUSED, TAB_INDEX], 71 72 WEBKIT = Y.UA.webkit, 73 74 // Widget nodeid-to-instance map. 75 _instances = {}; 76 77 /** 78 * A base class for widgets, providing: 79 * <ul> 80 * <li>The render lifecycle method, in addition to the init and destroy 81 * lifecycle methods provide by Base</li> 82 * <li>Abstract methods to support consistent MVC structure across 83 * widgets: renderer, renderUI, bindUI, syncUI</li> 84 * <li>Support for common widget attributes, such as boundingBox, contentBox, visible, 85 * disabled, focused, strings</li> 86 * </ul> 87 * 88 * @param config {Object} Object literal specifying widget configuration properties. 89 * 90 * @class Widget 91 * @constructor 92 * @extends Base 93 */ 94 function Widget(config) { 95 96 // kweight 97 var widget = this, 98 parentNode, 99 render, 100 constructor = widget.constructor; 101 102 widget._strs = {}; 103 widget._cssPrefix = constructor.CSS_PREFIX || _getClassName(constructor.NAME.toLowerCase()); 104 105 // We need a config for HTML_PARSER to work. 106 config = config || {}; 107 108 Widget.superclass.constructor.call(widget, config); 109 110 render = widget.get(RENDER); 111 112 if (render) { 113 // Render could be a node or boolean 114 if (render !== TRUE) { 115 parentNode = render; 116 } 117 widget.render(parentNode); 118 } 119 } 120 121 /** 122 * Static property provides a string to identify the class. 123 * <p> 124 * Currently used to apply class identifiers to the bounding box 125 * and to classify events fired by the widget. 126 * </p> 127 * 128 * @property NAME 129 * @type String 130 * @static 131 */ 132 Widget.NAME = "widget"; 133 134 /** 135 * Constant used to identify state changes originating from 136 * the DOM (as opposed to the JavaScript model). 137 * 138 * @property UI_SRC 139 * @type String 140 * @static 141 * @final 142 */ 143 UI = Widget.UI_SRC = "ui"; 144 145 /** 146 * Static property used to define the default attribute 147 * configuration for the Widget. 148 * 149 * @property ATTRS 150 * @type Object 151 * @static 152 */ 153 Widget.ATTRS = ATTRS; 154 155 // Trying to optimize kweight by setting up attrs this way saves about 0.4K min'd 156 157 /** 158 * @attribute id 159 * @writeOnce 160 * @default Generated using guid() 161 * @type String 162 */ 163 164 ATTRS[ID] = { 165 valueFn: "_guid", 166 writeOnce: TRUE 167 }; 168 169 /** 170 * Flag indicating whether or not this Widget 171 * has been through the render lifecycle phase. 172 * 173 * @attribute rendered 174 * @readOnly 175 * @default false 176 * @type boolean 177 */ 178 ATTRS[RENDERED] = { 179 value:FALSE, 180 readOnly: TRUE 181 }; 182 183 /** 184 * @attribute boundingBox 185 * @description The outermost DOM node for the Widget, used for sizing and positioning 186 * of a Widget as well as a containing element for any decorator elements used 187 * for skinning. 188 * @type String | Node 189 * @writeOnce 190 */ 191 ATTRS[BOUNDING_BOX] = { 192 valueFn:"_defaultBB", 193 setter: "_setBB", 194 writeOnce: TRUE 195 }; 196 197 /** 198 * @attribute contentBox 199 * @description A DOM node that is a direct descendant of a Widget's bounding box that 200 * houses its content. 201 * @type String | Node 202 * @writeOnce 203 */ 204 ATTRS[CONTENT_BOX] = { 205 valueFn:"_defaultCB", 206 setter: "_setCB", 207 writeOnce: TRUE 208 }; 209 210 /** 211 * @attribute tabIndex 212 * @description Number (between -32767 to 32767) indicating the widget's 213 * position in the default tab flow. The value is used to set the 214 * "tabIndex" attribute on the widget's bounding box. Negative values allow 215 * the widget to receive DOM focus programmatically (by calling the focus 216 * method), while being removed from the default tab flow. A value of 217 * null removes the "tabIndex" attribute from the widget's bounding box. 218 * @type Number 219 * @default null 220 */ 221 ATTRS[TAB_INDEX] = { 222 value: null, 223 validator: "_validTabIndex" 224 }; 225 226 /** 227 * @attribute focused 228 * @description Boolean indicating if the Widget, or one of its descendants, 229 * has focus. 230 * @readOnly 231 * @default false 232 * @type boolean 233 */ 234 ATTRS[FOCUSED] = { 235 value: FALSE, 236 readOnly:TRUE 237 }; 238 239 /** 240 * @attribute disabled 241 * @description Boolean indicating if the Widget should be disabled. The disabled implementation 242 * is left to the specific classes extending widget. 243 * @default false 244 * @type boolean 245 */ 246 ATTRS[DISABLED] = { 247 value: FALSE 248 }; 249 250 /** 251 * @attribute visible 252 * @description Boolean indicating whether or not the Widget is visible. 253 * @default TRUE 254 * @type boolean 255 */ 256 ATTRS[VISIBLE] = { 257 value: TRUE 258 }; 259 260 /** 261 * @attribute height 262 * @description String with units, or number, representing the height of the Widget. If a number is provided, 263 * the default unit, defined by the Widgets DEF_UNIT, property is used. 264 * @default EMPTY_STR 265 * @type {String | Number} 266 */ 267 ATTRS[HEIGHT] = { 268 value: EMPTY_STR 269 }; 270 271 /** 272 * @attribute width 273 * @description String with units, or number, representing the width of the Widget. If a number is provided, 274 * the default unit, defined by the Widgets DEF_UNIT, property is used. 275 * @default EMPTY_STR 276 * @type {String | Number} 277 */ 278 ATTRS[WIDTH] = { 279 value: EMPTY_STR 280 }; 281 282 /** 283 * @attribute strings 284 * @description Collection of strings used to label elements of the Widget's UI. 285 * @default null 286 * @type Object 287 */ 288 ATTRS[STRINGS] = { 289 value: {}, 290 setter: "_strSetter", 291 getter: "_strGetter" 292 }; 293 294 /** 295 * Whether or not to render the widget automatically after init, and optionally, to which parent node. 296 * 297 * @attribute render 298 * @type boolean | Node 299 * @writeOnce 300 */ 301 ATTRS[RENDER] = { 302 value:FALSE, 303 writeOnce:TRUE 304 }; 305 306 /** 307 * The css prefix which the static Widget.getClassName method should use when constructing class names 308 * 309 * @property CSS_PREFIX 310 * @type String 311 * @default Widget.NAME.toLowerCase() 312 * @private 313 * @static 314 */ 315 Widget.CSS_PREFIX = _getClassName(Widget.NAME.toLowerCase()); 316 317 /** 318 * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined 319 * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and 320 * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for 321 * the prefix and widget class name). 322 * <p> 323 * The instance based version of this method can be used to generate standard prefixed classnames, 324 * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you 325 * need to use a constant class name across different types instances. 326 * </p> 327 * @method getClassName 328 * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name 329 */ 330 Widget.getClassName = function() { 331 // arguments needs to be array'fied to concat 332 return _getClassName.apply(ClassNameManager, [Widget.CSS_PREFIX].concat(Y.Array(arguments), true)); 333 }; 334 335 _getWidgetClassName = Widget.getClassName; 336 337 /** 338 * Returns the widget instance whose bounding box contains, or is, the given node. 339 * <p> 340 * In the case of nested widgets, the nearest bounding box ancestor is used to 341 * return the widget instance. 342 * </p> 343 * @method getByNode 344 * @static 345 * @param node {Node | String} The node for which to return a Widget instance. If a selector 346 * string is passed in, which selects more than one node, the first node found is used. 347 * @return {Widget} Widget instance, or null if not found. 348 */ 349 Widget.getByNode = function(node) { 350 var widget, 351 widgetMarker = _getWidgetClassName(); 352 353 node = Node.one(node); 354 if (node) { 355 node = node.ancestor("." + widgetMarker, true); 356 if (node) { 357 widget = _instances[Y.stamp(node, true)]; 358 } 359 } 360 361 return widget || null; 362 }; 363 364 Y.extend(Widget, Y.Base, { 365 366 /** 367 * Returns a class name prefixed with the the value of the 368 * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property. 369 * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings. 370 * e.g. 371 * <code> 372 * <pre> 373 * // returns "yui-slider-foo-bar", for a slider instance 374 * var scn = slider.getClassName('foo','bar'); 375 * 376 * // returns "yui-overlay-foo-bar", for an overlay instance 377 * var ocn = overlay.getClassName('foo','bar'); 378 * </pre> 379 * </code> 380 * 381 * @method getClassName 382 * @param {String} [classnames*] One or more classname bits to be joined and prefixed 383 */ 384 getClassName: function () { 385 return _getClassName.apply(ClassNameManager, [this._cssPrefix].concat(Y.Array(arguments), true)); 386 }, 387 388 /** 389 * Initializer lifecycle implementation for the Widget class. Registers the 390 * widget instance, and runs through the Widget's HTML_PARSER definition. 391 * 392 * @method initializer 393 * @protected 394 * @param config {Object} Configuration object literal for the widget 395 */ 396 initializer: function(config) { 397 398 var bb = this.get(BOUNDING_BOX); 399 400 if (bb instanceof Node) { 401 this._mapInstance(Y.stamp(bb)); 402 } 403 404 /** 405 * Notification event, which widget implementations can fire, when 406 * they change the content of the widget. This event has no default 407 * behavior and cannot be prevented, so the "on" or "after" 408 * moments are effectively equivalent (with on listeners being invoked before 409 * after listeners). 410 * 411 * @event widget:contentUpdate 412 * @preventable false 413 * @param {EventFacade} e The Event Facade 414 */ 415 }, 416 417 /** 418 * Utility method used to add an entry to the boundingBox id to instance map. 419 * 420 * This method can be used to populate the instance with lazily created boundingBox Node references. 421 * 422 * @method _mapInstance 423 * @param {String} The boundingBox id 424 * @protected 425 */ 426 _mapInstance : function(id) { 427 _instances[id] = this; 428 }, 429 430 /** 431 * Destructor lifecycle implementation for the Widget class. Purges events attached 432 * to the bounding box and content box, removes them from the DOM and removes 433 * the Widget from the list of registered widgets. 434 * 435 * @method destructor 436 * @protected 437 */ 438 destructor: function() { 439 440 var boundingBox = this.get(BOUNDING_BOX), 441 bbGuid; 442 443 if (boundingBox instanceof Node) { 444 bbGuid = Y.stamp(boundingBox,true); 445 446 if (bbGuid in _instances) { 447 delete _instances[bbGuid]; 448 } 449 450 this._destroyBox(); 451 } 452 }, 453 454 /** 455 * <p> 456 * Destroy lifecycle method. Fires the destroy 457 * event, prior to invoking destructors for the 458 * class hierarchy. 459 * 460 * Overrides Base's implementation, to support arguments to destroy 461 * </p> 462 * <p> 463 * Subscribers to the destroy 464 * event can invoke preventDefault on the event object, to prevent destruction 465 * from proceeding. 466 * </p> 467 * @method destroy 468 * @param destroyAllNodes {Boolean} If true, all nodes contained within the Widget are 469 * removed and destroyed. Defaults to false due to potentially high run-time cost. 470 * @return {Widget} A reference to this object 471 * @chainable 472 */ 473 destroy: function(destroyAllNodes) { 474 this._destroyAllNodes = destroyAllNodes; 475 return Widget.superclass.destroy.apply(this); 476 }, 477 478 /** 479 * Removes and destroys the widgets rendered boundingBox, contentBox, 480 * and detaches bound UI events. 481 * 482 * @method _destroyBox 483 * @protected 484 */ 485 _destroyBox : function() { 486 487 var boundingBox = this.get(BOUNDING_BOX), 488 contentBox = this.get(CONTENT_BOX), 489 deep = this._destroyAllNodes, 490 same; 491 492 same = boundingBox && boundingBox.compareTo(contentBox); 493 494 if (this.UI_EVENTS) { 495 this._destroyUIEvents(); 496 } 497 498 this._unbindUI(boundingBox); 499 500 if (contentBox) { 501 if (deep) { 502 contentBox.empty(); 503 } 504 contentBox.remove(TRUE); 505 } 506 507 if (!same) { 508 if (deep) { 509 boundingBox.empty(); 510 } 511 boundingBox.remove(TRUE); 512 } 513 }, 514 515 /** 516 * Establishes the initial DOM for the widget. Invoking this 517 * method will lead to the creating of all DOM elements for 518 * the widget (or the manipulation of existing DOM elements 519 * for the progressive enhancement use case). 520 * <p> 521 * This method should only be invoked once for an initialized 522 * widget. 523 * </p> 524 * <p> 525 * It delegates to the widget specific renderer method to do 526 * the actual work. 527 * </p> 528 * 529 * @method render 530 * @chainable 531 * @final 532 * @param parentNode {Object | String} Optional. The Node under which the 533 * Widget is to be rendered. This can be a Node instance or a CSS selector string. 534 * <p> 535 * If the selector string returns more than one Node, the first node will be used 536 * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox 537 * are not currently in the document. If it's not provided, the Widget will be rendered 538 * to the body of the current document in this case. 539 * </p> 540 */ 541 render: function(parentNode) { 542 543 if (!this.get(DESTROYED) && !this.get(RENDERED)) { 544 /** 545 * Lifecycle event for the render phase, fired prior to rendering the UI 546 * for the widget (prior to invoking the widget's renderer method). 547 * <p> 548 * Subscribers to the "on" moment of this event, will be notified 549 * before the widget is rendered. 550 * </p> 551 * <p> 552 * Subscribers to the "after" moment of this event, will be notified 553 * after rendering is complete. 554 * </p> 555 * 556 * @event render 557 * @preventable _defRenderFn 558 * @param {EventFacade} e The Event Facade 559 */ 560 this.publish(RENDER, { 561 queuable:FALSE, 562 fireOnce:TRUE, 563 defaultTargetOnly:TRUE, 564 defaultFn: this._defRenderFn 565 }); 566 567 this.fire(RENDER, {parentNode: (parentNode) ? Node.one(parentNode) : null}); 568 } 569 return this; 570 }, 571 572 /** 573 * Default render handler 574 * 575 * @method _defRenderFn 576 * @protected 577 * @param {EventFacade} e The Event object 578 * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method 579 */ 580 _defRenderFn : function(e) { 581 this._parentNode = e.parentNode; 582 583 this.renderer(); 584 this._set(RENDERED, TRUE); 585 586 this._removeLoadingClassNames(); 587 }, 588 589 /** 590 * Creates DOM (or manipulates DOM for progressive enhancement) 591 * This method is invoked by render() and is not chained 592 * automatically for the class hierarchy (unlike initializer, destructor) 593 * so it should be chained manually for subclasses if required. 594 * 595 * @method renderer 596 * @protected 597 */ 598 renderer: function() { 599 // kweight 600 var widget = this; 601 602 widget._renderUI(); 603 widget.renderUI(); 604 605 widget._bindUI(); 606 widget.bindUI(); 607 608 widget._syncUI(); 609 widget.syncUI(); 610 }, 611 612 /** 613 * Configures/Sets up listeners to bind Widget State to UI/DOM 614 * 615 * This method is not called by framework and is not chained 616 * automatically for the class hierarchy. 617 * 618 * @method bindUI 619 * @protected 620 */ 621 bindUI: EMPTY_FN, 622 623 /** 624 * Adds nodes to the DOM 625 * 626 * This method is not called by framework and is not chained 627 * automatically for the class hierarchy. 628 * 629 * @method renderUI 630 * @protected 631 */ 632 renderUI: EMPTY_FN, 633 634 /** 635 * Refreshes the rendered UI, based on Widget State 636 * 637 * This method is not called by framework and is not chained 638 * automatically for the class hierarchy. 639 * 640 * @method syncUI 641 * @protected 642 * 643 */ 644 syncUI: EMPTY_FN, 645 646 /** 647 * @method hide 648 * @description Hides the Widget by setting the "visible" attribute to "false". 649 * @chainable 650 */ 651 hide: function() { 652 return this.set(VISIBLE, FALSE); 653 }, 654 655 /** 656 * @method show 657 * @description Shows the Widget by setting the "visible" attribute to "true". 658 * @chainable 659 */ 660 show: function() { 661 return this.set(VISIBLE, TRUE); 662 }, 663 664 /** 665 * @method focus 666 * @description Causes the Widget to receive the focus by setting the "focused" 667 * attribute to "true". 668 * @chainable 669 */ 670 focus: function () { 671 return this._set(FOCUSED, TRUE); 672 }, 673 674 /** 675 * @method blur 676 * @description Causes the Widget to lose focus by setting the "focused" attribute 677 * to "false" 678 * @chainable 679 */ 680 blur: function () { 681 return this._set(FOCUSED, FALSE); 682 }, 683 684 /** 685 * @method enable 686 * @description Set the Widget's "disabled" attribute to "false". 687 * @chainable 688 */ 689 enable: function() { 690 return this.set(DISABLED, FALSE); 691 }, 692 693 /** 694 * @method disable 695 * @description Set the Widget's "disabled" attribute to "true". 696 * @chainable 697 */ 698 disable: function() { 699 return this.set(DISABLED, TRUE); 700 }, 701 702 /** 703 * @method _uiSizeCB 704 * @protected 705 * @param {boolean} expand 706 */ 707 _uiSizeCB : function(expand) { 708 this.get(CONTENT_BOX).toggleClass(_getWidgetClassName(CONTENT, "expanded"), expand); 709 }, 710 711 /** 712 * Helper method to collect the boundingBox and contentBox and append to the provided parentNode, if not 713 * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used 714 * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and 715 * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered 716 * to the current document's body. 717 * 718 * @method _renderBox 719 * @private 720 * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and 721 * the contentBox are not currently in the document, the widget will be rendered to the current document's body. 722 */ 723 _renderBox: function(parentNode) { 724 725 // TODO: Performance Optimization [ More effective algo to reduce Node refs, compares, replaces? ] 726 727 var widget = this, // kweight 728 contentBox = widget.get(CONTENT_BOX), 729 boundingBox = widget.get(BOUNDING_BOX), 730 srcNode = widget.get(SRC_NODE), 731 defParentNode = widget.DEF_PARENT_NODE, 732 733 doc = (srcNode && srcNode.get(OWNER_DOCUMENT)) || boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT); 734 735 // If srcNode (assume it's always in doc), have contentBox take its place (widget render responsible for re-use of srcNode contents) 736 if (srcNode && !srcNode.compareTo(contentBox) && !contentBox.inDoc(doc)) { 737 srcNode.replace(contentBox); 738 } 739 740 if (!boundingBox.compareTo(contentBox.get(PARENT_NODE)) && !boundingBox.compareTo(contentBox)) { 741 // If contentBox box is already in the document, have boundingBox box take it's place 742 if (contentBox.inDoc(doc)) { 743 contentBox.replace(boundingBox); 744 } 745 boundingBox.appendChild(contentBox); 746 } 747 748 parentNode = parentNode || (defParentNode && Node.one(defParentNode)); 749 750 if (parentNode) { 751 parentNode.appendChild(boundingBox); 752 } else if (!boundingBox.inDoc(doc)) { 753 Node.one(BODY).insert(boundingBox, 0); 754 } 755 }, 756 757 /** 758 * Setter for the boundingBox attribute 759 * 760 * @method _setBB 761 * @private 762 * @param {Node|String} node 763 * @return Node 764 */ 765 _setBB: function(node) { 766 return this._setBox(this.get(ID), node, this.BOUNDING_TEMPLATE, true); 767 }, 768 769 /** 770 * Setter for the contentBox attribute 771 * 772 * @method _setCB 773 * @private 774 * @param {Node|String} node 775 * @return Node 776 */ 777 _setCB: function(node) { 778 return (this.CONTENT_TEMPLATE === null) ? this.get(BOUNDING_BOX) : this._setBox(null, node, this.CONTENT_TEMPLATE, false); 779 }, 780 781 /** 782 * Returns the default value for the boundingBox attribute. 783 * 784 * For the Widget class, this will most commonly be null (resulting in a new 785 * boundingBox node instance being created), unless a srcNode was provided 786 * and CONTENT_TEMPLATE is null, in which case it will be srcNode. 787 * This behavior was introduced in 3.17.2 to accomodate single-box widgets 788 * whose BB & CB both point to srcNode (e.g. Y.Button). 789 * 790 * @method _defaultBB 791 * @protected 792 */ 793 _defaultBB : function() { 794 var node = this.get(SRC_NODE), 795 nullCT = (this.CONTENT_TEMPLATE === null); 796 797 return ((node && nullCT) ? node : null); 798 }, 799 800 /** 801 * Returns the default value for the contentBox attribute. 802 * 803 * For the Widget class, this will be the srcNode if provided, otherwise null (resulting in 804 * a new contentBox node instance being created) 805 * 806 * @method _defaultCB 807 * @protected 808 */ 809 _defaultCB : function(node) { 810 return this.get(SRC_NODE) || null; 811 }, 812 813 /** 814 * Helper method to set the bounding/content box, or create it from 815 * the provided template if not found. 816 * 817 * @method _setBox 818 * @private 819 * 820 * @param {String} id The node's id attribute 821 * @param {Node|String} node The node reference 822 * @param {String} template HTML string template for the node 823 * @param {boolean} isBounding true if this is the boundingBox, false if it's the contentBox 824 * @return {Node} The node 825 */ 826 _setBox : function(id, node, template, isBounding) { 827 828 node = Node.one(node); 829 830 if (!node) { 831 node = Node.create(template); 832 833 if (isBounding) { 834 this._bbFromTemplate = true; 835 } else { 836 this._cbFromTemplate = true; 837 } 838 } 839 840 if (!node.get(ID)) { 841 node.set(ID, id || Y.guid()); 842 } 843 844 return node; 845 }, 846 847 /** 848 * Initializes the UI state for the Widget's bounding/content boxes. 849 * 850 * @method _renderUI 851 * @protected 852 */ 853 _renderUI: function() { 854 this._renderBoxClassNames(); 855 this._renderBox(this._parentNode); 856 }, 857 858 /** 859 * Applies standard class names to the boundingBox and contentBox 860 * 861 * @method _renderBoxClassNames 862 * @protected 863 */ 864 _renderBoxClassNames : function() { 865 var classes = this._getClasses(), 866 cl, 867 boundingBox = this.get(BOUNDING_BOX), 868 i; 869 870 boundingBox.addClass(_getWidgetClassName()); 871 872 // Start from Widget Sub Class 873 for (i = classes.length-3; i >= 0; i--) { 874 cl = classes[i]; 875 boundingBox.addClass(cl.CSS_PREFIX || _getClassName(cl.NAME.toLowerCase())); 876 } 877 878 // Use instance based name for content box 879 this.get(CONTENT_BOX).addClass(this.getClassName(CONTENT)); 880 }, 881 882 /** 883 * Removes class names representative of the widget's loading state from 884 * the boundingBox. 885 * 886 * @method _removeLoadingClassNames 887 * @protected 888 */ 889 _removeLoadingClassNames: function () { 890 891 var boundingBox = this.get(BOUNDING_BOX), 892 contentBox = this.get(CONTENT_BOX), 893 instClass = this.getClassName(LOADING), 894 widgetClass = _getWidgetClassName(LOADING); 895 896 boundingBox.removeClass(widgetClass) 897 .removeClass(instClass); 898 899 contentBox.removeClass(widgetClass) 900 .removeClass(instClass); 901 }, 902 903 /** 904 * Sets up DOM and CustomEvent listeners for the widget. 905 * 906 * @method _bindUI 907 * @protected 908 */ 909 _bindUI: function() { 910 this._bindAttrUI(this._UI_ATTRS.BIND); 911 this._bindDOM(); 912 }, 913 914 /** 915 * @method _unbindUI 916 * @protected 917 */ 918 _unbindUI : function(boundingBox) { 919 this._unbindDOM(boundingBox); 920 }, 921 922 /** 923 * Sets up DOM listeners, on elements rendered by the widget. 924 * 925 * @method _bindDOM 926 * @protected 927 */ 928 _bindDOM : function() { 929 var oDocument = this.get(BOUNDING_BOX).get(OWNER_DOCUMENT), 930 focusHandle = Widget._hDocFocus; 931 932 // Shared listener across all Widgets. 933 if (!focusHandle) { 934 focusHandle = Widget._hDocFocus = oDocument.on("focus", this._onDocFocus, this); 935 focusHandle.listeners = { 936 count: 0 937 }; 938 } 939 940 focusHandle.listeners[Y.stamp(this, true)] = true; 941 focusHandle.listeners.count++; 942 943 // Fix for Webkit: 944 // Document doesn't receive focus in Webkit when the user mouses 945 // down on it, so the "focused" attribute won't get set to the 946 // correct value. Keeping this instance based for now, potential better performance. 947 // Otherwise we'll end up looking up widgets from the DOM on every mousedown. 948 if (WEBKIT){ 949 this._hDocMouseDown = oDocument.on("mousedown", this._onDocMouseDown, this); 950 } 951 }, 952 953 /** 954 * @method _unbindDOM 955 * @protected 956 */ 957 _unbindDOM : function(boundingBox) { 958 959 var focusHandle = Widget._hDocFocus, 960 yuid = Y.stamp(this, true), 961 focusListeners, 962 mouseHandle = this._hDocMouseDown; 963 964 if (focusHandle) { 965 966 focusListeners = focusHandle.listeners; 967 968 if (focusListeners[yuid]) { 969 delete focusListeners[yuid]; 970 focusListeners.count--; 971 } 972 973 if (focusListeners.count === 0) { 974 focusHandle.detach(); 975 Widget._hDocFocus = null; 976 } 977 } 978 979 if (WEBKIT && mouseHandle) { 980 mouseHandle.detach(); 981 } 982 }, 983 984 /** 985 * Updates the widget UI to reflect the attribute state. 986 * 987 * @method _syncUI 988 * @protected 989 */ 990 _syncUI: function() { 991 this._syncAttrUI(this._UI_ATTRS.SYNC); 992 }, 993 994 /** 995 * Sets the height on the widget's bounding box element 996 * 997 * @method _uiSetHeight 998 * @protected 999 * @param {String | Number} val 1000 */ 1001 _uiSetHeight: function(val) { 1002 this._uiSetDim(HEIGHT, val); 1003 this._uiSizeCB((val !== EMPTY_STR && val !== AUTO)); 1004 }, 1005 1006 /** 1007 * Sets the width on the widget's bounding box element 1008 * 1009 * @method _uiSetWidth 1010 * @protected 1011 * @param {String | Number} val 1012 */ 1013 _uiSetWidth: function(val) { 1014 this._uiSetDim(WIDTH, val); 1015 }, 1016 1017 /** 1018 * @method _uiSetDim 1019 * @private 1020 * @param {String} dim The dimension - "width" or "height" 1021 * @param {Number | String} val The value to set 1022 */ 1023 _uiSetDim: function(dimension, val) { 1024 this.get(BOUNDING_BOX).setStyle(dimension, L.isNumber(val) ? val + this.DEF_UNIT : val); 1025 }, 1026 1027 /** 1028 * Sets the visible state for the UI 1029 * 1030 * @method _uiSetVisible 1031 * @protected 1032 * @param {boolean} val 1033 */ 1034 _uiSetVisible: function(val) { 1035 this.get(BOUNDING_BOX).toggleClass(this.getClassName(HIDDEN), !val); 1036 }, 1037 1038 /** 1039 * Sets the disabled state for the UI 1040 * 1041 * @method _uiSetDisabled 1042 * @protected 1043 * @param {boolean} val 1044 */ 1045 _uiSetDisabled: function(val) { 1046 this.get(BOUNDING_BOX).toggleClass(this.getClassName(DISABLED), val); 1047 }, 1048 1049 /** 1050 * Sets the focused state for the UI 1051 * 1052 * @method _uiSetFocused 1053 * @protected 1054 * @param {boolean} val 1055 * @param {string} src String representing the source that triggered an update to 1056 * the UI. 1057 */ 1058 _uiSetFocused: function(val, src) { 1059 var boundingBox = this.get(BOUNDING_BOX); 1060 boundingBox.toggleClass(this.getClassName(FOCUSED), val); 1061 1062 if (src !== UI) { 1063 if (val) { 1064 boundingBox.focus(); 1065 } else { 1066 boundingBox.blur(); 1067 } 1068 } 1069 }, 1070 1071 /** 1072 * Set the tabIndex on the widget's rendered UI 1073 * 1074 * @method _uiSetTabIndex 1075 * @protected 1076 * @param Number 1077 */ 1078 _uiSetTabIndex: function(index) { 1079 var boundingBox = this.get(BOUNDING_BOX); 1080 1081 if (L.isNumber(index)) { 1082 boundingBox.set(TAB_INDEX, index); 1083 } else { 1084 boundingBox.removeAttribute(TAB_INDEX); 1085 } 1086 }, 1087 1088 /** 1089 * @method _onDocMouseDown 1090 * @description "mousedown" event handler for the owner document of the 1091 * widget's bounding box. 1092 * @protected 1093 * @param {EventFacade} evt The event facade for the DOM focus event 1094 */ 1095 _onDocMouseDown: function (evt) { 1096 if (this._domFocus) { 1097 this._onDocFocus(evt); 1098 } 1099 }, 1100 1101 /** 1102 * DOM focus event handler, used to sync the state of the Widget with the DOM 1103 * 1104 * @method _onDocFocus 1105 * @protected 1106 * @param {EventFacade} evt The event facade for the DOM focus event 1107 */ 1108 _onDocFocus: function (evt) { 1109 var widget = Widget.getByNode(evt.target), 1110 activeWidget = Widget._active; 1111 1112 if (activeWidget && (activeWidget !== widget)) { 1113 activeWidget._domFocus = false; 1114 activeWidget._set(FOCUSED, false, {src:UI}); 1115 1116 Widget._active = null; 1117 } 1118 1119 if (widget) { 1120 widget._domFocus = true; 1121 widget._set(FOCUSED, true, {src:UI}); 1122 1123 Widget._active = widget; 1124 } 1125 }, 1126 1127 /** 1128 * Generic toString implementation for all widgets. 1129 * 1130 * @method toString 1131 * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ] 1132 */ 1133 toString: function() { 1134 // Using deprecated name prop for kweight squeeze. 1135 return this.name + "[" + this.get(ID) + "]"; 1136 }, 1137 1138 /** 1139 * Default unit to use for dimension values 1140 * 1141 * @property DEF_UNIT 1142 * @type String 1143 */ 1144 DEF_UNIT : "px", 1145 1146 /** 1147 * Default node to render the bounding box to. If not set, 1148 * will default to the current document body. 1149 * 1150 * @property DEF_PARENT_NODE 1151 * @type String | Node 1152 */ 1153 DEF_PARENT_NODE : null, 1154 1155 /** 1156 * Property defining the markup template for content box. If your Widget doesn't 1157 * need the dual boundingBox/contentBox structure, set CONTENT_TEMPLATE to null, 1158 * and contentBox and boundingBox will both point to the same Node. 1159 * 1160 * @property CONTENT_TEMPLATE 1161 * @type String 1162 */ 1163 CONTENT_TEMPLATE : DIV, 1164 1165 /** 1166 * Property defining the markup template for bounding box. 1167 * 1168 * @property BOUNDING_TEMPLATE 1169 * @type String 1170 */ 1171 BOUNDING_TEMPLATE : DIV, 1172 1173 /** 1174 * @method _guid 1175 * @protected 1176 */ 1177 _guid : function() { 1178 return Y.guid(); 1179 }, 1180 1181 /** 1182 * @method _validTabIndex 1183 * @protected 1184 * @param {Number} tabIndex 1185 */ 1186 _validTabIndex : function (tabIndex) { 1187 return (L.isNumber(tabIndex) || L.isNull(tabIndex)); 1188 }, 1189 1190 /** 1191 * Binds after listeners for the list of attributes provided 1192 * 1193 * @method _bindAttrUI 1194 * @private 1195 * @param {Array} attrs 1196 */ 1197 _bindAttrUI : function(attrs) { 1198 var i, 1199 l = attrs.length; 1200 1201 for (i = 0; i < l; i++) { 1202 this.after(attrs[i] + CHANGE, this._setAttrUI); 1203 } 1204 }, 1205 1206 /** 1207 * Invokes the _uiSet=ATTR NAME> method for the list of attributes provided 1208 * 1209 * @method _syncAttrUI 1210 * @private 1211 * @param {Array} attrs 1212 */ 1213 _syncAttrUI : function(attrs) { 1214 var i, l = attrs.length, attr; 1215 for (i = 0; i < l; i++) { 1216 attr = attrs[i]; 1217 this[_UISET + _toInitialCap(attr)](this.get(attr)); 1218 } 1219 }, 1220 1221 /** 1222 * @method _setAttrUI 1223 * @private 1224 * @param {EventFacade} e 1225 */ 1226 _setAttrUI : function(e) { 1227 if (e.target === this) { 1228 this[_UISET + _toInitialCap(e.attrName)](e.newVal, e.src); 1229 } 1230 }, 1231 1232 /** 1233 * The default setter for the strings attribute. Merges partial sets 1234 * into the full string set, to allow users to partial sets of strings 1235 * 1236 * @method _strSetter 1237 * @protected 1238 * @param {Object} strings 1239 * @return {String} The full set of strings to set 1240 */ 1241 _strSetter : function(strings) { 1242 return Y.merge(this.get(STRINGS), strings); 1243 }, 1244 1245 /** 1246 * Helper method to get a specific string value 1247 * 1248 * @deprecated Used by deprecated WidgetLocale implementations. 1249 * @method getString 1250 * @param {String} key 1251 * @return {String} The string 1252 */ 1253 getString : function(key) { 1254 return this.get(STRINGS)[key]; 1255 }, 1256 1257 /** 1258 * Helper method to get the complete set of strings for the widget 1259 * 1260 * @deprecated Used by deprecated WidgetLocale implementations. 1261 * @method getStrings 1262 * @param {String} key 1263 * @return {String} The strings 1264 */ 1265 getStrings : function() { 1266 return this.get(STRINGS); 1267 }, 1268 1269 /** 1270 * The lists of UI attributes to bind and sync for widget's _bindUI and _syncUI implementations 1271 * 1272 * @property _UI_ATTRS 1273 * @type Object 1274 * @private 1275 */ 1276 _UI_ATTRS : { 1277 BIND: UI_ATTRS, 1278 SYNC: UI_ATTRS 1279 } 1280 }); 1281 1282 Y.Widget = Widget; 1283 1284 1285 }, '3.17.2', { 1286 "requires": [ 1287 "attribute", 1288 "base-base", 1289 "base-pluginhost", 1290 "classnamemanager", 1291 "event-focus", 1292 "node-base", 1293 "node-style" 1294 ], 1295 "skinnable": true 1296 });
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 |