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