nouislider-rails 6.0.1 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23943f5ff9d0c4c8250cf42650d496927b85a4ba
4
- data.tar.gz: 923878695c0f36b2a92ccf75360d4163e284a997
3
+ metadata.gz: 8c836710318e117437b0d7f90ee0208a50214103
4
+ data.tar.gz: c2f2a7fac403d92f029e1490ba3986e55e040f97
5
5
  SHA512:
6
- metadata.gz: 36e0aad250ccd59357bfa374bcefd6084346a2c0c74941939fed9ca6bffb23f8be25a6a9613455d165155bfca92fb00421e287132029c619daf6b1bcebef0ea4
7
- data.tar.gz: 481477ce08cc866531a19df13f3f5a9be53afead08afb1e3afdc44faa6925f991928fd3af216246534412b72bf9e1635d8b9dee987aca16da6b7850cc448a626
6
+ metadata.gz: 7107eca155b0c8861b5993e252a65b57200c2ee09d211697fed7e898ba71173fe45ecdc280ae984c53e33eb74e64d7c96dd50dbc975adb929158107ca0305f9c
7
+ data.tar.gz: 5cde020e4e9a4858e4d0ef526d511c90f728bc03754ba033cebb889ad60874ed3de015d0109a43b552de315c3e28385d5964d22585ee53f8310576db675808b2
@@ -1,5 +1,5 @@
1
1
  module Nouislider
2
2
  module Rails
3
- VERSION = "6.0.1"
3
+ VERSION = "6.1.0"
4
4
  end
5
5
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Charles Lee"]
10
10
  spec.email = ["chug2k@gmail.com"]
11
11
  spec.summary = "jquery-nouislider.js for the Rails asset pipeline."
12
- spec.description = "Currently tracking 6.0 of jquery-noUiSlider: https://github.com/leongersen/noUiSlider). All credit and thanks to @leongersen for the awesome library."
12
+ spec.description = "Currently tracking 6.1 of jquery-noUiSlider: https://github.com/leongersen/noUiSlider). All credit and thanks to @leongersen for the awesome library."
13
13
  spec.homepage = "https://github.com/chug2k/nouislider-rails"
14
14
  spec.license = "MIT"
15
15
 
@@ -0,0 +1,389 @@
1
+ /**@preserve
2
+ $.Link (part of noUiSlider) - WTFPL */
3
+
4
+ /*jslint browser: true */
5
+ /*jslint sub: true */
6
+ /*jslint white: true */
7
+
8
+ (function( $ ){
9
+
10
+ 'use strict';
11
+
12
+ // Throw an error if formatting options are incompatible.
13
+ function throwEqualError( F, a, b ) {
14
+ if ( (F[a] || F[b]) && (F[a] === F[b]) ) {
15
+ throw new Error("(Link) '"+a+"' can't match '"+b+"'.'");
16
+ }
17
+ }
18
+
19
+ // Test in an object is an instance of jQuery or Zepto.
20
+ function isInstance ( a ) {
21
+ return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) );
22
+ }
23
+
24
+ var
25
+ /** @const */ Formatting = [
26
+ /* 0 */ 'decimals'
27
+ /* 1 */ ,'mark'
28
+ /* 2 */ ,'thousand'
29
+ /* 3 */ ,'prefix'
30
+ /* 4 */ ,'postfix'
31
+ /* 5 */ ,'encoder'
32
+ /* 6 */ ,'decoder'
33
+ /* 7 */ ,'negative'
34
+ /* 8 */ ,'negativeBefore'
35
+ /* 9 */ ,'to'
36
+ /* 10 */ ,'from'
37
+ ],
38
+ /** @const */ FormatDefaults = [
39
+ /* 0 */ 2
40
+ /* 1 */ ,'.'
41
+ /* 2 */ ,''
42
+ /* 3 */ ,''
43
+ /* 4 */ ,''
44
+ /* 5 */ ,function(a){ return a; }
45
+ /* 6 */ ,function(a){ return a; }
46
+ /* 7 */ ,'-'
47
+ /* 8 */ ,''
48
+ /* 9 */ ,function(a){ return a; }
49
+ /* 10 */ ,function(a){ return a; }
50
+ ];
51
+
52
+
53
+ /** @constructor */
54
+ function Format( options ){
55
+
56
+ // If no settings where provided, the defaults will be loaded.
57
+ if ( options === undefined ){
58
+ options = {};
59
+ }
60
+
61
+ if ( typeof options !== 'object' ){
62
+ throw new Error("(Format) 'format' option must be an object.");
63
+ }
64
+
65
+ var settings = {};
66
+
67
+ // Copy all values into a new object.
68
+ $(Formatting).each(function(i, val){
69
+
70
+ if ( options[val] === undefined ){
71
+
72
+ settings[val] = FormatDefaults[i];
73
+
74
+ // When we aren't loading defaults, validate the entry.
75
+ } else if ( (typeof options[val]) === (typeof FormatDefaults[i]) ) {
76
+
77
+ // Support for up to 7 decimals.
78
+ // More can't be guaranteed due to floating point issues.
79
+ if ( val === 'decimals' ){
80
+ if ( options[val] < 0 || options[val] > 7 ){
81
+ throw new Error("(Format) 'format.decimals' option must be between 0 and 7.");
82
+ }
83
+ }
84
+
85
+ settings[val] = options[val];
86
+
87
+ // If the value isn't valid, emit an error.
88
+ } else {
89
+ throw new Error("(Format) 'format."+val+"' must be a " + typeof FormatDefaults[i] + ".");
90
+ }
91
+ });
92
+
93
+ // Some values can't be extracted from a
94
+ // string if certain combinations are present.
95
+ throwEqualError(settings, 'mark', 'thousand');
96
+ throwEqualError(settings, 'prefix', 'negative');
97
+ throwEqualError(settings, 'prefix', 'negativeBefore');
98
+
99
+ this.settings = settings;
100
+ }
101
+
102
+ // Shorthand for internal value get
103
+ Format.prototype.v = function ( a ) {
104
+ return this.settings[a];
105
+ };
106
+
107
+ Format.prototype.to = function ( number ) {
108
+
109
+ function reverse ( a ) {
110
+ return a.split('').reverse().join('');
111
+ }
112
+
113
+ number = this.v('encoder')( number );
114
+
115
+ var decimals = this.v('decimals'),
116
+ negative = '', preNegative = '', base = '', mark = '';
117
+
118
+ // Rounding away decimals might cause a value of -0
119
+ // when using very small ranges. Remove those cases.
120
+ if ( parseFloat(number.toFixed(decimals)) === 0 ) {
121
+ number = '0';
122
+ }
123
+
124
+ if ( number < 0 ) {
125
+ negative = this.v('negative');
126
+ preNegative = this.v('negativeBefore');
127
+ }
128
+
129
+ // Round to proper decimal count
130
+ number = Math.abs(number).toFixed(decimals).toString();
131
+ number = number.split('.');
132
+
133
+ // Group numbers in sets of three.
134
+ if ( this.v('thousand') ) {
135
+ base = reverse(number[0]).match(/.{1,3}/g);
136
+ base = reverse(base.join(reverse( this.v('thousand') )));
137
+ } else {
138
+ base = number[0];
139
+ }
140
+
141
+ // Ignore the decimal separator if decimals are set to 0.
142
+ if ( this.v('mark') && number.length > 1 ) {
143
+ mark = this.v('mark') + number[1];
144
+ }
145
+
146
+ // Return the finalized formatted number.
147
+ return this.v('to')(preNegative +
148
+ this.v('prefix') +
149
+ negative +
150
+ base +
151
+ mark +
152
+ this.v('postfix'));
153
+ };
154
+
155
+ Format.prototype.from = function ( input ) {
156
+
157
+ function esc(s){
158
+ return s.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&');
159
+ }
160
+
161
+ var isNeg;
162
+ // The set request might want to ignore this handle.
163
+ // Test for 'undefined' too, as a two-handle slider
164
+ // can still be set with an integer.
165
+ if ( input === null || input === undefined ) {
166
+ return false;
167
+ }
168
+
169
+ input = this.v('from')(input);
170
+
171
+ // Remove formatting and set period for float parsing.
172
+ input = input.toString();
173
+
174
+ // Replace the preNegative indicator.
175
+ isNeg = input.replace(new RegExp('^' + esc( this.v('negativeBefore') )), '');
176
+
177
+ // Check if the value changed by removing the negativeBefore symbol.
178
+ if( input !== isNeg ) {
179
+ input = isNeg;
180
+ isNeg = '-';
181
+ } else {
182
+ isNeg = '';
183
+ }
184
+
185
+ // If prefix is set and the number is actually prefixed.
186
+ input = input.replace(new RegExp('^'+esc( this.v('prefix') )), '');
187
+
188
+ // Only replace if a negative sign is set.
189
+ if ( this.v('negative') ) {
190
+
191
+ // Reset isNeg to prevent double '-' insertion.
192
+ isNeg = '';
193
+
194
+ // Reset the negative sign to '-'
195
+ input = input.replace(new RegExp('^'+esc( this.v('negative') )), '-');
196
+ }
197
+
198
+ // Clean the input string
199
+ input = input
200
+ // If postfix is set and the number is postfixed.
201
+ .replace( new RegExp(esc( this.v('postfix') ) + '$'), '')
202
+ // Remove the separator every three digits.
203
+ .replace( new RegExp(esc( this.v('thousand') ), 'g'), '')
204
+ // Set the decimal separator back to period.
205
+ .replace( this.v('mark'), '.');
206
+
207
+ // Run the user defined decoder. Returns input by default.
208
+ input = this.v('decoder')( parseFloat( isNeg + input ) );
209
+
210
+ // Ignore invalid input
211
+ if (isNaN( input )) {
212
+ return false;
213
+ }
214
+
215
+ return input;
216
+ };
217
+
218
+
219
+ /** @expose */
220
+ /** @constructor */
221
+ function Link ( entry, update ) {
222
+
223
+ if ( typeof entry !== "object" ) {
224
+ $.error("(Link) Initialize with an object.");
225
+ }
226
+
227
+ // Make sure Link isn't called as a function, in which case
228
+ // the 'this' scope would be the window.
229
+ return new Link.prototype.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update );
230
+ }
231
+
232
+ Link.prototype.setTooltip = function ( target, method ) {
233
+
234
+ // By default, use the 'html' method.
235
+ this.method = method || 'html';
236
+
237
+ // Use jQuery to create the element
238
+ this.el = $( target.replace('-tooltip-', '') || '<div/>' )[0];
239
+ };
240
+
241
+ Link.prototype.setHidden = function ( target ) {
242
+
243
+ this.method = 'val';
244
+
245
+ this.el = document.createElement('input');
246
+ this.el.name = target;
247
+ this.el.type = 'hidden';
248
+ };
249
+
250
+ Link.prototype.setField = function ( target ) {
251
+
252
+ // Returns nulled array.
253
+ function at(a,b,c){
254
+ return [c?a:b, c?b:a];
255
+ }
256
+
257
+ // In IE < 9, .bind() isn't available, need this link in .change().
258
+ var that = this;
259
+
260
+ // Default to .val if this is an input element.
261
+ this.method = 'val';
262
+ // Set the slider to a new value on change.
263
+ this.target = target.on('change', function( e ){
264
+ that.obj.val(
265
+ at(null, $(e.target).val(), that.N),
266
+ { 'link': that, 'set': true }
267
+ );
268
+ });
269
+ };
270
+
271
+
272
+ // Initialisor
273
+ /** @constructor */
274
+ Link.prototype.init = function ( target, method, format, update ) {
275
+
276
+ // Write all formatting to this object.
277
+ // No validation needed, as we'll merge these with the parent
278
+ // format options first.
279
+ this.formatting = format;
280
+
281
+ // Store the update option.
282
+ this.update = !update;
283
+
284
+ // If target is a string, a new hidden input will be created.
285
+ if ( typeof target === 'string' && target.indexOf('-tooltip-') === 0 ) {
286
+ this.setTooltip( target, method );
287
+ return;
288
+ }
289
+
290
+ // If the string doesn't begin with '-', which is reserved, add a new hidden input.
291
+ if ( typeof target === 'string' && target.indexOf('-') !== 0 ) {
292
+ this.setHidden( target );
293
+ return;
294
+ }
295
+
296
+ // The target can also be a function, which will be called.
297
+ if ( typeof target === 'function' ) {
298
+ this.target = false;
299
+ this.method = target;
300
+ return;
301
+ }
302
+
303
+ if ( isInstance(target) ) {
304
+ // If a jQuery/Zepto input element is provided, but no method is set,
305
+ // the element can assume it needs to respond to 'change'...
306
+
307
+ if ( !method ) {
308
+
309
+ if ( target.is('input, select, textarea') ) {
310
+ this.setField( target );
311
+ return;
312
+ }
313
+
314
+ // If no method is set, and we are not auto-binding an input, default to 'html'.
315
+ method = 'html';
316
+ }
317
+
318
+ // The method must exist on the element.
319
+ if ( typeof method === 'function' || (typeof method === 'string' && target[method]) ) {
320
+ this.method = method;
321
+ this.target = target;
322
+ return;
323
+ }
324
+ }
325
+
326
+ // Nothing matched, throw error.
327
+ throw new RangeError("(Link) Invalid Link.");
328
+ };
329
+
330
+ // Provides external items with the slider value.
331
+ Link.prototype.write = function ( value, handle, slider, update ) {
332
+
333
+ // Don't synchronize this Link.
334
+ if ( this.update && update === false ) {
335
+ return;
336
+ }
337
+
338
+ this.actual = value;
339
+
340
+ // Format values for display.
341
+ value = this.format( value );
342
+
343
+ // Store the numerical value.
344
+ this.saved = value;
345
+
346
+ // Branch between serialization to a function or an object.
347
+ if ( typeof this.method === 'function' ) {
348
+ // When target is undefined, the target was a function.
349
+ // In that case, provided the slider as the calling scope.
350
+ // Use [0] to get the DOM element, not the $ instance.
351
+ this.method.call( this.target[0] || slider[0], value, handle, slider );
352
+ } else {
353
+ this.target[ this.method ]( value, handle, slider );
354
+ }
355
+ };
356
+
357
+ // Set formatting options.
358
+ Link.prototype.setFormatting = function ( options ) {
359
+ this.formatting = new Format($.extend({},
360
+ options,
361
+ this.formatting instanceof Format ? this.formatting.settings : this.formatting
362
+ ));
363
+ };
364
+
365
+ Link.prototype.setObject = function ( obj ) {
366
+ this.obj = obj;
367
+ };
368
+
369
+ Link.prototype.setIndex = function ( index ) {
370
+ this.N = index;
371
+ };
372
+
373
+ // Parses slider value to user defined display.
374
+ Link.prototype.format = function ( a ) {
375
+ return this.formatting.to(a);
376
+ };
377
+
378
+ // Converts a formatted value back to a real number.
379
+ Link.prototype.getValue = function ( a ) {
380
+ return this.formatting.from(a);
381
+ };
382
+
383
+ // We can now test for Link.init to be an instance of Link.
384
+ Link.prototype.init.prototype = Link.prototype;
385
+
386
+ /** @expose */
387
+ $.Link = Link;
388
+
389
+ }( window['jQuery'] || window['Zepto'] ));
@@ -1,1650 +1,1252 @@
1
- /*! $.noUiSlider - WTFPL - refreshless.com/nouislider/ */
1
+ /**@preserve
2
+ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */
2
3
 
