Show:
                            /*jshint expr:true, onevar:false */
                            
                            /**
                            Extension for `Tree` that adds the concept of selection state for nodes.
                            
                            @module tree
                            @submodule tree-selectable
                            @main tree-selectable
                            **/
                            
                            var Do = Y.Do;
                            
                            /**
                            Extension for `Tree` that adds the concept of selection state for nodes.
                            
                            @class Tree.Selectable
                            @constructor
                            @extensionfor Tree
                            **/
                            
                            /**
                            Fired when a node is selected.
                            
                            @event select
                            @param {Tree.Node} node Node being selected.
                            @preventable _defSelectFn
                            **/
                            var EVT_SELECT = 'select';
                            
                            /**
                            Fired when a node is unselected.
                            
                            @event unselect
                            @param {Tree.Node} node Node being unselected.
                            @preventable _defUnselectFn
                            **/
                            var EVT_UNSELECT = 'unselect';
                            
                            function Selectable() {}
                            
                            Selectable.prototype = {
                                // -- Protected Properties -------------------------------------------------
                            
                                /**
                                Mapping of node ids to node instances for nodes in this tree that are
                                currently selected.
                            
                                @property {Object} _selectedMap
                                @protected
                                **/
                            
                                // -- Lifecycle ------------------------------------------------------------
                                initializer: function () {
                                    this.nodeExtensions = this.nodeExtensions.concat(Y.Tree.Node.Selectable);
                                    this._selectedMap   = {};
                            
                                    Do.after(this._selectableAfterDefAddFn, this, '_defAddFn');
                                    Do.after(this._selectableAfterDefClearFn, this, '_defClearFn');
                                    Do.after(this._selectableAfterDefRemoveFn, this, '_defRemoveFn');
                            
                                    this._selectableEvents = [
                                        this.after('multiSelectChange', this._afterMultiSelectChange)
                                    ];
                                },
                            
                                destructor: function () {
                                    (new Y.EventHandle(this._selectableEvents)).detach();
                            
                                    this._selectableEvents = null;
                                    this._selectedMap      = null;
                                },
                            
                                // -- Public Methods -------------------------------------------------------
                            
                                /**
                                Returns an array of nodes that are currently selected.
                            
                                @method getSelectedNodes
                                @return {Tree.Node.Selectable[]} Array of selected nodes.
                                **/
                                getSelectedNodes: function () {
                                    return Y.Object.values(this._selectedMap);
                                },
                            
                                /**
                                Selects the specified node.
                            
                                @method selectNode
                                @param {Tree.Node.Selectable} node Node to select.
                                @param {Object} [options] Options.
                                    @param {Boolean} [options.silent=false] If `true`, the `select` event
                                        will be suppressed.
                                    @param {String} [options.src] Source of the change, to be passed along
                                        to the event facade of the resulting event. This can be used to
                                        distinguish between changes triggered by a user and changes
                                        triggered programmatically, for example.
                                @chainable
                                **/
                                selectNode: function (node, options) {
                                    // Instead of calling node.isSelected(), we look for the node in this
                                    // tree's selectedMap, which ensures that the `select` event will fire
                                    // in cases such as a node being added to this tree with its selected
                                    // state already set to true.
                                    if (!this._selectedMap[node.id]) {
                                        this._fireTreeEvent(EVT_SELECT, {
                                            node: node,
                                            src : options && options.src
                                        }, {
                                            defaultFn: this._defSelectFn,
                                            silent   : options && options.silent
                                        });
                                    }
                            
                                    return this;
                                },
                            
                                /**
                                Unselects all selected nodes.
                            
                                @method unselect
                                @param {Object} [options] Options.
                                    @param {Boolean} [options.silent=false] If `true`, the `unselect` event
                                        will be suppressed.
                                    @param {String} [options.src] Source of the change, to be passed along
                                        to the event facade of the resulting event. This can be used to
                                        distinguish between changes triggered by a user and changes
                                        triggered programmatically, for example.
                                @chainable
                                **/
                                unselect: function (options) {
                                    for (var id in this._selectedMap) {
                                        if (this._selectedMap.hasOwnProperty(id)) {
                                            this.unselectNode(this._selectedMap[id], options);
                                        }
                                    }
                            
                                    return this;
                                },
                            
                                /**
                                Unselects the specified node.
                            
                                @method unselectNode
                                @param {Tree.Node.Selectable} node Node to unselect.
                                @param {Object} [options] Options.
                                    @param {Boolean} [options.silent=false] If `true`, the `unselect` event
                                        will be suppressed.
                                    @param {String} [options.src] Source of the change, to be passed along
                                        to the event facade of the resulting event. This can be used to
                                        distinguish between changes triggered by a user and changes
                                        triggered programmatically, for example.
                                @chainable
                                **/
                                unselectNode: function (node, options) {
                                    if (node.isSelected() || this._selectedMap[node.id]) {
                                        this._fireTreeEvent(EVT_UNSELECT, {
                                            node: node,
                                            src : options && options.src
                                        }, {
                                            defaultFn: this._defUnselectFn,
                                            silent   : options && options.silent
                                        });
                                    }
                            
                                    return this;
                                },
                            
                                // -- Protected Methods ----------------------------------------------------
                                _selectableAfterDefAddFn: function (e) {
                                    // If the node is marked as selected, we need go through the select
                                    // flow.
                                    if (e.node.isSelected()) {
                                        this.selectNode(e.node);
                                    }
                                },
                            
                                _selectableAfterDefClearFn: function () {
                                    this._selectedMap = {};
                                },
                            
                                _selectableAfterDefRemoveFn: function (e) {
                                    delete e.node.state.selected;
                                    delete this._selectedMap[e.node.id];
                                },
                            
                                // -- Protected Event Handlers ---------------------------------------------
                                _afterMultiSelectChange: function () {
                                    this.unselect();
                                },
                            
                                _defSelectFn: function (e) {
                                    if (!this.get('multiSelect')) {
                                        this.unselect();
                                    }
                            
                                    e.node.state.selected = true;
                                    this._selectedMap[e.node.id] = e.node;
                                },
                            
                                _defUnselectFn: function (e) {
                                    delete e.node.state.selected;
                                    delete this._selectedMap[e.node.id];
                                }
                            };
                            
                            Selectable.ATTRS = {
                                /**
                                Whether or not to allow multiple nodes to be selected at once.
                            
                                @attribute {Boolean} multiSelect
                                @default false
                                **/
                                multiSelect: {
                                    value: false
                                }
                            };
                            
                            Y.Tree.Selectable = Selectable;