4 $.fn.noUiSlider = function (options, flag) {
6 // test for mouse, pointer or touch
7 var EVENT = window.navigator.msPointerEnabled ? 2 : 'ontouchend' in document ? 3 : 1;
8 if (window.debug && console) {
12 // shorthand for test=function, calling
13 function call(f, scope, args) {
14 if (typeof f === "function") {
19 // function wrapper for calculating to and from range values
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);
25 from : function (range, value) {
26 return (value * 100) / this._length(range);
28 is : function (range, value) {
29 return ((value * this._length(range)) / 100) + range[0];
31 _length : function (range) {
32 return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]);
36 // bounce handles of eachother, the edges of the slider
37 function correct(proposal, slider, handle) {
40 setup = slider.data('setup'),
41 handles = setup.handles,
42 settings = setup.settings,
45 proposal = proposal < 0 ? 0 : proposal > 100 ? 100 : proposal;
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;
52 var other = parseFloat(handles[0][0].style[pos]) + settings.margin;
53 proposal = proposal < other ? other : proposal;
58 var per = percentage.from(settings.range, settings.step);
59 proposal = Math.round(proposal / per) * per;
66 // get standarised clientX and clientY
69 return [(f.clientX || f.originalEvent.clientX || f.originalEvent.touches[0].clientX), (f.clientY || f.originalEvent.clientY || f.originalEvent.touches[0].clientY)];
75 // get native inline style value in %
76 function place(handle, pos) {
77 return parseFloat(handle[0].style[pos]);
80 // simplified defaults
89 // contains all methods
91 create : function () {
93 return this.each(function () {
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));
101 settings = $.extend(defaults, options),
103 handlehtml = '<a><div></div></a>',
104 // save this to variable, // allows identification
105 slider = $(this).data('_isnS_', true),
108 // the way the handles are positioned for this slider, top/left
110 // for quick orientation testing and array matching
116 return !isNaN(parseFloat(e)) && isFinite(e);
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;
122 settings.start = num(settings.start) ? [settings.start, 0] : settings.start;
124 // logs bad input values, if possible
125 $.each(settings, function (a, b) {
128 settings[a] = parseFloat(b);
129 } else if (typeof b == "object" && num(b[0])) {
130 b[0] = parseFloat(b[0]);
132 b[1] = parseFloat(b[1]);
137 b = typeof b == "undefined" ? "x" : b;
142 e = b.length != 2 || !num(b[0]) || !num(b[1]);
145 e = (b < 1 || b > 2 || !num(b));
148 e = b != "lower" && b != "upper" && typeof b != "boolean";
151 e = (b != "vertical" && b != "horizontal");
155 e = typeof b != "undefined" && !num(b);
157 case 'serialization':
158 e = typeof b != "object" || !num(b.resolution) || (typeof b.to == 'object' && b.to.length < settings.handles);
161 e = typeof b != "function";
166 console.error('Bad input for ' + a + ' on slider:', slider);
171 settings.margin = settings.margin ? percentage.from(settings.range, settings.margin) : 0;
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];
178 if (settings.orientation == "vertical") {
179 classes += "vertical";
183 classes += "horizontal";
188 classes += settings.connect ? settings.connect == "lower" ? " connect lower" : " connect" : "";
190 slider.addClass(classes);
192 for (var i = 0; i < settings.handles; i++) {
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);
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
206 handles[i].find('div').on(onEvent, function (e) {
208 $('body').bind('selectstart' + bind, function () {
212 if (!slider.hasClass('disabled')) {
214 $('body').addClass('TOUCH');
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;
223 $(document).on(moveEvent, function (f) {
227 var currentClick = client(f);
229 if (currentClick[0] == "x") {
233 currentClick[0] -= originalClick[0];
234 currentClick[1] -= originalClick[1];
237 previousClick[0] != currentClick[0], previousClick[1] != currentClick[1]
239 proposal = originalPosition + ((currentClick[orientation] * 100) / (orientation ? slider.height() : slider.width()));
240 proposal = correct(proposal, slider, handle);
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);
249 previousClick = currentClick;
251 }).on(offEvent, function () {
254 $('body').removeClass('TOUCH');
255 if (slider.find('.active').removeClass('active').end().data('_n')) {
256 slider.data('_n', false).change();
262 }).on('click', function (e) {
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);
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')
287 .change(function (a) {
290 } else if (settings.serialization.to[i] == false) {
291 handles[i].data('input', {
293 if (typeof a != 'undefined') {
294 this.handle.data('noUiVal', a);
296 return this.handle.data('noUiVal');
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();
310 $(this).data('setup', {
321 if (typeof arguments[0] !== 'undefined') {
323 var val = typeof arguments[0] == 'number' ? [arguments[0]] : arguments[0];
325 return this.each(function () {
327 var setup = $(this).data('setup');
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));
339 var handles = $(this).data('setup').handles,
341 for (var i = 0; i < handles.length; i++) {
342 re.push(parseFloat(handles[i].data('input').val()));
344 return re.length == 1 ? re[0] : re;
348 disabled : function () {
349 return flag ? $(this).addClass('disabled') : $(this).removeClass('disabled');
353 // remap the native/current val function to noUiSlider
354 var $_val = jQuery.fn.val;
356 jQuery.fn.val = function () {
357 return this.data('_isnS_') ? methods.val.apply(this, arguments) : $_val.apply(this, arguments);
360 return options == "disabled" ? methods.disabled.apply(this) : methods.create.apply(this);