3
4
  /*jslint browser: true */
4
- /*jslint devel: true */
5
- /*jslint continue: true */
6
- /*jslint plusplus: true */
7
5
  /*jslint sub: true */
8
6
  /*jslint white: true */
9
-
10
- // ==ClosureCompiler==
11
- // @externs_url http://refreshless.com/externs/jquery-1.8.js
12
- // @compilation_level ADVANCED_OPTIMIZATIONS
13
- // @warning_level VERBOSE
14
- // ==/ClosureCompiler==
7
+ /*jslint continue: true */
8
+ /*jslint plusplus: true */
15
9
 
16
10
  (function( $ ){
17
11
 
18
- 'use strict';
19
-
20
- var
21
- // Cache the document selector;
22
- /** @const */ doc = $(document),
23
- // Namespace for binding and unbinding slider events;
24
- /** @const */ namespace = '.nui',
25
- // Copy of the current value function;
26
- /** @const */ $val = $.fn.val,
27
- // Determine the events to bind. IE11 implements pointerEvents without
28
- // a prefix, which breaks compatibility with the IE10 implementation.
29
- /** @const */ actions = window.navigator.pointerEnabled ? {
30
- start: 'pointerdown',
31
- move: 'pointermove',
32
- end: 'pointerup'
33
- } : window.navigator.msPointerEnabled ? {
34
- start: 'MSPointerDown',
35
- move: 'MSPointerMove',
36
- end: 'MSPointerUp'
37
- } : {
38
- start: 'mousedown touchstart',
39
- move: 'mousemove touchmove',
40
- end: 'mouseup touchend'
41
- },
42
- // Re-usable list of classes;
43
- /** @const */ Classes = [
44
- /* 0 */ 'noUi-target'
45
- /* 1 */ ,'noUi-base'
46
- /* 2 */ ,'noUi-origin'
47
- /* 3 */ ,'noUi-handle'
48
- /* 4 */ ,'noUi-horizontal'
49
- /* 5 */ ,'noUi-vertical'
50
- /* 6 */ ,'noUi-background'
51
- /* 7 */ ,'noUi-connect'
52
- /* 8 */ ,'noUi-ltr'
53
- /* 9 */ ,'noUi-rtl'
54
- /* 10 */ ,'noUi-dragable'
55
- /* 11 */ ,''
56
- /* 12 */ ,'noUi-state-drag'
57
- /* 13 */ ,''
58
- /* 14 */ ,'noUi-state-tap'
59
- /* 15 */ ,'noUi-active'
60
- /* 16 */ ,'noUi-extended'
61
- /* 17 */ ,'noUi-stacking'
62
- ],
63
- /** @const */ Formatting = [
64
- /* 0 */ 'decimals'
65
- /* 1 */ ,'mark'
66
- /* 2 */ ,'thousand'
67
- /* 3 */ ,'prefix'
68
- /* 4 */ ,'postfix'
69
- /* 5 */ ,'encoder'
70
- /* 6 */ ,'decoder'
71
- /* 7 */ ,'negative'
72
- /* 8 */ ,'negativeBefore'
73
- ],
74
- /** @const */ FormatDefaults = [
75
- /* 0 */ 2
76
- /* 1 */ ,'.'
77
- /* 2 */ ,''
78
- /* 3 */ ,''
79
- /* 4 */ ,''
80
- /* 5 */ ,function(a){ return a; }
81
- /* 6 */ ,function(a){ return a; }
82
- /* 7 */ ,'-'
83
- /* 8 */ ,''
84
- ];
85
-
86
-
87
- // Error handling
88
-
89
- function throwError( message ){
90
- throw new RangeError('noUiSlider: ' + message);
91
- }
92
-
93
- // Throw an error if formatting options are incompatible.
94
- function throwEqualError( F, a, b ) {
95
- if ( (F[a] || F[b]) && (F[a] === F[b]) ) {
96
- throwError("(Link) '"+a+"' can't match '"+b+"'.'");
97
- }
98
- }
12
+ 'use strict';
13
+
14
+ var
15
+ // Cache the document selector;
16
+ /** @const */
17
+ doc = $(document),
18
+ // Namespace for binding and unbinding slider events;
19
+ /** @const */
20
+ namespace = '.nui',
21
+ // Determine the events to bind. IE11 implements pointerEvents without
22
+ // a prefix, which breaks compatibility with the IE10 implementation.
23
+ /** @const */
24
+ actions = window.navigator['pointerEnabled'] ? {
25
+ start: 'pointerdown',
26
+ move: 'pointermove',
27
+ end: 'pointerup'
28
+ } : window.navigator['msPointerEnabled'] ? {
29
+ start: 'MSPointerDown',
30
+ move: 'MSPointerMove',
31
+ end: 'MSPointerUp'
32
+ } : {
33
+ start: 'mousedown touchstart',
34
+ move: 'mousemove touchmove',
35
+ end: 'mouseup touchend'
36
+ },
37
+ // Re-usable list of classes;
38
+ /** @const */
39
+ Classes = [
40
+ /* 0 */ 'noUi-target'
41
+ /* 1 */ ,'noUi-base'
42
+ /* 2 */ ,'noUi-origin'
43
+ /* 3 */ ,'noUi-handle'
44
+ /* 4 */ ,'noUi-horizontal'
45
+ /* 5 */ ,'noUi-vertical'
46
+ /* 6 */ ,'noUi-background'
47
+ /* 7 */ ,'noUi-connect'
48
+ /* 8 */ ,'noUi-ltr'
49
+ /* 9 */ ,'noUi-rtl'
50
+ /* 10 */ ,'noUi-dragable'
51
+ /* 11 */ ,''
52
+ /* 12 */ ,'noUi-state-drag'
53
+ /* 13 */ ,''
54
+ /* 14 */ ,'noUi-state-tap'
55
+ /* 15 */ ,'noUi-active'
56
+ /* 16 */ ,'noUi-extended'
57
+ /* 17 */ ,'noUi-stacking'
58
+ ];
99
59
 
100
60
 
101
61
  // General helpers
102
62
 
103
- // Limits a value to 0 - 100
104
- function limit ( a ) {
105
- return Math.max(Math.min(a, 100), 0);
106
- }
63
+ // Limits a value to 0 - 100
64
+ function limit ( a ) {
65
+ return Math.max(Math.min(a, 100), 0);
66
+ }
107
67
 
108
- // Round a value to the closest 'to'.
109
- function closest ( value, to ) {
110
- return Math.round(value / to) * to;
111
- }
68
+ // Round a value to the closest 'to'.
69
+ function closest ( value, to ) {
70
+ return Math.round(value / to) * to;
71
+ }
112
72
 
113
- // Determine the size of a sub-range in relation to a full range.
114
- function subRangeRatio ( pa, pb ) {
115
- return (100 / (pb - pa));
116
- }
73
+ // Determine the size of a sub-range in relation to a full range.
74
+ function subRangeRatio ( pa, pb ) {
75
+ return (100 / (pb - pa));
76
+ }
117
77
 
118
78
 
119
79
  // Type validation
120
80
 
121
- function typeMatch ( a, b ) {
122
- return (typeof a) === (typeof b);
123
- }
81
+ // Checks whether a value is numerical.
82
+ function isNumeric ( a ) {
83
+ return typeof a === 'number' && !isNaN( a ) && isFinite( a );
84
+ }
124
85
 
125
- // Test in an object is an instance of jQuery or Zepto.
126
- function isInstance ( a ) {
127
- return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) );
128
- }
129
-
130
- // Checks whether a value is numerical.
131
- function isNumeric ( a ) {
132
- return typeof a === 'number' && !isNaN( a ) && isFinite( a );
133
- }
134
-
135
- // Wraps a variable as an array, if it isn't one yet.
136
- function asArray ( a ) {
137
- return $.isArray(a) ? a : [a];
138
- }
86
+ // Wraps a variable as an array, if it isn't one yet.
87
+ function asArray ( a ) {
88
+ return $.isArray(a) ? a : [a];
89
+ }
139
90
 
140
91
 
141
92
  // Class handling
142
93
 
143
- // Sets a class and removes it after [duration] ms.
144
- function addClassFor ( element, className, duration ) {
145
- element.addClass(className);
146
- setTimeout(function(){
147
- element.removeClass(className);
148
- }, duration);
149
- }
150
-
151
- // Tests if element has a class, adds it if not. Returns original state.
152
- function getsClass ( element, className ) {
153
-
154
- var has = element.hasClass(className);
155
-
156
- if ( !has ) {
157
- element.addClass( className );
158
- }
159
-
160
- return has;
161
- }
94
+ // Sets a class and removes it after [duration] ms.
95
+ function addClassFor ( element, className, duration ) {
96
+ element.addClass(className);
97
+ setTimeout(function(){
98
+ element.removeClass(className);
99
+ }, duration);
100
+ }
162
101
 
163
102
 
164
103
  // Value calculation
165
104
 
166
- // (percentage) How many percent is this value of this range?
167
- function fromPercentage ( range, value ) {
168
- return (value * 100) / ( range[1] - range[0] );
169
- }
105
+ // (percentage) How many percent is this value of this range?
106
+ function fromPercentage ( range, value ) {
107
+ return (value * 100) / ( range[1] - range[0] );
108
+ }
170
109
 
171
- // (percentage) Where is this value on this range?
172
- function toPercentage ( range, value ) {
173
- return fromPercentage( range, range[0] < 0 ?
174
- value + Math.abs(range[0]) :
175
- value - range[0] );
176
- }
110
+ // (percentage) Where is this value on this range?
111
+ function toPercentage ( range, value ) {
112
+ return fromPercentage( range, range[0] < 0 ?
113
+ value + Math.abs(range[0]) :
114
+ value - range[0] );
115
+ }
177
116
 
178
- // (value) How much is this percentage on this range?
179
- function isPercentage ( range, value ) {
180
- return ((value * ( range[1] - range[0] )) / 100) + range[0];
181
- }
117
+ // (value) How much is this percentage on this range?
118
+ function isPercentage ( range, value ) {
119
+ return ((value * ( range[1] - range[0] )) / 100) + range[0];
120
+ }
182
121
 
183
- // (percentage)
184
- function toStepping ( options, value ) {
122
+ // (percentage)
123
+ function toStepping ( options, value ) {
185
124
 
186
- if ( value >= options.xVal.slice(-1)[0] ){
187
- return 100;
188
- }
125
+ if ( value >= options.xVal.slice(-1)[0] ){
126
+ return 100;
127
+ }
189
128
 
190
- var j = 1, va, vb, pa, pb;
191
- while ( value >= options.xVal[j] ){
192
- j++;
193
- }
129
+ var j = 1, va, vb, pa, pb;
130
+ while ( value >= options.xVal[j] ){
131
+ j++;
132
+ }
194
133
 
195
- va = options.xVal[j-1];
196
- vb = options.xVal[j];
197
- pa = options.xPct[j-1];
198
- pb = options.xPct[j];
134
+ va = options.xVal[j-1];
135
+ vb = options.xVal[j];
136
+ pa = options.xPct[j-1];
137
+ pb = options.xPct[j];
199
138
 
200
- return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
201
- }
139
+ return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb));
140
+ }
202
141
 
