materialize-sass 0.97.8 → 1.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +1 -0
  3. data/.gitignore +1 -0
  4. data/README.md +34 -32
  5. data/Rakefile +38 -21
  6. data/assets/javascripts/materialize/anime.min.js +417 -0
  7. data/assets/javascripts/materialize/autocomplete.js +504 -0
  8. data/assets/javascripts/materialize/buttons.js +409 -0
  9. data/assets/javascripts/materialize/cards.js +34 -0
  10. data/assets/javascripts/materialize/carousel.js +797 -0
  11. data/assets/javascripts/materialize/cash.js +990 -0
  12. data/assets/javascripts/materialize/characterCounter.js +180 -0
  13. data/assets/javascripts/materialize/chips.js +564 -0
  14. data/assets/javascripts/materialize/collapsible.js +337 -0
  15. data/assets/javascripts/materialize/component.js +57 -0
  16. data/assets/javascripts/materialize/datepicker.js +935 -0
  17. data/assets/javascripts/materialize/dropdown.js +659 -0
  18. data/assets/javascripts/materialize/extras/nouislider.js +2147 -0
  19. data/assets/javascripts/materialize/extras/nouislider.min.js +1 -0
  20. data/assets/javascripts/materialize/forms.js +244 -0
  21. data/assets/javascripts/materialize/global.js +408 -0
  22. data/assets/javascripts/materialize/materialbox.js +513 -0
  23. data/assets/javascripts/materialize/modal.js +449 -0
  24. data/assets/javascripts/materialize/parallax.js +173 -0
  25. data/assets/javascripts/materialize/pushpin.js +179 -0
  26. data/assets/javascripts/materialize/range.js +310 -0
  27. data/assets/javascripts/materialize/scrollspy.js +328 -0
  28. data/assets/javascripts/materialize/select.js +497 -0
  29. data/assets/javascripts/materialize/sidenav.js +655 -0
  30. data/assets/javascripts/materialize/slider.js +424 -0
  31. data/assets/javascripts/materialize/tabs.js +476 -0
  32. data/assets/javascripts/materialize/tapTarget.js +364 -0
  33. data/assets/javascripts/materialize/timepicker.js +647 -0
  34. data/assets/javascripts/materialize/toasts.js +355 -0
  35. data/assets/javascripts/materialize/tooltip.js +351 -0
  36. data/{app/assets → assets}/javascripts/materialize/waves.js +42 -47
  37. data/{app/assets → assets}/javascripts/materialize-sprockets.js +12 -13
  38. data/assets/javascripts/materialize.js +12374 -0
  39. data/assets/stylesheets/materialize/components/_badges.scss +55 -0
  40. data/{app/assets → assets}/stylesheets/materialize/components/_buttons.scss +99 -58
  41. data/{app/assets → assets}/stylesheets/materialize/components/_cards.scss +14 -6
  42. data/{app/assets → assets}/stylesheets/materialize/components/_carousel.scss +12 -7
  43. data/{app/assets → assets}/stylesheets/materialize/components/_chips.scss +13 -6
  44. data/{app/assets → assets}/stylesheets/materialize/components/_collapsible.scss +16 -15
  45. data/assets/stylesheets/materialize/components/_color-classes.scss +32 -0
  46. data/{app/assets/stylesheets/materialize/components/_color.scss → assets/stylesheets/materialize/components/_color-variables.scss} +2 -44
  47. data/assets/stylesheets/materialize/components/_datepicker.scss +191 -0
  48. data/{app/assets → assets}/stylesheets/materialize/components/_dropdown.scss +35 -15
  49. data/{app/assets → assets}/stylesheets/materialize/components/_global.scss +96 -125
  50. data/{app/assets → assets}/stylesheets/materialize/components/_grid.scss +45 -36
  51. data/{app/assets → assets}/stylesheets/materialize/components/_icons-material-design.scss +0 -0
  52. data/{app/assets → assets}/stylesheets/materialize/components/_materialbox.scss +13 -12
  53. data/{app/assets → assets}/stylesheets/materialize/components/_modal.scss +7 -3
  54. data/{app/assets → assets}/stylesheets/materialize/components/_navbar.scss +29 -11
  55. data/assets/stylesheets/materialize/components/_normalize.scss +447 -0
  56. data/{app/assets → assets}/stylesheets/materialize/components/_preloader.scss +2 -2
  57. data/assets/stylesheets/materialize/components/_pulse.scss +34 -0
  58. data/{app/assets/stylesheets/materialize/components/_sideNav.scss → assets/stylesheets/materialize/components/_sidenav.scss} +47 -47
  59. data/{app/assets → assets}/stylesheets/materialize/components/_slider.scss +0 -0
  60. data/{app/assets → assets}/stylesheets/materialize/components/_table_of_contents.scss +5 -5
  61. data/{app/assets → assets}/stylesheets/materialize/components/_tabs.scss +10 -10
  62. data/assets/stylesheets/materialize/components/_tapTarget.scss +103 -0
  63. data/assets/stylesheets/materialize/components/_timepicker.scss +183 -0
  64. data/{app/assets → assets}/stylesheets/materialize/components/_toast.scss +7 -14
  65. data/{app/assets → assets}/stylesheets/materialize/components/_tooltip.scss +3 -3
  66. data/assets/stylesheets/materialize/components/_transitions.scss +13 -0
  67. data/{app/assets → assets}/stylesheets/materialize/components/_typography.scss +8 -9
  68. data/{app/assets → assets}/stylesheets/materialize/components/_variables.scss +65 -29
  69. data/assets/stylesheets/materialize/components/_waves.scss +114 -0
  70. data/{app/assets → assets}/stylesheets/materialize/components/forms/_checkboxes.scss +26 -46
  71. data/{app/assets → assets}/stylesheets/materialize/components/forms/_file-input.scss +6 -0
  72. data/{app/assets → assets}/stylesheets/materialize/components/forms/_forms.scss +0 -0
  73. data/{app/assets → assets}/stylesheets/materialize/components/forms/_input-fields.scss +131 -63
  74. data/assets/stylesheets/materialize/components/forms/_radio-buttons.scss +115 -0
  75. data/{app/assets → assets}/stylesheets/materialize/components/forms/_range.scss +35 -33
  76. data/{app/assets → assets}/stylesheets/materialize/components/forms/_select.scss +88 -19
  77. data/{app/assets → assets}/stylesheets/materialize/components/forms/_switches.scss +32 -21
  78. data/assets/stylesheets/materialize/extras/nouislider.css +406 -0
  79. data/{app/assets → assets}/stylesheets/materialize.scss +10 -9
  80. data/lib/materialize-sass/engine.rb +9 -7
  81. data/lib/materialize-sass/helpers.rb +38 -0
  82. data/lib/materialize-sass/version.rb +1 -1
  83. data/lib/materialize-sass.rb +13 -28
  84. data/materialize-sass.gemspec +5 -5
  85. metadata +97 -119
  86. data/app/assets/fonts/roboto/Roboto-Bold.eot +0 -0
  87. data/app/assets/fonts/roboto/Roboto-Bold.ttf +0 -0
  88. data/app/assets/fonts/roboto/Roboto-Bold.woff +0 -0
  89. data/app/assets/fonts/roboto/Roboto-Bold.woff2 +0 -0
  90. data/app/assets/fonts/roboto/Roboto-Light.eot +0 -0
  91. data/app/assets/fonts/roboto/Roboto-Light.ttf +0 -0
  92. data/app/assets/fonts/roboto/Roboto-Light.woff +0 -0
  93. data/app/assets/fonts/roboto/Roboto-Light.woff2 +0 -0
  94. data/app/assets/fonts/roboto/Roboto-Medium.eot +0 -0
  95. data/app/assets/fonts/roboto/Roboto-Medium.ttf +0 -0
  96. data/app/assets/fonts/roboto/Roboto-Medium.woff +0 -0
  97. data/app/assets/fonts/roboto/Roboto-Medium.woff2 +0 -0
  98. data/app/assets/fonts/roboto/Roboto-Regular.eot +0 -0
  99. data/app/assets/fonts/roboto/Roboto-Regular.ttf +0 -0
  100. data/app/assets/fonts/roboto/Roboto-Regular.woff +0 -0
  101. data/app/assets/fonts/roboto/Roboto-Regular.woff2 +0 -0
  102. data/app/assets/fonts/roboto/Roboto-Thin.eot +0 -0
  103. data/app/assets/fonts/roboto/Roboto-Thin.ttf +0 -0
  104. data/app/assets/fonts/roboto/Roboto-Thin.woff +0 -0
  105. data/app/assets/fonts/roboto/Roboto-Thin.woff2 +0 -0
  106. data/app/assets/javascripts/materialize/animation.js +0 -9
  107. data/app/assets/javascripts/materialize/buttons.js +0 -267
  108. data/app/assets/javascripts/materialize/cards.js +0 -26
  109. data/app/assets/javascripts/materialize/carousel.js +0 -454
  110. data/app/assets/javascripts/materialize/character_counter.js +0 -72
  111. data/app/assets/javascripts/materialize/chips.js +0 -289
  112. data/app/assets/javascripts/materialize/collapsible.js +0 -160
  113. data/app/assets/javascripts/materialize/date_picker/picker.date.js +0 -1430
  114. data/app/assets/javascripts/materialize/date_picker/picker.js +0 -1123
  115. data/app/assets/javascripts/materialize/dropdown.js +0 -265
  116. data/app/assets/javascripts/materialize/extras/nouislider.js +0 -1666
  117. data/app/assets/javascripts/materialize/extras/nouislider.min.js +0 -1
  118. data/app/assets/javascripts/materialize/forms.js +0 -682
  119. data/app/assets/javascripts/materialize/global.js +0 -98
  120. data/app/assets/javascripts/materialize/hammer.min.js +0 -1
  121. data/app/assets/javascripts/materialize/init.js +0 -174
  122. data/app/assets/javascripts/materialize/initial.js +0 -11
  123. data/app/assets/javascripts/materialize/jquery.easing.1.3.js +0 -205
  124. data/app/assets/javascripts/materialize/jquery.hammer.js +0 -33
  125. data/app/assets/javascripts/materialize/jquery.timeago.min.js +0 -1
  126. data/app/assets/javascripts/materialize/materialbox.js +0 -269
  127. data/app/assets/javascripts/materialize/modal.js +0 -184
  128. data/app/assets/javascripts/materialize/parallax.js +0 -58
  129. data/app/assets/javascripts/materialize/prism.js +0 -8
  130. data/app/assets/javascripts/materialize/pushpin.js +0 -71
  131. data/app/assets/javascripts/materialize/scrollFire.js +0 -48
  132. data/app/assets/javascripts/materialize/scrollspy.js +0 -284
  133. data/app/assets/javascripts/materialize/sideNav.js +0 -370
  134. data/app/assets/javascripts/materialize/slider.js +0 -321
  135. data/app/assets/javascripts/materialize/tabs.js +0 -164
  136. data/app/assets/javascripts/materialize/toasts.js +0 -137
  137. data/app/assets/javascripts/materialize/tooltip.js +0 -236
  138. data/app/assets/javascripts/materialize/transitions.js +0 -169
  139. data/app/assets/javascripts/materialize/velocity.min.js +0 -5
  140. data/app/assets/javascripts/materialize.js +0 -5
  141. data/app/assets/stylesheets/materialize/components/_mixins.scss +0 -5
  142. data/app/assets/stylesheets/materialize/components/_normalize.scss +0 -424
  143. data/app/assets/stylesheets/materialize/components/_prefixer.scss +0 -384
  144. data/app/assets/stylesheets/materialize/components/_roboto.scss +0 -49
  145. data/app/assets/stylesheets/materialize/components/_waves.scss +0 -177
  146. data/app/assets/stylesheets/materialize/components/date_picker/_default.date.scss +0 -435
  147. data/app/assets/stylesheets/materialize/components/date_picker/_default.scss +0 -201
  148. data/app/assets/stylesheets/materialize/components/date_picker/_default.time.scss +0 -125
  149. data/app/assets/stylesheets/materialize/components/forms/_radio-buttons.scss +0 -117
  150. data/app/assets/stylesheets/materialize/extras/nouislider.css +0 -259
