| 1 | efrain | 1 | YUI.add('gallery-sm-treeview', function (Y, NAME) {
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | var Micro = Y.Template.Micro;
 | 
        
           |  |  | 4 |   | 
        
           |  |  | 5 | Y.namespace('TreeView').Templates = {
 | 
        
           |  |  | 6 |     children: Micro.compile(
 | 
        
           |  |  | 7 |         '<ul class="<%= data.classNames.children %>" ' +
 | 
        
           |  |  | 8 |   | 
        
           |  |  | 9 |             '<% if (data.node.isRoot()) { %>' +
 | 
        
           |  |  | 10 |                 'role="tree" tabindex="0"' +
 | 
        
           |  |  | 11 |             '<% } else { %>' +
 | 
        
           |  |  | 12 |                 'role="group"' +
 | 
        
           |  |  | 13 |             '<% } %>' +
 | 
        
           |  |  | 14 |   | 
        
           |  |  | 15 |         '></ul>'
 | 
        
           |  |  | 16 |     ),
 | 
        
           |  |  | 17 |   | 
        
           |  |  | 18 |     node: Micro.compile(
 | 
        
           |  |  | 19 |         '<li id="<%= data.node.id %>" class="<%= data.nodeClassNames.join(" ") %>" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
 | 
        
           |  |  | 20 |             '<div class="<%= data.classNames.row %>" data-node-id="<%= data.node.id %>">' +
 | 
        
           |  |  | 21 |                 '<span class="<%= data.classNames.indicator %>"><s></s></span>' +
 | 
        
           |  |  | 22 |                 '<span class="<%= data.classNames.icon %>"></span>' +
 | 
        
           |  |  | 23 |                 '<span id="<%= data.node.id %>-label" class="<%= data.classNames.label %>"><%== data.node.label %></span>' +
 | 
        
           |  |  | 24 |             '</div>' +
 | 
        
           |  |  | 25 |         '</li>'
 | 
        
           |  |  | 26 |     )
 | 
        
           |  |  | 27 | };
 | 
        
           |  |  | 28 | /*jshint expr:true, onevar:false */
 | 
        
           |  |  | 29 |   | 
        
           |  |  | 30 | /**
 | 
        
           |  |  | 31 | Provides the `Y.TreeView` widget.
 | 
        
           |  |  | 32 |   | 
        
           |  |  | 33 | @module gallery-sm-treeview
 | 
        
           |  |  | 34 | @main gallery-sm-treeview
 | 
        
           |  |  | 35 | **/
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 | /**
 | 
        
           |  |  | 38 | TreeView widget.
 | 
        
           |  |  | 39 |   | 
        
           |  |  | 40 | @class TreeView
 | 
        
           |  |  | 41 | @constructor
 | 
        
           |  |  | 42 | @extends View
 | 
        
           |  |  | 43 | @uses Tree
 | 
        
           |  |  | 44 | @uses Tree.Labelable
 | 
        
           |  |  | 45 | @uses Tree.Openable
 | 
        
           |  |  | 46 | @uses Tree.Selectable
 | 
        
           |  |  | 47 | **/
 | 
        
           |  |  | 48 |   | 
        
           |  |  | 49 | var getClassName = Y.ClassNameManager.getClassName,
 | 
        
           |  |  | 50 |   | 
        
           |  |  | 51 | TreeView = Y.Base.create('treeView', Y.View, [
 | 
        
           |  |  | 52 |     Y.Tree,
 | 
        
           |  |  | 53 |     Y.Tree.Labelable,
 | 
        
           |  |  | 54 |     Y.Tree.Openable,
 | 
        
           |  |  | 55 |     Y.Tree.Selectable
 | 
        
           |  |  | 56 | ], {
 | 
        
           |  |  | 57 |     // -- Public Properties ----------------------------------------------------
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |     /**
 | 
        
           |  |  | 60 |     CSS class names used by this treeview.
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |     @property {Object} classNames
 | 
        
           |  |  | 63 |     @param {String} canHaveChildren Class name indicating that a tree node can
 | 
        
           |  |  | 64 |         contain child nodes (whether or not it actually does).
 | 
        
           |  |  | 65 |     @param {String} children Class name for a list of child nodes.
 | 
        
           |  |  | 66 |     @param {String} hasChildren Class name indicating that a tree node has one
 | 
        
           |  |  | 67 |         or more child nodes.
 | 
        
           |  |  | 68 |     @param {String} icon Class name for a tree node's icon.
 | 
        
           |  |  | 69 |     @param {String} indicator Class name for an open/closed indicator.
 | 
        
           |  |  | 70 |     @param {String} label Class name for a tree node's user-visible label.
 | 
        
           |  |  | 71 |     @param {String} node Class name for a tree node item.
 | 
        
           |  |  | 72 |     @param {String} noTouch Class name added to the TreeView container when not
 | 
        
           |  |  | 73 |         using a touchscreen device.
 | 
        
           |  |  | 74 |     @param {String} open Class name indicating that a tree node is open.
 | 
        
           |  |  | 75 |     @param {String} row Class name for a row container encompassing the
 | 
        
           |  |  | 76 |         indicator and label within a tree node.
 | 
        
           |  |  | 77 |     @param {String} selected Class name for a tree node that's selected.
 | 
        
           |  |  | 78 |     @param {String} touch Class name added to the TreeView container when using
 | 
        
           |  |  | 79 |         a touchscreen device.
 | 
        
           |  |  | 80 |     @param {String} treeview Class name for the TreeView container.
 | 
        
           |  |  | 81 |     **/
 | 
        
           |  |  | 82 |     classNames: {
 | 
        
           |  |  | 83 |         canHaveChildren: getClassName('treeview-can-have-children'),
 | 
        
           |  |  | 84 |         children       : getClassName('treeview-children'),
 | 
        
           |  |  | 85 |         hasChildren    : getClassName('treeview-has-children'),
 | 
        
           |  |  | 86 |         icon           : getClassName('treeview-icon'),
 | 
        
           |  |  | 87 |         indicator      : getClassName('treeview-indicator'),
 | 
        
           |  |  | 88 |         label          : getClassName('treeview-label'),
 | 
        
           |  |  | 89 |         node           : getClassName('treeview-node'),
 | 
        
           |  |  | 90 |         noTouch        : getClassName('treeview-notouch'),
 | 
        
           |  |  | 91 |         open           : getClassName('treeview-open'),
 | 
        
           |  |  | 92 |         row            : getClassName('treeview-row'),
 | 
        
           |  |  | 93 |         selected       : getClassName('treeview-selected'),
 | 
        
           |  |  | 94 |         touch          : getClassName('treeview-touch'),
 | 
        
           |  |  | 95 |         treeview       : getClassName('treeview')
 | 
        
           |  |  | 96 |     },
 | 
        
           |  |  | 97 |   | 
        
           |  |  | 98 |     /**
 | 
        
           |  |  | 99 |     Whether or not this TreeView has been rendered.
 | 
        
           |  |  | 100 |   | 
        
           |  |  | 101 |     @property {Boolean} rendered
 | 
        
           |  |  | 102 |     @default false
 | 
        
           |  |  | 103 |     **/
 | 
        
           |  |  | 104 |     rendered: false,
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |     /**
 | 
        
           |  |  | 107 |     Default templates used to render this TreeView.
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |     @property {Object} templates
 | 
        
           |  |  | 110 |     **/
 | 
        
           |  |  | 111 |     templates: Y.TreeView.Templates,
 | 
        
           |  |  | 112 |   | 
        
           |  |  | 113 |     // -- Protected Properties -------------------------------------------------
 | 
        
           |  |  | 114 |   | 
        
           |  |  | 115 |     /**
 | 
        
           |  |  | 116 |     Simple way to type-check that this is a TreeView instance.
 | 
        
           |  |  | 117 |   | 
        
           |  |  | 118 |     @property {Boolean} _isYUITreeView
 | 
        
           |  |  | 119 |     @default true
 | 
        
           |  |  | 120 |     @protected
 | 
        
           |  |  | 121 |     **/
 | 
        
           |  |  | 122 |     _isYUITreeView: true,
 | 
        
           |  |  | 123 |   | 
        
           |  |  | 124 |     /**
 | 
        
           |  |  | 125 |     Cached value of the `lazyRender` attribute.
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |     @property {Boolean} _lazyRender
 | 
        
           |  |  | 128 |     @protected
 | 
        
           |  |  | 129 |     **/
 | 
        
           |  |  | 130 |   | 
        
           |  |  | 131 |     // -- Lifecycle Methods ----------------------------------------------------
 | 
        
           |  |  | 132 |   | 
        
           |  |  | 133 |     initializer: function (config) {
 | 
        
           |  |  | 134 |         if (config && config.templates) {
 | 
        
           |  |  | 135 |             this.templates = Y.merge(this.templates, config.templates);
 | 
        
           |  |  | 136 |         }
 | 
        
           |  |  | 137 |   | 
        
           |  |  | 138 |         this._renderQueue = {};
 | 
        
           |  |  | 139 |         this._attachTreeViewEvents();
 | 
        
           |  |  | 140 |     },
 | 
        
           |  |  | 141 |   | 
        
           |  |  | 142 |     destructor: function () {
 | 
        
           |  |  | 143 |         clearTimeout(this._renderTimeout);
 | 
        
           |  |  | 144 |         this._detachTreeViewEvents();
 | 
        
           |  |  | 145 |   | 
        
           |  |  | 146 |         this._renderQueue = null;
 | 
        
           |  |  | 147 |     },
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |     // -- Public Methods -------------------------------------------------------
 | 
        
           |  |  | 150 |   | 
        
           |  |  | 151 |     destroyNode: function (node, options) {
 | 
        
           |  |  | 152 |         node._htmlNode = null;
 | 
        
           |  |  | 153 |         return Y.Tree.prototype.destroyNode.call(this, node, options);
 | 
        
           |  |  | 154 |     },
 | 
        
           |  |  | 155 |   | 
        
           |  |  | 156 |     /**
 | 
        
           |  |  | 157 |     Returns the HTML node (as a `Y.Node` instance) associated with the specified
 | 
        
           |  |  | 158 |     `Tree.Node` instance, if any.
 | 
        
           |  |  | 159 |   | 
        
           |  |  | 160 |     @method getHTMLNode
 | 
        
           |  |  | 161 |     @param {Tree.Node} treeNode Tree node.
 | 
        
           |  |  | 162 |     @return {Node} `Y.Node` instance associated with the given tree node, or
 | 
        
           |  |  | 163 |         `undefined` if one was not found.
 | 
        
           |  |  | 164 |     **/
 | 
        
           |  |  | 165 |     getHTMLNode: function (treeNode) {
 | 
        
           |  |  | 166 |         if (!treeNode._htmlNode) {
 | 
        
           |  |  | 167 |             treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
 | 
        
           |  |  | 168 |         }
 | 
        
           |  |  | 169 |   | 
        
           |  |  | 170 |         return treeNode._htmlNode;
 | 
        
           |  |  | 171 |     },
 | 
        
           |  |  | 172 |   | 
        
           |  |  | 173 |     /**
 | 
        
           |  |  | 174 |     Renders this TreeView into its container.
 | 
        
           |  |  | 175 |   | 
        
           |  |  | 176 |     If the container hasn't already been added to the current document, it will
 | 
        
           |  |  | 177 |     be appended to the `<body>` element.
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |     @method render
 | 
        
           |  |  | 180 |     @chainable
 | 
        
           |  |  | 181 |     **/
 | 
        
           |  |  | 182 |     render: function () {
 | 
        
           |  |  | 183 |         var container     = this.get('container'),
 | 
        
           |  |  | 184 |             isTouchDevice = 'ontouchstart' in Y.config.win;
 | 
        
           |  |  | 185 |   | 
        
           |  |  | 186 |         container.addClass(this.classNames.treeview);
 | 
        
           |  |  | 187 |         container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
 | 
        
           |  |  | 188 |   | 
        
           |  |  | 189 |         this._childrenNode = this.renderChildren(this.rootNode, {
 | 
        
           |  |  | 190 |             container: container
 | 
        
           |  |  | 191 |         });
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |         if (!container.inDoc()) {
 | 
        
           |  |  | 194 |             Y.one('body').append(container);
 | 
        
           |  |  | 195 |         }
 | 
        
           |  |  | 196 |   | 
        
           |  |  | 197 |         this.rendered = true;
 | 
        
           |  |  | 198 |   | 
        
           |  |  | 199 |         return this;
 | 
        
           |  |  | 200 |     },
 | 
        
           |  |  | 201 |   | 
        
           |  |  | 202 |     /**
 | 
        
           |  |  | 203 |     Renders the children of the specified tree node.
 | 
        
           |  |  | 204 |   | 
        
           |  |  | 205 |     If a container is specified, it will be assumed to be an existing rendered
 | 
        
           |  |  | 206 |     tree node, and the children will be rendered (or re-rendered) inside it.
 | 
        
           |  |  | 207 |   | 
        
           |  |  | 208 |     @method renderChildren
 | 
        
           |  |  | 209 |     @param {Tree.Node} treeNode Tree node whose children should be rendered.
 | 
        
           |  |  | 210 |     @param {Object} [options] Options.
 | 
        
           |  |  | 211 |         @param {Node} [options.container] `Y.Node` instance of a container into
 | 
        
           |  |  | 212 |             which the children should be rendered. If the container already
 | 
        
           |  |  | 213 |             contains rendered children, they will be re-rendered in place.
 | 
        
           |  |  | 214 |     @return {Node} `Y.Node` instance containing the rendered children.
 | 
        
           |  |  | 215 |     **/
 | 
        
           |  |  | 216 |     renderChildren: function (treeNode, options) {
 | 
        
           |  |  | 217 |         options || (options = {});
 | 
        
           |  |  | 218 |   | 
        
           |  |  | 219 |         var container    = options.container,
 | 
        
           |  |  | 220 |             childrenNode = container && container.one('>.' + this.classNames.children),
 | 
        
           |  |  | 221 |             lazyRender   = this._lazyRender;
 | 
        
           |  |  | 222 |   | 
        
           |  |  | 223 |         if (!childrenNode) {
 | 
        
           |  |  | 224 |             childrenNode = Y.Node.create(this.templates.children({
 | 
        
           |  |  | 225 |                 classNames: this.classNames,
 | 
        
           |  |  | 226 |                 node      : treeNode,
 | 
        
           |  |  | 227 |                 treeview  : this // not currently used, but may be useful for custom templates
 | 
        
           |  |  | 228 |             }));
 | 
        
           |  |  | 229 |         }
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |         if (treeNode.hasChildren()) {
 | 
        
           |  |  | 232 |             childrenNode.set('aria-expanded', treeNode.isOpen());
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 |             for (var i = 0, len = treeNode.children.length; i < len; i++) {
 | 
        
           |  |  | 235 |                 var child = treeNode.children[i];
 | 
        
           |  |  | 236 |   | 
        
           |  |  | 237 |                 this.renderNode(child, {
 | 
        
           |  |  | 238 |                     container     : childrenNode,
 | 
        
           |  |  | 239 |                     renderChildren: !lazyRender || child.isOpen()
 | 
        
           |  |  | 240 |                 });
 | 
        
           |  |  | 241 |             }
 | 
        
           |  |  | 242 |         }
 | 
        
           |  |  | 243 |   | 
        
           |  |  | 244 |         // Keep track of whether or not this node's children have been rendered
 | 
        
           |  |  | 245 |         // so we'll know whether we need to render them later if the node is
 | 
        
           |  |  | 246 |         // opened.
 | 
        
           |  |  | 247 |         treeNode.state.renderedChildren = true;
 | 
        
           |  |  | 248 |   | 
        
           |  |  | 249 |         if (container) {
 | 
        
           |  |  | 250 |             container.append(childrenNode);
 | 
        
           |  |  | 251 |         }
 | 
        
           |  |  | 252 |   | 
        
           |  |  | 253 |         return childrenNode;
 | 
        
           |  |  | 254 |     },
 | 
        
           |  |  | 255 |   | 
        
           |  |  | 256 |     /**
 | 
        
           |  |  | 257 |     Renders the specified tree node and its children (if any).
 | 
        
           |  |  | 258 |   | 
        
           |  |  | 259 |     If a container is specified, the rendered node will be appended to it.
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |     @method renderNode
 | 
        
           |  |  | 262 |     @param {Tree.Node} treeNode Tree node to render.
 | 
        
           |  |  | 263 |     @param {Object} [options] Options.
 | 
        
           |  |  | 264 |         @param {Node} [options.container] `Y.Node` instance of a container to
 | 
        
           |  |  | 265 |             which the rendered tree node should be appended.
 | 
        
           |  |  | 266 |         @param {Boolean} [options.renderChildren=false] Whether or not to render
 | 
        
           |  |  | 267 |             this node's children.
 | 
        
           |  |  | 268 |     @return {Node} `Y.Node` instance of the rendered tree node.
 | 
        
           |  |  | 269 |     **/
 | 
        
           |  |  | 270 |     renderNode: function (treeNode, options) {
 | 
        
           |  |  | 271 |         options || (options = {});
 | 
        
           |  |  | 272 |   | 
        
           |  |  | 273 |         var classNames     = this.classNames,
 | 
        
           |  |  | 274 |             hasChildren    = treeNode.hasChildren(),
 | 
        
           |  |  | 275 |             htmlNode       = treeNode._htmlNode,
 | 
        
           |  |  | 276 |             nodeClassNames = {},
 | 
        
           |  |  | 277 |             className;
 | 
        
           |  |  | 278 |   | 
        
           |  |  | 279 |         // Build the hash of CSS classes for this node.
 | 
        
           |  |  | 280 |         nodeClassNames[classNames.node]            = true;
 | 
        
           |  |  | 281 |         nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
 | 
        
           |  |  | 282 |         nodeClassNames[classNames.hasChildren]     = hasChildren;
 | 
        
           |  |  | 283 |   | 
        
           |  |  | 284 |         if (htmlNode) {
 | 
        
           |  |  | 285 |             // This node has already been rendered, so we just need to update
 | 
        
           |  |  | 286 |             // the DOM instead of re-rendering it from scratch.
 | 
        
           |  |  | 287 |             htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
 | 
        
           |  |  | 288 |   | 
        
           |  |  | 289 |             for (className in nodeClassNames) {
 | 
        
           |  |  | 290 |                 if (nodeClassNames.hasOwnProperty(className)) {
 | 
        
           |  |  | 291 |                     htmlNode.toggleClass(className, nodeClassNames[className]);
 | 
        
           |  |  | 292 |                 }
 | 
        
           |  |  | 293 |             }
 | 
        
           |  |  | 294 |         } else {
 | 
        
           |  |  | 295 |             // This node hasn't been rendered yet, so render it from scratch.
 | 
        
           |  |  | 296 |             var enabledClassNames = [];
 | 
        
           |  |  | 297 |   | 
        
           |  |  | 298 |             for (className in nodeClassNames) {
 | 
        
           |  |  | 299 |                 if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
 | 
        
           |  |  | 300 |                     enabledClassNames.push(className);
 | 
        
           |  |  | 301 |                 }
 | 
        
           |  |  | 302 |             }
 | 
        
           |  |  | 303 |   | 
        
           |  |  | 304 |             htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
 | 
        
           |  |  | 305 |                 classNames    : classNames,
 | 
        
           |  |  | 306 |                 nodeClassNames: enabledClassNames,
 | 
        
           |  |  | 307 |                 node          : treeNode,
 | 
        
           |  |  | 308 |                 treeview      : this // not currently used, but may be useful for custom templates
 | 
        
           |  |  | 309 |             }));
 | 
        
           |  |  | 310 |         }
 | 
        
           |  |  | 311 |   | 
        
           |  |  | 312 |         this._syncNodeOpenState(treeNode, htmlNode);
 | 
        
           |  |  | 313 |         this._syncNodeSelectedState(treeNode, htmlNode);
 | 
        
           |  |  | 314 |   | 
        
           |  |  | 315 |         if (hasChildren) {
 | 
        
           |  |  | 316 |             if (options.renderChildren) {
 | 
        
           |  |  | 317 |                 this.renderChildren(treeNode, {
 | 
        
           |  |  | 318 |                     container: htmlNode
 | 
        
           |  |  | 319 |                 });
 | 
        
           |  |  | 320 |             }
 | 
        
           |  |  | 321 |         } else {
 | 
        
           |  |  | 322 |             // If children were previously rendered but this node no longer has
 | 
        
           |  |  | 323 |             // children, remove the empty child list.
 | 
        
           |  |  | 324 |             var childrenNode = htmlNode.one('>.' + classNames.children);
 | 
        
           |  |  | 325 |   | 
        
           |  |  | 326 |             if (childrenNode) {
 | 
        
           |  |  | 327 |                 childrenNode.remove(true);
 | 
        
           |  |  | 328 |             }
 | 
        
           |  |  | 329 |         }
 | 
        
           |  |  | 330 |   | 
        
           |  |  | 331 |         treeNode.state.rendered = true;
 | 
        
           |  |  | 332 |   | 
        
           |  |  | 333 |         if (options.container) {
 | 
        
           |  |  | 334 |             options.container.append(htmlNode);
 | 
        
           |  |  | 335 |         }
 | 
        
           |  |  | 336 |   | 
        
           |  |  | 337 |         return htmlNode;
 | 
        
           |  |  | 338 |     },
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |     // -- Protected Methods ----------------------------------------------------
 | 
        
           |  |  | 341 |   | 
        
           |  |  | 342 |     _attachTreeViewEvents: function () {
 | 
        
           |  |  | 343 |         this._treeViewEvents || (this._treeViewEvents = []);
 | 
        
           |  |  | 344 |   | 
        
           |  |  | 345 |         var classNames = this.classNames,
 | 
        
           |  |  | 346 |             container  = this.get('container');
 | 
        
           |  |  | 347 |   | 
        
           |  |  | 348 |         this._treeViewEvents.push(
 | 
        
           |  |  | 349 |             // Custom events.
 | 
        
           |  |  | 350 |             this.after({
 | 
        
           |  |  | 351 |                 add              : this._afterAdd,
 | 
        
           |  |  | 352 |                 clear            : this._afterClear,
 | 
        
           |  |  | 353 |                 close            : this._afterClose,
 | 
        
           |  |  | 354 |                 multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
 | 
        
           |  |  | 355 |                 open             : this._afterOpen,
 | 
        
           |  |  | 356 |                 remove           : this._afterRemove,
 | 
        
           |  |  | 357 |                 select           : this._afterSelect,
 | 
        
           |  |  | 358 |                 unselect         : this._afterUnselect
 | 
        
           |  |  | 359 |             }),
 | 
        
           |  |  | 360 |   | 
        
           |  |  | 361 |             // DOM events.
 | 
        
           |  |  | 362 |             container.on('mousedown', this._onMouseDown, this),
 | 
        
           |  |  | 363 |   | 
        
           |  |  | 364 |             container.delegate('click', this._onIndicatorClick,
 | 
        
           |  |  | 365 |                 '.' + classNames.indicator, this),
 | 
        
           |  |  | 366 |   | 
        
           |  |  | 367 |             container.delegate('click', this._onRowClick,
 | 
        
           |  |  | 368 |                 '.' + classNames.row, this),
 | 
        
           |  |  | 369 |   | 
        
           |  |  | 370 |             container.delegate('dblclick', this._onRowDoubleClick,
 | 
        
           |  |  | 371 |                 '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
 | 
        
           |  |  | 372 |         );
 | 
        
           |  |  | 373 |     },
 | 
        
           |  |  | 374 |   | 
        
           |  |  | 375 |     _detachTreeViewEvents: function () {
 | 
        
           |  |  | 376 |         (new Y.EventHandle(this._treeViewEvents)).detach();
 | 
        
           |  |  | 377 |     },
 | 
        
           |  |  | 378 |   | 
        
           |  |  | 379 |     _processRenderQueue: function () {
 | 
        
           |  |  | 380 |         if (!this.rendered) {
 | 
        
           |  |  | 381 |             return;
 | 
        
           |  |  | 382 |         }
 | 
        
           |  |  | 383 |   | 
        
           |  |  | 384 |         var queue = this._renderQueue,
 | 
        
           |  |  | 385 |             node;
 | 
        
           |  |  | 386 |   | 
        
           |  |  | 387 |         for (var id in queue) {
 | 
        
           |  |  | 388 |             if (queue.hasOwnProperty(id)) {
 | 
        
           |  |  | 389 |                 node = this.getNodeById(id);
 | 
        
           |  |  | 390 |   | 
        
           |  |  | 391 |                 if (node) {
 | 
        
           |  |  | 392 |                     this.renderNode(node, queue[id]);
 | 
        
           |  |  | 393 |                 }
 | 
        
           |  |  | 394 |             }
 | 
        
           |  |  | 395 |         }
 | 
        
           |  |  | 396 |   | 
        
           |  |  | 397 |         this._renderQueue = {};
 | 
        
           |  |  | 398 |     },
 | 
        
           |  |  | 399 |   | 
        
           |  |  | 400 |     _queueRender: function (node, options) {
 | 
        
           |  |  | 401 |         if (!this.rendered) {
 | 
        
           |  |  | 402 |             return;
 | 
        
           |  |  | 403 |         }
 | 
        
           |  |  | 404 |   | 
        
           |  |  | 405 |         var queue = this._renderQueue,
 | 
        
           |  |  | 406 |             self  = this;
 | 
        
           |  |  | 407 |   | 
        
           |  |  | 408 |         clearTimeout(this._renderTimeout);
 | 
        
           |  |  | 409 |   | 
        
           |  |  | 410 |         queue[node.id] = Y.merge(queue[node.id], options);
 | 
        
           |  |  | 411 |   | 
        
           |  |  | 412 |         this._renderTimeout = setTimeout(function () {
 | 
        
           |  |  | 413 |             self._processRenderQueue();
 | 
        
           |  |  | 414 |         }, 15);
 | 
        
           |  |  | 415 |   | 
        
           |  |  | 416 |         return this;
 | 
        
           |  |  | 417 |     },
 | 
        
           |  |  | 418 |   | 
        
           |  |  | 419 |     /**
 | 
        
           |  |  | 420 |     Setter for the `lazyRender` attribute.
 | 
        
           |  |  | 421 |   | 
        
           |  |  | 422 |     Just caches the value in a property for faster lookups.
 | 
        
           |  |  | 423 |   | 
        
           |  |  | 424 |     @method _setLazyRender
 | 
        
           |  |  | 425 |     @return {Boolean} Value.
 | 
        
           |  |  | 426 |     @protected
 | 
        
           |  |  | 427 |     **/
 | 
        
           |  |  | 428 |     _setLazyRender: function (value) {
 | 
        
           |  |  | 429 |         /*jshint boss:true */
 | 
        
           |  |  | 430 |         return this._lazyRender = value;
 | 
        
           |  |  | 431 |     },
 | 
        
           |  |  | 432 |   | 
        
           |  |  | 433 |     _syncNodeOpenState: function (node, htmlNode) {
 | 
        
           |  |  | 434 |         htmlNode || (htmlNode = this.getHTMLNode(node));
 | 
        
           |  |  | 435 |   | 
        
           |  |  | 436 |         if (!htmlNode) {
 | 
        
           |  |  | 437 |             return;
 | 
        
           |  |  | 438 |         }
 | 
        
           |  |  | 439 |   | 
        
           |  |  | 440 |         if (node.isOpen()) {
 | 
        
           |  |  | 441 |             htmlNode
 | 
        
           |  |  | 442 |                 .addClass(this.classNames.open)
 | 
        
           |  |  | 443 |                 .set('aria-expanded', true);
 | 
        
           |  |  | 444 |         } else {
 | 
        
           |  |  | 445 |             htmlNode
 | 
        
           |  |  | 446 |                 .removeClass(this.classNames.open)
 | 
        
           |  |  | 447 |                 .set('aria-expanded', false);
 | 
        
           |  |  | 448 |         }
 | 
        
           |  |  | 449 |     },
 | 
        
           |  |  | 450 |   | 
        
           |  |  | 451 |     _syncNodeSelectedState: function (node, htmlNode) {
 | 
        
           |  |  | 452 |         htmlNode || (htmlNode = this.getHTMLNode(node));
 | 
        
           |  |  | 453 |   | 
        
           |  |  | 454 |         if (!htmlNode) {
 | 
        
           |  |  | 455 |             return;
 | 
        
           |  |  | 456 |         }
 | 
        
           |  |  | 457 |   | 
        
           |  |  | 458 |         var multiSelect = this.get('multiSelect');
 | 
        
           |  |  | 459 |   | 
        
           |  |  | 460 |         if (node.isSelected()) {
 | 
        
           |  |  | 461 |             htmlNode.addClass(this.classNames.selected);
 | 
        
           |  |  | 462 |   | 
        
           |  |  | 463 |             if (multiSelect) {
 | 
        
           |  |  | 464 |                 // It's only necessary to set aria-selected when multi-select is
 | 
        
           |  |  | 465 |                 // enabled and focus can't be used to track the selection state.
 | 
        
           |  |  | 466 |                 htmlNode.set('aria-selected', true);
 | 
        
           |  |  | 467 |             } else {
 | 
        
           |  |  | 468 |                 htmlNode.set('tabIndex', 0);
 | 
        
           |  |  | 469 |             }
 | 
        
           |  |  | 470 |         } else {
 | 
        
           |  |  | 471 |             htmlNode
 | 
        
           |  |  | 472 |                 .removeClass(this.classNames.selected)
 | 
        
           |  |  | 473 |                 .removeAttribute('tabIndex');
 | 
        
           |  |  | 474 |   | 
        
           |  |  | 475 |             if (multiSelect) {
 | 
        
           |  |  | 476 |                 htmlNode.set('aria-selected', false);
 | 
        
           |  |  | 477 |             }
 | 
        
           |  |  | 478 |         }
 | 
        
           |  |  | 479 |     },
 | 
        
           |  |  | 480 |   | 
        
           |  |  | 481 |     // -- Protected Event Handlers ---------------------------------------------
 | 
        
           |  |  | 482 |   | 
        
           |  |  | 483 |     _afterAdd: function (e) {
 | 
        
           |  |  | 484 |         // Nothing to do if the treeview hasn't been rendered yet.
 | 
        
           |  |  | 485 |         if (!this.rendered) {
 | 
        
           |  |  | 486 |             return;
 | 
        
           |  |  | 487 |         }
 | 
        
           |  |  | 488 |   | 
        
           |  |  | 489 |         var parent       = e.parent,
 | 
        
           |  |  | 490 |             parentIsRoot = parent.isRoot(),
 | 
        
           |  |  | 491 |             treeNode     = e.node,
 | 
        
           |  |  | 492 |   | 
        
           |  |  | 493 |             htmlChildren,
 | 
        
           |  |  | 494 |             htmlParent;
 | 
        
           |  |  | 495 |   | 
        
           |  |  | 496 |         if (parentIsRoot) {
 | 
        
           |  |  | 497 |             htmlChildren = this._childrenNode;
 | 
        
           |  |  | 498 |         } else {
 | 
        
           |  |  | 499 |             htmlParent   = this.getHTMLNode(parent),
 | 
        
           |  |  | 500 |             htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
 | 
        
           |  |  | 501 |         }
 | 
        
           |  |  | 502 |   | 
        
           |  |  | 503 |         if (htmlChildren) {
 | 
        
           |  |  | 504 |             // Parent's children have already been rendered. Instead of
 | 
        
           |  |  | 505 |             // re-rendering all of them, just render the new node and insert it
 | 
        
           |  |  | 506 |             // at the correct position.
 | 
        
           |  |  | 507 |             htmlChildren.insert(this.renderNode(treeNode, {
 | 
        
           |  |  | 508 |                 renderChildren: !this._lazyRender || treeNode.isOpen()
 | 
        
           |  |  | 509 |             }), e.index);
 | 
        
           |  |  | 510 |   | 
        
           |  |  | 511 |             // Schedule the parent node to be re-rendered in order to update its
 | 
        
           |  |  | 512 |             // state. This is done asynchronously and throttled in order to
 | 
        
           |  |  | 513 |             // avoid re-rendering the parent many times if multiple children are
 | 
        
           |  |  | 514 |             // added in quick succession.
 | 
        
           |  |  | 515 |             if (!parentIsRoot) {
 | 
        
           |  |  | 516 |                 this._queueRender(parent);
 | 
        
           |  |  | 517 |             }
 | 
        
           |  |  | 518 |         } else if (!parentIsRoot) {
 | 
        
           |  |  | 519 |             // Either the parent hasn't been rendered yet, or its children
 | 
        
           |  |  | 520 |             // haven't been rendered yet. Schedule it to be rendered. This is
 | 
        
           |  |  | 521 |             // done asynchronously and throttled in order to avoid re-rendering
 | 
        
           |  |  | 522 |             // the parent many times if multiple children are added in quick
 | 
        
           |  |  | 523 |             // succession.
 | 
        
           |  |  | 524 |             this._queueRender(parent, {renderChildren: true});
 | 
        
           |  |  | 525 |         }
 | 
        
           |  |  | 526 |     },
 | 
        
           |  |  | 527 |   | 
        
           |  |  | 528 |     _afterClear: function () {
 | 
        
           |  |  | 529 |         // Nothing to do if the treeview hasn't been rendered yet.
 | 
        
           |  |  | 530 |         if (!this.rendered) {
 | 
        
           |  |  | 531 |             return;
 | 
        
           |  |  | 532 |         }
 | 
        
           |  |  | 533 |   | 
        
           |  |  | 534 |         clearTimeout(this._renderTimeout);
 | 
        
           |  |  | 535 |         this._renderQueue = {};
 | 
        
           |  |  | 536 |   | 
        
           |  |  | 537 |         delete this._childrenNode;
 | 
        
           |  |  | 538 |         this.rendered = false;
 | 
        
           |  |  | 539 |   | 
        
           |  |  | 540 |         this.get('container').empty();
 | 
        
           |  |  | 541 |         this.render();
 | 
        
           |  |  | 542 |     },
 | 
        
           |  |  | 543 |   | 
        
           |  |  | 544 |     _afterClose: function (e) {
 | 
        
           |  |  | 545 |         if (this.rendered) {
 | 
        
           |  |  | 546 |             this._syncNodeOpenState(e.node);
 | 
        
           |  |  | 547 |         }
 | 
        
           |  |  | 548 |     },
 | 
        
           |  |  | 549 |   | 
        
           |  |  | 550 |     _afterOpen: function (e) {
 | 
        
           |  |  | 551 |         if (!this.rendered) {
 | 
        
           |  |  | 552 |             return;
 | 
        
           |  |  | 553 |         }
 | 
        
           |  |  | 554 |   | 
        
           |  |  | 555 |         var treeNode = e.node,
 | 
        
           |  |  | 556 |             htmlNode = this.getHTMLNode(treeNode);
 | 
        
           |  |  | 557 |   | 
        
           |  |  | 558 |         // If this node's children haven't been rendered yet, render them.
 | 
        
           |  |  | 559 |         if (!treeNode.state.renderedChildren) {
 | 
        
           |  |  | 560 |             this.renderChildren(treeNode, {
 | 
        
           |  |  | 561 |                 container: htmlNode
 | 
        
           |  |  | 562 |             });
 | 
        
           |  |  | 563 |         }
 | 
        
           |  |  | 564 |   | 
        
           |  |  | 565 |         this._syncNodeOpenState(treeNode, htmlNode);
 | 
        
           |  |  | 566 |     },
 | 
        
           |  |  | 567 |   | 
        
           |  |  | 568 |     _afterRemove: function (e) {
 | 
        
           |  |  | 569 |         if (!this.rendered) {
 | 
        
           |  |  | 570 |             return;
 | 
        
           |  |  | 571 |         }
 | 
        
           |  |  | 572 |   | 
        
           |  |  | 573 |         var treeNode = e.node,
 | 
        
           |  |  | 574 |             parent   = e.parent;
 | 
        
           |  |  | 575 |   | 
        
           |  |  | 576 |         // If this node is in the render queue, remove it from the queue.
 | 
        
           |  |  | 577 |         if (this._renderQueue[treeNode.id]) {
 | 
        
           |  |  | 578 |             delete this._renderQueue[treeNode.id];
 | 
        
           |  |  | 579 |         }
 | 
        
           |  |  | 580 |   | 
        
           |  |  | 581 |         // Remove DOM nodes associated with this node and any of its
 | 
        
           |  |  | 582 |         // descendants, and mark all nodes as unrendered so that they'll be
 | 
        
           |  |  | 583 |         // re-rendered if they're reinserted in the tree.
 | 
        
           |  |  | 584 |         var htmlNode = this.getHTMLNode(treeNode);
 | 
        
           |  |  | 585 |   | 
        
           |  |  | 586 |         if (htmlNode) {
 | 
        
           |  |  | 587 |             htmlNode
 | 
        
           |  |  | 588 |                 .empty()
 | 
        
           |  |  | 589 |                 .remove(true);
 | 
        
           |  |  | 590 |   | 
        
           |  |  | 591 |             treeNode._htmlNode = null;
 | 
        
           |  |  | 592 |         }
 | 
        
           |  |  | 593 |   | 
        
           |  |  | 594 |         if (!treeNode.state.destroyed) {
 | 
        
           |  |  | 595 |             treeNode.traverse(function (node) {
 | 
        
           |  |  | 596 |                 node._htmlNode              = null;
 | 
        
           |  |  | 597 |                 node.state.rendered         = false;
 | 
        
           |  |  | 598 |                 node.state.renderedChildren = false;
 | 
        
           |  |  | 599 |             });
 | 
        
           |  |  | 600 |         }
 | 
        
           |  |  | 601 |   | 
        
           |  |  | 602 |         // Re-render the parent to update its state if this was its last child.
 | 
        
           |  |  | 603 |         if (parent && !parent.hasChildren()) {
 | 
        
           |  |  | 604 |             this.renderNode(parent);
 | 
        
           |  |  | 605 |         }
 | 
        
           |  |  | 606 |     },
 | 
        
           |  |  | 607 |   | 
        
           |  |  | 608 |     _afterSelect: function (e) {
 | 
        
           |  |  | 609 |         if (this.rendered) {
 | 
        
           |  |  | 610 |             this._syncNodeSelectedState(e.node);
 | 
        
           |  |  | 611 |         }
 | 
        
           |  |  | 612 |     },
 | 
        
           |  |  | 613 |   | 
        
           |  |  | 614 |     _afterTreeViewMultiSelectChange: function (e) {
 | 
        
           |  |  | 615 |         if (!this.rendered) {
 | 
        
           |  |  | 616 |             return;
 | 
        
           |  |  | 617 |         }
 | 
        
           |  |  | 618 |   | 
        
           |  |  | 619 |         var container = this.get('container'),
 | 
        
           |  |  | 620 |             rootList  = container.one('> .' + this.classNames.children),
 | 
        
           |  |  | 621 |             htmlNodes = container.all('.' + this.classNames.node);
 | 
        
           |  |  | 622 |   | 
        
           |  |  | 623 |         if (e.newVal) {
 | 
        
           |  |  | 624 |             rootList.set('aria-multiselectable', true);
 | 
        
           |  |  | 625 |             htmlNodes.set('aria-selected', false);
 | 
        
           |  |  | 626 |         } else {
 | 
        
           |  |  | 627 |             // When multiselect is disabled, aria-selected must not be set on
 | 
        
           |  |  | 628 |             // any nodes, since focus is used to indicate selection.
 | 
        
           |  |  | 629 |             rootList.removeAttribute('aria-multiselectable');
 | 
        
           |  |  | 630 |             htmlNodes.removeAttribute('aria-selected');
 | 
        
           |  |  | 631 |         }
 | 
        
           |  |  | 632 |     },
 | 
        
           |  |  | 633 |   | 
        
           |  |  | 634 |     _afterUnselect: function (e) {
 | 
        
           |  |  | 635 |         if (this.rendered) {
 | 
        
           |  |  | 636 |             this._syncNodeSelectedState(e.node);
 | 
        
           |  |  | 637 |         }
 | 
        
           |  |  | 638 |     },
 | 
        
           |  |  | 639 |   | 
        
           |  |  | 640 |     _onIndicatorClick: function (e) {
 | 
        
           |  |  | 641 |         var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
 | 
        
           |  |  | 642 |   | 
        
           |  |  | 643 |         // Indicator clicks shouldn't toggle selection state, so don't allow
 | 
        
           |  |  | 644 |         // this event to propagate to the _onRowClick() handler.
 | 
        
           |  |  | 645 |         e.stopImmediatePropagation();
 | 
        
           |  |  | 646 |   | 
        
           |  |  | 647 |         this.getNodeById(rowNode.getData('node-id')).toggleOpen();
 | 
        
           |  |  | 648 |     },
 | 
        
           |  |  | 649 |   | 
        
           |  |  | 650 |     _onMouseDown: function (e) {
 | 
        
           |  |  | 651 |         // This prevents the tree from momentarily grabbing focus before focus
 | 
        
           |  |  | 652 |         // is set on a node.
 | 
        
           |  |  | 653 |         e.preventDefault();
 | 
        
           |  |  | 654 |     },
 | 
        
           |  |  | 655 |   | 
        
           |  |  | 656 |     _onRowClick: function (e) {
 | 
        
           |  |  | 657 |         // Ignore buttons other than the left button.
 | 
        
           |  |  | 658 |         if (e.button > 1) {
 | 
        
           |  |  | 659 |             return;
 | 
        
           |  |  | 660 |         }
 | 
        
           |  |  | 661 |   | 
        
           |  |  | 662 |         var node = this.getNodeById(e.currentTarget.getData('node-id'));
 | 
        
           |  |  | 663 |   | 
        
           |  |  | 664 |         if (this.get('multiSelect')) {
 | 
        
           |  |  | 665 |             node[node.isSelected() ? 'unselect' : 'select']();
 | 
        
           |  |  | 666 |         } else {
 | 
        
           |  |  | 667 |             node.select();
 | 
        
           |  |  | 668 |         }
 | 
        
           |  |  | 669 |     },
 | 
        
           |  |  | 670 |   | 
        
           |  |  | 671 |     _onRowDoubleClick: function (e) {
 | 
        
           |  |  | 672 |         // Ignore buttons other than the left button.
 | 
        
           |  |  | 673 |         if (e.button > 1) {
 | 
        
           |  |  | 674 |             return;
 | 
        
           |  |  | 675 |         }
 | 
        
           |  |  | 676 |   | 
        
           |  |  | 677 |         this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
 | 
        
           |  |  | 678 |     }
 | 
        
           |  |  | 679 | }, {
 | 
        
           |  |  | 680 |     ATTRS: {
 | 
        
           |  |  | 681 |         /**
 | 
        
           |  |  | 682 |         When `true`, a node's children won't be rendered until the first time
 | 
        
           |  |  | 683 |         that node is opened.
 | 
        
           |  |  | 684 |   | 
        
           |  |  | 685 |         This can significantly speed up the time it takes to render a large
 | 
        
           |  |  | 686 |         tree, but might not make sense if you're using CSS that doesn't hide the
 | 
        
           |  |  | 687 |         contents of closed nodes.
 | 
        
           |  |  | 688 |   | 
        
           |  |  | 689 |         @attribute {Boolean} lazyRender
 | 
        
           |  |  | 690 |         @default true
 | 
        
           |  |  | 691 |         **/
 | 
        
           |  |  | 692 |         lazyRender: {
 | 
        
           |  |  | 693 |             lazyAdd: false, // to ensure that the setter runs on init
 | 
        
           |  |  | 694 |             setter : '_setLazyRender',
 | 
        
           |  |  | 695 |             value  : true
 | 
        
           |  |  | 696 |         }
 | 
        
           |  |  | 697 |     }
 | 
        
           |  |  | 698 | });
 | 
        
           |  |  | 699 |   | 
        
           |  |  | 700 | Y.TreeView = Y.mix(TreeView, Y.TreeView);
 | 
        
           |  |  | 701 |   | 
        
           |  |  | 702 |   | 
        
           |  |  | 703 | }, 'gallery-2013.06.20-02-07', {
 | 
        
           |  |  | 704 |     "requires": [
 | 
        
           |  |  | 705 |         "base-build",
 | 
        
           |  |  | 706 |         "classnamemanager",
 | 
        
           |  |  | 707 |         "template-micro",
 | 
        
           |  |  | 708 |         "tree",
 | 
        
           |  |  | 709 |         "tree-labelable",
 | 
        
           |  |  | 710 |         "tree-openable",
 | 
        
           |  |  | 711 |         "tree-selectable",
 | 
        
           |  |  | 712 |         "view"
 | 
        
           |  |  | 713 |     ],
 | 
        
           |  |  | 714 |     "skinnable": true
 | 
        
           |  |  | 715 | });
 |