203
- // (value)
204
- function fromStepping ( options, value ) {
142
+ // (value)
143
+ function fromStepping ( options, value ) {
205
144
 
206
- // There is no range group that fits 100
207
- if ( value >= 100 ){
208
- return options.xVal.slice(-1)[0];
209
- }
145
+ // There is no range group that fits 100
146
+ if ( value >= 100 ){
147
+ return options.xVal.slice(-1)[0];
148
+ }
210
149
 
211
- var j = 1, va, vb, pa, pb;
212
- while ( value >= options.xPct[j] ){
213
- j++;
214
- }
150
+ var j = 1, va, vb, pa, pb;
151
+ while ( value >= options.xPct[j] ){
152
+ j++;
153
+ }
215
154
 
216
- va = options.xVal[j-1];
217
- vb = options.xVal[j];
218
- pa = options.xPct[j-1];
219
- pb = options.xPct[j];
155
+ va = options.xVal[j-1];
156
+ vb = options.xVal[j];
157
+ pa = options.xPct[j-1];
158
+ pb = options.xPct[j];
220
159
 
221
- return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
222
- }
160
+ return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb));
161
+ }
223
162
 
224
- // (percentage) Get the step that applies at a certain value.
225
- function getStep ( options, value ){
163
+ // (percentage) Get the step that applies at a certain value.
164
+ function getStep ( options, value ){
226
165
 
227
- var j = 1, a, b;
228
- while ( value >= options.xPct[j] ){
229
- j++;
230
- }
166
+ var j = 1, a, b;
167
+ while ( value >= options.xPct[j] ){
168
+ j++;
169
+ }
231
170
 
232
- if ( options.snap ) {
171
+ if ( options.snap ) {
233
172
 
234
- a = options.xPct[j-1];
235
- b = options.xPct[j];
173
+ a = options.xPct[j-1];
174
+ b = options.xPct[j];
236
175
 
237
- if ((value - a) > ((b-a)/2)){
238
- return b;
239
- }
176
+ if ((value - a) > ((b-a)/2)){
177
+ return b;
178
+ }
240
179
 
241
- return a;
242
- }
180
+ return a;
181
+ }
243
182
 
244
- if ( !options.xSteps[j-1] ){
245
- return value;
246
- }
183
+ if ( !options.xSteps[j-1] ){
184
+ return value;
185
+ }
247
186
 
248
- return options.xPct[j-1] + closest(
249
- value - options.xPct[j-1],
250
- options.xSteps[j-1]
251
- );
252
- }
187
+ return options.xPct[j-1] + closest(
188
+ value - options.xPct[j-1],
189
+ options.xSteps[j-1]
190
+ );
191
+ }
253
192
 
254
193
 
255
194
  // Event handling
256
195
 
257
- // Provide a clean event with standardized offset values.
258
- function fixEvent ( e ) {
259
-
260
- // Prevent scrolling and panning on touch events, while
261
- // attempting to slide. The tap event also depends on this.
262
- e.preventDefault();
263
-
264
- // Filter the event to register the type, which can be
265
- // touch, mouse or pointer. Offset changes need to be
266
- // made on an event specific basis.
267
- var touch = e.type.indexOf('touch') === 0
268
- ,mouse = e.type.indexOf('mouse') === 0
269
- ,pointer = e.type.indexOf('pointer') === 0
270
- ,x,y, event = e;
271
-
272
- // IE10 implemented pointer events with a prefix;
273
- if ( e.type.indexOf('MSPointer') === 0 ) {
274
- pointer = true;
275
- }
276
-
277
- // Get the originalEvent, if the event has been wrapped
278
- // by jQuery. Zepto doesn't wrap the event.
279
- if ( e.originalEvent ) {
280
- e = e.originalEvent;
281
- }
196
+ // Provide a clean event with standardized offset values.
197
+ function fixEvent ( e ) {
282
198
 
283
- if ( touch ) {
284
- // noUiSlider supports one movement at a time,
285
- // so we can select the first 'changedTouch'.
286
- x = e.changedTouches[0].pageX;
287
- y = e.changedTouches[0].pageY;
288
- }
199
+ // Prevent scrolling and panning on touch events, while
200
+ // attempting to slide. The tap event also depends on this.
201
+ e.preventDefault();
289
202
 
290
- if ( mouse || pointer ) {
203
+ // Filter the event to register the type, which can be
204
+ // touch, mouse or pointer. Offset changes need to be
205
+ // made on an event specific basis.
206
+ var touch = e.type.indexOf('touch') === 0
207
+ ,mouse = e.type.indexOf('mouse') === 0
208
+ ,pointer = e.type.indexOf('pointer') === 0
209
+ ,x,y, event = e;
291
210
 
292
- // Polyfill the pageXOffset and pageYOffset
293
- // variables for IE7 and IE8;
294
- if( !pointer && window.pageXOffset === undefined ){
295
- window.pageXOffset = document.documentElement.scrollLeft;
296
- window.pageYOffset = document.documentElement.scrollTop;
297
- }
211
+ // IE10 implemented pointer events with a prefix;
212
+ if ( e.type.indexOf('MSPointer') === 0 ) {
213
+ pointer = true;
214
+ }
298
215
 
299
- x = e.clientX + window.pageXOffset;
300
- y = e.clientY + window.pageYOffset;
301
- }
216
+ // Get the originalEvent, if the event has been wrapped
217
+ // by jQuery. Zepto doesn't wrap the event.
218
+ if ( e.originalEvent ) {
219
+ e = e.originalEvent;
220
+ }
302
221
 
303
- event.points = [x, y];
304
- event.cursor = mouse;
222
+ if ( touch ) {
223
+ // noUiSlider supports one movement at a time,
224
+ // so we can select the first 'changedTouch'.
225
+ x = e.changedTouches[0].pageX;
226
+ y = e.changedTouches[0].pageY;
227
+ }
305
228
 
306
- return event;
307
- }
308
-
309
-
310
- // Organize formatting in an object.
311
-
312
- /** @constructor */
313
- function Format( options ){
229
+ if ( mouse || pointer ) {
314
230
 
315
- // If no settings where provided, the defaults will be loaded.
316
- if ( options === undefined ){
317
- options = {};
318
- }
319
-
320
- if ( typeof options !== 'object' ){
321
- throwError("(Format) 'format' option must be an object.");
322
- }
323
-
324
- var settings = {};
325
-
326
- // Copy all values into a new object.
327
- $(Formatting).each(function(i, val){
328
-
329
- if ( options[val] === undefined ){
330
-
331
- settings[val] = FormatDefaults[i];
332
-
333
- // When we aren't loading defaults, validate the entry.
334
- } else if ( typeMatch(options[val], FormatDefaults[i]) ) {
335
-
336
- // Support for up to 7 decimals.
337
- // More can't be guaranteed due to floating point issues.
338
- if ( val === 'decimals' ){
339
- if ( options[val] < 0 || options[val] > 7 ){
340
- throwError("(Format) 'format.decimals' option must be between 0 and 7.");
341
- }
342
- }
343
-
344
- settings[val] = options[val];
345
-
346
- // If the value isn't valid, emit an error.
347
- } else {
348
- throwError("(Format) 'format."+val+"' must be a " + typeof FormatDefaults[i] + ".");
349
- }
350
- });
351
-
352
- // Some values can't be extracted from a
353
- // string if certain combinations are present.
354
- throwEqualError(settings, 'mark', 'thousand');
355
- throwEqualError(settings, 'prefix', 'negative');
356
- throwEqualError(settings, 'prefix', 'negativeBefore');
357
-
358
- this.settings = settings;
359
- }
360
-
361
- // Shorthand for internal value get
362
- Format.prototype.v = function ( a ) {
363
- return this.settings[a];
364
- };
365
-
366
- Format.prototype.to = function ( number ) {
367
-
368
- function reverse ( a ) {
369
- return a.split('').reverse().join('');
370
- }
371
-
372
- number = this.v('encoder')( number );
373
-
374
- var negative = '', preNegative = '', base = '', mark = '';
375
-
376
- if ( number < 0 ) {
377
- negative = this.v('negative');
378
- preNegative = this.v('negativeBefore');
379
- }
231
+ // Polyfill the pageXOffset and pageYOffset
232
+ // variables for IE7 and IE8;
233
+ if( !pointer && window.pageXOffset === undefined ){
234
+ window.pageXOffset = document.documentElement.scrollLeft;
235
+ window.pageYOffset = document.documentElement.scrollTop;
236
+ }
380
237
 
381
- // Round to proper decimal count
382
- number = Math.abs(number).toFixed( this.v('decimals') ).toString();
383
- number = number.split('.');
384
-
385
- // Rounding away decimals might cause a value of -0
386
- // when using very small ranges. Remove those cases.
387
- if ( parseFloat(number) === 0 ) {
388
- number[0] = '0';
389
- }
390
-
391
- // Group numbers in sets of three.
392
- if ( this.v('thousand') ) {
393
- base = reverse(number[0]).match(/.{1,3}/g);
394
- base = reverse(base.join(reverse( this.v('thousand') )));
395
- } else {
396
- base = number[0];
397
- }
238
+ x = e.clientX + window.pageXOffset;
239
+ y = e.clientY + window.pageYOffset;
240
+ }
398
241
 
399
- // Ignore the decimal separator if decimals are set to 0.
400
- if ( this.v('mark') && number.length > 1 ) {
401
- mark = this.v('mark') + number[1];
402
- }
242
+ event.points = [x, y];
243
+ event.cursor = mouse;
403
244
 
404
- // Return the finalized formatted number.
405
- return preNegative +
406
- this.v('prefix') +
407
- negative +
408
- base +
409
- mark +
410
- this.v('postfix');
411
- };
412
-
413
- Format.prototype.from = function ( input ) {
414
-
415
- function esc(s){
416
- return s.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&');
417
- }
418
-
419
- var isNeg;
420
- // The set request might want to ignore this handle.
421
- // Test for 'undefined' too, as a two-handle slider
422
- // can still be set with an integer.
423
- if( input === null || input === undefined ) {
424
- return false;
425
- }
426
-
427
- // Remove formatting and set period for float parsing.
428
- input = input.toString();
429
-
430
- // Replace the preNegative indicator.
431
- isNeg = input.replace(new RegExp('^' + esc( this.v('negativeBefore') )), '');
432
-
433
- // Check if the value changed by removing the negativeBefore symbol.
434
- if( input !== isNeg ) {
435
- input = isNeg;
436
- isNeg = '-';
437
- } else {
438
- isNeg = '';
439
- }
440
-
441
- // If prefix is set and the number is actually prefixed.
442
- input = input.replace(new RegExp('^'+esc( this.v('prefix') )), '');
443
-
444
- // Only replace if a negative sign is set.
445
- if ( this.v['negative'] ) {
446
-
447
- // Reset isNeg to prevent double '-' insertion.
448
- isNeg = '';
449
-
450
- // Reset the negative sign to '-'
451
- input = input.replace(new RegExp('^'+esc( this.v('negative') )), '-');
452
- }
453
-
454
- // Clean the input string
455
- input = input
456
- // If postfix is set and the number is postfixed.
457
- .replace( new RegExp(esc( this.v('postfix') ) + '$'), '')
458
- // Remove the separator every three digits.
459
- .replace( new RegExp(esc( this.v('thousand') ), 'g'), '')
460
- // Set the decimal separator back to period.
461
- .replace( this.v('mark'), '.');
462
-
463
- // Run the user defined decoder. Returns input by default.
464
- input = this.v('decoder')( parseFloat( isNeg + input ) );
465
-
466
- // Ignore invalid input
467
- if (isNaN( input )) {
468
- return false;
469
- }
470
-
471
- return input;
472
- };
473
-
474
-
475
- // Serialization target
476
-
477
- /** @constructor */
478
- function Link( entry, update ){
479
-
480
- // Make sure Link isn't called as a function, in which case
481
- // the 'this' scope would be the window.
482
- if ( !(this instanceof Link) ) {
483
- throw new Error( "Link: " +
484
- "Don't use Link as a function. " +
485
- "Use the 'new' keyword.");
486
- }
487
-
488
- if ( !entry ) {
489
- throw new RangeError("Link: missing parameters.");
490
- }
491
-
492
- // Write all formatting to this object.
493
- // No validation needed, as we'll merge these with the parent
494
- // format options first.
495
- this.formatting = entry['format'] || {};
496
-
497
- // Store the update option.
498
- this.update = !update;
499
-
500
- // In IE < 9, .bind() isn't available, need this link in .change().
501
- var that = this,
245
+ return event;
246
+ }
502
247
 
503
- // Get values from the input.
504
- target = entry['target'] || function(){},
505
- method = entry['method'],
506
248
 
507
- // Find the type of this link.
508
- isTooltip = ( typeof target === 'string' && target.indexOf('-tooltip-') === 0 ),
509
- isHidden = ( typeof target === 'string' && target.indexOf('-') !== 0 ),
510
- isMethod = ( typeof target === 'function' ),
511
- is$ = ( isInstance(target) ),
512
- isInput = ( is$ && target.is('input, select, textarea') ),
513
- methodIsFunction = ( is$ && typeof method === 'function' ),
514
- methodIsName = ( is$ && typeof method === 'string' && target[method] );
515
-
516
- // If target is a string, a new hidden input will be created.
517
- if ( isTooltip ) {
518
-
519
- // By default, use the 'html' method.
520
- this.method = method || 'html';
521
-
522
- // Use jQuery to create the element
523
- this.el = $( target.replace('-tooltip-', '') || '<div/>' )[0];
524
-
525
- return;
526
- }
527
-
528
- // If the string doesn't begin with '-', which is reserved, add a new hidden input.
529
- if ( isHidden ) {
530
-
531
- this.method = 'val';
249
+ // Input validation
532
250
 
533
- this.el = document.createElement('input');
534
- this.el.name = target;
535
- this.el.type = 'hidden';
251
+ function testStep ( parsed, entry ) {
252
+
253
+ if ( !isNumeric( entry ) ) {
254
+ throw new Error("noUiSlider: 'step' is not numeric.");
255
+ }
256
+
257
+ // The step option can still be used to set stepping
258
+ // for linear sliders. Overwritten if set in 'range'.
259
+ parsed.xSteps[0] = entry;
260
+ }
261
+
262
+ function testRange ( parsed, entry ) {
263
+
264
+ // Filter incorrect input.
265
+ if ( typeof entry !== 'object' || $.isArray(entry) ) {
266
+ throw new Error("noUiSlider: 'range' is not an object.");
267
+ }
268
+
269
+ // Loop all entries.
270
+ $.each( entry, function ( index, value ) {
271
+
272
+ var percentage;
273
+
274
+ // Wrap numerical input in an array.
275
+ if ( typeof value === "number" ) {
276
+ value = [value];
277
+ }
278
+
279
+ // Reject any invalid input.
280
+ if ( !$.isArray( value ) ){
281
+ throw new Error("noUiSlider: 'range' contains invalid value.");
282
+ }
283
+
284
+ // Covert min/max syntax to 0 and 100.
285
+ if ( index === 'min' ) {
286
+ percentage = 0;
287
+ } else if ( index === 'max' ) {
288
+ percentage = 100;
289
+ } else {
290
+ percentage = parseFloat( index );
291
+ }
292
+
293
+ // Check for correct input.
294
+ if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
295
+ throw new Error("noUiSlider: 'range' value isn't numeric.");
296
+ }
297
+
298
+ // Store values.
299
+ parsed.xPct.push( percentage );
300
+ parsed.xVal.push( value[0] );
301
+
302
+ // NaN will evaluate to false too, but to keep
303
+ // logging clear, set step explicitly. Make sure
304
+ // not to override the 'step' setting with false.
305
+ if ( !percentage ) {
306
+ if ( !isNaN( value[1] ) ) {
307
+ parsed.xSteps[0] = value[1];
308
+ }
309
+ } else {
310
+ parsed.xSteps.push( isNaN(value[1]) ? false : value[1] );
311
+ }
312
+ });
313
+
314
+ $.each(parsed.xSteps, function(i,n){
315
+
316
+ // Ignore 'false' stepping.
317
+ if ( !n ) {
318
+ return true;
319
+ }
320
+
321
+ // Check if step fits. Not required, but this might serve some goal.
322
+ // !((parsed.xVal[i+1] - parsed.xVal[i]) % n);
323
+
324
+ // Factor to range ratio
325
+ parsed.xSteps[i] = fromPercentage([
326
+ parsed.xVal[i]
327
+ ,parsed.xVal[i+1]
328
+ ], n) / subRangeRatio (
329
+ parsed.xPct[i],
330
+ parsed.xPct[i+1] );
331
+ });
332
+ }
333
+
334
+ function testStart ( parsed, entry ) {
335
+
336
+ if ( typeof entry === "number" ) {
337
+ entry = [entry];
338
+ }
339
+
340
+ // Validate input. Values aren't tested, the internal Link will do
341
+ // that and provide a valid location.
342
+ if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) {
343
+ throw new Error("noUiSlider: 'start' option is incorrect.");
344
+ }
345
+
346
+ // Store the number of handles.
347
+ parsed.handles = entry.length;
348
+
349
+ // When the slider is initialized, the .val method will
350
+ // be called with the start options.
351
+ parsed.start = entry;
352
+ }
353
+
354
+ function testSnap ( parsed, entry ) {
355
+
356
+ // Enforce 100% stepping within subranges.
357
+ parsed.snap = entry;
358
+
359
+ if ( typeof entry !== 'boolean' ){
360
+ throw new Error("noUiSlider: 'snap' option must be a boolean.");
361
+ }
362
+ }
363
+
364
+ function testConnect ( parsed, entry ) {
365
+
366
+ if ( entry === 'lower' && parsed.handles === 1 ) {
367
+ parsed.connect = 1;
368
+ } else if ( entry === 'upper' && parsed.handles === 1 ) {
369
+ parsed.connect = 2;
370
+ } else if ( entry === true && parsed.handles === 2 ) {
371
+ parsed.connect = 3;
372
+ } else if ( entry === false ) {
373
+ parsed.connect = 0;
374
+ } else {
375
+ throw new Error("noUiSlider: 'connect' option doesn't match handle count.");
376
+ }
377
+ }
378
+
379
+ function testOrientation ( parsed, entry ) {
380
+
381
+ // Set orientation to an a numerical value for easy
382
+ // array selection.
383
+ switch ( entry ){
384
+ case 'horizontal':
385
+ parsed.ort = 0;
386
+ break;
387
+ case 'vertical':
388
+ parsed.ort = 1;
389
+ break;
390
+ default:
391
+ throw new Error("noUiSlider: 'orientation' option is invalid.");
392
+ }
393
+ }
394
+
395
+ function testMargin ( parsed, entry ) {
396
+
397
+ if ( parsed.xPct.length > 2 ) {
398
+ throw new Error("noUiSlider: 'margin' option is only supported on linear sliders.");
399
+ }
400
+
401
+ // Parse value to range and store. As xVal is checked
402
+ // to be no bigger than 2, use it as range.
403
+ parsed.margin = fromPercentage(parsed.xVal, entry);
404
+
405
+ if ( !isNumeric(entry) ){
406
+ throw new Error("noUiSlider: 'margin' option must be numeric.");
407
+ }
408
+ }
409
+
410
+ function testDirection ( parsed, entry ) {
411
+
412
+ // Set direction as a numerical value for easy parsing.
413
+ // Invert connection for RTL sliders, so that the proper
414
+ // handles get the connect/background classes.
415
+ switch ( entry ) {
416
+ case 'ltr':
417
+ parsed.dir = 0;
418
+ break;
419
+ case 'rtl':
420
+ parsed.dir = 1;
421
+ parsed.connect = [0,2,1,3][parsed.connect];
422
+ break;
423
+ default:
424
+ throw new Error("noUiSlider: 'direction' option was not recognized.");
425
+ }
426
+ }
427
+
428
+ function testBehaviour ( parsed, entry ) {
429
+
430
+ // Make sure the input is a string.
431
+ if ( typeof entry !== 'string' ) {
432
+ throw new Error("noUiSlider: 'behaviour' must be a string containing options.");
433
+ }
434
+
435
+ // Check if the string contains any keywords.
436
+ // None are required.
437
+ var tap = entry.indexOf('tap') >= 0,
438
+ extend = entry.indexOf('extend') >= 0,
439
+ drag = entry.indexOf('drag') >= 0,
440
+ fixed = entry.indexOf('fixed') >= 0,
441
+ snap = entry.indexOf('snap') >= 0;
442
+
443
+ parsed.events = {
444
+ tap: tap || snap,
445
+ extend: extend,
446
+ drag: drag,
447
+ fixed: fixed,
448
+ snap: snap
449
+ };
450
+ }
451
+
452
+ function testSerialization ( parsed, entry, sliders ) {
453
+
454
+ parsed.ser = [ entry['lower'], entry['upper'] ];
455
+ parsed.formatting = entry['format'];
456
+
457
+ $.each( parsed.ser, function( index, linkInstances ){
458
+
459
+ // Check if the provided option is an array.
460
+ if ( !$.isArray(linkInstances) ) {
461
+ throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' must be an array.");
462
+ }
463
+
464
+ $.each(linkInstances, function(){
465
+
466
+ // Check if entry is a Link.
467
+ if ( !(this instanceof $.Link) ) {
468
+ throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' can only contain Link instances.");
469
+ }
470
+
471
+ // Assign properties.
472
+ this.setIndex ( index );
473
+ this.setObject( sliders );
474
+ this.setFormatting( entry['format'] );
475
+ });
476
+ });
477
+
478
+ // If the slider has two handles and is RTL,
479
+ // reverse the serialization input. For one handle,
480
+ // lower is still lower.
481
+ if ( parsed.dir && parsed.handles > 1 ) {
482
+ parsed.ser.reverse();
483
+ }
484
+ }
485
+
486
+ // Test all developer settings and parse to assumption-safe values.
487
+ function test ( options, sliders ){
488
+
489
+ /* Every input option is tested and parsed. This'll prevent
490
+ endless validation in internal methods. These tests are
491
+ structured with an item for every option available. An
492
+ option can be marked as required by setting the 'r' flag.
493
+ The testing function is provided with three arguments:
494
+ - The provided value for the option;
495
+ - A reference to the options object;
496
+ - The name for the option;
497
+
498
+ The testing function returns false when an error is detected,
499
+ or true when everything is OK. It can also modify the option
500
+ object, to make sure all values can be correctly looped elsewhere. */
501
+
502
+ var parsed = {
503
+ xPct: []
504
+ ,xVal: []
505
+ ,xSteps: [ false ]
506
+ ,margin: 0
507
+ }, tests;
508
+
509
+ tests = {
510
+ 'step': { r: false, t: testStep },
511
+ 'range': { r: true, t: testRange },
512
+ 'start': { r: true, t: testStart },
513
+ 'snap': { r: false, t: testSnap },
514
+ 'connect': { r: true, t: testConnect },
515
+ 'orientation': { r: false, t: testOrientation },
516
+ 'margin': { r: false, t: testMargin },
517
+ 'direction': { r: true, t: testDirection },
518
+ 'behaviour': { r: true, t: testBehaviour },
519
+ 'serialization': { r: true, t: testSerialization }
520
+ };
521
+
522
+ // Set defaults where applicable.
523
+ options = $.extend({
524
+ 'connect': false,
525
+ 'direction': 'ltr',
526
+ 'behaviour': 'tap',
527
+ 'orientation': 'horizontal'
528
+ }, options);
529
+
530
+ // Make sure the test for serialization runs.
531
+ options['serialization'] = $.extend({
532
+ 'lower': []
533
+ ,'upper': []
534
+ ,'format': {}
535
+ }, options['serialization']);
536
+
537
+ // Run all options through a testing mechanism to ensure correct
538
+ // input. It should be noted that options might get modified to
539
+ // be handled properly. E.g. wrapping integers in arrays.
540
+ $.each( tests, function( name, test ){
541
+
542
+ if ( options[name] === undefined ) {
543
+
544
+ if ( test.r ) {
545
+ throw new Error("noUiSlider: '" + name + "' is required.");
546
+ }
547
+
548
+ return true;
549
+ }
550
+
551
+ test.t( parsed, options[name], sliders );
552
+ });
553
+
554
+ // Pre-define the styles.
555
+ parsed.style = parsed.ort ? 'top' : 'left';
556
+
557
+ return parsed;
558
+ }
536
559
 
