提交 | 用户 | 时间
|
58d006
|
1 |
/* noUiSlider - refreshless.com/nouislider/ */ |
A |
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 |
|
14 |
} |
|
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 |
// Define an extendible object with base classes for the various |
|
37 |
// structure elements in the slider. These can be extended by simply |
|
38 |
// pushing to the array, which reduces '.addClass()' calls. |
|
39 |
,stdCls = { |
|
40 |
base: [clsList[0], clsList[13]] |
|
41 |
,origin: [clsList[1]] |
|
42 |
,handle: [clsList[2]] |
|
43 |
} |
|
44 |
// The percentage object contains some well tested math to turn values |
|
45 |
// to and from percentages. It can be a bit strange to wrap your head |
|
46 |
// around the individual calls, but they'll do their job with all positive |
|
47 |
// and negative input values. |
|
48 |
,percentage = { |
|
49 |
to: function ( range, value ) { |
|
50 |
value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0]; |
|
51 |
return (value * 100) / this.len(range); |
|
52 |
} |
|
53 |
,from: function ( range, value ) { |
|
54 |
return (value * 100) / this.len(range); |
|
55 |
} |
|
56 |
,is: function ( range, value ) { |
|
57 |
return ((value * this.len(range)) / 100) + range[0]; |
|
58 |
} |
|
59 |
,len: function ( range ) { |
|
60 |
return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]); |
|
61 |
} |
|
62 |
}; |
|
63 |
|
|
64 |
// When the browser supports MsPointerEvents, |
|
65 |
// Don't bind touch or mouse events. The touch events are |
|
66 |
// currently only implemented by IE(10), but they are stable |
|
67 |
// and convenient to use. |
|
68 |
if ( window.navigator.msPointerEnabled ) { |
|
69 |
actions = { |
|
70 |
start: 'MSPointerDown' + namespace |
|
71 |
,move: 'MSPointerMove' + namespace |
|
72 |
,end: 'MSPointerUp' + namespace |
|
73 |
}; |
|
74 |
} |
|
75 |
|
|
76 |
// Shorthand for stopping propagation on an object. |
|
77 |
// Calling a function prevents having to define one inline. |
|
78 |
function stopPropagation ( e ) { |
|
79 |
e.stopPropagation(); |
|
80 |
} |
|
81 |
|
|
82 |
// Test an array of objects, and calls them if they are a function. |
|
83 |
function call ( f, scope, args ) { |
|
84 |
$.each(f,function(i,q){ |
|
85 |
if (typeof q === "function") { |
|
86 |
q.call(scope, args); |
|
87 |
} |
|
88 |
}); |
|
89 |
} |
|
90 |
|
|
91 |
// Test if there is anything that should prevent an event from being |
|
92 |
// handled, such as a disabled state of a slider moving in the 'tap' event. |
|
93 |
function blocked ( e ) { |
|
94 |
return ( e.data.base.data('target').is('[class*="noUi-state-"], [disabled]') ); |
|
95 |
} |
|
96 |
|
|
97 |
function fixEvent ( e, preventDefault ) { |
|
98 |
|
|
99 |
// Required (in at the very least Chrome) to prevent |
|
100 |
// scrolling and panning while attempting to slide. |
|
101 |
// The tap event also depends on this. |
|
102 |
if( preventDefault ) { |
|
103 |
e.preventDefault(); |
|
104 |
} |
|
105 |
|
|
106 |
// Filter the event to register the type, |
|
107 |
// which can be touch, mouse or pointer. Since noUiSlider 4 |
|
108 |
// so longer binds touch OR mouse, but rather touch AND mouse, |
|
109 |
// offset changes need to be made on an event specific basis. |
|
110 |
var jQueryEvent = e |
|
111 |
,touch = e.type.indexOf('touch') === 0 |
|
112 |
,mouse = e.type.indexOf('mouse') === 0 |
|
113 |
,pointer = e.type.indexOf('MSPointer') === 0 |
|
114 |
,x,y; |
|
115 |
|
|
116 |
// Fetch the event where jQuery didn't make any modifications. |
|
117 |
e = e.originalEvent; |
|
118 |
|
|
119 |
if (touch) { |
|
120 |
// noUiSlider supports one movement at a time, for now. |
|
121 |
// It is therefore safe to select the first 'changedTouch'. |
|
122 |
x = e.changedTouches[0].pageX; |
|
123 |
y = e.changedTouches[0].pageY; |
|
124 |
} |
|
125 |
if (mouse) { |
|
126 |
|
|
127 |
// Polyfill the pageXOffset and pageYOffset |
|
128 |
// variables for IE7 and IE8; |
|
129 |
if(window.pageXOffset === UNDEF){ |
|
130 |
window.pageXOffset = document.documentElement.scrollLeft; |
|
131 |
window.pageYOffset = document.documentElement.scrollTop; |
|
132 |
} |
|
133 |
|
|
134 |
x = e.clientX + window.pageXOffset; |
|
135 |
y = e.clientY + window.pageYOffset; |
|
136 |
} |
|
137 |
if (pointer) { |
|
138 |
x = e.pageX; |
|
139 |
y = e.pageY; |
|
140 |
} |
|
141 |
|
|
142 |
return { pass: jQueryEvent.data, e:e, x:x, y:y, t: [touch, mouse, pointer] }; |
|
143 |
|
|
144 |
} |
|
145 |
|
|
146 |
function getPercentage( a ){ |
|
147 |
return parseFloat(this.style[a]); |
|
148 |
} |
|
149 |
|
|
150 |
function test ( o, set ){ |
|
151 |
|
|
152 |
// Checks whether a variable is numerical. |
|
153 |
function num(e){ |
|
154 |
return !isNaN(e) && isFinite(e); |
|
155 |
} |
|
156 |
// Checks whether a variable is a candidate to be a |
|
157 |
// valid serialization target. |
|
158 |
function ser(r){ |
|
159 |
return ( r instanceof $ || typeof r === 'string' || r === false ); |
|
160 |
} |
|
161 |
|
|
162 |
|
|
163 |
/** |
|
164 |
These tests are structured with an item for every option available. |
|
165 |
Every item contains an 'r' flag, which marks a required option, and |
|
166 |
a 't' function, which in turn takes some arguments: |
|
167 |
- a reference to options object |
|
168 |
- the value for the option |
|
169 |
- the option name (optional); |
|
170 |
The testing function returns false when an error is detected, |
|
171 |
or true when everything is OK. Every test also has an 'init' |
|
172 |
method which appends the parent object to all children. |
|
173 |
**/ |
|
174 |
var TESTS = { |
|
175 |
/* Handles. Has default, can be 1 or 2; |
|
176 |
*/ |
|
177 |
"handles": { |
|
178 |
r: true |
|
179 |
,t: function(o,q){ |
|
180 |
q = parseInt(q, 10); |
|
181 |
return ( q === 1 || q === 2 ); |
|
182 |
} |
|
183 |
} |
|
184 |
/* Range. |
|
185 |
* Must be an array of two numerical floats, |
|
186 |
* which can't be identical. |
|
187 |
*/ |
|
188 |
,"range": { |
|
189 |
r: true |
|
190 |
,t: function(o,q,w){ |
|
191 |
if(q.length!==2){ |
|
192 |
return false; |
|
193 |
} |
|
194 |
// Reset the array to floats |
|
195 |
q = [parseFloat(q[0]),parseFloat(q[1])]; |
|
196 |
// Test if those floats are numerical |
|
197 |
if(!num(q[0])||!num(q[1])){ |
|
198 |
return false; |
|
199 |
} |
|
200 |
// When this test is run for range, the values can't |
|
201 |
// be identical. |
|
202 |
if(w==="range" && q[0] === q[1]){ |
|
203 |
return false; |
|
204 |
} |
|
205 |
o[w]=q; |
|
206 |
return true; |
|
207 |
} |
|
208 |
} |
|
209 |
/* Start. |
|
210 |
* Must be an array of two numerical floats when handles = 2; |
|
211 |
* Uses 'range' test. |
|
212 |
* When handles = 1, a single float is also allowed. |
|
213 |
*/ |
|
214 |
,"start": { |
|
215 |
r: true |
|
216 |
,t: function(o,q,w){ |
|
217 |
if(o.handles === 1){ |
|
218 |
if($.isArray(q)){ |
|
219 |
q=q[0]; |
|
220 |
} |
|
221 |
q = parseFloat(q); |
|
222 |
o.start = [q]; |
|
223 |
return num(q); |
|
224 |
} |
|
225 |
return this.parent.range.t(o,q,w); |
|
226 |
} |
|
227 |
} |
|
228 |
/* Connect. |
|
229 |
* Must be true or false when handles = 2; |
|
230 |
* Can use 'lower' and 'upper' when handles = 1. |
|
231 |
*/ |
|
232 |
,"connect": { |
|
233 |
t: function(o,q){ |
|
234 |
return ( q === true |
|
235 |
|| q === false |
|
236 |
|| ( q === 'lower' && o.handles === 1) |
|
237 |
|| ( q === 'upper' && o.handles === 1)); |
|
238 |
} |
|
239 |
} |
|
240 |
/* Connect. |
|
241 |
* Will default to horizontal, not required. |
|
242 |
*/ |
|
243 |
,"orientation": { |
|
244 |
t: function(o,q){ |
|
245 |
return ( q === "horizontal" || q === "vertical" ); |
|
246 |
} |
|
247 |
} |
|
248 |
/* Margin. |
|
249 |
* Must be a float, has a default value. |
|
250 |
*/ |
|
251 |
,"margin": { |
|
252 |
r: true |
|
253 |
,t: function(o,q,w){ |
|
254 |
q = parseFloat(q); |
|
255 |
o[w]=q; |
|
256 |
return num(q); |
|
257 |
} |
|
258 |
} |
|
259 |
/* Serialization. |
|
260 |
* Required, but has default. Resolution option can be missing, |
|
261 |
* 'to' can't. Must be an array when using two handles, can |
|
262 |
* be a singular value when using one handle. |
|
263 |
*/ |
|
264 |
,"serialization": { |
|
265 |
r: true |
|
266 |
,t: function(o,q){ |
|
267 |
|
|
268 |
if(!q.resolution){ |
|
269 |
o.serialization.resolution = 0.01; |
|
270 |
} else { |
|
271 |
switch(q.resolution){ |
|
272 |
case 1: |
|
273 |
case 0.1: |
|
274 |
case 0.01: |
|
275 |
case 0.001: |
|
276 |
case 0.0001: |
|
277 |
case 0.00001: |
|
278 |
break; |
|
279 |
default: |
|
280 |
return false; |
|
281 |
} |
|
282 |
} |
|
283 |
|
|
284 |
if(q.to){ |
|
285 |
|
|
286 |
if(o.handles === 1){ |
|
287 |
// Wrap the value for one handle into an array; |
|
288 |
if(!$.isArray(q.to)){ |
|
289 |
q.to = [q.to]; |
|
290 |
} |
|
291 |
// Write back to the options object; |
|
292 |
o.serialization.to = q.to; |
|
293 |
// Run test for valid serialization target. |
|
294 |
return ser(q.to[0]); |
|
295 |
} |
|
296 |
return (q.to.length === 2 && ser(q.to[0]) && ser(q.to[1])); |
|
297 |
|
|
298 |
} |
|
299 |
|
|
300 |
// If no 'to' option is specified, |
|
301 |
// the serialization option is invalid. |
|
302 |
return false; |
|
303 |
|
|
304 |
} |
|
305 |
} |
|
306 |
/* Slide. |
|
307 |
* Not required. Must be a function. |
|
308 |
*/ |
|
309 |
,"slide": { |
|
310 |
t: function(o,q){ |
|
311 |
return typeof q === "function"; |
|
312 |
} |
|
313 |
} |
|
314 |
/* Slide. |
|
315 |
* Not required. Tested using the 'margin' function. |
|
316 |
*/ |
|
317 |
,"step": { |
|
318 |
t: function(o,q,w){ |
|
319 |
return this.parent.margin.t(o,q,w); |
|
320 |
} |
|
321 |
} |
|
322 |
/* [init] |
|
323 |
* Not an option test. Calling this method will return the |
|
324 |
* parent object with some cross references that allow crawling |
|
325 |
* the object upward, which normally isn't possible in Javascript. |
|
326 |
*/ |
|
327 |
,"init": function(){ |
|
328 |
var obj = this; |
|
329 |
$.each(obj,function(i,c){ |
|
330 |
c.parent = obj; |
|
331 |
}); |
|
332 |
delete this.init; |
|
333 |
return this; |
|
334 |
} |
|
335 |
}, |
|
336 |
|
|
337 |
// Prepare a set of tests, by adding some internal reference |
|
338 |
// values not available in native Javascript object implementation. |
|
339 |
a = TESTS.init(); |
|
340 |
|
|
341 |
// Loop all provided tests; |
|
342 |
// v is the option set, i is the index for the current test. |
|
343 |
$.each(a, function( i, v ){ |
|
344 |
|
|
345 |
// If the value is required but not set, |
|
346 |
// or if the test fails, throw an error. |
|
347 |
if((v.r && (!o[i] && o[i] !== 0)) || ((o[i] || o[i] === 0) && !v.t(o,o[i],i))){ |
|
348 |
|
|
349 |
// For debugging purposes it might be very useful |
|
350 |
// to know what option caused the trouble. |
|
351 |
if(console&&console.log){ |
|
352 |
console.log( |
|
353 |
"Slider:\t\t\t", set, |
|
354 |
"\nOption:\t\t\t", i, |
|
355 |
"\nValue:\t\t\t", o[i] |
|
356 |
); |
|
357 |
} |
|
358 |
// Since 'error' will prevent further script execution, |
|
359 |
// log the error first. |
|
360 |
$.error("Error on noUiSlider initialisation."); |
|
361 |
return false; |
|
362 |
} |
|
363 |
|
|
364 |
}); |
|
365 |
|
|
366 |
} |
|
367 |
|
|
368 |
function closest( value, to ){ |
|
369 |
// Round a value to the closest 'to'. |
|
370 |
// Used with the 'step' option. |
|
371 |
return Math.round(value / to) * to; |
|
372 |
} |
|
373 |
|
|
374 |
function setHandle ( handle, to, forgive ) { |
|
375 |
|
|
376 |
var nui = handle.data('nui').options |
|
377 |
// Get the array of handles from the base. |
|
378 |
// Will be undefined at initialisation. |
|
379 |
,handles = handle.data('nui').base.data(clsList[12]) |
|
380 |
// Get some settings from the handle; |
|
381 |
,style = handle.data('nui').style |
|
382 |
,dec = handle.data('nui').decimals |
|
383 |
,hLimit; |
|
384 |
|
|
385 |
// Ignore the call if the handle won't move anyway. |
|
386 |
if(to === handle[0].getPercentage(style)) { |
|
387 |
return false; |
|
388 |
} |
|
389 |
|
|
390 |
// Limit 'to' to 0 - 100 |
|
391 |
to = to < 0 ? 0 : to > 100 ? 100 : to; |
|
392 |
|
|
393 |
// Handle the step option, or ignore it. |
|
394 |
if( nui.step && !forgive ){ |
|
395 |
to = closest( to, percentage.from(nui.range, nui.step)); |
|
396 |
} |
|
397 |
|
|
398 |
// Stop handling this call if the handle won't step to a new value. |
|
399 |
if(to === handle[0].getPercentage(style)) { |
|
400 |
return false; |
|
401 |
} |
|
402 |
|
|
403 |
// We're done if this is the only handle, |
|
404 |
// if the handle bounce is trusted to the user |
|
405 |
// or on initialisation when handles isn't defined yet. |
|
406 |
if( handle.siblings('.' + clsList[1]).length && !forgive && handles ){ |
|
407 |
|
|
408 |
// Otherwise, the handle should bounce, |
|
409 |
// and stop at the other handle. |
|
410 |
if ( handle.data('nui').number ) { |
|
411 |
hLimit = handles[0][0].getPercentage(style) + nui.margin; |
|
412 |
to = to < hLimit ? hLimit : to; |
|
413 |
} else { |
|
414 |
hLimit = handles[1][0].getPercentage(style) - nui.margin; |
|
415 |
to = to > hLimit ? hLimit : to; |
|
416 |
} |
|
417 |
|
|
418 |
// Stop handling this call if the handle can't move past another. |
|
419 |
if(to === handle[0].getPercentage(style)) { |
|
420 |
return false; |
|
421 |
} |
|
422 |
|
|
423 |
} |
|
424 |
|
|
425 |
// Fix for the z-index issue where the lower handle gets stuck |
|
426 |
// below the upper one. Since this function is called for every |
|
427 |
// movement, toggleClass cannot be used. |
|
428 |
if(handle.data('nui').number === 0 && to > 95){ |
|
429 |
handle.addClass(clsList[14]); |
|
430 |
} else { |
|
431 |
handle.removeClass(clsList[14]); |
|
432 |
} |
|
433 |
|
|
434 |
// Set handle to new location |
|
435 |
handle.css( style , to + '%'); |
|
436 |
|
|
437 |
// Write the value to the serialization object. |
|
438 |
handle.data('store').val(percentage.is(nui.range, to).toFixed(dec)); |
|
439 |
|
|
440 |
return true; |
|
441 |
|
|
442 |
} |
|
443 |
|
|
444 |
function store ( handle, S ) { |
|
445 |
|
|
446 |
var i = handle.data('nui').number; |
|
447 |
|
|
448 |
if( S.to[i] instanceof $ ) { |
|
449 |
|
|
450 |
// Attach a change event to the supplied jQuery object, |
|
451 |
// which will just trigger the val function on the parent. |
|
452 |
// In some cases, the change event will not fire on select elements, |
|
453 |
// so listen to 'blur' too. |
|
454 |
return S.to[i].on('change'+namespace+' blur'+namespace, function(){ |
|
455 |
var arr = [null, null]; |
|
456 |
arr[i] = $(this).val(); |
|
457 |
handle.data('nui').target.val(arr, true); |
|
458 |
}); |
|
459 |
|
|
460 |
} |
|
461 |
|
|
462 |
if ( typeof S.to[i] === "string" ) { |
|
463 |
|
|
464 |
// Append a new object to the noUiSlider base, |
|
465 |
// prevent change events flowing upward. |
|
466 |
return $('<input type="hidden" class="'+clsList[3]+'" name="' + S.to[i] + '">') |
|
467 |
.appendTo(handle).change(stopPropagation); |
|
468 |
|
|
469 |
} |
|
470 |
|
|
471 |
if ( S.to[i] === false ) { |
|
472 |
|
|
473 |
// Create an object capable of handling all jQuery calls. |
|
474 |
return { |
|
475 |
// The value will be stored a data on the handle. |
|
476 |
val : function(a) { |
|
477 |
// Value function provides a getter and a setter. |
|
478 |
// Can't just test for !a, as a might be 0. |
|
479 |
if ( a === UNDEF ) { |
|
480 |
// Either set... |
|
481 |
return this.handleElement.data('nui-val'); |
|
482 |
} |
|
483 |
// ... or return; |
|
484 |
this.handleElement.data('nui-val', a); |
|
485 |
} |
|
486 |
// The object could be mistaken for a jQuery object, |
|
487 |
// make sure that doesn't trigger any errors. |
|
488 |
,hasClass: function(){ |
|
489 |
return false; |
|
490 |
} |
|
491 |
// The val function needs access to the handle. |
|
492 |
,handleElement: handle |
|
493 |
}; |
|
494 |
} |
|
495 |
|
|
496 |
} |
|
497 |
|
|
498 |
function move( event ) { |
|
499 |
|
|
500 |
// This function is called often, keep it light. |
|
501 |
|
|
502 |
event = fixEvent( event, true ); |
|
503 |
|
|
504 |
if(!event) { |
|
505 |
return; |
|
506 |
} |
|
507 |
|
|
508 |
var base = event.pass.base |
|
509 |
,style = base.data('style') |
|
510 |
// Subtract the initial movement from the current event, |
|
511 |
// while taking vertical sliders into account. |
|
512 |
,proposal = event.x - event.pass.startEvent.x |
|
513 |
,baseSize = style === 'left' ? base.width() : base.height(); |
|
514 |
|
|
515 |
// This loop prevents a long ternary for the proposal variable. |
|
516 |
if(style === 'top') { |
|
517 |
proposal = event.y - event.pass.startEvent.y; |
|
518 |
} |
|
519 |
|
|
520 |
proposal = event.pass.position + ( ( proposal * 100 ) / baseSize ); |
|
521 |
|
|
522 |
setHandle( event.pass.handle, proposal ); |
|
523 |
|
|
524 |
// Trigger the 'slide' event, pass the target so that it is 'this'. |
|
525 |
call( |
|
526 |
[ event.pass.base.data('options').slide ] |
|
527 |
,event.pass.base.data('target') |
|
528 |
); |
|
529 |
|
|
530 |
} |
|
531 |
|
|
532 |
function end ( event ) { |
|
533 |
|
|
534 |
if ( blocked( event ) ) { |
|
535 |
return; |
|
536 |
} |
|
537 |
|
|
538 |
// Handle is no longer active; |
|
539 |
event.data.handle.children().removeClass(clsList[4]); |
|
540 |
|
|
541 |
// Unbind move and end events, to prevent |
|
542 |
// them stacking up over and over; |
|
543 |
all.off(actions.move); |
|
544 |
all.off(actions.end); |
|
545 |
$('body').off(namespace); |
|
546 |
|
|
547 |
event.data.base.data('target').change(); |
|
548 |
|
|
549 |
} |
|
550 |
|
|
551 |
function start ( event ) { |
|
552 |
|
|
553 |
// When the slider is in a transitional state, stop. |
|
554 |
// Also prevents interaction with disabled sliders. |
|
555 |
if ( blocked( event ) ) { |
|
556 |
return; |
|
557 |
} |
|
558 |
|
|
559 |
event = fixEvent( event ); |
|
560 |
|
|
561 |
if(!event) { |
|
562 |
return; |
|
563 |
} |
|
564 |
|
|
565 |
var handle = event.pass.handle |
|
566 |
,position = handle[0].getPercentage( handle.data('nui').style ); |
|
567 |
|
|
568 |
handle.children().addClass('noUi-active'); |
|
569 |
|
|
570 |
// Attach the move event handler, while |
|
571 |
// passing all relevant information along. |
|
572 |
all.on(actions.move, { |
|
573 |
startEvent: event |
|
574 |
,position: position |
|
575 |
,base: event.pass.base |
|
576 |
,handle: handle |
|
577 |
}, move); |
|
578 |
|
|
579 |
all.on(actions.end, { base: event.pass.base, handle: handle }, end); |
|
580 |
|
|
581 |
// Prevent text selection when dragging the handles. |
|
582 |
// This doesn't prevent the browser defaulting to the I like cursor. |
|
583 |
$('body').on('selectstart' + namespace, function(){ return false; }); |
|
584 |
|
|
585 |
} |
|
586 |
|
|
587 |
function selfEnd( event ) { |
|
588 |
// Trigger the end handler. Supply correct data using a |
|
589 |
// fake object that contains all required information; |
|
590 |
end({ data: { base: event.data.base, handle: event.data.handle } }); |
|
591 |
// Stop propagation so that the tap handler doesn't interfere; |
|
592 |
event.stopPropagation(); |
|
593 |
} |
|
594 |
|
|
595 |
function tap ( event ) { |
|
596 |
|
|
597 |
if ( blocked( event ) || event.data.base.find('.' + clsList[4]).length ) { |
|
598 |
return; |
|
599 |
} |
|
600 |
|
|
601 |
event = fixEvent( event ); |
|
602 |
|
|
603 |
// The event handler might have rejected this event. |
|
604 |
if(!event) { |
|
605 |
return; |
|
606 |
} |
|
607 |
|
|
608 |
// Getting variables from the event is not required, but |
|
609 |
// shortens other expressions and is far more convenient; |
|
610 |
var i, handle, hCenter, base = event.pass.base |
|
611 |
,handles = event.pass.handles |
|
612 |
,style = base.data('style') |
|
613 |
,eventXY = event[style === 'left' ? 'x' : 'y'] |
|
614 |
,baseSize = style === 'left' ? base.width() : base.height() |
|
615 |
|
|
616 |
// Create a standard set off offsets compensated with the |
|
617 |
// scroll distance. When required, correct for scrolling. |
|
618 |
// This is a bug, as far as I can see, in IE(10?). |
|
619 |
,correction = { |
|
620 |
x: ( event.t[2] ? window.pageXOffset : 0 ) |
|
621 |
} |
|
622 |
,offset = { |
|
623 |
handles: [] |
|
624 |
,base: { |
|
625 |
left: base.offset().left - correction.x |
|
626 |
,top: base.offset().top |
|
627 |
} |
|
628 |
}; |
|
629 |
|
|
630 |
// Loop handles and add data to the offset list. |
|
631 |
for (i = 0; i < handles.length; i++ ) { |
|
632 |
offset.handles.push({ |
|
633 |
left: handles[i].offset().left - correction.x |
|
634 |
,top: handles[i].offset().top |
|
635 |
}); |
|
636 |
} |
|
637 |
|
|
638 |
// Calculate the central point between the handles; |
|
639 |
hCenter = handles.length === 1 ? 0 : |
|
640 |
(( offset.handles[0][style] + offset.handles[1][style] ) / 2 ); |
|
641 |
|
|
642 |
// If there is just one handle, |
|
643 |
// or the lower handles in closest to the event, |
|
644 |
// select the first handle. Otherwise, pick the second. |
|
645 |
if ( handles.length === 1 || eventXY < hCenter ){ |
|
646 |
handle = handles[0]; |
|
647 |
} else { |
|
648 |
handle = handles[1]; |
|
649 |
} |
|
650 |
|
|
651 |
// Flag the slider as it is now in a transitional state. |
|
652 |
// Transition takes 300 ms, so re-enable the slider afterwards. |
|
653 |
base.addClass(clsList[5]); |
|
654 |
setTimeout(function(){ |
|
655 |
base.removeClass(clsList[5]); |
|
656 |
}, 300); |
|
657 |
|
|
658 |
// Calculate the new position for the handle and |
|
659 |
// trigger the movement. |
|
660 |
setHandle( |
|
661 |
handle |
|
662 |
,(((eventXY - offset.base[style]) * 100) / baseSize) |
|
663 |
); |
|
664 |
|
|
665 |
// Trigger the 'slide' event, pass the target so that it is 'this'. |
|
666 |
call( |
|
667 |
[ handle.data('nui').options.slide ] |
|
668 |
,base.data('target') |
|
669 |
); |
|
670 |
|
|
671 |
base.data('target').change(); |
|
672 |
|
|
673 |
} |
|
674 |
|
|
675 |
function create ( ) { |
|
676 |
|
|
677 |
return this.each(function( index, target ){ |
|
678 |
|
|
679 |
// Target is the wrapper that will receive all external |
|
680 |
// scripting interaction. It has no styling and serves no |
|
681 |
// other function. |
|
682 |
target = $(target); |
|
683 |
target.addClass(clsList[6]); |
|
684 |
|
|
685 |
// Base is the internal main 'bar'. |
|
686 |
var i, style, decimals, handle |
|
687 |
,base = $('<div/>').appendTo(target) |
|
688 |
,handles = [] |
|
689 |
,cls = { |
|
690 |
base: stdCls.base |
|
691 |
,origin: [ |
|
692 |
stdCls.origin.concat([clsList[1] + clsList[7]]) |
|
693 |
,stdCls.origin.concat([clsList[1] + clsList[8]]) |
|
694 |
] |
|
695 |
,handle: [ |
|
696 |
stdCls.handle.concat([clsList[2] + clsList[7]]) |
|
697 |
,stdCls.handle.concat([clsList[2] + clsList[8]]) |
|
698 |
] |
|
699 |
}; |
|
700 |
|
|
701 |
// Set defaults where applicable; |
|
702 |
options = $.extend({ |
|
703 |
handles: 2 |
|
704 |
,margin: 0 |
|
705 |
,orientation: "horizontal" |
|
706 |
}, options) || {}; |
|
707 |
|
|
708 |
// Set a default for serialization; |
|
709 |
if(!options.serialization){ |
|
710 |
options.serialization = { |
|
711 |
to : [false, false] |
|
712 |
,resolution : 0.01 |
|
713 |
}; |
|
714 |
} |
|
715 |
|
|
716 |
// Run all options through a testing mechanism to ensure correct |
|
717 |
// input. The test function will throw errors, so there is |
|
718 |
// no need to capture the result of this call. It should be noted |
|
719 |
// that options might get modified to be handled properly. E.g. |
|
720 |
// wrapping integers in arrays. |
|
721 |
test(options, target); |
|
722 |
|
|
723 |
// I can't type serialization any more, and it doesn't compress |
|
724 |
// very well, so shorten it. |
|
725 |
options.S = options.serialization; |
|
726 |
|
|
727 |
|
|
728 |
// INCOMPLETE |
|
729 |
if( options.connect ) { |
|
730 |
cls.origin[0].push(clsList[9]); |
|
731 |
if( options.connect === "lower" ){ |
|
732 |
// Add some styling classes to the base; |
|
733 |
cls.base.push(clsList[9], clsList[9] + clsList[7]); |
|
734 |
// When using the option 'Lower', there is only one |
|
735 |
// handle, and thus only one origin. |
|
736 |
cls.origin[0].push(clsList[13]); |
|
737 |
} else { |
|
738 |
cls.base.push(clsList[9] + clsList[8]); |
|
739 |
} |
|
740 |
} |
|
741 |
|
|
742 |
// Parse the syntactic sugar that is the serialization |
|
743 |
// resolution option to a usable integer. |
|
744 |
style = options.orientation === 'vertical' ? 'top' : 'left'; |
|
745 |
|
|
746 |
decimals = options.S.resolution.toString().split('.'); |
|
747 |
|
|
748 |
// Checking for a string "1", since the resolution needs |
|
749 |
// to be cast to a string to split in on the period. |
|
750 |
decimals = decimals[0] === "1" ? 0 : decimals[1].length; |
|
751 |
|
|
752 |
// Add classes for horizontal and vertical sliders. |
|
753 |
// The horizontal class is provided for completeness, |
|
754 |
// as it isn't used in the default theme. |
|
755 |
if( options.orientation === "vertical" ){ |
|
756 |
cls.base.push(clsList[10]); |
|
757 |
} else { |
|
758 |
cls.base.push(clsList[11]); |
|
759 |
} |
|
760 |
|
|
761 |
// Merge base classes with default; |
|
762 |
base.addClass(cls.base.join(" ")).data('target', target); |
|
763 |
|
|
764 |
for (i = 0; i < options.handles; i++ ) { |
|
765 |
|
|
766 |
handle = $('<div><div/></i>').appendTo(base); |
|
767 |
|
|
768 |
// Add all default and option-specific classes to the |
|
769 |
// origins and handles. |
|
770 |
handle.addClass(cls.origin[i].join(" ")); |
|
771 |
handle.children().addClass(cls.handle[i].join(" ")); |
|
772 |
|
|
773 |
// These events are only bound to the visual handle element, |
|
774 |
// not the 'real' origin element. |
|
775 |
handle.children() |
|
776 |
.on(actions.start, { base: base, handle: handle }, start) |
|
777 |
.on(actions.end, { base: base, handle: handle }, selfEnd); |
|
778 |
|
|
779 |
// Make sure every handle has access to all primary |
|
780 |
// variables. Can't uses jQuery's .data( obj ) structure |
|
781 |
// here, as 'store' needs some values from the 'nui' object. |
|
782 |
handle.data('nui', { |
|
783 |
target: target |
|
784 |
,decimals: decimals |
|
785 |
,options: options |
|
786 |
,base: base |
|
787 |
,style: style |
|
788 |
,number: i |
|
789 |
}).data('store', store ( |
|
790 |
handle |
|
791 |
,options.S |
|
792 |
)); |
|
793 |
|
|
794 |
// Attach a function to the native DOM element, |
|
795 |
// since jQuery wont let me get the current value in percentages. |
|
796 |
handle[0].getPercentage = getPercentage; |
|
797 |
|
|
798 |
// Make handles loop-able |
|
799 |
handles.push(handle); |
|
800 |
|
|
801 |
// Set the handle to its initial position; |
|
802 |
setHandle(handle, percentage.to(options.range, options.start[i])); |
|
803 |
|
|
804 |
} |
|
805 |
|
|
806 |
// The base could use the handles too; |
|
807 |
base.data({ |
|
808 |
options: options |
|
809 |
,handles: handles |
|
810 |
,style: style |
|
811 |
}); |
|
812 |
|
|
813 |
// Add a downstream reference to the target as well. |
|
814 |
target.data({ |
|
815 |
base: base |
|
816 |
,handles: handles |
|
817 |
}); |
|
818 |
|
|
819 |
// The tap event. |
|
820 |
base.on(actions.end, { base: base, handles: handles }, tap); |
|
821 |
|
|
822 |
}); |
|
823 |
|
|
824 |
} |
|
825 |
|
|
826 |
function val ( args, ignore ) { |
|
827 |
|
|
828 |
// Setter |
|
829 |
if( args !== UNDEF ){ |
|
830 |
|
|
831 |
// If the val is to be set to a number, which is valid |
|
832 |
// when using a one-handle slider, wrap it in an array. |
|
833 |
if(!$.isArray(args)){ |
|
834 |
args = [args]; |
|
835 |
} |
|
836 |
|
|
837 |
// Setting is handled properly for each slider in the data set. |
|
838 |
return this.each(function(){ |
|
839 |
|
|
840 |
$.each($(this).data(clsList[12]), function(i, handle){ |
|
841 |
|
|
842 |
// The set request might want to ignore this handle. |
|
843 |
if( args[i] === null ) { |
|
844 |
return; |
|
845 |
} |
|
846 |
|
|
847 |
// Calculate a new position for the handle. |
|
848 |
var value, current |
|
849 |
,range = handle.data('nui').options.range |
|
850 |
,to = percentage.to( |
|
851 |
range |
|
852 |
,parseFloat(args[i]) |
|
853 |
), |
|
854 |
|
|
855 |
// Set handle to new location, and make sure developer |
|
856 |
// input is always accepted. The ignore flag indicates |
|
857 |
// input from user facing elements. |
|
858 |
result = setHandle(handle, to, (ignore === true ? false : true)); |
|
859 |
|
|
860 |
// If the value of the input doesn't match the slider, |
|
861 |
// reset it. |
|
862 |
if(!result){ |
|
863 |
|
|
864 |
// Get the 'store' object, which can be an input element |
|
865 |
// or a wrapper arround a 'data' call. |
|
866 |
value = handle.data('store').val(); |
|
867 |
current = percentage.is(range, |
|
868 |
handle[0].getPercentage(handle.data('nui').style) |
|
869 |
).toFixed(handle.data('nui').decimals); |
|
870 |
|
|
871 |
// Sometimes the input is changed to a value the slider |
|
872 |
// has rejected. This can occur when using 'select' or |
|
873 |
// 'input[type="number"]' elements. In this case, |
|
874 |
// set the value back to the input. |
|
875 |
if(value !== current){ |
|
876 |
handle.data('store').val(current); |
|
877 |
} |
|
878 |
} |
|
879 |
|
|
880 |
}); |
|
881 |
|
|
882 |
}); |
|
883 |
|
|
884 |
} |
|
885 |
|
|
886 |
// Or, if the function was called without arguments, |
|
887 |
// act as a 'getter'; |
|
888 |
|
|
889 |
var re = []; |
|
890 |
|
|
891 |
// Loop the handles, and get the value from the input |
|
892 |
// for every handle on its' own. |
|
893 |
$.each($(this).data(clsList[12]), function(i, handle){ |
|
894 |
re.push( handle.data('store').val() ); |
|
895 |
}); |
|
896 |
|
|
897 |
// If the slider has just one handle, return a single value. |
|
898 |
// Otherwise, return an array. |
|
899 |
return ( re.length === 1 ? re[0] : re) ; |
|
900 |
|
|
901 |
} |
|
902 |
|
|
903 |
// Overwrite the native jQuery val() function |
|
904 |
// with a simple handler. noUiSlider will use the internal |
|
905 |
// value method, anything else will use the standard method. |
|
906 |
$.fn.val = function(){ |
|
907 |
return this.hasClass(clsList[6]) |
|
908 |
? val.apply(this, arguments) |
|
909 |
: $VAL.apply(this, arguments); |
|
910 |
}; |
|
911 |
|
|
912 |
return create.apply(this, arguments); |
|
913 |
|
|
914 |
}; |
|
915 |
|
|
916 |
}(jQuery)); |