jquery-nouislider-rails 3.2.1 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -18,15 +18,11 @@ And then execute:
18
18
  In your app/assets/javascript manifest file:
19
19
  //=require jquery.nouislider
20
20
 
21
- In your app/assets/stylesheets manifest file, you can include either of the default slider styles:
22
-
23
- //=require 'nouislider.fox.css'
24
-
25
- or
26
-
27
- //=require 'nouislider.space.css'
21
+ In your app/assets/stylesheets manifest file:
22
+ //=require jquery.nouislider
28
23
 
29
- You are encouraged to use your own stylesheet, but these two will give you a good base to start from.
24
+ You are encouraged to use your own stylesheet. The styles for 4.0 have change significantly.
25
+ A reference is here: http://refreshless.com/nouislider/slider-design-styles
30
26
 
31
27
  ## Contributing
32
28
 
@@ -1,7 +1,7 @@
1
1
  module Jquery
2
2
  module Nouislider
3
3
  module Rails
4
- VERSION = "3.2.1"
4
+ VERSION = "4.0.1"
5
5
  end
6
6
  end
7
7
  end
@@ -1,364 +1,826 @@
1
- /* noUiSlider 3.2.1 */
2
- (function ($) {
3
-
4
- $.fn.noUiSlider = function (options, flag) {
5
-
6
- // test for mouse, pointer or touch
7
- var EVENT = window.navigator.msPointerEnabled ? 2 : 'ontouchend' in document ? 3 : 1;
8
- if (window.debug && console) {
9
- console.log(EVENT);
10
- }
11
-
12
- // shorthand for test=function, calling
13
- function call(f, scope, args) {
14
- if (typeof f === "function") {
15
- f.call(scope, args);
1
+ /* noUiSlider 4.0.0 */
2
+ (function($, UNDEF){
3
+
4
+ $.fn.noUiSlider = function( options ){
5
+
6
+ var namespace = '.nui'
7
+ // Create a shorthand for document event binding
8
+ ,all = $(document)
9
+ // Create a map of touch and mouse actions
10
+ ,actions = {
11
+ start: 'mousedown' + namespace + ' touchstart' + namespace
12
+ ,move: 'mousemove' + namespace + ' touchmove' + namespace
13
+ ,end: 'mouseup' + namespace + ' touchend' + namespace
16
14
  }
17
- }
18
-
19
- // function wrapper for calculating to and from range values
20
- var percentage = {
21
- to : function (range, value) {
22
- value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0];
23
- return (value * 100) / this._length(range);
24
- },
25
- from : function (range, value) {
26
- return (value * 100) / this._length(range);
27
- },
28
- is : function (range, value) {
29
- return ((value * this._length(range)) / 100) + range[0];
30
- },
31
- _length : function (range) {
32
- return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]);
15
+ // Make a copy of the current val function.
16
+ ,$VAL = $.fn.val
17
+ // Define a set of standard HTML classes for
18
+ // the various structures noUiSlider uses.
19
+ ,clsList = [
20
+ 'noUi-base' // 0
21
+ ,'noUi-origin' // 1
22
+ ,'noUi-handle' // 2
23
+ ,'noUi-input' // 3
24
+ ,'noUi-active' // 4
25
+ ,'noUi-state-tap' // 5
26
+ ,'noUi-target' // 6
27
+ ,'-lower' // 7
28
+ ,'-upper' // 8
29
+ ,'noUi-connect' // 9
30
+ ,'noUi-vertical' // 10
31
+ ,'noUi-horizontal' // 11
32
+ ,'handles' // 12
33
+ ,'noUi-background' // 13
34
+ ,'noUi-z-index' // 14
35
+ ]
36
+ ,stdCls = {
37
+ base: [clsList[0], clsList[13]]
38
+ ,origin: [clsList[1]]
39
+ ,handle: [clsList[2]]
33
40
  }
41
+ ,percentage = {
42
+ to : function (range, value) {
43
+ value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0];
44
+ return (value * 100) / this._length(range);
45
+ },
46
+ from : function (range, value) {
47
+ return (value * 100) / this._length(range);
48
+ },
49
+ is : function (range, value) {
50
+ return ((value * this._length(range)) / 100) + range[0];
51
+ },
52
+ _length : function (range) {
53
+ return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]);
54
+ }
55
+ };
56
+
57
+ if ( window.navigator.msPointerEnabled ) {
58
+ actions = {
59
+ start: 'MSPointerDown' + namespace
60
+ ,move: 'MSPointerMove' + namespace
61
+ ,end: 'MSPointerUp' + namespace
62
+ };
63
+ }
64
+
65
+ function __sp ( e ) {
66
+ e.stopPropagation();
34
67
  }
35
68
 
36
- // bounce handles of eachother, the edges of the slider
37
- function correct(proposal, slider, handle) {
38
-
39
- var
40
- setup = slider.data('setup'),
41
- handles = setup.handles,
42
- settings = setup.settings,
43
- pos = setup.pos;
44
-
45
- proposal = proposal < 0 ? 0 : proposal > 100 ? 100 : proposal;
46
-
47
- if (settings.handles == 2) {
48
- if (handle.is(':first-child')) {
49
- var other = parseFloat(handles[1][0].style[pos]) - settings.margin;
50
- proposal = proposal > other ? other : proposal;
51
- } else {
52
- var other = parseFloat(handles[0][0].style[pos]) + settings.margin;
53
- proposal = proposal < other ? other : proposal;
69
+ function call ( f, scope, args ) {
70
+ $.each(f,function(i,q){
71
+ if (typeof q === "function") {
72
+ q.call(scope, args);
54
73
  }
74
+ });
75
+ }
76
+
77
+ function blocked ( e ) {
78
+ return ( e.data.base.data('target').is('[class*="noUi-state-"], [disabled]') );
79
+ }
80
+
81
+ function fixEvent ( e, preventDefault ) {
82
+
83
+ // Required (in at the very least Chrome) to prevent
84
+ // scrolling and panning while attempting to slide.
85
+ // The tap event also depends on this.
86
+ if( preventDefault ) {
87
+ e.preventDefault();
55
88
  }
56
-
57
- if (settings.step) {
58
- var per = percentage.from(settings.range, settings.step);
59
- proposal = Math.round(proposal / per) * per;
89
+
90
+ var jQueryEvent = e
91
+ ,touch = e.type.indexOf('touch') === 0
92
+ ,mouse = e.type.indexOf('mouse') === 0
93
+ ,pointer = e.type.indexOf('MSPointer') === 0
94
+ ,x,y;
95
+
96
+ e = e.originalEvent;
97
+
98
+ if (touch) {
99
+ x = e.changedTouches[0].pageX;
100
+ y = e.changedTouches[0].pageY;
60
101
  }
61
-
62
- return proposal;
63
-
64
- }
65
-
66
- // get standarised clientX and clientY
67
- function client(f) {
68
- try {
69
- return [(f.clientX || f.originalEvent.clientX || f.originalEvent.touches[0].clientX), (f.clientY || f.originalEvent.clientY || f.originalEvent.touches[0].clientY)];
70
- } catch (e) {
71
- return ['x', 'y'];
102
+ if (mouse) {
103
+
104
+ // Polyfill the pageXOffset and pageYOffset
105
+ // variables for IE7 and IE8;
106
+ if(window.pageXOffset === UNDEF){
107
+ window.pageXOffset = document.documentElement.scrollLeft;
108
+ window.pageYOffset = document.documentElement.scrollTop;
109
+ }
110
+
111
+ x = e.clientX + window.pageXOffset;
112
+ y = e.clientY + window.pageYOffset;
72
113
  }
114
+ if (pointer) {
115
+ x = e.pageX;
116
+ y = e.pageY;
117
+ }
118
+
119
+ return { pass: jQueryEvent.data, e:e, x:x, y:y, t: [touch, mouse, pointer] };
120
+
73
121
  }
74
122
 
75
- // get native inline style value in %
76
- function place(handle, pos) {
77
- return parseFloat(handle[0].style[pos]);
123
+ function getPercentage( a ){
124
+ return parseFloat(this.style[a]);
78
125
  }
126
+
127
+ function test ( o, set ){
79
128
 
80
- // simplified defaults
81
- var defaults = {
82
- handles : 2,
83
- serialization : {
84
- to : ['', ''],
85
- resolution : 0.01
129
+ // checks is number is numerical
130
+ function num(e){
131
+ return !isNaN(e) && isFinite(e);
132
+ }
133
+ function ser(r){
134
+ return ( r instanceof $ || typeof r === 'string' || r === false );
86
135
  }
87
- };
88
-
89
- // contains all methods
90
- methods = {
91
- create : function () {
92
-
93
- return this.each(function () {
94
136
 
95
- // set handle to position
96
- function setHandle(handle, to, slider) {
97
- handle.css(pos, to + '%').data('input').val(percentage.is(settings.range, to).toFixed(res));
137
+
138
+ /**
139
+ These tests are structured with an item for every option available.
140
+ Every item contains an 'r' flag, which marks a required option, and
141
+ a 't' function, which in turn takes some arguments:
142
+ - a reference to options object
143
+ - the value for the option
144
+ - the option name (optional);
145
+ The testing function returns false when an error is detected,
146
+ or true when everything is OK. Every test also has an 'init'
147
+ method which appends the parent object to all children.
148
+ **/
149
+ var TESTS = {
150
+ "handles": {
151
+ r: true // has default
152
+ ,t: function(o,q){
153
+ q = parseInt(q, 10);
154
+ return ( q === 1 || q === 2 );
98
155
  }
99
-
100
- var
101
- settings = $.extend(defaults, options),
102
- // handles
103
- handlehtml = '<a><div></div></a>',
104
- // save this to variable, // allows identification
105
- slider = $(this).data('_isnS_', true),
106
- // array of handles
107
- handles = [],
108
- // the way the handles are positioned for this slider, top/left
109
- pos,
110
- // for quick orientation testing and array matching
111
- orientation,
112
- // append classes
113
- classes = "",
114
- // tests numerical
115
- num = function (e) {
116
- return !isNaN(parseFloat(e)) && isFinite(e);
117
- },
118
- // counts decimals in serialization, sets default
119
- split = (settings.serialization.resolution = settings.serialization.resolution || 0.01).toString().split('.'),
120
- res = split[0] == 1 ? 0 : split[1].length;
121
-
122
- settings.start = num(settings.start) ? [settings.start, 0] : settings.start;
123
-
124
- // logs bad input values, if possible
125
- $.each(settings, function (a, b) {
126
-
127
- if (num(b)) {
128
- settings[a] = parseFloat(b);
129
- } else if (typeof b == "object" && num(b[0])) {
130
- b[0] = parseFloat(b[0]);
131
- if (num(b[1])) {
132
- b[1] = parseFloat(b[1]);
156
+ }
157
+ ,"range": {
158
+ r: true
159
+ ,t: function(o,q,w){
160
+ if(q.length!==2){
161
+ return false;
162
+ }
163
+ q = [parseFloat(q[0]),parseFloat(q[1])];
164
+ if(!num(q[0])||!num(q[1])){
165
+ return false;
166
+ }
167
+ o[w]=q;
168
+ return true;
169
+ }
170
+ }
171
+ ,"start": {
172
+ r: true
173
+ ,t: function(o,q,w){
174
+ if(o.handles === 1){
175
+ if($.isArray(q)){
176
+ q=q[0];
133
177
  }
178
+ q = parseFloat(q);
179
+ o.start = [q];
180
+ return num(q);
134
181
  }
182
+ return this.parent.range.t(o,q,w);
183
+ }
184
+ }
185
+ ,"connect": {
186
+ t: function(o,q){
187
+ return ( q === true
188
+ || q === false
189
+ || ( q === 'lower' && o.handles === 1)
190
+ || ( q === 'upper' && o.handles === 1));
191
+ }
192
+ }
193
+ ,"orientation": {
194
+ t: function(o,q){
195
+ return ( q === "horizontal" || q === "vertical" );
196
+ }
197
+ }
198
+ ,"margin": {
199
+ r: true // has default
200
+ ,t: function(o,q,w){
201
+ q = parseFloat(q);
202
+ o[w]=q;
203
+ return num(q);
204
+ }
205
+ }
206
+ ,"serialization": {
207
+ r: true // has default
208
+ ,t: function(o,q){
135
209
 
136
- var e = false;
137
- b = typeof b == "undefined" ? "x" : b;
138
-
139
- switch (a) {
140
- case 'range':
141
- case 'start':
142
- e = b.length != 2 || !num(b[0]) || !num(b[1]);
143
- break;
144
- case 'handles':
145
- e = (b < 1 || b > 2 || !num(b));
146
- break;
147
- case 'connect':
148
- e = b != "lower" && b != "upper" && typeof b != "boolean";
149
- break;
150
- case 'orientation':
151
- e = (b != "vertical" && b != "horizontal");
152
- break;
153
- case 'margin':
154
- case 'step':
155
- e = typeof b != "undefined" && !num(b);
156
- break;
157
- case 'serialization':
158
- e = typeof b != "object" || !num(b.resolution) || (typeof b.to == 'object' && b.to.length < settings.handles);
159
- break;
160
- case 'slide':
161
- e = typeof b != "function";
162
- break;
210
+ if(!q.resolution){
211
+ o.serialization.resolution = 0.01;
212
+ } else {
213
+ switch(q.resolution){
214
+ case 1:
215
+ case 0.1:
216
+ case 0.01:
217
+ case 0.001:
218
+ case 0.0001:
219
+ case 0.00001:
220
+ break;
221
+ default:
222
+ return false;
223
+ }
163
224
  }
164
225
 
165
- if (e && console) {
166
- console.error('Bad input for ' + a + ' on slider:', slider);
167
- }
226
+ if(q.to){
227
+
228
+ if(o.handles === 1){
229
+ if(!$.isArray(q.to)){
230
+ q.to = [q.to];
231
+ }
232
+ o.serialization.to = q.to;
233
+ return ser(q.to[0]);
234
+ }
235
+ return (q.to.length === 2 && ser(q.to[0]) && ser(q.to[1]));
168
236
 
237
+ }
238
+
239
+ return false;
240
+
241
+ }
242
+ }
243
+ ,"slide": {
244
+ t: function(o,q){
245
+ return typeof q === "function";
246
+ }
247
+ }
248
+ ,"step": {
249
+ t: function(o,q,w){
250
+ return this.parent.margin.t(o,q,w);
251
+ }
252
+ }
253
+ ,"init": function(){
254
+ var obj = this;
255
+ $.each(obj,function(i,c){
256
+ c.parent = obj;
169
257
  });
258
+ delete this.init;
259
+ return this;
260
+ }
261
+ },
262
+
263
+ // Prepare a set of tests, by adding some internal reference
264
+ // values not available in native Javascript object implementation.
265
+ a = TESTS.init();
266
+
267
+ // Loop all provided tests;
268
+ // v is the option set, i is the index for the current test.
269
+ $.each(a, function( i, v ){
270
+
271
+ // If the value is required but not set,
272
+ // or if the test fails, throw an error.
273
+ if((v.r && (!o[i] && o[i] !== 0)) || ((o[i] || o[i] === 0) && !v.t(o,o[i],i))){
274
+
275
+ // For debugging purposes it might be very useful
276
+ // to know what option caused the trouble.
277
+ if(console&&console.log){
278
+ console.log(
279
+ "Slider:\t\t\t", set,
280
+ "\nOption:\t\t\t", i,
281
+ "\nValue:\t\t\t", o[i]
282
+ );
283
+ }
284
+ $.error("Error on noUiSlider initialisation.");
285
+ return false;
286
+ }
170
287
 
171
- settings.margin = settings.margin ? percentage.from(settings.range, settings.margin) : 0;
288
+ });
172
289
 
173
- // tests serialization to be strings or jQuery objects
174
- if (settings.serialization.to instanceof jQuery || typeof settings.serialization.to == 'string' || settings.serialization.to === false) {
175
- settings.serialization.to = [settings.serialization.to];
290
+ }
291
+
292
+ function closest( value, to ){
293
+ return Math.round(value / to) * to;
294
+ }
295
+
296
+ function setHandle ( handle, to, forgive ) {
297
+
298
+ var nui = handle.data('nui').options
299
+ // Get the array of handles from the base.
300
+ // Will be undefined at initialisation.
301
+ ,handles = handle.data('nui').base.data(clsList[12])
302
+ // Get some settings from the handle
303
+ ,style = handle.data('nui').style
304
+ ,dec = handle.data('nui').decimals
305
+ ,hLimit;
306
+
307
+ // Ignore the call if the handle won't move anyway.
308
+ if(to === handle[0].getPercentage(style)) {
309
+ return false;
310
+ }
311
+
312
+ // Limit `to` to 0 - 100
313
+ to = to < 0 ? 0 : to > 100 ? 100 : to;
314
+
315
+ // Handle the step option, or ignore it.
316
+ if( nui.step && !forgive ){
317
+ to = closest( to, percentage.from(nui.range, nui.step));
318
+ }
319
+
320
+ // Stop handling this call if the handle won't step to a new value.
321
+ if(to === handle[0].getPercentage(style)) {
322
+ return false;
323
+ }
324
+
325
+ // We're done if this is the only handle,
326
+ // if the handle bounce is trusted to the user
327
+ // or on initialisation when handles isn't defined yet.
328
+ if( handle.siblings('.' + clsList[1]).length && !forgive && handles ){
329
+
330
+ // Otherwise, the handle should bounce,
331
+ // and stop at the other handle.
332
+ if ( handle.data('nui').number ) {
333
+ hLimit = handles[0][0].getPercentage(style) + nui.margin;
334
+ to = to < hLimit ? hLimit : to;
335
+ } else {
336
+ hLimit = handles[1][0].getPercentage(style) - nui.margin;
337
+ to = to > hLimit ? hLimit : to;
338
+ }
339
+
340
+ // Stop handling this call if the handle can't move past another.
341
+ if(to === handle[0].getPercentage(style)) {
342
+ return false;
343
+ }
344
+
345
+ }
346
+
347
+ // Fix for the z-index issue where the lower handle gets stuck
348
+ // below the upper one. Since this function is called for every
349
+ // movement, toggleClass cannot be used.
350
+ if(handle.data('nui').number === 0 && to > 95){
351
+ handle.addClass(clsList[14]);
352
+ } else {
353
+ handle.removeClass(clsList[14]);
354
+ }
355
+
356
+ // Set handle to new location
357
+ handle.css( style , to + '%');
358
+
359
+ // Write the value to the serialization object.
360
+ handle.data('store').val(percentage.is(nui.range, to).toFixed(dec));
361
+
362
+ return true;
363
+
364
+ }
365
+
366
+ function store ( handle, S ) {
367
+
368
+ var i = handle.data('nui').number;
369
+
370
+ if( S.to[i] instanceof $ ) {
371
+
372
+ // Attach a change event to the supplied jQuery object,
373
+ // which will just trigger the val function on the parent.
374
+ // In some cases, the change event will not fire on select elements,
375
+ // so listen to 'blur' too.
376
+ return S.to[i].on('change'+namespace+' blur'+namespace, function(){
377
+ var arr = [null, null];
378
+ arr[i] = $(this).val();
379
+ handle.data('nui').target.val(arr, true);
380
+ });
381
+
382
+ }
383
+
384
+ if ( typeof S.to[i] === "string" ) {
385
+
386
+ // Append a new object to the noUiSlider base,
387
+ // prevent change events flowing upward.
388
+ return $('<input type="hidden" class="'+clsList[3]+'" name="' + S.to[i] + '">')
389
+ .appendTo(handle).change(__sp);
390
+
391
+ }
392
+
393
+ if ( S.to[i] === false ) {
394
+
395
+ // Create an object capable of handling all jQuery calls.
396
+ return {
397
+ // The value will be stored a data on the handle.
398
+ val : function(a) {
399
+ // Value function provides a getter and a setter.
400
+ // Can't just test for !a, as a might be 0.
401
+ if ( a === UNDEF ) {
402
+ // Either set...
403
+ return this._handle.data('nui-val');
404
+ }
405
+ // ... or return;
406
+ this._handle.data('nui-val', a);
176
407
  }
177
-
178
- if (settings.orientation == "vertical") {
179
- classes += "vertical";
180
- pos = 'top';
181
- orientation = 1;
182
- } else {
183
- classes += "horizontal";
184
- pos = 'left';
185
- orientation = 0;
408
+ // The object could be mistaken for a jQuery object,
409
+ // make sure that doesn't trigger any errors.
410
+ ,hasClass: function(){
411
+ return false;
186
412
  }
413
+ // The val function needs access to the handle.
414
+ ,_handle: handle
415
+ };
416
+ }
417
+
418
+ }
187
419
 
188
- classes += settings.connect ? settings.connect == "lower" ? " connect lower" : " connect" : "";
420
+ function move( event ) {
189
421
 
190
- slider.addClass(classes);
422
+ // This function is called often, keep it light.
423
+
424
+ event = fixEvent( event, true );
191
425
 
192
- for (var i = 0; i < settings.handles; i++) {
426
+ if(!event) {
427
+ return;
428
+ }
193
429
 
194
- handles[i] = slider.append(handlehtml).children(':last');
195
- var setTo = percentage.to(settings.range, settings.start[i]);
196
- handles[i].css(pos, setTo + '%');
197
- if (setTo == 100 && handles[i].is(':first-child')) {
198
- handles[i].css('z-index', 2);
199
- }
430
+ var base = event.pass.base
431
+ ,style = base.data('style')
432
+ // Subtract the initial movement from the current event,
433
+ // while taking vertical sliders into account.
434
+ ,proposal = event.x - event.pass.startEvent.x
435
+ ,baseSize = style === 'left' ? base.width() : base.height();
436
+
437
+ if(style === 'top') {
438
+ proposal = event.y - event.pass.startEvent.y;
439
+ }
440
+
441
+ proposal = event.pass.position + ( ( proposal * 100 ) / baseSize );
442
+
443
+ setHandle( event.pass.handle, proposal );
200
444
 
201
- var bind = '.noUiSlider',
202
- onEvent = (EVENT === 1 ? 'mousedown' : EVENT === 2 ? 'MSPointerDown' : 'touchstart') + bind + 'X',
203
- moveEvent = (EVENT === 1 ? 'mousemove' : EVENT === 2 ? 'MSPointerMove' : 'touchmove') + bind,
204
- offEvent = (EVENT === 1 ? 'mouseup' : EVENT === 2 ? 'MSPointerUp' : 'touchend') + bind
445
+ // Trigger the 'slide' event, pass the target so that it is 'this'.
446
+ call(
447
+ [ event.pass.base.data('options').slide ]
448
+ ,event.pass.base.data('target')
449
+ );
450
+
451
+ }
205
452
 
206
- handles[i].find('div').on(onEvent, function (e) {
453
+ function end ( event ) {
207
454
 
208
- $('body').bind('selectstart' + bind, function () {
209
- return false;
210
- });
455
+ if ( blocked( event ) ) {
456
+ return;
457
+ }
458
+
459
+ // Handle is no longer active;
460
+ event.data.handle.children().removeClass(clsList[4]);
461
+
462
+ // Unbind move and end events, to prevent
463
+ // them stacking up over and over;
464
+ all.off(actions.move);
465
+ all.off(actions.end);
466
+ $('body').off(namespace);
467
+
468
+ event.data.base.data('target').change();
469
+
470
+ }
211
471
 
212
- if (!slider.hasClass('disabled')) {
472
+ function start ( event ) {
213
473
 
214
- $('body').addClass('TOUCH');
474
+ // When the slider is in a transitional state, stop.
475
+ // Also prevents interaction with disabled sliders.
476
+ if ( blocked( event ) ) {
477
+ return;
478
+ }
479
+
480
+ event = fixEvent( event );
215
481
 
216
- var handle = $(this).addClass('active').parent(),
217
- unbind = handle.add($(document)).add('body'),
218
- originalPosition = parseFloat(handle[0].style[pos]),
219
- originalClick = client(e),
220
- previousClick = originalClick,
221
- previousProposal = false;
482
+ if(!event) {
483
+ return;
484
+ }
222
485
 
223
- $(document).on(moveEvent, function (f) {
486
+ var handle = event.pass.handle
487
+ ,position = handle[0].getPercentage( handle.data('nui').style );
488
+
489
+ handle.children().addClass('noUi-active');
490
+
491
+ // Attach the move event handler, while
492
+ // passing all relevant information along.
493
+ all.on(actions.move, {
494
+ startEvent: event
495
+ ,position: position
496
+ ,base: event.pass.base
497
+ ,handle: handle
498
+ }, move);
499
+
500
+ all.on(actions.end, { base: event.pass.base, handle: handle }, end);
501
+
502
+ $('body').on('selectstart' + namespace, function(){ return false; });
503
+
504
+ }
505
+
506
+ function selfEnd( event ) {
507
+ // Trigger the end handler. Supply correct data using a
508
+ // fake object that contains all required information;
509
+ end({ data: { base: event.data.base, handle: event.data.handle } });
510
+ // Stop propagation so that the tap handler doesn't interfere;
511
+ event.stopPropagation();
512
+ }
513
+
514
+ function tap ( event ) {
224
515
 
225
- f.preventDefault();
516
+ if ( blocked( event ) || event.data.base.find('.' + clsList[4]).length ) {
517
+ return;
518
+ }
519
+
520
+ event = fixEvent( event );
226
521
 
227
- var currentClick = client(f);
522
+ // The event handler might have rejected this event.
523
+ if(!event) {
524
+ return;
525
+ }
526
+
527
+ // Getting variables from the event is not required, but
528
+ // shortens other expressions and is far more convenient;
529
+ var i, handle, base = event.pass.base
530
+ ,handles = event.pass.handles
531
+ ,style = base.data('style')
532
+ ,eventXY = event[style === 'left' ? 'x' : 'y']
533
+ ,baseSize = style === 'left' ? base.width() : base.height()
534
+
535
+ // Create a standard set off offsets compensated with the
536
+ // scroll distance. When required, correct for scrolling.
537
+ // This is a bug, as far as I can see, in IE(10?).
538
+ ,correction = {
539
+ x: ( event.t[2] ? window.pageXOffset : 0 )
540
+ }
541
+ ,offset = {
542
+ handles: []
543
+ ,base: {
544
+ left: base.offset().left - correction.x
545
+ ,top: base.offset().top
546
+ }
547
+ };
548
+
549
+ // Loop handles and add data to the offset list.
550
+ for (i = 0; i < handles.length; i++ ) {
551
+ offset.handles.push({
552
+ left: handles[i].offset().left - correction.x
553
+ ,top: handles[i].offset().top
554
+ });
555
+ }
556
+
557
+ // Calculate the central point between the handles;
558
+ var handleCenter = handles.length === 1 ? 0 :
559
+ (( offset.handles[0][style] + offset.handles[1][style] ) / 2 );
560
+
561
+ // If there is just one handle,
562
+ // or the lower handles in closest to the event,
563
+ // select the first handle. Otherwise, pick the second.
564
+ if ( handles.length === 1 || eventXY < handleCenter ){
565
+ handle = handles[0];
566
+ } else {
567
+ handle = handles[1];
568
+ }
228
569
 
229
- if (currentClick[0] == "x") {
230
- return;
231
- }
570
+ // Flag the slider as it is now in a transitional state.
571
+ // Transition takes 300 ms, so re-enable the slider afterwards.
572
+ base.addClass(clsList[5]);
573
+ setTimeout(function(){
574
+ base.removeClass(clsList[5]);
575
+ }, 300);
576
+
577
+ // Calculate the new position for the handle and
578
+ // trigger the movement.
579
+ setHandle(
580
+ handle
581
+ ,(((eventXY - offset.base[style]) * 100) / baseSize)
582
+ );
583
+
584
+ // Trigger the 'slide' event, pass the target so that it is 'this'.
585
+ call(
586
+ [ handle.data('nui').options.slide ]
587
+ ,base.data('target')
588
+ );
589
+
590
+ base.data('target').change();
232
591
 
233
- currentClick[0] -= originalClick[0];
234
- currentClick[1] -= originalClick[1];
592
+ }
593
+
594
+ function create ( ) {
595
+
596
+ return this.each(function( index, target ){
597
+
598
+ // Target is the wrapper that will receive all external
599
+ // scripting interaction. It has no styling and serves no
600
+ // other function.
601
+ target = $(target);
602
+ target.addClass(clsList[6]);
603
+
604
+ // Base is the internal main 'bar'.
605
+ var i, style, decimals, handle
606
+ ,base = $('<div/>').appendTo(target)
607
+ ,handles = []
608
+ ,cls = {
609
+ base: stdCls.base
610
+ ,origin: [
611
+ stdCls.origin.concat([clsList[1] + clsList[7]])
612
+ ,stdCls.origin.concat([clsList[1] + clsList[8]])
613
+ ]
614
+ ,handle: [
615
+ stdCls.handle.concat([clsList[2] + clsList[7]])
616
+ ,stdCls.handle.concat([clsList[2] + clsList[8]])
617
+ ]
618
+ };
619
+
620
+ // Set defaults where applicable;
621
+ options = $.extend({
622
+ handles: 2
623
+ ,margin: 0
624
+ ,orientation: "horizontal"
625
+ }, options) || {};
626
+
627
+ // Set a default for serialization;
628
+ if(!options.serialization){
629
+ options.serialization = {
630
+ to : [false, false]
631
+ ,resolution : 0.01
632
+ };
633
+ }
634
+
635
+ // Run all options through a testing mechanism to ensure correct
636
+ // input. The test function will throw errors, so there is
637
+ // no need to capture the result of this call. It should be noted
638
+ // that options might get modified to be handled properly. E.g.
639
+ // wrapping integers in arrays.
640
+ test(options, target);
641
+
642
+ // I can't type serialization any more, and it doesn't compress
643
+ // very well, so shorten it.
644
+ options.S = options.serialization;
645
+
646
+
647
+ // INCOMPLETE
648
+ if( options.connect ) {
649
+ cls.origin[0].push(clsList[9]);
650
+ if( options.connect === "lower" ){
651
+ // Add some styling classes to the base;
652
+ cls.base.push(clsList[9], clsList[9] + clsList[7]);
653
+ // When using the option 'Lower', there is only one
654
+ // handle, and thus only one origin.
655
+ cls.origin[0].push(clsList[13]);
656
+ } else {
657
+ cls.base.push(clsList[9] + clsList[8]);
658
+ }
659
+ }
235
660
 
236
- var movement = [
237
- previousClick[0] != currentClick[0], previousClick[1] != currentClick[1]
238
- ],
239
- proposal = originalPosition + ((currentClick[orientation] * 100) / (orientation ? slider.height() : slider.width()));
240
- proposal = correct(proposal, slider, handle);
661
+ // Parse the syntactic sugar that is the serialization
662
+ // resolution option to a usable integer.
663
+ style = options.orientation === 'vertical' ? 'top' : 'left';
664
+
665
+ decimals = options.S.resolution.toString().split('.');
666
+ // Test ==, not ===
667
+ decimals = decimals[0] == 1 ? 0 : decimals[1].length;
668
+
669
+ // Add classes for horizontal and vertical sliders.
670
+ // The horizontal class is provided for completeness,
671
+ // as it isn't used in the default theme.
672
+ if( options.orientation === "vertical" ){
673
+ cls.base.push(clsList[10]);
674
+ } else {
675
+ cls.base.push(clsList[11]);
676
+ }
677
+
678
+ // Merge base classes with default;
679
+ base.addClass(cls.base.join(" ")).data('target', target);
680
+
681
+ for (i = 0; i < options.handles; i++ ) {
682
+
683
+ handle = $('<div><div/></i>').appendTo(base);
684
+
685
+ // Add all default and option-specific classes to the
686
+ // origins and handles.
687
+ handle.addClass(cls.origin[i].join(" "));
688
+ handle.children().addClass(cls.handle[i].join(" "));
689
+
690
+ // These events are only bound to the visual handle element,
691
+ // not the `real` origin element.
692
+ handle.children()
693
+ .on(actions.start, { base: base, handle: handle }, start)
694
+ .on(actions.end, { base: base, handle: handle }, selfEnd);
695
+
696
+ // Make sure every handle has access to all primary
697
+ // variables. Can't uses jQuery's .data( obj ) structure
698
+ // here, as `store` needs some values from the `nui` object.
699
+ handle.data('nui', {
700
+ target: target
701
+ ,decimals: decimals
702
+ ,options: options
703
+ ,base: base
704
+ ,style: style
705
+ ,number: i
706
+ }).data('store', store (
707
+ handle
708
+ ,options.S
709
+ ));
710
+
711
+ // Attach a function to the native DOM element,
712
+ // since jQuery wont let me get the current value in percentages.
713
+ handle[0].getPercentage = getPercentage;
714
+
715
+ // Make handles loop-able
716
+ handles.push(handle);
717
+
718
+ // Set the handle to its initial position;
719
+ setHandle(handle, percentage.to(options.range, options.start[i]));
241
720
 
242
- if (movement[orientation] && proposal != previousProposal) {
243
- handle.css(pos, proposal + '%').data('input').val(percentage.is(settings.range, proposal).toFixed(res));
244
- call(settings.slide, slider.data('_n', true));
245
- previousProposal = proposal;
246
- handle.css('z-index', handles.length == 2 && proposal == 100 && handle.is(':first-child') ? 2 : 1);
247
- }
721
+ }
722
+
723
+ // The base could use the handles too;
724
+ base.data({
725
+ options: options
726
+ ,handles: handles
727
+ ,style: style
728
+ });
729
+
730
+ target.data({
731
+ base: base
732
+ ,handles: handles
733
+ });
734
+
735
+ // The tap event.
736
+ base.on(actions.end, { base: base, handles: handles }, tap);
248
737
 
249
- previousClick = currentClick;
738
+ });
250
739
 
251
- }).on(offEvent, function () {
740
+ }
252
741
 
253
- unbind.off(bind);
254
- $('body').removeClass('TOUCH');
255
- if (slider.find('.active').removeClass('active').end().data('_n')) {
256
- slider.data('_n', false).change();
257
- }
742
+ function val ( args, ignore ) {
258
743
 
259
- });
744
+ // Setter
745
+ if( args !== UNDEF ){
260
746
 
261
- }
262
- }).on('click', function (e) {
263
- e.stopPropagation();
264
- });
747
+ // If the val is to be set to a number, which is valid
748
+ // when using a one-handle slider, wrap it in an array.
749
+ if(!$.isArray(args)){
750
+ args = [args];
751
+ }
752
+
753
+ // Setting is handled properly for each slider in the data set.
754
+ return this.each(function(){
265
755
 
266
- }
756
+ $.each($(this).data(clsList[12]), function(i, handle){
267
757
 
268
- if (EVENT == 1) {
269
- slider.on('click', function (f) {
270
- if (!slider.hasClass('disabled')) {
271
- var currentClick = client(f),
272
- proposal = ((currentClick[orientation] - slider.offset()[pos]) * 100) / (orientation ? slider.height() : slider.width()),
273
- handle = handles.length > 1 ? (currentClick[orientation] < (handles[0].offset()[pos] + handles[1].offset()[pos]) / 2 ? handles[0] : handles[1]) : handles[0];
274
- setHandle(handle, correct(proposal, slider, handle), slider);
275
- call(settings.slide, slider);
276
- slider.change();
758
+ // The set request might want to ignore this handle.
759
+ if( args[i] === null ) {
760
+ return;
761
+ }
762
+
763
+ // Calculate a new position for the handle.
764
+ var value, current
765
+ ,range = handle.data('nui').options.range
766
+ ,to = percentage.to(
767
+ range
768
+ ,parseFloat(args[i])
769
+ ),
770
+
771
+ // Set handle to new location, and make sure developer
772
+ // input is always accepted. The ignore flag indicates
773
+ // input from user facing elements.
774
+ result = setHandle(handle, to, (ignore === true ? false : true));
775
+
776
+ // If the value of the input doesn't match the slider,
777
+ // reset it.
778
+ if(!result){
779
+
780
+ value = handle.data('store').val();
781
+ current = percentage.is(range,
782
+ handle[0].getPercentage(handle.data('nui').style)
783
+ ).toFixed(handle.data('nui').decimals);
784
+
785
+ if(value !== current){
786
+ handle.data('store').val(current);
277
787
  }
278
- });
279
- }
280
-
281
- for (var i = 0; i < handles.length; i++) {
282
- var val = percentage.is(settings.range, place(handles[i], pos)).toFixed(res);
283
- if (typeof settings.serialization.to[i] == 'string') {
284
- handles[i].data('input',
285
- slider.append('<input type="hidden" name="' + settings.serialization.to[i] + '">').find('input:last')
286
- .val(val)
287
- .change(function (a) {
288
- a.stopPropagation();
289
- }));
290
- } else if (settings.serialization.to[i] == false) {
291
- handles[i].data('input', {
292
- val : function (a) {
293
- if (typeof a != 'undefined') {
294
- this.handle.data('noUiVal', a);
295
- } else {
296
- return this.handle.data('noUiVal');
297
- }
298
- },
299
- handle : handles[i]
300
- });
301
- } else {
302
- handles[i].data('input', settings.serialization.to[i].data('handleNR', i).val(val).change(function () {
303
- var arr = [null, null];
304
- arr[$(this).data('handleNR')] = $(this).val();
305
- slider.val(arr);
306
- }));
307
788
  }
308
- }
309
-
310
- $(this).data('setup', {
311
- settings : settings,
312
- handles : handles,
313
- pos : pos,
314
- res : res
789
+
315
790
  });
316
791
 
317
792
  });
318
- },
319
- val : function () {
320
-
321
- if (typeof arguments[0] !== 'undefined') {
322
-
323
- var val = typeof arguments[0] == 'number' ? [arguments[0]] : arguments[0];
324
-
325
- return this.each(function () {
326
793
 
327
- var setup = $(this).data('setup');
794
+ }
328
795
 
329
- for (var i = 0; i < setup.handles.length; i++) {
330
- if (val[i] != null) {
331
- var proposal = correct(percentage.to(setup.settings.range, val[i]), $(this), setup.handles[i]);
332
- setup.handles[i].css(setup.pos, proposal + '%').data('input').val(percentage.is(setup.settings.range, proposal).toFixed(setup.res));
333
- }
334
- }
335
- });
796
+ // Or, if the function was called without arguments,
797
+ // act as a 'getter';
798
+
799
+ var re = [];
336
800
 
337
- } else {
801
+ // Loop the handles, and get the value from the input
802
+ // for every handle on its' own.
803
+ $.each($(this).data(clsList[12]), function(i, handle){
804
+ re.push( handle.data('store').val() );
805
+ });
338
806
 
339
- var handles = $(this).data('setup').handles,
340
- re = [];
341
- for (var i = 0; i < handles.length; i++) {
342
- re.push(parseFloat(handles[i].data('input').val()));
343
- }
344
- return re.length == 1 ? re[0] : re;
807
+ // If the slider has just one handle, return a single value.
808
+ // Otherwise, return an array.
809
+ return ( re.length === 1 ? re[0] : re) ;
345
810
 
346
- }
347
- },
348
- disabled : function () {
349
- return flag ? $(this).addClass('disabled') : $(this).removeClass('disabled');
350
- }
351
811
  }
352
812
 
353
- // remap the native/current val function to noUiSlider
354
- var $_val = jQuery.fn.val;
355
-
356
- jQuery.fn.val = function () {
357
- return this.data('_isnS_') ? methods.val.apply(this, arguments) : $_val.apply(this, arguments);
358
- }
813
+ // Overwrite the native jQuery val() function
814
+ // with a simple handler. noUiSlider will use the internal
815
+ // value method, anything else will use the standard method.
816
+ $.fn.val = function(){
817
+ return this.hasClass(clsList[6])
818
+ ? val.apply(this, arguments)
819
+ : $VAL.apply(this, arguments);
820
+ };
359
821
 
360
- return options == "disabled" ? methods.disabled.apply(this) : methods.create.apply(this);
822
+ return create.apply(this, arguments);
361
823
 
362
- }
824
+ };
363
825
 
364
- })(jQuery);
826
+ }(jQuery));