537
- return;
538
- }
539
560
 
540
- // The target can also be a function, which will be called.
541
- if ( isMethod ) {
542
- this.target = false;
543
- this.method = target;
544
- return;
545
- }
561
+ // DOM additions
546
562
 
547
- // If the target is and $ element.
548
- if ( is$ ) {
563
+ // Append a handle to the base.
564
+ function addHandle ( options, index ) {
565
+
566
+ var handle = $('<div><div/></div>').addClass( Classes[2] ),
567
+ additions = [ '-lower', '-upper' ];
549
568
 
550
- // The method must exist on the element.
551
- if ( method && ( methodIsFunction || methodIsName ) ) {
552
- this.target = target;
553
- this.method = method;
554
- return;
555
- }
569
+ if ( options.dir ) {
570
+ additions.reverse();
571
+ }
556
572
 
557
- // If a jQuery/Zepto input element is provided, but no method is set,
558
- // the element can assume it needs to respond to 'change'...
559
- if ( !method && isInput ) {
573
+ handle.children().addClass(
574
+ Classes[3] + " " + Classes[3]+additions[index]
575
+ );
560
576
 
561
- // Default to .val if this is an input element.
562
- this.method = 'val';
563
- this.target = target;
577
+ return handle;
578
+ }
564
579
 
565
- // Set the slider to a new value on change.
566
- this.target.on('change', function( e ){
580
+ // Create a copy of an element-creating Link.
581
+ function addElement ( handle, link ) {
582
+
583
+ // If the Link requires creation of a new element,
584
+ // create this element and return a new Link instance.
585
+ if ( link.el ) {
567
586
 
568
- // Returns null array.
569
- function at(a,b,c){
570
- return [c?a:b, c?b:a];
571
- }
587
+ link = new $.Link({
588
+ 'target': $(link.el).clone().appendTo( handle ),
589
+ 'method': link.method,
590
+ 'format': link.formatting
591
+ }, true);
592
+ }
572
593
 
573
- var output = at(null, $(e.target).val(), that.N);
594
+ // Otherwise, return the reference.
595
+ return link;
596
+ }
574
597
 
575
- that.obj.val(output, { 'link': that });
576
- });
598
+ // Loop all links for a handle.
599
+ function addElements ( elements, handle, formatting ) {
577
600
 
578
- return;
579
- }
601
+ var index, list = [], standard = new $.Link({}, true);
580
602
 
581
- // ... or not.
582
- if ( !method && !isInput ) {
603
+ // Use the Link interface to provide unified
604
+ // formatting for the .val() method.
605
+ standard.setFormatting(formatting);
583
606
 
584
- // Default arbitrarily to 'html'.
585
- this.method = 'html';
586
- this.target = target;
607
+ // The list now contains at least one element.
608
+ list.push( standard );
587
609
 
588
- return;
589
- }
590
- }
610
+ // Loop all links in either 'lower' or 'upper'.
611
+ for ( index = 0; index < elements.length; index++ ) {
612
+ list.push(addElement(handle, elements[index]));
613
+ }
591
614
 
592
- throw new RangeError("Link: Invalid Link.");
593
- }
615
+ return list;
616
+ }
594
617
 
