Socket
Socket
Sign inDemoInstall

nouislider-browser

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nouislider-browser - npm Package Compare versions

Comparing version 4.2.2-1 to 5.0.0-1

1978

jquery.nouislider.js

@@ -1,123 +0,134 @@

/** noUiSlider
** @author: Léon Gersen
** @documentation: http://refreshless.com/nouislider/
**/
/*jslint browser: true, devel: true, plusplus: true, white: true, unparam: true */
(function( $, undefined ){
/*! $.noUiSlider
@version 5.0.0
@author Leon Gersen https://twitter.com/LeonGersen
@license WTFPL http://www.wtfpl.net/about/
@documentation http://refreshless.com/nouislider/
*/
"use strict";
// ==ClosureCompiler==
// @externs_url http://refreshless.com/externs/jquery-1.8.js
// @compilation_level ADVANCED_OPTIMIZATIONS
// @warning_level VERBOSE
// ==/ClosureCompiler==
if ( $.zepto && !$.fn.removeData ) {
throw new ReferenceError("Zepto is loaded without the data module.");
/*jshint laxcomma: true */
/*jshint smarttabs: true */
/*jshint sub: true */
/*jslint browser: true */
/*jslint continue: true */
/*jslint plusplus: true */
/*jslint white: true */
/*jslint sub: true */
(function( $ ){
'use strict';
if ( $['zepto'] && !$.fn.removeData ) {
throw new ReferenceError('Zepto is loaded without the data module.');
}
$.fn.noUiSlider = function( options ){
$.fn['noUiSlider'] = function( options, rebuild ){
var namespace = '.nui'
// Create a shorthand for document event binding.
,all = $(document)
// Create a map of touch and mouse actions.
,actions = {
start: 'mousedown touchstart'
,move: 'mousemove touchmove'
,end: 'mouseup touchend'
}
// Make a copy of the current 'val' function.
,$VAL = $.fn.val
// Define a set of standard HTML classes for
// the various structures noUiSlider uses.
,clsList = [
'noUi-base' // 0
,'noUi-origin' // 1
,'noUi-handle' // 2
,'noUi-input' // 3
,'noUi-active' // 4
,'noUi-state-tap' // 5
,'noUi-target' // 6
,'-lower' // 7
,'-upper' // 8
,'noUi-connect' // 9
,'noUi-vertical' // 10
,'noUi-horizontal' // 11
,'noUi-background' // 12
,'noUi-z-index' // 13
]
// Define an extendible object with base classes for the various
// structure elements in the slider. These can be extended by simply
// pushing to the array, which reduces '.addClass()' calls.
,stdCls = {
base: [clsList[0]]
,origin: [clsList[1]]
,handle: [clsList[2]]
}
// This object contains some well tested functions to convert
// values to and from percentages. It can be a bit strange to wrap
// your head around the individual calls, but they'll do their job
// with all positive and negative input values.
,percentage = {
to: function ( range, value ) {
value = range[0] < 0 ? value + Math.abs(range[0]) : value - range[0];
return (value * 100) / this.len(range);
}
,from: function ( range, value ) {
return (value * 100) / this.len(range);
}
,is: function ( range, value ) {
return ((value * this.len(range)) / 100) + range[0];
}
,len: function ( range ) {
return (range[0] > range[1] ? range[0] - range[1] : range[1] - range[0]);
}
}
// Event handlers bound to elements to perform basic tasks.
,eventHandlers = [
// Assign input field values to the slider,
// and signal unevaluated input.
function ( ) {
var
// Cache the document and body selectors;
doc = $(document)
,body = $('body')
this.target.val([
!this.i ? this.val() : null
, this.i ? this.val() : null
], { trusted: false });
// Namespace for binding and unbinding slider events;
,namespace = '.nui'
}
// Shorthand for stopping propagation on an object.
// Calling a function prevents having to define
// one within other code.
,function ( e ) {
e.stopPropagation();
}
];
// Copy of the current value function;
,$VAL = $.fn.val
// When the browser supports MsPointerEvents,
// don't bind touch or mouse events. The touch events are
// currently only implemented by IE10, but they are stable
// and convenient to use. IE11 implements pointerEvents without
// Re-usable list of classes;
,clsList = [
/* 0 */ 'noUi-base'
/* 1 */ ,'noUi-origin'
/* 2 */ ,'noUi-handle'
/* 3 */ ,'noUi-input'
/* 4 */ ,'noUi-active'
/* 5 */ ,'noUi-state-tap'
/* 6 */ ,'noUi-target'
/* 7 */ ,'-lower'
/* 8 */ ,'-upper'
/* 9 */ ,'noUi-connect'
/* 10 */ ,'noUi-horizontal'
/* 11 */ ,'noUi-vertical'
/* 12 */ ,'noUi-background'
/* 13 */ ,'noUi-stacking'
/* 14 */ ,'noUi-block'
/* 15 */ ,'noUi-state-blocked'
/* 16 */ ,'noUi-ltr'
/* 17 */ ,'noUi-rtl'
/* 18 */ ,'noUi-dragable'
/* 19 */ ,'noUi-extended'
/* 20 */ ,'noUi-state-drag'
]
// Determine the events to bind. IE11 implements pointerEvents without
// a prefix, which breaks compatibility with the IE10 implementation.
if( window.navigator.pointerEnabled ) {
actions = {
start: 'pointerdown'
,move: 'pointermove'
,end: 'pointerup'
};
} else if ( window.navigator.msPointerEnabled ) {
actions = {
start: 'MSPointerDown'
,move: 'MSPointerMove'
,end: 'MSPointerUp'
};
,actions = window.navigator['pointerEnabled'] ? {
start: 'pointerdown'
,move: 'pointermove'
,end: 'pointerup'
} : window.navigator['msPointerEnabled'] ? {
start: 'MSPointerDown'
,move: 'MSPointerMove'
,end: 'MSPointerUp'
} : {
start: 'mousedown touchstart'
,move: 'mousemove touchmove'
,end: 'mouseup touchend'
};
// Percentage calculation
// (percentage) How many percent is this value of this range?
function fromPercentage ( range, value ) {
return (value * 100) / ( range[1] - range[0] );
}
// Test an array of objects, and calls them if they are a function.
function call ( f, scope, args ) {
// (percentage) Where is this value on this range?
function toPercentage ( range, value ) {
return fromPercentage( range, range[0] < 0 ?
value + Math.abs(range[0]) :
value - range[0] );
}
// (value) How much is this percentage on this range?
function isPercentage ( range, value ) {
return ((value * ( range[1] - range[0] )) / 100) + range[0];
}
// Type tests
// Test in an object is an instance of jQuery or Zepto.
function isInstance ( a ) {
return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) );
}
// Checks whether a value is numerical.
function isNumeric ( a ) {
return !isNaN( parseFloat( a ) ) && isFinite( a );
}
// General helper functions
// Test an array of objects, and calls them if they are a function.
function call ( functions, scope ) {
// Allow the passing of an unwrapped function.
// Leaves other code a more comprehensible.
if( !$.isArray(f) ){
f = [f];
if( !$.isArray( functions ) ){
functions = [ functions ];
}
$.each(f,function(i,q){
if (typeof q === "function") {
q.call(scope, args);
$.each( functions, function(){
if (typeof this === 'function') {
this.call(scope);
}

@@ -127,19 +138,69 @@ });

function instance ( object ) {
return object instanceof $ || ( $.zepto && $.zepto.isZ ( object ) );
// Returns a proxy to set a target using the public value method.
function setN ( target, number ) {
return function(){
// Determine the correct position to set,
// leave the other one unchanged.
var val = [null, null];
val[ number ] = $(this).val();
// Trigger the 'set' callback
target.val(val, true);
};
}
// Round a value to the closest 'to'.
function closest ( value, to ){
return Math.round(value / to) * to;
}
// Format output value to specified standards.
function format ( value, options ) {
// Round the value to the resolution that was set
// with the serialization options.
value = value.toFixed( options['decimals'] );
// Rounding away decimals might cause a value of -0
// when using very small ranges. Remove those cases.
if ( parseFloat(value) === 0 ) {
value = value.replace('-0', '0');
}
// Apply the proper decimal mark to the value.
return value.replace( '.', options['serialization']['mark'] );
}
// Determine the handle closest to an event.
function closestHandle ( handles, location, style ) {
if ( handles.length === 1 ) {
return handles[0];
}
var total = handles[0].offset()[style] +
handles[1].offset()[style];
return handles[ location < total / 2 ? 0 : 1 ];
}
// Round away small numbers in floating point implementation.
function digits ( value, round ) {
return parseFloat(value.toFixed(round));
}
// Event abstraction
// Provide a clean event with standardized offset values.
function fixEvent ( e ) {
// Required (in at the very least Chrome) to prevent
// scrolling and panning while attempting to slide.
// The tap event also depends on this. This doesn't
// seem to prevent panning in Firefox, which is an issue.
// Prevent-default will also stop Chrome from setting a text-cursor.
// Prevent scrolling and panning on touch events, while
// attempting to slide. The tap event also depends on this.
e.preventDefault();
// Filter the event to register the type,
// which can be touch, mouse or pointer. Since noUiSlider 4
// so longer binds touch OR mouse, but rather touch AND mouse,
// offset changes need to be made on an event specific basis.
// Filter the event to register the type, which can be
// touch, mouse or pointer. Offset changes need to be
// made on an event specific basis.
var touch = e.type.indexOf('touch') === 0

@@ -150,4 +211,3 @@ ,mouse = e.type.indexOf('mouse') === 0

// IE10 implemented pointer events with a prefix,
// so we'll needs to check for those, too.
// IE10 implemented pointer events with a prefix;
if ( e.type.indexOf('MSPointer') === 0 ) {

@@ -164,4 +224,4 @@ pointer = true;

if ( touch ) {
// noUiSlider supports one movement at a time, for now.
// It is therefore safe to select the first 'changedTouch'.
// noUiSlider supports one movement at a time,
// so we can select the first 'changedTouch'.
x = e.changedTouches[0].pageX;

@@ -183,719 +243,880 @@ y = e.changedTouches[0].pageY;

return $.extend( event, { x:x, y:y } );
return $.extend( event, {
'pointX': x
,'pointY': y
,cursor: mouse
});
}
// Handler for attaching events trough a proxy
function attach ( events, target, callback, scope, noAbstraction ) {
// Handler for attaching events trough a proxy
function attach ( events, element, callback, pass ) {
var target = pass.target;
// Add the noUiSlider namespace to all events.
events = events.replace( /\s/g, namespace + ' ' ) + namespace;
// The 'noAbstraction' argument can be set to prevent
// event checking, and instead just proxy the event to
// the right namespace. 'noAbstraction' can be level 1 or 2.
if ( noAbstraction ) {
if ( noAbstraction > 1 ){
scope = $.extend(target, scope);
}
return target.on( events, $.proxy( callback, scope ));
}
// Bind a closure on the target.
return element.on( events, function( e ){
// Make the callback available in a lower scope
scope.handler = callback;
// jQuery and Zepto handle unset attributes differently.
var disabled = target.attr('disabled');
disabled = !( disabled === undefined || disabled === null );
return target.on( events, $.proxy( function( e ){
// Test if there is anything that should prevent an event
// from being handled, such as a disabled state or an active
// 'tap' transition. Prevent interaction with disabled sliders.
if( this.target.is('[class*="noUi-state-"], [disabled]') ) {
// 'tap' transition.
if( target.hasClass('noUi-state-tap') || disabled ) {
return false;
}
// Call the event handler with the original event as argument.
// The handler won't know it has been passed trough this
// proxy, and it won't have to filter event validity, because
// that was done here. Since the scope can just be 'this',
// there is no need to use .call().
this.handler( fixEvent ( e ) );
}, scope ));
// Call the event handler with three arguments:
// - The event;
// - An object with data for the event;
// - The slider options;
// Having the slider options as a function parameter prevents
// getting it in every function, which muddies things up.
callback (
fixEvent( e )
,pass
,target.data('base').data('options')
);
});
}
// Checks whether a variable is numerical.
function isNumeric ( a ) {
return !isNaN( parseFloat( a ) ) && isFinite( a );
}
// jQuery doesn't have a method to return a CSS value as a percentage.
function getPercentage( a ){
return parseFloat(this.style[a]);
}
// Serialization and value storage
function test ( o, set ){
// Store a value on all serialization targets, or get the current value.
function serialize ( a ) {
// Checks whether a variable is a candidate to be a
// valid serialization target.
function ser(r){
return ( instance ( r ) || typeof r === 'string' || r === false );
/*jshint validthis: true */
// Re-scope target for availability within .each;
var target = this.target;
// Get the value for this handle
if ( a === undefined ) {
return this.element.data('value');
}
// These tests are structured with an item for every option available.
// Every item contains an 'r' flag, which marks a required option, and
// a 't' function, which in turn takes some arguments:
// - the value for the option
// - [optional] a reference to options object
// - [optional] the option name
// The testing function returns false when an error is detected,
// or true when everything is OK. Every test also has an 'init'
// method which appends the parent object to all children.
// Write the value to all serialization objects
// or store a new value on the handle
if ( a === true ) {
a = this.element.data('value');
} else {
this.element.data('value', a);
}
var TESTS = {
/* Handles.
* Has default, can be 1 or 2.
*/
"handles": {
r: true
,t: function(q){
q = parseInt(q, 10);
return ( q === 1 || q === 2 );
}
// Prevent a serialization call if the value wasn't initialized.
if ( a === undefined ) {
return;
}
// If the provided element was a function,
// call it with the slider as scope. Otherwise,
// simply call the function on the object.
$.each( this.elements, function() {
if ( typeof this === 'function' ) {
this.call(target, a);
} else {
this[0][this[1]](a);
}
/* Range.
* Must be an array of two numerical floats,
* which can't be identical.
*/
,"range": {
r: true
,t: function(q,o,w){
if(q.length!==2){
return false;
}
// Reset the array to floats
q = [parseFloat(q[0]),parseFloat(q[1])];
// Test if those floats are numerical
if(!isNumeric(q[0])||!isNumeric(q[1])){
return false;
}
// When this test is run for range, the values can't
// be identical.
if(w==="range" && q[0] === q[1]){
return false;
}
// The lowest value must really be the lowest value.
if(q[1]<q[0]){
return false;
}
o[w]=q;
return true;
}
}
/* Start.
* Must be an array of two numerical floats when handles = 2;
* Uses 'range' test.
* When handles = 1, a single float is also allowed.
*/
,"start": {
r: true
,t: function(q,o,w){
if(o.handles === 1){
if($.isArray(q)){
q=q[0];
}
q = parseFloat(q);
o.start = [q];
return isNumeric(q);
}
return this.parent.range.t(q,o,w);
}
}
/* Connect.
* Must be true or false when handles = 2;
* Can use 'lower' and 'upper' when handles = 1.
*/
,"connect": {
t: function(q,o){
return ( q === true
|| q === false
|| ( q === 'lower' && o.handles === 1)
|| ( q === 'upper' && o.handles === 1));
}
}
/* Connect.
* Will default to horizontal, not required.
*/
,"orientation": {
t: function(q){
return ( q === "horizontal" || q === "vertical" );
}
}
/* Margin.
* Must be a float, has a default value.
*/
,"margin": {
r: true
,t: function(q,o,w){
q = parseFloat(q);
o[w]=q;
return isNumeric(q);
}
}
/* Serialization.
* Required, but has default. 'resolution' and 'mark' option,
* are allowed to be missing, 'to' isn't. Must be an array
* when using two handles, can be a single value
* when using one handle. 'mark' can only be period (.) or
* comma (,) to make sure the value can be parsed properly.
*/
,"serialization": {
r: true
,t: function(q,o){
});
}
if(!q.resolution){
o.serialization.resolution = 0.01;
} else {
switch(q.resolution){
case 1:
case 0.1:
case 0.01:
case 0.001:
case 0.0001:
case 0.00001:
break;
default:
return false;
}
}
// Map serialization to [ element, method ]. Attach events where required.
function storeElement ( handle, item, number ) {
if(!q.mark){
o.serialization.mark = '.';
} else {
return ( q.mark === '.' || q.mark === ',' );
}
// Add a change event to the supplied jQuery objects,
// which triggers the value-setting function on the target.
if ( isInstance( item ) ) {
if(q.to){
var elements = [], target = handle.data('target');
if(o.handles === 1){
// Wrap the value for one handle into an array.
if(!$.isArray(q.to)){
q.to = [q.to];
}
// Write back to the options object;
o.serialization.to = q.to;
// Run test for valid serialization target.
return ser(q.to[0]);
}
return (q.to.length === 2 && ser(q.to[0]) && ser(q.to[1]));
// Link the field to the other handle if the
// slider is inverted.
if ( handle.data('options').direction ) {
number = number ? 0 : 1;
}
}
// Loop all items so the change event is properly bound,
// and the items can individually be added to the array.
item.each(function(){
// If no 'to' option is specified,
// the serialization option is invalid.
return false;
// Bind the change event.
$(this).on('change' + namespace, setN( target, number ));
}
}
/* Slide.
* Not required. Must be a function.
*/
,"slide": {
t: function(q){
return typeof q === "function";
}
}
/* Set.
* Not required. Must be a function.
* Tested using the 'slide' test.
*/
,"set": {
t: function(q,o){
return this.parent.slide.t(q,o);
}
}
/* Step.
* Not required. Tested using the 'margin' test.
*/
,"step": {
t: function(q,o,w){
return this.parent.margin.t(q,o,w);
}
}
/* [init]
* Not an option test. Calling this method will return the
* parent object with some cross references that allow
* crawling the object in an upward direction, which
* normally isn't possible in JavaScript.
*/
,"init": function(){
var obj = this;
$.each(obj,function(i,c){
c.parent = obj;
});
delete this.init;
return this;
}
},
// Store the element with the proper handler.
elements.push([ $(this), 'val' ]);
});
// Prepare a set of tests, by adding some internal reference
// values not available in native JavaScript object implementation.
a = TESTS.init();
return elements;
}
// Loop all provided tests;
// 'v' is the option set, 'i' is the index for the current test.
$.each(a, function( i, v ){
// Append a new input to the noUiSlider base.
// Prevent the change event from flowing upward.
if ( typeof item === 'string' ) {
// If the value is required but not set,
// or if the test fails, throw an error.
if((v.r && (!o[i] && o[i] !== 0)) || ((o[i] || o[i] === 0) && !v.t(o[i],o,i))){
item = [ $('<input type="hidden" name="'+ item +'">')
.appendTo(handle)
.addClass(clsList[3])
.change(function ( e ) {
e.stopPropagation();
}), 'val'];
}
// For debugging purposes it might be very useful to know
// what option caused the trouble. Since throwing an error
// will prevent further script execution, log the error
// first. Test for console, as it might not be available.
if( console && console.log && console.group ){
console.group( "Invalid noUiSlider initialisation:" );
console.log( "Option:\t", i );
console.log( "Value:\t", o[i] );
console.log( "Slider:\t", set[0] );
console.groupEnd();
}
return [item];
}
throw new RangeError("noUiSlider");
}
// Access point and abstraction for serialization.
function store ( handle, i, serialization ) {
var elements = [];
// Loops all items in the provided serialization setting,
// add the proper events to them or create new input fields,
// and add them as data to the handle so they can be kept
// in sync with the slider value.
$.each( serialization['to'][i], function( index ){
elements = elements.concat(
storeElement( handle, serialization['to'][i][index], i )
);
});
return {
element: handle
,elements: elements
,target: handle.data('target')
,'val': serialize
};
}
function closest( value, to ){
// Round a value to the closest 'to'.
// Used with the 'step' option.
return Math.round(value / to) * to;
}
function format ( value, target ) {
// Handle placement
// Round the value to the resolution that was set
// with the serialization options.
value = value.toFixed( target.data('decimals') );
// Fire callback on unsuccessful handle movement.
function block ( base, stateless ) {
// Apply the proper decimal mark to the value.
return value.replace( '.', target.data('mark') );
var target = base.data('target');
if ( !target.hasClass(clsList[14]) ){
// The visual effects should not always be applied.
if ( !stateless ) {
target.addClass(clsList[15]);
setTimeout(function(){
target.removeClass(clsList[15]);
}, 450);
}
target.addClass(clsList[14]);
call( base.data('options').block, target );
}
}
function setHandle ( handle, to, forgive ) {
// Change inline style and apply proper classes.
function placeHandle ( handle, to ) {
var nui = handle.data('nui').options
// Get the array of handles from the base.
// Will be undefined at initialisation.
,handles = handle.data('nui').base.data('handles')
// Get some settings from the handle;
,style = handle.data('nui').style
,hLimit;
var settings = handle.data('options');
// Make sure the value can be parsed.
// This will catch any potential NaN, even though
// no internal function calling setHandle should pass
// invalided parameters.
if( !isNumeric(to) ) {
return false;
to = digits(to, 7);
// If the slider can move, remove the class
// indicating the block state.
handle.data('target').removeClass(clsList[14]);
// Set handle to new location
handle.css( settings['style'], to + '%' ).data('pct', to);
// Force proper handle stacking
if ( handle.is(':first-child') ) {
handle.toggleClass(clsList[13], to > 50 );
}
// Ignore the call if the handle won't move anyway.
if( to === handle[0].gPct(style) ) {
return false;
if ( settings['direction'] ) {
to = 100 - to;
}
// Limit 'to' to 0 - 100
to = to < 0 ? 0 : to > 100 ? 100 : to;
// Write the value to the serialization object.
handle.data('store').val(
format ( isPercentage( settings['range'], to ), settings )
);
}
// Handle the step option, or ignore it.
if( nui.step && !forgive ){
to = closest( to, percentage.from(nui.range, nui.step));
}
// Test suggested values and apply margin, step.
function setHandle ( handle, to ) {
// Stop handling this call if the handle won't step to a new value.
if( to === handle[0].gPct(style) ) {
var base = handle.data('base'), settings = base.data('options'),
handles = base.data('handles'), lower = 0, upper = 100;
// Catch invalid user input
if ( !isNumeric( to ) ){
return false;
}
// We're done if this is the only handle,
// if the handle bounce is trusted to the user
// or on initialisation when handles isn't defined yet.
if( handle.siblings('.' + clsList[1]).length && !forgive && handles ){
// Handle the step option.
if ( settings['step'] ){
to = closest( to, settings['step'] );
}
// Otherwise, the handle should bounce,
// and stop at the other handle.
if ( handle.data('nui').number ) {
hLimit = handles[0][0].gPct(style) + nui.margin;
to = to < hLimit ? hLimit : to;
if ( handles.length > 1 ){
if ( handle[0] !== handles[0][0] ) {
lower = digits(handles[0].data('pct')+settings['margin'],7);
} else {
hLimit = handles[1][0].gPct(style) - nui.margin;
to = to > hLimit ? hLimit : to;
upper = digits(handles[1].data('pct')-settings['margin'],7);
}
}
// Stop handling this call if the handle can't move past another.
if( to === handle[0].gPct(style) ) {
return false;
}
// Limit position to boundaries. When the handles aren't set yet,
// they return -1 as a percentage value.
to = Math.min( Math.max( to, lower ), upper < 0 ? 100 : upper );
// Stop handling this call if the handle can't move past another.
// Return an array containing the hit limit, so the caller can
// provide feedback. ( block callback ).
if ( to === handle.data('pct') ) {
return [!lower ? false : lower, upper === 100 ? false : upper];
}
// Fix for the z-index issue where the lower handle gets stuck
// below the upper one. Since this function is called for every
// movement, toggleClass cannot be used.
if( handle.data('nui').number === 0 && to > 95 ){
handle.addClass(clsList[13]);
} else {
handle.removeClass(clsList[13]);
}
placeHandle ( handle, to );
return true;
}
// Set handle to new location
handle.css( style , to + '%' );
// Handles movement by tapping
function jump ( base, handle, to, callbacks ) {
// Write the value to the serialization object.
handle.data('store').val(
format ( percentage.is( nui.range, to ), handle.data('nui').target )
);
// Flag the slider as it is now in a transitional state.
// Transition takes 300 ms, so re-enable the slider afterwards.
base.addClass(clsList[5]);
setTimeout(function(){
base.removeClass(clsList[5]);
}, 300);
return true;
// Move the handle to the new position.
setHandle( handle, to );
// Trigger the 'slide' and 'set' callbacks,
// pass the target so that it is 'this'.
call( callbacks, base.data('target') );
base.data('target').change();
}
function store ( handle, S ) {
var i = handle.data('nui').number, scope = {
target: handle.data('nui').target
,options: handle.data('nui').options
,handle: handle
,i: i
};
// Event handlers
if( instance ( S.to[i] ) ) {
// Handle movement on document for handle and range drag.
function move ( event, Dt, Op ) {
// Add a change event to the supplied jQuery object, which
// will just trigger the 'val' function on the parent. In some
// cases, the change event will not fire on select elements,
// so listen to 'blur' too.
attach ( 'change blur'
,S.to[i]
,eventHandlers[0]
,scope
,2 );
// Map event movement to a slider percentage.
var handles = Dt.handles, limits,
proposal = event[ Dt.point ] - Dt.start[ Dt.point ];
// Triggering the 'set' callback should not occur on the 'blur'
// event, so bind it only to 'change'.
attach ( 'change'
,S.to[i]
,scope.options.set
,scope.target
,1 );
proposal = ( proposal * 100 ) / Dt.size;
return S.to[i];
}
if ( handles.length === 1 ) {
if ( typeof S.to[i] === "string" ) {
// Run handle placement, receive true for success or an
// array with potential limits.
limits = setHandle( handles[0], Dt.positions[0] + proposal );
// Append a new object to the noUiSlider base,
// prevent change events flowing upward.
return $('<input type="hidden" name="' + S.to[i] + '">')
.appendTo(handle)
.addClass(clsList[3])
.change(eventHandlers[1]);
}
if ( limits !== true ) {
if ( S.to[i] === false ) {
// Create an object capable of handling all jQuery calls.
return {
// The value will be stored a data on the handle.
val : function(a) {
// Value function provides a getter and a setter.
// Can't just test for !a, as a might be 0.
// When no argument is provided, return the value.
// Otherwise, set the value.
if ( a === undefined ) {
return this.handleElement.data('nui-val');
}
this.handleElement.data('nui-val', a);
if ( $.inArray ( handles[0].data('pct'), limits ) >= 0 ){
block ( Dt.base, !Op['margin'] );
}
// The object could be mistaken for a jQuery object,
// make sure that doesn't trigger any errors.
,hasClass: function(){
return false;
}
// The val function needs access to the handle.
,handleElement: handle
};
}
}
return;
}
function move( event ) {
} else {
// This function is called often, keep it light.
// Dragging the range could be implemented by forcing the
// 'move' event on both handles, but this solution proved
// lagging on slower devices, resulting in range errors. The
// slightly ugly solution below is considerably faster, and
// it can't move the handle out of sync. Bypass the standard
// setting method, as other checks are needed.
var base = this.base
,style = base.data('style')
// Subtract the initial movement from the current event,
// while taking vertical sliders into account.
,proposal = event.x - this.startEvent.x
,baseSize = style === 'left' ? base.width() : base.height();
var l1, u1, l2, u2;
// This loop prevents a long ternary for the proposal variable.
if( style === 'top' ) {
proposal = event.y - this.startEvent.y;
}
// Round the proposal to the step setting.
if ( Op['step'] ) {
proposal = closest( proposal, Op['step'] );
}
proposal = this.position + ( ( proposal * 100 ) / baseSize );
// Determine the new position, store it twice. Once for
// limiting, once for checking whether placement should occur.
l1 = l2 = Dt.positions[0] + proposal;
u1 = u2 = Dt.positions[1] + proposal;
setHandle( this.handle, proposal );
// Round the values within a sensible range.
if ( l1 < 0 ) {
u1 += -1 * l1;
l1 = 0;
} else if ( u1 > 100 ) {
l1 -= ( u1 - 100 );
u1 = 100;
}
// Trigger the 'slide' event, pass the target so that it is 'this'.
call( base.data('options').slide
,base.data('target') );
// Don't perform placement if no handles are to be changed.
// Check if the lowest value is set to zero.
if ( l2 < 0 && !l1 && !handles[0].data('pct') ) {
return;
}
// The highest value is limited to 100%.
if ( u1 === 100 && u2 > 100 && handles[1].data('pct') === 100 ){
return;
}
placeHandle ( handles[0], l1 );
placeHandle ( handles[1], u1 );
}
// Trigger the 'slide' event, if the handle was moved.
call( Op['slide'], Dt.target );
}
function end ( ) {
// Unbind move events on document, call callbacks.
function end ( event, Dt, Op ) {
var base = this.base
,handle = this.handle;
// The handle is no longer active, so remove the class.
if ( Dt.handles.length === 1 ) {
Dt.handles[0].data('grab').removeClass(clsList[4]);
}
// The handle is no longer active, so remove
// the class.
handle.children().removeClass(clsList[4]);
// Remove cursor styles and text-selection events bound to the body.
if ( event.cursor ) {
body.css('cursor', '').off( namespace );
}
// Unbind move and end events, to prevent
// them stacking up over and over;
all.off(actions.move);
all.off(actions.end);
// Unbind the move and end events, which are added on 'start'.
doc.off( namespace );
// Some text-selection events are bound to the body.
$('body').off(namespace);
// Trigger the change event.
base.data('target').change();
Dt.target.removeClass( clsList[14] +' '+ clsList[20]).change();
// Trigger the 'end' callback.
call( handle.data('nui').options.set
,base.data('target') );
call( Op['set'], Dt.target );
}
function start ( event ) {
// Bind move events on document.
function start ( event, Dt, Op ) {
var handle = this.handle
,position = handle[0].gPct( handle.data('nui').style );
// Mark the handle as 'active' so it can be styled.
if( Dt.handles.length === 1 ) {
Dt.handles[0].data('grab').addClass(clsList[4]);
}
handle.children().addClass(clsList[4]);
// A drag should never propagate up to the 'tap' event.
event.stopPropagation();
// Attach the move event handler, while
// passing all relevant information along.
attach ( actions.move, all, move, {
startEvent: event
,position: position
,base: this.base
,target: this.target
,handle: handle
// Attach the move event.
attach ( actions.move, doc, move, {
start: event
,base: Dt.base
,target: Dt.target
,handles: Dt.handles
,positions: [ Dt.handles[0].data('pct')
,Dt.handles[ Dt.handles.length - 1 ].data('pct') ]
,point: Op['orientation'] ? 'pointY' : 'pointX'
,size: Op['orientation'] ? Dt.base.height() : Dt.base.width()
});
attach ( actions.end, all, end, {
base: this.base
,target: this.target
,handle: handle
// Unbind all movement when the drag ends.
attach ( actions.end, doc, end, {
target: Dt.target
,handles: Dt.handles
});
// Prevent text selection when dragging the handles.
// This doesn't prevent the browser defaulting to the I like cursor.
$('body').on(
'selectstart' + namespace
,function( ){ return false; }
);
// Text selection isn't an issue on touch devices,
// so adding additional callbacks isn't required.
if ( event.cursor ) {
// Prevent the 'I' cursor and extend the range-drag cursor.
body.css('cursor', $(event.target).css('cursor'));
// Mark the target with a dragging state.
if ( Dt.handles.length > 1 ) {
Dt.target.addClass(clsList[20]);
}
// Prevent text selection when dragging the handles.
body.on('selectstart' + namespace, function( ){
return false;
});
}
}
function selfEnd( event ) {
// Move closest handle to tapped location.
function tap ( event, Dt, Op ) {
// Stop propagation so that the tap handler doesn't interfere;
var base = Dt.base, handle, to, point, size;
// The tap event shouldn't propagate up to trigger 'edge'.
event.stopPropagation();
// Trigger the end handler. Supply the current scope,
// which contains all required information.
end.call( this );
// Determine the direction of the slider.
if ( Op['orientation'] ) {
point = event['pointY'];
size = base.height();
} else {
point = event['pointX'];
size = base.width();
}
// Find the closest handle and calculate the tapped point.
handle = closestHandle( base.data('handles'), point, Op['style'] );
to = (( point - base.offset()[ Op['style'] ] ) * 100 ) / size;
// The set handle to the new position.
jump( base, handle, to, [ Op['slide'], Op['set'] ]);
}
function tap ( event ) {
// Move handle to edges when target gets tapped.
function edge ( event, Dt, Op ) {
// If the target contains an active handle, don't trigger
// this event. Tapping shouldn't be possible while dragging.
if ( this.base.find('.' + clsList[4]).length ) {
return;
var handles = Dt.base.data('handles'), to, i;
i = Op['orientation'] ? event['pointY'] : event['pointX'];
i = i < Dt.base.offset()[Op['style']];
to = i ? 0 : 100;
i = i ? 0 : handles.length - 1;
jump ( Dt.base, handles[i], to, [ Op['slide'], Op['set'] ]);
}
// API
// Validate and standardize input.
function test ( input, sliders ){
/* Every input option is tested and parsed. This'll prevent
endless validation in internal methods. These tests are
structured with an item for every option available. An
option can be marked as required by setting the 'r' flag.
The testing function is provided with three arguments:
- The provided value for the option;
- A reference to the options object;
- The name for the option;
The testing function returns false when an error is detected,
or true when everything is OK. It can also modify the option
object, to make sure all values can be correctly looped elsewhere. */
function values ( a ) {
if ( a.length !== 2 ){
return false;
}
// Convert the array to floats
a = [ parseFloat(a[0]), parseFloat(a[1]) ];
// Test if all values are numerical
if( !isNumeric(a[0]) || !isNumeric(a[1]) ){
return false;
}
// The lowest value must really be the lowest value.
if( a[1] < a[0] ){
return false;
}
return a;
}
// Getting variables from the event is not required, but
// shortens other expressions and is far more convenient;
var i, handle, hCenter, base = this.base
,handles = this.handles
,style = base.data('style')
,eventXY = event[style === 'left' ? 'x' : 'y']
,baseSize = style === 'left' ? base.width() : base.height()
,offset = {
handles: []
,base: {
left: base.offset().left
,top: base.offset().top
var serialization = {
resolution: function(q,o){
// Parse the syntactic sugar that is the serialization
// resolution option to a usable integer.
// Checking for a string '1', since the resolution needs
// to be cast to a string to split in on the period.
switch( q ){
case 1:
case 0.1:
case 0.01:
case 0.001:
case 0.0001:
case 0.00001:
q = q.toString().split('.');
o['decimals'] = q[0] === '1' ? 0 : q[1].length;
break;
case undefined:
o['decimals'] = 2;
break;
default:
return false;
}
};
// Loop handles and add data to the offset list.
for (i = 0; i < handles.length; i++ ) {
offset.handles.push({
left: handles[i].offset().left
,top: handles[i].offset().top
});
}
return true;
}
,mark: function(q,o,w){
// Calculate the central point between the handles;
hCenter = handles.length === 1 ? 0 :
(( offset.handles[0][style] + offset.handles[1][style] ) / 2 );
if ( !q ) {
o[w]['mark'] = '.';
return true;
}
// If there is just one handle,
// or the lower handles in closest to the event,
// select the first handle. Otherwise, pick the second.
if ( handles.length === 1 || eventXY < hCenter ){
handle = handles[0];
} else {
handle = handles[1];
}
switch( q ){
case '.':
case ',':
return true;
default:
return false;
}
}
,to: function(q,o,w){
// Flag the slider as it is now in a transitional state.
// Transition takes 300 ms, so re-enable the slider afterwards.
base.addClass(clsList[5]);
setTimeout(function(){
base.removeClass(clsList[5]);
}, 300);
// Checks whether a variable is a candidate to be a
// valid serialization target.
function ser(r){
return isInstance ( r ) ||
typeof r === 'string' ||
typeof r === 'function' ||
r === false ||
( isInstance ( r[0] ) &&
typeof r[0][r[1]] === 'function' );
}
// Calculate the new position for the handle and
// trigger the movement.
setHandle(
handle
,(((eventXY - offset.base[style]) * 100) / baseSize)
);
// Flatten the serialization array into a reliable
// set of elements, which can be tested and looped.
function filter ( value ) {
// Trigger the 'slide' and 'set' callbacks,
// pass the target so that it is 'this'.
call( [ handle.data('nui').options.slide
,handle.data('nui').options.set ]
,base.data('target') );
var items = [[],[]];
base.data('target').change();
// If a single value is provided it can be pushed
// immediately.
if ( ser(value) ) {
items[0].push(value);
} else {
}
// Otherwise, determine whether this is an
// array of single elements or sets.
$.each(value, function(i, val) {
function create ( options ) {
// Don't handle an overflow of elements.
if( i > 1 ){
return;
}
return this.each(function( index, target ){
// Decide if this is a group or not
if( ser(val) ){
items[i].push(val);
} else {
items[i] = items[i].concat(val);
}
});
}
// Target is the wrapper that will receive all external
// scripting interaction. It has no styling and serves no
// other function.
target = $(target);
target.addClass(clsList[6]);
return items;
}
// Base is the internal main 'bar'.
var i, style, decimals, handle
,base = $('<div/>').appendTo(target)
,handles = []
,cls = {
base: stdCls.base
,origin: [
stdCls.origin.concat([clsList[1] + clsList[7]])
,stdCls.origin.concat([clsList[1] + clsList[8]])
]
,handle: [
stdCls.handle.concat([clsList[2] + clsList[7]])
,stdCls.handle.concat([clsList[2] + clsList[8]])
]
};
if ( !q ) {
o[w]['to'] = [[],[]];
} else {
// Set defaults where applicable;
options = $.extend({
handles: 2
,margin: 0
,orientation: "horizontal"
}, options) || {};
var i, j;
// Set a default for serialization;
if(!options.serialization){
options.serialization = {
to : [false, false]
,resolution : 0.01
,mark: '.'
};
// Flatten the serialization array
q = filter ( q );
// Reverse the API for RTL sliders.
if ( o['direction'] && q[1].length ) {
q.reverse();
}
// Test all elements in the flattened array.
for ( i = 0; i < o['handles']; i++ ) {
for ( j = 0; j < q[i].length; j++ ) {
// Return false on invalid input
if( !ser(q[i][j]) ){
return false;
}
// Remove 'false' elements, since those
// won't be handled anyway.
if( !q[i][j] ){
q[i].splice(j, 1);
}
}
}
// Write the new values back
o[w]['to'] = q;
}
return true;
}
}, tests = {
/* Handles.
* Has default, can be 1 or 2.
*/
'handles': {
'r': true
,'t': function(q){
q = parseInt(q, 10);
return ( q === 1 || q === 2 );
}
}
/* Range.
* Must be an array of two numerical floats,
* which can't be identical.
*/
,'range': {
'r': true
,'t': function(q,o,w){
// Run all options through a testing mechanism to ensure correct
// input. The test function will throw errors, so there is
// no need to capture the result of this call. It should be noted
// that options might get modified to be handled properly. E.g.
// wrapping integers in arrays.
test(options, target);
o[w] = values(q);
// I can't type serialization any more, and it doesn't compress
// very well, so shorten it.
options.S = options.serialization;
// The values can't be identical.
return o[w] && o[w][0] !== o[w][1];
}
}
/* Start.
* Must be an array of two numerical floats when handles = 2;
* Uses 'range' test.
* When handles = 1, a single float is also allowed.
*/
,'start': {
'r': true
,'t': function(q,o,w){
if( o['handles'] === 1 ){
if( $.isArray(q) ){
q = q[0];
}
q = parseFloat(q);
o.start = [q];
return isNumeric(q);
}
// Apply the required connection classes to the elements
// that need them. Some classes are made up for several segments
// listed in the class list, to allow easy renaming and provide
// a minor compression benefit.
if( options.connect ) {
o[w] = values(q);
return !!o[w];
}
}
/* Connect.
* Must be true or false when handles = 2;
* Can use 'lower' and 'upper' when handles = 1.
*/
,'connect': {
'r': true
,'t': function(q,o,w){
if( options.connect === "lower" ){
// Add some styling classes to the base;
cls.base.push(clsList[9], clsList[9] + clsList[7]);
// When using the option 'Lower', there is only one
// handle, and thus only one origin.
cls.origin[0].push(clsList[12]);
} else {
cls.base.push(clsList[9] + clsList[8], clsList[12]);
cls.origin[0].push(clsList[9]);
if ( q === 'lower' ) {
o[w] = 1;
} else if ( q === 'upper' ) {
o[w] = 2;
} else if ( q === true ) {
o[w] = 3;
} else if ( q === false ) {
o[w] = 0;
} else {
return false;
}
return true;
}
}
/* Connect.
* Will default to horizontal, not required.
*/
,'orientation': {
't': function(q,o,w){
switch (q){
case 'horizontal':
o[w] = 0;
break;
case 'vertical':
o[w] = 1;
break;
default: return false;
}
return true;
}
}
/* Margin.
* Must be a float, has a default value.
*/
,'margin': {
'r': true
,'t': function(q,o,w){
q = parseFloat(q);
o[w] = fromPercentage(o['range'], q);
return isNumeric(q);
}
}
/* Direction.
* Required, can be 'ltr' or 'rtl'.
*/
,'direction': {
'r': true
,'t': function(q,o,w){
} else {
cls.base.push(clsList[12]);
switch ( q ) {
case 'ltr': o[w] = 0;
break;
case 'rtl': o[w] = 1;
// Invert connection for RTL sliders;
o['connect'] = [0,2,1,3][o['connect']];
break;
default:
return false;
}
return true;
}
}
/* Behaviour.
* Required, defines responses to tapping and
* dragging elements.
*/
,'behaviour': {
'r': true
,'t': function(q,o,w){
// Parse the syntactic sugar that is the serialization
// resolution option to a usable integer.
style = options.orientation === 'vertical' ? 'top' : 'left';
o[w] = {
'tap': q !== (q = q.replace('tap', ''))
,'extend': q !== (q = q.replace('extend', ''))
,'drag': q !== (q = q.replace('drag', ''))
,'fixed': q !== (q = q.replace('fixed', ''))
};
decimals = options.S.resolution.toString().split('.');
return !q.replace('none','').replace(/\-/g,'');
}
}
/* Serialization.
* Required, but has default. Must be an array
* when using two handles, can be a single value when using
* one handle. 'mark' can be period (.) or comma (,).
*/
,'serialization': {
'r': true
,'t': function(q,o,w){
// Checking for a string "1", since the resolution needs
// to be cast to a string to split in on the period.
decimals = decimals[0] === "1" ? 0 : decimals[1].length;
return serialization.to( q['to'], o, w ) &&
serialization.resolution( q['resolution'], o ) &&
serialization.mark( q['mark'], o, w );
}
}
/* Slide.
* Not required. Must be a function.
*/
,'slide': {
't': function(q){
return $.isFunction(q);
}
}
/* Set.
* Not required. Must be a function.
* Tested using the 'slide' test.
*/
,'set': {
't': function(q){
return $.isFunction(q);
}
}
/* Block.
* Not required. Must be a function.
* Tested using the 'slide' test.
*/
,'block': {
't': function(q){
return $.isFunction(q);
}
}
/* Step.
* Not required.
*/
,'step': {
't': function(q,o,w){
q = parseFloat(q);
o[w] = fromPercentage ( o['range'], q );
return isNumeric(q);
}
}
};
// Add classes for horizontal and vertical sliders.
// The horizontal class is provided for completeness,
// as it isn't used in the default theme.
if( options.orientation === "vertical" ){
cls.base.push(clsList[10]);
} else {
cls.base.push(clsList[11]);
$.each( tests, function( name, test ){
/*jslint devel: true */
var value = input[name], isSet = value !== undefined;
// If the value is required but not set, fail.
if( ( test['r'] && !isSet ) ||
// If the test returns false, fail.
( isSet && !test['t']( value, input, name ) ) ){
// For debugging purposes it might be very useful to know
// what option caused the trouble. Since throwing an error
// will prevent further script execution, log the error
// first. Test for console, as it might not be available.
if( console && console.log && console.group ){
console.group( 'Invalid noUiSlider initialisation:' );
console.log( 'Option:\t', name );
console.log( 'Value:\t', value );
console.log( 'Slider(s):\t', sliders );
console.groupEnd();
}
throw new RangeError('noUiSlider');
}
});
}
// Merge base classes with default;
base.addClass(cls.base.join(" ")).data('target', target);
// Parse options, add classes, attach events, create HTML.
function create ( options ) {
// Make data accessible in functions throughout the plugin.
target.data({
base: base
,mark: options.S.mark
,decimals: decimals
});
/*jshint validthis: true */
for (i = 0; i < options.handles; i++ ) {
// Store the original set of options on all targets,
// so they can be re-used and re-tested later.
// Make sure to break the relation with the options,
// which will be changed by the 'test' function.
this.data('options', $.extend(true, {}, options));
// Set defaults where applicable;
options = $.extend({
'handles': 2
,'margin': 0
,'connect': false
,'direction': 'ltr'
,'behaviour': 'tap'
,'orientation': 'horizontal'
}, options);
// Make sure the test for serialization runs.
options['serialization'] = options['serialization'] || {};
// Run all options through a testing mechanism to ensure correct
// input. The test function will throw errors, so there is
// no need to capture the result of this call. It should be noted
// that options might get modified to be handled properly. E.g.
// wrapping integers in arrays.
test( options, this );
// Pre-define the styles.
options['style'] = options['orientation'] ? 'top' : 'left';
return this.each(function(){
var target = $(this), i, dragable, handles = [], handle,
base = $('<div/>').appendTo(target);
// Throw an error if the slider was already initialized.
if ( target.data('base') ) {
throw new Error('Slider was already initialized.');
}
// Apply classes and data to the target.
target.data('base', base).addClass([
clsList[6]
,clsList[16 + options['direction']]
,clsList[10 + options['orientation']] ].join(' '));
for (i = 0; i < options['handles']; i++ ) {
handle = $('<div><div/></div>').appendTo(base);

@@ -905,99 +1126,146 @@

// origins and handles.
handle.addClass(cls.origin[i].join(" "));
handle.children().addClass(cls.handle[i].join(" "));
handle.addClass( clsList[1] );
// These events are only bound to the visual handle element,
// not the 'real' origin element.
attach ( actions.start, handle.children(), start, {
base: base
,target: target
,handle: handle
handle.children().addClass([
clsList[2]
,clsList[2] + clsList[ 7 + options['direction'] +
( options['direction'] ? -1 * i : i ) ]].join(' ') );
// Make sure every handle has access to all variables.
handle.data({
'base': base
,'target': target
,'options': options
,'grab': handle.children()
,'pct': -1
}).attr('data-style', options['style']);
// Every handle has a storage point, which takes care
// of triggering the proper serialization callbacks.
handle.data({
'store': store(handle, i, options['serialization'])
});
attach ( actions.end, handle.children(), selfEnd, {
// Store handles on the base
handles.push(handle);
}
// Apply the required connection classes to the elements
// that need them. Some classes are made up for several
// segments listed in the class list, to allow easy
// renaming and provide a minor compression benefit.
switch ( options['connect'] ) {
case 1: target.addClass( clsList[9] );
handles[0].addClass( clsList[12] );
break;
case 3: handles[1].addClass( clsList[12] );
/* falls through */
case 2: handles[0].addClass( clsList[9] );
/* falls through */
case 0: target.addClass(clsList[12]);
break;
}
// Merge base classes with default,
// and store relevant data on the base element.
base.addClass( clsList[0] ).data({
'target': target
,'options': options
,'handles': handles
});
// Use the public value method to set the start values.
target.val( options['start'] );
// Attach the standard drag event to the handles.
if ( !options['behaviour']['fixed'] ) {
for ( i = 0; i < handles.length; i++ ) {
// These events are only bound to the visual handle
// element, not the 'real' origin element.
attach ( actions.start, handles[i].children(), start, {
base: base
,target: target
,handles: [ handles[i] ]
});
}
}
// Attach the tap event to the slider base.
if ( options['behaviour']['tap'] ) {
attach ( actions.start, base, tap, {
base: base
,target: target
,handle: handle
});
}
// Make sure every handle has access to all primary
// variables. Can't uses jQuery's .data( obj ) structure
// here, as 'store' needs some values from the 'nui' object.
handle.data('nui', {
target: target
,decimals: decimals
,options: options
,base: base
,style: style
,number: i
}).data('store', store (
handle
,options.S
));
// Extend tapping behaviour to target
if ( options['behaviour']['extend'] ) {
// Write a function to the native DOM element, since
// jQuery wont let me get the current value in percentages.
handle[0].gPct = getPercentage;
target.addClass( clsList[19] );
// Make handles loop-able
handles.push(handle);
// Set the handle to its initial position;
setHandle(handle, percentage.to(options.range, options.start[i]));
if ( options['behaviour']['tap'] ) {
attach ( actions.start, target, edge, {
base: base
,target: target
});
}
}
// The base could use the handles too;
base.data({
options: options
,handles: handles
,style: style
});
// Make the range dragable.
if ( options['behaviour']['drag'] ){
// Add a reference to the handles on the target as well.
target.data({
handles: handles
});
dragable = base.find('.'+clsList[9]).addClass(clsList[18]);
// Attach the the tap event to the slider base.
attach ( actions.end, base, tap, {
base: base
,target: target
,handles: handles
});
// When the range is fixed, the entire range can
// be dragged by the handles. The handle in the first
// origin will propagate the start event upward,
// but it needs to be bound manually on the other.
if ( options['behaviour']['fixed'] ) {
dragable = dragable
.add( base.children().not(dragable).data('grab') );
}
attach ( actions.start, dragable, start, {
base: base
,target: target
,handles: handles
});
}
});
}
// Return value for the slider, relative to 'range'.
function getValue ( ) {
var re = [];
/*jshint validthis: true */
var base = $(this).data('base'), answer = [];
// Loop the handles, and get the value from the input
// for every handle on its' own.
$.each( $(this).data('handles'), function( i, handle ){
re.push( handle.data('store').val() );
$.each( base.data('handles'), function(){
answer.push( $(this).data('store').val() );
});
// If the slider has just one handle, return a single value.
// Otherwise, return an array.
return ( re.length === 1 ? re[0] : re );
// Otherwise, return an array, which is in reverse order
// if the slider is used RTL.
if ( answer.length === 1 ) {
return answer[0];
}
if ( base.data('options').direction ) {
return answer.reverse();
}
return answer;
}
function val ( args, modifiers ) {
// Set value for the slider, relative to 'range'.
function setValue ( args, set ) {
// If the function is called without arguments,
// act as a 'getter'. Call the getValue function
// in the same scope as this call.
if( args === undefined ){
return getValue.call( this );
}
/*jshint validthis: true */
// Passing the modifiers argument is not required.
// The input might also be 'true', to indicate that the
// 'set' event should be called.
modifiers = modifiers === true ? { trigger: true } : ( modifiers || {} );
// If the val is to be set to a number, which is valid
// If the value is to be set to a number, which is valid
// when using a one-handle slider, wrap it in an array.

@@ -1009,45 +1277,39 @@ if( !$.isArray(args) ){

// Setting is handled properly for each slider in the data set.
// Note that the val method is called on the target, which can
// therefore be used in the function.
return this.each(function( index, target ){
return this.each(function(){
// Make sure 'target' is a jQuery element.
target = $(target);
var b = $(this).data('base'), to, i,
handles = Array.prototype.slice.call(b.data('handles'),0),
settings = b.data('options');
$.each( $(this).data('handles'), function( j, handle ){
// If there are multiple handles to be set run the setting
// mechanism twice for the first handle, to make sure it
// can be bounced of the second one properly.
if ( handles.length > 1) {
handles[2] = handles[0];
}
// The RTL settings is implemented by reversing the front-end,
// internal mechanisms are the same.
if ( settings['direction'] ) {
args.reverse();
}
for ( i = 0; i < handles.length; i++ ){
// Calculate a new position for the handle.
to = args[ i%2 ];
// The set request might want to ignore this handle.
// Test for 'undefined' too, as a two-handle slider
// can still be set with an integer.
if( args[j] === null || args[j] === undefined ) {
return;
if( to === null || to === undefined ) {
continue;
}
// Calculate a new position for the handle.
var value, current
,range = handle.data('nui').options.range
,to = args[j], result;
// Assume the input can be trusted.
modifiers.trusted = true;
// Handle user facing input correction. The value is
// 'trusted' when a developer provides it from the 'val'
// method, not when it comes from an input element.
if ( modifiers.trusted === false || args.length === 1 ) {
modifiers.trusted = false;
}
// If one handle isn't set, the other can't move past it.
if ( args.length === 2 && $.inArray( null, args ) >= 0 ) {
modifiers.trusted = false;
}
// Add support for the comma (,) as a decimal symbol.
// Replace it by a period so it is handled properly by
// parseFloat. Omitting this would result in a removal
// of decimals. This is relevant on trusted input too,
// as a developer might input a comma separated string
// using the 'val' method.
if( $.type(to) === "string" ) {
// of decimals. This way, the developer can also
// input a comma separated string.
if( $.type(to) === 'string' ) {
to = to.replace(',', '.');

@@ -1057,55 +1319,109 @@ }

// Calculate the new handle position
to = percentage.to( range, parseFloat( to ) );
to = toPercentage( settings['range'], parseFloat( to ) );
// Set handle to new location, and make sure developer
// input is always accepted. The 'trusted' flag indicates
// input that is not coming from user facing elements.
result = setHandle( handle, to, modifiers.trusted );
// The 'val' method allows for an external modifier,
// to specify a request for an 'set' event.
if( modifiers.trigger ) {
call( handle.data('nui').options.set
,target );
// Invert the value if this is an right-to-left slider.
if ( settings['direction'] ) {
to = 100 - to;
}
// If the value of the input doesn't match the slider,
// reset it.
if( !result ){
// reset it. Sometimes the input is changed to a value the
// slider has rejected. This can occur when using 'select'
// or 'input[type="number"]' elements. In this case, set
// the value back to the input.
if ( setHandle( handles[i], to ) !== true ){
handles[i].data('store').val( true );
}
// Get the 'store' object, which can be an input
// element or a wrapper around a 'data' call.
value = handle.data('store').val();
// Optionally trigger the 'set' event.
if( set === true ) {
call( settings['set'], $(this) );
}
}
});
}
// Get the value for the current position.
current = percentage.is(
range
,handle[0].gPct(handle.data('nui').style)
);
// Unbind all attached events, remove classed and HTML.
function destroy ( target ) {
// Sometimes the input is changed to a value the slider
// has rejected. This can occur when using 'select' or
// 'input[type="number"]' elements. In this case,
// set the value back to the input.
if( value !== current ){
handle.data('store').val( format( current, target ) );
}
}
});
// Start the list of elements to be unbound with the target.
var elements = [[target,'']];
// Get the fields bound to both handles.
$.each(target.data('base').data('handles'), function(){
elements = elements.concat( $(this).data('store').elements );
});
// Remove all events added by noUiSlider.
$.each(elements, function(){
if( this.length > 1 ){
this[0].off( namespace );
}
});
// Remove all classes from the target.
target.removeClass(clsList.join(' '));
// Empty the target and remove all data.
target.empty().removeData('base options');
}
// Overwrite the native jQuery 'val' function
// with a simple handler. noUiSlider will use the internal
// value method, anything else will use the standard method.
// Merge options with current initialization, destroy slider
// and reinitialize.
function build ( options ) {
/*jshint validthis: true */
return this.each(function(){
// When uninitialised, jQuery will return '',
// Zepto returns undefined. Both are falsy.
var values = $(this).val() || false,
current = $(this).data('options'),
// Extend the current setup with the new options.
setup = $.extend( {}, current, options );
// If there was a slider initialised, remove it first.
if ( values !== false ) {
destroy( $(this) );
}
// Make the destroy method publicly accessible.
if( !options ) {
return;
}
// Create a new slider
$(this)['noUiSlider']( setup );
// Set the slider values back. If the start options changed,
// it gets precedence.
if ( values !== false && setup.start === current.start ) {
$(this).val( values );
}
});
}
// Overwrite the native jQuery value function
// with a simple handler. noUiSlider will use the internal
// value method, anything else will use the standard method.
$.fn.val = function(){
return this.hasClass(clsList[6])
? val.apply(this, arguments)
: $VAL.apply(this, arguments);
// If the function is called without arguments,
// act as a 'getter'. Call the getValue function
// in the same scope as this call.
if ( this.hasClass( clsList[6] ) ){
return arguments.length ?
setValue.apply( this, arguments ) :
getValue.apply( this );
}
// If this isn't noUiSlider, continue with jQuery's
// original method.
return $VAL.apply( this, arguments );
};
return create.call( this, options );
return ( rebuild ? build : create ).call( this, options );
};
}( $ ));
}( window['jQuery'] || window['Zepto'] ));
{
"name": "nouislider-browser",
"version": "4.2.2-1",
"version": "5.0.0-1",
"description": "npm package for nouislider",

@@ -5,0 +5,0 @@ "main": "index.js",

# noUiSlider
noUiSlider is lightweight plugin that was developed to be a jQuery UI alternative. It features cross-browser support, a `just-another-input-type` style of getting and setting values, a wide range of options and support for a bunch off touch devices. It works wonders on Android phones, iPhone & iPad, Windows phone and touch-screen laptops and tablets. It works excellent on the desktop too; All modern browsers and IE7+ are supported. The end result? A lean, extendible and bloat-less plugin that'll just do its job. To add even more flexibility, noUiSlider is compatible with both jQuery and Zepto.js. Oh, and the licensing terms are simple: [just do what you want](http://refreshless.com/nouislider/terms-of-use).
noUiSlider is lightweight plugin, developed to be a jQuery UI alternative. It features cross-browser support, a `just-another-input-type` style of getting and setting values, a wide range of options and support for a bunch of touch devices. It works wonders on Android phones, iPhone & iPad, Windows phone and touch-screen laptops and tablets. It works excellent on the desktop too; All modern browsers and IE7+ are supported. The end result? A lean, extendible and bloat-less plugin that'll just do its job. To add even more flexibility, noUiSlider is compatible with both jQuery and Zepto.js. Oh, and the licensing terms are simple: [just do what you want](http://refreshless.com/nouislider/terms-of-use).

@@ -8,3 +8,3 @@ Documentation

An extended documentation, including **examples**, **options** and **configuration details**, is available here: [noUiSlider documentation](http://refreshless.com/nouislider/).
An extensive documentation, including **examples**, **options** and **configuration details**, is available here: [noUiSlider documentation](http://refreshless.com/nouislider/).

@@ -14,5 +14,13 @@ Changes

**Changelog for version 4.2.2:**
+ Changed implementation of pointerEvents to be compatible with IE11.
**Changelog for version 5.0.0:**
**Please note:** noUiSlider 5 is a *major* revision, which means it isn't 100% compatible with version 4. The Javascript API is **compatible**, but your **stylesheet might break**.
+ Added support for dragging the slider range.
+ Added option to disable 'tap'.
+ Added `extend` settings, which allows for designs where the handles fit within the slider bar.
+ Added `rebuild` method.
+ Brand new design, improved class structure.
+ Compatibility with Google Closure compiler in Advanced mode.
Version numbering

@@ -26,26 +34,14 @@ ------------------------------

**CSS** ([CSSMinifier](http://cssminifier.com/))
The stylesheet is trimmed of whitespace and comments to provide a `min` version.
The stylesheet is trimmed to remove whitespace and comments to provide a `min` version.
**JS** ([Google Closure Compiler](http://closure-compiler.appspot.com/home))
The plugin is compressed using the Google Closure compiler, using the 'simple' optimization option.
The plugin is compressed using the Google Closure compiler. The source was adapted to facilitate the `ADVANCED_OPTIMIZATIONS` level.
**Code** ([JsLint](http://jslint.com/))
The plugin code is checked using JsLint, with the following options:
```
browser: true
devel: true
plusplus: true
unparam: true
white: true
```
The plugin code is checked using JsLint. Any remaining errors and warnings are intentional.
Please note that while some errors remain without these options, they are merely differences in coding style. Using `++` for example, is in my opinion very clear in a `for` loop. Some jQuery methods offer callbacks noUiSlider doesn't require, thus requiring `unparam`, and the `devel` option is required for the `console` statements.
Known issues
------------
There are some minor issues remaining in noUiSlider. It is a priority to fix these issues, but they may be fixed by browser changes in the future.
There are some minor issues remaining in noUiSlider. It is a priority to fix these issues, but they may be fixed by browser updates in the future.
+ Firefox will prefer scrolling to dragging the slider on touch events. The `preventDefault()` call that prevents this in other browser seems to fail here.
+ Safari for windows has the same issue, but only on vertical scrolling.
+ Firefox and Safari on Windows will emulate mouse-events on touch screens, but prefer scrolling to dragging the slider.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc