nouislider-rails 6.0.1 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'] ));