595
- // Provides external items with the slider value.
596
- Link.prototype.write = function ( options, value, handle, slider, update ) {
618
+ // Go over all Links and assign them to a handle.
619
+ function addLinks ( options, handles ) {
597
620
 
598
- // Don't synchronize this Link.
599
- if ( this.update && update === false ) {
600
- return;
601
- }
621
+ var index, links = [];
602
622
 
603
- // Convert the value to the slider stepping/range.
604
- value = fromStepping( options, value );
623
+ // Copy the links into a new array, instead of modifying
624
+ // the 'options.ser' list. This allows replacement of the invalid
625
+ // '.el' Links, while the others are still passed by reference.
626
+ for ( index = 0; index < options.handles; index++ ) {
605
627
 
606
- // Format values for display.
607
- value = this.format( value );
628
+ // Append a new array.
629
+ links[index] = addElements(
630
+ options.ser[index],
631
+ handles[index].children(),
632
+ options.formatting
633
+ );
634
+ }
608
635
 
609
- // Store the numerical value.
610
- this.saved = value;
636
+ return links;
637
+ }
611
638
 
612
- // Branch between serialization to a function or an object.
613
- if ( typeof this.method === 'function' ) {
614
- // When target is undefined, the target was a function.
615
- // In that case, provided the slider as the calling scope.
616
- // Use [0] to get the DOM element, not the $ instance.
617
- this.method.call( this.target[0] || slider[0], value, handle, slider );
618
- } else {
619
- this.target[ this.method ]( value, handle, slider );
620
- }
621
- };
639
+ // Add the proper connection classes.
640
+ function addConnection ( connect, target, handles ) {
622
641
 
623
- // Parses slider value to user defined display.
624
- Link.prototype.format = function ( a ) {
625
- return this.formatting.to(a);
626
- };
642
+ // Apply the required connection classes to the elements
643
+ // that need them. Some classes are made up for several
644
+ // segments listed in the class list, to allow easy
645
+ // renaming and provide a minor compression benefit.
646
+ switch ( connect ) {
647
+ case 1: target.addClass( Classes[7] );
648
+ handles[0].addClass( Classes[6] );
649
+ break;
650
+ case 3: handles[1].addClass( Classes[6] );
651
+ /* falls through */
652
+ case 2: handles[0].addClass( Classes[7] );
653
+ /* falls through */
654
+ case 0: target.addClass(Classes[6]);
655
+ break;
656
+ }
657
+ }
627
658
 
628
- // Converts a formatted value back to a real number.
629
- Link.prototype.valueOf = function ( a ) {
630
- return this.formatting.from(a);
631
- };
659
+ // Add handles and loop Link elements.
660
+ function addHandles ( options, base ) {
632
661
 
662
+ var index, handles = [];
633
663
 
634
- // Input validation
664
+ // Append handles.
665
+ for ( index = 0; index < options.handles; index++ ) {
635
666
 
636
- function testStep ( parsed, entry ) {
637
-
638
- if ( !isNumeric( entry ) ) {
639
- throwError("'step' is not numeric.");
640
- }
641
-
642
- // The step option can still be used to set stepping
643
- // for linear sliders. Overwritten if set in 'range'.
644
- parsed.xSteps[0] = entry;
645
- }
646
-
647
- function testRange ( parsed, entry ) {
648
-
649
- // Filter incorrect input.
650
- if ( typeof entry !== 'object' || $.isArray(entry) ) {
651
- throwError("'range' is not an object.");
652
- }
653
-
654
- // Loop all entries.
655
- $.each( entry, function ( index, value ) {
656
-
657
- var percentage;
658
-
659
- // Wrap numerical input in an array.
660
- if ( typeof value === "number" ) {
661
- value = [value];
662
- }
663
-
664
- // Reject any invalid input.
665
- if ( !$.isArray( value ) ){
666
- throwError("'range' contains invalid value.");
667
- }
668
-
669
- // Covert min/max syntax to 0 and 100.
670
- if ( index === 'min' ) {
671
- percentage = 0;
672
- } else if ( index === 'max' ) {
673
- percentage = 100;
674
- } else {
675
- percentage = parseFloat( index );
676
- }
677
-
678
- // Check for correct input.
679
- if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) {
680
- throwError("'range' value isn't numeric.");
681
- }
682
-
683
- // Store values.
684
- parsed.xPct.push( percentage );
685
- parsed.xVal.push( value[0] );
686
-
687
- // NaN will evaluate to false too, but to keep
688
- // logging clear, set step explicitly. Make sure
689
- // not to override the 'step' setting with false.
690
- if ( !percentage ) {
691
- if ( !isNaN( value[1] ) ) {
692
- parsed.xSteps[0] = value[1];
693
- }
694
- } else {
695
- parsed.xSteps.push( isNaN(value[1]) ? false : value[1] );
696
- }
697
- });
698
-
699
- $.each(parsed.xSteps, function(i,n){
700
-
701
- // Ignore 'false' stepping.
702
- if ( !n ) {
703
- return true;
704
- }
705
-
706
- // Check if step fits. Not required, but this might serve some goal.
707
- // !((parsed.xVal[i+1] - parsed.xVal[i]) % n);
708
-
709
- // Factor to range ratio
710
- parsed.xSteps[i] = fromPercentage([
711
- parsed.xVal[i]
712
- ,parsed.xVal[i+1]
713
- ], n) / subRangeRatio (
714
- parsed.xPct[i],
715
- parsed.xPct[i+1] );
716
- });
717
- }
718
-
719
- function testStart ( parsed, entry ) {
720
-
721
- if ( typeof entry === "number" ) {
722
- entry = [entry];
723
- }
724
-
725
- // Validate input. Values aren't tested, the internal Link will do
726
- // that and provide a valid location.
727
- if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) {
728
- throwError("'start' option is incorrect.");
729
- }
730
-
731
- // Store the number of handles.
732
- parsed.handles = entry.length;
733
-
734
- // When the slider is initialized, the .val method will
735
- // be called with the start options.
736
- parsed.start = entry;
737
- }
738
-
739
- function testSnap ( parsed, entry ) {
740
-
741
- // Enforce 100% stepping within subranges.
742
- parsed.snap = entry;
743
-
744
- if ( typeof entry !== 'boolean' ){
745
- throwError("'snap' option must be a boolean.");
746
- }
747
- }
748
-
749
- function testConnect ( parsed, entry ) {
750
-
751
- if ( entry === 'lower' && parsed.handles === 1 ) {
752
- parsed.connect = 1;
753
- } else if ( entry === 'upper' && parsed.handles === 1 ) {
754
- parsed.connect = 2;
755
- } else if ( entry === true && parsed.handles === 2 ) {
756
- parsed.connect = 3;
757
- } else if ( entry === false ) {
758
- parsed.connect = 0;
759
- } else {
760
- throwError("'connect' option was doesn't match handle count.");
761
- }
762
- }
763
-
764
- function testOrientation ( parsed, entry ) {
765
-
766
- // Set orientation to an a numerical value for easy
767
- // array selection.
768
- switch ( entry ){
769
- case 'horizontal':
770
- parsed.ort = 0;
771
- break;
772
- case 'vertical':
773
- parsed.ort = 1;
774
- break;
775
- default:
776
- throwError("'orientation' option is invalid.");
777
- }
778
- }
779
-
780
- function testMargin ( parsed, entry ) {
781
-
782
- if ( parsed.xPct.length > 2 ) {
783
- throwError("'margin' option is only supported on linear sliders.");
784
- }
785
-
786
- // Parse value to range and store. As xVal is checked
787
- // to be no bigger than 2, use it as range.
788
- parsed.margin = fromPercentage(parsed.xVal, entry);
789
-
790
- if ( !isNumeric(entry) ){
791
- throwError("'margin' option must be numeric.");
792
- }
793
- }
794
-
795
- function testDirection ( parsed, entry ) {
796
-
797
- // Set direction as a numerical value for easy parsing.
798
- // Invert connection for RTL sliders, so that the proper
799
- // handles get the connect/background classes.
800
- switch ( entry ) {
801
- case 'ltr':
802
- parsed.dir = 0;
803
- break;
804
- case 'rtl':
805
- parsed.dir = 1;
806
- parsed.connect = [0,2,1,3][parsed.connect];
807
- break;
808
- default:
809
- throwError("'direction' option was not recognized.");
810
- }
811
- }
812
-
813
- function testBehaviour ( parsed, entry ) {
814
-
815
- // Make sure the input is a string.
816
- if ( typeof entry !== 'string' ) {
817
- throwError("'behaviour' must be a string containing options.");
818
- }
819
-
820
- // Check if the string contains any keywords.
821
- // None are required.
822
- var tap = entry.indexOf('tap') >= 0,
823
- extend = entry.indexOf('extend') >= 0,
824
- drag = entry.indexOf('drag') >= 0,
825
- fixed = entry.indexOf('fixed') >= 0,
826
- snap = entry.indexOf('snap') >= 0;
827
-
828
- parsed.events = {
829
- tap: tap || snap,
830
- extend: extend,
831
- drag: drag,
832
- fixed: fixed,
833
- snap: snap
834
- };
835
- }
836
-
837
- function testSerialization ( parsed, entry, sliders ) {
838
-
839
- parsed.ser = [ entry['lower'], entry['upper'] ];
840
- parsed.formatting = new Format( entry['format'] );
841
-
842
- $.each( parsed.ser, function( i, a ){
843
-
844
- // Check if the provided option is an array.
845
- if ( !$.isArray(a) ) {
846
- throwError("'serialization."+(!i?'lower':'upper')+"' must be an array.");
847
- }
848
-
849
- $.each(a, function(){
850
-
851
- // Check if entry is a Link.
852
- if ( !(this instanceof Link) ) {
853
- throwError("'serialization."+(!i?'lower':'upper')+"' can only contain Link instances.");
854
- }
855
-
856
- // Assign other properties.
857
- this.N = i;
858
- this.obj = sliders;
859
- this.scope = this.scope || sliders;
860
-
861
- // Run internal validator.
862
- this.formatting = new Format($.extend({}
863
- ,entry['format']
864
- ,this.formatting
865
- ));
866
- });
867
- });
868
-
869
- // If the slider has two handles and is RTL,
870
- // reverse the serialization input. For one handle,
871
- // lower is still lower.
872
- if ( parsed.dir && parsed.handles > 1 ) {
873
- parsed.ser.reverse();
874
- }
875
- }
876
-
877
- // Test all developer settings and parse to assumption-safe values.
878
- function test ( options, sliders ){
879
-
880
- /* Every input option is tested and parsed. This'll prevent
881
- endless validation in internal methods. These tests are
882
- structured with an item for every option available. An
883
- option can be marked as required by setting the 'r' flag.
884
- The testing function is provided with three arguments:
885
- - The provided value for the option;
886
- - A reference to the options object;
887
- - The name for the option;
888
-
889
- The testing function returns false when an error is detected,
890
- or true when everything is OK. It can also modify the option
891
- object, to make sure all values can be correctly looped elsewhere. */
892
-
893
- var parsed = {
894
- xPct: []
895
- ,xVal: []
896
- ,xSteps: [ false ]
897
- ,margin: 0
898
- }, tests;
899
-
900
- tests = {
901
- 'step': { r: false, t: testStep },
902
- 'range': { r: true, t: testRange },
903
- 'start': { r: true, t: testStart },
904
- 'snap': { r: false, t: testSnap },
905
- 'connect': { r: true, t: testConnect },
906
- 'orientation': { r: false, t: testOrientation },
907
- 'margin': { r: false, t: testMargin },
908
- 'direction': { r: true, t: testDirection },
909
- 'behaviour': { r: true, t: testBehaviour },
910
- 'serialization': { r: true, t: testSerialization }
911
- };
912
-
913
- // Set defaults where applicable.
914
- options = $.extend({
915
- 'connect': false
916
- ,'direction': 'ltr'
917
- ,'behaviour': 'tap'
918
- ,'orientation': 'horizontal'
919
- }, options);
920
-
921
- // Make sure the test for serialization runs.
922
- options['serialization'] = $.extend({
923
- 'lower': []
924
- ,'upper': []
925
- ,'format': {}
926
- }, options['serialization']);
927
-
928
- // Run all options through a testing mechanism to ensure correct
929
- // input. It should be noted that options might get modified to
930
- // be handled properly. E.g. wrapping integers in arrays.
931
- $.each( tests, function( name, test ){
932
-
933
- if ( options[name] === undefined ) {
934
- if ( test.r ) {
935
- throwError("'" + name + "' is required.");
936
- } else {
937
- return true;
938
- }
939
- }
940
-
941
- test.t( parsed, options[name], sliders );
942
- });
943
-
944
- // Pre-define the styles.
945
- parsed.style = parsed.ort ? 'top' : 'left';
946
-
947
- return parsed;
948
- }
667
+ // Keep a list of all added handles.
668
+ handles.push( addHandle( options, index ).appendTo(base) );
669
+ }
949
670
 
671
+ return handles;
672
+ }
950
673
 
