| OLD | NEW | 
 | (Empty) | 
|    1 <!-- |  | 
|    2 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. |  | 
|    3 This code may only be used under the BSD style license found at http://polymer.g
     ithub.io/LICENSE.txt |  | 
|    4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |  | 
|    5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
     BUTORS.txt |  | 
|    6 Code distributed by Google as part of the polymer project is also |  | 
|    7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
     TS.txt |  | 
|    8 --> |  | 
|    9  |  | 
|   10 <link rel="import" href="../polymer/polymer.html"> |  | 
|   11 <link rel="import" href="iron-selection.html"> |  | 
|   12  |  | 
|   13 <script> |  | 
|   14  |  | 
|   15   /** @polymerBehavior */ |  | 
|   16   Polymer.IronSelectableBehavior = { |  | 
|   17  |  | 
|   18     properties: { |  | 
|   19  |  | 
|   20       /** |  | 
|   21        * If you want to use the attribute value of an element for `selected` ins
     tead of the index, |  | 
|   22        * set this to the name of the attribute. |  | 
|   23        * |  | 
|   24        * @attribute attrForSelected |  | 
|   25        * @type {string} |  | 
|   26        */ |  | 
|   27       attrForSelected: { |  | 
|   28         type: String, |  | 
|   29         value: null |  | 
|   30       }, |  | 
|   31  |  | 
|   32       /** |  | 
|   33        * Gets or sets the selected element. The default is to use the index of t
     he item. |  | 
|   34        * |  | 
|   35        * @attribute selected |  | 
|   36        * @type {string} |  | 
|   37        */ |  | 
|   38       selected: { |  | 
|   39         type: String, |  | 
|   40         notify: true |  | 
|   41       }, |  | 
|   42  |  | 
|   43       /** |  | 
|   44        * Returns the currently selected item. |  | 
|   45        * |  | 
|   46        * @attribute selectedItem |  | 
|   47        * @type {Object} |  | 
|   48        */ |  | 
|   49       selectedItem: { |  | 
|   50         type: Object, |  | 
|   51         readOnly: true, |  | 
|   52         notify: true |  | 
|   53       }, |  | 
|   54  |  | 
|   55       /** |  | 
|   56        * The event that fires from items when they are selected. Selectable |  | 
|   57        * will listen for this event from items and update the selection state. |  | 
|   58        * Set to empty string to listen to no events. |  | 
|   59        * |  | 
|   60        * @attribute activateEvent |  | 
|   61        * @type {string} |  | 
|   62        * @default 'tap' |  | 
|   63        */ |  | 
|   64       activateEvent: { |  | 
|   65         type: String, |  | 
|   66         value: 'tap', |  | 
|   67         observer: '_activateEventChanged' |  | 
|   68       }, |  | 
|   69  |  | 
|   70       /** |  | 
|   71        * This is a CSS selector sting.  If this is set, only items that matches 
     the CSS selector |  | 
|   72        * are selectable. |  | 
|   73        * |  | 
|   74        * @attribute selectable |  | 
|   75        * @type {string} |  | 
|   76        */ |  | 
|   77       selectable: String, |  | 
|   78  |  | 
|   79       /** |  | 
|   80        * The class to set on elements when selected. |  | 
|   81        * |  | 
|   82        * @attribute selectedClass |  | 
|   83        * @type {string} |  | 
|   84        */ |  | 
|   85       selectedClass: { |  | 
|   86         type: String, |  | 
|   87         value: 'iron-selected' |  | 
|   88       }, |  | 
|   89  |  | 
|   90       /** |  | 
|   91        * The attribute to set on elements when selected. |  | 
|   92        * |  | 
|   93        * @attribute selectedAttribute |  | 
|   94        * @type {string} |  | 
|   95        */ |  | 
|   96       selectedAttribute: { |  | 
|   97         type: String, |  | 
|   98         value: null |  | 
|   99       } |  | 
|  100  |  | 
|  101     }, |  | 
|  102  |  | 
|  103     observers: [ |  | 
|  104       '_updateSelected(attrForSelected, selected)' |  | 
|  105     ], |  | 
|  106  |  | 
|  107     excludedLocalNames: { |  | 
|  108       'template': 1 |  | 
|  109     }, |  | 
|  110  |  | 
|  111     created: function() { |  | 
|  112       this._bindFilterItem = this._filterItem.bind(this); |  | 
|  113       this._selection = new Polymer.IronSelection(this._applySelection.bind(this
     )); |  | 
|  114     }, |  | 
|  115  |  | 
|  116     attached: function() { |  | 
|  117       this._observer = this._observeItems(this); |  | 
|  118       this._contentObserver = this._observeContent(this); |  | 
|  119     }, |  | 
|  120  |  | 
|  121     detached: function() { |  | 
|  122       if (this._observer) { |  | 
|  123         this._observer.disconnect(); |  | 
|  124       } |  | 
|  125       if (this._contentObserver) { |  | 
|  126         this._contentObserver.disconnect(); |  | 
|  127       } |  | 
|  128       this._removeListener(this.activateEvent); |  | 
|  129     }, |  | 
|  130  |  | 
|  131     /** |  | 
|  132      * Returns an array of selectable items. |  | 
|  133      * |  | 
|  134      * @property items |  | 
|  135      * @type Array |  | 
|  136      */ |  | 
|  137     get items() { |  | 
|  138       var nodes = Polymer.dom(this).queryDistributedElements(this.selectable || 
     '*'); |  | 
|  139       return Array.prototype.filter.call(nodes, this._bindFilterItem); |  | 
|  140     }, |  | 
|  141  |  | 
|  142     /** |  | 
|  143      * Returns the index of the given item. |  | 
|  144      * |  | 
|  145      * @method indexOf |  | 
|  146      * @param {Object} item |  | 
|  147      * @returns Returns the index of the item |  | 
|  148      */ |  | 
|  149     indexOf: function(item) { |  | 
|  150       return this.items.indexOf(item); |  | 
|  151     }, |  | 
|  152  |  | 
|  153     /** |  | 
|  154      * Selects the given value. |  | 
|  155      * |  | 
|  156      * @method select |  | 
|  157      * @param {string} value the value to select. |  | 
|  158      */ |  | 
|  159     select: function(value) { |  | 
|  160       this.selected = value; |  | 
|  161     }, |  | 
|  162  |  | 
|  163     /** |  | 
|  164      * Selects the previous item. |  | 
|  165      * |  | 
|  166      * @method selectPrevious |  | 
|  167      */ |  | 
|  168     selectPrevious: function() { |  | 
|  169       var length = this.items.length; |  | 
|  170       var index = (Number(this._valueToIndex(this.selected)) - 1 + length) % len
     gth; |  | 
|  171       this.selected = this._indexToValue(index); |  | 
|  172     }, |  | 
|  173  |  | 
|  174     /** |  | 
|  175      * Selects the next item. |  | 
|  176      * |  | 
|  177      * @method selectNext |  | 
|  178      */ |  | 
|  179     selectNext: function() { |  | 
|  180       var index = (Number(this._valueToIndex(this.selected)) + 1) % this.items.l
     ength; |  | 
|  181       this.selected = this._indexToValue(index); |  | 
|  182     }, |  | 
|  183  |  | 
|  184     _addListener: function(eventName) { |  | 
|  185       this.listen(this, eventName, '_activateHandler'); |  | 
|  186     }, |  | 
|  187  |  | 
|  188     _removeListener: function(eventName) { |  | 
|  189       // There is no unlisten yet... |  | 
|  190       // https://github.com/Polymer/polymer/issues/1639 |  | 
|  191       //this.removeEventListener(eventName, this._bindActivateHandler); |  | 
|  192     }, |  | 
|  193  |  | 
|  194     _activateEventChanged: function(eventName, old) { |  | 
|  195       this._removeListener(old); |  | 
|  196       this._addListener(eventName); |  | 
|  197     }, |  | 
|  198  |  | 
|  199     _updateSelected: function() { |  | 
|  200       this._selectSelected(this.selected); |  | 
|  201     }, |  | 
|  202  |  | 
|  203     _selectSelected: function(selected) { |  | 
|  204       this._selection.select(this._valueToItem(this.selected)); |  | 
|  205     }, |  | 
|  206  |  | 
|  207     _filterItem: function(node) { |  | 
|  208       return !this.excludedLocalNames[node.localName]; |  | 
|  209     }, |  | 
|  210  |  | 
|  211     _valueToItem: function(value) { |  | 
|  212       return (value == null) ? null : this.items[this._valueToIndex(value)]; |  | 
|  213     }, |  | 
|  214  |  | 
|  215     _valueToIndex: function(value) { |  | 
|  216       if (this.attrForSelected) { |  | 
|  217         for (var i = 0, item; item = this.items[i]; i++) { |  | 
|  218           if (this._valueForItem(item) == value) { |  | 
|  219             return i; |  | 
|  220           } |  | 
|  221         } |  | 
|  222       } else { |  | 
|  223         return Number(value); |  | 
|  224       } |  | 
|  225     }, |  | 
|  226  |  | 
|  227     _indexToValue: function(index) { |  | 
|  228       if (this.attrForSelected) { |  | 
|  229         var item = this.items[index]; |  | 
|  230         if (item) { |  | 
|  231           return this._valueForItem(item); |  | 
|  232         } |  | 
|  233       } else { |  | 
|  234         return index; |  | 
|  235       } |  | 
|  236     }, |  | 
|  237  |  | 
|  238     _valueForItem: function(item) { |  | 
|  239       return item[this.attrForSelected] || item.getAttribute(this.attrForSelecte
     d); |  | 
|  240     }, |  | 
|  241  |  | 
|  242     _applySelection: function(item, isSelected) { |  | 
|  243       if (this.selectedClass) { |  | 
|  244         this.toggleClass(this.selectedClass, isSelected, item); |  | 
|  245       } |  | 
|  246       if (this.selectedAttribute) { |  | 
|  247         this.toggleAttribute(this.selectedAttribute, isSelected, item); |  | 
|  248       } |  | 
|  249       this._selectionChange(); |  | 
|  250       this.fire('iron-' + (isSelected ? 'select' : 'deselect'), {item: item}); |  | 
|  251     }, |  | 
|  252  |  | 
|  253     _selectionChange: function() { |  | 
|  254       this._setSelectedItem(this._selection.get()); |  | 
|  255     }, |  | 
|  256  |  | 
|  257     // observe content changes under the given node. |  | 
|  258     _observeContent: function(node) { |  | 
|  259       var content = node.querySelector('content'); |  | 
|  260       if (content && content.parentElement === node) { |  | 
|  261         return this._observeItems(node.domHost); |  | 
|  262       } |  | 
|  263     }, |  | 
|  264  |  | 
|  265     // observe items change under the given node. |  | 
|  266     _observeItems: function(node) { |  | 
|  267       var observer = new MutationObserver(function() { |  | 
|  268         if (this.selected != null) { |  | 
|  269           this._updateSelected(); |  | 
|  270         } |  | 
|  271       }.bind(this)); |  | 
|  272       observer.observe(node, { |  | 
|  273         childList: true, |  | 
|  274         subtree: true |  | 
|  275       }); |  | 
|  276       return observer; |  | 
|  277     }, |  | 
|  278  |  | 
|  279     _activateHandler: function(e) { |  | 
|  280       // TODO: remove this when https://github.com/Polymer/polymer/issues/1639 i
     s fixed so we |  | 
|  281       // can just remove the old event listener. |  | 
|  282       if (e.type !== this.activateEvent) { |  | 
|  283         return; |  | 
|  284       } |  | 
|  285       var t = e.target; |  | 
|  286       var items = this.items; |  | 
|  287       while (t && t != this) { |  | 
|  288         var i = items.indexOf(t); |  | 
|  289         if (i >= 0) { |  | 
|  290           var value = this._indexToValue(i); |  | 
|  291           this._itemActivate(value, t); |  | 
|  292           return; |  | 
|  293         } |  | 
|  294         t = t.parentNode; |  | 
|  295       } |  | 
|  296     }, |  | 
|  297  |  | 
|  298     _itemActivate: function(value, item) { |  | 
|  299       if (!this.fire('iron-activate', |  | 
|  300           {selected: value, item: item}, {cancelable: true}).defaultPrevented) { |  | 
|  301         this.select(value); |  | 
|  302       } |  | 
|  303     } |  | 
|  304  |  | 
|  305   }; |  | 
|  306  |  | 
|  307 </script> |  | 
| OLD | NEW |