Update Polymer and pull in iron-list
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / paper-slider / paper-slider-extracted.js
blob81cd11a4656ff064d6af1a9f5fa7c9c865684de9
3   Polymer({
4     is: 'paper-slider',
6     behaviors: [
7       Polymer.IronFormElementBehavior,
8       Polymer.PaperInkyFocusBehavior,
9       Polymer.IronRangeBehavior
10     ],
12     properties: {
14       /**
15        * If true, the slider thumb snaps to tick marks evenly spaced based
16        * on the `step` property value.
17        */
18       snaps: {
19         type: Boolean,
20         value: false,
21         notify: true
22       },
24       /**
25        * If true, a pin with numeric value label is shown when the slider thumb
26        * is pressed. Use for settings for which users need to know the exact
27        * value of the setting.
28        */
29       pin: {
30         type: Boolean,
31         value: false,
32         notify: true
33       },
35       /**
36        * The number that represents the current secondary progress.
37        */
38       secondaryProgress: {
39         type: Number,
40         value: 0,
41         notify: true,
42         observer: '_secondaryProgressChanged'
43       },
45       /**
46        * If true, an input is shown and user can use it to set the slider value.
47        */
48       editable: {
49         type: Boolean,
50         value: false
51       },
53       /**
54        * The immediate value of the slider.  This value is updated while the user
55        * is dragging the slider.
56        */
57       immediateValue: {
58         type: Number,
59         value: 0,
60         readOnly: true,
61         notify: true
62       },
64       /**
65        * The maximum number of markers
66        */
67       maxMarkers: {
68         type: Number,
69         value: 0,
70         notify: true,
71         observer: '_maxMarkersChanged'
72       },
74       /**
75        * If true, the knob is expanded
76        */
77       expand: {
78         type: Boolean,
79         value: false,
80         readOnly: true
81       },
83       /**
84        * True when the user is dragging the slider.
85        */
86       dragging: {
87         type: Boolean,
88         value: false,
89         readOnly: true
90       },
92       transiting: {
93         type: Boolean,
94         value: false,
95         readOnly: true
96       },
98       markers: {
99         type: Array,
100         readOnly: true,
101         value: []
102       },
103     },
105     observers: [
106       '_updateKnob(value, min, max, snaps, step)',
107       '_minChanged(min)',
108       '_maxChanged(max)',
109       '_valueChanged(value)',
110       '_immediateValueChanged(immediateValue)'
111     ],
113     hostAttributes: {
114       role: 'slider',
115       tabindex: 0
116     },
118     keyBindings: {
119       'left down pagedown home': '_decrementKey',
120       'right up pageup end': '_incrementKey'
121     },
123     ready: function() {
124       // issue polymer/polymer#1305
126       this.async(function() {
127         this._updateKnob(this.value);
128       }, 1);
129     },
131     /**
132      * Increases value by `step` but not above `max`.
133      * @method increment
134      */
135     increment: function() {
136       this.value = this._clampValue(this.value + this.step);
137     },
139     /**
140      * Decreases value by `step` but not below `min`.
141      * @method decrement
142      */
143     decrement: function() {
144       this.value = this._clampValue(this.value - this.step);
145     },
147     _updateKnob: function(value) {
148       this._positionKnob(this._calcRatio(value));
149     },
151     _minChanged: function() {
152       this.setAttribute('aria-valuemin', this.min);
153     },
155     _maxChanged: function() {
156       this.setAttribute('aria-valuemax', this.max);
157     },
159     _valueChanged: function() {
160       this.setAttribute('aria-valuenow', this.value);
161       this.fire('value-change');
162     },
164     _immediateValueChanged: function() {
165       if (this.dragging) {
166         this.fire('immediate-value-change');
167       } else {
168         this.value = this.immediateValue;
169       }
170     },
172     _secondaryProgressChanged: function() {
173       this.secondaryProgress = this._clampValue(this.secondaryProgress);
174     },
176     _fixForInput: function(immediateValue) {
177       // paper-input/issues/114
178       return this.immediateValue.toString();
179     },
181     _expandKnob: function() {
182       this._setExpand(true);
183     },
185     _resetKnob: function() {
186       this.cancelDebouncer('expandKnob');
187       this._setExpand(false);
188     },
190     _positionKnob: function(ratio) {
191       this._setImmediateValue(this._calcStep(this._calcKnobPosition(ratio)));
192       this._setRatio(this._calcRatio(this.immediateValue));
194       this.$.sliderKnob.style.left = (this.ratio * 100) + '%';
195     },
197     _inputChange: function() {
198       this.value = this.$$('#input').value;
199       this.fire('change');
200     },
202     _calcKnobPosition: function(ratio) {
203       return (this.max - this.min) * ratio + this.min;
204     },
206     _onTrack: function(event) {
207       event.stopPropagation();
208       switch (event.detail.state) {
209         case 'start':
210           this._trackStart(event);
211           break;
212         case 'track':
213           this._trackX(event);
214           break;
215         case 'end':
216           this._trackEnd();
217           break;
218       }
219     },
221     _trackStart: function(event) {
222       this._w = this.$.sliderBar.offsetWidth;
223       this._x = this.ratio * this._w;
224       this._startx = this._x || 0;
225       this._minx = - this._startx;
226       this._maxx = this._w - this._startx;
227       this.$.sliderKnob.classList.add('dragging');
229       this._setDragging(true);
230     },
232     _trackX: function(e) {
233       if (!this.dragging) {
234         this._trackStart(e);
235       }
237       var dx = Math.min(this._maxx, Math.max(this._minx, e.detail.dx));
238       this._x = this._startx + dx;
240       var immediateValue = this._calcStep(this._calcKnobPosition(this._x / this._w));
241       this._setImmediateValue(immediateValue);
243       // update knob's position
244       var translateX = ((this._calcRatio(immediateValue) * this._w) - this._startx);
245       this.translate3d(translateX + 'px', 0, 0, this.$.sliderKnob);
246     },
248     _trackEnd: function() {
249       var s = this.$.sliderKnob.style;
251       this.$.sliderKnob.classList.remove('dragging');
252       this._setDragging(false);
253       this._resetKnob();
254       this.value = this.immediateValue;
256       s.transform = s.webkitTransform = '';
258       this.fire('change');
259     },
261     _knobdown: function(event) {
262       this._expandKnob();
264       // cancel selection
265       event.preventDefault();
267       // set the focus manually because we will called prevent default
268       this.focus();
269     },
271     _bardown: function(event) {
272       this._w = this.$.sliderBar.offsetWidth;
273       var rect = this.$.sliderBar.getBoundingClientRect();
274       var ratio = (event.detail.x - rect.left) / this._w;
275       var prevRatio = this.ratio;
277       this._setTransiting(true);
279       this._positionKnob(ratio);
281       this.debounce('expandKnob', this._expandKnob, 60);
283       // if the ratio doesn't change, sliderKnob's animation won't start
284       // and `_knobTransitionEnd` won't be called
285       // Therefore, we need to manually update the `transiting` state
287       if (prevRatio === this.ratio) {
288         this._setTransiting(false);
289       }
291       this.async(function() {
292         this.fire('change');
293       });
295       // cancel selection
296       event.preventDefault();
297     },
299     _knobTransitionEnd: function(event) {
300       if (event.target === this.$.sliderKnob) {
301         this._setTransiting(false);
302       }
303     },
305     _maxMarkersChanged: function(maxMarkers) {
306       var l = (this.max - this.min) / this.step;
307       if (!this.snaps && l > maxMarkers) {
308         this._setMarkers([]);
309       } else {
310         this._setMarkers(new Array(l));
311       }
312     },
314     _mergeClasses: function(classes) {
315       return Object.keys(classes).filter(
316         function(className) {
317           return classes[className];
318         }).join(' ');
319     },
321     _getClassNames: function() {
322       return this._mergeClasses({
323         disabled: this.disabled,
324         pin: this.pin,
325         snaps: this.snaps,
326         ring: this.immediateValue <= this.min,
327         expand: this.expand,
328         dragging: this.dragging,
329         transiting: this.transiting,
330         editable: this.editable
331       });
332     },
334     _incrementKey: function(event) {
335       if (event.detail.key === 'end') {
336         this.value = this.max;
337       } else {
338         this.increment();
339       }
340       this.fire('change');
341     },
343     _decrementKey: function(event) {
344       if (event.detail.key === 'home') {
345         this.value = this.min;
346       } else {
347         this.decrement();
348       }
349       this.fire('change');
350     }
351   });
353   /**
354    * Fired when the slider's value changes.
355    *
356    * @event value-change
357    */
359   /**
360    * Fired when the slider's immediateValue changes.
361    *
362    * @event immediate-value-change
363    */
365   /**
366    * Fired when the slider's value changes due to user interaction.
367    *
368    * Changes to the slider's value due to changes in an underlying
369    * bound variable will not trigger this event.
370    *
371    * @event change
372    */