951
- // DOM additions
674
+ // Initialize a single slider.
675
+ function addSlider ( options, target ) {
952
676
 
953
- // Append a handle to the base.
954
- function addHandle ( options, index ) {
955
-
956
- var handle = $('<div><div/></div>').addClass( Classes[2] ),
957
- additions = [ '-lower', '-upper' ];
958
-
959
- if ( options.dir ) {
960
- additions.reverse();
961
- }
962
-
963
- handle.children().addClass(
964
- Classes[3] + " " + Classes[3]+additions[index]
965
- );
966
-
967
- return handle;
968
- }
969
-
970
- // Create a copy of an element-creating Link.
971
- function addElement ( handle, link ) {
972
-
973
- // If the Link requires creation of a new element,
974
- // create this element and return a new Link instance.
975
- if ( link.el ) {
976
- link = new Link({
977
- 'target': $(link.el).clone().appendTo( handle ),
978
- 'method': link.method,
979
- 'format': link.formatting
980
- }, true);
981
- }
982
-
983
- // Otherwise, return the reference.
984
- return link;
985
- }
986
-
987
- // Loop all links for a handle.
988
- function addElements ( elements, handle, formatting ) {
989
-
990
- var index, list = [];
991
-
992
- // Use the Link interface to provide unified
993
- // formatting for the .val() method.
994
- list.push(
995
- new Link({
996
- 'format': formatting
997
- }, true)
998
- );
999
-
1000
- // Loop all links in either 'lower' or 'upper'.
1001
- for ( index = 0; index < elements.length; index++ ) {
1002
- list.push(addElement(handle, elements[index]));
1003
- }
1004
-
1005
- return list;
1006
- }
1007
-
1008
- // Go over all Links and assign them to a handle.
1009
- function addLinks ( options, handles ) {
1010
-
1011
- var index, links = [];
1012
-
1013
- // Copy the links into a new array, instead of modifying
1014
- // the 'options.ser' list. This allows replacement of the invalid
1015
- // '.el' Links, while the others are still passed by reference.
1016
- for ( index = 0; index < options.handles; index++ ) {
1017
-
1018
- // Append a new array.
1019
- links[index] = addElements(
1020
- options.ser[index],
1021
- handles[index].children(),
1022
- options.formatting
1023
- );
1024
- }
1025
-
1026
- return links;
1027
- }
1028
-
1029
- // Add the proper connection classes.
1030
- function addConnection ( connect, target, handles ) {
1031
-
1032
- // Apply the required connection classes to the elements
1033
- // that need them. Some classes are made up for several
1034
- // segments listed in the class list, to allow easy
1035
- // renaming and provide a minor compression benefit.
1036
- switch ( connect ) {
1037
- case 1: target.addClass( Classes[7] );
1038
- handles[0].addClass( Classes[6] );
1039
- break;
1040
- case 3: handles[1].addClass( Classes[6] );
1041
- /* falls through */
1042
- case 2: handles[0].addClass( Classes[7] );
1043
- /* falls through */
1044
- case 0: target.addClass(Classes[6]);
1045
- break;
1046
- }
1047
- }
1048
-
1049
- // Add handles and loop Link elements.
1050
- function addHandles ( options, base ) {
1051
-
1052
- var index, handles = [];
1053
-
1054
- // Append handles.
1055
- for ( index = 0; index < options.handles; index++ ) {
1056
-
1057
- // Keep a list of all added handles.
1058
- handles.push( addHandle( options, index ).appendTo(base) );
1059
- }
1060
-
1061
- return handles;
1062
- }
1063
-
1064
- // Initialize a single slider.
1065
- function addSlider ( options, target ) {
1066
-
1067
- // Apply classes and data to the target.
1068
- target.addClass([
1069
- Classes[0],
1070
- Classes[8 + options.dir],
1071
- Classes[4 + options.ort]
1072
- ].join(' '));
1073
-
1074
- return $('<div/>').appendTo(target).addClass( Classes[1] );
1075
- }
677
+ // Apply classes and data to the target.
678
+ target.addClass([
679
+ Classes[0],
680
+ Classes[8 + options.dir],
681
+ Classes[4 + options.ort]
682
+ ].join(' '));
683
+
684
+ return $('<div/>').appendTo(target).addClass( Classes[1] );
685
+ }
1076
686
 
1077
687
 
1078
688
  // Slider scope
1079
689
 