@@ -0,0 +1,2147 @@
1
+ /*!
2
+ * Materialize 1.0.0 (http://materializecss.com)
3
+ * Copyright 2014-2015 Materialize
4
+ * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE)
5
+ */
6
+
7
+ // wNumb
8
+ (function(){function r(b){return b.split("").reverse().join("")}function s(b,f,c){if((b[f]||b[c])&&b[f]===b[c])throw Error(f);}function v(b,f,c,d,e,p,q,k,l,h,n,a){q=a;var m,g=n="";p&&(a=p(a));if("number"!==typeof a||!isFinite(a))return!1;b&&0===parseFloat(a.toFixed(b))&&(a=0);0>a&&(m=!0,a=Math.abs(a));b&&(p=Math.pow(10,b),a=(Math.round(a*p)/p).toFixed(b));a=a.toString();-1!==a.indexOf(".")&&(b=a.split("."),a=b[0],c&&(n=c+b[1]));f&&(a=r(a).match(/.{1,3}/g),a=r(a.join(r(f))));m&&k&&(g+=k);d&&(g+=d);
9
+ m&&l&&(g+=l);g=g+a+n;e&&(g+=e);h&&(g=h(g,q));return g}function w(b,f,c,d,e,h,q,k,l,r,n,a){var m;b="";n&&(a=n(a));if(!a||"string"!==typeof a)return!1;k&&a.substring(0,k.length)===k&&(a=a.replace(k,""),m=!0);d&&a.substring(0,d.length)===d&&(a=a.replace(d,""));l&&a.substring(0,l.length)===l&&(a=a.replace(l,""),m=!0);e&&a.slice(-1*e.length)===e&&(a=a.slice(0,-1*e.length));f&&(a=a.split(f).join(""));c&&(a=a.replace(c,"."));m&&(b+="-");b=Number((b+a).replace(/[^0-9\.\-.]/g,""));q&&(b=q(b));return"number"===
10
+ typeof b&&isFinite(b)?b:!1}function x(b){var f,c,d,e={};for(f=0;f<h.length;f+=1)c=h[f],d=b[c],void 0===d?e[c]="negative"!==c||e.negativeBefore?"mark"===c&&"."!==e.thousand?".":!1:"-":"decimals"===c?0<d&&8>d&&(e[c]=d):"encoder"===c||"decoder"===c||"edit"===c||"undo"===c?"function"===typeof d&&(e[c]=d):"string"===typeof d&&(e[c]=d);s(e,"mark","thousand");s(e,"prefix","negative");s(e,"prefix","negativeBefore");return e}function u(b,f,c){var d,e=[];for(d=0;d<h.length;d+=1)e.push(b[h[d]]);e.push(c);return f.apply("",
11
+ e)}function t(b){if(!(this instanceof t))return new t(b);"object"===typeof b&&(b=x(b),this.to=function(f){return u(b,v,f)},this.from=function(f){return u(b,w,f)})}var h="decimals thousand mark prefix postfix encoder decoder negativeBefore negative edit undo".split(" ");window.wNumb=t})();
12
+
13
+ /*! nouislider - 9.1.0 - 2016-12-10 16:00:32 */
14
+
15
+ (function (factory) {
16
+
17
+ if ( typeof define === 'function' && define.amd ) {
18
+
19
+ // AMD. Register as an anonymous module.
20
+ define([], factory);
21
+
22
+ } else if ( typeof exports === 'object' ) {
23
+
24
+ // Node/CommonJS
25
+ module.exports = factory();
26
+
27
+ } else {
28
+
29
+ // Browser globals
30
+ window.noUiSlider = factory();
31
+ }
32
+
33
+ }(function( ){
34
+
35
+ 'use strict';
36
+
37
+
38
+ // Creates a node, adds it to target, returns the new node.
39
+ function addNodeTo ( target, className ) {
40
+ var div = document.createElement('div');
41
+ addClass(div, className);
42
+ target.appendChild(div);
43
+ return div;
44
+ }
45
+
46
+ // Removes duplicates from an array.
47
+ function unique ( array ) {
48
+ return array.filter(function(a){
49
+ return !this[a] ? this[a] = true : false;
50
+ }, {});
51
+ }
52
+
53
+ // Round a value to the closest 'to'.
54
+ function closest ( value, to ) {
55
+ return Math.round(value / to) * to;
56
+ }
57
+
58
+ // Current position of an element relative to the document.
59
+ function offset ( elem, orientation ) {
60
+
61
+ var rect = elem.getBoundingClientRect(),
62
+ doc = elem.ownerDocument,
63
+ docElem = doc.documentElement,
64
+ pageOffset = getPageOffset();
65
+
66
+ // getBoundingClientRect contains left scroll in Chrome on Android.
67
+ // I haven't found a feature detection that proves this. Worst case
68
+ // scenario on mis-match: the 'tap' feature on horizontal sliders breaks.
69
+ if ( /webkit.*Chrome.*Mobile/i.test(navigator.userAgent) ) {
70
+ pageOffset.x = 0;
71
+ }
72
+
73
+ return orientation ? (rect.top + pageOffset.y - docElem.clientTop) : (rect.left + pageOffset.x - docElem.clientLeft);
74
+ }
75
+
76
+ // Checks whether a value is numerical.
77
+ function isNumeric ( a ) {
78
+ return typeof a === 'number' && !isNaN( a ) && isFinite( a );
79
+ }
80
+
81
+ // Sets a class and removes it after [duration] ms.
82
+ function addClassFor ( element, className, duration ) {
83
+ if (duration > 0) {
84
+ addClass(element, className);
85
+ setTimeout(function(){
86
+ removeClass(element, className);
87
+ }, duration);
88
+ }
89
+ }
90
+
91
+ // Limits a value to 0 - 100
92
+ function limit ( a ) {
93
+ return Math.max(Math.min(a, 100), 0);
94
+ }
95
+
96
+ // Wraps a variable as an array, if it isn't one yet.
97
+ // Note that an input array is returned by reference!
98
+ function asArray ( a ) {
99
+ return Array.isArray(a) ? a : [a];
100
+ }
101
+
102
+ // Counts decimals
103
+ function countDecimals ( numStr ) {
104
+ numStr = String(numStr);
105
+ var pieces = numStr.split(".");
106
+ return pieces.length > 1 ? pieces[1].length : 0;
107
+ }
108
+
109
+ // http://youmightnotneedjquery.com/#add_class
110
+ function addClass ( el, className ) {
111
+ if ( el.classList ) {
112
+ el.classList.add(className);
113
+ } else {
114
+ el.className += ' ' + className;
115
+ }
116
+ }
117
+
118
+ // http://youmightnotneedjquery.com/#remove_class
119
+ function removeClass ( el, className ) {
120
+ if ( el.classList ) {
121
+ el.classList.remove(className);
122
+ } else {
123
+ el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
124
+ }
125
+ }
126
+
127
+ // https://plainjs.com/javascript/attributes/adding-removing-and-testing-for-classes-9/
128
+ function hasClass ( el, className ) {
129
+ return el.classList ? el.classList.contains(className) : new RegExp('\\b' + className + '\\b').test(el.className);
130
+ }
131
+
132
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
133
+ function getPageOffset ( ) {
134
+
135
+ var supportPageOffset = window.pageXOffset !== undefined,
136
+ isCSS1Compat = ((document.compatMode || "") === "CSS1Compat"),
137
+ x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft,
138
+ y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
139
+
140
+ return {
141
+ x: x,
142
+ y: y
143
+ };
144
+ }
145
+
146
+ // we provide a function to compute constants instead
147
+ // of accessing window.* as soon as the module needs it
148
+ // so that we do not compute anything if not needed
149
+ function getActions ( ) {
150
+
151
+ // Determine the events to bind. IE11 implements pointerEvents without
152
+ // a prefix, which breaks compatibility with the IE10 implementation.
153
+ return window.navigator.pointerEnabled ? {
154
+ start: 'pointerdown',
155
+ move: 'pointermove',
156
+ end: 'pointerup'
157
+ } : window.navigator.msPointerEnabled ? {
158
+ start: 'MSPointerDown',
159
+ move: 'MSPointerMove',
160
+ end: 'MSPointerUp'
161
+ } : {
162
+ start: 'mousedown touchstart',
163
+ move: 'mousemove touchmove',
164
+ end: 'mouseup touchend'
165
+ };
166
+ }
167
+
168
+
169
+ // Value calculation
170
+
171
+ // Determine the size of a sub-range in relation to a full range.
172
+ function subRangeRatio ( pa, pb ) {
173
+ return (100 / (pb - pa));
174
+ }
175
+
176
+ // (percentage) How many percent is this value of this range?
177
+ function fromPercentage ( range, value ) {
178
+ return (value * 100) / ( range[1] - range[0] );
179
+ }
180
+
181
+ // (percentage) Where is this value on this range?
182
+ function toPercentage ( range, value ) {
183
+ return fromPercentage( range, range[0] < 0 ?
184
+ value + Math.abs(range[0]) :
185
+ value - range[0] );
186
+ }
187
+
188
+ // (value) How much is this percentage on this range?
189
+ function isPercentage ( range, value ) {
190
+ return ((value * ( range[1] - range[0] )) / 100) + range[0];
191
+ }
192
+
193
+
194
+ // Range conversion
195
+
196
+ function getJ ( value, arr ) {
197
+
198
+ var j = 1;
199
+
200
+ while ( value >= arr[j] ){
201
+ j += 1;
202
+ }
203
+
204
+ return j;
205
+ }
206
+
207
+ // (percentage) Input a value, find where, on a scale of 0-100, it applies.
208
+ function toStepping ( xVal, xPct, value ) {
209
+
210
+ if ( value >= xVal.slice(-1)[0] ){
211
+ return 100;
212
+ }
213
+
214
+ var j = getJ( value, xVal ), va, vb, pa, pb;
215
+
216
+ va = xVal[j-1];
217
+ vb = xVal[j];
218
+ pa = xPct[j-1];
219
+ pb = xPct[j];
220
+
221
+ return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
222
+ }
223
+
224
+ // (value) Input a percentage, find where it is on the specified range.
225
+ function fromStepping ( xVal, xPct, value ) {
226
+
227
+ // There is no range group that fits 100
228
+ if ( value >= 100 ){
229
+ return xVal.slice(-1)[0];
230
+ }
231
+
232
+ var j = getJ( value, xPct ), va, vb, pa, pb;
233
+
234
+ va = xVal[j-1];
235
+ vb = xVal[j];
236
+ pa = xPct[j-1];
237
+ pb = xPct[j];
238
+
239
+ return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
240
+ }
241
+
242
+ // (percentage) Get the step that applies at a certain value.
243
+ function getStep ( xPct, xSteps, snap, value ) {
244
+
245
+ if ( value === 100 ) {
246
+ return value;
247
+ }
248
+
249
+ var j = getJ( value, xPct ), a, b;
250
+
251
+ // If 'snap' is set, steps are used as fixed points on the slider.
252
+ if ( snap ) {
253
+
254
+ a = xPct[j-1];
255
+ b = xPct[j];
256
+
257
+ // Find the closest position, a or b.
258
+ if ((value - a) > ((b-a)/2)){
259
+ return b;
260
+ }
261
+
262
+ return a;
263
+ }
264
+
265
+ if ( !xSteps[j-1] ){
266
+ return value;
267
+ }
268
+
269
+ return xPct[j-1] + closest(
270
+ value - xPct[j-1],
271
+ xSteps[j-1]
272
+ );
273
+ }
274
+
275
+
276
+ // Entry parsing
277
+
278
+ function handleEntryPoint ( index, value, that ) {
279
+
280
+ var percentage;
281
+
282
+ // Wrap numerical input in an array.
283
+ if ( typeof value === "number" ) {
284
+ value = [value];
285
+ }
286
+
287
+ // Reject any invalid input, by testing whether value is an array.
288
+ if ( Object.prototype.toString.call( value ) !== '[object Array]' ){
289
+ throw new Error("noUiSlider: 'range' contains invalid value.");
290
+ }
291
+
292
+ // Covert min/max syntax to 0 and 100.
293
+ if ( index === 'min' ) {
294
+ percentage = 0;
295
+ } else if ( index === 'max' ) {
296
+ percentage = 100;
297
+ } else {
298
+ percentage = parseFloat( index );
299
+ }
300
+
301
+ // Check for correct input.
302
+ if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
303
+ throw new Error("noUiSlider: 'range' value isn't numeric.");
304
+ }
305
+
306
+ // Store values.
307
+ that.xPct.push( percentage );
308
+ that.xVal.push( value[0] );
309
+
310
+ // NaN will evaluate to false too, but to keep
311
+ // logging clear, set step explicitly. Make sure
312
+ // not to override the 'step' setting with false.
313
+ if ( !percentage ) {
314
+ if ( !isNaN( value[1] ) ) {
315
+ that.xSteps[0] = value[1];
316
+ }
317
+ } else {
318
+ that.xSteps.push( isNaN(value[1]) ? false : value[1] );
319
+ }
320
+
321
+ that.xHighestCompleteStep.push(0);
322
+ }
323
+
324
+ function handleStepPoint ( i, n, that ) {
325
+
326
+ // Ignore 'false' stepping.
327
+ if ( !n ) {
328
+ return true;
329
+ }
330
+
331
+ // Factor to range ratio
332
+ that.xSteps[i] = fromPercentage([
333
+ that.xVal[i]
334
+ ,that.xVal[i+1]
335
+ ], n) / subRangeRatio (
336
+ that.xPct[i],
337
+ that.xPct[i+1] );
338
+
339
+ var totalSteps = (that.xVal[i+1] - that.xVal[i]) / that.xNumSteps[i];
340
+ var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1);
341
+ var step = that.xVal[i] + (that.xNumSteps[i] * highestStep);
342
+
343
+ that.xHighestCompleteStep[i] = step;
344
+ }
345
+
346
+
347
+ // Interface
348
+
349
+ // The interface to Spectrum handles all direction-based
350
+ // conversions, so the above values are unaware.
351
+
352
+ function Spectrum ( entry, snap, direction, singleStep ) {
353
+
354
+ this.xPct = [];
355
+ this.xVal = [];
356
+ this.xSteps = [ singleStep || false ];
357
+ this.xNumSteps = [ false ];
358
+ this.xHighestCompleteStep = [];
359
+
360
+ this.snap = snap;
361
+ this.direction = direction;
362
+
363
+ var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ];
364
+
365
+ // Map the object keys to an array.
366
+ for ( index in entry ) {
367
+ if ( entry.hasOwnProperty(index) ) {
368
+ ordered.push([entry[index], index]);
369
+ }
370
+ }
371
+
372
+ // Sort all entries by value (numeric sort).
373
+ if ( ordered.length && typeof ordered[0][0] === "object" ) {
374
+ ordered.sort(function(a, b) { return a[0][0] - b[0][0]; });
375
+ } else {
376
+ ordered.sort(function(a, b) { return a[0] - b[0]; });
377
+ }
378
+
379
+
380
+ // Convert all entries to subranges.
381
+ for ( index = 0; index < ordered.length; index++ ) {
382
+ handleEntryPoint(ordered[index][1], ordered[index][0], this);
383
+ }
384
+
385
+ // Store the actual step values.
386
+ // xSteps is sorted in the same order as xPct and xVal.
387
+ this.xNumSteps = this.xSteps.slice(0);
388
+
389
+ // Convert all numeric steps to the percentage of the subrange they represent.
390
+ for ( index = 0; index < this.xNumSteps.length; index++ ) {
391
+ handleStepPoint(index, this.xNumSteps[index], this);
392
+ }
393
+ }
394
+
395
+ Spectrum.prototype.getMargin = function ( value ) {
396
+
397
+ var step = this.xNumSteps[0];
398
+
399
+ if ( step && ((value / step) % 1) !== 0 ) {
400
+ throw new Error("noUiSlider: 'limit', 'margin' and 'padding' must be divisible by step.");
401
+ }
402
+
403
+ return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false;
404
+ };
405
+
406
+ Spectrum.prototype.toStepping = function ( value ) {
407
+
408
+ value = toStepping( this.xVal, this.xPct, value );
409
+
410
+ return value;
411
+ };
412
+
413
+ Spectrum.prototype.fromStepping = function ( value ) {
414
+
415
+ return fromStepping( this.xVal, this.xPct, value );
416
+ };
417
+
418
+ Spectrum.prototype.getStep = function ( value ) {
419
+
420
+ value = getStep(this.xPct, this.xSteps, this.snap, value );
421
+
422
+ return value;
423
+ };
424
+
425
+ Spectrum.prototype.getNearbySteps = function ( value ) {
426
+
427
+ var j = getJ(value, this.xPct);
428
+
429
+ return {
430
+ stepBefore: { startValue: this.xVal[j-2], step: this.xNumSteps[j-2], highestStep: this.xHighestCompleteStep[j-2] },
431
+ thisStep: { startValue: this.xVal[j-1], step: this.xNumSteps[j-1], highestStep: this.xHighestCompleteStep[j-1] },
432
+ stepAfter: { startValue: this.xVal[j-0], step: this.xNumSteps[j-0], highestStep: this.xHighestCompleteStep[j-0] }
433
+ };
434
+ };
435
+
436
+ Spectrum.prototype.countStepDecimals = function () {
437
+ var stepDecimals = this.xNumSteps.map(countDecimals);
438
+ return Math.max.apply(null, stepDecimals);
439
+ };
440
+
441
+ // Outside testing
442
+ Spectrum.prototype.convert = function ( value ) {
443
+ return this.getStep(this.toStepping(value));
444
+ };
445
+
446
+ /* Every input option is tested and parsed. This'll prevent
447
+ endless validation in internal methods. These tests are
448
+ structured with an item for every option available. An
449
+ option can be marked as required by setting the 'r' flag.
450
+ The testing function is provided with three arguments:
451
+ - The provided value for the option;
452
+ - A reference to the options object;
453
+ - The name for the option;
454
+
455
+ The testing function returns false when an error is detected,
456
+ or true when everything is OK. It can also modify the option
457
+ object, to make sure all values can be correctly looped elsewhere. */
458
+
459
+ var defaultFormatter = { 'to': function( value ){
460
+ return value !== undefined && value.toFixed(2);
461
+ }, 'from': Number };
462
+
463
+ function testStep ( parsed, entry ) {
464
+
465
+ if ( !isNumeric( entry ) ) {
466
+ throw new Error("noUiSlider: 'step' is not numeric.");
467
+ }
468
+
469
+ // The step option can still be used to set stepping
470
+ // for linear sliders. Overwritten if set in 'range'.
471
+ parsed.singleStep = entry;
472
+ }
473
+
474
+ function testRange ( parsed, entry ) {
475
+
476
+ // Filter incorrect input.
477
+ if ( typeof entry !== 'object' || Array.isArray(entry) ) {
478
+ throw new Error("noUiSlider: 'range' is not an object.");
479
+ }
480
+
481
+ // Catch missing start or end.
482
+ if ( entry.min === undefined || entry.max === undefined ) {
483
+ throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'.");
484
+ }
485
+
486
+ // Catch equal start or end.
487
+ if ( entry.min === entry.max ) {
488
+ throw new Error("noUiSlider: 'range' 'min' and 'max' cannot be equal.");
489
+ }
490
+
491
+ parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep);
492
+ }
493
+
494
+ function testStart ( parsed, entry ) {
495
+
496
+ entry = asArray(entry);
497
+
498
+ // Validate input. Values aren't tested, as the public .val method
499
+ // will always provide a valid location.
500
+ if ( !Array.isArray( entry ) || !entry.length ) {
501
+ throw new Error("noUiSlider: 'start' option is incorrect.");
502
+ }
503
+
504
+ // Store the number of handles.
505
+ parsed.handles = entry.length;
506
+
507
+ // When the slider is initialized, the .val method will
508
+ // be called with the start options.
509
+ parsed.start = entry;
510
+ }
511
+
512
+ function testSnap ( parsed, entry ) {
513
+
514
+ // Enforce 100% stepping within subranges.
515
+ parsed.snap = entry;
516
+
517
+ if ( typeof entry !== 'boolean' ){
518
+ throw new Error("noUiSlider: 'snap' option must be a boolean.");
519
+ }
520
+ }
521
+
522
+ function testAnimate ( parsed, entry ) {
523
+
524
+ // Enforce 100% stepping within subranges.
525
+ parsed.animate = entry;
526
+
527
+ if ( typeof entry !== 'boolean' ){
528
+ throw new Error("noUiSlider: 'animate' option must be a boolean.");
529
+ }
530
+ }
531
+
532
+ function testAnimationDuration ( parsed, entry ) {
533
+
534
+ parsed.animationDuration = entry;
535
+
536
+ if ( typeof entry !== 'number' ){
537
+ throw new Error("noUiSlider: 'animationDuration' option must be a number.");
538
+ }
539
+ }
540
+
541
+ function testConnect ( parsed, entry ) {
542
+
543
+ var connect = [false];
544
+ var i;
545
+
546
+ // Map legacy options
547
+ if ( entry === 'lower' ) {
548
+ entry = [true, false];
549
+ }
550
+
551
+ else if ( entry === 'upper' ) {
552
+ entry = [false, true];
553
+ }
554
+
555
+ // Handle boolean options
556
+ if ( entry === true || entry === false ) {
557
+
558
+ for ( i = 1; i < parsed.handles; i++ ) {
559
+ connect.push(entry);
560
+ }
561
+
562
+ connect.push(false);
563
+ }
564
+
565
+ // Reject invalid input
566
+ else if ( !Array.isArray( entry ) || !entry.length || entry.length !== parsed.handles + 1 ) {
567
+ throw new Error("noUiSlider: 'connect' option doesn't match handle count.");
568
+ }
569
+
570
+ else {
571
+ connect = entry;
572
+ }
573
+
574
+ parsed.connect = connect;
575
+ }
576
+
577
+ function testOrientation ( parsed, entry ) {
578
+
579
+ // Set orientation to an a numerical value for easy
580
+ // array selection.
581
+ switch ( entry ){
582
+ case 'horizontal':
583
+ parsed.ort = 0;
584
+ break;
585
+ case 'vertical':
586
+ parsed.ort = 1;
587
+ break;
588
+ default:
589
+ throw new Error("noUiSlider: 'orientation' option is invalid.");
590
+ }
591
+ }
592
+
593
+ function testMargin ( parsed, entry ) {
594
+
595
+ if ( !isNumeric(entry) ){
596
+ throw new Error("noUiSlider: 'margin' option must be numeric.");
597
+ }
598
+
599
+ // Issue #582
600
+ if ( entry === 0 ) {
601
+ return;
602
+ }
603
+
604
+ parsed.margin = parsed.spectrum.getMargin(entry);
605
+
606
+ if ( !parsed.margin ) {
607
+ throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.");
608
+ }
609
+ }
610
+
611
+ function testLimit ( parsed, entry ) {
612
+
613
+ if ( !isNumeric(entry) ){
614
+ throw new Error("noUiSlider: 'limit' option must be numeric.");
615
+ }
616
+
617
+ parsed.limit = parsed.spectrum.getMargin(entry);
618
+
619
+ if ( !parsed.limit || parsed.handles < 2 ) {
620
+ throw new Error("noUiSlider: 'limit' option is only supported on linear sliders with 2 or more handles.");
621
+ }
622
+ }
623
+
624
+ function testPadding ( parsed, entry ) {
625
+
626
+ if ( !isNumeric(entry) ){
627
+ throw new Error("noUiSlider: 'padding' option must be numeric.");
628
+ }
629
+
630
+ if ( entry === 0 ) {
631
+ return;
632
+ }
633
+
634
+ parsed.padding = parsed.spectrum.getMargin(entry);
635
+
636
+ if ( !parsed.padding ) {
637
+ throw new Error("noUiSlider: 'padding' option is only supported on linear sliders.");
638
+ }
639
+
640
+ if ( parsed.padding < 0 ) {
641
+ throw new Error("noUiSlider: 'padding' option must be a positive number.");
642
+ }
643
+
644
+ if ( parsed.padding >= 50 ) {
645
+ throw new Error("noUiSlider: 'padding' option must be less than half the range.");
646
+ }
647
+ }
648
+
649
+ function testDirection ( parsed, entry ) {
650
+
651
+ // Set direction as a numerical value for easy parsing.
652
+ // Invert connection for RTL sliders, so that the proper
653
+ // handles get the connect/background classes.
654
+ switch ( entry ) {
655
+ case 'ltr':
656
+ parsed.dir = 0;
657
+ break;
658
+ case 'rtl':
659
+ parsed.dir = 1;
660
+ break;
661
+ default:
662
+ throw new Error("noUiSlider: 'direction' option was not recognized.");
663
+ }
664
+ }
665
+
666
+ function testBehaviour ( parsed, entry ) {
667
+
668
+ // Make sure the input is a string.
669
+ if ( typeof entry !== 'string' ) {
670
+ throw new Error("noUiSlider: 'behaviour' must be a string containing options.");
671
+ }
672
+
673
+ // Check if the string contains any keywords.
674
+ // None are required.
675
+ var tap = entry.indexOf('tap') >= 0;
676
+ var drag = entry.indexOf('drag') >= 0;
677
+ var fixed = entry.indexOf('fixed') >= 0;
678
+ var snap = entry.indexOf('snap') >= 0;
679
+ var hover = entry.indexOf('hover') >= 0;
680
+
681
+ if ( fixed ) {
682
+
683
+ if ( parsed.handles !== 2 ) {
684
+ throw new Error("noUiSlider: 'fixed' behaviour must be used with 2 handles");
685
+ }
686
+
687
+ // Use margin to enforce fixed state
688
+ testMargin(parsed, parsed.start[1] - parsed.start[0]);
689
+ }
690
+
691
+ parsed.events = {
692
+ tap: tap || snap,
693
+ drag: drag,
694
+ fixed: fixed,
695
+ snap: snap,
696
+ hover: hover
697
+ };
698
+ }
699
+
700
+ function testTooltips ( parsed, entry ) {
701
+
702
+ if ( entry === false ) {
703
+ return;
704
+ }
705
+
706
+ else if ( entry === true ) {
707
+
708
+ parsed.tooltips = [];
709
+
710
+ for ( var i = 0; i < parsed.handles; i++ ) {
711
+ parsed.tooltips.push(true);
712
+ }
713
+ }
714
+
715
+ else {
716
+
717
+ parsed.tooltips = asArray(entry);
718
+
719
+ if ( parsed.tooltips.length !== parsed.handles ) {
720
+ throw new Error("noUiSlider: must pass a formatter for all handles.");
721
+ }
722
+
723
+ parsed.tooltips.forEach(function(formatter){
724
+ if ( typeof formatter !== 'boolean' && (typeof formatter !== 'object' || typeof formatter.to !== 'function') ) {
725
+ throw new Error("noUiSlider: 'tooltips' must be passed a formatter or 'false'.");
726
+ }
727
+ });
728
+ }
729
+ }
730
+
731
+ function testFormat ( parsed, entry ) {
732
+
733
+ parsed.format = entry;
734
+
735
+ // Any object with a to and from method is supported.
736
+ if ( typeof entry.to === 'function' && typeof entry.from === 'function' ) {
737
+ return true;
738
+ }
739
+
740
+ throw new Error("noUiSlider: 'format' requires 'to' and 'from' methods.");
741
+ }
742
+
743
+ function testCssPrefix ( parsed, entry ) {
744
+
745
+ if ( entry !== undefined && typeof entry !== 'string' && entry !== false ) {
746
+ throw new Error("noUiSlider: 'cssPrefix' must be a string or `false`.");
747
+ }
748
+
749
+ parsed.cssPrefix = entry;
750
+ }
751
+
752
+ function testCssClasses ( parsed, entry ) {
753
+
754
+ if ( entry !== undefined && typeof entry !== 'object' ) {
755
+ throw new Error("noUiSlider: 'cssClasses' must be an object.");
756
+ }
757
+
758
+ if ( typeof parsed.cssPrefix === 'string' ) {
759
+ parsed.cssClasses = {};
760
+
761
+ for ( var key in entry ) {
762
+ if ( !entry.hasOwnProperty(key) ) { continue; }
763
+
764
+ parsed.cssClasses[key] = parsed.cssPrefix + entry[key];
765
+ }
766
+ } else {
767
+ parsed.cssClasses = entry;
768
+ }
769
+ }
770
+
771
+ function testUseRaf ( parsed, entry ) {
772
+ if ( entry === true || entry === false ) {
773
+ parsed.useRequestAnimationFrame = entry;
774
+ } else {
775
+ throw new Error("noUiSlider: 'useRequestAnimationFrame' option should be true (default) or false.");
776
+ }
777
+ }
778
+
779
+ // Test all developer settings and parse to assumption-safe values.
780
+ function testOptions ( options ) {
781
+
782
+ // To prove a fix for #537, freeze options here.
783
+ // If the object is modified, an error will be thrown.
784
+ // Object.freeze(options);
785
+
786
+ var parsed = {
787
+ margin: 0,
788
+ limit: 0,
789
+ padding: 0,
790
+ animate: true,
791
+ animationDuration: 300,
792
+ format: defaultFormatter
793
+ };
794
+
795
+ // Tests are executed in the order they are presented here.
796
+ var tests = {
797
+ 'step': { r: false, t: testStep },
798
+ 'start': { r: true, t: testStart },
799
+ 'connect': { r: true, t: testConnect },
800
+ 'direction': { r: true, t: testDirection },
801
+ 'snap': { r: false, t: testSnap },
802
+ 'animate': { r: false, t: testAnimate },
803
+ 'animationDuration': { r: false, t: testAnimationDuration },
804
+ 'range': { r: true, t: testRange },
805
+ 'orientation': { r: false, t: testOrientation },
806
+ 'margin': { r: false, t: testMargin },
807
+ 'limit': { r: false, t: testLimit },
808
+ 'padding': { r: false, t: testPadding },
809
+ 'behaviour': { r: true, t: testBehaviour },
810
+ 'format': { r: false, t: testFormat },
811
+ 'tooltips': { r: false, t: testTooltips },
812
+ 'cssPrefix': { r: false, t: testCssPrefix },
813
+ 'cssClasses': { r: false, t: testCssClasses },
814
+ 'useRequestAnimationFrame': { r: false, t: testUseRaf }
815
+ };
816
+
817
+ var defaults = {
818
+ 'connect': false,
819
+ 'direction': 'ltr',
820
+ 'behaviour': 'tap',
821
+ 'orientation': 'horizontal',
822
+ 'cssPrefix' : 'noUi-',
823
+ 'cssClasses': {
824
+ target: 'target',
825
+ base: 'base',
826
+ origin: 'origin',
827
+ handle: 'handle',
828
+ handleTouchArea: 'handle-touch-area',
829
+ handleLower: 'handle-lower',
830
+ handleUpper: 'handle-upper',
831
+ horizontal: 'horizontal',
832
+ vertical: 'vertical',
833
+ background: 'background',
834
+ connect: 'connect',
835
+ ltr: 'ltr',
836
+ rtl: 'rtl',
837
+ draggable: 'draggable',
838
+ drag: 'state-drag',
839
+ tap: 'state-tap',
840
+ active: 'active',
841
+ tooltip: 'tooltip',
842
+ pips: 'pips',
843
+ pipsHorizontal: 'pips-horizontal',
844
+ pipsVertical: 'pips-vertical',
845
+ marker: 'marker',
846
+ markerHorizontal: 'marker-horizontal',
847
+ markerVertical: 'marker-vertical',
848
+ markerNormal: 'marker-normal',
849
+ markerLarge: 'marker-large',
850
+ markerSub: 'marker-sub',
851
+ value: 'value',
852
+ valueHorizontal: 'value-horizontal',
853
+ valueVertical: 'value-vertical',
854
+ valueNormal: 'value-normal',
855
+ valueLarge: 'value-large',
856
+ valueSub: 'value-sub'
857
+ },
858
+ 'useRequestAnimationFrame': true
859
+ };
860
+
861
+ // Run all options through a testing mechanism to ensure correct
862
+ // input. It should be noted that options might get modified to
863
+ // be handled properly. E.g. wrapping integers in arrays.
864
+ Object.keys(tests).forEach(function( name ){
865
+
866
+ // If the option isn't set, but it is required, throw an error.
867
+ if ( options[name] === undefined && defaults[name] === undefined ) {
868
+
869
+ if ( tests[name].r ) {
870
+ throw new Error("noUiSlider: '" + name + "' is required.");
871
+ }
872
+
873
+ return true;
874
+ }
875
+
876
+ tests[name].t( parsed, options[name] === undefined ? defaults[name] : options[name] );
877
+ });
878
+
879
+ // Forward pips options
880
+ parsed.pips = options.pips;
881
+
882
+ var styles = [['left', 'top'], ['right', 'bottom']];
883
+
884
+ // Pre-define the styles.
885
+ parsed.style = styles[parsed.dir][parsed.ort];
886
+ parsed.styleOposite = styles[parsed.dir?0:1][parsed.ort];
887
+
888
+ return parsed;
889
+ }
890
+
891
+
892
+ function closure ( target, options, originalOptions ){
893
+
894
+ var actions = getActions( );
895
+
896
+ // All variables local to 'closure' are prefixed with 'scope_'
897
+ var scope_Target = target;
898
+ var scope_Locations = [];
899
+ var scope_Base;
900
+ var scope_Handles;
901
+ var scope_HandleNumbers = [];
902
+ var scope_ActiveHandle = false;
903
+ var scope_Connects;
904
+ var scope_Spectrum = options.spectrum;
905
+ var scope_Values = [];
906
+ var scope_Events = {};
907
+ var scope_Self;
908
+
909
+
910
+ // Append a origin to the base
911
+ function addOrigin ( base, handleNumber ) {
912
+
913
+ var origin = addNodeTo(base, options.cssClasses.origin);
914
+ var handle = addNodeTo(origin, options.cssClasses.handle);
915
+ addNodeTo(handle, options.cssClasses.handleTouchArea);
916
+
917
+ handle.setAttribute('data-handle', handleNumber);
918
+
919
+ if ( handleNumber === 0 ) {
920
+ addClass(handle, options.cssClasses.handleLower);
921
+ }
922
+
923
+ else if ( handleNumber === options.handles - 1 ) {
924
+ addClass(handle, options.cssClasses.handleUpper);
925
+ }
926
+
927
+ return origin;
928
+ }
929
+
930
+ // Insert nodes for connect elements
931
+ function addConnect ( base, add ) {
932
+
933
+ if ( !add ) {
934
+ return false;
935
+ }
936
+
937
+ return addNodeTo(base, options.cssClasses.connect);
938
+ }
939
+
940
+ // Add handles to the slider base.
941
+ function addElements ( connectOptions, base ) {
942
+
943
+ scope_Handles = [];
944
+ scope_Connects = [];
945
+
946
+ scope_Connects.push(addConnect(base, connectOptions[0]));
947
+
948
+ // [::::O====O====O====]
949
+ // connectOptions = [0, 1, 1, 1]
950
+
951
+ for ( var i = 0; i < options.handles; i++ ) {
952
+ // Keep a list of all added handles.
953
+ scope_Handles.push(addOrigin(base, i));
954
+ scope_HandleNumbers[i] = i;
955
+ scope_Connects.push(addConnect(base, connectOptions[i + 1]));
956
+ }
957
+ }
958
+
959
+ // Initialize a single slider.
960
+ function addSlider ( target ) {
961
+
962
+ // Apply classes and data to the target.
963
+ addClass(target, options.cssClasses.target);
964
+
965
+ if ( options.dir === 0 ) {
966
+ addClass(target, options.cssClasses.ltr);
967
+ } else {
968
+ addClass(target, options.cssClasses.rtl);
969
+ }
970
+
971
+ if ( options.ort === 0 ) {
972
+ addClass(target, options.cssClasses.horizontal);
973
+ } else {
974
+ addClass(target, options.cssClasses.vertical);
975
+ }
976
+
977
+ scope_Base = addNodeTo(target, options.cssClasses.base);
978
+ }
979
+
980
+
981
+ function addTooltip ( handle, handleNumber ) {
982
+
983
+ if ( !options.tooltips[handleNumber] ) {
984
+ return false;
985
+ }
986
+
987
+ return addNodeTo(handle.firstChild, options.cssClasses.tooltip);
988
+ }
989
+
990
+ // The tooltips option is a shorthand for using the 'update' event.
991
+ function tooltips ( ) {
992
+
993
+ // Tooltips are added with options.tooltips in original order.
994
+ var tips = scope_Handles.map(addTooltip);
995
+
996
+ bindEvent('update', function(values, handleNumber, unencoded) {
997
+
998
+ if ( !tips[handleNumber] ) {
999
+ return;
1000
+ }
1001
+
1002
+ var formattedValue = values[handleNumber];
1003
+
1004
+ if ( options.tooltips[handleNumber] !== true ) {
1005
+ formattedValue = options.tooltips[handleNumber].to(unencoded[handleNumber]);
1006
+ }
1007
+
1008
+ tips[handleNumber].innerHTML = '<span>' + formattedValue + '</span>';
1009
+ });
1010
+ }
1011
+
1012
+
1013
+ function getGroup ( mode, values, stepped ) {
1014
+
1015
+ // Use the range.
1016
+ if ( mode === 'range' || mode === 'steps' ) {
1017
+ return scope_Spectrum.xVal;
1018
+ }
1019
+
1020
+ if ( mode === 'count' ) {
1021
+
1022
+ // Divide 0 - 100 in 'count' parts.
1023
+ var spread = ( 100 / (values-1) ), v, i = 0;
1024
+ values = [];
1025
+
1026
+ // List these parts and have them handled as 'positions'.
1027
+ while ((v=i++*spread) <= 100 ) {
1028
+ values.push(v);
1029
+ }
1030
+
1031
+ mode = 'positions';
1032
+ }
1033
+
1034
+ if ( mode === 'positions' ) {
1035
+
1036
+ // Map all percentages to on-range values.
1037
+ return values.map(function( value ){
1038
+ return scope_Spectrum.fromStepping( stepped ? scope_Spectrum.getStep( value ) : value );
1039
+ });
1040
+ }
1041
+
1042
+ if ( mode === 'values' ) {
1043
+
1044
+ // If the value must be stepped, it needs to be converted to a percentage first.
1045
+ if ( stepped ) {
1046
+
1047
+ return values.map(function( value ){
1048
+
1049
+ // Convert to percentage, apply step, return to value.
1050
+ return scope_Spectrum.fromStepping( scope_Spectrum.getStep( scope_Spectrum.toStepping( value ) ) );
1051
+ });
1052
+
1053
+ }
1054
+
1055
+ // Otherwise, we can simply use the values.
1056
+ return values;
1057
+ }
1058
+ }
1059
+
1060
+ function generateSpread ( density, mode, group ) {
1061
+
1062
+ function safeIncrement(value, increment) {
1063
+ // Avoid floating point variance by dropping the smallest decimal places.
1064
+ return (value + increment).toFixed(7) / 1;
1065
+ }
1066
+
1067
+ var indexes = {},
1068
+ firstInRange = scope_Spectrum.xVal[0],
1069
+ lastInRange = scope_Spectrum.xVal[scope_Spectrum.xVal.length-1],
1070
+ ignoreFirst = false,
1071
+ ignoreLast = false,
1072
+ prevPct = 0;
1073
+
1074
+ // Create a copy of the group, sort it and filter away all duplicates.
1075
+ group = unique(group.slice().sort(function(a, b){ return a - b; }));
1076
+
1077
+ // Make sure the range starts with the first element.
1078
+ if ( group[0] !== firstInRange ) {
1079
+ group.unshift(firstInRange);
1080
+ ignoreFirst = true;
1081
+ }
1082
+
1083
+ // Likewise for the last one.
1084
+ if ( group[group.length - 1] !== lastInRange ) {
1085
+ group.push(lastInRange);
1086
+ ignoreLast = true;
1087
+ }
1088
+
1089
+ group.forEach(function ( current, index ) {
1090
+
1091
+ // Get the current step and the lower + upper positions.
1092
+ var step, i, q,
1093
+ low = current,
1094
+ high = group[index+1],
1095
+ newPct, pctDifference, pctPos, type,
1096
+ steps, realSteps, stepsize;
1097
+
1098
+ // When using 'steps' mode, use the provided steps.
1099
+ // Otherwise, we'll step on to the next subrange.
1100
+ if ( mode === 'steps' ) {
1101
+ step = scope_Spectrum.xNumSteps[ index ];
1102
+ }
1103
+
1104
+ // Default to a 'full' step.
1105
+ if ( !step ) {
1106
+ step = high-low;
1107
+ }
1108
+
1109
+ // Low can be 0, so test for false. If high is undefined,
1110
+ // we are at the last subrange. Index 0 is already handled.
1111
+ if ( low === false || high === undefined ) {
1112
+ return;
1113
+ }
1114
+
1115
+ // Make sure step isn't 0, which would cause an infinite loop (#654)
1116
+ step = Math.max(step, 0.0000001);
1117
+
1118
+ // Find all steps in the subrange.
1119
+ for ( i = low; i <= high; i = safeIncrement(i, step) ) {
1120
+
1121
+ // Get the percentage value for the current step,
1122
+ // calculate the size for the subrange.
1123
+ newPct = scope_Spectrum.toStepping( i );
1124
+ pctDifference = newPct - prevPct;
1125
+
1126
+ steps = pctDifference / density;
1127
+ realSteps = Math.round(steps);
1128
+
1129
+ // This ratio represents the ammount of percentage-space a point indicates.
1130
+ // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided.
1131
+ // Round the percentage offset to an even number, then divide by two
1132
+ // to spread the offset on both sides of the range.
1133
+ stepsize = pctDifference/realSteps;
1134
+
1135
+ // Divide all points evenly, adding the correct number to this subrange.
1136
+ // Run up to <= so that 100% gets a point, event if ignoreLast is set.
1137
+ for ( q = 1; q <= realSteps; q += 1 ) {
1138
+
1139
+ // The ratio between the rounded value and the actual size might be ~1% off.
1140
+ // Correct the percentage offset by the number of points
1141
+ // per subrange. density = 1 will result in 100 points on the
1142
+ // full range, 2 for 50, 4 for 25, etc.
1143
+ pctPos = prevPct + ( q * stepsize );
1144
+ indexes[pctPos.toFixed(5)] = ['x', 0];
1145
+ }
1146
+
1147
+ // Determine the point type.
1148
+ type = (group.indexOf(i) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 );
1149
+
1150
+ // Enforce the 'ignoreFirst' option by overwriting the type for 0.
1151
+ if ( !index && ignoreFirst ) {
1152
+ type = 0;
1153
+ }
1154
+
1155
+ if ( !(i === high && ignoreLast)) {
1156
+ // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value.
1157
+ indexes[newPct.toFixed(5)] = [i, type];
1158
+ }
1159
+
1160
+ // Update the percentage count.
1161
+ prevPct = newPct;
1162
+ }
1163
+ });
1164
+
1165
+ return indexes;
1166
+ }
1167
+
1168
+ function addMarking ( spread, filterFunc, formatter ) {
1169
+
1170
+ var element = document.createElement('div'),
1171
+ out = '',
1172
+ valueSizeClasses = [
1173
+ options.cssClasses.valueNormal,
1174
+ options.cssClasses.valueLarge,
1175
+ options.cssClasses.valueSub
1176
+ ],
1177
+ markerSizeClasses = [
1178
+ options.cssClasses.markerNormal,
1179
+ options.cssClasses.markerLarge,
1180
+ options.cssClasses.markerSub
1181
+ ],
1182
+ valueOrientationClasses = [
1183
+ options.cssClasses.valueHorizontal,
1184
+ options.cssClasses.valueVertical
1185
+ ],
1186
+ markerOrientationClasses = [
1187
+ options.cssClasses.markerHorizontal,
1188
+ options.cssClasses.markerVertical
1189
+ ];
1190
+
1191
+ addClass(element, options.cssClasses.pips);
1192
+ addClass(element, options.ort === 0 ? options.cssClasses.pipsHorizontal : options.cssClasses.pipsVertical);
1193
+
1194
+ function getClasses( type, source ){
1195
+ var a = source === options.cssClasses.value,
1196
+ orientationClasses = a ? valueOrientationClasses : markerOrientationClasses,
1197
+ sizeClasses = a ? valueSizeClasses : markerSizeClasses;
1198
+
1199
+ return source + ' ' + orientationClasses[options.ort] + ' ' + sizeClasses[type];
1200
+ }
1201
+
1202
+ function getTags( offset, source, values ) {
1203
+ return 'class="' + getClasses(values[1], source) + '" style="' + options.style + ': ' + offset + '%"';
1204
+ }
1205
+
1206
+ function addSpread ( offset, values ){
1207
+
1208
+ // Apply the filter function, if it is set.
1209
+ values[1] = (values[1] && filterFunc) ? filterFunc(values[0], values[1]) : values[1];
1210
+
1211
+ // Add a marker for every point
1212
+ out += '<div ' + getTags(offset, options.cssClasses.marker, values) + '></div>';
1213
+
1214
+ // Values are only appended for points marked '1' or '2'.
1215
+ if ( values[1] ) {
1216
+ out += '<div ' + getTags(offset, options.cssClasses.value, values) + '>' + formatter.to(values[0]) + '</div>';
1217
+ }
1218
+ }
1219
+
1220
+ // Append all points.
1221
+ Object.keys(spread).forEach(function(a){
1222
+ addSpread(a, spread[a]);
1223
+ });
1224
+
1225
+ element.innerHTML = out;
1226
+
1227
+ return element;
1228
+ }
1229
+
1230
+ function pips ( grid ) {
1231
+
1232
+ var mode = grid.mode,
1233
+ density = grid.density || 1,
1234
+ filter = grid.filter || false,
1235
+ values = grid.values || false,
1236
+ stepped = grid.stepped || false,
1237
+ group = getGroup( mode, values, stepped ),
1238
+ spread = generateSpread( density, mode, group ),
1239
+ format = grid.format || {
1240
+ to: Math.round
1241
+ };
1242
+
1243
+ return scope_Target.appendChild(addMarking(
1244
+ spread,
1245
+ filter,
1246
+ format
1247
+ ));
1248
+ }
1249
+
1250
+
1251
+ // Shorthand for base dimensions.
1252
+ function baseSize ( ) {
1253
+ var rect = scope_Base.getBoundingClientRect(), alt = 'offset' + ['Width', 'Height'][options.ort];
1254
+ return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]);
1255
+ }
1256
+
1257
+ // Handler for attaching events trough a proxy.
1258
+ function attachEvent ( events, element, callback, data ) {
1259
+
1260
+ // This function can be used to 'filter' events to the slider.
1261
+ // element is a node, not a nodeList
1262
+
1263
+ var method = function ( e ){
1264
+
1265
+ if ( scope_Target.hasAttribute('disabled') ) {
1266
+ return false;
1267
+ }
1268
+
1269
+ // Stop if an active 'tap' transition is taking place.
1270
+ if ( hasClass(scope_Target, options.cssClasses.tap) ) {
1271
+ return false;
1272
+ }
1273
+
1274
+ e = fixEvent(e, data.pageOffset);
1275
+
1276
+ // Handle reject of multitouch
1277
+ if ( !e ) {
1278
+ return false;
1279
+ }
1280
+
1281
+ // Ignore right or middle clicks on start #454
1282
+ if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) {
1283
+ return false;
1284
+ }
1285
+
1286
+ // Ignore right or middle clicks on start #454
1287
+ if ( data.hover && e.buttons ) {
1288
+ return false;
1289
+ }
1290
+
1291
+ e.calcPoint = e.points[ options.ort ];
1292
+
1293
+ // Call the event handler with the event [ and additional data ].
1294
+ callback ( e, data );
1295
+ };
1296
+
1297
+ var methods = [];
1298
+
1299
+ // Bind a closure on the target for every event type.
1300
+ events.split(' ').forEach(function( eventName ){
1301
+ element.addEventListener(eventName, method, false);
1302
+ methods.push([eventName, method]);
1303
+ });
1304
+
1305
+ return methods;
1306
+ }
1307
+
1308
+ // Provide a clean event with standardized offset values.
1309
+ function fixEvent ( e, pageOffset ) {
1310
+
1311
+ // Prevent scrolling and panning on touch events, while
1312
+ // attempting to slide. The tap event also depends on this.
1313
+ e.preventDefault();
1314
+
1315
+ // Filter the event to register the type, which can be
1316
+ // touch, mouse or pointer. Offset changes need to be
1317
+ // made on an event specific basis.
1318
+ var touch = e.type.indexOf('touch') === 0;
1319
+ var mouse = e.type.indexOf('mouse') === 0;
1320
+ var pointer = e.type.indexOf('pointer') === 0;
1321
+ var x;
1322
+ var y;
1323
+
1324
+ // IE10 implemented pointer events with a prefix;
1325
+ if ( e.type.indexOf('MSPointer') === 0 ) {
1326
+ pointer = true;
1327
+ }
1328
+
1329
+ if ( touch ) {
1330
+
1331
+ // Fix bug when user touches with two or more fingers on mobile devices.
1332
+ // It's useful when you have two or more sliders on one page,
1333
+ // that can be touched simultaneously.
1334
+ // #649, #663, #668
1335
+ if ( e.touches.length > 1 ) {
1336
+ return false;
1337
+ }
1338
+
1339
+ // noUiSlider supports one movement at a time,
1340
+ // so we can select the first 'changedTouch'.
1341
+ x = e.changedTouches[0].pageX;
1342
+ y = e.changedTouches[0].pageY;
1343
+ }
1344
+
1345
+ pageOffset = pageOffset || getPageOffset();
1346
+
1347
+ if ( mouse || pointer ) {
1348
+ x = e.clientX + pageOffset.x;
1349
+ y = e.clientY + pageOffset.y;
1350
+ }
1351
+
1352
+ e.pageOffset = pageOffset;
1353
+ e.points = [x, y];
1354
+ e.cursor = mouse || pointer; // Fix #435
1355
+
1356
+ return e;
1357
+ }
1358
+
1359
+ // Translate a coordinate in the document to a percentage on the slider
1360
+ function calcPointToPercentage ( calcPoint ) {
1361
+ var location = calcPoint - offset(scope_Base, options.ort);
1362
+ var proposal = ( location * 100 ) / baseSize();
1363
+ return options.dir ? 100 - proposal : proposal;
1364
+ }
1365
+
1366
+ // Find handle closest to a certain percentage on the slider
1367
+ function getClosestHandle ( proposal ) {
1368
+
1369
+ var closest = 100;
1370
+ var handleNumber = false;
1371
+
1372
+ scope_Handles.forEach(function(handle, index){
1373
+
1374
+ // Disabled handles are ignored
1375
+ if ( handle.hasAttribute('disabled') ) {
1376
+ return;
1377
+ }
1378
+
1379
+ var pos = Math.abs(scope_Locations[index] - proposal);
1380
+
1381
+ if ( pos < closest ) {
1382
+ handleNumber = index;
1383
+ closest = pos;
1384
+ }
1385
+ });
1386
+
1387
+ return handleNumber;
1388
+ }
1389
+
1390
+ // Moves handle(s) by a percentage
1391
+ // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...])
1392
+ function moveHandles ( upward, proposal, locations, handleNumbers ) {
1393
+
1394
+ var proposals = locations.slice();
1395
+
1396
+ var b = [!upward, upward];
1397
+ var f = [upward, !upward];
1398
+
1399
+ // Copy handleNumbers so we don't change the dataset
1400
+ handleNumbers = handleNumbers.slice();
1401
+
1402
+ // Check to see which handle is 'leading'.
1403
+ // If that one can't move the second can't either.
1404
+ if ( upward ) {
1405
+ handleNumbers.reverse();
1406
+ }
1407
+
1408
+ // Step 1: get the maximum percentage that any of the handles can move
1409
+ if ( handleNumbers.length > 1 ) {
1410
+
1411
+ handleNumbers.forEach(function(handleNumber, o) {
1412
+
1413
+ var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o]);
1414
+
1415
+ // Stop if one of the handles can't move.
1416
+ if ( to === false ) {
1417
+ proposal = 0;
1418
+ } else {
1419
+ proposal = to - proposals[handleNumber];
1420
+ proposals[handleNumber] = to;
1421
+ }
1422
+ });
1423
+ }
1424
+
1425
+ // If using one handle, check backward AND forward
1426
+ else {
1427
+ b = f = [true];
1428
+ }
1429
+
1430
+ var state = false;
1431
+
1432
+ // Step 2: Try to set the handles with the found percentage
1433
+ handleNumbers.forEach(function(handleNumber, o) {
1434
+ state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state;
1435
+ });
1436
+
1437
+ // Step 3: If a handle moved, fire events
1438
+ if ( state ) {
1439
+ handleNumbers.forEach(function(handleNumber){
1440
+ fireEvent('update', handleNumber);
1441
+ fireEvent('slide', handleNumber);
1442
+ });
1443
+ }
1444
+ }
1445
+
1446
+ // External event handling
1447
+ function fireEvent ( eventName, handleNumber, tap ) {
1448
+
1449
+ Object.keys(scope_Events).forEach(function( targetEvent ) {
1450
+
1451
+ var eventType = targetEvent.split('.')[0];
1452
+
1453
+ if ( eventName === eventType ) {
1454
+ scope_Events[targetEvent].forEach(function( callback ) {
1455
+
1456
+ callback.call(
1457
+ // Use the slider public API as the scope ('this')
1458
+ scope_Self,
1459
+ // Return values as array, so arg_1[arg_2] is always valid.
1460
+ scope_Values.map(options.format.to),
1461
+ // Handle index, 0 or 1
1462
+ handleNumber,
1463
+ // Unformatted slider values
1464
+ scope_Values.slice(),
1465
+ // Event is fired by tap, true or false
1466
+ tap || false,
1467
+ // Left offset of the handle, in relation to the slider
1468
+ scope_Locations.slice()
1469
+ );
1470
+ });
1471
+ }
1472
+ });
1473
+ }
1474
+
1475
+
1476
+ // Fire 'end' when a mouse or pen leaves the document.
1477
+ function documentLeave ( event, data ) {
1478
+ if ( event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null ){
1479
+ eventEnd (event, data);
1480
+ }
1481
+ }
1482
+
1483
+ // Handle movement on document for handle and range drag.
1484
+ function eventMove ( event, data ) {
1485
+
1486
+ // Fix #498
1487
+ // Check value of .buttons in 'start' to work around a bug in IE10 mobile (data.buttonsProperty).
1488
+ // https://connect.microsoft.com/IE/feedback/details/927005/mobile-ie10-windows-phone-buttons-property-of-pointermove-event-always-zero
1489
+ // IE9 has .buttons and .which zero on mousemove.
1490
+ // Firefox breaks the spec MDN defines.
1491
+ if ( navigator.appVersion.indexOf("MSIE 9") === -1 && event.buttons === 0 && data.buttonsProperty !== 0 ) {
1492
+ return eventEnd(event, data);
1493
+ }
1494
+
1495
+ // Check if we are moving up or down
1496
+ var movement = (options.dir ? -1 : 1) * (event.calcPoint - data.startCalcPoint);
1497
+
1498
+ // Convert the movement into a percentage of the slider width/height
1499
+ var proposal = (movement * 100) / data.baseSize;
1500
+
1501
+ moveHandles(movement > 0, proposal, data.locations, data.handleNumbers);
1502
+ }
1503
+
1504
+ // Unbind move events on document, call callbacks.
1505
+ function eventEnd ( event, data ) {
1506
+
1507
+ // The handle is no longer active, so remove the class.
1508
+ if ( scope_ActiveHandle ) {
1509
+ removeClass(scope_ActiveHandle, options.cssClasses.active);
1510
+ scope_ActiveHandle = false;
1511
+ }
1512
+
1513
+ // Remove cursor styles and text-selection events bound to the body.
1514
+ if ( event.cursor ) {
1515
+ document.body.style.cursor = '';
1516
+ document.body.removeEventListener('selectstart', document.body.noUiListener);
1517
+ }
1518
+
1519
+ // Unbind the move and end events, which are added on 'start'.
1520
+ document.documentElement.noUiListeners.forEach(function( c ) {
1521
+ document.documentElement.removeEventListener(c[0], c[1]);
1522
+ });
1523
+
1524
+ // Remove dragging class.
1525
+ removeClass(scope_Target, options.cssClasses.drag);
1526
+
1527
+ setZindex();
1528
+
1529
+ data.handleNumbers.forEach(function(handleNumber){
1530
+ fireEvent('set', handleNumber);
1531
+ fireEvent('change', handleNumber);
1532
+ fireEvent('end', handleNumber);
1533
+ });
1534
+ }
1535
+
1536
+ // Bind move events on document.
1537
+ function eventStart ( event, data ) {
1538
+
1539
+ if ( data.handleNumbers.length === 1 ) {
1540
+
1541
+ var handle = scope_Handles[data.handleNumbers[0]];
1542
+
1543
+ // Ignore 'disabled' handles
1544
+ if ( handle.hasAttribute('disabled') ) {
1545
+ return false;
1546
+ }
1547
+
1548
+ // Mark the handle as 'active' so it can be styled.
1549
+ scope_ActiveHandle = handle.children[0];
1550
+ addClass(scope_ActiveHandle, options.cssClasses.active);
1551
+ }
1552
+
1553
+ // Fix #551, where a handle gets selected instead of dragged.
1554
+ event.preventDefault();
1555
+
1556
+ // A drag should never propagate up to the 'tap' event.
1557
+ event.stopPropagation();
1558
+
1559
+ // Attach the move and end events.
1560
+ var moveEvent = attachEvent(actions.move, document.documentElement, eventMove, {
1561
+ startCalcPoint: event.calcPoint,
1562
+ baseSize: baseSize(),
1563
+ pageOffset: event.pageOffset,
1564
+ handleNumbers: data.handleNumbers,
1565
+ buttonsProperty: event.buttons,
1566
+ locations: scope_Locations.slice()
1567
+ });
1568
+
1569
+ var endEvent = attachEvent(actions.end, document.documentElement, eventEnd, {
1570
+ handleNumbers: data.handleNumbers
1571
+ });
1572
+
1573
+ var outEvent = attachEvent("mouseout", document.documentElement, documentLeave, {
1574
+ handleNumbers: data.handleNumbers
1575
+ });
1576
+
1577
+ document.documentElement.noUiListeners = moveEvent.concat(endEvent, outEvent);
1578
+
1579
+ // Text selection isn't an issue on touch devices,
1580
+ // so adding cursor styles can be skipped.
1581
+ if ( event.cursor ) {
1582
+
1583
+ // Prevent the 'I' cursor and extend the range-drag cursor.
1584
+ document.body.style.cursor = getComputedStyle(event.target).cursor;
1585
+
1586
+ // Mark the target with a dragging state.
1587
+ if ( scope_Handles.length > 1 ) {
1588
+ addClass(scope_Target, options.cssClasses.drag);
1589
+ }
1590
+
1591
+ var f = function(){
1592
+ return false;
1593
+ };
1594
+
1595
+ document.body.noUiListener = f;
1596
+
1597
+ // Prevent text selection when dragging the handles.
1598
+ document.body.addEventListener('selectstart', f, false);
1599
+ }
1600
+
1601
+ data.handleNumbers.forEach(function(handleNumber){
1602
+ fireEvent('start', handleNumber);
1603
+ });
1604
+ }
1605
+
1606
+ // Move closest handle to tapped location.
1607
+ function eventTap ( event ) {
1608
+
1609
+ // The tap event shouldn't propagate up
1610
+ event.stopPropagation();
1611
+
1612
+ var proposal = calcPointToPercentage(event.calcPoint);
1613
+ var handleNumber = getClosestHandle(proposal);
1614
+
1615
+ // Tackle the case that all handles are 'disabled'.
1616
+ if ( handleNumber === false ) {
1617
+ return false;
1618
+ }
1619
+
1620
+ // Flag the slider as it is now in a transitional state.
1621
+ // Transition takes a configurable amount of ms (default 300). Re-enable the slider after that.
1622
+ if ( !options.events.snap ) {
1623
+ addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
1624
+ }
1625
+
1626
+ setHandle(handleNumber, proposal, true, true);
1627
+
1628
+ setZindex();
1629
+
1630
+ fireEvent('slide', handleNumber, true);
1631
+ fireEvent('set', handleNumber, true);
1632
+ fireEvent('change', handleNumber, true);
1633
+ fireEvent('update', handleNumber, true);
1634
+
1635
+ if ( options.events.snap ) {
1636
+ eventStart(event, { handleNumbers: [handleNumber] });
1637
+ }
1638
+ }
1639
+
1640
+ // Fires a 'hover' event for a hovered mouse/pen position.
1641
+ function eventHover ( event ) {
1642
+
1643
+ var proposal = calcPointToPercentage(event.calcPoint);
1644
+
1645
+ var to = scope_Spectrum.getStep(proposal);
1646
+ var value = scope_Spectrum.fromStepping(to);
1647
+
1648
+ Object.keys(scope_Events).forEach(function( targetEvent ) {
1649
+ if ( 'hover' === targetEvent.split('.')[0] ) {
1650
+ scope_Events[targetEvent].forEach(function( callback ) {
1651
+ callback.call( scope_Self, value );
1652
+ });
1653
+ }
1654
+ });
1655
+ }
1656
+
1657
+ // Attach events to several slider parts.
1658
+ function bindSliderEvents ( behaviour ) {
1659
+
1660
+ // Attach the standard drag event to the handles.
1661
+ if ( !behaviour.fixed ) {
1662
+
1663
+ scope_Handles.forEach(function( handle, index ){
1664
+
1665
+ // These events are only bound to the visual handle
1666
+ // element, not the 'real' origin element.
1667
+ attachEvent ( actions.start, handle.children[0], eventStart, {
1668
+ handleNumbers: [index]
1669
+ });
1670
+ });
1671
+ }
1672
+
1673
+ // Attach the tap event to the slider base.
1674
+ if ( behaviour.tap ) {
1675
+ attachEvent (actions.start, scope_Base, eventTap, {});
1676
+ }
1677
+
1678
+ // Fire hover events
1679
+ if ( behaviour.hover ) {
1680
+ attachEvent (actions.move, scope_Base, eventHover, { hover: true });
1681
+ }
1682
+
1683
+ // Make the range draggable.
1684
+ if ( behaviour.drag ){
1685
+
1686
+ scope_Connects.forEach(function( connect, index ){
1687
+
1688
+ if ( connect === false || index === 0 || index === scope_Connects.length - 1 ) {
1689
+ return;
1690
+ }
1691
+
1692
+ var handleBefore = scope_Handles[index - 1];
1693
+ var handleAfter = scope_Handles[index];
1694
+ var eventHolders = [connect];
1695
+
1696
+ addClass(connect, options.cssClasses.draggable);
1697
+
1698
+ // When the range is fixed, the entire range can
1699
+ // be dragged by the handles. The handle in the first
1700
+ // origin will propagate the start event upward,
1701
+ // but it needs to be bound manually on the other.
1702
+ if ( behaviour.fixed ) {
1703
+ eventHolders.push(handleBefore.children[0]);
1704
+ eventHolders.push(handleAfter.children[0]);
1705
+ }
1706
+
1707
+ eventHolders.forEach(function( eventHolder ) {
1708
+ attachEvent ( actions.start, eventHolder, eventStart, {
1709
+ handles: [handleBefore, handleAfter],
1710
+ handleNumbers: [index - 1, index]
1711
+ });
1712
+ });
1713
+ });
1714
+ }
1715
+ }
1716
+
1717
+
1718
+ // Split out the handle positioning logic so the Move event can use it, too
1719
+ function checkHandlePosition ( reference, handleNumber, to, lookBackward, lookForward ) {
1720
+
1721
+ // For sliders with multiple handles, limit movement to the other handle.
1722
+ // Apply the margin option by adding it to the handle positions.
1723
+ if ( scope_Handles.length > 1 ) {
1724
+
1725
+ if ( lookBackward && handleNumber > 0 ) {
1726
+ to = Math.max(to, reference[handleNumber - 1] + options.margin);
1727
+ }
1728
+
1729
+ if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
1730
+ to = Math.min(to, reference[handleNumber + 1] - options.margin);
1731
+ }
1732
+ }
1733
+
1734
+ // The limit option has the opposite effect, limiting handles to a
1735
+ // maximum distance from another. Limit must be > 0, as otherwise
1736
+ // handles would be unmoveable.
1737
+ if ( scope_Handles.length > 1 && options.limit ) {
1738
+
1739
+ if ( lookBackward && handleNumber > 0 ) {
1740
+ to = Math.min(to, reference[handleNumber - 1] + options.limit);
1741
+ }
1742
+
1743
+ if ( lookForward && handleNumber < scope_Handles.length - 1 ) {
1744
+ to = Math.max(to, reference[handleNumber + 1] - options.limit);
1745
+ }
1746
+ }
1747
+
1748
+ // The padding option keeps the handles a certain distance from the
1749
+ // edges of the slider. Padding must be > 0.
1750
+ if ( options.padding ) {
1751
+
1752
+ if ( handleNumber === 0 ) {
1753
+ to = Math.max(to, options.padding);
1754
+ }
1755
+
1756
+ if ( handleNumber === scope_Handles.length - 1 ) {
1757
+ to = Math.min(to, 100 - options.padding);
1758
+ }
1759
+ }
1760
+
1761
+ to = scope_Spectrum.getStep(to);
1762
+
1763
+ // Limit percentage to the 0 - 100 range
1764
+ to = limit(to);
1765
+
1766
+ // Return false if handle can't move
1767
+ if ( to === reference[handleNumber] ) {
1768
+ return false;
1769
+ }
1770
+
1771
+ return to;
1772
+ }
1773
+
1774
+ function toPct ( pct ) {
1775
+ return pct + '%';
1776
+ }
1777
+
1778
+ // Updates scope_Locations and scope_Values, updates visual state
1779
+ function updateHandlePosition ( handleNumber, to ) {
1780
+
1781
+ // Update locations.
1782
+ scope_Locations[handleNumber] = to;
1783
+
1784
+ // Convert the value to the slider stepping/range.
1785
+ scope_Values[handleNumber] = scope_Spectrum.fromStepping(to);
1786
+
1787
+ // Called synchronously or on the next animationFrame
1788
+ var stateUpdate = function() {
1789
+ scope_Handles[handleNumber].style[options.style] = toPct(to);
1790
+ updateConnect(handleNumber);
1791
+ updateConnect(handleNumber + 1);
1792
+ };
1793
+
1794
+ // Set the handle to the new position.
1795
+ // Use requestAnimationFrame for efficient painting.
1796
+ // No significant effect in Chrome, Edge sees dramatic performace improvements.
1797
+ // Option to disable is useful for unit tests, and single-step debugging.
1798
+ if ( window.requestAnimationFrame && options.useRequestAnimationFrame ) {
1799
+ window.requestAnimationFrame(stateUpdate);
1800
+ } else {
1801
+ stateUpdate();
1802
+ }
1803
+ }
1804
+
1805
+ function setZindex ( ) {
1806
+
1807
+ scope_HandleNumbers.forEach(function(handleNumber){
1808
+ // Handles before the slider middle are stacked later = higher,
1809
+ // Handles after the middle later is lower
1810
+ // [[7] [8] .......... | .......... [5] [4]
1811
+ var dir = (scope_Locations[handleNumber] > 50 ? -1 : 1);
1812
+ var zIndex = 3 + (scope_Handles.length + (dir * handleNumber));
1813
+ scope_Handles[handleNumber].childNodes[0].style.zIndex = zIndex;
1814
+ });
1815
+ }
1816
+
1817
+ // Test suggested values and apply margin, step.
1818
+ function setHandle ( handleNumber, to, lookBackward, lookForward ) {
1819
+
1820
+ to = checkHandlePosition(scope_Locations, handleNumber, to, lookBackward, lookForward);
1821
+
1822
+ if ( to === false ) {
1823
+ return false;
1824
+ }
1825
+
1826
+ updateHandlePosition(handleNumber, to);
1827
+
1828
+ return true;
1829
+ }
1830
+
1831
+ // Updates style attribute for connect nodes
1832
+ function updateConnect ( index ) {
1833
+
1834
+ // Skip connects set to false
1835
+ if ( !scope_Connects[index] ) {
1836
+ return;
1837
+ }
1838
+
1839
+ var l = 0;
1840
+ var h = 100;
1841
+
1842
+ if ( index !== 0 ) {
1843
+ l = scope_Locations[index - 1];
1844
+ }
1845
+
1846
+ if ( index !== scope_Connects.length - 1 ) {
1847
+ h = scope_Locations[index];
1848
+ }
1849
+
1850
+ scope_Connects[index].style[options.style] = toPct(l);
1851
+ scope_Connects[index].style[options.styleOposite] = toPct(100 - h);
1852
+ }
1853
+
1854
+ // ...
1855
+ function setValue ( to, handleNumber ) {
1856
+
1857
+ // Setting with null indicates an 'ignore'.
1858
+ // Inputting 'false' is invalid.
1859
+ if ( to === null || to === false ) {
1860
+ return;
1861
+ }
1862
+
1863
+ // If a formatted number was passed, attemt to decode it.
1864
+ if ( typeof to === 'number' ) {
1865
+ to = String(to);
1866
+ }
1867
+
1868
+ to = options.format.from(to);
1869
+
1870
+ // Request an update for all links if the value was invalid.
1871
+ // Do so too if setting the handle fails.
1872
+ if ( to !== false && !isNaN(to) ) {
1873
+ setHandle(handleNumber, scope_Spectrum.toStepping(to), false, false);
1874
+ }
1875
+ }
1876
+
1877
+ // Set the slider value.
1878
+ function valueSet ( input, fireSetEvent ) {
1879
+
1880
+ var values = asArray(input);
1881
+ var isInit = scope_Locations[0] === undefined;
1882
+
1883
+ // Event fires by default
1884
+ fireSetEvent = (fireSetEvent === undefined ? true : !!fireSetEvent);
1885
+
1886
+ values.forEach(setValue);
1887
+
1888
+ // Animation is optional.
1889
+ // Make sure the initial values were set before using animated placement.
1890
+ if ( options.animate && !isInit ) {
1891
+ addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration);
1892
+ }
1893
+
1894
+ // Now that all base values are set, apply constraints
1895
+ scope_HandleNumbers.forEach(function(handleNumber){
1896
+ setHandle(handleNumber, scope_Locations[handleNumber], true, false);
1897
+ });
1898
+
1899
+ setZindex();
1900
+
1901
+ scope_HandleNumbers.forEach(function(handleNumber){
1902
+
1903
+ fireEvent('update', handleNumber);
1904
+
1905
+ // Fire the event only for handles that received a new value, as per #579
1906
+ if ( values[handleNumber] !== null && fireSetEvent ) {
1907
+ fireEvent('set', handleNumber);
1908
+ }
1909
+ });
1910
+ }
1911
+
1912
+ // Reset slider to initial values
1913
+ function valueReset ( fireSetEvent ) {
1914
+ valueSet(options.start, fireSetEvent);
1915
+ }
1916
+
1917
+ // Get the slider value.
1918
+ function valueGet ( ) {
1919
+
1920
+ var values = scope_Values.map(options.format.to);
1921
+
1922
+ // If only one handle is used, return a single value.
1923
+ if ( values.length === 1 ){
1924
+ return values[0];
1925
+ }
1926
+
1927
+ return values;
1928
+ }
1929
+
1930
+ // Removes classes from the root and empties it.
1931
+ function destroy ( ) {
1932
+
1933
+ for ( var key in options.cssClasses ) {
1934
+ if ( !options.cssClasses.hasOwnProperty(key) ) { continue; }
1935
+ removeClass(scope_Target, options.cssClasses[key]);
1936
+ }
1937
+
1938
+ while (scope_Target.firstChild) {
1939
+ scope_Target.removeChild(scope_Target.firstChild);
1940
+ }
1941
+
1942
+ delete scope_Target.noUiSlider;
1943
+ }
1944
+
1945
+ // Get the current step size for the slider.
1946
+ function getCurrentStep ( ) {
1947
+
1948
+ // Check all locations, map them to their stepping point.
1949
+ // Get the step point, then find it in the input list.
1950
+ return scope_Locations.map(function( location, index ){
1951
+
1952
+ var nearbySteps = scope_Spectrum.getNearbySteps( location );
1953
+ var value = scope_Values[index];
1954
+ var increment = nearbySteps.thisStep.step;
1955
+ var decrement = null;
1956
+
1957
+ // If the next value in this step moves into the next step,
1958
+ // the increment is the start of the next step - the current value
1959
+ if ( increment !== false ) {
1960
+ if ( value + increment > nearbySteps.stepAfter.startValue ) {
1961
+ increment = nearbySteps.stepAfter.startValue - value;
1962
+ }
1963
+ }
1964
+
1965
+
1966
+ // If the value is beyond the starting point
1967
+ if ( value > nearbySteps.thisStep.startValue ) {
1968
+ decrement = nearbySteps.thisStep.step;
1969
+ }
1970
+
1971
+ else if ( nearbySteps.stepBefore.step === false ) {
1972
+ decrement = false;
1973
+ }
1974
+
1975
+ // If a handle is at the start of a step, it always steps back into the previous step first
1976
+ else {
1977
+ decrement = value - nearbySteps.stepBefore.highestStep;
1978
+ }
1979
+
1980
+
1981
+ // Now, if at the slider edges, there is not in/decrement
1982
+ if ( location === 100 ) {
1983
+ increment = null;
1984
+ }
1985
+
1986
+ else if ( location === 0 ) {
1987
+ decrement = null;
1988
+ }
1989
+
1990
+ // As per #391, the comparison for the decrement step can have some rounding issues.
1991
+ var stepDecimals = scope_Spectrum.countStepDecimals();
1992
+
1993
+ // Round per #391
1994
+ if ( increment !== null && increment !== false ) {
1995
+ increment = Number(increment.toFixed(stepDecimals));
1996
+ }
1997
+
1998
+ if ( decrement !== null && decrement !== false ) {
1999
+ decrement = Number(decrement.toFixed(stepDecimals));
2000
+ }
2001
+
2002
+ return [decrement, increment];
2003
+ });
2004
+ }
2005
+
2006
+ // Attach an event to this slider, possibly including a namespace
2007
+ function bindEvent ( namespacedEvent, callback ) {
2008
+ scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || [];
2009
+ scope_Events[namespacedEvent].push(callback);
2010
+
2011
+ // If the event bound is 'update,' fire it immediately for all handles.
2012
+ if ( namespacedEvent.split('.')[0] === 'update' ) {
2013
+ scope_Handles.forEach(function(a, index){
2014
+ fireEvent('update', index);
2015
+ });
2016
+ }
2017
+ }
2018
+
2019
+ // Undo attachment of event
2020
+ function removeEvent ( namespacedEvent ) {
2021
+
2022
+ var event = namespacedEvent && namespacedEvent.split('.')[0];
2023
+ var namespace = event && namespacedEvent.substring(event.length);
2024
+
2025
+ Object.keys(scope_Events).forEach(function( bind ){
2026
+
2027
+ var tEvent = bind.split('.')[0],
2028
+ tNamespace = bind.substring(tEvent.length);
2029
+
2030
+ if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) {
2031
+ delete scope_Events[bind];
2032
+ }
2033
+ });
2034
+ }
2035
+
2036
+ // Updateable: margin, limit, padding, step, range, animate, snap
2037
+ function updateOptions ( optionsToUpdate, fireSetEvent ) {
2038
+
2039
+ // Spectrum is created using the range, snap, direction and step options.
2040
+ // 'snap' and 'step' can be updated, 'direction' cannot, due to event binding.
2041
+ // If 'snap' and 'step' are not passed, they should remain unchanged.
2042
+ var v = valueGet();
2043
+
2044
+ var updateAble = ['margin', 'limit', 'padding', 'range', 'animate', 'snap', 'step', 'format'];
2045
+
2046
+ // Only change options that we're actually passed to update.
2047
+ updateAble.forEach(function(name){
2048
+ if ( optionsToUpdate[name] !== undefined ) {
2049
+ originalOptions[name] = optionsToUpdate[name];
2050
+ }
2051
+ });
2052
+
2053
+ var newOptions = testOptions(originalOptions);
2054
+
2055
+ // Load new options into the slider state
2056
+ updateAble.forEach(function(name){
2057
+ if ( optionsToUpdate[name] !== undefined ) {
2058
+ options[name] = newOptions[name];
2059
+ }
2060
+ });
2061
+
2062
+ // Save current spectrum direction as testOptions in testRange call
2063
+ // doesn't rely on current direction
2064
+ newOptions.spectrum.direction = scope_Spectrum.direction;
2065
+ scope_Spectrum = newOptions.spectrum;
2066
+
2067
+ // Limit, margin and padding depend on the spectrum but are stored outside of it. (#677)
2068
+ options.margin = newOptions.margin;
2069
+ options.limit = newOptions.limit;
2070
+ options.padding = newOptions.padding;
2071
+
2072
+ // Invalidate the current positioning so valueSet forces an update.
2073
+ scope_Locations = [];
2074
+ valueSet(optionsToUpdate.start || v, fireSetEvent);
2075
+ }
2076
+
2077
+ // Throw an error if the slider was already initialized.
2078
+ if ( scope_Target.noUiSlider ) {
2079
+ throw new Error('Slider was already initialized.');
2080
+ }
2081
+
2082
+ // Create the base element, initialise HTML and set classes.
2083
+ // Add handles and connect elements.
2084
+ addSlider(scope_Target);
2085
+ addElements(options.connect, scope_Base);
2086
+
2087
+ scope_Self = {
2088
+ destroy: destroy,
2089
+ steps: getCurrentStep,
2090
+ on: bindEvent,
2091
+ off: removeEvent,
2092
+ get: valueGet,
2093
+ set: valueSet,
2094
+ reset: valueReset,
2095
+ // Exposed for unit testing, don't use this in your application.
2096
+ __moveHandles: function(a, b, c) { moveHandles(a, b, scope_Locations, c); },
2097
+ options: originalOptions, // Issue #600, #678
2098
+ updateOptions: updateOptions,
2099
+ target: scope_Target, // Issue #597
2100
+ pips: pips // Issue #594
2101
+ };
2102
+
2103
+ // Attach user events.
2104
+ bindSliderEvents(options.events);
2105
+
2106
+ // Use the public value method to set the start values.
2107
+ valueSet(options.start);
2108
+
2109
+ if ( options.pips ) {
2110
+ pips(options.pips);
2111
+ }
2112
+
2113
+ if ( options.tooltips ) {
2114
+ tooltips();
2115
+ }
2116
+
2117
+ return scope_Self;
2118
+
2119
+ }
2120
+
2121
+
2122
+ // Run the standard initializer
2123
+ function initialize ( target, originalOptions ) {
2124
+
2125
+ if ( !target.nodeName ) {
2126
+ throw new Error('noUiSlider.create requires a single element.');
2127
+ }
2128
+
2129
+ if (originalOptions.tooltips === undefined) {
2130
+ originalOptions.tooltips = true;
2131
+ }
2132
+
2133
+ // Test the options and create the slider environment;
2134
+ var options = testOptions( originalOptions, target );
2135
+ var api = closure( target, options, originalOptions );
2136
+
2137
+ target.noUiSlider = api;
2138
+
2139
+ return api;
2140
+ }
2141
+
2142
+ // Use an object instead of a function for future expansibility;
2143
+ return {
2144
+ create: initialize
2145
+ };
2146
+
2147
+ }));