1080
- function closure ( target, options, originalOptions ){
690
+ function closure ( target, options, originalOptions ){
1081
691
 
1082
692
  // Internal variables
1083
693
 
1084
- // All variables local to 'closure' are marked $.
1085
- var $Target = $(target),
1086
- $Locations = [-1, -1],
1087
- $Base,
1088
- $Serialization,
1089
- $Handles;
694
+ // All variables local to 'closure' are marked $.
695
+ var $Target = $(target),
696
+ $Locations = [-1, -1],
697
+ $Base,
698
+ $Serialization,
699
+ $Handles;
1090
700
 
1091
- // Shorthand for base dimensions.
1092
- function baseSize ( ) {
1093
- return $Base[['width', 'height'][options.ort]]();
1094
- }
701
+ // Shorthand for base dimensions.
702
+ function baseSize ( ) {
703
+ return $Base[['width', 'height'][options.ort]]();
704
+ }
1095
705
 
1096
706
 
1097
707
  // External event handling
1098
708
 
1099
- function fireEvents ( events ) {
709
+ function fireEvents ( events ) {
1100
710
 
1101
- // Use the external api to get the values.
1102
- // Wrap the values in an array, as .trigger takes
1103
- // only one additional argument.
1104
- var index, values = [ $Target.val() ];
711
+ // Use the external api to get the values.
712
+ // Wrap the values in an array, as .trigger takes
713
+ // only one additional argument.
714
+ var index, values = [ $Target.val() ];
1105
715
 
1106
- for ( index = 0; index < events.length; index++ ){
1107
- $Target.trigger(events[index], values);
1108
- }
1109
- }
716
+ for ( index = 0; index < events.length; index++ ){
717
+ $Target.trigger(events[index], values);
718
+ }
719
+ }
1110
720
 
1111
721
 
1112
722
  // Handle placement
1113
723
 
1114
- // Test suggested values and apply margin, step.
1115
- function setHandle ( handle, to, delimit ) {
1116
-
1117
- var n = handle[0] !== $Handles[0][0] ? 1 : 0,
1118
- lower = $Locations[0] + options.margin,
1119
- upper = $Locations[1] - options.margin;
1120
-
1121
- // Don't delimit range dragging.
1122
- if ( delimit && $Handles.length > 1 ) {
1123
- to = n ? Math.max( to, lower ) : Math.min( to, upper );
1124
- }
1125
-
1126
- // Handle the step option.
1127
- if ( to < 100 ){
1128
- to = getStep(options, to);
1129
- }
1130
-
1131
- // Limit to 0/100 for .val input, trim anything beyond 7 digits, as
1132
- // JavaScript has some issues in its floating point implementation.
1133
- to = limit(parseFloat(to.toFixed(7)));
1134
-
1135
- // Return falsy if handle can't move. False for 0 or 100 limit,
1136
- // '0' for limiting by another handle.
1137
- if ( to === $Locations[n] ) {
1138
- if ( $Handles.length === 1 ) {
1139
- return false;
1140
- }
1141
- return ( to === lower || to === upper ) ? 0 : false;
1142
- }
1143
-
1144
- // Set the handle to the new position.
1145
- handle.css( options.style, to + '%' );
1146
-
1147
- // Force proper handle stacking
1148
- if ( handle.is(':first-child') ) {
1149
- handle.toggleClass(Classes[17], to > 50 );
1150
- }
724
+ // Test suggested values and apply margin, step.
725
+ function setHandle ( handle, to, delimit ) {
726
+
727
+ var n = handle[0] !== $Handles[0][0] ? 1 : 0,
728
+ lower = $Locations[0] + options.margin,
729
+ upper = $Locations[1] - options.margin;
730
+
731
+ // Don't delimit range dragging.
732
+ if ( delimit && $Handles.length > 1 ) {
733
+ to = n ? Math.max( to, lower ) : Math.min( to, upper );
734
+ }
735
+
736
+ // Handle the step option.
737
+ if ( to < 100 ){
738
+ to = getStep(options, to);
739
+ }
740
+
741
+ // Limit to 0/100 for .val input, trim anything beyond 7 digits, as
742
+ // JavaScript has some issues in its floating point implementation.
743
+ to = limit(parseFloat(to.toFixed(7)));
744
+
745
+ // Return falsy if handle can't move. False for 0 or 100 limit,
746
+ // '0' for limiting by another handle.
747
+ if ( to === $Locations[n] ) {
748
+ if ( $Handles.length === 1 ) {
749
+ return false;
750
+ }
751
+ return ( to === lower || to === upper ) ? 0 : false;
752
+ }
753
+
754
+ // Set the handle to the new position.
755
+ handle.css( options.style, to + '%' );
756
+
757
+ // Force proper handle stacking
758
+ if ( handle.is(':first-child') ) {
759
+ handle.toggleClass(Classes[17], to > 50 );
760
+ }
761
+
762
+ // Update locations.
763
+ $Locations[n] = to;
764
+
765
+ // Invert the value if this is a right-to-left slider.
766
+ if ( options.dir ) {
767
+ to = 100 - to;
768
+ }
769
+
770
+ // Write values to serialization Links.
771
+ // Convert the value to the correct relative representation.
772
+ // Convert the value to the slider stepping/range.
773
+ $($Serialization[n]).each(function(){
774
+ this.write( fromStepping( options, to ), handle.children(), $Target );
775
+ });
776
+
777
+ return true;
778
+ }
779
+
780
+ // Delimit proposed values for handle positions.
781
+ function getPositions ( a, b, delimit ) {
782
+
783
+ // Add movement to current position.
784
+ var c = a + b[0], d = a + b[1];
785
+
786
+ // Only alter the other position on drag,
787
+ // not on standard sliding.
788
+ if ( delimit ) {
789
+ if ( c < 0 ) {
790
+ d += Math.abs(c);
791
+ }
792
+ if ( d > 100 ) {
793
+ c -= ( d - 100 );
794
+ }
795
+
796
+ // Limit values to 0 and 100.
797
+ return [limit(c), limit(d)];
798
+ }
799
+
800
+ return [c,d];
801
+ }
802
+
803
+ // Handles movement by tapping.
804
+ function jump ( handle, to, instant ) {
805
+
806
+ if ( !instant ) {
807
+ // Flag the slider as it is now in a transitional state.
808
+ // Transition takes 300 ms, so re-enable the slider afterwards.
809
+ addClassFor( $Target, Classes[14], 300 );
810
+ }
811
+
812
+ // Move the handle to the new position.
813
+ setHandle( handle, to, false );
814
+
815
+ fireEvents(['slide', 'set', 'change']);
816
+ }
1151
817
 
1152
- // Update locations.
1153
- $Locations[n] = to;
1154
818
 
1155
- // Invert the value if this is a right-to-left slider.
1156
- if ( options.dir ) {
1157
- to = 100 - to;
1158
- }
819
+ // Events
1159
820
 
1160
- // Write values to serialization Links.
1161
- // Convert the value to the correct relative representation.
1162
- $($Serialization[n]).each(function(){
1163
- this.write( options, to, handle.children(), $Target );
1164
- });
821
+ // Handler for attaching events trough a proxy.
822
+ function attach ( events, element, callback, data ) {
1165
823
 
1166
- return true;
1167
- }
824
+ // Add the noUiSlider namespace to all events.
825
+ events = events.replace( /\s/g, namespace + ' ' ) + namespace;
1168
826
 
1169
- // Delimit proposed values for handle positions.
1170
- function getPositions ( a, b, delimit ) {
827
+ // Bind a closure on the target.
828
+ return element.on( events, function( e ){
1171
829
 
1172
- // Add movement to current position.
1173
- var c = a + b[0], d = a + b[1];
830
+ // jQuery and Zepto handle unset attributes differently.
831
+ var disabled = $Target.attr('disabled');
832
+ disabled = !( disabled === undefined || disabled === null );
1174
833
 
1175
- // Only alter the other position on drag,
1176
- // not on standard sliding.
1177
- if ( delimit ) {
1178
- if ( c < 0 ) {
1179
- d += Math.abs(c);
1180
- }
1181
- if ( d > 100 ) {
1182
- c -= ( d - 100 );
1183
- }
834
+ // Test if there is anything that should prevent an event
835
+ // from being handled, such as a disabled state or an active
836
+ // 'tap' transition.
837
+ if( $Target.hasClass( Classes[14] ) || disabled ) {
838
+ return false;
839
+ }
1184
840
 
1185
- // Limit values to 0 and 100.
1186
- return [limit(c), limit(d)];
1187
- }
841
+ e = fixEvent(e);
842
+ e.calcPoint = e.points[ options.ort ];
1188
843
 
1189
- return [c,d];
1190
- }
844
+ // Call the event handler with the event [ and additional data ].
845
+ callback ( e, data );
846
+ });
847
+ }
1191
848
 
1192
- // Handles movement by tapping.
1193
- function jump ( handle, to, instant ) {
849
+ // Handle movement on document for handle and range drag.
850
+ function move ( event, data ) {
1194
851
 
1195
- if ( !instant ) {
1196
- // Flag the slider as it is now in a transitional state.
1197
- // Transition takes 300 ms, so re-enable the slider afterwards.
1198
- addClassFor( $Target, Classes[14], 300 );
1199
- }
852
+ var handles = data.handles || $Handles, positions, state = false,
853
+ proposal = ((event.calcPoint - data.start) * 100) / baseSize(),
854
+ h = handles[0][0] !== $Handles[0][0] ? 1 : 0;
1200
855
 
1201
- // Move the handle to the new position.
1202
- setHandle( handle, to, false );
856
+ // Calculate relative positions for the handles.
857
+ positions = getPositions( proposal, data.positions, handles.length > 1);
1203
858
 
1204
- fireEvents(['slide', 'set', 'change']);
1205
- }
859
+ state = setHandle ( handles[0], positions[h], handles.length === 1 );
1206
860
 
861
+ if ( handles.length > 1 ) {
862
+ state = setHandle ( handles[1], positions[h?0:1], false ) || state;
863
+ }
1207
864
 
1208
- // Events
865
+ // Fire the 'slide' event if any handle moved.
866
+ if ( state ) {
867
+ fireEvents(['slide']);
868
+ }
869
+ }
1209
870
 
1210
- // Handler for attaching events trough a proxy.
1211
- function attach ( events, element, callback, data ) {
871
+ // Unbind move events on document, call callbacks.
872
+ function end ( event ) {
1212
873
 
1213
- // Add the noUiSlider namespace to all events.
1214
- events = events.replace( /\s/g, namespace + ' ' ) + namespace;
874
+ // The handle is no longer active, so remove the class.
875
+ $('.' + Classes[15]).removeClass(Classes[15]);
1215
876
 
1216
- // Bind a closure on the target.
1217
- return element.on( events, function( e ){
877
+ // Remove cursor styles and text-selection events bound to the body.
878
+ if ( event.cursor ) {
879
+ $('body').css('cursor', '').off( namespace );
880
+ }
1218
881
 
1219
- // jQuery and Zepto handle unset attributes differently.
1220
- var disabled = $Target.attr('disabled');
1221
- disabled = !( disabled === undefined || disabled === null );
882
+ // Unbind the move and end events, which are added on 'start'.
883
+ doc.off( namespace );
1222
884
 
1223
- // Test if there is anything that should prevent an event
1224
- // from being handled, such as a disabled state or an active
1225
- // 'tap' transition.
1226
- if( $Target.hasClass( Classes[14] ) || disabled ) {
1227
- return false;
1228
- }
885
+ // Remove dragging class.
886
+ $Target.removeClass(Classes[12]);
1229
887
 
1230
- e = fixEvent(e);
1231
- e.calcPoint = e.points[ options.ort ];
888
+ // Fire the change and set events.
889
+ fireEvents(['set', 'change']);
890
+ }
1232
891
 
1233
- // Call the event handler with the event [ and additional data ].
1234
- callback ( e, data );
1235
- });
1236
- }
892
+ // Bind move events on document.
893
+ function start ( event, data ) {
1237
894
 
1238
- // Handle movement on document for handle and range drag.
1239
- function move ( event, data ) {
895
+ // Mark the handle as 'active' so it can be styled.
896
+ if( data.handles.length === 1 ) {
897
+ data.handles[0].children().addClass(Classes[15]);
898
+ }
1240
899
 
1241
- var handles = data.handles || $Handles, positions, state = false,
1242
- proposal = ((event.calcPoint - data.start) * 100) / baseSize(),
1243
- h = handles[0][0] !== $Handles[0][0] ? 1 : 0;
900
+ // A drag should never propagate up to the 'tap' event.
901
+ event.stopPropagation();
1244
902
 
1245
- // Calculate relative positions for the handles.
1246
- positions = getPositions( proposal, data.positions, handles.length > 1);
903
+ // Attach the move event.
904
+ attach ( actions.move, doc, move, {
905
+ start: event.calcPoint,
906
+ handles: data.handles,
907
+ positions: [
908
+ $Locations[0],
909
+ $Locations[$Handles.length - 1]
910
+ ]
911
+ });
1247
912
 
1248
- state = setHandle ( handles[0], positions[h], handles.length === 1 );
913
+ // Unbind all movement when the drag ends.
914
+ attach ( actions.end, doc, end, null );
1249
915
 
1250
- if ( handles.length > 1 ) {
1251
- state = setHandle ( handles[1], positions[h?0:1], false ) || state;
1252
- }
916
+ // Text selection isn't an issue on touch devices,
917
+ // so adding cursor styles can be skipped.
918
+ if ( event.cursor ) {
1253
919
 
1254
- // Fire the 'slide' event if any handle moved.
1255
- if ( state ) {
1256
- fireEvents(['slide']);
1257
- }
1258
- }
920
+ // Prevent the 'I' cursor and extend the range-drag cursor.
921
+ $('body').css('cursor', $(event.target).css('cursor'));
1259
922
 
1260
- // Unbind move events on document, call callbacks.
1261
- function end ( event ) {
923
+ // Mark the target with a dragging state.
924
+ if ( $Handles.length > 1 ) {
925
+ $Target.addClass(Classes[12]);
926
+ }
1262
927
 
1263
- // The handle is no longer active, so remove the class.
1264
- $('.' + Classes[15]).removeClass(Classes[15]);
928
+ // Prevent text selection when dragging the handles.
929
+ $('body').on('selectstart' + namespace, false);
930
+ }
931
+ }
1265
932
 
1266
- // Remove cursor styles and text-selection events bound to the body.
1267
- if ( event.cursor ) {
1268
- $('body').css('cursor', '').off( namespace );
1269
- }
933
+ // Move closest handle to tapped location.
934
+ function tap ( event ) {
1270
935
 
1271
- // Unbind the move and end events, which are added on 'start'.
1272
- doc.off( namespace );
936
+ var location = event.calcPoint, total = 0, to;
1273
937
 
1274
- // Remove dragging class.
1275
- $Target.removeClass(Classes[12]);
938
+ // The tap event shouldn't propagate up and cause 'edge' to run.
939
+ event.stopPropagation();
1276
940
 
1277
- // Fire the change and set events.
1278
- fireEvents(['set', 'change']);
1279
- }
941
+ // Add up the handle offsets.
942
+ $.each( $Handles, function(){
943
+ total += this.offset()[ options.style ];
944
+ });
1280
945
 
1281
- // Bind move events on document.
1282
- function start ( event, data ) {
946
+ // Find the handle closest to the tapped position.
947
+ total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1;
1283
948
 
1284
- // Mark the handle as 'active' so it can be styled.
1285
- if( data.handles.length === 1 ) {
1286
- data.handles[0].children().addClass(Classes[15]);
1287
- }
949
+ location -= $Base.offset()[ options.style ];
1288
950
 
1289
- // A drag should never propagate up to the 'tap' event.
1290
- event.stopPropagation();
951
+ // Calculate the new position.
952
+ to = ( location * 100 ) / baseSize();
1291
953
 
1292
- // Attach the move event.
1293
- attach ( actions.move, doc, move, {
1294
- start: event.calcPoint,
1295
- handles: data.handles,
1296
- positions: [
1297
- $Locations[0],
1298
- $Locations[$Handles.length - 1]
1299
- ]
1300
- });
954
+ // Find the closest handle and calculate the tapped point.
955
+ // The set handle to the new position.
956
+ jump( $Handles[total], to, options.events.snap );
1301
957
 
1302
- // Unbind all movement when the drag ends.
1303
- attach ( actions.end, doc, end, null );
958
+ if ( options.events.snap ) {
959
+ start(event, { handles: [$Handles[total]] });
960
+ }
961
+ }
1304
962
 
1305
- // Text selection isn't an issue on touch devices,
1306
- // so adding cursor styles can be skipped.
1307
- if ( event.cursor ) {
963
+ // Move handle to edges when target gets tapped.
964
+ function edge ( event ) {
1308
965
 
1309
- // Prevent the 'I' cursor and extend the range-drag cursor.
1310
- $('body').css('cursor', $(event.target).css('cursor'));
966
+ var i = event.calcPoint < $Base.offset()[ options.style ],
967
+ to = i ? 0 : 100;
1311
968
 
1312
- // Mark the target with a dragging state.
1313
- if ( $Handles.length > 1 ) {
1314
- $Target.addClass(Classes[12]);
1315
- }
969
+ i = i ? 0 : $Handles.length - 1;
1316
970
 
1317
- // Prevent text selection when dragging the handles.
1318
- $('body').on('selectstart' + namespace, false);
1319
- }
1320
- }
971
+ jump( $Handles[i], to, false );
972
+ }
1321
973
 
1322
- // Move closest handle to tapped location.
1323
- function tap ( event ) {
974
+ // Attach events to several slider parts.
975
+ function events ( behaviour ) {
1324
976
 
1325
- var location = event.calcPoint, total = 0, to;
977
+ var i, drag;
1326
978
 
1327
- // The tap event shouldn't propagate up and cause 'edge' to run.
1328
- event.stopPropagation();
979
+ // Attach the standard drag event to the handles.
980
+ if ( !behaviour.fixed ) {
1329
981
 
1330
- // Add up the handle offsets.
1331
- $.each( $Handles, function(){
1332
- total += this.offset()[ options.style ];
1333
- });
982
+ for ( i = 0; i < $Handles.length; i++ ) {
1334
983
 
1335
- // Find the handle closest to the tapped position.
1336
- total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1;
984
+ // These events are only bound to the visual handle
985
+ // element, not the 'real' origin element.
986
+ attach ( actions.start, $Handles[i].children(), start, {
987
+ handles: [ $Handles[i] ]
988
+ });
989
+ }
990
+ }
1337
991
 
1338
- location -= $Base.offset()[ options.style ];
992
+ // Attach the tap event to the slider base.
993
+ if ( behaviour.tap ) {
994
+ attach ( actions.start, $Base, tap, {
995
+ handles: $Handles
996
+ });
997
+ }
1339
998
 
1340
- // Calculate the new position.
1341
- to = ( location * 100 ) / baseSize();
999
+ // Extend tapping behaviour to target
1000
+ if ( behaviour.extend ) {
1342
1001
 
1343
- // Find the closest handle and calculate the tapped point.
1344
- // The set handle to the new position.
1345
- jump( $Handles[total], to, options.events.snap );
1002
+ $Target.addClass( Classes[16] );
1346
1003
 
1347
- if ( options.events.snap ) {
1348
- start(event, { handles: [$Handles[total]] });
1349
- }
1350
- }
1004
+ if ( behaviour.tap ) {
1005
+ attach ( actions.start, $Target, edge, {
1006
+ handles: $Handles
1007
+ });
1008
+ }
1009
+ }
1351
1010
 
1352
- // Move handle to edges when target gets tapped.
1353
- function edge ( event ) {
1011
+ // Make the range dragable.
1012
+ if ( behaviour.drag ){
1354
1013
 
1355
- var i = event.calcPoint < $Base.offset()[ options.style ],
1356
- to = i ? 0 : 100;
1014
+ drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] );
1357
1015
 
1358
- i = i ? 0 : $Handles.length - 1;
1016
+ // When the range is fixed, the entire range can
1017
+ // be dragged by the handles. The handle in the first
1018
+ // origin will propagate the start event upward,
1019
+ // but it needs to be bound manually on the other.
1020
+ if ( behaviour.fixed ) {
1021
+ drag = drag.add($Base.children().not( drag ).children());
1022
+ }
1359
1023
 
1360
- jump( $Handles[i], to, false );
1361
- }
1024
+ attach ( actions.start, drag, start, {
1025
+ handles: $Handles
1026
+ });
1027
+ }
1028
+ }
1362
1029
 
1363
- // Attach events to several slider parts.
1364
- function events ( behaviour ) {
1365
1030
 
1366
- var i, drag;
1031
+ // Initialize slider
1367
1032
 
1368
- // Attach the standard drag event to the handles.
1369
- if ( !behaviour.fixed ) {
1033
+ // Throw an error if the slider was already initialized.
1034
+ if ( $Target.hasClass(Classes[0]) ) {
1035
+ throw new Error('Slider was already initialized.');
1036
+ }
1370
1037
 
1371
- for ( i = 0; i < $Handles.length; i++ ) {
1038
+ // Create the base element, initialise HTML and set classes.
1039
+ // Add handles and links.
1040
+ $Base = addSlider( options, $Target );
1041
+ $Handles = addHandles( options, $Base );
1042
+ $Serialization = addLinks( options, $Handles );
1372
1043
 
1373
- // These events are only bound to the visual handle
1374
- // element, not the 'real' origin element.
1375
- attach ( actions.start, $Handles[i].children(), start, {
1376
- handles: [ $Handles[i] ]
1377
- });
1378
- }
1379
- }
1044
+ // Set the connect classes.
1045
+ addConnection ( options.connect, $Target, $Handles );
1380
1046
 
1381
- // Attach the tap event to the slider base.
1382
- if ( behaviour.tap ) {
1383
- attach ( actions.start, $Base, tap, {
1384
- handles: $Handles
1385
- });
1386
- }
1047
+ // Attach user events.
1048
+ events( options.events );
1387
1049
 
1388
- // Extend tapping behaviour to target
1389
- if ( behaviour.extend ) {
1390
1050
 
1391
- $Target.addClass( Classes[16] );
1051
+ // Methods
1392
1052
 
1393
- if ( behaviour.tap ) {
1394
- attach ( actions.start, $Target, edge, {
1395
- handles: $Handles
1396
- });
1397
- }
1398
- }
1053
+ // Set the slider value.
1054
+ /** @expose */
1055
+ target.vSet = function ( ) {
1056
+
1057
+ var args = Array.prototype.slice.call( arguments, 0 ),
1058
+ callback, link, update, animate,
1059
+ i, count, actual, to, values = asArray( args[0] );
1060
+
1061
+ // Extract modifiers for value method.
1062
+ if ( typeof args[1] === 'object' ) {
1063
+ callback = args[1]['set'];
1064
+ link = args[1]['link'];
1065
+ update = args[1]['update'];
1066
+ animate = args[1]['animate'];
1067
+
1068
+ // Support the 'true' option.
1069
+ } else if ( args[1] === true ) {
1070
+ callback = true;
1071
+ }
1072
+
1073
+ // The RTL settings is implemented by reversing the front-end,
1074
+ // internal mechanisms are the same.
1075
+ if ( options.dir && options.handles > 1 ) {
1076
+ values.reverse();
1077
+ }
1078
+
1079
+ // Animation is optional.
1080
+ if ( animate ) {
1081
+ addClassFor( $Target, Classes[14], 300 );
1082
+ }
1083
+
1084
+ // Determine how often to set the handles.
1085
+ count = $Handles.length > 1 ? 3 : 1;
1086
+ if ( values.length === 1 ) {
1087
+ count = 1;
1088
+ }
1089
+
1090
+ // If there are multiple handles to be set run the setting
1091
+ // mechanism twice for the first handle, to make sure it
1092
+ // can be bounced of the second one properly.
1093
+ for ( i = 0; i < count; i++ ) {
1094
+
1095
+ to = link || $Serialization[i%2][0];
1096
+ to = to.getValue( values[i%2] );
1097
+
1098
+ if ( to === false ) {
1099
+ continue;
1100
+ }
1101
+
1102
+ // Calculate the new handle position
1103
+ to = toStepping( options, to );
1104
+
1105
+ // Invert the value if this is a right-to-left slider.
1106
+ if ( options.dir ) {
1107
+ to = 100 - to;
1108
+ }
1109
+
1110
+ // Force delimitation.
1111
+ if ( setHandle( $Handles[i%2], to, true ) === true ) {
1112
+ continue;
1113
+ }
1114
+
1115
+ // Reset the input if it doesn't match the slider.
1116
+ $($Serialization[i%2]).each(function(index){
1117
+
1118
+ if (!index) {
1119
+ actual = this.actual;
1120
+ return true;
1121
+ }
1122
+
1123
+ this.write(
1124
+ actual,
1125
+ $Handles[i%2].children(),
1126
+ $Target,
1127
+ update
1128
+ );
1129
+ });
1130
+ }
1131
+
1132
+ // Optionally fire the 'set' event.
1133
+ if( callback === true ) {
1134
+ fireEvents(['set']);
1135
+ }
1136
+
1137
+ return this;
1138
+ };
1139
+
1140
+ // Get the slider value.
1141
+ /** @expose */
1142
+ target.vGet = function ( ) {
1143
+
1144
+ var i, retour = [];
1145
+
1146
+ // Get the value from all handles.
1147
+ for ( i = 0; i < options.handles; i++ ){
1148
+ retour[i] = $Serialization[i][0].saved;
1149
+ }
1150
+
1151
+ // If only one handle is used, return a single value.
1152
+ if ( retour.length === 1 ){
1153
+ return retour[0];
1154
+ }
1155
+
1156
+ if ( options.dir ) {
1157
+ return retour.reverse();
1158
+ }
1159
+
1160
+ return retour;
1161
+ };
1162
+
1163
+ // Destroy the slider and unbind all events.
1164
+ /** @expose */
1165
+ target.destroy = function ( ) {
1166
+
1167
+ // Loop all linked serialization objects and unbind all
1168
+ // events in the noUiSlider namespace.
1169
+ $.each($Serialization, function(){
1170
+ $.each(this, function(){
1171
+ // Won't remove 'change' when bound implicitly.
1172
+ if ( this.target ) {
1173
+ this.target.off( namespace );
1174
+ }
1175
+ });
1176
+ });
1177
+
1178
+ // Unbind events on the slider, remove all classes and child elements.
1179
+ $(this).off(namespace)
1180
+ .removeClass(Classes.join(' '))
1181
+ .empty();
1182
+
1183
+ // Return the original options from the closure.
1184
+ return originalOptions;
1185
+ };
1399
1186
 
1400
- // Make the range dragable.
1401
- if ( behaviour.drag ){
1402
1187
 
1403
- drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] );
1188
+ // Value setting
1404
1189
 
1405
- // When the range is fixed, the entire range can
1406
- // be dragged by the handles. The handle in the first
1407
- // origin will propagate the start event upward,
1408
- // but it needs to be bound manually on the other.
1409
- if ( behaviour.fixed ) {
1410
- drag = drag.add($Base.children().not( drag ).children());
1411
- }
1190
+ // Use the public value method to set the start values.
1191
+ $Target.val( options.start );
1192
+ }
1412
1193
 
1413
- attach ( actions.start, drag, start, {
1414
- handles: $Handles
1415
- });
1416
- }
1417
- }
1418
1194
 
1195
+ // Access points
1419
1196
 
1420
- // Initialize slider
1197
+ // Run the standard initializer
1198
+ function initialize ( originalOptions ) {
1421
1199
 
1422
- // Throw an error if the slider was already initialized.
1423
- if ( !$Target.is(':empty') ) {
1424
- throw new Error('Slider was already initialized.');
1425
- }
1200
+ // Throw error if group is empty.
1201
+ if ( !this.length ){
1202
+ throw new Error("noUiSlider: Can't initialize slider on empty selection.");
1203
+ }
1426
1204
 
1427
- // Create the base element, initialise HTML and set classes.
1428
- // Add handles and links.
1429
- $Base = addSlider( options, $Target );
1430
- $Handles = addHandles( options, $Base );
1431
- $Serialization = addLinks( options, $Handles );
1205
+ // Test the options once, not for every slider.
1206
+ var options = test( originalOptions, this );
1432
1207
 
1433
- // Set the connect classes.
1434
- addConnection ( options.connect, $Target, $Handles );
1208
+ // Loop all items, and provide a new closed-scope environment.
1209
+ return this.each(function(){
1210
+ closure(this, options, originalOptions);
1211
+ });
1212
+ }
1435
1213
 
1436
- // Attach user events.
1437
- events( options.events );
1214
+ // Destroy the slider, then re-enter initialization.
1215
+ function rebuild ( options ) {
1438
1216
 
1217
+ return this.each(function(){
1439
1218
 
1440
- // Methods
1219
+ // Get the current values from the slider,
1220
+ // including the initialization options.
1221
+ var values = $(this).val(),
1222
+ originalOptions = this.destroy(),
1441
1223
 
1442
- // Set the slider value.
1443
- target.vSet = function ( values, callback, link, update, animate ){
1444
-
1445
- var i, to;
1446
-
1447
- // The RTL settings is implemented by reversing the front-end,
1448
- // internal mechanisms are the same.
1449
- if ( options.dir && options.handles > 1 ) {
1450
- values.reverse();
1451
- }
1452
-
1453
- // Animation is optional.
1454
- if ( animate ) {
1455
- addClassFor( $Target, Classes[14], 300 );
1456
- }
1457
-
1458
- // If there are multiple handles to be set run the setting
1459
- // mechanism twice for the first handle, to make sure it
1460
- // can be bounced of the second one properly.
1461
- for ( i = 0; i < ( $Handles.length > 1 ? 3 : 1 ); i++ ) {
1462
-
1463
- to = link || $Serialization[i%2][0];
1464
- to = to.valueOf( values[i%2] );
1465
-
1466
- if ( to === false ) {
1467
- continue;
1468
- }
1469
-
1470
- // Calculate the new handle position
1471
- to = toStepping( options, to );
1472
-
1473
- // Invert the value if this is a right-to-left slider.
1474
- if ( options.dir ) {
1475
- to = 100 - to;
1476
- }
1477
-
1478
- // Force delimitation.
1479
- if ( setHandle( $Handles[i%2], to, true ) === true ) {
1480
- continue;
1481
- }
1482
-
1483
- // Reset the input if it doesn't match the slider.
1484
- $($Serialization[i%2]).each(function(){
1485
- this.write(
1486
- options,
1487
- $Locations[i%2],
1488
- $Handles[i%2].children(),
1489
- $Target,
1490
- update
1491
- );
1492
- });
1493
- }
1494
-
1495
- // Optionally fire the 'set' event.
1496
- if( callback === true ) {
1497
- fireEvents(['set']);
1498
- }
1499
-
1500
- return this;
1501
- };
1502
-
1503
- // Get the slider value.
1504
- target.vGet = function ( ){
1505
-
1506
- var i, retour = [];
1507
-
1508
- // Get the value from all handles.
1509
- for ( i = 0; i < options.handles; i++ ){
1510
- retour[i] = $Serialization[i][0].saved;
1511
- }
1512
-
1513
- // If only one handle is used, return a single value.
1514
- if ( retour.length === 1 ){
1515
- return retour[0];
1516
- }
1517
-
1518
- if ( options.dir && options.handles > 1 ) {
1519
- return retour.reverse();
1520
- }
1521
-
1522
- return retour;
1523
- };
1524
-
1525
- // Destroy the slider and unbind all events.
1526
- target.destroy = function ( ){
1527
-
1528
- // Loop all linked serialization objects and unbind all
1529
- // events in the noUiSlider namespace.
1530
- $.each($Serialization, function(){
1531
- $.each(this, function(){
1532
- // Won't remove 'change' when bound implicitly.
1533
- if ( this.target ) {
1534
- this.target.off( namespace );
1535
- }
1536
- });
1537
- });
1538
-
1539
- // Unbind events on the slider, remove all classes and child elements.
1540
- $(this).off(namespace)
1541
- .removeClass(Classes.join(' '))
1542
- .empty();
1543
-
1544
- // Return the original options from the closure.
1545
- return originalOptions;
1546
- };
1224
+ // Extend the previous options with the newly provided ones.
1225
+ newOptions = $.extend( {}, originalOptions, options );
1547
1226
 
1227
+ // Run the standard initializer.
1228
+ $(this).noUiSlider( newOptions );
1548
1229
 
1549
- // Value setting
1230
+ // If the start option hasn't changed,
1231
+ // reset the previous values.
1232
+ if ( originalOptions.start === newOptions.start ) {
1233
+ $(this).val(values);
1234
+ }
1235
+ });
1236
+ }
1550
1237
 
1551
- // Use the public value method to set the start values.
1552
- $Target.val( options.start );
1553
- }
1554
1238
 
1239
+ // Remap the serialization constructor for legacy support.
1240
+ /** @expose */
1241
+ $.noUiSlider = { 'Link': $.Link };
1555
1242
 
1556
- // Access points
1243
+ // Extend jQuery/Zepto with the noUiSlider method.
1244
+ /** @expose */
1245
+ $.fn.noUiSlider = function ( options, re ) {
1246
+ return ( re ? rebuild : initialize ).call(this, options);
1247
+ };
1557
1248
 
1558
- // Run the standard initializer
1559
- function initialize ( originalOptions ) {
1560
-
1561
- // Throw error if group is empty.
1562
- if ( !this.length ){
1563
- throwError("Can't initialize slider on empty selection.");
1564
- }
1565
-
1566
- // Test the options once, not for every slider.
1567
- var options = test( originalOptions, this );
1568
-
1569
- // Loop all items, and provide a new closed-scope environment.
1570
- return this.each(function(){
1571
- closure(this, options, originalOptions);
1572
- });
1573
- }
1574
-
1575
- // Destroy the slider, then re-enter initialization.
1576
- function rebuild ( options ) {
1577
-
1578
- return this.each(function(){
1579
-
1580
- // Get the current values from the slider,
1581
- // including the initialization options.
1582
- var values = $(this).val(),
1583
- originalOptions = this.destroy(),
1584
-
1585
- // Extend the previous options with the newly provided ones.
1586
- newOptions = $.extend( {}, originalOptions, options );
1587
-
1588
- // Run the standard initializer.
1589
- $(this).noUiSlider( newOptions );
1590
-
1591
- // If the start option hasn't changed,
1592
- // reset the previous values.
1593
- if ( originalOptions.start === newOptions.start ) {
1594
- $(this).val(values);
1595
- }
1596
- });
1597
- }
1598
-
1599
-
1600
- // Expose serialization constructor.
1601
- /** @expose */
1602
- $.noUiSlider = { 'Link': Link };
1603
-
1604
- // Extend jQuery/Zepto with the noUiSlider method.
1605
- /** @expose */
1606
- $.fn.noUiSlider = function ( options, re ) {
1607
- return ( re ? rebuild : initialize ).call(this, options);
1608
- };
1609
-
1610
- $.fn.val = function ( ) {
1611
-
1612
- // Convert the function arguments to an array.
1613
- var args = Array.prototype.slice.call( arguments, 0 ),
1614
- set, link, update, animate;
1615
-
1616
- // Test if there are arguments, and if not, call the 'get' method.
1617
- if ( !args.length ) {
1618
-
1619
- // Determine whether to use the native val method.
1620
- if ( this.hasClass(Classes[0]) ) {
1621
- return this[0].vGet();
1622
- }
1623
-
1624
- return $val.apply( this );
1625
- }
1626
-
1627
- // Extract modifiers for value method.
1628
- if ( typeof args[1] === 'object' ) {
1629
- set = args[1]['set'];
1630
- link = args[1]['link'];
1631
- update = args[1]['update'];
1632
- animate = args[1]['animate'];
1633
-
1634
- // Support the 'true' option.
1635
- } else if ( args[1] === true ) {
1636
- set = true;
1637
- }
1638
-
1639
- // Loop all individual items, and handle setting appropriately.
1640
- return this.each(function(){
1641
-
1642
- if ( $(this).hasClass(Classes[0]) ) {
1643
- this.vSet( asArray(args[0]), set, link, update, animate );
1644
- } else {
1645
- $val.apply( $(this), args );
1646
- }
1647
- });
1648
- };
1249
+ // Attach a classbased val handler.
1250
+ $.classVal(Classes[0], 'vGet', 'vSet', false);
1649
1251
 
1650
1252
  }( window['jQuery'] || window['Zepto'] ));