From e9b34abf3422b7a09c652f84805b2b34d501c3d5 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 18 May 2014 17:16:26 +0200 Subject: [PATCH 01/28] Started work on #208, #221, #236, #265 --- Link.js | 17 +++- jquery.nouislider.js | 205 +++++++++++++++++++++++++------------------ tests/run.html | 1 + tests/step-limit.js | 26 ++++++ 4 files changed, 161 insertions(+), 88 deletions(-) create mode 100644 tests/step-limit.js diff --git a/Link.js b/Link.js index 66752ca2..5a22dae5 100644 --- a/Link.js +++ b/Link.js @@ -335,14 +335,19 @@ var return; } + // Store the numerical value. this.actual = value; // Format values for display. value = this.format( value ); - // Store the numerical value. + // Store the formatted value. this.saved = value; + // Store parameters for use in reset() + this.resetHandle = handle; + this.resetSlider = slider; + // Branch between serialization to a function or an object. if ( typeof this.method === 'function' ) { // When target is undefined, the target was a function. @@ -375,11 +380,21 @@ var return this.formatting.to(a); }; + // Allow calling the 'write' method from cache. + Link.prototype.reset = function ( update ) { + this.write( this.actual, this.resetHandle, this.resetSlider, update ); + }; + // Converts a formatted value back to a real number. Link.prototype.getValue = function ( a ) { return this.formatting.from(a); }; + // Return saved (formatted) value. + Link.prototype.getSaved = function ( a ) { + return this.saved; + }; + // We can now test for Link.init to be an instance of Link. Link.prototype.init.prototype = Link.prototype; diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 1b55831d..ba007db6 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -163,10 +163,10 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); } - // (percentage) Get the step that applies at a certain value. - function getStep ( options, value ){ + // (j) Get the applicable step position. + function getStepPoint ( options, value ) { - var j = 1, a, b; + var j = 1; // Find the proper step for rtl sliders by search in inverse direction. // Fixes issue #262. @@ -174,6 +174,14 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ j++; } + return j; + } + + // (percentage) Get the step that applies at a certain value. + function getStep ( options, value ){ + + var j = getStepPoint( options, value ), a, b; + if ( options.snap ) { a = options.xPct[j-1]; @@ -323,6 +331,9 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } }); + // Store the actual step values. + parsed.xNumSteps = parsed.xSteps.slice(0); + $.each(parsed.xSteps, function(i,n){ // Ignore 'false' stepping. @@ -512,10 +523,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ object, to make sure all values can be correctly looped elsewhere. */ var parsed = { - xPct: [] - ,xVal: [] - ,xSteps: [ false ] - ,margin: 0 + xPct: [], + xVal: [], + xSteps: [ false ], + xNumSteps: [ false ], + margin: 0 }, tests; // Tests are executed in the order they are presented here. @@ -552,6 +564,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // be handled properly. E.g. wrapping integers in arrays. $.each( tests, function( name, test ){ + // If the option isn't set, but it is required, throw an error. if ( options[name] === undefined ) { if ( test.r ) { @@ -717,8 +730,9 @@ function closure ( target, options, originalOptions ){ } -// External event handling +// Helpers + // External event handling function fireEvents ( events ) { // Use the external api to get the values. @@ -731,37 +745,47 @@ function closure ( target, options, originalOptions ){ } } + // Check if the range is effectively 0. + function isNullRange ( ) { + return options.xVal.length === 2 && options.xVal[0] === options.xVal[1]; + } + // Handle placement // Test suggested values and apply margin, step. - function setHandle ( handle, to, delimit ) { + function setHandle ( handle, to ) { var n = handle[0] !== $Handles[0][0] ? 1 : 0, lower = $Locations[0] + options.margin, upper = $Locations[1] - options.margin; - // Don't delimit range dragging. - if ( delimit && $Handles.length > 1 ) { - to = n ? Math.max( to, lower ) : Math.min( to, upper ); - } + // Check if the slider has no range. If so, lock in the center. + if ( isNullRange() ) { - // Handle the step option. - if ( to < 100 ){ - to = getStep(options, to); - } + to = 50; + + } else { + + // For sliders with multiple handles, + // limit movement to the other handle. + if ( $Handles.length > 1 ) { + to = n ? Math.max( to, lower ) : Math.min( to, upper ); + } + + // Handle the step option. + if ( to < 100 ){ + to = getStep(options, to); + } - // Limit to 0/100 for .val input, trim anything beyond 7 digits, as - // JavaScript has some issues in its floating point implementation. - to = limit(parseFloat(to.toFixed(7))); + // Limit to 0/100 for .val input, trim anything beyond 7 digits, as + // JavaScript has some issues in its floating point implementation. + to = limit(parseFloat(to.toFixed(7))); - // Return falsy if handle can't move. False for 0 or 100 limit, - // '0' for limiting by another handle. - if ( to === $Locations[n] ) { - if ( $Handles.length === 1 ) { + // Return false if handle can't move. + if ( to === $Locations[n] ) { return false; } - return ( to === lower || to === upper ) ? 0 : false; } // Set the handle to the new position. @@ -823,7 +847,7 @@ function closure ( target, options, originalOptions ){ } // Move the handle to the new position. - setHandle( handle, to, false ); + setHandle( handle, to ); fireEvents(['slide', 'set', 'change']); } @@ -834,20 +858,27 @@ function closure ( target, options, originalOptions ){ // Handler for attaching events trough a proxy. function attach ( events, element, callback, data ) { + // This function can be used to 'filter' events to the slider. + // Add the noUiSlider namespace to all events. events = events.replace( /\s/g, namespace + ' ' ) + namespace; // Bind a closure on the target. return element.on( events, function( e ){ - // jQuery and Zepto handle unset attributes differently. - var disabled = $Target.attr('disabled'); - disabled = !( disabled === undefined || disabled === null ); + // jQuery and Zepto (1) handle unset attributes differently, + // but always falsy; #208 + if ( !!$Target.attr('disabled') ) { + return false; + } + + // Stop if an active 'tap' transition is taking place. + if ( $Target.hasClass( Classes[14] ) ) { + return false; + } - // Test if there is anything that should prevent an event - // from being handled, such as a disabled state or an active - // 'tap' transition. - if( $Target.hasClass( Classes[14] ) || disabled ) { + // Ignore all events if the range is effectively 0. #236 + if ( isNullRange() ) { return false; } @@ -869,10 +900,10 @@ function closure ( target, options, originalOptions ){ // Calculate relative positions for the handles. positions = getPositions( proposal, data.positions, handles.length > 1); - state = setHandle ( handles[0], positions[h], handles.length === 1 ); + state = setHandle ( handles[0], positions[h] ); if ( handles.length > 1 ) { - state = setHandle ( handles[1], positions[h?0:1], false ) || state; + state = setHandle ( handles[1], positions[h?0:1] ) || state; } // Fire the 'slide' event if any handle moved. @@ -1041,6 +1072,38 @@ function closure ( target, options, originalOptions ){ } +// Helpers + + // Set handles from the .val method. + function loopValues ( i, values, link, update ) { + + // Use the passed link, or default to the first one, + // which stores the value. + link = link || $Serialization[i%2][0]; + + var to = link.getValue( values[i%2] ); + + if ( to !== false ) { + + // Calculate the new handle position + to = toStepping( options, to ); + + // Invert the value if this is a right-to-left slider. + if ( options.dir ) { + to = 100 - to; + } + + // Set the handle. + if ( setHandle( $Handles[i%2], to ) === true ) { + return; + } + } + + // If it the handle cannot be set, correct the Link. + link.reset( update ); + } + + // Initialize slider // Throw an error if the slider was already initialized. @@ -1068,19 +1131,13 @@ function closure ( target, options, originalOptions ){ target.vSet = function ( ) { var args = Array.prototype.slice.call( arguments, 0 ), - callback, link, update, animate, - i, count, actual, to, values = asArray( args[0] ); - - // Extract modifiers for value method. - if ( typeof args[1] === 'object' ) { - callback = args[1]['set']; - link = args[1]['link']; - update = args[1]['update']; - animate = args[1]['animate']; + i, count, values = asArray( args[0] ); // Support the 'true' option. - } else if ( args[1] === true ) { - callback = true; + if ( args[1] === true ) { + args[1] = { 'set': true }; + } else if ( typeof args[1] !== 'object' ) { + args[1] = {}; } // The RTL settings is implemented by reversing the front-end, @@ -1090,7 +1147,7 @@ function closure ( target, options, originalOptions ){ } // Animation is optional. - if ( animate ) { + if ( args[1]['animate'] ) { addClassFor( $Target, Classes[14], 300 ); } @@ -1104,46 +1161,11 @@ function closure ( target, options, originalOptions ){ // mechanism twice for the first handle, to make sure it // can be bounced of the second one properly. for ( i = 0; i < count; i++ ) { - - to = link || $Serialization[i%2][0]; - to = to.getValue( values[i%2] ); - - if ( to === false ) { - continue; - } - - // Calculate the new handle position - to = toStepping( options, to ); - - // Invert the value if this is a right-to-left slider. - if ( options.dir ) { - to = 100 - to; - } - - // Force delimitation. - if ( setHandle( $Handles[i%2], to, true ) === true ) { - continue; - } - - // Reset the input if it doesn't match the slider. - $($Serialization[i%2]).each(function(index){ - - if (!index) { - actual = this.actual; - return true; - } - - this.write( - actual, - $Handles[i%2].children(), - $Target, - update - ); - }); + loopValues( i, values, args[1]['link'], args[1]['update'] ); } // Optionally fire the 'set' event. - if( callback === true ) { + if ( args[1]['set'] === true ) { fireEvents(['set']); } @@ -1158,7 +1180,7 @@ function closure ( target, options, originalOptions ){ // Get the value from all handles. for ( i = 0; i < options.handles; i++ ){ - retour[i] = $Serialization[i][0].saved; + retour[i] = $Serialization[i][0].getSaved(); } // If only one handle is used, return a single value. @@ -1197,8 +1219,17 @@ function closure ( target, options, originalOptions ){ return originalOptions; }; + // Get the current step size for the slider. + /** @expose */ + target.getStep = function ( ) { + + // Check all locations, map them to their stepping point. + return $.map($Locations, function( value ){ + // Get the step point, then find it in the input list. + return options.xNumSteps[getStepPoint(options, value) - 1]; + }); + }; -// Value setting // Use the public value method to set the start values. $Target.val( options.start ); @@ -1255,7 +1286,7 @@ function closure ( target, options, originalOptions ){ // Override the .val() method. Test every element. Is it a slider? Go to // the slider value handling. No? Use the standard method. - // Note how $.fn.val extects 'this' to be an instance of $. For convenience, + // Note how $.fn.val expects 'this' to be an instance of $. For convenience, // the above 'value' function does too. $.fn.val = function ( ) { diff --git a/tests/run.html b/tests/run.html index 7972ce3a..f4875326 100644 --- a/tests/run.html +++ b/tests/run.html @@ -70,5 +70,6 @@ + diff --git a/tests/step-limit.js b/tests/step-limit.js new file mode 100644 index 00000000..3a9b02b4 --- /dev/null +++ b/tests/step-limit.js @@ -0,0 +1,26 @@ + + test( "Testing handling of odd-numbered steps", function(){ + + Q.html('\ +
\ + '); + + var slider = $('.slider'); + + slider.noUiSlider({ + range: { min: 3, max: 106 }, + start: [ 20, 50 ], + step: 10 + }); + + deepEqual( slider.val(), ['23.00', '53.00'] ); + + slider.val([50, 106]); + deepEqual( slider.val(), ['53.00', '106.00'], 'Slider reached edge outside from step.' ); + + slider.val([71, 105]); + deepEqual( slider.val(), ['73.00', '103.00'], 'Slider steps back into stepping with lower points as origin.' ); + + slider.val([71, 101]); + deepEqual( slider.val(), ['73.00', '103.00'] ); + }); From ee53e7993550f8cacfad4794f3d0f058bcb3714c Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Mon, 19 May 2014 22:02:03 +0200 Subject: [PATCH 02/28] Added spread calculation. Needs tests, RTL testing, vertical. --- jquery.nouislider.js | 71 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/jquery.nouislider.js b/jquery.nouislider.js index ba007db6..1e6720f6 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -182,11 +182,13 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ var j = getStepPoint( options, value ), a, b; + // If 'snap' is set, steps are used as fixed points on the slider. if ( options.snap ) { a = options.xPct[j-1]; b = options.xPct[j]; + // Find the closest position, a or b. if ((value - a) > ((b-a)/2)){ return b; } @@ -1103,6 +1105,20 @@ function closure ( target, options, originalOptions ){ link.reset( update ); } + // Returns the input array, respecting the slider direction configuration. + function inSliderOrder ( values ) { + + // If only one handle is used, return a single value. + if ( values.length === 1 ){ + return values[0]; + } + + if ( options.dir ) { + return values.reverse(); + } + + return values; + } // Initialize slider @@ -1183,16 +1199,7 @@ function closure ( target, options, originalOptions ){ retour[i] = $Serialization[i][0].getSaved(); } - // If only one handle is used, return a single value. - if ( retour.length === 1 ){ - return retour[0]; - } - - if ( options.dir ) { - return retour.reverse(); - } - - return retour; + return inSliderOrder( retour ); }; // Destroy the slider and unbind all events. @@ -1224,12 +1231,54 @@ function closure ( target, options, originalOptions ){ target.getStep = function ( ) { // Check all locations, map them to their stepping point. - return $.map($Locations, function( value ){ + var retour = $.map($Locations, function( value ){ // Get the step point, then find it in the input list. return options.xNumSteps[getStepPoint(options, value) - 1]; }); + + // Return values in the proper order. + return inSliderOrder( retour ); }; + target.getSpread = function ( edged ) { + + // We could get just the large edges. + if ( edged ) { + return options.xVal.slice(); + } + + // We'll build a list of steps. + var indexes = {}; + + $.each(options.xVal, function ( index, value ) { + + // Get the current step and the lower + upper positions. + var step = options.xNumSteps[ index ], + low = options.xVal[index], + high = options.xVal[index+1], + i; + + // Low can be 0. + if ( low === false || !high ) { + return; + } + + if ( !step && !index ) { + indexes['0'] = low; + return; + } + + // Find all steps in the subrange. + for ( i = low; i < high; i += step ) { + indexes[toStepping(options, i).toFixed(5)] = i; + } + }); + + // Add the 'max' value to the end of the list. + indexes['100'] = options.xVal[ options.xVal.length - 1 ]; + + return indexes; + }; // Use the public value method to set the start values. $Target.val( options.start ); From c2655ea72aded8bb38ddff120d48ecfdc2acce15 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sat, 24 May 2014 22:26:10 +0200 Subject: [PATCH 03/28] Started adding public API, moved Link integration. --- Link.js | 201 +++++++++++++++++++++++++++---------------- jquery.nouislider.js | 152 ++++++++++++++++---------------- nouislider.more.js | 44 ++++++++++ 3 files changed, 243 insertions(+), 154 deletions(-) create mode 100644 nouislider.more.js diff --git a/Link.js b/Link.js index 5a22dae5..7eaefbb1 100644 --- a/Link.js +++ b/Link.js @@ -21,8 +21,9 @@ $.Link (part of noUiSlider) - WTFPL */ return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) ); } + var -/** @const */ Formatting = [ +/** @const */ FormatNames = [ /* 0 */ 'decimals' /* 1 */ ,'mark' /* 2 */ ,'thousand' @@ -47,12 +48,25 @@ var /* 8 */ ,'' /* 9 */ ,function(a){ return a; } /* 10 */ ,function(a){ return a; } + ], +/** @const */ Public_Methods = [ + 'setValue', + 'setFormatting', + 'setChangeHandler', + 'setTarget', + 'resetValue', + 'getValue', + 'getFormattedValue' ]; - /** @constructor */ function Format( options ){ + // Allow 'init' from existing object. + if ( options instanceof Format ) { + return options; + } + // If no settings where provided, the defaults will be loaded. if ( options === undefined ){ options = {}; @@ -65,7 +79,7 @@ var var settings = {}; // Copy all values into a new object. - $(Formatting).each(function(i, val){ + $(FormatNames).each(function(i, val){ if ( options[val] === undefined ){ @@ -216,7 +230,6 @@ var }; -/** @expose */ /** @constructor */ function Link ( entry, update ) { @@ -226,10 +239,15 @@ var // Make sure Link isn't called as a function, in which case // the 'this' scope would be the window. - return new Link.prototype.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update ); + this.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update ); + + this.changeHandler = function ( e ) { + this.changeHandlerMethod(e); + } } - Link.prototype.setTooltip = function ( target, method ) { +// Target types + Link.prototype.configureTooltip = function ( target, method ) { // By default, use the 'html' method. this.method = method || 'html'; @@ -238,7 +256,7 @@ var this.el = $( target.replace('-tooltip-', '') || '
' )[0]; }; - Link.prototype.setHidden = function ( target ) { + Link.prototype.configureHidden = function ( target ) { this.method = 'val'; @@ -247,49 +265,35 @@ var this.el.type = 'hidden'; }; - Link.prototype.setField = function ( target ) { - - // Returns nulled array. - function at(a,b,c){ - return [c?a:b, c?b:a]; - } - - // In IE < 9, .bind() isn't available, need this link in .change(). - var that = this; + Link.prototype.configureField = function ( target ) { // Default to .val if this is an input element. this.method = 'val'; // Set the slider to a new value on change. - this.target = target.on('change', function( e ){ - that.obj.val( - at(null, $(e.target).val(), that.N), - { 'link': that, 'set': true } - ); - }); + this.target = target.on('change', this.changeHandler); }; - // Initialisor - /** @constructor */ +// Initialisor + + // Gets arguments from constructor. Link.prototype.init = function ( target, method, format, update ) { - // Write all formatting to this object. - // No validation needed, as we'll merge these with the parent - // format options first. - this.formatting = format; + // Create a new Formatter. The constructor accepts 'undefined'. + this.formatInstance = new Format(format); // Store the update option. this.update = !update; // If target is a string, a new hidden input will be created. if ( typeof target === 'string' && target.indexOf('-tooltip-') === 0 ) { - this.setTooltip( target, method ); + this.configureTooltip( target, method ); return; } // If the string doesn't begin with '-', which is reserved, add a new hidden input. if ( typeof target === 'string' && target.indexOf('-') !== 0 ) { - this.setHidden( target ); + this.configureHidden( target ); return; } @@ -300,18 +304,18 @@ var return; } - if ( isInstance(target) ) { - // If a jQuery/Zepto input element is provided, but no method is set, - // the element can assume it needs to respond to 'change'... + if ( isInstance( target ) ) { if ( !method ) { + // If a jQuery/Zepto input element is provided, but no method is set, + // the element can assume it needs to respond to 'change'... if ( target.is('input, select, textarea') ) { - this.setField( target ); + this.configureField( target ); return; } - // If no method is set, and we are not auto-binding an input, default to 'html'. + // If no method is set, and we are not auto-binding an input, default to 'html'. method = 'html'; } @@ -327,78 +331,123 @@ var throw new RangeError("(Link) Invalid Link."); }; + +// Public setters + // Provides external items with the slider value. - Link.prototype.write = function ( value, handle, slider, update ) { + Link.prototype['setValue'] = function ( value, update ) { // Don't synchronize this Link. if ( this.update && update === false ) { return; } - // Store the numerical value. - this.actual = value; + // Ignore named arguments value and update, so only the passed-on + // arguments remain. + var args = Array.prototype.slice.call( arguments ), + additionalArgs = args.slice(2); - // Format values for display. - value = this.format( value ); + console.log(this); - // Store the formatted value. - this.saved = value; + // Store some values. The actual, numerical value, + // the formatted value and the parameters for use in 'resetValue'. + // Slice additionalArgs to break the relation. + this.actual = value; + this.saved = this.formatInstance.to( value ); + this.args = additionalArgs.slice(); + + // Prepend the value to the function arguments. + additionalArgs.unshift( this.saved ); - // Store parameters for use in reset() - this.resetHandle = handle; - this.resetSlider = slider; + // When target is undefined, the target was a function. + // In that case, provided the slider as the calling scope. + // Use [0] to get the DOM element, not the $ instance. // Branch between serialization to a function or an object. - if ( typeof this.method === 'function' ) { - // When target is undefined, the target was a function. - // In that case, provided the slider as the calling scope. - // Use [0] to get the DOM element, not the $ instance. - this.method.call( this.target[0] || slider[0], value, handle, slider ); - } else { - this.target[ this.method ]( value, handle, slider ); - } + ( typeof this.method === 'function' ? + this.method : + this.target[ this.method ] ).apply( this.target, additionalArgs ); }; // Set formatting options. - Link.prototype.setFormatting = function ( options ) { - this.formatting = new Format($.extend({}, - options, - this.formatting instanceof Format ? this.formatting.settings : this.formatting - )); + Link.prototype['setFormatting'] = function ( options ) { + this.formatInstance = new Format ( + $.extend( {}, options, this.formatInstance.settings ) + ); }; - Link.prototype.setObject = function ( obj ) { - this.obj = obj; - }; + // Set the event handler to be triggered when a linked input changes. + Link.prototype['setChangeHandler'] = function ( handler ) { + this.changeHandlerMethod = handler; + } - Link.prototype.setIndex = function ( index ) { - this.N = index; + // Set the target if it isn't set yet. + Link.prototype['setTarget'] = function ( target ) { + if ( !this.target ) { + this.target = target; + } }; - // Parses slider value to user defined display. - Link.prototype.format = function ( a ) { - return this.formatting.to(a); - }; - // Allow calling the 'write' method from cache. - Link.prototype.reset = function ( update ) { - this.write( this.actual, this.resetHandle, this.resetSlider, update ); +// Public getters + + // Allow calling the 'setValue' method from cache. + Link.prototype['resetValue'] = function ( update ) { + var args = this.args.slice(); + args.unshift(this.actual, update); + this.setValue.apply(this, args); }; // Converts a formatted value back to a real number. - Link.prototype.getValue = function ( a ) { - return this.formatting.from(a); + Link.prototype['getValue'] = function ( a ) { + return this.formatInstance.from(a); }; // Return saved (formatted) value. - Link.prototype.getSaved = function ( a ) { + Link.prototype['getFormattedValue'] = function ( a ) { return this.saved; }; - // We can now test for Link.init to be an instance of Link. - Link.prototype.init.prototype = Link.prototype; + +// Expose a wrapper, not Link + + function closure ( a, b ) { + + var linkInstance = new Link(a,b), + that = this; + + // Forward calls, but don't expose the actual Link. + $.each(Public_Methods, function( index, value ){ + that[value] = function(){ + return linkInstance[value].apply(linkInstance, arguments); + }; + }); + + // See if this Link needs individual targets based on its usage. + // If so, return the element that needs to be copied by the + // implementing interface. + this.needsClone = function(){ + return linkInstance.el || false; + }; + + // Create a new instance. + this.clone = function( target ){ + return new LinkWrapper({ + 'target': target, + 'method': this.method, + 'format': this.formatInstance + }, true); + }; + } + + function LinkWrapper ( a, b ) { + if ( !(this instanceof LinkWrapper) ) { + return new LinkWrapper(a,b); + } + closure.call( this, a, b ); + } /** @expose */ - $.Link = Link; + $.Link = LinkWrapper; }( window['jQuery'] || window['Zepto'] )); diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 1e6720f6..f5864d74 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -78,6 +78,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return (100 / (pb - pa)); } + // Returns reordered array. + function reIndex ( a, b, c ){ + return [c?a:b, c?b:a]; + } + // Type validation @@ -358,9 +363,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ function testStart ( parsed, entry ) { - if ( typeof entry === "number" ) { - entry = [entry]; - } + entry = asArray(entry); // Validate input. Values aren't tested, the internal Link will do // that and provide a valid location. @@ -486,17 +489,31 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' must be an array."); } - $.each(linkInstances, function(){ + $.each(linkInstances, function( ignore, linkInstance ){ // Check if entry is a Link. - if ( !(this instanceof $.Link) ) { + if ( !(linkInstance instanceof $.Link) ) { throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' can only contain Link instances."); } // Assign properties. - this.setIndex ( index ); - this.setObject( sliders ); - this.setFormatting( entry['format'] ); + linkInstance.setTarget( sliders ); + linkInstance.setFormatting( entry['format'] ); + + // Set the changehandler to call .val on input change. + linkInstance.setChangeHandler(function( e ){ + linkInstance.target.val( + // Determine which array position to 'null' based on 'index'. + reIndex( + null, + $(e.target).val(), + index + ), { + 'link': linkInstance, + 'set': true + } + ); + }); }); }); @@ -605,28 +622,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return handle; } - // Create a copy of an element-creating Link. - function addElement ( handle, link ) { - - // If the Link requires creation of a new element, - // create this element and return a new Link instance. - if ( link.el ) { - - link = new $.Link({ - 'target': $(link.el).clone().appendTo( handle ), - 'method': link.method, - 'format': link.formatting - }, true); - } - - // Otherwise, return the reference. - return link; - } - // Loop all links for a handle. function addElements ( elements, handle, formatting ) { - var index, list = [], standard = new $.Link({}, true); + var list = [], standard = new $.Link({}, true), + oldElement, newElement; // Use the Link interface to provide unified // formatting for the .val() method. @@ -636,9 +636,26 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ list.push( standard ); // Loop all links in either 'lower' or 'upper'. - for ( index = 0; index < elements.length; index++ ) { - list.push(addElement(handle, elements[index])); - } + $.each(elements, function( index, linkInstance ){ + + // If the Link requires creation of a new element, + // create this element and return a new Link instance. + oldElement = linkInstance.needsClone(); + + if ( oldElement ) { + + // Clone the old element (which isn't in the DOM) and add it to the handle. + newElement = $( oldElement ).clone().appendTo( handle ); + + // Create a new linkInstance. + list.push( linkInstance.clone( newElement ) ); + + } else { + + // This linkInstance needs no clone, use it immediately. + list.push( linkInstance ); + } + }); return list; } @@ -810,7 +827,7 @@ function closure ( target, options, originalOptions ){ // Convert the value to the correct relative representation. // Convert the value to the slider stepping/range. $($Serialization[n]).each(function(){ - this.write( fromStepping( options, to ), handle.children(), $Target ); + this.setValue( fromStepping( options, to ), true, handle.children(), $Target ); }); return true; @@ -1077,13 +1094,13 @@ function closure ( target, options, originalOptions ){ // Helpers // Set handles from the .val method. - function loopValues ( i, values, link, update ) { + function loopValues ( i, values, linkInstance, update ) { - // Use the passed link, or default to the first one, + // Use the passed linkInstance, or default to the first one, // which stores the value. - link = link || $Serialization[i%2][0]; + linkInstance = linkInstance || $Serialization[i%2][0]; - var to = link.getValue( values[i%2] ); + var to = linkInstance.getValue( values[i%2] ); if ( to !== false ) { @@ -1101,8 +1118,8 @@ function closure ( target, options, originalOptions ){ } } - // If it the handle cannot be set, correct the Link. - link.reset( update ); + // If it the handle cannot be set, correct the linkInstance. + linkInstance.resetValue( update ); } // Returns the input array, respecting the slider direction configuration. @@ -1146,7 +1163,7 @@ function closure ( target, options, originalOptions ){ /** @expose */ target.vSet = function ( ) { - var args = Array.prototype.slice.call( arguments, 0 ), + var args = Array.prototype.slice.call( arguments ), i, count, values = asArray( args[0] ); // Support the 'true' option. @@ -1196,7 +1213,7 @@ function closure ( target, options, originalOptions ){ // Get the value from all handles. for ( i = 0; i < options.handles; i++ ){ - retour[i] = $Serialization[i][0].getSaved(); + retour[i] = $Serialization[i][0].getFormattedValue(); } return inSliderOrder( retour ); @@ -1240,44 +1257,23 @@ function closure ( target, options, originalOptions ){ return inSliderOrder( retour ); }; - target.getSpread = function ( edged ) { - - // We could get just the large edges. - if ( edged ) { - return options.xVal.slice(); - } - - // We'll build a list of steps. - var indexes = {}; - - $.each(options.xVal, function ( index, value ) { - - // Get the current step and the lower + upper positions. - var step = options.xNumSteps[ index ], - low = options.xVal[index], - high = options.xVal[index+1], - i; - - // Low can be 0. - if ( low === false || !high ) { - return; - } - - if ( !step && !index ) { - indexes['0'] = low; - return; - } - - // Find all steps in the subrange. - for ( i = low; i < high; i += step ) { - indexes[toStepping(options, i).toFixed(5)] = i; + // Return clones of some private variables. + /** @expose */ + target.api = function ( ) { + return { + 'slider': this, + 'range': { + 'positions': options.xPct.slice(), + 'values': options.xVal.slice() + }, + 'steps': { + 'positions': options.xSteps.slice(), + 'values': options.xNumSteps.slice() + }, + 'toStepping': function ( value ) { + return toStepping( options, value ); } - }); - - // Add the 'max' value to the end of the list. - indexes['100'] = options.xVal[ options.xVal.length - 1 ]; - - return indexes; + } }; // Use the public value method to set the start values. @@ -1364,8 +1360,8 @@ function closure ( target, options, originalOptions ){ // Extend jQuery/Zepto with the noUiSlider method. /** @expose */ - $.fn.noUiSlider = function ( options, re ) { - return ( re ? rebuild : initialize ).call(this, options); + $.fn.noUiSlider = function ( options, rebuildFlag ) { + return options === 'api' ? this[0].api() : ( rebuildFlag ? rebuild : initialize ).call(this, options); }; }( window['jQuery'] || window['Zepto'] )); diff --git a/nouislider.more.js b/nouislider.more.js new file mode 100644 index 00000000..79c05230 --- /dev/null +++ b/nouislider.more.js @@ -0,0 +1,44 @@ +(function( $ ){ + + /** @expose */ + $.noUiSlider.spread = function ( api, mode ) { + + // Set 'mode' to true for only the actual range points. + if ( mode === true ) { + return api.range.values; + } + + // We'll build a list of steps. + var indexes = {}, last = api.range.values.length - 1; + + $.each(api.range.values, function ( index, value ) { + + // Get the current step and the lower + upper positions. + var step = api.steps.values[ index ], + low = api.range.values[index], + high = api.range.values[index+1], + i; + + // Low can be 0. + if ( low === false || !high ) { + return; + } + + if ( !step && !index ) { + indexes['0'] = low; + return; + } + + // Find all steps in the subrange. + for ( i = low; i < high; i += step ) { + indexes[api.toStepping(i).toFixed(5)] = i; + } + }); + + // Add the 'max' value to the end of the list. + indexes['100'] = api.range.values[ last ]; + + return indexes; + }; + +}( window['jQuery'] || window['Zepto'] )); From 9c8147f0b27db9e2dd1954e31331729ceb6095da Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 25 May 2014 21:51:49 +0200 Subject: [PATCH 04/28] Did some work on decoupling Link, Format --- Format.js | 226 ++++++++++++++++++++++++++++++++++ Link.js | 286 ++++++------------------------------------- jquery.nouislider.js | 232 +++++++++++++++++------------------ 3 files changed, 379 insertions(+), 365 deletions(-) create mode 100644 Format.js diff --git a/Format.js b/Format.js new file mode 100644 index 00000000..6366916f --- /dev/null +++ b/Format.js @@ -0,0 +1,226 @@ +/**@preserve +Numerical formatting engine - Link Protocol */ + +//(function(){ + +var +/** @const */ FormatNames = [ +/* 0 */ 'decimals' +/* 1 */ ,'mark' +/* 2 */ ,'thousand' +/* 3 */ ,'prefix' +/* 4 */ ,'postfix' +/* 5 */ ,'encoder' +/* 6 */ ,'decoder' +/* 7 */ ,'negative' +/* 8 */ ,'negativeBefore' +/* 9 */ ,'to' +/* 10 */ ,'from' + ], +/** @const */ FormatDefaults = [ +/* 0 */ 2 +/* 1 */ ,'.' +/* 2 */ ,'' +/* 3 */ ,'' +/* 4 */ ,'' +/* 5 */ ,function(a){ return a; } +/* 6 */ ,function(a){ return a; } +/* 7 */ ,'-' +/* 8 */ ,'' +/* 9 */ ,function(a){ return a; } +/* 10 */ ,function(a){ return a; } + ]; + + function esc(s){ + return s.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&'); + } + + function reverse ( a ) { + return a.split('').reverse().join(''); + } + + // Throw an error if formatting options are incompatible. + function throwEqualError( F, a, b ) { + if ( (F[a] || F[b]) && (F[a] === F[b]) ) { + throw new Error("(Link) '"+a+"' can't match '"+b+"'.'"); + } + } + + function validate ( options ) { + + var settings = {}; + + // Copy all values into a new object. + $(FormatNames).each(function(i, val){ + + if ( options[val] === undefined ){ + + settings[val] = FormatDefaults[i]; + + // When we aren't loading defaults, validate the entry. + } else if ( (typeof options[val]) === (typeof FormatDefaults[i]) ) { + + // Support for up to 7 decimals. + // More can't be guaranteed due to floating point issues. + if ( val === 'decimals' ){ + if ( options[val] < 0 || options[val] > 7 ){ + throw new Error("(Format) 'format.decimals' option must be between 0 and 7."); + } + } + + settings[val] = options[val]; + + // If the value isn't valid, emit an error. + } else { + throw new Error("(Format) 'format."+val+"' must be a " + typeof FormatDefaults[i] + "."); + } + }); + + // Some values can't be extracted from a + // string if certain combinations are present. + throwEqualError(settings, 'mark', 'thousand'); + throwEqualError(settings, 'prefix', 'negative'); + throwEqualError(settings, 'prefix', 'negativeBefore'); + + return settings; + } + + function toFormatting ( options, number ) { + + number = options['encoder']( number ); + + var decimals = options['decimals'], + negative = '', preNegative = '', base = '', mark = ''; + + // Rounding away decimals might cause a value of -0 + // when using very small ranges. Remove those cases. + if ( parseFloat(number.toFixed(decimals)) === 0 ) { + number = '0'; + } + + if ( number < 0 ) { + negative = options['negative']; + preNegative = options['negativeBefore']; + } + + // Round to proper decimal count + number = Math.abs(number).toFixed(decimals).toString(); + number = number.split('.'); + + // Group numbers in sets of three. + if ( options['thousand'] ) { + base = reverse(number[0]).match(/.{1,3}/g); + base = reverse(base.join(reverse( options['thousand'] ))); + } else { + base = number[0]; + } + + // Ignore the decimal separator if decimals are set to 0. + if ( options['mark'] && number.length > 1 ) { + mark = options['mark'] + number[1]; + } + + // Return the finalized formatted number. + return options['to'](preNegative + + options['prefix'] + + negative + + base + + mark + + options['postfix']); + } + + function fromFormatting ( options, input ) { + + var isNeg; + // 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 ( input === null || input === undefined ) { + return false; + } + + input = options['from'](input); + + // Remove formatting and set period for float parsing. + input = input.toString(); + + // Replace the preNegative indicator. + isNeg = input.replace( new RegExp( '^' + esc(options['negativeBefore']) ), ''); + + // Check if the value changed by removing the negativeBefore symbol. + if( input !== isNeg ) { + input = isNeg; + isNeg = '-'; + } else { + isNeg = ''; + } + + // If prefix is set and the number is actually prefixed. + input = input.replace(new RegExp( '^' + esc(options['prefix']) ), ''); + + // Only replace if a negative sign is set. + if ( options['negative'] ) { + + // Reset isNeg to prevent double '-' insertion. + isNeg = ''; + + // Reset the negative sign to '-' + input = input.replace(new RegExp( '^' + esc(options['negative']) ), '-'); + } + + // Clean the input string + input = input + // If postfix is set and the number is postfixed. + .replace( new RegExp(esc( options['postfix'] ) + '$'), '') + // Remove the separator every three digits. + .replace( new RegExp(esc( options['thousand'] ), 'g'), '') + // Set the decimal separator back to period. + .replace( options['mark'], '.'); + + // Run the user defined decoder. Returns input by default. + input = options['decoder']( parseFloat( isNeg + input ) ); + + // Ignore invalid input + if (isNaN( input )) { + return false; + } + + return input; + } + + + /** @constructor */ + function Format( options ){ + + // Allow 'init' from existing object. + if ( options instanceof Format ) { + return options; + } + + // If no settings where provided, the defaults will be loaded. + if ( options === undefined ){ + options = {}; + } + + if ( typeof options !== 'object' ){ + throw new Error("(Format) 'format' option must be an object."); + } + + this.settings = validate( options ); + } + + Format.prototype.extendUpon = function ( formatInstance ) { +// console.log( formatInstance.settings ); +// this.settings = $.extend( {}, formatInstance.settings, this.settings ); +// console.log( this.settings ); + }; + + Format.prototype.to = function(a){ + return toFormatting(this.settings, a); + }; + + Format.prototype.from = function(a){ + return fromFormatting(this.settings, a); + }; + +//}()); diff --git a/Link.js b/Link.js index 7eaefbb1..7f908918 100644 --- a/Link.js +++ b/Link.js @@ -9,13 +9,6 @@ $.Link (part of noUiSlider) - WTFPL */ 'use strict'; - // Throw an error if formatting options are incompatible. - function throwEqualError( F, a, b ) { - if ( (F[a] || F[b]) && (F[a] === F[b]) ) { - throw new Error("(Link) '"+a+"' can't match '"+b+"'.'"); - } - } - // Test in an object is an instance of jQuery or Zepto. function isInstance ( a ) { return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) ); @@ -23,227 +16,31 @@ $.Link (part of noUiSlider) - WTFPL */ var -/** @const */ FormatNames = [ -/* 0 */ 'decimals' -/* 1 */ ,'mark' -/* 2 */ ,'thousand' -/* 3 */ ,'prefix' -/* 4 */ ,'postfix' -/* 5 */ ,'encoder' -/* 6 */ ,'decoder' -/* 7 */ ,'negative' -/* 8 */ ,'negativeBefore' -/* 9 */ ,'to' -/* 10 */ ,'from' - ], -/** @const */ FormatDefaults = [ -/* 0 */ 2 -/* 1 */ ,'.' -/* 2 */ ,'' -/* 3 */ ,'' -/* 4 */ ,'' -/* 5 */ ,function(a){ return a; } -/* 6 */ ,function(a){ return a; } -/* 7 */ ,'-' -/* 8 */ ,'' -/* 9 */ ,function(a){ return a; } -/* 10 */ ,function(a){ return a; } - ], /** @const */ Public_Methods = [ + 'implement', 'setValue', - 'setFormatting', - 'setChangeHandler', - 'setTarget', 'resetValue', - 'getValue', - 'getFormattedValue' + 'fromFormat', + 'toFormat' ]; - /** @constructor */ - function Format( options ){ - - // Allow 'init' from existing object. - if ( options instanceof Format ) { - return options; - } - - // If no settings where provided, the defaults will be loaded. - if ( options === undefined ){ - options = {}; - } - - if ( typeof options !== 'object' ){ - throw new Error("(Format) 'format' option must be an object."); - } - - var settings = {}; - - // Copy all values into a new object. - $(FormatNames).each(function(i, val){ - - if ( options[val] === undefined ){ - - settings[val] = FormatDefaults[i]; - - // When we aren't loading defaults, validate the entry. - } else if ( (typeof options[val]) === (typeof FormatDefaults[i]) ) { - - // Support for up to 7 decimals. - // More can't be guaranteed due to floating point issues. - if ( val === 'decimals' ){ - if ( options[val] < 0 || options[val] > 7 ){ - throw new Error("(Format) 'format.decimals' option must be between 0 and 7."); - } - } - - settings[val] = options[val]; - - // If the value isn't valid, emit an error. - } else { - throw new Error("(Format) 'format."+val+"' must be a " + typeof FormatDefaults[i] + "."); - } - }); - - // Some values can't be extracted from a - // string if certain combinations are present. - throwEqualError(settings, 'mark', 'thousand'); - throwEqualError(settings, 'prefix', 'negative'); - throwEqualError(settings, 'prefix', 'negativeBefore'); - - this.settings = settings; - } - - // Shorthand for internal value get - Format.prototype.v = function ( a ) { - return this.settings[a]; - }; - - Format.prototype.to = function ( number ) { - - function reverse ( a ) { - return a.split('').reverse().join(''); - } - - number = this.v('encoder')( number ); - - var decimals = this.v('decimals'), - negative = '', preNegative = '', base = '', mark = ''; - - // Rounding away decimals might cause a value of -0 - // when using very small ranges. Remove those cases. - if ( parseFloat(number.toFixed(decimals)) === 0 ) { - number = '0'; - } - - if ( number < 0 ) { - negative = this.v('negative'); - preNegative = this.v('negativeBefore'); - } - - // Round to proper decimal count - number = Math.abs(number).toFixed(decimals).toString(); - number = number.split('.'); - - // Group numbers in sets of three. - if ( this.v('thousand') ) { - base = reverse(number[0]).match(/.{1,3}/g); - base = reverse(base.join(reverse( this.v('thousand') ))); - } else { - base = number[0]; - } - - // Ignore the decimal separator if decimals are set to 0. - if ( this.v('mark') && number.length > 1 ) { - mark = this.v('mark') + number[1]; - } - - // Return the finalized formatted number. - return this.v('to')(preNegative + - this.v('prefix') + - negative + - base + - mark + - this.v('postfix')); - }; - - Format.prototype.from = function ( input ) { - - function esc(s){ - return s.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&'); - } - - var isNeg; - // 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 ( input === null || input === undefined ) { - return false; - } - - input = this.v('from')(input); - - // Remove formatting and set period for float parsing. - input = input.toString(); - - // Replace the preNegative indicator. - isNeg = input.replace(new RegExp('^' + esc( this.v('negativeBefore') )), ''); - - // Check if the value changed by removing the negativeBefore symbol. - if( input !== isNeg ) { - input = isNeg; - isNeg = '-'; - } else { - isNeg = ''; - } - - // If prefix is set and the number is actually prefixed. - input = input.replace(new RegExp('^'+esc( this.v('prefix') )), ''); - - // Only replace if a negative sign is set. - if ( this.v('negative') ) { - - // Reset isNeg to prevent double '-' insertion. - isNeg = ''; - - // Reset the negative sign to '-' - input = input.replace(new RegExp('^'+esc( this.v('negative') )), '-'); - } - - // Clean the input string - input = input - // If postfix is set and the number is postfixed. - .replace( new RegExp(esc( this.v('postfix') ) + '$'), '') - // Remove the separator every three digits. - .replace( new RegExp(esc( this.v('thousand') ), 'g'), '') - // Set the decimal separator back to period. - .replace( this.v('mark'), '.'); - - // Run the user defined decoder. Returns input by default. - input = this.v('decoder')( parseFloat( isNeg + input ) ); - - // Ignore invalid input - if (isNaN( input )) { - return false; - } - - return input; - }; - - /** @constructor */ function Link ( entry, update ) { if ( typeof entry !== "object" ) { - $.error("(Link) Initialize with an object."); + throw new Error("(Link) Initialize with an object."); } - // Make sure Link isn't called as a function, in which case - // the 'this' scope would be the window. - this.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update ); + var that = this; + // Forward calls within scope. this.changeHandler = function ( e ) { - this.changeHandlerMethod(e); + that.changeHandlerMethod(e); } + + // Make sure Link isn't called as a function, in which case + // the 'this' scope would be the window. + this.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update ); } // Target types @@ -270,6 +67,7 @@ var // Default to .val if this is an input element. this.method = 'val'; // Set the slider to a new value on change. + this.target = target.on('change', this.changeHandler); }; @@ -332,7 +130,25 @@ var }; -// Public setters +// Developer calls + + Link.prototype['implement'] = function ( alternativeTarget, baseFormattingOptions, changeHandlerMethod ) { + + // Set the target if it isn't set yet. + if ( alternativeTarget && !this.target ) { + this.target = target; + } + + // Set formatting options. + // this.formatInstance.extendUpon( new Format(baseFormattingOptions) ); + + this.changeHandlerMethod = changeHandlerMethod; + + return this; + }; + + +// Value interaction // Provides external items with the slider value. Link.prototype['setValue'] = function ( value, update ) { @@ -347,17 +163,14 @@ var var args = Array.prototype.slice.call( arguments ), additionalArgs = args.slice(2); - console.log(this); - // Store some values. The actual, numerical value, // the formatted value and the parameters for use in 'resetValue'. // Slice additionalArgs to break the relation. this.actual = value; - this.saved = this.formatInstance.to( value ); this.args = additionalArgs.slice(); // Prepend the value to the function arguments. - additionalArgs.unshift( this.saved ); + additionalArgs.unshift( this.toFormat() ); // When target is undefined, the target was a function. // In that case, provided the slider as the calling scope. @@ -369,28 +182,6 @@ var this.target[ this.method ] ).apply( this.target, additionalArgs ); }; - // Set formatting options. - Link.prototype['setFormatting'] = function ( options ) { - this.formatInstance = new Format ( - $.extend( {}, options, this.formatInstance.settings ) - ); - }; - - // Set the event handler to be triggered when a linked input changes. - Link.prototype['setChangeHandler'] = function ( handler ) { - this.changeHandlerMethod = handler; - } - - // Set the target if it isn't set yet. - Link.prototype['setTarget'] = function ( target ) { - if ( !this.target ) { - this.target = target; - } - }; - - -// Public getters - // Allow calling the 'setValue' method from cache. Link.prototype['resetValue'] = function ( update ) { var args = this.args.slice(); @@ -398,14 +189,17 @@ var this.setValue.apply(this, args); }; + +// Format interaction + // Converts a formatted value back to a real number. - Link.prototype['getValue'] = function ( a ) { - return this.formatInstance.from(a); + Link.prototype['fromFormat'] = function ( value ) { + return this.formatInstance.from( value ); }; - // Return saved (formatted) value. - Link.prototype['getFormattedValue'] = function ( a ) { - return this.saved; + // Format a real number using the formatter. + Link.prototype['toFormat'] = function ( value ) { + return this.formatInstance.to( this.actual ); }; diff --git a/jquery.nouislider.js b/jquery.nouislider.js index f5864d74..fab00c33 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -78,11 +78,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return (100 / (pb - pa)); } - // Returns reordered array. - function reIndex ( a, b, c ){ - return [c?a:b, c?b:a]; - } - // Type validation @@ -365,8 +360,8 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ entry = asArray(entry); - // Validate input. Values aren't tested, the internal Link will do - // that and provide a valid location. + // Validate input. Values aren't tested, as the public .val method + // will always provide a valid location. if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) { throw new Error("noUiSlider: 'start' option is incorrect."); } @@ -486,34 +481,19 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Check if the provided option is an array. if ( !$.isArray(linkInstances) ) { - throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' must be an array."); + throw new Error( "noUiSlider: 'serialization." + + (!index ? 'lower' : 'upper') + "' must be an array." ); } $.each(linkInstances, function( ignore, linkInstance ){ // Check if entry is a Link. - if ( !(linkInstance instanceof $.Link) ) { - throw new Error("noUiSlider: 'serialization."+(!index ? 'lower' : 'upper')+"' can only contain Link instances."); + if ( !( linkInstance instanceof $.Link ) ) { + throw new Error( "noUiSlider: 'serialization." + + (!index ? 'lower' : 'upper') + "' can only contain Link instances." ); } - // Assign properties. - linkInstance.setTarget( sliders ); - linkInstance.setFormatting( entry['format'] ); - - // Set the changehandler to call .val on input change. - linkInstance.setChangeHandler(function( e ){ - linkInstance.target.val( - // Determine which array position to 'null' based on 'index'. - reIndex( - null, - $(e.target).val(), - index - ), { - 'link': linkInstance, - 'set': true - } - ); - }); + linkInstance.implement( sliders, parsed.formatting, createChangeHandler( index )); }); }); @@ -622,65 +602,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return handle; } - // Loop all links for a handle. - function addElements ( elements, handle, formatting ) { - - var list = [], standard = new $.Link({}, true), - oldElement, newElement; - - // Use the Link interface to provide unified - // formatting for the .val() method. - standard.setFormatting(formatting); - - // The list now contains at least one element. - list.push( standard ); - - // Loop all links in either 'lower' or 'upper'. - $.each(elements, function( index, linkInstance ){ - - // If the Link requires creation of a new element, - // create this element and return a new Link instance. - oldElement = linkInstance.needsClone(); - - if ( oldElement ) { - - // Clone the old element (which isn't in the DOM) and add it to the handle. - newElement = $( oldElement ).clone().appendTo( handle ); - - // Create a new linkInstance. - list.push( linkInstance.clone( newElement ) ); - - } else { - - // This linkInstance needs no clone, use it immediately. - list.push( linkInstance ); - } - }); - - return list; - } - - // Go over all Links and assign them to a handle. - function addLinks ( options, handles ) { - - var index, links = []; - - // Copy the links into a new array, instead of modifying - // the 'options.ser' list. This allows replacement of the invalid - // '.el' Links, while the others are still passed by reference. - for ( index = 0; index < options.handles; index++ ) { - - // Append a new array. - links[index] = addElements( - options.ser[index], - handles[index].children(), - options.formatting - ); - } - - return links; - } - // Add the proper connection classes. function addConnection ( connect, target, handles ) { @@ -701,7 +622,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } } - // Add handles and loop Link elements. + // Add handles to the slider base. function addHandles ( options, base ) { var index, handles = []; @@ -730,6 +651,85 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } +// Implement the $.Link library + + // Go over all Links and assign them to a handle. + function addLinks ( options, handles ) { + + var index, links = []; + + // Copy the links into a new array, instead of modifying + // the 'options.ser' list. This allows replacement of the invalid + // '.el' Links, while the others are still passed by reference. + for ( index = 0; index < options.handles; index++ ) { + + // Use the Link interface to provide unified + // formatting for the .val() method. + // The list now contains at least one element. + links[index] = [ new $.Link({}, true).implement(null, options.formatting) ]; + + // Loop all links in either 'lower' or 'upper'. + $.each(options.ser[index], function( ignore, linkInstance ){ + + // If the Link requires creation of a new element, + // create this element and return a new Link instance. + var targetElement = linkInstance.needsClone(); + + if ( targetElement ) { + + // Clone the old element (which isn't in the DOM) and add it to the handle. + targetElement = $( targetElement ).clone().appendTo( handles[index].children() ); + + // Create a new linkInstance. + links[index].push( linkInstance.clone( targetElement ) ); + + } else { + + // This linkInstance needs no clone, use it immediately. + links[index].push( linkInstance ); + } + }); + } + + return links; + } + + // Create a new function which calls .val on input change. + function createChangeHandler ( index ) { + + // Returns reordered array. + function reIndex ( a, b, c ){ + return [c?a:b, c?b:a]; + } + + return function ( e ){ + + console.log(this); + + this.target.val( + // Determine which array position to 'null' based on 'index'. + reIndex( null, $( e.target ).val(), index ), { + 'link': this, + 'set': true + } + ); + } + } + + // Get value from a Link + function aquireValue ( value, linkInstance, update ) { + + var to = linkInstance.fromFormat( value ); + + // If it the handle cannot be set, correct the linkInstance. + if ( to === false ) { + linkInstance.resetValue( update ); + } + + return to; + } + + // Slider scope function closure ( target, options, originalOptions ){ @@ -1093,34 +1093,6 @@ function closure ( target, options, originalOptions ){ // Helpers - // Set handles from the .val method. - function loopValues ( i, values, linkInstance, update ) { - - // Use the passed linkInstance, or default to the first one, - // which stores the value. - linkInstance = linkInstance || $Serialization[i%2][0]; - - var to = linkInstance.getValue( values[i%2] ); - - if ( to !== false ) { - - // Calculate the new handle position - to = toStepping( options, to ); - - // Invert the value if this is a right-to-left slider. - if ( options.dir ) { - to = 100 - to; - } - - // Set the handle. - if ( setHandle( $Handles[i%2], to ) === true ) { - return; - } - } - - // If it the handle cannot be set, correct the linkInstance. - linkInstance.resetValue( update ); - } // Returns the input array, respecting the slider direction configuration. function inSliderOrder ( values ) { @@ -1164,7 +1136,7 @@ function closure ( target, options, originalOptions ){ target.vSet = function ( ) { var args = Array.prototype.slice.call( arguments ), - i, count, values = asArray( args[0] ); + i, count, to, values = asArray( args[0] ); // Support the 'true' option. if ( args[1] === true ) { @@ -1194,7 +1166,29 @@ function closure ( target, options, originalOptions ){ // mechanism twice for the first handle, to make sure it // can be bounced of the second one properly. for ( i = 0; i < count; i++ ) { - loopValues( i, values, args[1]['link'], args[1]['update'] ); + + to = aquireValue( + // The passed-in value. + values[i%2], + // The passed linkInstance, or default to the first one. + args[1]['link'] || $Serialization[i%2][0], + // Whether the link updates for the new value. + args[1]['update'] + ); + + if ( to !== false ) { + + // Calculate the new handle position + to = toStepping( options, to ); + + // Invert the value if this is a right-to-left slider. + if ( options.dir ) { + to = 100 - to; + } + + // Set the handle. + setHandle( $Handles[i%2], to ); + } } // Optionally fire the 'set' event. @@ -1213,7 +1207,7 @@ function closure ( target, options, originalOptions ){ // Get the value from all handles. for ( i = 0; i < options.handles; i++ ){ - retour[i] = $Serialization[i][0].getFormattedValue(); + retour[i] = $Serialization[i][0].toFormat(); } return inSliderOrder( retour ); From 28e1a750385deac72bb6e34a07d7ca814d5b582c Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Tue, 3 Jun 2014 21:12:01 +0200 Subject: [PATCH 05/28] Started updating tests to new link system --- Format.js | 226 --------- Link.js | 247 ---------- jquery.nouislider.js | 436 ++++++++++-------- nouislider.more.js | 44 -- tests/{inputs.js => 01.inputs.js} | 24 +- tests/02.shared-link.js | 42 ++ tests/03.format.js | 79 ++++ tests/{select.js => 04.select.js} | 31 +- tests/{set.js => 05.set.js} | 20 +- tests/{events.js => 06.events.js} | 10 +- tests/07.rtl-set.js | 30 ++ tests/{update.js => 08.update.js} | 66 +-- tests/{links.js => 09.links.js} | 65 ++- tests/{rtl.js => 10.rtl.js} | 57 +-- tests/{val.js => 11.val.js} | 8 +- tests/{update-link.js => 12.update-link.js} | 38 +- tests/{default-val.js => 13.default-val.js} | 5 +- ...non-linear-rtl.js => 14.non-linear-rtl.js} | 12 +- tests/{step-limit.js => 15.step-limit.js} | 5 +- tests/errors.js | 69 --- tests/format.js | 82 ---- tests/run.html | 57 +-- tests/shared-link.js | 47 -- 23 files changed, 561 insertions(+), 1139 deletions(-) delete mode 100644 Format.js delete mode 100644 Link.js delete mode 100644 nouislider.more.js rename tests/{inputs.js => 01.inputs.js} (79%) create mode 100644 tests/02.shared-link.js create mode 100644 tests/03.format.js rename tests/{select.js => 04.select.js} (68%) rename tests/{set.js => 05.set.js} (74%) rename tests/{events.js => 06.events.js} (92%) create mode 100644 tests/07.rtl-set.js rename tests/{update.js => 08.update.js} (50%) rename tests/{links.js => 09.links.js} (76%) rename tests/{rtl.js => 10.rtl.js} (50%) rename tests/{val.js => 11.val.js} (93%) rename tests/{update-link.js => 12.update-link.js} (74%) rename tests/{default-val.js => 13.default-val.js} (93%) rename tests/{non-linear-rtl.js => 14.non-linear-rtl.js} (90%) rename tests/{step-limit.js => 15.step-limit.js} (91%) delete mode 100644 tests/errors.js delete mode 100644 tests/format.js delete mode 100644 tests/shared-link.js diff --git a/Format.js b/Format.js deleted file mode 100644 index 6366916f..00000000 --- a/Format.js +++ /dev/null @@ -1,226 +0,0 @@ -/**@preserve -Numerical formatting engine - Link Protocol */ - -//(function(){ - -var -/** @const */ FormatNames = [ -/* 0 */ 'decimals' -/* 1 */ ,'mark' -/* 2 */ ,'thousand' -/* 3 */ ,'prefix' -/* 4 */ ,'postfix' -/* 5 */ ,'encoder' -/* 6 */ ,'decoder' -/* 7 */ ,'negative' -/* 8 */ ,'negativeBefore' -/* 9 */ ,'to' -/* 10 */ ,'from' - ], -/** @const */ FormatDefaults = [ -/* 0 */ 2 -/* 1 */ ,'.' -/* 2 */ ,'' -/* 3 */ ,'' -/* 4 */ ,'' -/* 5 */ ,function(a){ return a; } -/* 6 */ ,function(a){ return a; } -/* 7 */ ,'-' -/* 8 */ ,'' -/* 9 */ ,function(a){ return a; } -/* 10 */ ,function(a){ return a; } - ]; - - function esc(s){ - return s.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&'); - } - - function reverse ( a ) { - return a.split('').reverse().join(''); - } - - // Throw an error if formatting options are incompatible. - function throwEqualError( F, a, b ) { - if ( (F[a] || F[b]) && (F[a] === F[b]) ) { - throw new Error("(Link) '"+a+"' can't match '"+b+"'.'"); - } - } - - function validate ( options ) { - - var settings = {}; - - // Copy all values into a new object. - $(FormatNames).each(function(i, val){ - - if ( options[val] === undefined ){ - - settings[val] = FormatDefaults[i]; - - // When we aren't loading defaults, validate the entry. - } else if ( (typeof options[val]) === (typeof FormatDefaults[i]) ) { - - // Support for up to 7 decimals. - // More can't be guaranteed due to floating point issues. - if ( val === 'decimals' ){ - if ( options[val] < 0 || options[val] > 7 ){ - throw new Error("(Format) 'format.decimals' option must be between 0 and 7."); - } - } - - settings[val] = options[val]; - - // If the value isn't valid, emit an error. - } else { - throw new Error("(Format) 'format."+val+"' must be a " + typeof FormatDefaults[i] + "."); - } - }); - - // Some values can't be extracted from a - // string if certain combinations are present. - throwEqualError(settings, 'mark', 'thousand'); - throwEqualError(settings, 'prefix', 'negative'); - throwEqualError(settings, 'prefix', 'negativeBefore'); - - return settings; - } - - function toFormatting ( options, number ) { - - number = options['encoder']( number ); - - var decimals = options['decimals'], - negative = '', preNegative = '', base = '', mark = ''; - - // Rounding away decimals might cause a value of -0 - // when using very small ranges. Remove those cases. - if ( parseFloat(number.toFixed(decimals)) === 0 ) { - number = '0'; - } - - if ( number < 0 ) { - negative = options['negative']; - preNegative = options['negativeBefore']; - } - - // Round to proper decimal count - number = Math.abs(number).toFixed(decimals).toString(); - number = number.split('.'); - - // Group numbers in sets of three. - if ( options['thousand'] ) { - base = reverse(number[0]).match(/.{1,3}/g); - base = reverse(base.join(reverse( options['thousand'] ))); - } else { - base = number[0]; - } - - // Ignore the decimal separator if decimals are set to 0. - if ( options['mark'] && number.length > 1 ) { - mark = options['mark'] + number[1]; - } - - // Return the finalized formatted number. - return options['to'](preNegative + - options['prefix'] + - negative + - base + - mark + - options['postfix']); - } - - function fromFormatting ( options, input ) { - - var isNeg; - // 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 ( input === null || input === undefined ) { - return false; - } - - input = options['from'](input); - - // Remove formatting and set period for float parsing. - input = input.toString(); - - // Replace the preNegative indicator. - isNeg = input.replace( new RegExp( '^' + esc(options['negativeBefore']) ), ''); - - // Check if the value changed by removing the negativeBefore symbol. - if( input !== isNeg ) { - input = isNeg; - isNeg = '-'; - } else { - isNeg = ''; - } - - // If prefix is set and the number is actually prefixed. - input = input.replace(new RegExp( '^' + esc(options['prefix']) ), ''); - - // Only replace if a negative sign is set. - if ( options['negative'] ) { - - // Reset isNeg to prevent double '-' insertion. - isNeg = ''; - - // Reset the negative sign to '-' - input = input.replace(new RegExp( '^' + esc(options['negative']) ), '-'); - } - - // Clean the input string - input = input - // If postfix is set and the number is postfixed. - .replace( new RegExp(esc( options['postfix'] ) + '$'), '') - // Remove the separator every three digits. - .replace( new RegExp(esc( options['thousand'] ), 'g'), '') - // Set the decimal separator back to period. - .replace( options['mark'], '.'); - - // Run the user defined decoder. Returns input by default. - input = options['decoder']( parseFloat( isNeg + input ) ); - - // Ignore invalid input - if (isNaN( input )) { - return false; - } - - return input; - } - - - /** @constructor */ - function Format( options ){ - - // Allow 'init' from existing object. - if ( options instanceof Format ) { - return options; - } - - // If no settings where provided, the defaults will be loaded. - if ( options === undefined ){ - options = {}; - } - - if ( typeof options !== 'object' ){ - throw new Error("(Format) 'format' option must be an object."); - } - - this.settings = validate( options ); - } - - Format.prototype.extendUpon = function ( formatInstance ) { -// console.log( formatInstance.settings ); -// this.settings = $.extend( {}, formatInstance.settings, this.settings ); -// console.log( this.settings ); - }; - - Format.prototype.to = function(a){ - return toFormatting(this.settings, a); - }; - - Format.prototype.from = function(a){ - return fromFormatting(this.settings, a); - }; - -//}()); diff --git a/Link.js b/Link.js deleted file mode 100644 index 7f908918..00000000 --- a/Link.js +++ /dev/null @@ -1,247 +0,0 @@ -/**@preserve -$.Link (part of noUiSlider) - WTFPL */ - -/*jslint browser: true */ -/*jslint sub: true */ -/*jslint white: true */ - -(function( $ ){ - - 'use strict'; - - // Test in an object is an instance of jQuery or Zepto. - function isInstance ( a ) { - return a instanceof $ || ( $['zepto'] && $['zepto']['isZ'](a) ); - } - - -var -/** @const */ Public_Methods = [ - 'implement', - 'setValue', - 'resetValue', - 'fromFormat', - 'toFormat' - ]; - -/** @constructor */ - function Link ( entry, update ) { - - if ( typeof entry !== "object" ) { - throw new Error("(Link) Initialize with an object."); - } - - var that = this; - - // Forward calls within scope. - this.changeHandler = function ( e ) { - that.changeHandlerMethod(e); - } - - // Make sure Link isn't called as a function, in which case - // the 'this' scope would be the window. - this.init( entry['target']||function(){}, entry['method'], entry['format']||{}, update ); - } - -// Target types - Link.prototype.configureTooltip = function ( target, method ) { - - // By default, use the 'html' method. - this.method = method || 'html'; - - // Use jQuery to create the element - this.el = $( target.replace('-tooltip-', '') || '
' )[0]; - }; - - Link.prototype.configureHidden = function ( target ) { - - this.method = 'val'; - - this.el = document.createElement('input'); - this.el.name = target; - this.el.type = 'hidden'; - }; - - Link.prototype.configureField = function ( target ) { - - // Default to .val if this is an input element. - this.method = 'val'; - // Set the slider to a new value on change. - - this.target = target.on('change', this.changeHandler); - }; - - -// Initialisor - - // Gets arguments from constructor. - Link.prototype.init = function ( target, method, format, update ) { - - // Create a new Formatter. The constructor accepts 'undefined'. - this.formatInstance = new Format(format); - - // Store the update option. - this.update = !update; - - // If target is a string, a new hidden input will be created. - if ( typeof target === 'string' && target.indexOf('-tooltip-') === 0 ) { - this.configureTooltip( target, method ); - return; - } - - // If the string doesn't begin with '-', which is reserved, add a new hidden input. - if ( typeof target === 'string' && target.indexOf('-') !== 0 ) { - this.configureHidden( target ); - return; - } - - // The target can also be a function, which will be called. - if ( typeof target === 'function' ) { - this.target = false; - this.method = target; - return; - } - - if ( isInstance( target ) ) { - - if ( !method ) { - - // If a jQuery/Zepto input element is provided, but no method is set, - // the element can assume it needs to respond to 'change'... - if ( target.is('input, select, textarea') ) { - this.configureField( target ); - return; - } - - // If no method is set, and we are not auto-binding an input, default to 'html'. - method = 'html'; - } - - // The method must exist on the element. - if ( typeof method === 'function' || (typeof method === 'string' && target[method]) ) { - this.method = method; - this.target = target; - return; - } - } - - // Nothing matched, throw error. - throw new RangeError("(Link) Invalid Link."); - }; - - -// Developer calls - - Link.prototype['implement'] = function ( alternativeTarget, baseFormattingOptions, changeHandlerMethod ) { - - // Set the target if it isn't set yet. - if ( alternativeTarget && !this.target ) { - this.target = target; - } - - // Set formatting options. - // this.formatInstance.extendUpon( new Format(baseFormattingOptions) ); - - this.changeHandlerMethod = changeHandlerMethod; - - return this; - }; - - -// Value interaction - - // Provides external items with the slider value. - Link.prototype['setValue'] = function ( value, update ) { - - // Don't synchronize this Link. - if ( this.update && update === false ) { - return; - } - - // Ignore named arguments value and update, so only the passed-on - // arguments remain. - var args = Array.prototype.slice.call( arguments ), - additionalArgs = args.slice(2); - - // Store some values. The actual, numerical value, - // the formatted value and the parameters for use in 'resetValue'. - // Slice additionalArgs to break the relation. - this.actual = value; - this.args = additionalArgs.slice(); - - // Prepend the value to the function arguments. - additionalArgs.unshift( this.toFormat() ); - - // When target is undefined, the target was a function. - // In that case, provided the slider as the calling scope. - // Use [0] to get the DOM element, not the $ instance. - - // Branch between serialization to a function or an object. - ( typeof this.method === 'function' ? - this.method : - this.target[ this.method ] ).apply( this.target, additionalArgs ); - }; - - // Allow calling the 'setValue' method from cache. - Link.prototype['resetValue'] = function ( update ) { - var args = this.args.slice(); - args.unshift(this.actual, update); - this.setValue.apply(this, args); - }; - - -// Format interaction - - // Converts a formatted value back to a real number. - Link.prototype['fromFormat'] = function ( value ) { - return this.formatInstance.from( value ); - }; - - // Format a real number using the formatter. - Link.prototype['toFormat'] = function ( value ) { - return this.formatInstance.to( this.actual ); - }; - - -// Expose a wrapper, not Link - - function closure ( a, b ) { - - var linkInstance = new Link(a,b), - that = this; - - // Forward calls, but don't expose the actual Link. - $.each(Public_Methods, function( index, value ){ - that[value] = function(){ - return linkInstance[value].apply(linkInstance, arguments); - }; - }); - - // See if this Link needs individual targets based on its usage. - // If so, return the element that needs to be copied by the - // implementing interface. - this.needsClone = function(){ - return linkInstance.el || false; - }; - - // Create a new instance. - this.clone = function( target ){ - return new LinkWrapper({ - 'target': target, - 'method': this.method, - 'format': this.formatInstance - }, true); - }; - } - - function LinkWrapper ( a, b ) { - if ( !(this instanceof LinkWrapper) ) { - return new LinkWrapper(a,b); - } - closure.call( this, a, b ); - } - - /** @expose */ - $.Link = LinkWrapper; - -}( window['jQuery'] || window['Zepto'] )); diff --git a/jquery.nouislider.js b/jquery.nouislider.js index fab00c33..2b00c7d1 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -58,7 +58,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ /* 15 */ ,'noUi-active' /* 16 */ ,'noUi-extended' /* 17 */ ,'noUi-stacking' - ]; + ], + /** @const */ + defaultFormatter = { 'to': function( value ){ + return value.toFixed(2); + }, 'from': Number }; // General helpers @@ -122,7 +126,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return ((value * ( range[1] - range[0] )) / 100) + range[0]; } - // (percentage) + // (percentage) Input a value, find where, on a scale of 0-100, it applies. function toStepping ( options, value ) { if ( value >= options.xVal.slice(-1)[0] ){ @@ -142,7 +146,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); } - // (value) + // (value) Input a percentage, find where it is on the specified range. function fromStepping ( options, value ) { // There is no range group that fits 100 @@ -343,9 +347,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return true; } - // Check if step fits. Not required, but this might serve some goal. - // !((parsed.xVal[i+1] - parsed.xVal[i]) % n); - // Factor to range ratio parsed.xSteps[i] = fromPercentage([ parsed.xVal[i] @@ -472,37 +473,16 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ }; } - function testSerialization ( parsed, entry, sliders ) { - - parsed.ser = [ entry['lower'], entry['upper'] ]; - parsed.formatting = entry['format']; - - $.each( parsed.ser, function( index, linkInstances ){ - - // Check if the provided option is an array. - if ( !$.isArray(linkInstances) ) { - throw new Error( "noUiSlider: 'serialization." + - (!index ? 'lower' : 'upper') + "' must be an array." ); - } + function testFormat ( parsed, entry ) { - $.each(linkInstances, function( ignore, linkInstance ){ - - // Check if entry is a Link. - if ( !( linkInstance instanceof $.Link ) ) { - throw new Error( "noUiSlider: 'serialization." + - (!index ? 'lower' : 'upper') + "' can only contain Link instances." ); - } - - linkInstance.implement( sliders, parsed.formatting, createChangeHandler( index )); - }); - }); + parsed.format = entry; - // If the slider has two handles and is RTL, - // reverse the serialization input. For one handle, - // lower is still lower. - if ( parsed.dir && parsed.handles > 1 ) { - parsed.ser.reverse(); + // Any object with a to and from method is supported. + if ( typeof entry['to'] === 'function' && typeof entry['from'] === 'function' ) { + return true; } + + throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); } // Test all developer settings and parse to assumption-safe values. @@ -526,7 +506,8 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ xVal: [], xSteps: [ false ], xNumSteps: [ false ], - margin: 0 + margin: 0, + format: defaultFormatter }, tests; // Tests are executed in the order they are presented here. @@ -540,7 +521,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ 'orientation': { r: false, t: testOrientation }, 'margin': { r: false, t: testMargin }, 'behaviour': { r: true, t: testBehaviour }, - 'serialization': { r: true, t: testSerialization } + 'format': { r: false, t: testFormat } }; // Set defaults where applicable. @@ -551,13 +532,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ 'orientation': 'horizontal' }, options); - // Make sure the test for serialization runs. - options['serialization'] = $.extend({ - 'lower': [] - ,'upper': [] - ,'format': {} - }, options['serialization']); - // Run all options through a testing mechanism to ensure correct // input. It should be noted that options might get modified to // be handled properly. E.g. wrapping integers in arrays. @@ -651,83 +625,76 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } -// Implement the $.Link library +// Legend - // Go over all Links and assign them to a handle. - function addLinks ( options, handles ) { + function generateSpread ( options, mode, stepped ) { - var index, links = []; + var indexes = {}; - // Copy the links into a new array, instead of modifying - // the 'options.ser' list. This allows replacement of the invalid - // '.el' Links, while the others are still passed by reference. - for ( index = 0; index < options.handles; index++ ) { + // Set 'mode' to true for only the actual range points. + if ( mode === 'range' ) { - // Use the Link interface to provide unified - // formatting for the .val() method. - // The list now contains at least one element. - links[index] = [ new $.Link({}, true).implement(null, options.formatting) ]; - - // Loop all links in either 'lower' or 'upper'. - $.each(options.ser[index], function( ignore, linkInstance ){ + $.each(options.xVal, function(i,v){ + indexes[options.xPct[i]] = v; + }); + } + + if ( mode === 'steps' ) { - // If the Link requires creation of a new element, - // create this element and return a new Link instance. - var targetElement = linkInstance.needsClone(); + // We'll build a list of steps. + var last = options.xVal.length - 1; - if ( targetElement ) { + $.each(options.xVal, function ( index, value ) { - // Clone the old element (which isn't in the DOM) and add it to the handle. - targetElement = $( targetElement ).clone().appendTo( handles[index].children() ); + // Get the current step and the lower + upper positions. + var step = options.xNumSteps[ index ], + low = options.xVal[index], + high = options.xVal[index+1], + i; - // Create a new linkInstance. - links[index].push( linkInstance.clone( targetElement ) ); + // Low can be 0. + if ( low === false || !high ) { + return; + } - } else { + if ( !step && !index ) { + indexes['0'] = low; + return; + } - // This linkInstance needs no clone, use it immediately. - links[index].push( linkInstance ); + // Find all steps in the subrange. + for ( i = low; i < high; i += step ) { + indexes[toStepping(options, i).toFixed(5)] = i; } }); - } - return links; - } - - // Create a new function which calls .val on input change. - function createChangeHandler ( index ) { - - // Returns reordered array. - function reIndex ( a, b, c ){ - return [c?a:b, c?b:a]; + // Add the 'max' value to the end of the list. + indexes['100'] = options.xVal[ last ]; } - - return function ( e ){ - - console.log(this); - - this.target.val( - // Determine which array position to 'null' based on 'index'. - reIndex( null, $( e.target ).val(), index ), { - 'link': this, - 'set': true - } - ); + + // Provide an array based on the number of points to be displayed. + if ( typeof mode === 'number' ) { + + var spread = ( 100 / (mode-1) ), v, i = 0; + + mode = []; + + while ((v=i++*spread) <= 100 ) { + mode.push(v); + } } - } + + // Provide the stepped value for all points in an array. + if ( $.isArray(mode) ) { - // Get value from a Link - function aquireValue ( value, linkInstance, update ) { - - var to = linkInstance.fromFormat( value ); - - // If it the handle cannot be set, correct the linkInstance. - if ( to === false ) { - linkInstance.resetValue( update ); + $.each(mode, function(ignore, value){ + indexes[value] = fromStepping(options, stepped ? getStep(options, value) : value); + }); } - return to; - } + return indexes; + }; + // Slider scope @@ -740,17 +707,24 @@ function closure ( target, options, originalOptions ){ var $Target = $(target), $Locations = [-1, -1], $Base, - $Serialization, - $Handles; + $Handles, + $Values = [], + // libLink + triggerPos = ['lower', 'upper']; + + // Invert the libLink connection for rtl sliders. + if ( options.dir ) { + triggerPos.reverse(); + } + + +// Helpers // Shorthand for base dimensions. function baseSize ( ) { return $Base[['width', 'height'][options.ort]](); } - -// Helpers - // External event handling function fireEvents ( events ) { @@ -769,13 +743,28 @@ function closure ( target, options, originalOptions ){ return options.xVal.length === 2 && options.xVal[0] === options.xVal[1]; } + // Returns the input array, respecting the slider direction configuration. + function inSliderOrder ( values ) { + + // If only one handle is used, return a single value. + if ( values.length === 1 ){ + return values[0]; + } + + if ( options.dir ) { + return values.reverse(); + } + + return values; + } + // Handle placement // Test suggested values and apply margin, step. function setHandle ( handle, to ) { - var n = handle[0] !== $Handles[0][0] ? 1 : 0, + var trigger = handle[0] !== $Handles[0][0] ? 1 : 0, lower = $Locations[0] + options.margin, upper = $Locations[1] - options.margin; @@ -789,7 +778,7 @@ function closure ( target, options, originalOptions ){ // For sliders with multiple handles, // limit movement to the other handle. if ( $Handles.length > 1 ) { - to = n ? Math.max( to, lower ) : Math.min( to, upper ); + to = trigger ? Math.max( to, lower ) : Math.min( to, upper ); } // Handle the step option. @@ -802,7 +791,7 @@ function closure ( target, options, originalOptions ){ to = limit(parseFloat(to.toFixed(7))); // Return false if handle can't move. - if ( to === $Locations[n] ) { + if ( to === $Locations[trigger] ) { return false; } } @@ -816,19 +805,17 @@ function closure ( target, options, originalOptions ){ } // Update locations. - $Locations[n] = to; + $Locations[trigger] = to; // Invert the value if this is a right-to-left slider. if ( options.dir ) { to = 100 - to; } - // Write values to serialization Links. - // Convert the value to the correct relative representation. // Convert the value to the slider stepping/range. - $($Serialization[n]).each(function(){ - this.setValue( fromStepping( options, to ), true, handle.children(), $Target ); - }); + $Values[trigger] = fromStepping( options, to ); + + LinkUpdate(triggerPos[trigger]); return true; } @@ -871,6 +858,52 @@ function closure ( target, options, originalOptions ){ fireEvents(['slide', 'set', 'change']); } + // Loop values from value method and apply them. + function setValues ( count, values ) { + + var i, trigger, to; + + // 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. + for ( i = 0; i < count; i++ ) { + + trigger = i%2; + + // Get the current argument from the array. + to = values[trigger]; + + // Setting with null indicates an 'ignore'. + if ( to === null ) { + continue; + } + + // If a formatted number was passed, attemt to decode it. + if ( typeof to === 'number' ) { + to = String(to); + } + + to = options.format.from( to ); + + // Request an update for all links if the value was invalid. + if ( to === false || isNaN(to) ) { + LinkUpdate(triggerPos[trigger]); + continue; + } + + // Calculate the new handle position + to = toStepping( options, to ); + + // Invert the value if this is a right-to-left slider. + if ( options.dir ) { + to = 100 - to; + } + + // Set the handle. + setHandle( $Handles[trigger], to ); + } + } + // Events @@ -1091,24 +1124,6 @@ function closure ( target, options, originalOptions ){ } -// Helpers - - - // Returns the input array, respecting the slider direction configuration. - function inSliderOrder ( values ) { - - // If only one handle is used, return a single value. - if ( values.length === 1 ){ - return values[0]; - } - - if ( options.dir ) { - return values.reverse(); - } - - return values; - } - // Initialize slider // Throw an error if the slider was already initialized. @@ -1120,7 +1135,6 @@ function closure ( target, options, originalOptions ){ // Add handles and links. $Base = addSlider( options, $Target ); $Handles = addHandles( options, $Base ); - $Serialization = addLinks( options, $Handles ); // Set the connect classes. addConnection ( options.connect, $Target, $Handles ); @@ -1129,14 +1143,81 @@ function closure ( target, options, originalOptions ){ events( options.events ); +// libLink integration + + // Create a new function which calls .val on input change. + function createChangeHandler ( trigger ) { + return function ( e, value ){ + // Determine which array position to 'null' based on 'trigger'. + $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); + } + } + + // Called by libLink when it wants a set of links updated. + function LinkUpdate ( flag ) { + // The API might not have been set yet. + + var trigger = $.inArray(flag, triggerPos); + + try { + $Target[0].linkAPI[flag].change( + $Values[trigger], + $Handles[trigger].children(), + $Target + ); + } catch ( ignore ) { } + } + + // Called by libLink to append an element to the slider. + function LinkConfirm ( flag, element ) { + + // Find the trigger for the passed flag. + var trigger = $.inArray(flag, triggerPos); + + // If set, append the element to the handle it belongs to. + if ( element ) { + element.appendTo( $Handles[trigger].children() ); + } + + // The public API is reversed for rtl sliders, so the changeHandler + // should not be aware of the inverted trigger positions. + if ( options.dir ) { + trigger = trigger === 1 ? 0 : 1; + } + + return createChangeHandler( trigger ); + } + + /** @expose */ + target.LinkUpdate = LinkUpdate; + /** @expose */ + target.LinkConfirm = LinkConfirm; + /** @expose */ + target.LinkDefaultFlag = 'lower'; + /** @expose */ + target.LinkDefaultFormatter = options.format; + + + + target.spread1 = generateSpread(options, 'range'); + target.spread2 = generateSpread(options, 'steps'); + target.spread3 = generateSpread(options, [0,25,50,75,100]); + target.spread4 = generateSpread(options, [0,25,50,75,100], true); + target.spread5 = generateSpread(options, 8); + target.spread6 = generateSpread(options, 8, true); + // todo, add support for value input [ 200, 1000, 4000, 6000 ] and return the percentages. + + + // Methods + // Set the slider value. /** @expose */ target.vSet = function ( ) { var args = Array.prototype.slice.call( arguments ), - i, count, to, values = asArray( args[0] ); + count, values = asArray( args[0] ); // Support the 'true' option. if ( args[1] === true ) { @@ -1158,38 +1239,12 @@ function closure ( target, options, originalOptions ){ // Determine how often to set the handles. count = $Handles.length > 1 ? 3 : 1; + if ( values.length === 1 ) { count = 1; } - // 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. - for ( i = 0; i < count; i++ ) { - - to = aquireValue( - // The passed-in value. - values[i%2], - // The passed linkInstance, or default to the first one. - args[1]['link'] || $Serialization[i%2][0], - // Whether the link updates for the new value. - args[1]['update'] - ); - - if ( to !== false ) { - - // Calculate the new handle position - to = toStepping( options, to ); - - // Invert the value if this is a right-to-left slider. - if ( options.dir ) { - to = 100 - to; - } - - // Set the handle. - setHandle( $Handles[i%2], to ); - } - } + setValues ( count, values ); // Optionally fire the 'set' event. if ( args[1]['set'] === true ) { @@ -1207,7 +1262,7 @@ function closure ( target, options, originalOptions ){ // Get the value from all handles. for ( i = 0; i < options.handles; i++ ){ - retour[i] = $Serialization[i][0].toFormat(); + retour[i] = options.format.to( $Values[i] ); } return inSliderOrder( retour ); @@ -1217,17 +1272,6 @@ function closure ( target, options, originalOptions ){ /** @expose */ target.destroy = function ( ) { - // Loop all linked serialization objects and unbind all - // events in the noUiSlider namespace. - $.each($Serialization, function(){ - $.each(this, function(){ - // Won't remove 'change' when bound implicitly. - if ( this.target ) { - this.target.off( namespace ); - } - }); - }); - // Unbind events on the slider, remove all classes and child elements. $(this).off(namespace) .removeClass(Classes.join(' ')) @@ -1237,6 +1281,18 @@ function closure ( target, options, originalOptions ){ return originalOptions; }; + // Place elements back on the slider. + /** @expose */ + target.reappend = function ( ) { + + // The API keeps a list of elements: we can re-append them on rebuild. + $.each(this.linkAPI, function( trigger, elements ){ + $.each(elements, function(i, element) { + element.appendTo( $Handles[trigger].children() ); + }); + }); + }; + // Get the current step size for the slider. /** @expose */ target.getStep = function ( ) { @@ -1251,25 +1307,6 @@ function closure ( target, options, originalOptions ){ return inSliderOrder( retour ); }; - // Return clones of some private variables. - /** @expose */ - target.api = function ( ) { - return { - 'slider': this, - 'range': { - 'positions': options.xPct.slice(), - 'values': options.xVal.slice() - }, - 'steps': { - 'positions': options.xSteps.slice(), - 'values': options.xNumSteps.slice() - }, - 'toStepping': function ( value ) { - return toStepping( options, value ); - } - } - }; - // Use the public value method to set the start values. $Target.val( options.start ); } @@ -1310,6 +1347,9 @@ function closure ( target, options, originalOptions ){ // Run the standard initializer. $(this).noUiSlider( newOptions ); + // Place Link elements back. + this.reappend(); + // If the start option hasn't changed, // reset the previous values. if ( originalOptions.start === newOptions.start ) { @@ -1348,14 +1388,10 @@ function closure ( target, options, originalOptions ){ }); }; -// Remap the serialization constructor for legacy support. - /** @expose */ - $.noUiSlider = { 'Link': $.Link }; - // Extend jQuery/Zepto with the noUiSlider method. /** @expose */ $.fn.noUiSlider = function ( options, rebuildFlag ) { - return options === 'api' ? this[0].api() : ( rebuildFlag ? rebuild : initialize ).call(this, options); + return ( rebuildFlag ? rebuild : initialize ).call(this, options); }; }( window['jQuery'] || window['Zepto'] )); diff --git a/nouislider.more.js b/nouislider.more.js deleted file mode 100644 index 79c05230..00000000 --- a/nouislider.more.js +++ /dev/null @@ -1,44 +0,0 @@ -(function( $ ){ - - /** @expose */ - $.noUiSlider.spread = function ( api, mode ) { - - // Set 'mode' to true for only the actual range points. - if ( mode === true ) { - return api.range.values; - } - - // We'll build a list of steps. - var indexes = {}, last = api.range.values.length - 1; - - $.each(api.range.values, function ( index, value ) { - - // Get the current step and the lower + upper positions. - var step = api.steps.values[ index ], - low = api.range.values[index], - high = api.range.values[index+1], - i; - - // Low can be 0. - if ( low === false || !high ) { - return; - } - - if ( !step && !index ) { - indexes['0'] = low; - return; - } - - // Find all steps in the subrange. - for ( i = low; i < high; i += step ) { - indexes[api.toStepping(i).toFixed(5)] = i; - } - }); - - // Add the 'max' value to the end of the list. - indexes['100'] = api.range.values[ last ]; - - return indexes; - }; - -}( window['jQuery'] || window['Zepto'] )); diff --git a/tests/inputs.js b/tests/01.inputs.js similarity index 79% rename from tests/inputs.js rename to tests/01.inputs.js index db1cfa61..28503ad5 100644 --- a/tests/inputs.js +++ b/tests/01.inputs.js @@ -10,23 +10,19 @@ '); Q.find(".slider").noUiSlider({ - range: { + start: [ 20, 80 ], + range: { 'min': [ 0 ], 'max': [ 100 ] } - ,start: [ 20, 80 ] - ,serialization: { - lower: [ - new Link({ - target: 'lower' - }) - ] - ,upper: [ - new Link({ - target: 'upper' - }) - ] - } + }); + + $('.slider').Link('lower', { + target: 'lower' + }); + + $('.slider').Link('upper', { + target: 'upper' }); $("#slidera").val([50, 61]); diff --git a/tests/02.shared-link.js b/tests/02.shared-link.js new file mode 100644 index 00000000..9a4f3201 --- /dev/null +++ b/tests/02.shared-link.js @@ -0,0 +1,42 @@ + + test( "Shared link element", function(){ + + Q.html('\ +
\ +
\ + \ + '); + + var span = $('#slider-span'); + + $(".slider").noUiSlider({ + start: [ 0, 10 ], + behaviour: 'drag', + connect: true, + range: { + 'min': [ -20 ], + 'max': [ 20 ] + } + }); + + $(".slider").Link('lower', { + target: span, + format: wNumb({ + mark: ',', + decimals: 1, + thousand: '.', + encoder: function( decoded ){ + return decoded * 10000; + }, + decoder: function( encoded ){ + return encoded / 10000; + } + }) + }); + + $("#sliderb").val( 1 ); + + deepEqual( $("#slidera").val(), ['0.00', '10.00'] ); + equal( span.html(), '10.000,0' ); + + }); diff --git a/tests/03.format.js b/tests/03.format.js new file mode 100644 index 00000000..be6cd383 --- /dev/null +++ b/tests/03.format.js @@ -0,0 +1,79 @@ + + test( "Serialization and formatting options", function(){ + + Q.html('\ +
\ + \ + \ + \ + '); + + $("#slider").noUiSlider({ + start: [ 0, 0.002 ], + connect: true, + behaviour: 'drag', + range: { + 'min': [ 0.001 ], + 'max': [ 0.003 ] + }, + format: wNumb({ + decimals: 5 + }) + }); + + var one = { + target: '-inline-
', + method: function( value, handle, slider ){ + ok( $("#slider")[0] === slider[0] ); + ok( handle.hasClass('noUi-handle') ); + }, + format: wNumb({ + decimals: 6, + prefix: '#', + negativeBefore: '!', + negative: '' + }) + }; + + var two = { + target: $("#inputChange"), + format: wNumb({ + mark: '.', + thousand: ',', + prefix: '$', + postfix: ' p.p.', + decimals: 3, + negativeBefore: '-', + negative: '', + encoder: function( a ){ + return a * 1E7; + }, + decoder: function( a ){ + return a / 1E7; + } + }) + }; + + var three = { + target: $("#inputVal"), + method: 'val' + }; + + $("#slider").Link('lower', one, two); + $("#slider").Link('upper', three); + + $("#inputChange").val( "15000" ).trigger('change'); + + equal( $("#inputChange").val(), "$15,000.000 p.p.", "Testing interpretation of improper data" ); + + $("#inputVal").val( 3 ); + + equal( $("#inputVal").val(), "3" ); + + $("#inputVal").trigger("change"); + + equal ( $("#inputVal").val(), "3", "Input doesn't respond to change events." ); + + deepEqual( $("#slider").val(), ["0.00150", "0.00200"] ); + + }); diff --git a/tests/select.js b/tests/04.select.js similarity index 68% rename from tests/select.js rename to tests/04.select.js index 5132474d..72e44286 100644 --- a/tests/select.js +++ b/tests/04.select.js @@ -5,9 +5,9 @@
\ \ '); - + var input = $('#input'); - + for( var i = -20; i <= 40; i++ ){ input.append( '' @@ -15,26 +15,23 @@ } $('.slider').noUiSlider({ - range: { + start: [ 10, 30 ], + connect: true, + range: { 'min': -20, 'max': 40 } - ,start: [10,30] - ,connect: true - ,serialization: { - lower: [ - new Link({ - target: input, - format: { - decimals: 0 - } - }) - ] - } + }); + + $('.slider').Link({ + target: input, + format: wNumb({ + decimals: 0 + }) }); input.val( 40 ).change(); - + equal( input.val(), '30', 'Select was reset properly.' ); - + }); diff --git a/tests/set.js b/tests/05.set.js similarity index 74% rename from tests/set.js rename to tests/05.set.js index ae924561..a1fc1d18 100644 --- a/tests/set.js +++ b/tests/05.set.js @@ -6,21 +6,21 @@ '); var slider = $('.slider'); - + slider.noUiSlider({ - range: { + start: [ 50, 100 ], + connect: true, + range: { 'min': 30, 'max': 980 - } - ,start: [50,100] - ,connect: true - ,serialization: { - lower: [] - } + }, + format: wNumb({ + decimals: 2 + }) }); slider.val( [ 150, 600 ] ); - + deepEqual( slider.val(), ['150.00', '600.00'], 'Slider correctly overstepped limits.' ); - + }); diff --git a/tests/events.js b/tests/06.events.js similarity index 92% rename from tests/events.js rename to tests/06.events.js index 7b898256..276e892e 100644 --- a/tests/events.js +++ b/tests/06.events.js @@ -14,12 +14,10 @@ 'min': [ 50 ], 'max': [ 90 ] }, - serialization: { - format: { - decimals: 0, - postfix: ',-' - } - } + format: wNumb({ + decimals: 0, + postfix: ',-' + }) }); var w = slider.width(); diff --git a/tests/07.rtl-set.js b/tests/07.rtl-set.js new file mode 100644 index 00000000..e89da42e --- /dev/null +++ b/tests/07.rtl-set.js @@ -0,0 +1,30 @@ + + test( "RTL slider multiple value set.", function(){ + + Q.html('\ +
\ +
\ + 0\ +
\ + '); + + var slider = $("#slider").noUiSlider({ + range: {min: 0.201, max: 1}, + step: 0.01, + start: [0.401], + direction: "rtl", + orientation: "vertical" + }); + + slider.Link('lower', { + target: $("#min") + }); + + equal(slider.val(), 0.4); + + slider.val(0.201, true); + equal(slider.val(), 0.2); + + slider.val(0.201, true); + equal(slider.val(), 0.2); + }); diff --git a/tests/update.js b/tests/08.update.js similarity index 50% rename from tests/update.js rename to tests/08.update.js index 7b185786..5e0a70b9 100644 --- a/tests/update.js +++ b/tests/08.update.js @@ -10,34 +10,32 @@ input = $('.input'); slider.noUiSlider({ - range: { + range: { 'min': 30, 'max': 980 - } - ,start: [-3000, 1000] - ,connect: true - ,serialization: { - lower: [ - new Link({ - target: input - }) - ], - upper: [ - new Link({ - target: "newInput" - }) - ], - format: { - encoder: function( value ){ - return 3 * Number(value); - }, - decoder: function( value ){ - return Number(value) / 3; - }, - mark: '|', - decimals: 4 - } - } + }, + start: [-3000, 1000], + connect: true, + format: wNumb({ + encoder: function( value ){ + console.log('en', value, 3 * Number(value)); + return 3 * Number(value); + }, + decoder: function( value ){ + console.log('de', value); + return Number(value) / 3; + }, + mark: '|', + decimals: 4 + }) + }); + + slider.Link('lower', { + target: input + }); + + slider.Link('upper', { + target: "newInput" }); deepEqual( slider.val(), ['90|0000', '1000|0000'], 'Slider handles encoder values.' ); @@ -51,16 +49,18 @@ 'max': 6000 }, start: [ -40, 1500 ], - serialization: { - format: { - mark: '.', - decimals: 2 - } - } + format: wNumb({ + mark: '.', + decimals: 2 + }) }, true); equal( Q.find('input').length, 1, 'Hidden input is gone.' ); - equal( Q.find('input.input').val(), '90|0000', "Input didn't update." ); + + equal( Q.find('input[name="newInput"]').length, 1 ); + + // As of noUiSlider 7, the input method doesn't remove Links. + //equal( Q.find('input.input').val(), '90|0000', "Input didn't update." ); deepEqual( slider.val(), ['-40.00', '1500.00'], 'Slider has new settings.' ); diff --git a/tests/links.js b/tests/09.links.js similarity index 76% rename from tests/links.js rename to tests/09.links.js index 3a693e99..42552752 100644 --- a/tests/links.js +++ b/tests/09.links.js @@ -30,6 +30,9 @@ } function linkMethodFunction( value, handle, sliderInstance ){ + + console.log(val); + equal(value, vals[val2++], "Value as expected, change ("+val2+"/4)."); ok(handle.hasClass("noUi-handle"), "Handle is really a handle."); ok(sliderInstance.hasClass("noUi-target"), "Slider is really a slider."); @@ -41,41 +44,37 @@ range: { 'min': -100, 'max': 9000 - } - ,start: [-500, 10000] - ,connect: true - ,serialization: { - lower: [ - new Link({ - target: linkTargetFunction - }), - new Link({ - target: box, - method: linkMethodFunction - }), - new Link({ - target: "hiddenInputField", - format: { - decimals: 1, - negative: '', - prefix: '++', - negativeBefore: '=' - } - }) - ], - upper: [ - new Link({ - target: item, - method: "cake" - }), - new Link({ - target: thing, - method: "html" - }) - ] - } + }, + start: [-500, 10000], + connect: true, + format: wNumb({ + decimals: 2 + }) }); + slider.Link('lower', { + target: linkTargetFunction + }, { + target: box, + method: linkMethodFunction + }, { + target: "hiddenInputField", + format: wNumb({ + decimals: 1, + negative: '', + prefix: '++', + negativeBefore: '=' + }) + }) + + slider.Link('upper', { + target: item, + method: "cake" + }, { + target: thing, + method: "html" + }); + equal(item.html(), '9000.00', 'Setting by custom method works.'); ok($('input[name="hiddenInputField"]').is('input'), "Input field was generated."); diff --git a/tests/rtl.js b/tests/10.rtl.js similarity index 50% rename from tests/rtl.js rename to tests/10.rtl.js index cc669589..12f6b1d1 100644 --- a/tests/rtl.js +++ b/tests/10.rtl.js @@ -18,19 +18,21 @@ behaviour: 'tap', connect: true, direction : 'rtl', - serialization: { - lower: [ - new Link({ - target: $("#min") - }) - ], - upper: [ - new Link({ - target: $("#max") - }) - ] + format: { + to: function( a ){ + return a.toFixed(2); + }, + from: Number } }); + + slider.Link('lower', { + target: $("#min") + }); + slider.Link('upper', { + target: $("#max") + }); + deepEqual(slider.val(), ["10.00", "40.00"], "Proper start handling in RTL."); slider.val([20,30]); @@ -43,36 +45,3 @@ deepEqual(slider.val(), ["30.00", "35.00"], "RTL slider understepped properly."); }); - - test( "RTL slider multiple value set.", function(){ - - Q.html('\ -
\ -
\ - 0\ -
\ - '); - - var slider = $("#slider").noUiSlider({ - range: {min: 0.201, max: 1}, - step: 0.01, - start: [0.401], - direction: "rtl", - orientation: "vertical", - serialization: { - lower: [ - new Link({ - target: $("#min") - }) - ] - } - }); - - equal(slider.val(), 0.4); - - slider.val(0.201, true); - equal(slider.val(), 0.2); - - slider.val(0.201, true); - equal(slider.val(), 0.2); - }); diff --git a/tests/val.js b/tests/11.val.js similarity index 93% rename from tests/val.js rename to tests/11.val.js index 0cd6a04e..53406bee 100644 --- a/tests/val.js +++ b/tests/11.val.js @@ -10,11 +10,9 @@ start: [ 0, 10 ], behaviour: 'drag', connect: true, - serialization: { - format: { - 'decimals': 1 - } - } + format: wNumb({ + 'decimals': 1 + }) }); deepEqual(slider.val(), ["0.0", "10.0"]); diff --git a/tests/update-link.js b/tests/12.update-link.js similarity index 74% rename from tests/update-link.js rename to tests/12.update-link.js index 73f59cc3..d7021071 100644 --- a/tests/update-link.js +++ b/tests/12.update-link.js @@ -11,38 +11,38 @@ input1 = $("#input1"), input2 = $("#input2"); - var itemCustom = $.Link({ + slider.noUiSlider({ + range: { + min: 30, + max: 120 + }, + start: [ 50, 100 ], + format: wNumb({ + prefix: '€', + mark: '*', + decimals: 2 + }) + }); + + slider.Link('lower', { target: $('#input1'), - format: { + format: wNumb({ prefix: '$', mark: ',', decimals: 3, - to: function( value ) { + edit: function( value ) { return 'Hi! ' + value; }, - from: function( value ) { + undo: function( value ) { return value.slice(4); } - } + }) }); - var itemInherit = $.Link({ + slider.Link('upper', { target: $('#input2') }); - slider.noUiSlider({ - range: { min: 30, max: 120 }, - start: [ 50, 100 ], - serialization: { - lower: [ itemCustom ], - upper: [ itemInherit ], - format: { - prefix: '€', - mark: '*' - } - } - }); - deepEqual(slider.val(), ["€50*00", "€100*00"]); equal(input1.val(), "Hi! $50,000"); equal(input2.val(), "€100*00"); diff --git a/tests/default-val.js b/tests/13.default-val.js similarity index 93% rename from tests/default-val.js rename to tests/13.default-val.js index e64a4d78..04f2bf5f 100644 --- a/tests/default-val.js +++ b/tests/13.default-val.js @@ -14,7 +14,10 @@ var slider = $("#simpleslider").noUiSlider({ range: { min: 20, max: 120 }, - start: 30 + start: 30, + format: wNumb({ + decimals: 2 + }) }); one.val(35); diff --git a/tests/non-linear-rtl.js b/tests/14.non-linear-rtl.js similarity index 90% rename from tests/non-linear-rtl.js rename to tests/14.non-linear-rtl.js index 66b795b0..9041c9b2 100644 --- a/tests/non-linear-rtl.js +++ b/tests/14.non-linear-rtl.js @@ -18,9 +18,9 @@ }, start: 44, direction : 'rtl', - serialization: { - format: { decimals: 0 } - } + format: wNumb({ + decimals: 0 + }) }); sliderLTR.noUiSlider({ @@ -31,9 +31,9 @@ 'max': 50 }, start: 44, - serialization: { - format: { decimals: 0 } - } + format: wNumb({ + decimals: 0 + }) }); equal(sliderRTL.val(), '40', 'Start stepping on rtl works'); diff --git a/tests/step-limit.js b/tests/15.step-limit.js similarity index 91% rename from tests/step-limit.js rename to tests/15.step-limit.js index 3a9b02b4..126dfc0d 100644 --- a/tests/step-limit.js +++ b/tests/15.step-limit.js @@ -10,7 +10,10 @@ slider.noUiSlider({ range: { min: 3, max: 106 }, start: [ 20, 50 ], - step: 10 + step: 10, + format: wNumb({ + decimals: 2 + }) }); deepEqual( slider.val(), ['23.00', '53.00'] ); diff --git a/tests/errors.js b/tests/errors.js deleted file mode 100644 index 41674fa1..00000000 --- a/tests/errors.js +++ /dev/null @@ -1,69 +0,0 @@ - - test( "Testing input validation.", function(){ - - Q.html('\ -
\ -
\ -
\ -
\ - '); - - var settings = { - start: [10,30], - connect: true, - range: { - 'min': -20, - 'max': 40 - } - }; - - throws(function(){ - $('.slider1').noUiSlider($.extend({}, { - serialization: { - lower: [ - new Link({ - target: input, - format: { - decimals: 8 - } - }) - ] - } - }, settings)); - }, "Decimal count."); - - throws(function(){ - $('.slider2').noUiSlider($.extend({}, { - serialization: { - format: { - negative: function(){} - } - } - }, settings)); - }, "Format item type."); - - throws(function(){ - $('.slider3').noUiSlider($.extend({}, { - serialization: { - lower: [ - new Link({ - target: function(){}, - format: { - mark: ',' - } - }) - ], - format: { - thousand: ',' - } - } - }, settings)); - }, "Incompatible equal formatting options."); - - throws(function(){ - $('.slider4').noUiSlider({ - range: { }, - start: [20, 30] - }); - }, "Missing 'min' or 'max' in range."); - }); diff --git a/tests/format.js b/tests/format.js deleted file mode 100644 index bd77cd6a..00000000 --- a/tests/format.js +++ /dev/null @@ -1,82 +0,0 @@ - - test( "Serialization and formatting options", function(){ - - Q.html('\ -
\ - \ - \ - \ - '); - - $("#slider").noUiSlider({ - range: { - 'min': [ 0.001 ], - 'max': [ 0.003 ] - } - ,start: [ 0, 0.002 ] - ,connect: true - ,behaviour: 'drag' - ,serialization: { - lower: [ - - new Link({ - target: '-tooltip-
', - method: function( value, handle, slider ){ - ok( $("#slider")[0] === slider[0] ); - ok( handle.hasClass('noUi-handle') ); - }, - format: { - decimals: 6 - ,prefix: '#' - ,negativeBefore: '!' - ,negative: '' - } - }), - - new Link({ - target: $("#inputChange"), - format: { - mark: '.' - ,thousand: ',' - ,prefix: '$' - ,postfix: ' p.p.' - ,decimals: 3 - ,negativeBefore: '-' - ,negative: '' - ,encoder: function( a ){ - return a * 1E7; - } - ,decoder: function( a ){ - return a / 1E7; - } - } - }) - ] - ,upper: [ - - new Link({ - target: $("#inputVal"), - method: 'val' - }) - ] - ,format: { - decimals: 5 - } - } - }); - - $("#inputChange").val( "15000" ).trigger('change'); - - equal( $("#inputChange").val(), "$15,000.000 p.p.", "Testing interpretation of improper data" ); - - $("#inputVal").val( 3 ); - - equal( $("#inputVal").val(), "3" ); - - $("#inputVal").trigger("change"); - - equal ( $("#inputVal").val(), "3", "Input doesn't respond to change events." ); - - deepEqual( $("#slider").val(), ["0.00150", "0.00200"] ); - - }); diff --git a/tests/run.html b/tests/run.html index f4875326..fe7bab7a 100644 --- a/tests/run.html +++ b/tests/run.html @@ -4,19 +4,14 @@ Testing @@ -31,19 +26,11 @@ --> - + + - - - - - + @@ -56,20 +43,20 @@ var Q = $("#qunit-fixture"); - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/tests/shared-link.js b/tests/shared-link.js deleted file mode 100644 index 2aecd614..00000000 --- a/tests/shared-link.js +++ /dev/null @@ -1,47 +0,0 @@ - - test( "Shared link element", function(){ - - Q.html('\ -
\ -
\ - \ - '); - - var span = $('#slider-span'); - - $("#slidera, #sliderb").noUiSlider({ - range: { - 'min': [ -20 ], - 'max': [ 20 ] - } - ,start: [ 0, 10 ] - ,behaviour: 'drag' - ,connect: true - ,serialization: { - lower: [ - - new Link({ - target: span, - format: { - mark: ',' - ,decimals: 1 - ,thousand: '.' - ,encoder: function( decoded ){ - return decoded * 10000; - } - ,decoder: function( encoded ){ - return encoded / 10000; - } - } - }) - - ] - } - }); - - $("#sliderb").val( 1 ); - - deepEqual( $("#slidera").val(), ['0.00', '10.00'] ); - equal( span.html(), '10.000,0' ); - - }); From 5b1c6c2500d1f3993d89cf7defeba128934c2af7 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Wed, 4 Jun 2014 21:33:34 +0200 Subject: [PATCH 06/28] Worked on spread generation --- jquery.nouislider.js | 54 +++++++++----- tests/goof.html | 171 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 tests/goof.html diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 2b00c7d1..64c75eda 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -126,6 +126,9 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return ((value * ( range[1] - range[0] )) / 100) + range[0]; } + +// Range conversion + // (percentage) Input a value, find where, on a scale of 0-100, it applies. function toStepping ( options, value ) { @@ -631,15 +634,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ var indexes = {}; - // Set 'mode' to true for only the actual range points. - if ( mode === 'range' ) { - - $.each(options.xVal, function(i,v){ - indexes[options.xPct[i]] = v; - }); - } - - if ( mode === 'steps' ) { + if ( mode === 'range' || mode === 'steps' ) { // We'll build a list of steps. var last = options.xVal.length - 1; @@ -657,19 +652,38 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return; } - if ( !step && !index ) { - indexes['0'] = low; - return; + // Generate a list of points between the range-points. + var increment = step ? (step / 10) : ((high - low) / 20); + + // These sub-points are marked 'false' to indicate + // the values shouldn't be displayed. + for ( i = low; i < high; i += increment ) { + indexes[toStepping(options, i).toFixed(5)] = [i, false]; } - - // Find all steps in the subrange. - for ( i = low; i < high; i += step ) { - indexes[toStepping(options, i).toFixed(5)] = i; + + // Set 'mode' to 'range' for only the actual range points. + if ( mode === 'range' ) { + indexes[options.xPct[index].toFixed(5)] = [value, true]; } + + // ... or to 'steps' to generate points for all steps. + if ( mode === 'steps' ) { + + if ( !step && !index ) { + indexes[0] = [low, true]; + return; + } + + // Find all steps in the subrange. + for ( i = low; i < high; i += step ) { + indexes[toStepping(options, i).toFixed(5)] = [i, true]; + } + } + }); // Add the 'max' value to the end of the list. - indexes['100'] = options.xVal[ last ]; + indexes[100] = [options.xVal[ last ], true]; } // Provide an array based on the number of points to be displayed. @@ -688,7 +702,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ if ( $.isArray(mode) ) { $.each(mode, function(ignore, value){ - indexes[value] = fromStepping(options, stepped ? getStep(options, value) : value); + + var step = getStep(options, value); + + // TODO indexes[ step ] ?? + indexes[ stepped ? step : value ] = fromStepping(options, stepped ? step : value); }); } diff --git a/tests/goof.html b/tests/goof.html new file mode 100644 index 00000000..80b65be7 --- /dev/null +++ b/tests/goof.html @@ -0,0 +1,171 @@ + + + + Testing + + + + + + + + + + + +
+
+
+
+
+
+
+ + + From e10bd46f772c0482ee6a660006a20c6e30b50053 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Thu, 5 Jun 2014 22:28:21 +0200 Subject: [PATCH 07/28] Added non-linear grid generation --- jquery.nouislider.js | 52 ++++++++++++++++++++++++-------------------- tests/goof.html | 20 ----------------- 2 files changed, 28 insertions(+), 44 deletions(-) diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 64c75eda..8d0037b6 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -126,9 +126,9 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return ((value * ( range[1] - range[0] )) / 100) + range[0]; } - + // Range conversion - + // (percentage) Input a value, find where, on a scale of 0-100, it applies. function toStepping ( options, value ) { @@ -637,7 +637,8 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ if ( mode === 'range' || mode === 'steps' ) { // We'll build a list of steps. - var last = options.xVal.length - 1; + var last = options.xVal.length - 1, + prevPct = 0; $.each(options.xVal, function ( index, value ) { @@ -652,59 +653,62 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return; } - // Generate a list of points between the range-points. - var increment = step ? (step / 10) : ((high - low) / 20); - - // These sub-points are marked 'false' to indicate - // the values shouldn't be displayed. - for ( i = low; i < high; i += increment ) { - indexes[toStepping(options, i).toFixed(5)] = [i, false]; - } - // Set 'mode' to 'range' for only the actual range points. if ( mode === 'range' ) { indexes[options.xPct[index].toFixed(5)] = [value, true]; } - + // ... or to 'steps' to generate points for all steps. if ( mode === 'steps' ) { - + if ( !step && !index ) { indexes[0] = [low, true]; return; } // Find all steps in the subrange. - for ( i = low; i < high; i += step ) { - indexes[toStepping(options, i).toFixed(5)] = [i, true]; + for ( i = low; i <= high; i += step ) { + + var newPct = toStepping(options, i), + pctDifference = newPct - prevPct, + pctDifferenceRound = Math.round(pctDifference), + pctRatio = pctDifference/pctDifferenceRound; + + for ( var q = 1; q < pctDifferenceRound; q += 1 ) { + var pos = prevPct + (pctRatio*q); + indexes[pos.toFixed(5)] = [pos, false]; + } + + indexes[newPct.toFixed(5)] = [i, true]; + prevPct = newPct; } } - + }); // Add the 'max' value to the end of the list. indexes[100] = [options.xVal[ last ], true]; } - + // Provide an array based on the number of points to be displayed. if ( typeof mode === 'number' ) { - + var spread = ( 100 / (mode-1) ), v, i = 0; - + mode = []; - + while ((v=i++*spread) <= 100 ) { mode.push(v); } } - + // Provide the stepped value for all points in an array. if ( $.isArray(mode) ) { $.each(mode, function(ignore, value){ - + var step = getStep(options, value); - + // TODO indexes[ step ] ?? indexes[ stepped ? step : value ] = fromStepping(options, stepped ? step : value); }); diff --git a/tests/goof.html b/tests/goof.html index 80b65be7..57093ec3 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -132,26 +132,6 @@ $.each(spread, addSpread); }; - function addPoints ( element, every ) { -/* - - console.log(keys); - - for ( var i = 0; i < 101; i+=1 ) { - var classes = ''; - if ( classes ) { - element.append('
'); - } - }*/ - } - - addPoints(legend4, { - 10: 'point point-large', - 1: 'point point-single', - 5: 'point point-sub' - }); - - $.each( slider[0].spread3, function ( index, value ) { legend3.append('
' + value + '
'); }); From 7f4411cc7ba6d38c439455ebb17a2eee651f3a6d Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 6 Jun 2014 22:45:42 +0200 Subject: [PATCH 08/28] More spread forms, started public methods TODO, figure out spreading on density --- jquery.nouislider.js | 195 ++++++++++++++++++++++++++++--------------- tests/goof.html | 81 ++++++++---------- 2 files changed, 162 insertions(+), 114 deletions(-) diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 8d0037b6..3a07058b 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -82,6 +82,13 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return (100 / (pb - pa)); } + // Removes duplicates from an array. + function unique(array) { + return $.grep(array, function(el, index) { + return index == $.inArray(el, array); + }); + } + // Type validation @@ -630,89 +637,142 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Legend - function generateSpread ( options, mode, stepped ) { + function generateSpread ( options, mode, values, stepped ) { - var indexes = {}; + var indexes = {}, group, density = 4; - if ( mode === 'range' || mode === 'steps' ) { + switch ( mode ) { + case 'range': + case 'steps': - // We'll build a list of steps. - var last = options.xVal.length - 1, - prevPct = 0; + // Use the range. + group = options.xVal; + break; - $.each(options.xVal, function ( index, value ) { + case 'count': + // Divide 0 - 100 in 'count' parts. + var spread = ( 100 / (values-1) ), v, i = 0; + values = []; - // Get the current step and the lower + upper positions. - var step = options.xNumSteps[ index ], - low = options.xVal[index], - high = options.xVal[index+1], - i; - - // Low can be 0. - if ( low === false || !high ) { - return; + // List these parts and have them handled as 'positions'. + while ((v=i++*spread) <= 100 ) { + values.push(v); } - // Set 'mode' to 'range' for only the actual range points. - if ( mode === 'range' ) { - indexes[options.xPct[index].toFixed(5)] = [value, true]; - } + case 'positions': - // ... or to 'steps' to generate points for all steps. - if ( mode === 'steps' ) { + // Map all percentages to on-range values. + group = $.map(values, function( value ){ + return fromStepping( options, stepped ? getStep(options, value) : value ); + }); + + break; + + case 'values': - if ( !step && !index ) { - indexes[0] = [low, true]; - return; - } + // If the value must be stepped, it needs to be converted to a percentage first. + if ( stepped ) { - // Find all steps in the subrange. - for ( i = low; i <= high; i += step ) { + group = $.map(values, function( value ){ - var newPct = toStepping(options, i), - pctDifference = newPct - prevPct, - pctDifferenceRound = Math.round(pctDifference), - pctRatio = pctDifference/pctDifferenceRound; + // Convert to percentage, apply step, return to value. + return fromStepping(options, getStep(options, toStepping(options, value))); + }); - for ( var q = 1; q < pctDifferenceRound; q += 1 ) { - var pos = prevPct + (pctRatio*q); - indexes[pos.toFixed(5)] = [pos, false]; - } + } else { - indexes[newPct.toFixed(5)] = [i, true]; - prevPct = newPct; - } + // Otherwise, we can simply use the values. + group = values; } - }); + break; + } - // Add the 'max' value to the end of the list. - indexes[100] = [options.xVal[ last ], true]; + // We'll build a list of steps. + var last = group.length - 1, firstInRange = options.xVal[0], + lastInRange = options.xVal[options.xVal.length-1], + ignoreFirst = 1, ignoreLast = 1, prevPct = 0; + + // Create a copy of the group, sort it and filter away all duplicates. + group = unique(group.slice().sort(function(a, b){ return a - b; })); + + // Make sure the range starts with the first element. + if ( group[0] !== firstInRange ) { + group.unshift(firstInRange); + ignoreFirst = 0; } - // Provide an array based on the number of points to be displayed. - if ( typeof mode === 'number' ) { + // Likewise for the last one. + if ( group[last] !== lastInRange ) { + group.push(lastInRange); + ignoreLast = 0; + } - var spread = ( 100 / (mode-1) ), v, i = 0; + $.each(group, function ( index, value ) { - mode = []; + // Get the current step and the lower + upper positions. + var step, i, + low = group[index], + high = group[index+1]; - while ((v=i++*spread) <= 100 ) { - mode.push(v); + // When using 'steps' mode, use the provided steps. + // Otherwise, we'll step on to the next subrange. + if ( mode === 'steps' ) { + step = options.xNumSteps[ index ] + } + + // Default to a 'full' step. + if ( !step ) { + step = high-low; } - } - // Provide the stepped value for all points in an array. - if ( $.isArray(mode) ) { + // Low can be 0, so test for false. If high is undefined, + // we are at the last subrange. Index 0 is already handled. + if ( low === false || high === undefined ) { + return; + } - $.each(mode, function(ignore, value){ + // Find all steps in the subrange. + for ( i = low; i <= high; i += step ) { - var step = getStep(options, value); + // Get the percentage value for the current step, + // calculate the size for the subrange. + var newPct = toStepping(options, i), + pctDifference = newPct - prevPct, + pctDifferenceRound = Math.round(pctDifference), + pctRatio = pctDifference/pctDifferenceRound || 1, - // TODO indexes[ step ] ?? - indexes[ stepped ? step : value ] = fromStepping(options, stepped ? step : value); - }); - } + // Correct the percentage offset by the number of points + // per subrange. density = 1 will result in 100 points on the + // full range, 2 for 50, 4 for 25, etc. + firstStart = prevPct;// + (density / 2); + + // Divide all points evenly, adding the correct number to this subrange. + for ( var q = density; q < pctDifferenceRound; q += density ) { + + // The ratio between the rounded value and the actual size might be ~1% off. + var pos = firstStart + (pctRatio*q); + console.log(pos); + indexes[pos.toFixed(5)] = [pos, 0]; + } + + var type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); + + // Enforce the 'ignoreFirst' option by overwriting the type for 0. + if ( !index && !ignoreFirst && !low ) { + type = 0; + } + + // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. + indexes[newPct.toFixed(5)] = [i, type]; + + // Update the percentage count. + prevPct = newPct; + } + }); + + // Add the 'max' value to the end of the list. + indexes[100] = [group[ last ], ignoreLast]; return indexes; }; @@ -1220,20 +1280,8 @@ function closure ( target, options, originalOptions ){ target.LinkDefaultFormatter = options.format; - - target.spread1 = generateSpread(options, 'range'); - target.spread2 = generateSpread(options, 'steps'); - target.spread3 = generateSpread(options, [0,25,50,75,100]); - target.spread4 = generateSpread(options, [0,25,50,75,100], true); - target.spread5 = generateSpread(options, 8); - target.spread6 = generateSpread(options, 8, true); - // todo, add support for value input [ 200, 1000, 4000, 6000 ] and return the percentages. - - - // Methods - // Set the slider value. /** @expose */ target.vSet = function ( ) { @@ -1329,6 +1377,12 @@ function closure ( target, options, originalOptions ){ return inSliderOrder( retour ); }; + // Create a legend for a slider. + /** @expose */ + target.getSpread = function ( mode, values, stepped ) { + return generateSpread( options, mode, values, stepped ); + }; + // Use the public value method to set the start values. $Target.val( options.start ); } @@ -1413,6 +1467,9 @@ function closure ( target, options, originalOptions ){ // Extend jQuery/Zepto with the noUiSlider method. /** @expose */ $.fn.noUiSlider = function ( options, rebuildFlag ) { + if ( options === 'getSpread' ) { + return this[0].getSpread.apply(this[0], Array.prototype.slice.call(arguments, 1)); + } return ( rebuildFlag ? rebuild : initialize ).call(this, options); }; diff --git a/tests/goof.html b/tests/goof.html index 57093ec3..b245a023 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -63,6 +63,9 @@ +
+
+
@@ -70,6 +73,8 @@
+
+
From 89012e79219b0d6e173f4142c0c19579f05ed4ff Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Mon, 9 Jun 2014 21:40:37 +0200 Subject: [PATCH 09/28] Added better legend support, density support --- jquery.nouislider.js | 293 ++++++++++++++++++++++++------------------- tests/goof.html | 95 ++++++++------ 2 files changed, 227 insertions(+), 161 deletions(-) diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 3a07058b..84fb046c 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -85,7 +85,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Removes duplicates from an array. function unique(array) { return $.grep(array, function(el, index) { - return index == $.inArray(el, array); + return index === $.inArray(el, array); }); } @@ -637,61 +637,61 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Legend - function generateSpread ( options, mode, values, stepped ) { + function getGroup ( options, mode, values, stepped ) { - var indexes = {}, group, density = 4; - - switch ( mode ) { - case 'range': - case 'steps': + // Use the range. + if ( mode === 'range' || mode === 'steps' ) { + return options.xVal; + } - // Use the range. - group = options.xVal; - break; + if ( mode === 'count' ) { - case 'count': - // Divide 0 - 100 in 'count' parts. - var spread = ( 100 / (values-1) ), v, i = 0; - values = []; + // Divide 0 - 100 in 'count' parts. + var spread = ( 100 / (values-1) ), v, i = 0; + values = []; - // List these parts and have them handled as 'positions'. - while ((v=i++*spread) <= 100 ) { - values.push(v); - } - - case 'positions': + // List these parts and have them handled as 'positions'. + while ((v=i++*spread) <= 100 ) { + values.push(v); + } - // Map all percentages to on-range values. - group = $.map(values, function( value ){ - return fromStepping( options, stepped ? getStep(options, value) : value ); - }); + mode = 'positions'; + } - break; + if ( mode === 'positions' ) { - case 'values': + // Map all percentages to on-range values. + return $.map(values, function( value ){ + return fromStepping( options, stepped ? getStep(options, value) : value ); + }); + } - // If the value must be stepped, it needs to be converted to a percentage first. - if ( stepped ) { + if ( mode === 'values' ) { - group = $.map(values, function( value ){ + // If the value must be stepped, it needs to be converted to a percentage first. + if ( stepped ) { - // Convert to percentage, apply step, return to value. - return fromStepping(options, getStep(options, toStepping(options, value))); - }); + return $.map(values, function( value ){ - } else { + // Convert to percentage, apply step, return to value. + return fromStepping(options, getStep(options, toStepping(options, value))); + }); - // Otherwise, we can simply use the values. - group = values; - } + } - break; + // Otherwise, we can simply use the values. + return values; } + } - // We'll build a list of steps. - var last = group.length - 1, firstInRange = options.xVal[0], + function generateSpread ( options, density, mode, group ) { + + var indexes = {}, + firstInRange = options.xVal[0], lastInRange = options.xVal[options.xVal.length-1], - ignoreFirst = 1, ignoreLast = 1, prevPct = 0; + ignoreFirst = false, + ignoreLast = false, + prevPct = 0; // Create a copy of the group, sort it and filter away all duplicates. group = unique(group.slice().sort(function(a, b){ return a - b; })); @@ -699,26 +699,28 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Make sure the range starts with the first element. if ( group[0] !== firstInRange ) { group.unshift(firstInRange); - ignoreFirst = 0; + ignoreFirst = true; } // Likewise for the last one. - if ( group[last] !== lastInRange ) { + if ( group[group.length - 1] !== lastInRange ) { group.push(lastInRange); - ignoreLast = 0; + ignoreLast = true; } $.each(group, function ( index, value ) { // Get the current step and the lower + upper positions. - var step, i, + var step, i, q, low = group[index], - high = group[index+1]; + high = group[index+1], + newPct, pctDifference, pctPos, type, + steps, realSteps, stepsize; // When using 'steps' mode, use the provided steps. // Otherwise, we'll step on to the next subrange. if ( mode === 'steps' ) { - step = options.xNumSteps[ index ] + step = options.xNumSteps[ index ]; } // Default to a 'full' step. @@ -737,46 +739,68 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Get the percentage value for the current step, // calculate the size for the subrange. - var newPct = toStepping(options, i), - pctDifference = newPct - prevPct, - pctDifferenceRound = Math.round(pctDifference), - pctRatio = pctDifference/pctDifferenceRound || 1, + newPct = toStepping(options, i); + pctDifference = newPct - prevPct; - // Correct the percentage offset by the number of points - // per subrange. density = 1 will result in 100 points on the - // full range, 2 for 50, 4 for 25, etc. - firstStart = prevPct;// + (density / 2); + steps = pctDifference / density; + realSteps = Math.round(steps); + + // This ratio represents the ammount of percentage-space a point indicates. + // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided. + // Round the percentage offset to an even number, then divide by two + // to spread the offset on both sides of the range. + stepsize = pctDifference/realSteps; // Divide all points evenly, adding the correct number to this subrange. - for ( var q = density; q < pctDifferenceRound; q += density ) { + // Run up to <= so that 100% gets a point, event if ignoreLast is set. + for ( q = 1; q <= realSteps; q += 1 ) { // The ratio between the rounded value and the actual size might be ~1% off. - var pos = firstStart + (pctRatio*q); - console.log(pos); - indexes[pos.toFixed(5)] = [pos, 0]; + // Correct the percentage offset by the number of points + // per subrange. density = 1 will result in 100 points on the + // full range, 2 for 50, 4 for 25, etc. + pctPos = prevPct + ( q * stepsize ); + indexes[pctPos.toFixed(5)] = ['x', 0]; // todo } - var type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); + // Determine the point type. + type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); // Enforce the 'ignoreFirst' option by overwriting the type for 0. - if ( !index && !ignoreFirst && !low ) { + if ( !index && ignoreFirst && !low ) { type = 0; } - // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. - indexes[newPct.toFixed(5)] = [i, type]; + if ( !(i === high && ignoreLast)) { + // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. + indexes[newPct.toFixed(5)] = [i, type]; + } // Update the percentage count. prevPct = newPct; } }); - // Add the 'max' value to the end of the list. - indexes[100] = [group[ last ], ignoreLast]; - return indexes; - }; + } + + function addMarking ( spread, element, filterFunc ) { + + function getClass( type, value ){ + return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; + } + function addSpread ( index, value ){ + + element.append('
'); + + if ( value[1] ) { + element.append('
' + Math.round(value[0]) + '
'); + } + } + + $.each(spread, addSpread); + } // Slider scope @@ -841,6 +865,61 @@ function closure ( target, options, originalOptions ){ } +// libLink integration + + // Create a new function which calls .val on input change. + function createChangeHandler ( trigger ) { + return function ( ignore, value ){ + // Determine which array position to 'null' based on 'trigger'. + $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); + }; + } + + // Called by libLink when it wants a set of links updated. + function LinkUpdate ( flag ) { + // The API might not have been set yet. + + var trigger = $.inArray(flag, triggerPos); + + try { + $Target[0].linkAPI[flag].change( + $Values[trigger], + $Handles[trigger].children(), + $Target + ); + } catch ( ignore ) { } + } + + // Called by libLink to append an element to the slider. + function LinkConfirm ( flag, element ) { + + // Find the trigger for the passed flag. + var trigger = $.inArray(flag, triggerPos); + + // If set, append the element to the handle it belongs to. + if ( element ) { + element.appendTo( $Handles[trigger].children() ); + } + + // The public API is reversed for rtl sliders, so the changeHandler + // should not be aware of the inverted trigger positions. + if ( options.dir ) { + trigger = trigger === 1 ? 0 : 1; + } + + return createChangeHandler( trigger ); + } + + /** @expose */ + target.LinkUpdate = LinkUpdate; + /** @expose */ + target.LinkConfirm = LinkConfirm; + /** @expose */ + target.LinkDefaultFlag = 'lower'; + /** @expose */ + target.LinkDefaultFormatter = options.format; + + // Handle placement // Test suggested values and apply margin, step. @@ -1225,61 +1304,6 @@ function closure ( target, options, originalOptions ){ events( options.events ); -// libLink integration - - // Create a new function which calls .val on input change. - function createChangeHandler ( trigger ) { - return function ( e, value ){ - // Determine which array position to 'null' based on 'trigger'. - $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); - } - } - - // Called by libLink when it wants a set of links updated. - function LinkUpdate ( flag ) { - // The API might not have been set yet. - - var trigger = $.inArray(flag, triggerPos); - - try { - $Target[0].linkAPI[flag].change( - $Values[trigger], - $Handles[trigger].children(), - $Target - ); - } catch ( ignore ) { } - } - - // Called by libLink to append an element to the slider. - function LinkConfirm ( flag, element ) { - - // Find the trigger for the passed flag. - var trigger = $.inArray(flag, triggerPos); - - // If set, append the element to the handle it belongs to. - if ( element ) { - element.appendTo( $Handles[trigger].children() ); - } - - // The public API is reversed for rtl sliders, so the changeHandler - // should not be aware of the inverted trigger positions. - if ( options.dir ) { - trigger = trigger === 1 ? 0 : 1; - } - - return createChangeHandler( trigger ); - } - - /** @expose */ - target.LinkUpdate = LinkUpdate; - /** @expose */ - target.LinkConfirm = LinkConfirm; - /** @expose */ - target.LinkDefaultFlag = 'lower'; - /** @expose */ - target.LinkDefaultFormatter = options.format; - - // Methods // Set the slider value. @@ -1357,7 +1381,7 @@ function closure ( target, options, originalOptions ){ // The API keeps a list of elements: we can re-append them on rebuild. $.each(this.linkAPI, function( trigger, elements ){ - $.each(elements, function(i, element) { + $.each(elements, function( ignore, element ) { element.appendTo( $Handles[trigger].children() ); }); }); @@ -1379,8 +1403,23 @@ function closure ( target, options, originalOptions ){ // Create a legend for a slider. /** @expose */ - target.getSpread = function ( mode, values, stepped ) { - return generateSpread( options, mode, values, stepped ); + target.createLegend = function ( grid ) { + + var mode = grid['mode'], + element = grid['element'], + density = grid['density'] || 1, + filter = grid['filter'] || false, + values = grid['values'] || false, + stepped = grid['stepped'] || false, + + group = getGroup( options, mode, values, stepped ), + spread = generateSpread( options, density, mode, group ); + + addMarking( + spread, + element, + filter + ); }; // Use the public value method to set the start values. @@ -1467,9 +1506,11 @@ function closure ( target, options, originalOptions ){ // Extend jQuery/Zepto with the noUiSlider method. /** @expose */ $.fn.noUiSlider = function ( options, rebuildFlag ) { - if ( options === 'getSpread' ) { - return this[0].getSpread.apply(this[0], Array.prototype.slice.call(arguments, 1)); + + if ( options === 'legend' ) { + return this[0].createLegend.apply(this[0], Array.prototype.slice.call(arguments, 1)); } + return ( rebuildFlag ? rebuild : initialize ).call(this, options); }; diff --git a/tests/goof.html b/tests/goof.html index b245a023..c6b5f6eb 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -94,49 +94,74 @@ console.log(this.vGet()); }); - function addMarking ( spread, element, filterFunc ) { + function filter500( value, type ){ + return value%1000 ? 2 : 1; + } - function getClass( type, value ){ - return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; - } + slider.noUiSlider('legend', { + element: $("#legend1"), + mode: 'range', + density: 3 + }); - function addSpread ( index, value ){ + slider.noUiSlider('legend', { + element: $("#legend2"), + mode: 'steps', + density: 2, + filter: filter500 + }); - element.append('
'); // '+value[1]+' + slider.noUiSlider('legend', { + element: $("#legend3"), + mode: 'positions', + values: [0,25,50,75,100] + }); - if ( value[1] ) { - element.append('
' + Math.round(value[0]) + '
'); - } - } + slider.noUiSlider('legend', { + element: $("#legend4"), + mode: 'positions', + values: [0,25,50,75,100], + stepped: true + }); - $.each(spread, addSpread); - }; + slider.noUiSlider('legend', { + element: $("#legend5"), + mode: 'count', + values: 8 + }); - function filter500( value, type ){ - return value%1000 ? 2 : 1; - } + slider.noUiSlider('legend', { + element: $("#legend6"), + mode: 'count', + values: 8, + stepped: true + }); -// addMarking(slider.noUiSlider('getSpread', 'range'), $("#legend1")); -// addMarking(slider.noUiSlider('getSpread', 'steps'), $("#legend2"), filter500); -// addMarking(slider.noUiSlider('getSpread', 'positions', [0,25,50,75,100]), $("#legend3")); -// addMarking(slider.noUiSlider('getSpread', 'positions', [0,25,50,75,100], true), $("#legend4")); -// addMarking(slider.noUiSlider('getSpread', 'count', 8), $("#legend5")); -// addMarking(slider.noUiSlider('getSpread', 'count', 8, true), $("#legend6")); -// addMarking(slider.noUiSlider('getSpread', 'values', [50, 552, 750, 940, 5000, 6080, 9000]), $("#legend7")); -// addMarking(slider.noUiSlider('getSpread', 'values', [50, 552, 750, 940, 5000, 6080, 9000], true), $("#legend8")); - - var slider0 = $("#slider0").noUiSlider({ - range: { - 'min': 20, - 'max': 100 - }, - step: 20, - start: 0 + slider.noUiSlider('legend', { + element: $("#legend7"), + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000] }); - - addMarking(slider0.noUiSlider('getSpread', 'steps'), $("#legend0"), function(v,t){ - return t===2?(v===60?1:2):t; + + slider.noUiSlider('legend', { + element: $("#legend8"), + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000], + stepped: true }); - + +// var slider0 = $("#slider0").noUiSlider({ +// range: { +// 'min': 0, +// 'max': 100 +// }, +// step: 10, +// start: 0 +// }); +// +// addMarking(slider0.noUiSlider('getSpread', 'steps'), $("#legend0"), function(v,t){ +// return t===2?(v===60?1:2):t; +// }); + From 34cda5517d95c85c4e5cf3723ac5039117dc9756 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 13 Jun 2014 21:07:23 +0200 Subject: [PATCH 10/28] Added legend classes, removed extend Extend is now default. --- jquery.nouislider.css | 9 ++++ jquery.nouislider.js | 50 +++++++++++-------- jquery.nouislider.legend.css | 94 ++++++++++++++++++++++++++++++++++++ tests/goof.html | 84 +++++++++++--------------------- 4 files changed, 162 insertions(+), 75 deletions(-) create mode 100644 jquery.nouislider.legend.css diff --git a/jquery.nouislider.css b/jquery.nouislider.css index 8bcf94cf..82757bb6 100644 --- a/jquery.nouislider.css +++ b/jquery.nouislider.css @@ -47,6 +47,15 @@ cursor: inherit !important; } +/* Painting and performance; + * Browsers can paint handles in their own layer. + */ +.noUi-origin, +.noUi-handle { + -webkit-transform: translate3d(0,0,0); + transform: translate3d(0,0,0); +} + /* Slider size and handle placement; */ .noUi-horizontal { diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 84fb046c..8258ec3d 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -56,7 +56,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ /* 13 */ ,'' /* 14 */ ,'noUi-state-tap' /* 15 */ ,'noUi-active' -/* 16 */ ,'noUi-extended' +/* 16 */ ,'' /* 17 */ ,'noUi-stacking' ], /** @const */ @@ -469,14 +469,12 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Check if the string contains any keywords. // None are required. var tap = entry.indexOf('tap') >= 0, - extend = entry.indexOf('extend') >= 0, drag = entry.indexOf('drag') >= 0, fixed = entry.indexOf('fixed') >= 0, snap = entry.indexOf('snap') >= 0; parsed.events = { tap: tap || snap, - extend: extend, drag: drag, fixed: fixed, snap: snap @@ -784,21 +782,38 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return indexes; } - function addMarking ( spread, element, filterFunc ) { + function addMarking ( options, spread, element, filterFunc ) { - function getClass( type, value ){ + var style = ['horizontal', 'vertical'][options.ort]; + + element.addClass('noUi-legend noUi-legend-'+style); + + function getSize( type, value ){ return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; } + function getTags( offset, source, values ) { + return 'class="' + source + ' ' + + source + '-' + style + ' ' + + source + getSize(values[1], values[0]) + + '" style="' + options.style + ': ' + offset + '%"'; + } + function addSpread ( offset, values ){ - function addSpread ( index, value ){ + // Invert the scale for rtl sliders. + if ( options.dir ) { + offset = 100 - offset; + } - element.append('
'); + // Add a marker for every point + element.append('
'); - if ( value[1] ) { - element.append('
' + Math.round(value[0]) + '
'); + // Values are only appended for points marked '1' or '2'. + if ( values[1] ) { + element.append('
' + Math.round(values[0]) + '
'); } } + // Append all points. $.each(spread, addSpread); } @@ -1248,21 +1263,15 @@ function closure ( target, options, originalOptions ){ // Attach the tap event to the slider base. if ( behaviour.tap ) { + attach ( actions.start, $Base, tap, { handles: $Handles }); - } - // Extend tapping behaviour to target - if ( behaviour.extend ) { - - $Target.addClass( Classes[16] ); - - if ( behaviour.tap ) { - attach ( actions.start, $Target, edge, { - handles: $Handles - }); - } + // Extend tapping behaviour to target + attach ( actions.start, $Target, edge, { + handles: $Handles + }); } // Make the range dragable. @@ -1416,6 +1425,7 @@ function closure ( target, options, originalOptions ){ spread = generateSpread( options, density, mode, group ); addMarking( + options, spread, element, filter diff --git a/jquery.nouislider.legend.css b/jquery.nouislider.legend.css new file mode 100644 index 00000000..954eb497 --- /dev/null +++ b/jquery.nouislider.legend.css @@ -0,0 +1,94 @@ + +/* Base; + * + */ +.noUi-legend, +.noUi-legend * { +-moz-box-sizing: border-box; + box-sizing: border-box; +} +.noUi-legend { + position: relative; + font: 400 12px Arial; + color: #999; +} + +/* Values; + * + */ +.noUi-value { + width: 40px; + position: absolute; + text-align: center; +} +.noUi-value-sub { + color: #ccc; + font-size: 10px; +} + +/* Markings; + * + */ +.noUi-marker { + position: absolute; + background: #CCC; +} +.noUi-marker-sub { + background: #AAA; +} +.noUi-marker-large { + background: #AAA; +} + +/* Horizontal layout; + * + */ +.noUi-legend-horizontal { + padding: 10px 0; + height: 50px; +} +.noUi-value-horizontal { + margin-left: -20px; + padding-top: 20px; +} +.noUi-value-horizontal.noUi-value-sub { + padding-top: 15px; +} + +.noUi-marker-horizontal.noUi-marker { + margin-left: -1px; + width: 2px; + height: 5px; +} +.noUi-marker-horizontal.noUi-marker-sub { + height: 10px; +} +.noUi-marker-horizontal.noUi-marker-large { + height: 15px; +} + +/* Vertical layout; + * + */ +.noUi-legend-vertical { + padding: 0 10px; + height: 300px; + display: inline-block; +} +.noUi-value-vertical { + width: 15px; + margin-left: 20px; + margin-top: -5px; +} + +.noUi-marker-vertical.noUi-marker { + width: 5px; + height: 2px; + margin-top: -1px; +} +.noUi-marker-vertical.noUi-marker-sub { + width: 10px; +} +.noUi-marker-vertical.noUi-marker-large { + width: 15px; +} diff --git a/tests/goof.html b/tests/goof.html index c6b5f6eb..f321188e 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -4,53 +4,17 @@ Testing @@ -58,6 +22,7 @@ + @@ -87,6 +52,8 @@ 'max': [ 10000 ] }, + direction: 'rtl', + start: 0 }).on('slide', function(){ @@ -150,18 +117,25 @@ stepped: true }); -// var slider0 = $("#slider0").noUiSlider({ -// range: { -// 'min': 0, -// 'max': 100 -// }, -// step: 10, -// start: 0 -// }); -// -// addMarking(slider0.noUiSlider('getSpread', 'steps'), $("#legend0"), function(v,t){ -// return t===2?(v===60?1:2):t; -// }); + var slider0 = $("#slider0").noUiSlider({ + range: { + 'min': 0, + 'max': 100 + }, + step: 10, + start: 0, + direction: 'rtl', + orientation: 'vertical' + }); + + slider0.noUiSlider('legend', { + element: $("#legend0"), + mode: 'steps', + density: 2, + filter: function(v,t){ + return t===2?(v===60?1:2):t; + } + }); From 90668e774fb7140352fd60941916e4ac637ffb81 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sat, 14 Jun 2014 15:50:46 +0200 Subject: [PATCH 11/28] Bugfixes and test updates --- jquery.nouislider.js | 45 ++++++++++------- tests/04.select.js | 12 ++--- tests/06.events.js | 2 +- tests/08.update.js | 2 +- tests/09.links.js | 33 ++++++------ tests/16.legend.js | 118 +++++++++++++++++++++++++++++++++++++++++++ tests/goof.html | 25 ++------- tests/run.html | 2 +- 8 files changed, 174 insertions(+), 65 deletions(-) create mode 100644 tests/16.legend.js diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 8258ec3d..456b32f7 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -89,6 +89,12 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ }); } + // Rounds a number to 7 supported decimals. + function accurateNumber( number ) { + var p = Math.pow(10, 7); + return Number((Math.round(number*p)/p).toFixed(7)); + } + // Type validation @@ -782,9 +788,10 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return indexes; } - function addMarking ( options, spread, element, filterFunc ) { + function addMarking ( options, spread, filterFunc ) { - var style = ['horizontal', 'vertical'][options.ort]; + var style = ['horizontal', 'vertical'][options.ort], + element = $('
'); element.addClass('noUi-legend noUi-legend-'+style); @@ -815,6 +822,8 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Append all points. $.each(spread, addSpread); + + return element; } @@ -830,15 +839,15 @@ function closure ( target, options, originalOptions ){ $Base, $Handles, $Values = [], - // libLink - triggerPos = ['lower', 'upper']; + // libLink. For rtl sliders, 'lower' and 'upper' should not be inverted + // for one-handle sliders, so trim 'upper' it that case. + triggerPos = ['lower', 'upper'].slice(0, options.handles); // Invert the libLink connection for rtl sliders. if ( options.dir ) { triggerPos.reverse(); } - // Helpers // Shorthand for base dimensions. @@ -892,17 +901,17 @@ function closure ( target, options, originalOptions ){ // Called by libLink when it wants a set of links updated. function LinkUpdate ( flag ) { - // The API might not have been set yet. var trigger = $.inArray(flag, triggerPos); - try { + // The API might not have been set yet. + if ( $Target[0].linkAPI && $Target[0].linkAPI[flag] ) { $Target[0].linkAPI[flag].change( $Values[trigger], $Handles[trigger].children(), $Target ); - } catch ( ignore ) { } + } } // Called by libLink to append an element to the slider. @@ -989,7 +998,7 @@ function closure ( target, options, originalOptions ){ } // Convert the value to the slider stepping/range. - $Values[trigger] = fromStepping( options, to ); + $Values[trigger] = accurateNumber(fromStepping( options, to )); LinkUpdate(triggerPos[trigger]); @@ -1388,12 +1397,14 @@ function closure ( target, options, originalOptions ){ /** @expose */ target.reappend = function ( ) { + var i, flag; + // The API keeps a list of elements: we can re-append them on rebuild. - $.each(this.linkAPI, function( trigger, elements ){ - $.each(elements, function( ignore, element ) { - element.appendTo( $Handles[trigger].children() ); - }); - }); + for ( i = 0; i < triggerPos.length; i++ ) { + if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { + this.linkAPI[flag].reconfirm(flag); + } + } }; // Get the current step size for the slider. @@ -1415,7 +1426,6 @@ function closure ( target, options, originalOptions ){ target.createLegend = function ( grid ) { var mode = grid['mode'], - element = grid['element'], density = grid['density'] || 1, filter = grid['filter'] || false, values = grid['values'] || false, @@ -1424,12 +1434,11 @@ function closure ( target, options, originalOptions ){ group = getGroup( options, mode, values, stepped ), spread = generateSpread( options, density, mode, group ); - addMarking( + $Target.after(addMarking( options, spread, - element, filter - ); + )); }; // Use the public value method to set the start values. diff --git a/tests/04.select.js b/tests/04.select.js index 72e44286..a2eb2de7 100644 --- a/tests/04.select.js +++ b/tests/04.select.js @@ -3,13 +3,13 @@ Q.html('\
\ - \ + \ '); - var input = $('#input'); + var select = $('#select'); for( var i = -20; i <= 40; i++ ){ - input.append( + select.append( '' ); } @@ -24,14 +24,14 @@ }); $('.slider').Link({ - target: input, + target: select, format: wNumb({ decimals: 0 }) }); - input.val( 40 ).change(); + select.val( 40 ).change(); - equal( input.val(), '30', 'Select was reset properly.' ); + equal( select.val(), '30', 'Select was reset properly.' ); }); diff --git a/tests/06.events.js b/tests/06.events.js index 276e892e..4a18495c 100644 --- a/tests/06.events.js +++ b/tests/06.events.js @@ -5,7 +5,7 @@
\ '); - var slider = $('.slider'); + var slider = $('.slider').width(242); slider.noUiSlider({ start: [ 30, 40 ], diff --git a/tests/08.update.js b/tests/08.update.js index 5e0a70b9..1f6615fe 100644 --- a/tests/08.update.js +++ b/tests/08.update.js @@ -55,7 +55,7 @@ }) }, true); - equal( Q.find('input').length, 1, 'Hidden input is gone.' ); + equal( Q.find('input').length, 2, 'Hidden input is still available.' ); equal( Q.find('input[name="newInput"]').length, 1 ); diff --git a/tests/09.links.js b/tests/09.links.js index 42552752..fa8fff78 100644 --- a/tests/09.links.js +++ b/tests/09.links.js @@ -12,31 +12,30 @@ this.html(val); }; - var val1 = 0, - val2 = 0, - vals = ['-100.00', '-100.00', '-90.00', '-90.00']; - var slider = $('.slider'), box = $('.box'), item = $('.item'), thing = $('.thing'); + var ltfCount = 0, lmfCount = 0, Expect = ['-100.00', '-90.00']; + function linkTargetFunction( value, handle, sliderInstance ){ - equal(value, vals[val1++], "Value as expected, change ("+val1+"/4)."); + + equal(value, Expect[ltfCount], "Value as expected, change ("+(ltfCount++)+"/2)."); + ok(handle.hasClass("noUi-handle"), "Handle is really a handle."); ok(handle.hasClass("noUi-handle-lower"), "Handle is really the lower handle."); ok(sliderInstance.hasClass("noUi-target"), "Slider is really a slider."); - ok(sliderInstance[0] === this, "Slider is scope of call."); + ok(sliderInstance[0] === this[0], "Slider is scope of call and $."); } function linkMethodFunction( value, handle, sliderInstance ){ - console.log(val); - - equal(value, vals[val2++], "Value as expected, change ("+val2+"/4)."); + equal(value, Expect[lmfCount], "Value as expected, change ("+(lmfCount++)+"/2)."); + ok(handle.hasClass("noUi-handle"), "Handle is really a handle."); ok(sliderInstance.hasClass("noUi-target"), "Slider is really a slider."); - ok(box[0] === this, "Element is scope of call."); + ok(box[0] === this[0], "Element is scope of call and $."); ok(slider[0] === sliderInstance[0], "Slider argument is slider."); } @@ -75,19 +74,21 @@ method: "html" }); - equal(item.html(), '9000.00', 'Setting by custom method works.'); - + // See if values bounced properly. + deepEqual(slider.val(), ['-100.00', '9000.00'], 'Slider has proper value'); + + // Check hidden input updates. ok($('input[name="hiddenInputField"]').is('input'), "Input field was generated."); - ok($('input[name="hiddenInputField"]').val(), '=++90.0', "Value is formatted as expected."); + equal($('input[name="hiddenInputField"]').val(), '=++100.0', "Value is formatted as expected."); + equal(item.html(), '9000.00', 'Setting by custom method works.'); slider.val([-90, 8051]); deepEqual(slider.val(), ['-90.00', '8051.00']); - // Link updated 4 times. - equal(val1, 4); - equal(thing.html(), '8051.00', 'Setting by existing method works.'); + equal(ltfCount, 2, 'Pre-bound link updated 4 times.'); + equal(lmfCount, 2, 'Post-bound Link updated 2 times.'); }); diff --git a/tests/16.legend.js b/tests/16.legend.js new file mode 100644 index 00000000..a57f0c6d --- /dev/null +++ b/tests/16.legend.js @@ -0,0 +1,118 @@ + + test( "Generation of legends", function(){ + + Q.html('\ +
\ +
\ + '); + + function filter500( value, type ){ + return value%1000 ? 2 : 1; + } + + var slider2 = $("#slider2").noUiSlider({ + range: { + 'min': [ 0 ], + '10%': [ 500, 500 ], + '50%': [ 4000, 1000 ], + 'max': [ 10000 ] + }, + start: 0 + }) + + // RANGE + + slider2.noUiSlider('legend', { + mode: 'range', + density: 3 + }); + + ok( slider2.next().hasClass('noUi-legend'), 'Legend was created' ); + + var markers = slider2.next().children('.noUi-marker').length; + ok( markers >= 32 && markers <= 34, 'Density of 1/3 was applied' ); + + // STEPS + + slider2.noUiSlider('legend', { + mode: 'steps', + density: 2, + filter: filter500 + }); + + var markers = slider2.next().children('.noUi-marker').length; + ok( markers >= 49 && markers <= 51, 'Density of 1/2 was applied' ); + + // POSITIONS + + slider2.noUiSlider('legend', { + mode: 'positions', + values: [0,25,50,75,100] + }); + + equal( slider2.next().children('.noUi-marker-large').length, 5, 'Large markers added for all values' ); + equal( slider2.next().children('.noUi-value').length, 5 ); + + var pos = []; + slider2.next().children('.noUi-value').each(function(){ + pos.push(parseInt(this.style.left)); + }); + + deepEqual(pos, [0,25,50,75,100], 'Values placed on proper positions'); + + // POSITIONS (STEPPED) + + slider2.noUiSlider('legend', { + mode: 'positions', + values: [0,25,50,75,100], + stepped: true + }); + + // todo + + // COUNT + + slider2.noUiSlider('legend', { + mode: 'count', + values: 8 + }); + + equal( slider2.next().children('.noUi-value').length, 8, 'Placed requested number of values' ); + + var pos2 = []; + slider2.next().children('.noUi-value').each(function(){ + pos2.push(parseInt(this.style.left)); + }); + + deepEqual(pos2, [0, Math.floor((100/7)*1), Math.floor((100/7)*2), Math.floor((100/7)*3), Math.floor((100/7)*4), Math.floor((100/7)*5), Math.floor((100/7)*6), 100], 'Values spread evenly'); + + // COUNT (STEPPED) + + slider2.noUiSlider('legend', { + mode: 'count', + values: 8, + stepped: true + }); + + // todo + + // VALUES + + slider2.noUiSlider('legend', { + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000] + }); + + equal( slider2.next().children('.noUi-value').length, 7, 'Placed requested number of values' ); + + // VALUES (STEPPED) + + slider2.noUiSlider('legend', { + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000], + stepped: true + }); + + equal( slider2.next().children('.noUi-value').length, 6, 'Removed duplicate in step' ); + + }); diff --git a/tests/goof.html b/tests/goof.html index f321188e..3bdee397 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -13,7 +13,7 @@ display: inline-block; margin-bottom: 50px; } - #legend0 { + #slider0 + .noUi-legend { margin-bottom: 50px; } @@ -29,21 +29,11 @@
-
- -
-
-
-
-
-
-
-
-
+
- @@ -58,5 +57,6 @@ + From a4e198117cda5f2cb7a574dac2832908538f2d37 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 22 Jun 2014 12:26:59 +0200 Subject: [PATCH 12/28] Moved all range calculations to an object model --- jquery.nouislider.js | 427 +++---------------------------------------- legend.js | 194 ++++++++++++++++++++ range.js | 240 ++++++++++++++++++++++++ tests/goof.html | 2 + tests/run.html | 2 + 5 files changed, 464 insertions(+), 401 deletions(-) create mode 100644 legend.js create mode 100644 range.js diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 456b32f7..b73666e5 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -72,23 +72,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return Math.max(Math.min(a, 100), 0); } - // Round a value to the closest 'to'. - function closest ( value, to ) { - return Math.round(value / to) * to; - } - - // Determine the size of a sub-range in relation to a full range. - function subRangeRatio ( pa, pb ) { - return (100 / (pb - pa)); - } - - // Removes duplicates from an array. - function unique(array) { - return $.grep(array, function(el, index) { - return index === $.inArray(el, array); - }); - } - // Rounds a number to 7 supported decimals. function accurateNumber( number ) { var p = Math.pow(10, 7); @@ -120,112 +103,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } -// Value calculation - - // (percentage) How many percent is this value of this range? - function fromPercentage ( range, value ) { - return (value * 100) / ( range[1] - range[0] ); - } - - // (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]; - } - - -// Range conversion - - // (percentage) Input a value, find where, on a scale of 0-100, it applies. - function toStepping ( options, value ) { - - if ( value >= options.xVal.slice(-1)[0] ){ - return 100; - } - - var j = 1, va, vb, pa, pb; - while ( value >= options.xVal[j] ){ - j++; - } - - va = options.xVal[j-1]; - vb = options.xVal[j]; - pa = options.xPct[j-1]; - pb = options.xPct[j]; - - return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); - } - - // (value) Input a percentage, find where it is on the specified range. - function fromStepping ( options, value ) { - - // There is no range group that fits 100 - if ( value >= 100 ){ - return options.xVal.slice(-1)[0]; - } - - var j = 1, va, vb, pa, pb; - while ( value >= options.xPct[j] ){ - j++; - } - - va = options.xVal[j-1]; - vb = options.xVal[j]; - pa = options.xPct[j-1]; - pb = options.xPct[j]; - - return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); - } - - // (j) Get the applicable step position. - function getStepPoint ( options, value ) { - - var j = 1; - - // Find the proper step for rtl sliders by search in inverse direction. - // Fixes issue #262. - while ( (options.dir ? (100 - value) : value) >= options.xPct[j] ){ - j++; - } - - return j; - } - - // (percentage) Get the step that applies at a certain value. - function getStep ( options, value ){ - - var j = getStepPoint( options, value ), a, b; - - // If 'snap' is set, steps are used as fixed points on the slider. - if ( options.snap ) { - - a = options.xPct[j-1]; - b = options.xPct[j]; - - // Find the closest position, a or b. - if ((value - a) > ((b-a)/2)){ - return b; - } - - return a; - } - - if ( !options.xSteps[j-1] ){ - return value; - } - - return options.xPct[j-1] + closest( - value - options.xPct[j-1], - options.xSteps[j-1] - ); - } - // Event handling @@ -292,7 +169,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // The step option can still be used to set stepping // for linear sliders. Overwritten if set in 'range'. - parsed.xSteps[0] = entry; + parsed.singleStep = entry; } function testRange ( parsed, entry ) { @@ -308,69 +185,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); } - // Loop all entries. - $.each( entry, function ( index, value ) { - - var percentage; - - // Wrap numerical input in an array. - if ( typeof value === "number" ) { - value = [value]; - } - - // Reject any invalid input. - if ( !$.isArray( value ) ){ - throw new Error("noUiSlider: 'range' contains invalid value."); - } - - // Covert min/max syntax to 0 and 100. - if ( index === 'min' ) { - percentage = 0; - } else if ( index === 'max' ) { - percentage = 100; - } else { - percentage = parseFloat( index ); - } - - // Check for correct input. - if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) { - throw new Error("noUiSlider: 'range' value isn't numeric."); - } - - // Store values. - parsed.xPct.push( percentage ); - parsed.xVal.push( value[0] ); - - // NaN will evaluate to false too, but to keep - // logging clear, set step explicitly. Make sure - // not to override the 'step' setting with false. - if ( !percentage ) { - if ( !isNaN( value[1] ) ) { - parsed.xSteps[0] = value[1]; - } - } else { - parsed.xSteps.push( isNaN(value[1]) ? false : value[1] ); - } - }); - - // Store the actual step values. - parsed.xNumSteps = parsed.xSteps.slice(0); - - $.each(parsed.xSteps, function(i,n){ - - // Ignore 'false' stepping. - if ( !n ) { - return true; - } - - // Factor to range ratio - parsed.xSteps[i] = fromPercentage([ - parsed.xVal[i] - ,parsed.xVal[i+1] - ], n) / subRangeRatio ( - parsed.xPct[i], - parsed.xPct[i+1] ); - }); + parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); } function testStart ( parsed, entry ) { @@ -516,10 +331,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ object, to make sure all values can be correctly looped elsewhere. */ var parsed = { - xPct: [], - xVal: [], - xSteps: [ false ], - xNumSteps: [ false ], margin: 0, format: defaultFormatter }, tests; @@ -574,12 +385,12 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // DOM additions // Append a handle to the base. - function addHandle ( options, index ) { + function addHandle ( direction, index ) { var handle = $('
').addClass( Classes[2] ), additions = [ '-lower', '-upper' ]; - if ( options.dir ) { + if ( direction ) { additions.reverse(); } @@ -611,222 +422,34 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } // Add handles to the slider base. - function addHandles ( options, base ) { + function addHandles ( nrHandles, direction, base ) { var index, handles = []; // Append handles. - for ( index = 0; index < options.handles; index++ ) { + for ( index = 0; index < nrHandles; index++ ) { // Keep a list of all added handles. - handles.push( addHandle( options, index ).appendTo(base) ); + handles.push( addHandle( direction, index ).appendTo(base) ); } return handles; } // Initialize a single slider. - function addSlider ( options, target ) { + function addSlider ( direction, orientation, target ) { // Apply classes and data to the target. target.addClass([ Classes[0], - Classes[8 + options.dir], - Classes[4 + options.ort] + Classes[8 + direction], + Classes[4 + orientation] ].join(' ')); return $('
').appendTo(target).addClass( Classes[1] ); } -// Legend - - function getGroup ( options, mode, values, stepped ) { - - // Use the range. - if ( mode === 'range' || mode === 'steps' ) { - return options.xVal; - } - - if ( mode === 'count' ) { - - // Divide 0 - 100 in 'count' parts. - var spread = ( 100 / (values-1) ), v, i = 0; - values = []; - - // List these parts and have them handled as 'positions'. - while ((v=i++*spread) <= 100 ) { - values.push(v); - } - - mode = 'positions'; - } - - if ( mode === 'positions' ) { - - // Map all percentages to on-range values. - return $.map(values, function( value ){ - return fromStepping( options, stepped ? getStep(options, value) : value ); - }); - } - - if ( mode === 'values' ) { - - // If the value must be stepped, it needs to be converted to a percentage first. - if ( stepped ) { - - return $.map(values, function( value ){ - - // Convert to percentage, apply step, return to value. - return fromStepping(options, getStep(options, toStepping(options, value))); - }); - - } - - // Otherwise, we can simply use the values. - return values; - } - } - - function generateSpread ( options, density, mode, group ) { - - var indexes = {}, - firstInRange = options.xVal[0], - lastInRange = options.xVal[options.xVal.length-1], - ignoreFirst = false, - ignoreLast = false, - prevPct = 0; - - // Create a copy of the group, sort it and filter away all duplicates. - group = unique(group.slice().sort(function(a, b){ return a - b; })); - - // Make sure the range starts with the first element. - if ( group[0] !== firstInRange ) { - group.unshift(firstInRange); - ignoreFirst = true; - } - - // Likewise for the last one. - if ( group[group.length - 1] !== lastInRange ) { - group.push(lastInRange); - ignoreLast = true; - } - - $.each(group, function ( index, value ) { - - // Get the current step and the lower + upper positions. - var step, i, q, - low = group[index], - high = group[index+1], - newPct, pctDifference, pctPos, type, - steps, realSteps, stepsize; - - // When using 'steps' mode, use the provided steps. - // Otherwise, we'll step on to the next subrange. - if ( mode === 'steps' ) { - step = options.xNumSteps[ index ]; - } - - // Default to a 'full' step. - if ( !step ) { - step = high-low; - } - - // Low can be 0, so test for false. If high is undefined, - // we are at the last subrange. Index 0 is already handled. - if ( low === false || high === undefined ) { - return; - } - - // Find all steps in the subrange. - for ( i = low; i <= high; i += step ) { - - // Get the percentage value for the current step, - // calculate the size for the subrange. - newPct = toStepping(options, i); - pctDifference = newPct - prevPct; - - steps = pctDifference / density; - realSteps = Math.round(steps); - - // This ratio represents the ammount of percentage-space a point indicates. - // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided. - // Round the percentage offset to an even number, then divide by two - // to spread the offset on both sides of the range. - stepsize = pctDifference/realSteps; - - // Divide all points evenly, adding the correct number to this subrange. - // Run up to <= so that 100% gets a point, event if ignoreLast is set. - for ( q = 1; q <= realSteps; q += 1 ) { - - // The ratio between the rounded value and the actual size might be ~1% off. - // Correct the percentage offset by the number of points - // per subrange. density = 1 will result in 100 points on the - // full range, 2 for 50, 4 for 25, etc. - pctPos = prevPct + ( q * stepsize ); - indexes[pctPos.toFixed(5)] = ['x', 0]; // todo - } - - // Determine the point type. - type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); - - // Enforce the 'ignoreFirst' option by overwriting the type for 0. - if ( !index && ignoreFirst && !low ) { - type = 0; - } - - if ( !(i === high && ignoreLast)) { - // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. - indexes[newPct.toFixed(5)] = [i, type]; - } - - // Update the percentage count. - prevPct = newPct; - } - }); - - return indexes; - } - - function addMarking ( options, spread, filterFunc ) { - - var style = ['horizontal', 'vertical'][options.ort], - element = $('
'); - - element.addClass('noUi-legend noUi-legend-'+style); - - function getSize( type, value ){ - return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; - } - function getTags( offset, source, values ) { - return 'class="' + source + ' ' + - source + '-' + style + ' ' + - source + getSize(values[1], values[0]) + - '" style="' + options.style + ': ' + offset + '%"'; - } - function addSpread ( offset, values ){ - - // Invert the scale for rtl sliders. - if ( options.dir ) { - offset = 100 - offset; - } - - // Add a marker for every point - element.append('
'); - - // Values are only appended for points marked '1' or '2'. - if ( values[1] ) { - element.append('
' + Math.round(values[0]) + '
'); - } - } - - // Append all points. - $.each(spread, addSpread); - - return element; - } - - // Slider scope function closure ( target, options, originalOptions ){ @@ -838,6 +461,7 @@ function closure ( target, options, originalOptions ){ $Locations = [-1, -1], $Base, $Handles, + $Spectrum = options.spectrum, $Values = [], // libLink. For rtl sliders, 'lower' and 'upper' should not be inverted // for one-handle sliders, so trim 'upper' it that case. @@ -870,7 +494,8 @@ function closure ( target, options, originalOptions ){ // Check if the range is effectively 0. function isNullRange ( ) { - return options.xVal.length === 2 && options.xVal[0] === options.xVal[1]; + return false; + return options.xVal.length === 2 && options.xVal[0] === options.xVal[1]; // TODO } // Returns the input array, respecting the slider direction configuration. @@ -968,7 +593,7 @@ function closure ( target, options, originalOptions ){ // Handle the step option. if ( to < 100 ){ - to = getStep(options, to); + to = $Spectrum.getStep( to ); } // Limit to 0/100 for .val input, trim anything beyond 7 digits, as @@ -998,7 +623,7 @@ function closure ( target, options, originalOptions ){ } // Convert the value to the slider stepping/range. - $Values[trigger] = accurateNumber(fromStepping( options, to )); + $Values[trigger] = accurateNumber( $Spectrum.fromStepping( to ) ); LinkUpdate(triggerPos[trigger]); @@ -1077,7 +702,7 @@ function closure ( target, options, originalOptions ){ } // Calculate the new handle position - to = toStepping( options, to ); + to = $Spectrum.toStepping( to ); // Invert the value if this is a right-to-left slider. if ( options.dir ) { @@ -1312,8 +937,8 @@ function closure ( target, options, originalOptions ){ // Create the base element, initialise HTML and set classes. // Add handles and links. - $Base = addSlider( options, $Target ); - $Handles = addHandles( options, $Base ); + $Base = addSlider( options.dir, options.ort, $Target ); + $Handles = addHandles( options.handles, options.dir, $Base ); // Set the connect classes. addConnection ( options.connect, $Target, $Handles ); @@ -1409,13 +1034,11 @@ function closure ( target, options, originalOptions ){ // Get the current step size for the slider. /** @expose */ - target.getStep = function ( ) { + target.getCurrentStep = function ( ) { // Check all locations, map them to their stepping point. - var retour = $.map($Locations, function( value ){ - // Get the step point, then find it in the input list. - return options.xNumSteps[getStepPoint(options, value) - 1]; - }); + // Get the step point, then find it in the input list. + var retour = $.map($Locations, $Spectrum.getApplicableStep); // Return values in the proper order. return inSliderOrder( retour ); @@ -1431,11 +1054,13 @@ function closure ( target, options, originalOptions ){ values = grid['values'] || false, stepped = grid['stepped'] || false, - group = getGroup( options, mode, values, stepped ), - spread = generateSpread( options, density, mode, group ); + group = getGroup( $Spectrum, mode, values, stepped ), + spread = generateSpread( $Spectrum, density, mode, group ); $Target.after(addMarking( - options, + options.style, + options.ort, + options.dir, spread, filter )); diff --git a/legend.js b/legend.js new file mode 100644 index 00000000..acb1cb1b --- /dev/null +++ b/legend.js @@ -0,0 +1,194 @@ + + // Removes duplicates from an array. + function unique(array) { + return $.grep(array, function(el, index) { + return index === $.inArray(el, array); + }); + } + +// Legend + + function getGroup ( $Spectrum, mode, values, stepped ) { + + // Use the range. + if ( mode === 'range' || mode === 'steps' ) { + return $Spectrum.xVal; + } + + if ( mode === 'count' ) { + + // Divide 0 - 100 in 'count' parts. + var spread = ( 100 / (values-1) ), v, i = 0; + values = []; + + // List these parts and have them handled as 'positions'. + while ((v=i++*spread) <= 100 ) { + values.push(v); + } + + mode = 'positions'; + } + + if ( mode === 'positions' ) { + + // Map all percentages to on-range values. + return $.map(values, function( value ){ + return $Spectrum.fromStepping( stepped ? $Spectrum.getStep( value ) : value ); + }); + } + + if ( mode === 'values' ) { + + // If the value must be stepped, it needs to be converted to a percentage first. + if ( stepped ) { + + return $.map(values, function( value ){ + + // Convert to percentage, apply step, return to value. + return $Spectrum.fromStepping( $Spectrum.getStep( $Spectrum.toStepping( value ) ) ); + }); + + } + + // Otherwise, we can simply use the values. + return values; + } + } + + function generateSpread ( $Spectrum, density, mode, group ) { + + var indexes = {}, + firstInRange = $Spectrum.xVal[0], + lastInRange = $Spectrum.xVal[$Spectrum.xVal.length-1], + ignoreFirst = false, + ignoreLast = false, + prevPct = 0; + + // Create a copy of the group, sort it and filter away all duplicates. + group = unique(group.slice().sort(function(a, b){ return a - b; })); + + // Make sure the range starts with the first element. + if ( group[0] !== firstInRange ) { + group.unshift(firstInRange); + ignoreFirst = true; + } + + // Likewise for the last one. + if ( group[group.length - 1] !== lastInRange ) { + group.push(lastInRange); + ignoreLast = true; + } + + $.each(group, function ( index, value ) { + + // Get the current step and the lower + upper positions. + var step, i, q, + low = group[index], + high = group[index+1], + newPct, pctDifference, pctPos, type, + steps, realSteps, stepsize; + + // When using 'steps' mode, use the provided steps. + // Otherwise, we'll step on to the next subrange. + if ( mode === 'steps' ) { + step = $Spectrum.xNumSteps[ index ]; + } + + // Default to a 'full' step. + if ( !step ) { + step = high-low; + } + + // Low can be 0, so test for false. If high is undefined, + // we are at the last subrange. Index 0 is already handled. + if ( low === false || high === undefined ) { + return; + } + + // Find all steps in the subrange. + for ( i = low; i <= high; i += step ) { + + // Get the percentage value for the current step, + // calculate the size for the subrange. + newPct = $Spectrum.toStepping( i ); + pctDifference = newPct - prevPct; + + steps = pctDifference / density; + realSteps = Math.round(steps); + + // This ratio represents the ammount of percentage-space a point indicates. + // For a density 1 the points/percentage = 1. For density 2, that percentage needs to be re-devided. + // Round the percentage offset to an even number, then divide by two + // to spread the offset on both sides of the range. + stepsize = pctDifference/realSteps; + + // Divide all points evenly, adding the correct number to this subrange. + // Run up to <= so that 100% gets a point, event if ignoreLast is set. + for ( q = 1; q <= realSteps; q += 1 ) { + + // The ratio between the rounded value and the actual size might be ~1% off. + // Correct the percentage offset by the number of points + // per subrange. density = 1 will result in 100 points on the + // full range, 2 for 50, 4 for 25, etc. + pctPos = prevPct + ( q * stepsize ); + indexes[pctPos.toFixed(5)] = ['x', 0]; // todo + } + + // Determine the point type. + type = ($.inArray(i, group) > -1) ? 1 : ( mode === 'steps' ? 2 : 0 ); + + // Enforce the 'ignoreFirst' option by overwriting the type for 0. + if ( !index && ignoreFirst && !low ) { + type = 0; + } + + if ( !(i === high && ignoreLast)) { + // Mark the 'type' of this point. 0 = plain, 1 = real value, 2 = step value. + indexes[newPct.toFixed(5)] = [i, type]; + } + + // Update the percentage count. + prevPct = newPct; + } + }); + + return indexes; + } + + function addMarking ( CSSstyle, orientation, direction, spread, filterFunc ) { + + var style = ['horizontal', 'vertical'][orientation], + element = $('
'); + + element.addClass('noUi-legend noUi-legend-'+style); + + function getSize( type, value ){ + return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; + } + function getTags( offset, source, values ) { + return 'class="' + source + ' ' + + source + '-' + style + ' ' + + source + getSize(values[1], values[0]) + + '" style="' + CSSstyle + ': ' + offset + '%"'; + } + function addSpread ( offset, values ){ + + // Invert the scale for rtl sliders. + if ( direction ) { + offset = 100 - offset; + } + + // Add a marker for every point + element.append('
'); + + // Values are only appended for points marked '1' or '2'. + if ( values[1] ) { + element.append('
' + Math.round(values[0]) + '
'); + } + } + + // Append all points. + $.each(spread, addSpread); + + return element; + } diff --git a/range.js b/range.js new file mode 100644 index 00000000..bb1365ee --- /dev/null +++ b/range.js @@ -0,0 +1,240 @@ +(function(){ + +// Helpers + + // Determine the size of a sub-range in relation to a full range. + function subRangeRatio ( pa, pb ) { // 0 + return (100 / (pb - pa)); + } + + // Round a value to the closest 'to'. + function closest ( value, to ) { // 0 + return Math.round(value / to) * to; + } + + // Checks whether a value is numerical. + function isNumeric ( a ) { + return typeof a === 'number' && !isNaN( a ) && isFinite( a ); + } + + +// Value calculation + + // (percentage) How many percent is this value of this range? + function fromPercentage ( range, value ) { // 1 + return (value * 100) / ( range[1] - range[0] ); + } + + // (percentage) Where is this value on this range? + function toPercentage ( range, value ) { // 0 + 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 ) { // 0 + return ((value * ( range[1] - range[0] )) / 100) + range[0]; + } + + +// Range conversion + + // (percentage) Input a value, find where, on a scale of 0-100, it applies. + function toStepping ( xVal, xPct, value ) { // 3 + + if ( value >= xVal.slice(-1)[0] ){ + return 100; + } + + var j = 1, va, vb, pa, pb; + while ( value >= xVal[j] ){ + j++; + } + + va = xVal[j-1]; + vb = xVal[j]; + pa = xPct[j-1]; + pb = xPct[j]; + + return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); + } + + // (value) Input a percentage, find where it is on the specified range. + function fromStepping ( xVal, xPct, value ) { // 3 + + // There is no range group that fits 100 + if ( value >= 100 ){ + return xVal.slice(-1)[0]; + } + + var j = 1, va, vb, pa, pb; + while ( value >= xPct[j] ){ + j++; + } + + va = xVal[j-1]; + vb = xVal[j]; + pa = xPct[j-1]; + pb = xPct[j]; + + return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); + } + + // (j) Get the applicable step position. + function getStepPoint ( direction, xPct, value ) { // 1 + + var j = 1; + + // Find the proper step for rtl sliders by search in inverse direction. + // Fixes issue #262. + while ( (direction ? (100 - value) : value) >= xPct[j] ){ + j++; + } + + return j; + } + + // (percentage) Get the step that applies at a certain value. + function getStep ( xPct, xSteps, snap, direction, value ) { // 3 + + var j = getStepPoint( direction, xPct, value ), a, b; + + // If 'snap' is set, steps are used as fixed points on the slider. + if ( snap ) { + + a = xPct[j-1]; + b = xPct[j]; + + // Find the closest position, a or b. + if ((value - a) > ((b-a)/2)){ + return b; + } + + return a; + } + + if ( !xSteps[j-1] ){ + return value; + } + + return xPct[j-1] + closest( + value - xPct[j-1], + xSteps[j-1] + ); + } + + +// Entry parsing + + function handleEntryPoint ( index, value, that ) { + + var percentage; + + // Wrap numerical input in an array. + if ( typeof value === "number" ) { + value = [value]; + } + + // Reject any invalid input, by testing whether value is an array. + if ( Object.prototype.toString.call( value ) !== '[object Array]' ){ + throw new Error("noUiSlider: 'range' contains invalid value."); + } + + // Covert min/max syntax to 0 and 100. + if ( index === 'min' ) { + percentage = 0; + } else if ( index === 'max' ) { + percentage = 100; + } else { + percentage = parseFloat( index ); + } + + // Check for correct input. + if ( !isNumeric( percentage ) || !isNumeric( value[0] ) ) { + throw new Error("noUiSlider: 'range' value isn't numeric."); + } + + // Store values. + that.xPct.push( percentage ); + that.xVal.push( value[0] ); + + // NaN will evaluate to false too, but to keep + // logging clear, set step explicitly. Make sure + // not to override the 'step' setting with false. + if ( !percentage ) { + if ( !isNaN( value[1] ) ) { + that.xSteps[0] = value[1]; + } + } else { + that.xSteps.push( isNaN(value[1]) ? false : value[1] ); + } + } + + function handleStepPoint ( i, n, that ) { + + // Ignore 'false' stepping. + if ( !n ) { + return true; + } + + // Factor to range ratio + that.xSteps[i] = fromPercentage([ + that.xVal[i] + ,that.xVal[i+1] + ], n) / subRangeRatio ( + that.xPct[i], + that.xPct[i+1] ); + } + + +// Interface + + function Spectrum ( entry, snap, direction, singleStep ) { + + this.xPct = []; + this.xVal = []; + this.xSteps = [ singleStep || false ]; + this.xNumSteps = [ false ]; + + this.snap = snap; + this.direction = direction; + + var that = this, index; + + // Loop all entries. + for ( index in entry ) { + if ( entry.hasOwnProperty(index) ) { + handleEntryPoint(index, entry[index], that); + } + } + + // Store the actual step values. + that.xNumSteps = that.xSteps.slice(0); + + for ( index in that.xNumSteps ) { + if ( that.xNumSteps.hasOwnProperty(index) ) { + handleStepPoint(Number(index), that.xNumSteps[index], that); + } + } + } + + Spectrum.prototype.toStepping = function ( value ) { + return toStepping(this.xVal, this.xPct, value ); + }; + + Spectrum.prototype.fromStepping = function ( value ) { + return fromStepping(this.xVal, this.xPct, value ); + }; + + Spectrum.prototype.getStep = function ( value ) { + return getStep(this.xPct, this.xSteps, this.snap, this.direction, value ); + }; + + Spectrum.prototype.getApplicableStep = function ( value ) { + return this.xNumSteps[getStepPoint(this.direction, this.xPct, value) - 1]; + }; + + window.Spectrum = Spectrum; + +}()); diff --git a/tests/goof.html b/tests/goof.html index 3bdee397..a74bbaf7 100644 --- a/tests/goof.html +++ b/tests/goof.html @@ -20,6 +20,8 @@ + + diff --git a/tests/run.html b/tests/run.html index 64a933df..6a9a5bed 100644 --- a/tests/run.html +++ b/tests/run.html @@ -29,6 +29,8 @@ + + From db8c707a2a9c420ac60beadc4d0197128a475773 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Tue, 24 Jun 2014 11:54:43 +0200 Subject: [PATCH 13/28] Moved RTL to Spectrum, added tests --- jquery.nouislider.js | 67 ++++++--------------------- range.js | 108 +++++++++++++++++++++++++++++-------------- tests/range.html | 71 ++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 86 deletions(-) create mode 100644 tests/range.html diff --git a/jquery.nouislider.js b/jquery.nouislider.js index b73666e5..2dfdb62f 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -72,12 +72,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return Math.max(Math.min(a, 100), 0); } - // Rounds a number to 7 supported decimals. - function accurateNumber( number ) { - var p = Math.pow(10, 7); - return Number((Math.round(number*p)/p).toFixed(7)); - } - // Type validation @@ -341,8 +335,8 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ 'start': { r: true, t: testStart }, 'connect': { r: true, t: testConnect }, 'direction': { r: true, t: testDirection }, - 'range': { r: true, t: testRange }, 'snap': { r: false, t: testSnap }, + 'range': { r: true, t: testRange }, 'orientation': { r: false, t: testOrientation }, 'margin': { r: false, t: testMargin }, 'behaviour': { r: true, t: testBehaviour }, @@ -492,12 +486,6 @@ function closure ( target, options, originalOptions ){ } } - // Check if the range is effectively 0. - function isNullRange ( ) { - return false; - return options.xVal.length === 2 && options.xVal[0] === options.xVal[1]; // TODO - } - // Returns the input array, respecting the slider direction configuration. function inSliderOrder ( values ) { @@ -578,32 +566,22 @@ function closure ( target, options, originalOptions ){ lower = $Locations[0] + options.margin, upper = $Locations[1] - options.margin; - // Check if the slider has no range. If so, lock in the center. - if ( isNullRange() ) { - - to = 50; - - } else { - - // For sliders with multiple handles, - // limit movement to the other handle. - if ( $Handles.length > 1 ) { - to = trigger ? Math.max( to, lower ) : Math.min( to, upper ); - } + // For sliders with multiple handles, + // limit movement to the other handle. + if ( $Handles.length > 1 ) { + to = trigger ? Math.max( to, lower ) : Math.min( to, upper ); + } - // Handle the step option. - if ( to < 100 ){ - to = $Spectrum.getStep( to ); - } + // Handle the step option. + to = $Spectrum.getStep( to ); - // Limit to 0/100 for .val input, trim anything beyond 7 digits, as - // JavaScript has some issues in its floating point implementation. - to = limit(parseFloat(to.toFixed(7))); + // Limit to 0/100 for .val input, trim anything beyond 7 digits, as + // JavaScript has some issues in its floating point implementation. + to = limit(parseFloat(to.toFixed(7))); - // Return false if handle can't move. - if ( to === $Locations[trigger] ) { - return false; - } + // Return false if handle can't move. + if ( to === $Locations[trigger] ) { + return false; } // Set the handle to the new position. @@ -617,13 +595,8 @@ function closure ( target, options, originalOptions ){ // Update locations. $Locations[trigger] = to; - // Invert the value if this is a right-to-left slider. - if ( options.dir ) { - to = 100 - to; - } - // Convert the value to the slider stepping/range. - $Values[trigger] = accurateNumber( $Spectrum.fromStepping( to ) ); + $Values[trigger] = $Spectrum.fromStepping( to ); LinkUpdate(triggerPos[trigger]); @@ -704,11 +677,6 @@ function closure ( target, options, originalOptions ){ // Calculate the new handle position to = $Spectrum.toStepping( to ); - // Invert the value if this is a right-to-left slider. - if ( options.dir ) { - to = 100 - to; - } - // Set the handle. setHandle( $Handles[trigger], to ); } @@ -739,11 +707,6 @@ function closure ( target, options, originalOptions ){ return false; } - // Ignore all events if the range is effectively 0. #236 - if ( isNullRange() ) { - return false; - } - e = fixEvent(e); e.calcPoint = e.points[ options.ort ]; diff --git a/range.js b/range.js index bb1365ee..5462bbc1 100644 --- a/range.js +++ b/range.js @@ -1,14 +1,18 @@ +/*jslint browser: true, white: true */ + (function(){ + 'use strict'; + // Helpers // Determine the size of a sub-range in relation to a full range. - function subRangeRatio ( pa, pb ) { // 0 + function subRangeRatio ( pa, pb ) { return (100 / (pb - pa)); } // Round a value to the closest 'to'. - function closest ( value, to ) { // 0 + function closest ( value, to ) { return Math.round(value / to) * to; } @@ -17,6 +21,12 @@ return typeof a === 'number' && !isNaN( a ) && isFinite( a ); } + // Rounds a number to 7 supported decimals. + function accurateNumber( number ) { + var p = Math.pow(10, 7); + return Number((Math.round(number*p)/p).toFixed(7)); + } + // Value calculation @@ -26,31 +36,39 @@ } // (percentage) Where is this value on this range? - function toPercentage ( range, value ) { // 0 + 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 ) { // 0 + function isPercentage ( range, value ) { return ((value * ( range[1] - range[0] )) / 100) + range[0]; } // Range conversion + function getJ ( value, arr ) { + + var j = 1; + + while ( value >= arr[j] ){ + j += 1; + } + + return j; + } + // (percentage) Input a value, find where, on a scale of 0-100, it applies. - function toStepping ( xVal, xPct, value ) { // 3 + function toStepping ( xVal, xPct, value ) { if ( value >= xVal.slice(-1)[0] ){ return 100; } - var j = 1, va, vb, pa, pb; - while ( value >= xVal[j] ){ - j++; - } + var j = getJ( value, xVal ), va, vb, pa, pb; va = xVal[j-1]; vb = xVal[j]; @@ -61,17 +79,14 @@ } // (value) Input a percentage, find where it is on the specified range. - function fromStepping ( xVal, xPct, value ) { // 3 + function fromStepping ( xVal, xPct, value ) { // There is no range group that fits 100 if ( value >= 100 ){ return xVal.slice(-1)[0]; } - var j = 1, va, vb, pa, pb; - while ( value >= xPct[j] ){ - j++; - } + var j = getJ( value, xPct ), va, vb, pa, pb; va = xVal[j-1]; vb = xVal[j]; @@ -81,24 +96,14 @@ return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); } - // (j) Get the applicable step position. - function getStepPoint ( direction, xPct, value ) { // 1 - - var j = 1; + // (percentage) Get the step that applies at a certain value. + function getStep ( xPct, xSteps, snap, value ) { - // Find the proper step for rtl sliders by search in inverse direction. - // Fixes issue #262. - while ( (direction ? (100 - value) : value) >= xPct[j] ){ - j++; + if ( value === 100 ) { + return value; } - return j; - } - - // (percentage) Get the step that applies at a certain value. - function getStep ( xPct, xSteps, snap, direction, value ) { // 3 - - var j = getStepPoint( direction, xPct, value ), a, b; + var j = getJ( value, xPct ), a, b; // If 'snap' is set, steps are used as fixed points on the slider. if ( snap ) { @@ -190,6 +195,9 @@ // Interface + // The interface to Spectrum handles all direction-based + // conversions, so the above values are unaware. + function Spectrum ( entry, snap, direction, singleStep ) { this.xPct = []; @@ -220,21 +228,53 @@ } Spectrum.prototype.toStepping = function ( value ) { - return toStepping(this.xVal, this.xPct, value ); + + value = toStepping( this.xVal, this.xPct, value ); + + // Invert the value if this is a right-to-left slider. + if ( this.direction ) { + value = 100 - value; + } + + return value; }; Spectrum.prototype.fromStepping = function ( value ) { - return fromStepping(this.xVal, this.xPct, value ); + + // Invert the value if this is a right-to-left slider. + if ( this.direction ) { + value = 100 - value; + } + + return accurateNumber(fromStepping( this.xVal, this.xPct, value )); }; Spectrum.prototype.getStep = function ( value ) { - return getStep(this.xPct, this.xSteps, this.snap, this.direction, value ); + + // Find the proper step for rtl sliders by search in inverse direction. + // Fixes issue #262. + if ( this.direction ) { + value = 100 - value; + } + + value = getStep(this.xPct, this.xSteps, this.snap, value ); + + if ( this.direction ) { + value = 100 - value; + } + + return value; }; Spectrum.prototype.getApplicableStep = function ( value ) { - return this.xNumSteps[getStepPoint(this.direction, this.xPct, value) - 1]; + return this.xNumSteps[getJ(value, this.xPct) - 1]; + }; + + // Outside testing + Spectrum.prototype.convert = function ( value ) { + return this.getStep(this.toStepping(value)); }; window.Spectrum = Spectrum; - + }()); diff --git a/tests/range.html b/tests/range.html new file mode 100644 index 00000000..d3e91f97 --- /dev/null +++ b/tests/range.html @@ -0,0 +1,71 @@ + + + + Testing + + + + + + + +
+ + + From e2193cfe2d6bb3a91086038162da101210f50387 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Wed, 25 Jun 2014 17:49:32 +0200 Subject: [PATCH 14/28] Fixed margin, + animate, - legend, - value flags --- jquery.nouislider.css | 12 ----- jquery.nouislider.js | 100 ++++++++++++-------------------------- range.js | 11 ++++- tests/try.html | 109 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 83 deletions(-) create mode 100644 tests/try.html diff --git a/jquery.nouislider.css b/jquery.nouislider.css index 82757bb6..7a75c738 100644 --- a/jquery.nouislider.css +++ b/jquery.nouislider.css @@ -67,12 +67,6 @@ left: -17px; top: -6px; } -.noUi-horizontal.noUi-extended { - padding: 0 15px; -} -.noUi-horizontal.noUi-extended .noUi-origin { - right: -15px; -} .noUi-vertical { width: 18px; } @@ -82,12 +76,6 @@ left: -6px; top: -17px; } -.noUi-vertical.noUi-extended { - padding: 15px 0; -} -.noUi-vertical.noUi-extended .noUi-origin { - bottom: -15px; -} /* Styling; */ diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 2dfdb62f..c3adc440 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -210,6 +210,16 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } } + function testAnimate ( parsed, entry ) { + + // Enforce 100% stepping within subranges. + parsed.animate = entry; + + if ( typeof entry !== 'boolean' ){ + throw new Error("noUiSlider: 'animate' option must be a boolean."); + } + } + function testConnect ( parsed, entry ) { if ( entry === 'lower' && parsed.handles === 1 ) { @@ -243,17 +253,13 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ function testMargin ( parsed, entry ) { - if ( parsed.xPct.length > 2 ) { - throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); - } - - // Parse value to range and store. As xVal is checked - // to be no bigger than 2, use it as range. - parsed.margin = fromPercentage(parsed.xVal, entry); - if ( !isNumeric(entry) ){ throw new Error("noUiSlider: 'margin' option must be numeric."); } + + if ( !(parsed.margin = parsed.spectrum.getMargin(entry)) ) { + throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); + } } function testDirection ( parsed, entry ) { @@ -326,6 +332,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ var parsed = { margin: 0, + animate: true, format: defaultFormatter }, tests; @@ -336,6 +343,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ 'connect': { r: true, t: testConnect }, 'direction': { r: true, t: testDirection }, 'snap': { r: false, t: testSnap }, + 'animate': { r: false, t: testAnimate }, 'range': { r: true, t: testRange }, 'orientation': { r: false, t: testOrientation }, 'margin': { r: false, t: testMargin }, @@ -552,8 +560,6 @@ function closure ( target, options, originalOptions ){ /** @expose */ target.LinkConfirm = LinkConfirm; /** @expose */ - target.LinkDefaultFlag = 'lower'; - /** @expose */ target.LinkDefaultFormatter = options.format; @@ -829,17 +835,6 @@ function closure ( target, options, originalOptions ){ } } - // Move handle to edges when target gets tapped. - function edge ( event ) { - - var i = event.calcPoint < $Base.offset()[ options.style ], - to = i ? 0 : 100; - - i = i ? 0 : $Handles.length - 1; - - jump( $Handles[i], to, false ); - } - // Attach events to several slider parts. function events ( behaviour ) { @@ -864,11 +859,6 @@ function closure ( target, options, originalOptions ){ attach ( actions.start, $Base, tap, { handles: $Handles }); - - // Extend tapping behaviour to target - attach ( actions.start, $Target, edge, { - handles: $Handles - }); } // Make the range dragable. @@ -914,17 +904,9 @@ function closure ( target, options, originalOptions ){ // Set the slider value. /** @expose */ - target.vSet = function ( ) { + target.vSet = function ( input ) { - var args = Array.prototype.slice.call( arguments ), - count, values = asArray( args[0] ); - - // Support the 'true' option. - if ( args[1] === true ) { - args[1] = { 'set': true }; - } else if ( typeof args[1] !== 'object' ) { - args[1] = {}; - } + var count, values = asArray( input ); // The RTL settings is implemented by reversing the front-end, // internal mechanisms are the same. @@ -933,7 +915,7 @@ function closure ( target, options, originalOptions ){ } // Animation is optional. - if ( args[1]['animate'] ) { + if ( options.animate ) { addClassFor( $Target, Classes[14], 300 ); } @@ -946,10 +928,9 @@ function closure ( target, options, originalOptions ){ setValues ( count, values ); - // Optionally fire the 'set' event. - if ( args[1]['set'] === true ) { - fireEvents(['set']); - } + // Fire the 'set' event. As of noUiSlider 7, + // this is no longer optional. + fireEvents(['set']); return this; }; @@ -1001,34 +982,20 @@ function closure ( target, options, originalOptions ){ // Check all locations, map them to their stepping point. // Get the step point, then find it in the input list. - var retour = $.map($Locations, $Spectrum.getApplicableStep); + var retour = $.map($Locations, function( location, index ){ + + var step = $Spectrum.getApplicableStep( location ), + value = $Values[index], + increment = step[2], + decrement = (value - step[2]) >= step[1] ? step[2] : step[0]; + + return [[decrement, increment]]; + }); // Return values in the proper order. return inSliderOrder( retour ); }; - // Create a legend for a slider. - /** @expose */ - target.createLegend = function ( grid ) { - - var mode = grid['mode'], - density = grid['density'] || 1, - filter = grid['filter'] || false, - values = grid['values'] || false, - stepped = grid['stepped'] || false, - - group = getGroup( $Spectrum, mode, values, stepped ), - spread = generateSpread( $Spectrum, density, mode, group ); - - $Target.after(addMarking( - options.style, - options.ort, - options.dir, - spread, - filter - )); - }; - // Use the public value method to set the start values. $Target.val( options.start ); } @@ -1113,11 +1080,6 @@ function closure ( target, options, originalOptions ){ // Extend jQuery/Zepto with the noUiSlider method. /** @expose */ $.fn.noUiSlider = function ( options, rebuildFlag ) { - - if ( options === 'legend' ) { - return this[0].createLegend.apply(this[0], Array.prototype.slice.call(arguments, 1)); - } - return ( rebuildFlag ? rebuild : initialize ).call(this, options); }; diff --git a/range.js b/range.js index 5462bbc1..bd07eabb 100644 --- a/range.js +++ b/range.js @@ -31,7 +31,7 @@ // Value calculation // (percentage) How many percent is this value of this range? - function fromPercentage ( range, value ) { // 1 + function fromPercentage ( range, value ) { return (value * 100) / ( range[1] - range[0] ); } @@ -227,6 +227,10 @@ } } + Spectrum.prototype.getMargin = function ( value ) { + return this.xPct.length === 2 ? fromPercentage(this.xVal, value) : false; + }; + Spectrum.prototype.toStepping = function ( value ) { value = toStepping( this.xVal, this.xPct, value ); @@ -267,7 +271,10 @@ }; Spectrum.prototype.getApplicableStep = function ( value ) { - return this.xNumSteps[getJ(value, this.xPct) - 1]; + + // If the value is 100%, return the negative step twice. + var j = getJ(value, this.xPct), offset = value === 100 ? 2 : 1; + return [this.xNumSteps[j-2], this.xVal[j-offset], this.xNumSteps[j-offset]]; }; // Outside testing diff --git a/tests/try.html b/tests/try.html new file mode 100644 index 00000000..61b87602 --- /dev/null +++ b/tests/try.html @@ -0,0 +1,109 @@ + + + + + Trying + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ + + + + From e7535bc30a691c46982d4e74a20370d3f50956ca Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sat, 26 Jul 2014 15:18:14 +0200 Subject: [PATCH 15/28] Pulled out options --- jquery.nouislider.js | 285 +++---------------------------------------- options.js | 246 +++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 267 deletions(-) create mode 100644 options.js diff --git a/jquery.nouislider.js b/jquery.nouislider.js index c3adc440..34f54de4 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -58,11 +58,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ /* 15 */ ,'noUi-active' /* 16 */ ,'' /* 17 */ ,'noUi-stacking' - ], - /** @const */ - defaultFormatter = { 'to': function( value ){ - return value.toFixed(2); - }, 'from': Number }; + ]; // General helpers @@ -72,14 +68,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ return Math.max(Math.min(a, 100), 0); } - -// Type validation - - // Checks whether a value is numerical. - function isNumeric ( a ) { - return typeof a === 'number' && !isNaN( a ) && isFinite( a ); - } - // Wraps a variable as an array, if it isn't one yet. function asArray ( a ) { return $.isArray(a) ? a : [a]; @@ -153,237 +141,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ } -// Input validation - - function testStep ( parsed, entry ) { - - if ( !isNumeric( entry ) ) { - throw new Error("noUiSlider: 'step' is not numeric."); - } - - // The step option can still be used to set stepping - // for linear sliders. Overwritten if set in 'range'. - parsed.singleStep = entry; - } - - function testRange ( parsed, entry ) { - - // Filter incorrect input. - if ( typeof entry !== 'object' || $.isArray(entry) ) { - throw new Error("noUiSlider: 'range' is not an object."); - } - - // Catch missing start or end. - if ( entry['min'] === undefined || - entry['max'] === undefined ) { - throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); - } - - parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); - } - - function testStart ( parsed, entry ) { - - entry = asArray(entry); - - // Validate input. Values aren't tested, as the public .val method - // will always provide a valid location. - if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) { - throw new Error("noUiSlider: 'start' option is incorrect."); - } - - // Store the number of handles. - parsed.handles = entry.length; - - // When the slider is initialized, the .val method will - // be called with the start options. - parsed.start = entry; - } - - function testSnap ( parsed, entry ) { - - // Enforce 100% stepping within subranges. - parsed.snap = entry; - - if ( typeof entry !== 'boolean' ){ - throw new Error("noUiSlider: 'snap' option must be a boolean."); - } - } - - function testAnimate ( parsed, entry ) { - - // Enforce 100% stepping within subranges. - parsed.animate = entry; - - if ( typeof entry !== 'boolean' ){ - throw new Error("noUiSlider: 'animate' option must be a boolean."); - } - } - - function testConnect ( parsed, entry ) { - - if ( entry === 'lower' && parsed.handles === 1 ) { - parsed.connect = 1; - } else if ( entry === 'upper' && parsed.handles === 1 ) { - parsed.connect = 2; - } else if ( entry === true && parsed.handles === 2 ) { - parsed.connect = 3; - } else if ( entry === false ) { - parsed.connect = 0; - } else { - throw new Error("noUiSlider: 'connect' option doesn't match handle count."); - } - } - - function testOrientation ( parsed, entry ) { - - // Set orientation to an a numerical value for easy - // array selection. - switch ( entry ){ - case 'horizontal': - parsed.ort = 0; - break; - case 'vertical': - parsed.ort = 1; - break; - default: - throw new Error("noUiSlider: 'orientation' option is invalid."); - } - } - - function testMargin ( parsed, entry ) { - - if ( !isNumeric(entry) ){ - throw new Error("noUiSlider: 'margin' option must be numeric."); - } - - if ( !(parsed.margin = parsed.spectrum.getMargin(entry)) ) { - throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); - } - } - - function testDirection ( parsed, entry ) { - - // Set direction as a numerical value for easy parsing. - // Invert connection for RTL sliders, so that the proper - // handles get the connect/background classes. - switch ( entry ) { - case 'ltr': - parsed.dir = 0; - break; - case 'rtl': - parsed.dir = 1; - parsed.connect = [0,2,1,3][parsed.connect]; - break; - default: - throw new Error("noUiSlider: 'direction' option was not recognized."); - } - } - - function testBehaviour ( parsed, entry ) { - - // Make sure the input is a string. - if ( typeof entry !== 'string' ) { - throw new Error("noUiSlider: 'behaviour' must be a string containing options."); - } - - // Check if the string contains any keywords. - // None are required. - var tap = entry.indexOf('tap') >= 0, - drag = entry.indexOf('drag') >= 0, - fixed = entry.indexOf('fixed') >= 0, - snap = entry.indexOf('snap') >= 0; - - parsed.events = { - tap: tap || snap, - drag: drag, - fixed: fixed, - snap: snap - }; - } - - function testFormat ( parsed, entry ) { - - parsed.format = entry; - - // Any object with a to and from method is supported. - if ( typeof entry['to'] === 'function' && typeof entry['from'] === 'function' ) { - return true; - } - - throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); - } - - // Test all developer settings and parse to assumption-safe values. - function test ( options, 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. */ - - var parsed = { - margin: 0, - animate: true, - format: defaultFormatter - }, tests; - - // Tests are executed in the order they are presented here. - tests = { - 'step': { r: false, t: testStep }, - 'start': { r: true, t: testStart }, - 'connect': { r: true, t: testConnect }, - 'direction': { r: true, t: testDirection }, - 'snap': { r: false, t: testSnap }, - 'animate': { r: false, t: testAnimate }, - 'range': { r: true, t: testRange }, - 'orientation': { r: false, t: testOrientation }, - 'margin': { r: false, t: testMargin }, - 'behaviour': { r: true, t: testBehaviour }, - 'format': { r: false, t: testFormat } - }; - - // Set defaults where applicable. - options = $.extend({ - 'connect': false, - 'direction': 'ltr', - 'behaviour': 'tap', - 'orientation': 'horizontal' - }, options); - - // Run all options through a testing mechanism to ensure correct - // input. It should be noted that options might get modified to - // be handled properly. E.g. wrapping integers in arrays. - $.each( tests, function( name, test ){ - - // If the option isn't set, but it is required, throw an error. - if ( options[name] === undefined ) { - - if ( test.r ) { - throw new Error("noUiSlider: '" + name + "' is required."); - } - - return true; - } - - test.t( parsed, options[name], sliders ); - }); - - // Pre-define the styles. - parsed.style = parsed.ort ? 'top' : 'left'; - - return parsed; - } - - // DOM additions // Append a handle to the base. @@ -474,6 +231,7 @@ function closure ( target, options, originalOptions ){ triggerPos.reverse(); } + // Helpers // Shorthand for base dimensions. @@ -521,7 +279,7 @@ function closure ( target, options, originalOptions ){ } // Called by libLink when it wants a set of links updated. - function LinkUpdate ( flag ) { + function linkUpdate ( flag ) { var trigger = $.inArray(flag, triggerPos); @@ -536,7 +294,7 @@ function closure ( target, options, originalOptions ){ } // Called by libLink to append an element to the slider. - function LinkConfirm ( flag, element ) { + function linkConfirm ( flag, element ) { // Find the trigger for the passed flag. var trigger = $.inArray(flag, triggerPos); @@ -556,9 +314,9 @@ function closure ( target, options, originalOptions ){ } /** @expose */ - target.LinkUpdate = LinkUpdate; + target.LinkUpdate = linkUpdate; /** @expose */ - target.LinkConfirm = LinkConfirm; + target.LinkConfirm = linkConfirm; /** @expose */ target.LinkDefaultFormatter = options.format; @@ -604,7 +362,7 @@ function closure ( target, options, originalOptions ){ // Convert the value to the slider stepping/range. $Values[trigger] = $Spectrum.fromStepping( to ); - LinkUpdate(triggerPos[trigger]); + linkUpdate(triggerPos[trigger]); return true; } @@ -632,21 +390,6 @@ function closure ( target, options, originalOptions ){ return [c,d]; } - // Handles movement by tapping. - function jump ( handle, to, instant ) { - - if ( !instant ) { - // Flag the slider as it is now in a transitional state. - // Transition takes 300 ms, so re-enable the slider afterwards. - addClassFor( $Target, Classes[14], 300 ); - } - - // Move the handle to the new position. - setHandle( handle, to ); - - fireEvents(['slide', 'set', 'change']); - } - // Loop values from value method and apply them. function setValues ( count, values ) { @@ -676,7 +419,7 @@ function closure ( target, options, originalOptions ){ // Request an update for all links if the value was invalid. if ( to === false || isNaN(to) ) { - LinkUpdate(triggerPos[trigger]); + linkUpdate(triggerPos[trigger]); continue; } @@ -826,9 +569,17 @@ function closure ( target, options, originalOptions ){ // Calculate the new position. to = ( location * 100 ) / baseSize(); + if ( !options.events.snap ) { + // Flag the slider as it is now in a transitional state. + // Transition takes 300 ms, so re-enable the slider afterwards. + addClassFor( $Target, Classes[14], 300 ); + } + // Find the closest handle and calculate the tapped point. // The set handle to the new position. - jump( $Handles[total], to, options.events.snap ); + setHandle( $Handles[total], to ); + + fireEvents(['slide', 'set', 'change']); if ( options.events.snap ) { start(event, { handles: [$Handles[total]] }); @@ -1012,7 +763,7 @@ function closure ( target, options, originalOptions ){ } // Test the options once, not for every slider. - var options = test( originalOptions, this ); + var options = test_noUiSliderOptions( originalOptions, this ); // Loop all items, and provide a new closed-scope environment. return this.each(function(){ diff --git a/options.js b/options.js new file mode 100644 index 00000000..711834c2 --- /dev/null +++ b/options.js @@ -0,0 +1,246 @@ +/* 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(){ + + // Wraps a variable as an array, if it isn't one yet. + function asArray ( a ) { + return $.isArray(a) ? a : [a]; + } + + // Checks whether a value is numerical. + function isNumeric ( a ) { + return typeof a === 'number' && !isNaN( a ) && isFinite( a ); + } + + /** @const */ + var defaultFormatter = { 'to': function( value ){ + return value.toFixed(2); + }, 'from': Number }; + + function testStep ( parsed, entry ) { + + if ( !isNumeric( entry ) ) { + throw new Error("noUiSlider: 'step' is not numeric."); + } + + // The step option can still be used to set stepping + // for linear sliders. Overwritten if set in 'range'. + parsed.singleStep = entry; + } + + function testRange ( parsed, entry ) { + + // Filter incorrect input. + if ( typeof entry !== 'object' || $.isArray(entry) ) { + throw new Error("noUiSlider: 'range' is not an object."); + } + + // Catch missing start or end. + if ( entry['min'] === undefined || + entry['max'] === undefined ) { + throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); + } + + parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); + } + + function testStart ( parsed, entry ) { + + entry = asArray(entry); + + // Validate input. Values aren't tested, as the public .val method + // will always provide a valid location. + if ( !$.isArray( entry ) || !entry.length || entry.length > 2 ) { + throw new Error("noUiSlider: 'start' option is incorrect."); + } + + // Store the number of handles. + parsed.handles = entry.length; + + // When the slider is initialized, the .val method will + // be called with the start options. + parsed.start = entry; + } + + function testSnap ( parsed, entry ) { + + // Enforce 100% stepping within subranges. + parsed.snap = entry; + + if ( typeof entry !== 'boolean' ){ + throw new Error("noUiSlider: 'snap' option must be a boolean."); + } + } + + function testAnimate ( parsed, entry ) { + + // Enforce 100% stepping within subranges. + parsed.animate = entry; + + if ( typeof entry !== 'boolean' ){ + throw new Error("noUiSlider: 'animate' option must be a boolean."); + } + } + + function testConnect ( parsed, entry ) { + + if ( entry === 'lower' && parsed.handles === 1 ) { + parsed.connect = 1; + } else if ( entry === 'upper' && parsed.handles === 1 ) { + parsed.connect = 2; + } else if ( entry === true && parsed.handles === 2 ) { + parsed.connect = 3; + } else if ( entry === false ) { + parsed.connect = 0; + } else { + throw new Error("noUiSlider: 'connect' option doesn't match handle count."); + } + } + + function testOrientation ( parsed, entry ) { + + // Set orientation to an a numerical value for easy + // array selection. + switch ( entry ){ + case 'horizontal': + parsed.ort = 0; + break; + case 'vertical': + parsed.ort = 1; + break; + default: + throw new Error("noUiSlider: 'orientation' option is invalid."); + } + } + + function testMargin ( parsed, entry ) { + + if ( !isNumeric(entry) ){ + throw new Error("noUiSlider: 'margin' option must be numeric."); + } + + if ( !(parsed.margin = parsed.spectrum.getMargin(entry)) ) { + throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); + } + } + + function testDirection ( parsed, entry ) { + + // Set direction as a numerical value for easy parsing. + // Invert connection for RTL sliders, so that the proper + // handles get the connect/background classes. + switch ( entry ) { + case 'ltr': + parsed.dir = 0; + break; + case 'rtl': + parsed.dir = 1; + parsed.connect = [0,2,1,3][parsed.connect]; + break; + default: + throw new Error("noUiSlider: 'direction' option was not recognized."); + } + } + + function testBehaviour ( parsed, entry ) { + + // Make sure the input is a string. + if ( typeof entry !== 'string' ) { + throw new Error("noUiSlider: 'behaviour' must be a string containing options."); + } + + // Check if the string contains any keywords. + // None are required. + var tap = entry.indexOf('tap') >= 0, + drag = entry.indexOf('drag') >= 0, + fixed = entry.indexOf('fixed') >= 0, + snap = entry.indexOf('snap') >= 0; + + parsed.events = { + tap: tap || snap, + drag: drag, + fixed: fixed, + snap: snap + }; + } + + function testFormat ( parsed, entry ) { + + parsed.format = entry; + + // Any object with a to and from method is supported. + if ( typeof entry['to'] === 'function' && typeof entry['from'] === 'function' ) { + return true; + } + + throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); + } + + // Test all developer settings and parse to assumption-safe values. + window.test_noUiSliderOptions = function ( options ){ + + var parsed = { + margin: 0, + animate: true, + format: defaultFormatter + }, tests; + + // Tests are executed in the order they are presented here. + tests = { + 'step': { r: false, t: testStep }, + 'start': { r: true, t: testStart }, + 'connect': { r: true, t: testConnect }, + 'direction': { r: true, t: testDirection }, + 'snap': { r: false, t: testSnap }, + 'animate': { r: false, t: testAnimate }, + 'range': { r: true, t: testRange }, + 'orientation': { r: false, t: testOrientation }, + 'margin': { r: false, t: testMargin }, + 'behaviour': { r: true, t: testBehaviour }, + 'format': { r: false, t: testFormat } + }; + + // Set defaults where applicable. + options = $.extend({ + 'connect': false, + 'direction': 'ltr', + 'behaviour': 'tap', + 'orientation': 'horizontal' + }, options); + + // Run all options through a testing mechanism to ensure correct + // input. It should be noted that options might get modified to + // be handled properly. E.g. wrapping integers in arrays. + $.each( tests, function( name, test ){ + + // If the option isn't set, but it is required, throw an error. + if ( options[name] === undefined ) { + + if ( test.r ) { + throw new Error("noUiSlider: '" + name + "' is required."); + } + + return true; + } + + test.t( parsed, options[name] ); + }); + + // Pre-define the styles. + parsed.style = parsed.ort ? 'top' : 'left'; + + return parsed; + } + +}()); From 1d52c1322d1086dbe9aec767e528508d61986aa6 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 1 Aug 2014 22:23:00 +0200 Subject: [PATCH 16/28] Updated Link, fixed linkUpdate not firing --- jquery.nouislider.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/jquery.nouislider.js b/jquery.nouislider.js index 34f54de4..a8b4383d 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -319,6 +319,8 @@ function closure ( target, options, originalOptions ){ target.LinkConfirm = linkConfirm; /** @expose */ target.LinkDefaultFormatter = options.format; + /** @expose */ + target.LinkDefaultFlag = 'lower'; // Handle placement @@ -418,16 +420,18 @@ function closure ( target, options, originalOptions ){ to = options.format.from( to ); // Request an update for all links if the value was invalid. - if ( to === false || isNaN(to) ) { - linkUpdate(triggerPos[trigger]); - continue; - } + if ( to !== false && !isNaN(to) ) { - // Calculate the new handle position - to = $Spectrum.toStepping( to ); + // Calculate the new handle position + to = $Spectrum.toStepping( to ); + + // Set the handle. + if ( setHandle( $Handles[trigger], to ) !== false ) { + continue; + } + } - // Set the handle. - setHandle( $Handles[trigger], to ); + linkUpdate(triggerPos[trigger]); } } From 1e0c3da84be704632d9ca9a56eb14b72e2409893 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 3 Aug 2014 14:18:10 +0200 Subject: [PATCH 17/28] New test suite, pips module --- jquery.nouislider.css | 3 + jquery.nouislider.js | 178 +++++++++++------- ...r.legend.css => jquery.nouislider.pips.css | 20 +- legend.js => jquery.nouislider.pips.js | 49 ++++- tests/02.shared-link.js | 42 ----- tests/05.set.js | 26 --- tests/07.rtl-set.js | 30 --- tests/08.update.js | 67 ------- tests/09.links.js | 94 --------- tests/10.rtl.js | 47 ----- tests/12.update-link.js | 58 ------ tests/15.step-limit.js | 29 --- tests/16.legend.js | 118 ------------ tests/addon_pips.js | 164 ++++++++++++++++ tests/{13.default-val.js => general_val.js} | 7 +- tests/goof.html | 124 ------------ tests/link_basic.js | 90 +++++++++ tests/{01.inputs.js => link_multiple.js} | 13 +- tests/{04.select.js => link_select.js} | 7 +- tests/link_update.js | 68 +++++++ tests/run.html | 64 ------- tests/slider.html | 72 +++++++ tests/{06.events.js => slider_events.js} | 12 +- ...linear-rtl.js => slider_non-linear-rtl.js} | 8 +- tests/slider_rtl.js | 54 ++++++ .../{11.val.js => slider_setting-getting.js} | 11 +- tests/slider_step.js | 27 +++ tests/slider_update.js | 63 +++++++ tests/slider_values.js | 26 +++ tests/try.html | 109 ----------- tests/{03.format.js => wnumb_basic.js} | 65 +++---- 31 files changed, 776 insertions(+), 969 deletions(-) rename jquery.nouislider.legend.css => jquery.nouislider.pips.css (86%) rename legend.js => jquery.nouislider.pips.js (83%) delete mode 100644 tests/02.shared-link.js delete mode 100644 tests/05.set.js delete mode 100644 tests/07.rtl-set.js delete mode 100644 tests/08.update.js delete mode 100644 tests/09.links.js delete mode 100644 tests/10.rtl.js delete mode 100644 tests/12.update-link.js delete mode 100644 tests/15.step-limit.js delete mode 100644 tests/16.legend.js create mode 100644 tests/addon_pips.js rename tests/{13.default-val.js => general_val.js} (85%) delete mode 100644 tests/goof.html create mode 100644 tests/link_basic.js rename tests/{01.inputs.js => link_multiple.js} (79%) rename tests/{04.select.js => link_select.js} (84%) create mode 100644 tests/link_update.js delete mode 100644 tests/run.html create mode 100644 tests/slider.html rename tests/{06.events.js => slider_events.js} (73%) rename tests/{14.non-linear-rtl.js => slider_non-linear-rtl.js} (92%) create mode 100644 tests/slider_rtl.js rename tests/{11.val.js => slider_setting-getting.js} (87%) create mode 100644 tests/slider_step.js create mode 100644 tests/slider_update.js create mode 100644 tests/slider_values.js delete mode 100644 tests/try.html rename tests/{03.format.js => wnumb_basic.js} (52%) diff --git a/jquery.nouislider.css b/jquery.nouislider.css index 7a75c738..1e34ac94 100644 --- a/jquery.nouislider.css +++ b/jquery.nouislider.css @@ -13,6 +13,9 @@ -moz-box-sizing: border-box; box-sizing: border-box; } +.noUi-target { + position: relative; +} .noUi-base { width: 100%; height: 100%; diff --git a/jquery.nouislider.js b/jquery.nouislider.js index a8b4383d..66dad6e4 100644 --- a/jquery.nouislider.js +++ b/jquery.nouislider.js @@ -2,7 +2,6 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ /*jslint browser: true */ -/*jslint sub: true */ /*jslint white: true */ /*jslint continue: true */ /*jslint plusplus: true */ @@ -84,6 +83,29 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ }, duration); } + // Delimit proposed values for handle positions. + function getPositions ( a, b, delimit ) { + + // Add movement to current position. + var c = a + b[0], d = a + b[1]; + + // Only alter the other position on drag, + // not on standard sliding. + if ( delimit ) { + if ( c < 0 ) { + d += Math.abs(c); + } + if ( d > 100 ) { + c -= ( d - 100 ); + } + + // Limit values to 0 and 100. + return [limit(c), limit(d)]; + } + + return [c,d]; + } + // Event handling @@ -313,6 +335,19 @@ function closure ( target, options, originalOptions ){ return createChangeHandler( trigger ); } + // Place elements back on the slider. + function reAppendLink ( ) { + + var i, flag; + + // The API keeps a list of elements: we can re-append them on rebuild. + for ( i = 0; i < triggerPos.length; i++ ) { + if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { + this.linkAPI[flag].reconfirm(flag); + } + } + }; + /** @expose */ target.LinkUpdate = linkUpdate; /** @expose */ @@ -322,8 +357,9 @@ function closure ( target, options, originalOptions ){ /** @expose */ target.LinkDefaultFlag = 'lower'; + /** @expose */ + target.reappend = reAppendLink; -// Handle placement // Test suggested values and apply margin, step. function setHandle ( handle, to ) { @@ -369,29 +405,6 @@ function closure ( target, options, originalOptions ){ return true; } - // Delimit proposed values for handle positions. - function getPositions ( a, b, delimit ) { - - // Add movement to current position. - var c = a + b[0], d = a + b[1]; - - // Only alter the other position on drag, - // not on standard sliding. - if ( delimit ) { - if ( c < 0 ) { - d += Math.abs(c); - } - if ( d > 100 ) { - c -= ( d - 100 ); - } - - // Limit values to 0 and 100. - return [limit(c), limit(d)]; - } - - return [c,d]; - } - // Loop values from value method and apply them. function setValues ( count, values ) { @@ -408,7 +421,8 @@ function closure ( target, options, originalOptions ){ to = values[trigger]; // Setting with null indicates an 'ignore'. - if ( to === null ) { + // Inputting 'false' is invalid. + if ( to === null || to === false ) { continue; } @@ -436,7 +450,6 @@ function closure ( target, options, originalOptions ){ } -// Events // Handler for attaching events trough a proxy. function attach ( events, element, callback, data ) { @@ -636,30 +649,9 @@ function closure ( target, options, originalOptions ){ } -// Initialize slider - - // Throw an error if the slider was already initialized. - if ( $Target.hasClass(Classes[0]) ) { - throw new Error('Slider was already initialized.'); - } - - // Create the base element, initialise HTML and set classes. - // Add handles and links. - $Base = addSlider( options.dir, options.ort, $Target ); - $Handles = addHandles( options.handles, options.dir, $Base ); - - // Set the connect classes. - addConnection ( options.connect, $Target, $Handles ); - - // Attach user events. - events( options.events ); - - -// Methods // Set the slider value. - /** @expose */ - target.vSet = function ( input ) { + function valueSet ( input ) { var count, values = asArray( input ); @@ -688,11 +680,10 @@ function closure ( target, options, originalOptions ){ fireEvents(['set']); return this; - }; + } // Get the slider value. - /** @expose */ - target.vGet = function ( ) { + function valueGet ( ) { var i, retour = []; @@ -702,38 +693,35 @@ function closure ( target, options, originalOptions ){ } return inSliderOrder( retour ); - }; + } + + // Destroy the slider and unbind all events. - /** @expose */ - target.destroy = function ( ) { + function destroyTarget ( ) { // Unbind events on the slider, remove all classes and child elements. $(this).off(namespace) .removeClass(Classes.join(' ')) .empty(); + delete this.LinkUpdate; + delete this.LinkConfirm; + delete this.LinkDefaultFormatter; + delete this.LinkDefaultFlag; + delete this.reappend; + delete this.vGet; + delete this.vSet; + delete this.getCurrentStep; + delete this.getInfo; + delete this.destroy; + // Return the original options from the closure. return originalOptions; }; - // Place elements back on the slider. - /** @expose */ - target.reappend = function ( ) { - - var i, flag; - - // The API keeps a list of elements: we can re-append them on rebuild. - for ( i = 0; i < triggerPos.length; i++ ) { - if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { - this.linkAPI[flag].reconfirm(flag); - } - } - }; - // Get the current step size for the slider. - /** @expose */ - target.getCurrentStep = function ( ) { + function getCurrentStep ( ) { // Check all locations, map them to their stepping point. // Get the step point, then find it in the input list. @@ -751,6 +739,45 @@ function closure ( target, options, originalOptions ){ return inSliderOrder( retour ); }; + + +// Initialize slider + + // Throw an error if the slider was already initialized. + if ( $Target.hasClass(Classes[0]) ) { + throw new Error('Slider was already initialized.'); + } + + // Create the base element, initialise HTML and set classes. + // Add handles and links. + $Base = addSlider( options.dir, options.ort, $Target ); + $Handles = addHandles( options.handles, options.dir, $Base ); + + // Set the connect classes. + addConnection ( options.connect, $Target, $Handles ); + + // Attach user events. + events( options.events ); + +// Methods + + /** @expose */ + target.vSet = valueSet; + /** @expose */ + target.vGet = valueGet; + /** @expose */ + target.destroy = destroyTarget; + /** @expose */ + target.getCurrentStep = getCurrentStep; + + target.getInfo = function(){ + return [ + $Spectrum, + options.style, + options.ort, + ]; + }; + // Use the public value method to set the start values. $Target.val( options.start ); } @@ -780,10 +807,15 @@ function closure ( target, options, originalOptions ){ return this.each(function(){ + // The rebuild flag can be used if the slider wasn't initialized yet. + if ( !this.destroy ) { + $(this).noUiSlider( options ); + return; + } + // Get the current values from the slider, // including the initialization options. - var values = $(this).val(), - originalOptions = this.destroy(), + var values = $(this).val(), originalOptions = this.destroy(), // Extend the previous options with the newly provided ones. newOptions = $.extend( {}, originalOptions, options ); diff --git a/jquery.nouislider.legend.css b/jquery.nouislider.pips.css similarity index 86% rename from jquery.nouislider.legend.css rename to jquery.nouislider.pips.css index 954eb497..d1a93971 100644 --- a/jquery.nouislider.legend.css +++ b/jquery.nouislider.pips.css @@ -2,13 +2,13 @@ /* Base; * */ -.noUi-legend, -.noUi-legend * { +.noUi-pips, +.noUi-pips * { -moz-box-sizing: border-box; box-sizing: border-box; } -.noUi-legend { - position: relative; +.noUi-pips { + position: absolute; font: 400 12px Arial; color: #999; } @@ -43,9 +43,12 @@ /* Horizontal layout; * */ -.noUi-legend-horizontal { +.noUi-pips-horizontal { padding: 10px 0; height: 50px; + top: 100%; + left: 0; + width: 100%; } .noUi-value-horizontal { margin-left: -20px; @@ -70,10 +73,11 @@ /* Vertical layout; * */ -.noUi-legend-vertical { +.noUi-pips-vertical { padding: 0 10px; - height: 300px; - display: inline-block; + height: 100%; + top: 0; + left: 100%; } .noUi-value-vertical { width: 15px; diff --git a/legend.js b/jquery.nouislider.pips.js similarity index 83% rename from legend.js rename to jquery.nouislider.pips.js index acb1cb1b..eb5ac9fc 100644 --- a/legend.js +++ b/jquery.nouislider.pips.js @@ -1,3 +1,4 @@ +(function(){ // Removes duplicates from an array. function unique(array) { @@ -6,7 +7,7 @@ }); } -// Legend +// Pips function getGroup ( $Spectrum, mode, values, stepped ) { @@ -57,13 +58,19 @@ function generateSpread ( $Spectrum, density, mode, group ) { - var indexes = {}, + var originalSpectrumDirection = $Spectrum.direction; + indexes = {}, firstInRange = $Spectrum.xVal[0], lastInRange = $Spectrum.xVal[$Spectrum.xVal.length-1], ignoreFirst = false, ignoreLast = false, prevPct = 0; + // This function loops the spectrum in an ltr linear fashion, + // while the toStepping method is direction aware. Trick it into + // believing it is ltr. + $Spectrum.direction = 0; + // Create a copy of the group, sort it and filter away all duplicates. group = unique(group.slice().sort(function(a, b){ return a - b; })); @@ -79,6 +86,8 @@ ignoreLast = true; } + var loglist = []; + $.each(group, function ( index, value ) { // Get the current step and the lower + upper positions. @@ -152,6 +161,9 @@ } }); + // Reset the spectrum. + $Spectrum.direction = originalSpectrumDirection; + return indexes; } @@ -160,7 +172,7 @@ var style = ['horizontal', 'vertical'][orientation], element = $('
'); - element.addClass('noUi-legend noUi-legend-'+style); + element.addClass('noUi-pips noUi-pips-'+style); function getSize( type, value ){ return [ '-normal', '-large', '-sub' ][(type&&filterFunc) ? filterFunc(value, type) : type]; @@ -173,7 +185,6 @@ } function addSpread ( offset, values ){ - // Invert the scale for rtl sliders. if ( direction ) { offset = 100 - offset; } @@ -192,3 +203,33 @@ return element; } + + $.fn.noUiSlider_pips = function ( grid ) { + + var mode = grid['mode'], + density = grid['density'] || 1, + filter = grid['filter'] || false, + values = grid['values'] || false, + stepped = grid['stepped'] || false; + + return this.each(function(){ + + var info = this.getInfo(), + group = getGroup( info[0], mode, values, stepped ), + spread = generateSpread( info[0], density, mode, group ); + + if ( mode === 'range' ) { + console.log(spread, Object.keys(spread).length); + } + + return $(this).append(addMarking( + info[1], + info[2], + info[0].direction, + spread, + filter + )); + }); + }; + +}()); diff --git a/tests/02.shared-link.js b/tests/02.shared-link.js deleted file mode 100644 index 9a4f3201..00000000 --- a/tests/02.shared-link.js +++ /dev/null @@ -1,42 +0,0 @@ - - test( "Shared link element", function(){ - - Q.html('\ -
\ -
\ - \ - '); - - var span = $('#slider-span'); - - $(".slider").noUiSlider({ - start: [ 0, 10 ], - behaviour: 'drag', - connect: true, - range: { - 'min': [ -20 ], - 'max': [ 20 ] - } - }); - - $(".slider").Link('lower', { - target: span, - format: wNumb({ - mark: ',', - decimals: 1, - thousand: '.', - encoder: function( decoded ){ - return decoded * 10000; - }, - decoder: function( encoded ){ - return encoded / 10000; - } - }) - }); - - $("#sliderb").val( 1 ); - - deepEqual( $("#slidera").val(), ['0.00', '10.00'] ); - equal( span.html(), '10.000,0' ); - - }); diff --git a/tests/05.set.js b/tests/05.set.js deleted file mode 100644 index a1fc1d18..00000000 --- a/tests/05.set.js +++ /dev/null @@ -1,26 +0,0 @@ - - test( "Setting values", function(){ - - Q.html('\ -
\ - '); - - var slider = $('.slider'); - - slider.noUiSlider({ - start: [ 50, 100 ], - connect: true, - range: { - 'min': 30, - 'max': 980 - }, - format: wNumb({ - decimals: 2 - }) - }); - - slider.val( [ 150, 600 ] ); - - deepEqual( slider.val(), ['150.00', '600.00'], 'Slider correctly overstepped limits.' ); - - }); diff --git a/tests/07.rtl-set.js b/tests/07.rtl-set.js deleted file mode 100644 index e89da42e..00000000 --- a/tests/07.rtl-set.js +++ /dev/null @@ -1,30 +0,0 @@ - - test( "RTL slider multiple value set.", function(){ - - Q.html('\ -
\ -
\ - 0\ -
\ - '); - - var slider = $("#slider").noUiSlider({ - range: {min: 0.201, max: 1}, - step: 0.01, - start: [0.401], - direction: "rtl", - orientation: "vertical" - }); - - slider.Link('lower', { - target: $("#min") - }); - - equal(slider.val(), 0.4); - - slider.val(0.201, true); - equal(slider.val(), 0.2); - - slider.val(0.201, true); - equal(slider.val(), 0.2); - }); diff --git a/tests/08.update.js b/tests/08.update.js deleted file mode 100644 index 1f6615fe..00000000 --- a/tests/08.update.js +++ /dev/null @@ -1,67 +0,0 @@ - - test( "Testing update method", function(){ - - Q.html('\ -
\ - \ - '); - - var slider = $('.slider'), - input = $('.input'); - - slider.noUiSlider({ - range: { - 'min': 30, - 'max': 980 - }, - start: [-3000, 1000], - connect: true, - format: wNumb({ - encoder: function( value ){ - console.log('en', value, 3 * Number(value)); - return 3 * Number(value); - }, - decoder: function( value ){ - console.log('de', value); - return Number(value) / 3; - }, - mark: '|', - decimals: 4 - }) - }); - - slider.Link('lower', { - target: input - }); - - slider.Link('upper', { - target: "newInput" - }); - - deepEqual( slider.val(), ['90|0000', '1000|0000'], 'Slider handles encoder values.' ); - equal( Q.find('input[name="newInput"]').val(), '1000|0000', 'Input has proper value.'); - - equal( Q.find('input').length, 2 ); - - slider.noUiSlider({ - range: { - 'min': -50, - 'max': 6000 - }, - start: [ -40, 1500 ], - format: wNumb({ - mark: '.', - decimals: 2 - }) - }, true); - - equal( Q.find('input').length, 2, 'Hidden input is still available.' ); - - equal( Q.find('input[name="newInput"]').length, 1 ); - - // As of noUiSlider 7, the input method doesn't remove Links. - //equal( Q.find('input.input').val(), '90|0000', "Input didn't update." ); - - deepEqual( slider.val(), ['-40.00', '1500.00'], 'Slider has new settings.' ); - - }); diff --git a/tests/09.links.js b/tests/09.links.js deleted file mode 100644 index fa8fff78..00000000 --- a/tests/09.links.js +++ /dev/null @@ -1,94 +0,0 @@ - - test( "Testing Links", function(){ - - Q.html('\ -
\ -
\ -
\ -
\ - '); - - $.fn.cake = function(val){ - this.html(val); - }; - - var slider = $('.slider'), - box = $('.box'), - item = $('.item'), - thing = $('.thing'); - - var ltfCount = 0, lmfCount = 0, Expect = ['-100.00', '-90.00']; - - function linkTargetFunction( value, handle, sliderInstance ){ - - equal(value, Expect[ltfCount], "Value as expected, change ("+(ltfCount++)+"/2)."); - - ok(handle.hasClass("noUi-handle"), "Handle is really a handle."); - ok(handle.hasClass("noUi-handle-lower"), "Handle is really the lower handle."); - ok(sliderInstance.hasClass("noUi-target"), "Slider is really a slider."); - ok(sliderInstance[0] === this[0], "Slider is scope of call and $."); - } - - function linkMethodFunction( value, handle, sliderInstance ){ - - equal(value, Expect[lmfCount], "Value as expected, change ("+(lmfCount++)+"/2)."); - - ok(handle.hasClass("noUi-handle"), "Handle is really a handle."); - ok(sliderInstance.hasClass("noUi-target"), "Slider is really a slider."); - ok(box[0] === this[0], "Element is scope of call and $."); - ok(slider[0] === sliderInstance[0], "Slider argument is slider."); - } - - slider.noUiSlider({ - range: { - 'min': -100, - 'max': 9000 - }, - start: [-500, 10000], - connect: true, - format: wNumb({ - decimals: 2 - }) - }); - - slider.Link('lower', { - target: linkTargetFunction - }, { - target: box, - method: linkMethodFunction - }, { - target: "hiddenInputField", - format: wNumb({ - decimals: 1, - negative: '', - prefix: '++', - negativeBefore: '=' - }) - }) - - slider.Link('upper', { - target: item, - method: "cake" - }, { - target: thing, - method: "html" - }); - - // See if values bounced properly. - deepEqual(slider.val(), ['-100.00', '9000.00'], 'Slider has proper value'); - - // Check hidden input updates. - ok($('input[name="hiddenInputField"]').is('input'), "Input field was generated."); - equal($('input[name="hiddenInputField"]').val(), '=++100.0', "Value is formatted as expected."); - - equal(item.html(), '9000.00', 'Setting by custom method works.'); - - slider.val([-90, 8051]); - - deepEqual(slider.val(), ['-90.00', '8051.00']); - - equal(thing.html(), '8051.00', 'Setting by existing method works.'); - - equal(ltfCount, 2, 'Pre-bound link updated 4 times.'); - equal(lmfCount, 2, 'Post-bound Link updated 2 times.'); - }); diff --git a/tests/10.rtl.js b/tests/10.rtl.js deleted file mode 100644 index 12f6b1d1..00000000 --- a/tests/10.rtl.js +++ /dev/null @@ -1,47 +0,0 @@ - - test( "RTL value method", function(){ - - Q.html('\ -
\ -
\ - 0\ - 0\ -
\ - '); - - var slider = $("#slider").noUiSlider({ - range: { - 'min': 0, - 'max': 50 - }, - start: [10, 40], - behaviour: 'tap', - connect: true, - direction : 'rtl', - format: { - to: function( a ){ - return a.toFixed(2); - }, - from: Number - } - }); - - slider.Link('lower', { - target: $("#min") - }); - slider.Link('upper', { - target: $("#max") - }); - - deepEqual(slider.val(), ["10.00", "40.00"], "Proper start handling in RTL."); - - slider.val([20,30]); - deepEqual(slider.val(), ["20.00", "30.00"]); - - slider.val([40,45]); - deepEqual(slider.val(), ["40.00", "45.00"], "RTL slider overstepped properly."); - - slider.val([30,35]); - deepEqual(slider.val(), ["30.00", "35.00"], "RTL slider understepped properly."); - - }); diff --git a/tests/12.update-link.js b/tests/12.update-link.js deleted file mode 100644 index d7021071..00000000 --- a/tests/12.update-link.js +++ /dev/null @@ -1,58 +0,0 @@ - - test( "Testing update method", function(){ - - Q.html('\ -
\ - \ - \ - '); - - var slider = $("#slider"), - input1 = $("#input1"), - input2 = $("#input2"); - - slider.noUiSlider({ - range: { - min: 30, - max: 120 - }, - start: [ 50, 100 ], - format: wNumb({ - prefix: '€', - mark: '*', - decimals: 2 - }) - }); - - slider.Link('lower', { - target: $('#input1'), - format: wNumb({ - prefix: '$', - mark: ',', - decimals: 3, - edit: function( value ) { - return 'Hi! ' + value; - }, - undo: function( value ) { - return value.slice(4); - } - }) - }); - - slider.Link('upper', { - target: $('#input2') - }); - - deepEqual(slider.val(), ["€50*00", "€100*00"]); - equal(input1.val(), "Hi! $50,000"); - equal(input2.val(), "€100*00"); - - slider.val([32, 118]); - deepEqual(slider.val(), ["€32*00", "€118*00"]); - - input1.val("Hi! $60,000").change(); - deepEqual(slider.val(), ["€60*00", "€118*00"]); - - slider.noUiSlider({ range: { min: 20, max: 130 } }, true); - deepEqual(slider.val(), ["€60*00", "€118*00"]); - }); diff --git a/tests/15.step-limit.js b/tests/15.step-limit.js deleted file mode 100644 index 126dfc0d..00000000 --- a/tests/15.step-limit.js +++ /dev/null @@ -1,29 +0,0 @@ - - test( "Testing handling of odd-numbered steps", function(){ - - Q.html('\ -
\ - '); - - var slider = $('.slider'); - - slider.noUiSlider({ - range: { min: 3, max: 106 }, - start: [ 20, 50 ], - step: 10, - format: wNumb({ - decimals: 2 - }) - }); - - deepEqual( slider.val(), ['23.00', '53.00'] ); - - slider.val([50, 106]); - deepEqual( slider.val(), ['53.00', '106.00'], 'Slider reached edge outside from step.' ); - - slider.val([71, 105]); - deepEqual( slider.val(), ['73.00', '103.00'], 'Slider steps back into stepping with lower points as origin.' ); - - slider.val([71, 101]); - deepEqual( slider.val(), ['73.00', '103.00'] ); - }); diff --git a/tests/16.legend.js b/tests/16.legend.js deleted file mode 100644 index a57f0c6d..00000000 --- a/tests/16.legend.js +++ /dev/null @@ -1,118 +0,0 @@ - - test( "Generation of legends", function(){ - - Q.html('\ -
\ -
\ - '); - - function filter500( value, type ){ - return value%1000 ? 2 : 1; - } - - var slider2 = $("#slider2").noUiSlider({ - range: { - 'min': [ 0 ], - '10%': [ 500, 500 ], - '50%': [ 4000, 1000 ], - 'max': [ 10000 ] - }, - start: 0 - }) - - // RANGE - - slider2.noUiSlider('legend', { - mode: 'range', - density: 3 - }); - - ok( slider2.next().hasClass('noUi-legend'), 'Legend was created' ); - - var markers = slider2.next().children('.noUi-marker').length; - ok( markers >= 32 && markers <= 34, 'Density of 1/3 was applied' ); - - // STEPS - - slider2.noUiSlider('legend', { - mode: 'steps', - density: 2, - filter: filter500 - }); - - var markers = slider2.next().children('.noUi-marker').length; - ok( markers >= 49 && markers <= 51, 'Density of 1/2 was applied' ); - - // POSITIONS - - slider2.noUiSlider('legend', { - mode: 'positions', - values: [0,25,50,75,100] - }); - - equal( slider2.next().children('.noUi-marker-large').length, 5, 'Large markers added for all values' ); - equal( slider2.next().children('.noUi-value').length, 5 ); - - var pos = []; - slider2.next().children('.noUi-value').each(function(){ - pos.push(parseInt(this.style.left)); - }); - - deepEqual(pos, [0,25,50,75,100], 'Values placed on proper positions'); - - // POSITIONS (STEPPED) - - slider2.noUiSlider('legend', { - mode: 'positions', - values: [0,25,50,75,100], - stepped: true - }); - - // todo - - // COUNT - - slider2.noUiSlider('legend', { - mode: 'count', - values: 8 - }); - - equal( slider2.next().children('.noUi-value').length, 8, 'Placed requested number of values' ); - - var pos2 = []; - slider2.next().children('.noUi-value').each(function(){ - pos2.push(parseInt(this.style.left)); - }); - - deepEqual(pos2, [0, Math.floor((100/7)*1), Math.floor((100/7)*2), Math.floor((100/7)*3), Math.floor((100/7)*4), Math.floor((100/7)*5), Math.floor((100/7)*6), 100], 'Values spread evenly'); - - // COUNT (STEPPED) - - slider2.noUiSlider('legend', { - mode: 'count', - values: 8, - stepped: true - }); - - // todo - - // VALUES - - slider2.noUiSlider('legend', { - mode: 'values', - values: [50, 552, 750, 940, 5000, 6080, 9000] - }); - - equal( slider2.next().children('.noUi-value').length, 7, 'Placed requested number of values' ); - - // VALUES (STEPPED) - - slider2.noUiSlider('legend', { - mode: 'values', - values: [50, 552, 750, 940, 5000, 6080, 9000], - stepped: true - }); - - equal( slider2.next().children('.noUi-value').length, 6, 'Removed duplicate in step' ); - - }); diff --git a/tests/addon_pips.js b/tests/addon_pips.js new file mode 100644 index 00000000..dac94735 --- /dev/null +++ b/tests/addon_pips.js @@ -0,0 +1,164 @@ + + function filter500( value, type ){ + return value%1000 ? 2 : 1; + } + + function test_slider(){ + + Q.html('\ +
\ + '); + + return Q.find("#slider").noUiSlider({ + range: { + 'min': [ 0 ], + '10%': [ 500, 500 ], + '50%': [ 4000, 1000 ], + 'max': [ 10000 ] + }, + start: 0 + }) + } + + // RANGE + + test( "Range", function(){ + + var slider = test_slider(); + + slider.noUiSlider_pips({ + mode: 'range', + density: 3 + }); + + ok( Q.find('.noUi-pips'), 'Pips where created' ); + + var markers = Q.find('.noUi-marker').length; + ok( markers >= 32 && markers <= 34, 'Density of 1/3 was applied' ); + + }); + + test( "Steps", function(){ + + var slider = test_slider(); + + // STEPS + + slider.noUiSlider_pips({ + mode: 'steps', + density: 2, + filter: filter500 + }); + + var markers = Q.find('.noUi-marker').length; + ok( markers >= 49 && markers <= 51, 'Density of 1/2 was applied' ); + + }); + + test( "Positions", function(){ + + var slider = test_slider(); + + // POSITIONS + + slider.noUiSlider_pips({ + mode: 'positions', + values: [0,25,50,75,100] + }); + + equal( Q.find('.noUi-marker-large').length, 5, 'Large markers added for all values' ); + equal( Q.find('.noUi-value').length, 5 ); + + var pos = []; + Q.find('.noUi-value').each(function(){ + pos.push(parseInt(this.style.left)); + }); + + deepEqual(pos, [0,25,50,75,100], 'Values placed on proper positions'); + + }); + + test( "Positions, stepped", function(){ + + expect(0); // TODO + + var slider = test_slider(); + + // POSITIONS (STEPPED) + + slider.noUiSlider_pips({ + mode: 'positions', + values: [0,25,50,75,100], + stepped: true + }); + + }); + + test( "Count", function(){ + + var slider = test_slider(); + + // COUNT + + slider.noUiSlider_pips({ + mode: 'count', + values: 8 + }); + + equal( Q.find('.noUi-value').length, 8, 'Placed requested number of values' ); + + var pos2 = []; + Q.find('.noUi-value').each(function(){ + pos2.push(parseInt(this.style.left)); + }); + + deepEqual(pos2, [0, Math.floor((100/7)*1), Math.floor((100/7)*2), Math.floor((100/7)*3), Math.floor((100/7)*4), Math.floor((100/7)*5), Math.floor((100/7)*6), 100], 'Values spread evenly'); + + }); + + test( "Count, stepped", function(){ + + expect(0); // TODO + + var slider = test_slider(); + + // COUNT (STEPPED) + + slider.noUiSlider_pips({ + mode: 'count', + values: 8, + stepped: true + }); + + }); + + // VALUES + + test( "Values", function(){ + + var slider = test_slider(); + + slider.noUiSlider_pips({ + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000] + }); + + equal( Q.find('.noUi-value').length, 7, 'Placed requested number of values' ); + + }); + + // VALUES (STEPPED) + + test( "Values, stepped", function(){ + + var slider = test_slider(); + + slider.noUiSlider_pips({ + mode: 'values', + values: [50, 552, 750, 940, 5000, 6080, 9000], + stepped: true + }); + + equal( Q.find('.noUi-value').length, 6, 'Removed duplicate in step' ); + + }); diff --git a/tests/13.default-val.js b/tests/general_val.js similarity index 85% rename from tests/13.default-val.js rename to tests/general_val.js index 04f2bf5f..7e456382 100644 --- a/tests/13.default-val.js +++ b/tests/general_val.js @@ -1,5 +1,5 @@ - test( "Value setting/getting on normal inputs", function(){ + test( "Make sure .val() behaviour didn't change.", function(){ Q.html('\ \ @@ -14,10 +14,7 @@ var slider = $("#simpleslider").noUiSlider({ range: { min: 20, max: 120 }, - start: 30, - format: wNumb({ - decimals: 2 - }) + start: 30 }); one.val(35); diff --git a/tests/goof.html b/tests/goof.html deleted file mode 100644 index a74bbaf7..00000000 --- a/tests/goof.html +++ /dev/null @@ -1,124 +0,0 @@ - - - - Testing - - - - - - - - - - - - - - -
-
- - - diff --git a/tests/link_basic.js b/tests/link_basic.js new file mode 100644 index 00000000..5c7b028c --- /dev/null +++ b/tests/link_basic.js @@ -0,0 +1,90 @@ + + test( "Link implementation", function(){ + + $.fn.customMethod = function(val){ + this.html(val); + }; + + Q.html('\ +
\ + \ +
\ +
\ + '); + + var slider = $('.slider'), input = $("#input-field"), div1 = $('#div-field1'), div2 = $('#div-field2'); + + throws(function(){ + + slider.Link().to('throws'); + + }, 'Can\'t link before slider.'); + + slider.noUiSlider({ + start: [ 0, 20 ], + connect: true, + step: 10, + range: { + 'min': -30, + 'max': 30 + }, + format: TEST_ROUND_FORMAT + }); + + deepEqual(slider.val(), ['0', '20']); + + slider.Link().to(input); + + equal(input.val(), '0', 'Slider value is written to input'); + + input.val(60).change(); + equal(input.val(), '20', 'Value was parsed and limited to handle max.'); + deepEqual(slider.val(), ['20', '20']); + + slider.val([null, 30]); + deepEqual(slider.val(), ['20', '30']); + + input.val(60).change(); + equal(input.val(), '30', 'Value was parsed and limited to slider max.'); + + input.val(23).change(); + input.val('Run, Forrest!').change(); + equal(input.val(), '20', 'Slider ignored invalid value and reset input.'); + + slider.Link(false); + slider.val([-20, 10]) + + equal(input.val(), '20', 'Input ignored the slider'); + + input.val(-20).change(); + deepEqual(slider.val(), ['-20', '10'], 'Input didn\t affect slider.'); + + slider.Link('upper').to(div1, 'customMethod'); + + equal(div1.html(), '10', 'HTML was set'); + + slider.Link('upper').to(div2, function( value, handle, sliderInstance ){ + + equal(value, 10, "Value as expected."); + + ok(handle.hasClass("noUi-handle"), "Handle is a handle."); + ok(sliderInstance.hasClass("noUi-target"), "Slider is a slider."); + ok(div2[0] === this[0], "Element is scope of call and $."); + ok(slider[0] === sliderInstance[0], "Slider argument is slider."); + + $(this).Link(false); + }); + + equal(div2.html(), '', 'Element wasn\t altered.'); + + slider.Link().to(function( value, handle, sliderInstance ){ + + equal(value, -20, "Value as expected."); + + ok(handle.hasClass("noUi-handle-lower"), "Handle is a handle."); + ok(sliderInstance.hasClass("noUi-target"), "Slider is a slider."); + ok(sliderInstance[0] === this[0], "Slider is scope of call and $."); + + $(this).Link(false); + }); + }); diff --git a/tests/01.inputs.js b/tests/link_multiple.js similarity index 79% rename from tests/01.inputs.js rename to tests/link_multiple.js index 28503ad5..8643a9c3 100644 --- a/tests/01.inputs.js +++ b/tests/link_multiple.js @@ -9,7 +9,7 @@ \ '); - Q.find(".slider").noUiSlider({ + Q.find('.slider').noUiSlider({ start: [ 20, 80 ], range: { 'min': [ 0 ], @@ -17,13 +17,8 @@ } }); - $('.slider').Link('lower', { - target: 'lower' - }); - - $('.slider').Link('upper', { - target: 'upper' - }); + Q.find('.slider').Link('lower').to('input-lower'); + Q.find('.slider').Link('upper').to('input-upper'); $("#slidera").val([50, 61]); $("#sliderb").val([51, 62]); @@ -40,4 +35,6 @@ equal( $(inputs[4]).val(), '52.00' ); equal( $(inputs[5]).val(), '63.00' ); + equal(Q.find('[name="input-lower"]').length, 3); + }); diff --git a/tests/04.select.js b/tests/link_select.js similarity index 84% rename from tests/04.select.js rename to tests/link_select.js index a2eb2de7..9ec5fd67 100644 --- a/tests/04.select.js +++ b/tests/link_select.js @@ -23,12 +23,7 @@ } }); - $('.slider').Link({ - target: select, - format: wNumb({ - decimals: 0 - }) - }); + $('.slider').Link('lower').to(select, null, TEST_ROUND_FORMAT); select.val( 40 ).change(); diff --git a/tests/link_update.js b/tests/link_update.js new file mode 100644 index 00000000..c54b750c --- /dev/null +++ b/tests/link_update.js @@ -0,0 +1,68 @@ + + test( "Test re-append of inline elements", function(){ + + Q.html('\ +
\ +
\ + \ +
\ + '); + + var slider = Q.find('.slider'), input = Q.find('#input-element'); + + slider.noUiSlider({ + start: [50, 70], + direction: 'rtl', + range: { + 'min': [ 0 ], + 'max': [ 100 ] + } + }); + + slider.Link().to('input-lower'); + slider.Link('upper').to(input); + + equal(input.val(), '70.00'); + equal(Q.find('[name="input-lower"]').length, 1); + equal(Q.find('[name="input-lower"]').val(), '50.00'); + + slider.Link(false); + equal(Q.find('[name="input-lower"]').length, 0, 'input was removed.'); + + slider[0].destroy(); + + throws(function(){ + slider.Link().to('input-throws'); + }, 'No slider to link to.'); + + slider.noUiSlider({ + start: [30, 40], + range: { + 'min': [ 0 ], + 'max': [ 150 ] + } + }); + + equal(input.val(), '70.00', 'Input didn\t change.'); + input.val(30).change(); + + deepEqual(slider.val(), ['30.00', '40.00'], 'Slider didn\'t change.'); + + slider.Link('lower').to('input-new'); + slider.Link('upper').to('-inline-'); + + equal(Q.find('[name="input-new"]').length, 1); + equal(Q.find('.noUi-handle > div').length, 1, 'Inline element was appended'); + equal(Q.find('.noUi-handle > div').html(), '40.00'); + + slider.noUiSlider({ + start: [40, 90], + direction: 'rtl' + }, true); + + equal(Q.find('.noUi-handle > div').html(), '90.00', 'Element is still there, with new value.'); + + equal(Q.find('[name="input-new"]').length, 1, 'Input was re-appended'); + equal(Q.find('[name="input-new"]').val(), '40.00', 'Value was set.'); + + }); diff --git a/tests/run.html b/tests/run.html deleted file mode 100644 index 6a9a5bed..00000000 --- a/tests/run.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - Testing - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - diff --git a/tests/slider.html b/tests/slider.html new file mode 100644 index 00000000..22327f89 --- /dev/null +++ b/tests/slider.html @@ -0,0 +1,72 @@ + + + + Testing + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/06.events.js b/tests/slider_events.js similarity index 73% rename from tests/06.events.js rename to tests/slider_events.js index 4a18495c..74e1e219 100644 --- a/tests/06.events.js +++ b/tests/slider_events.js @@ -13,11 +13,7 @@ range: { 'min': [ 50 ], 'max': [ 90 ] - }, - format: wNumb({ - decimals: 0, - postfix: ',-' - }) + } }); var w = slider.width(); @@ -25,7 +21,7 @@ var handles = slider.find('.noUi-handle'); slider.on("slide", function( event, values ){ - deepEqual( values, ['50,-', '70,-'], "Slide event has proper parameters." ); + deepEqual( values, ['50.00', '70.00'], "Slide event has proper parameters." ); }); handles.first().trigger($.Event('mousedown', { @@ -40,7 +36,7 @@ $(document).trigger('mouseup'); - deepEqual( slider.val(), ['50,-', '50,-'], "Slider didn't cross." ); + deepEqual( slider.val(), ['50.00', '50.00'], "Slider didn't cross." ); handles.last().trigger($.Event('mousedown', { clientX: 10, @@ -54,6 +50,6 @@ $(document).trigger('mouseup'); - deepEqual( slider.val(), ['50,-', '70,-'], "Slider moved properly." ); + deepEqual( slider.val(), ['50.00', '70.00'], "Slider moved properly." ); }); diff --git a/tests/14.non-linear-rtl.js b/tests/slider_non-linear-rtl.js similarity index 92% rename from tests/14.non-linear-rtl.js rename to tests/slider_non-linear-rtl.js index 9041c9b2..a0dc2498 100644 --- a/tests/14.non-linear-rtl.js +++ b/tests/slider_non-linear-rtl.js @@ -18,9 +18,7 @@ }, start: 44, direction : 'rtl', - format: wNumb({ - decimals: 0 - }) + format: TEST_ROUND_FORMAT }); sliderLTR.noUiSlider({ @@ -31,9 +29,7 @@ 'max': 50 }, start: 44, - format: wNumb({ - decimals: 0 - }) + format: TEST_ROUND_FORMAT }); equal(sliderRTL.val(), '40', 'Start stepping on rtl works'); diff --git a/tests/slider_rtl.js b/tests/slider_rtl.js new file mode 100644 index 00000000..17839d34 --- /dev/null +++ b/tests/slider_rtl.js @@ -0,0 +1,54 @@ + + test( "RTL slider multiple value set.", function(){ + + Q.html('\ +
\ +
\ + '); + + var slider = $("#slider").noUiSlider({ + range: {min: 0.201, max: 1}, + step: 0.01, + start: 0.401, + direction: "rtl", + orientation: "vertical", + format: { + to: function(x){ + return x.toFixed(1); + }, + from: Number + } + }); + + equal(slider.val(), 0.4); + + slider.val(0.201, true); + equal(slider.val(), 0.2); + + slider.val(0.201, true); + equal(slider.val(), 0.2); + + + var slider2 = $("#slider2").noUiSlider({ + range: { + 'min': 0, + 'max': 50 + }, + start: [10, 40], + behaviour: 'tap', + connect: true, + direction : 'rtl' + }); + + deepEqual(slider2.val(), ["10.00", "40.00"], "Proper start handling in RTL."); + + slider2.val([20,30]); + deepEqual(slider2.val(), ["20.00", "30.00"]); + + slider2.val([40,45]); + deepEqual(slider2.val(), ["40.00", "45.00"], "RTL slider overstepped properly."); + + slider2.val([30,35]); + deepEqual(slider2.val(), ["30.00", "35.00"], "RTL slider understepped properly."); + + }); diff --git a/tests/11.val.js b/tests/slider_setting-getting.js similarity index 87% rename from tests/11.val.js rename to tests/slider_setting-getting.js index 53406bee..f040cf1a 100644 --- a/tests/11.val.js +++ b/tests/slider_setting-getting.js @@ -1,5 +1,5 @@ - test( "Testing val setting", function(){ + test( "Value setting/getting", function(){ Q.html('\
\ @@ -10,9 +10,12 @@ start: [ 0, 10 ], behaviour: 'drag', connect: true, - format: wNumb({ - 'decimals': 1 - }) + format: { + to: function(x){ + return x.toFixed(1); + }, + from: Number + } }); deepEqual(slider.val(), ["0.0", "10.0"]); diff --git a/tests/slider_step.js b/tests/slider_step.js new file mode 100644 index 00000000..6d860405 --- /dev/null +++ b/tests/slider_step.js @@ -0,0 +1,27 @@ + + test( "Testing handling of odd-numbered steps", function(){ + + Q.html('\ +
\ + '); + + var slider = $('.slider'); + + slider.noUiSlider({ + range: { min: 3, max: 106 }, + start: [ 20, 50 ], + step: 10, + format: TEST_ROUND_FORMAT + }); + + deepEqual( slider.val(), ['23', '53'] ); + + slider.val([50, 106]); + deepEqual( slider.val(), ['53', '106'], 'Slider reached edge outside from step.' ); + + slider.val([71, 105]); + deepEqual( slider.val(), ['73', '103'], 'Slider steps back into stepping with lower points as origin.' ); + + slider.val([71, 101]); + deepEqual( slider.val(), ['73', '103'] ); + }); diff --git a/tests/slider_update.js b/tests/slider_update.js new file mode 100644 index 00000000..67f78ae4 --- /dev/null +++ b/tests/slider_update.js @@ -0,0 +1,63 @@ + + test( "Testing update method", function(){ + + Q.html('\ +
\ + \ + '); + + var slider = $('.slider'); + + slider.noUiSlider({ + range: { min: 20, max: 140 }, + start: 50, + format: TEST_ROUND_FORMAT + }); + + deepEqual(slider.val(), '50'); + + slider[0].destroy(); + + ok(slider.is(':empty'), 'Slider was cleared'); + + equal ( slider.val(), '', 'Slider has no value' ); + + slider.noUiSlider({ + range: { min: 30, max: 70 }, + start: [ 30, 60 ], + format: TEST_ROUND_FORMAT + }); + + deepEqual(slider.val(), ['30', '60']); + + slider.val(70); + deepEqual(slider.val(), ['60', '60']); + + slider.val(40); + deepEqual(slider.val(), ['40', '60']); + + equal ( slider.find('.noUi-connect').length, 0, 'Slider uses no connection' ); + + slider.noUiSlider({ + connect: true + }, true); + + equal ( slider.find('.noUi-connect').length, 1, 'Slider now connects' ); + + deepEqual(slider.val(), ['40', '60'], 'Value was unchanged'); + + slider.val([30,50]); + deepEqual(slider.val(), ['30', '50'], 'Can still set slider'); + + slider[0].destroy(); + + ok ( !slider[0].destroy, 'destroy method removed itself'); + + slider.noUiSlider({ + start: 30, + range: { min: 80, max: 1000 } + }, true); + + equal(slider.val(), '80.00', 'Slider was build, ignoring the rebuild flag.'); + + }); diff --git a/tests/slider_values.js b/tests/slider_values.js new file mode 100644 index 00000000..1d9d019c --- /dev/null +++ b/tests/slider_values.js @@ -0,0 +1,26 @@ + + test( "Values", function(){ + + Q.html('\ +
\ + '); + + var slider = $('.slider'); + + slider.noUiSlider({ + start: [ 50, 100 ], + connect: true, + range: { + 'min': 30, + 'max': 986 + }, + format: TEST_ROUND_FORMAT + }); + + deepEqual( slider.val(), ['50', '100'], 'Values where set' ); + + slider.val( [ 150, 600 ] ); + + deepEqual( slider.val(), ['150', '600'], 'Slider correctly overstepped limits.' ); + + }); diff --git a/tests/try.html b/tests/try.html deleted file mode 100644 index 61b87602..00000000 --- a/tests/try.html +++ /dev/null @@ -1,109 +0,0 @@ - - - - - Trying - - - - - - - - - - - - - - - - - -
- - -
- - -
- - -
- - - - - diff --git a/tests/03.format.js b/tests/wnumb_basic.js similarity index 52% rename from tests/03.format.js rename to tests/wnumb_basic.js index be6cd383..4acfa0b3 100644 --- a/tests/03.format.js +++ b/tests/wnumb_basic.js @@ -20,47 +20,34 @@ decimals: 5 }) }); - - var one = { - target: '-inline-
', - method: function( value, handle, slider ){ - ok( $("#slider")[0] === slider[0] ); - ok( handle.hasClass('noUi-handle') ); + + $("#slider").Link('lower').to('-inline-
', function( value, handle, slider ){ + ok( $("#slider")[0] === slider[0] ); + ok( handle.hasClass('noUi-handle') ); + }, wNumb({ + decimals: 6, + prefix: '#', + negativeBefore: '!', + negative: '' + })); + + $("#slider").Link('lower').to($("#inputChange"), null, wNumb({ + mark: '.', + thousand: ',', + prefix: '$', + postfix: ' p.p.', + decimals: 3, + negativeBefore: '-', + negative: '', + encoder: function( a ){ + return a * 1E7; }, - format: wNumb({ - decimals: 6, - prefix: '#', - negativeBefore: '!', - negative: '' - }) - }; - - var two = { - target: $("#inputChange"), - format: wNumb({ - mark: '.', - thousand: ',', - prefix: '$', - postfix: ' p.p.', - decimals: 3, - negativeBefore: '-', - negative: '', - encoder: function( a ){ - return a * 1E7; - }, - decoder: function( a ){ - return a / 1E7; - } - }) - }; - - var three = { - target: $("#inputVal"), - method: 'val' - }; + decoder: function( a ){ + return a / 1E7; + } + })); - $("#slider").Link('lower', one, two); - $("#slider").Link('upper', three); + $("#slider").Link('upper').to($("#inputVal"), 'val'); $("#inputChange").val( "15000" ).trigger('change'); From dc7bec9b653fc7bc55dea51b713154e0b201fead Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 3 Aug 2014 15:50:09 +0200 Subject: [PATCH 18/28] Buildable modules --- .gitmodules | 6 ++++ compile.bat | 29 +++++++++++++++++++ libLink | 1 + jquery.nouislider.js => src/module.base.js | 29 ++++++------------- options.js => src/module.options.js | 12 ++++---- range.js => src/module.range.js | 8 +++-- .../optional.pips.js | 6 ++-- wnumb | 1 + 8 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 .gitmodules create mode 100644 compile.bat create mode 160000 libLink rename jquery.nouislider.js => src/module.base.js (98%) rename options.js => src/module.options.js (96%) rename range.js => src/module.range.js (98%) rename jquery.nouislider.pips.js => src/optional.pips.js (98%) create mode 160000 wnumb diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..c95f97b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "libLink"] + path = libLink + url = https://github.com/leongersen/libLink +[submodule "wnumb"] + path = wnumb + url = https://github.com/leongersen/wnumb diff --git a/compile.bat b/compile.bat new file mode 100644 index 00000000..094a9bb4 --- /dev/null +++ b/compile.bat @@ -0,0 +1,29 @@ +@echo off + +:: Set all paths as variables +set "wnumb=wnumb\wnumb.js" +set "link=libLink\jquery.liblink.js" + +set "jquery=compiler\jquery-1.9.js" +set "zepto=compiler\zepto.js" + +set "range=src\module.range.js" +set "options=src\module.options.js" +set "source=src\module.base.js" +set "pips=src\optional.pips.js" + +set "result=jquery.nouislider.min.js" + +:: Remove the existing file so we can be sure a new one is generated. +if exist %result% ( + del %result% +) + +echo "Removed %result%, ready." + +PAUSE + +java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %result% + +echo "Done." +PAUSE diff --git a/libLink b/libLink new file mode 160000 index 00000000..1dce4e66 --- /dev/null +++ b/libLink @@ -0,0 +1 @@ +Subproject commit 1dce4e663faa621d3434d915aec77a5dd1f67435 diff --git a/jquery.nouislider.js b/src/module.base.js similarity index 98% rename from jquery.nouislider.js rename to src/module.base.js index 66dad6e4..75501a4f 100644 --- a/jquery.nouislider.js +++ b/src/module.base.js @@ -23,11 +23,11 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ // Determine the events to bind. IE11 implements pointerEvents without // a prefix, which breaks compatibility with the IE10 implementation. /** @const */ - actions = window.navigator['pointerEnabled'] ? { + actions = window.navigator.pointerEnabled ? { start: 'pointerdown', move: 'pointermove', end: 'pointerup' - } : window.navigator['msPointerEnabled'] ? { + } : window.navigator.msPointerEnabled ? { start: 'MSPointerDown', move: 'MSPointerMove', end: 'MSPointerUp' @@ -346,18 +346,13 @@ function closure ( target, options, originalOptions ){ this.linkAPI[flag].reconfirm(flag); } } - }; + } - /** @expose */ target.LinkUpdate = linkUpdate; - /** @expose */ target.LinkConfirm = linkConfirm; - /** @expose */ target.LinkDefaultFormatter = options.format; - /** @expose */ target.LinkDefaultFlag = 'lower'; - /** @expose */ target.reappend = reAppendLink; @@ -718,7 +713,7 @@ function closure ( target, options, originalOptions ){ // Return the original options from the closure. return originalOptions; - }; + } // Get the current step size for the slider. function getCurrentStep ( ) { @@ -737,7 +732,7 @@ function closure ( target, options, originalOptions ){ // Return values in the proper order. return inSliderOrder( retour ); - }; + } @@ -761,23 +756,18 @@ function closure ( target, options, originalOptions ){ // Methods - /** @expose */ target.vSet = valueSet; - /** @expose */ target.vGet = valueGet; - /** @expose */ target.destroy = destroyTarget; - /** @expose */ target.getCurrentStep = getCurrentStep; - target.getInfo = function(){ return [ $Spectrum, options.style, - options.ort, + options.ort ]; }; - + // Use the public value method to set the start values. $Target.val( options.start ); } @@ -794,7 +784,7 @@ function closure ( target, options, originalOptions ){ } // Test the options once, not for every slider. - var options = test_noUiSliderOptions( originalOptions, this ); + var options = $.noUiSlider.testOptions( originalOptions, this ); // Loop all items, and provide a new closed-scope environment. return this.each(function(){ @@ -865,9 +855,8 @@ function closure ( target, options, originalOptions ){ }; // Extend jQuery/Zepto with the noUiSlider method. - /** @expose */ $.fn.noUiSlider = function ( options, rebuildFlag ) { return ( rebuildFlag ? rebuild : initialize ).call(this, options); }; -}( window['jQuery'] || window['Zepto'] )); +}( window.jQuery || window.Zepto )); diff --git a/options.js b/src/module.options.js similarity index 96% rename from options.js rename to src/module.options.js index 711834c2..0f7dee1f 100644 --- a/options.js +++ b/src/module.options.js @@ -11,7 +11,7 @@ or true when everything is OK. It can also modify the option object, to make sure all values can be correctly looped elsewhere. */ -(function(){ +(function( $ ){ // Wraps a variable as an array, if it isn't one yet. function asArray ( a ) { @@ -52,7 +52,7 @@ throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); } - parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); + parsed.spectrum = new $.noUiSlider.Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); } function testStart ( parsed, entry ) { @@ -187,8 +187,7 @@ throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); } - // Test all developer settings and parse to assumption-safe values. - window.test_noUiSliderOptions = function ( options ){ + function testOptions ( options ) { var parsed = { margin: 0, @@ -243,4 +242,7 @@ return parsed; } -}()); + // Test all developer settings and parse to assumption-safe values. + $.noUiSlider.testOptions = testOptions; + +}( window.jQuery || window.Zepto )); diff --git a/range.js b/src/module.range.js similarity index 98% rename from range.js rename to src/module.range.js index bd07eabb..cfcf002f 100644 --- a/range.js +++ b/src/module.range.js @@ -1,6 +1,6 @@ /*jslint browser: true, white: true */ -(function(){ +(function( $ ){ 'use strict'; @@ -282,6 +282,8 @@ return this.getStep(this.toStepping(value)); }; - window.Spectrum = Spectrum; + $.noUiSlider = { + Spectrum: Spectrum + }; -}()); +}( window.jQuery || window.Zepto )); diff --git a/jquery.nouislider.pips.js b/src/optional.pips.js similarity index 98% rename from jquery.nouislider.pips.js rename to src/optional.pips.js index eb5ac9fc..8af599c5 100644 --- a/jquery.nouislider.pips.js +++ b/src/optional.pips.js @@ -1,4 +1,4 @@ -(function(){ +(function( $ ){ // Removes duplicates from an array. function unique(array) { @@ -58,7 +58,7 @@ function generateSpread ( $Spectrum, density, mode, group ) { - var originalSpectrumDirection = $Spectrum.direction; + var originalSpectrumDirection = $Spectrum.direction, indexes = {}, firstInRange = $Spectrum.xVal[0], lastInRange = $Spectrum.xVal[$Spectrum.xVal.length-1], @@ -232,4 +232,4 @@ }); }; -}()); +}( window.jQuery || window.Zepto )); diff --git a/wnumb b/wnumb new file mode 160000 index 00000000..3a5e1bb5 --- /dev/null +++ b/wnumb @@ -0,0 +1 @@ +Subproject commit 3a5e1bb5d5993b8129e4f9f530c776156526d955 From 567600b67a4dd546f616af5ecb677c0c3a3608d2 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 8 Aug 2014 21:37:08 +0200 Subject: [PATCH 19/28] Compr. script, fix animation on init, event tests --- compile.bat | 17 ++++++--- src/module.base.js | 49 ++++++++++-------------- src/module.options.js | 14 +++++-- src/module.range.js | 3 +- src/optional.pips.js | 9 +++-- tests/slider.html | 34 ++++++++--------- tests/slider_events.js | 84 ++++++++++++++++++++++++++++++++++-------- 7 files changed, 134 insertions(+), 76 deletions(-) diff --git a/compile.bat b/compile.bat index 094a9bb4..915a610c 100644 --- a/compile.bat +++ b/compile.bat @@ -12,18 +12,25 @@ set "options=src\module.options.js" set "source=src\module.base.js" set "pips=src\optional.pips.js" -set "result=jquery.nouislider.min.js" +set "minresult=jquery.nouislider.min.js" +set "fullresult=jquery.nouislider.full.min.js" :: Remove the existing file so we can be sure a new one is generated. -if exist %result% ( - del %result% +if exist %minresult% ( + del %minresult% + echo "Removed %minresult%." +) +if exist %fullresult% ( + del %fullresult% + echo "Removed %fullresult%," ) -echo "Removed %result%, ready." +echo "Ready." PAUSE -java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %result% +java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %range% --js %options% --js %source% --js_output_file %minresult% +java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %wnumb% --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %fullresult% echo "Done." PAUSE diff --git a/src/module.base.js b/src/module.base.js index 75501a4f..b22908f8 100644 --- a/src/module.base.js +++ b/src/module.base.js @@ -1,10 +1,5 @@ -/**@preserve -$.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ - /*jslint browser: true */ /*jslint white: true */ -/*jslint continue: true */ -/*jslint plusplus: true */ (function( $ ){ @@ -208,7 +203,7 @@ $.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ var index, handles = []; // Append handles. - for ( index = 0; index < nrHandles; index++ ) { + for ( index = 0; index < nrHandles; index += 1 ) { // Keep a list of all added handles. handles.push( addHandle( direction, index ).appendTo(base) ); @@ -269,7 +264,7 @@ function closure ( target, options, originalOptions ){ // only one additional argument. var index, values = [ $Target.val() ]; - for ( index = 0; index < events.length; index++ ){ + for ( index = 0; index < events.length; index += 1 ){ $Target.trigger(events[index], values); } } @@ -341,7 +336,7 @@ function closure ( target, options, originalOptions ){ var i, flag; // The API keeps a list of elements: we can re-append them on rebuild. - for ( i = 0; i < triggerPos.length; i++ ) { + for ( i = 0; i < triggerPos.length; i += 1 ) { if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { this.linkAPI[flag].reconfirm(flag); } @@ -408,7 +403,7 @@ function closure ( target, options, originalOptions ){ // 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. - for ( i = 0; i < count; i++ ) { + for ( i = 0; i < count; i += 1 ) { trigger = i%2; @@ -417,30 +412,22 @@ function closure ( target, options, originalOptions ){ // Setting with null indicates an 'ignore'. // Inputting 'false' is invalid. - if ( to === null || to === false ) { - continue; - } - - // If a formatted number was passed, attemt to decode it. - if ( typeof to === 'number' ) { - to = String(to); - } + if ( to !== null && to !== false ) { - to = options.format.from( to ); + // If a formatted number was passed, attemt to decode it. + if ( typeof to === 'number' ) { + to = String(to); + } - // Request an update for all links if the value was invalid. - if ( to !== false && !isNaN(to) ) { + to = options.format.from( to ); - // Calculate the new handle position - to = $Spectrum.toStepping( to ); + // Request an update for all links if the value was invalid. + // Do so too if setting the handle fails. + if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ) ) === false ) { - // Set the handle. - if ( setHandle( $Handles[trigger], to ) !== false ) { - continue; + linkUpdate(triggerPos[trigger]); } } - - linkUpdate(triggerPos[trigger]); } } @@ -606,7 +593,7 @@ function closure ( target, options, originalOptions ){ // Attach the standard drag event to the handles. if ( !behaviour.fixed ) { - for ( i = 0; i < $Handles.length; i++ ) { + for ( i = 0; i < $Handles.length; i += 1 ) { // These events are only bound to the visual handle // element, not the 'real' origin element. @@ -657,7 +644,9 @@ function closure ( target, options, originalOptions ){ } // Animation is optional. - if ( options.animate ) { + // Make sure the initial values where set before using animated + // placement. (no report, unit testing); + if ( options.animate && $Locations[0] !== -1 ) { addClassFor( $Target, Classes[14], 300 ); } @@ -683,7 +672,7 @@ function closure ( target, options, originalOptions ){ var i, retour = []; // Get the value from all handles. - for ( i = 0; i < options.handles; i++ ){ + for ( i = 0; i < options.handles; i += 1 ){ retour[i] = options.format.to( $Values[i] ); } diff --git a/src/module.options.js b/src/module.options.js index 0f7dee1f..19d6d469 100644 --- a/src/module.options.js +++ b/src/module.options.js @@ -1,3 +1,6 @@ +/*jslint browser: true */ +/*jslint white: true */ + /* 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 @@ -13,6 +16,8 @@ (function( $ ){ + 'use strict'; + // Wraps a variable as an array, if it isn't one yet. function asArray ( a ) { return $.isArray(a) ? a : [a]; @@ -47,8 +52,7 @@ } // Catch missing start or end. - if ( entry['min'] === undefined || - entry['max'] === undefined ) { + if ( entry.min === undefined || entry.max === undefined ) { throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); } @@ -130,7 +134,9 @@ throw new Error("noUiSlider: 'margin' option must be numeric."); } - if ( !(parsed.margin = parsed.spectrum.getMargin(entry)) ) { + parsed.margin = parsed.spectrum.getMargin(entry); + + if ( !parsed.margin ) { throw new Error("noUiSlider: 'margin' option is only supported on linear sliders."); } } @@ -180,7 +186,7 @@ parsed.format = entry; // Any object with a to and from method is supported. - if ( typeof entry['to'] === 'function' && typeof entry['from'] === 'function' ) { + if ( typeof entry.to === 'function' && typeof entry.from === 'function' ) { return true; } diff --git a/src/module.range.js b/src/module.range.js index cfcf002f..9c44754d 100644 --- a/src/module.range.js +++ b/src/module.range.js @@ -1,4 +1,5 @@ -/*jslint browser: true, white: true */ +/*jslint browser: true */ +/*jslint white: true */ (function( $ ){ diff --git a/src/optional.pips.js b/src/optional.pips.js index 8af599c5..81ff56e9 100644 --- a/src/optional.pips.js +++ b/src/optional.pips.js @@ -1,5 +1,10 @@ +/*jslint browser: true */ +/*jslint white: true */ + (function( $ ){ + 'use strict'; + // Removes duplicates from an array. function unique(array) { return $.grep(array, function(el, index) { @@ -218,10 +223,6 @@ group = getGroup( info[0], mode, values, stepped ), spread = generateSpread( info[0], density, mode, group ); - if ( mode === 'range' ) { - console.log(spread, Object.keys(spread).length); - } - return $(this).append(addMarking( info[1], info[2], diff --git a/tests/slider.html b/tests/slider.html index 22327f89..9b2903f8 100644 --- a/tests/slider.html +++ b/tests/slider.html @@ -16,18 +16,18 @@ + - - - - - - - - + + + + + + + @@ -38,18 +38,18 @@ - + @@ -57,16 +57,16 @@ - + - + - + - - - + + + diff --git a/tests/slider_events.js b/tests/slider_events.js index 74e1e219..75de2b46 100644 --- a/tests/slider_events.js +++ b/tests/slider_events.js @@ -1,27 +1,31 @@ test( "Triggering events", function(){ - Q.html('\ -
\ - '); + Q.html('
'); - var slider = $('.slider').width(242); + var slider = Q.find('.slider'); + + slider.width(302); // Border of 2 pixels slider.noUiSlider({ - start: [ 30, 40 ], + start: [ 0, 100 ], connect: true, range: { - 'min': [ 50 ], - 'max': [ 90 ] + 'min': [ 0 ], + 'max': [ 300 ] } }); - var w = slider.width(); + equal(slider.find('.noUi-base').length, 1); + equal(slider.find('.noUi-base').width(), 300, 'Testable width') var handles = slider.find('.noUi-handle'); - slider.on("slide", function( event, values ){ - deepEqual( values, ['50.00', '70.00'], "Slide event has proper parameters." ); + ok( !slider.hasClass('noUi-state-tap'), 'No transition on value set' ); + + slider.on('slide', function( event, values ){ + deepEqual( values, ['30.00', '100.00'], "Slide event has proper parameters." ); + slider.off('slide') }); handles.first().trigger($.Event('mousedown', { @@ -30,26 +34,76 @@ })); $(document).trigger($.Event('mousemove', { - clientX: 52, + clientX: 40, clientY: 0 })); $(document).trigger('mouseup'); - deepEqual( slider.val(), ['50.00', '50.00'], "Slider didn't cross." ); + + + + slider.on('set', function( event, values ){ + deepEqual( values, ['30.00', '249.00']); + slider.off('set') + }); handles.last().trigger($.Event('mousedown', { - clientX: 10, + clientX: 50, clientY: 0 })); $(document).trigger($.Event('mousemove', { - clientX: 130, + clientX: 100, + clientY: 0 + })); + + equal(handles.last().parent()[0].style.left, '50%', 'Proper half-way left offset.'); + + $(document).trigger($.Event('mousemove', { + clientX: 199, clientY: 0 })); $(document).trigger('mouseup'); - deepEqual( slider.val(), ['50.00', '70.00'], "Slider moved properly." ); + deepEqual(slider.val(), ['30.00', '249.00'], 'Value has changed.'); + + + slider.val([10, 20]); + ok( slider.hasClass('noUi-state-tap'), 'Animating class has been applied.' ); + + + QUnit.asyncTest( "Testing after animation", function(){ + + setTimeout(function() { + + QUnit.start(); + + ok( !slider.hasClass('noUi-state-tap'), 'Animation class is removed properly.' ); + + slider.on('change', function( event, values ){ + deepEqual( values, ['10.00', '10.00'], "Slide event has proper parameters." ); + slider.off('slide') + }); + + deepEqual( slider.val(), ['10.00', '20.00'] ); + + handles.last().trigger($.Event('mousedown', { + clientX: 1000, + clientY: 0 + })); + + $(document).trigger($.Event('mousemove', { + clientX: 980, + clientY: 0 + })); + + $(document).trigger('mouseup'); + + deepEqual( slider.val(), ['10.00', '10.00'], "Slider didn't cross." ); + + }, 350); + }); }); From e547239e816f70a582b1f2f48dc8c7323da97551 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sat, 9 Aug 2014 10:45:55 +0200 Subject: [PATCH 20/28] Added limit option (#308) --- src/module.base.js | 26 +++++++++++++++++++++----- src/module.options.js | 15 +++++++++++++++ tests/slider.html | 1 + tests/slider_limit.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 tests/slider_limit.js diff --git a/src/module.base.js b/src/module.base.js index b22908f8..eaab0243 100644 --- a/src/module.base.js +++ b/src/module.base.js @@ -352,16 +352,27 @@ function closure ( target, options, originalOptions ){ // Test suggested values and apply margin, step. - function setHandle ( handle, to ) { + function setHandle ( handle, to, noLimitOption ) { var trigger = handle[0] !== $Handles[0][0] ? 1 : 0, - lower = $Locations[0] + options.margin, - upper = $Locations[1] - options.margin; + lowerMargin = $Locations[0] + options.margin, + upperMargin = $Locations[1] - options.margin, + lowerLimit = $Locations[0] + options.limit, + upperLimit = $Locations[1] - options.limit; // For sliders with multiple handles, // limit movement to the other handle. + // Apply the margin option by adding it to the handle positions. if ( $Handles.length > 1 ) { - to = trigger ? Math.max( to, lower ) : Math.min( to, upper ); + to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin ); + } + + // The limit option has the opposite effect, limiting handles to a + // maximum distance from another. Limit must be > 0, as otherwise + // handles would be unmoveable. 'noLimitOption' is set to 'false' + // for the .val() method, except for pass 4/4. + if ( noLimitOption !== false && options.limit && $Handles.length > 1 ) { + to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit ); } // Handle the step option. @@ -400,6 +411,11 @@ function closure ( target, options, originalOptions ){ var i, trigger, to; + // With the limit option, we'll need another limiting pass. + if ( options.limit ) { + count += 1; + } + // 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. @@ -423,7 +439,7 @@ function closure ( target, options, originalOptions ){ // Request an update for all links if the value was invalid. // Do so too if setting the handle fails. - if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ) ) === false ) { + if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) { linkUpdate(triggerPos[trigger]); } diff --git a/src/module.options.js b/src/module.options.js index 19d6d469..20d64aa8 100644 --- a/src/module.options.js +++ b/src/module.options.js @@ -141,6 +141,19 @@ } } + function testLimit ( parsed, entry ) { + + if ( !isNumeric(entry) ){ + throw new Error("noUiSlider: 'limit' option must be numeric."); + } + + parsed.limit = parsed.spectrum.getMargin(entry); + + if ( !parsed.limit ) { + throw new Error("noUiSlider: 'limit' option is only supported on linear sliders."); + } + } + function testDirection ( parsed, entry ) { // Set direction as a numerical value for easy parsing. @@ -197,6 +210,7 @@ var parsed = { margin: 0, + limit: 0, animate: true, format: defaultFormatter }, tests; @@ -212,6 +226,7 @@ 'range': { r: true, t: testRange }, 'orientation': { r: false, t: testOrientation }, 'margin': { r: false, t: testMargin }, + 'limit': { r: false, t: testLimit }, 'behaviour': { r: true, t: testBehaviour }, 'format': { r: false, t: testFormat } }; diff --git a/tests/slider.html b/tests/slider.html index 9b2903f8..221b5ff1 100644 --- a/tests/slider.html +++ b/tests/slider.html @@ -51,6 +51,7 @@ + diff --git a/tests/slider_limit.js b/tests/slider_limit.js new file mode 100644 index 00000000..9cc6651e --- /dev/null +++ b/tests/slider_limit.js @@ -0,0 +1,33 @@ + + test( "Limit option", function(){ + + Q.html('\ +
\ + '); + + var slider = Q.find('.slider'); + + slider.noUiSlider({ + start: [ 50, 100 ], + limit: 30, + range: { + 'min': 30, + 'max': 986 + } + }); + + deepEqual( slider.val(), ['50.00', '80.00'], 'Limit applied on init.' ); + + slider.val( [ null, 600 ] ); + deepEqual( slider.val(), ['50.00', '80.00'], 'Handle can\'t leave limit.' ); + + slider.val( [ 150, 600 ] ); + deepEqual( slider.val(), ['150.00', '180.00'], 'Multiple set limit.' ); + + slider.noUiSlider({ direction: 'rtl' }, true); + + deepEqual( slider.val(), ['150.00', '180.00'], 'Re-init rtl keeps value.' ); + + slider.val( [ 120, 240 ] ); + deepEqual( slider.val(), ['120.00', '150.00'], 'RTL set.' ); + }); From c231455a4d888451e9dcdc3961dc78eaaccdb1eb Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Mon, 11 Aug 2014 20:36:26 +0200 Subject: [PATCH 21/28] Update liblink --- libLink | 2 +- src/module.base.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libLink b/libLink index 1dce4e66..e6a1ddf4 160000 --- a/libLink +++ b/libLink @@ -1 +1 @@ -Subproject commit 1dce4e663faa621d3434d915aec77a5dd1f67435 +Subproject commit e6a1ddf4bd411e9f793db80c8692314783d28200 diff --git a/src/module.base.js b/src/module.base.js index eaab0243..81e24c45 100644 --- a/src/module.base.js +++ b/src/module.base.js @@ -651,6 +651,11 @@ function closure ( target, options, originalOptions ){ // Set the slider value. function valueSet ( input ) { + // LibLink: don't accept new values when currently emitting changes. + if ( $Target[0].LinkIsEmitting ) { + return this; + } + var count, values = asArray( input ); // The RTL settings is implemented by reversing the front-end, From 9ccddb99633efc40d7dc10ede8726a19ed77fc32 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Thu, 14 Aug 2014 19:03:58 +0200 Subject: [PATCH 22/28] Fixed limit option when used with 'drag' --- src/module.base.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module.base.js b/src/module.base.js index 81e24c45..b71a9c1e 100644 --- a/src/module.base.js +++ b/src/module.base.js @@ -489,10 +489,10 @@ function closure ( target, options, originalOptions ){ // Calculate relative positions for the handles. positions = getPositions( proposal, data.positions, handles.length > 1); - state = setHandle ( handles[0], positions[h] ); + state = setHandle ( handles[0], positions[h], handles.length === 1 ); if ( handles.length > 1 ) { - state = setHandle ( handles[1], positions[h?0:1] ) || state; + state = setHandle ( handles[1], positions[h?0:1], false ) || state; } // Fire the 'slide' event if any handle moved. From f6dbb293070516ac9d194062a5a44c6b38c473df Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:05:11 +0200 Subject: [PATCH 23/28] Cleanup --- src/optional.pips.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/optional.pips.js b/src/optional.pips.js index 81ff56e9..a7c862d0 100644 --- a/src/optional.pips.js +++ b/src/optional.pips.js @@ -91,9 +91,7 @@ ignoreLast = true; } - var loglist = []; - - $.each(group, function ( index, value ) { + $.each(group, function ( index ) { // Get the current step and the lower + upper positions. var step, i, q, @@ -145,7 +143,7 @@ // per subrange. density = 1 will result in 100 points on the // full range, 2 for 50, 4 for 25, etc. pctPos = prevPct + ( q * stepsize ); - indexes[pctPos.toFixed(5)] = ['x', 0]; // todo + indexes[pctPos.toFixed(5)] = ['x', 0]; } // Determine the point type. @@ -211,11 +209,11 @@ $.fn.noUiSlider_pips = function ( grid ) { - var mode = grid['mode'], - density = grid['density'] || 1, - filter = grid['filter'] || false, - values = grid['values'] || false, - stepped = grid['stepped'] || false; + var mode = grid.mode, + density = grid.density || 1, + filter = grid.filter || false, + values = grid.values || false, + stepped = grid.stepped || false; return this.each(function(){ From 33940748de0c9a553916c208f239c68e7faf9890 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:05:22 +0200 Subject: [PATCH 24/28] Updated readme --- README.md | 55 ++----------------------------------------------------- 1 file changed, 2 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 884757fe..c2777591 100644 --- a/README.md +++ b/README.md @@ -7,30 +7,9 @@ Documentation An extensive documentation, including **examples**, **options** and **configuration details**, is available here: [noUiSlider documentation](http://refreshless.com/nouislider/). -Changes -------- - -**Changelog for version 6.2.0:** -*(Compatible with 6.0.0)* - -+ Removed the previously added `.classVal` and replaced it with a lightweight solution. -+ Fixed a bug in non-linear stepping for RTL sliders. (#262) -+ Added checks for `min` and `max` in `range`. (#255) -+ Added the minified version in the source, so it can be managed with Bower. (#252) - -**Changelog for version 6.0.0:** - -**Please note:** noUiSlider 6 is a *major* revision, which means it isn't compatible with version 5. Your stylesheet will keep working, but the JavaScript API has changed, and your current implementation will no longer work. - -+ Added optional **non-linear** ranges, including stepping. -+ Added new behaviour settings. -+ Added object-oriented serialization. -+ Change events to use jQuery's/Zepto's `.on` and `.off`. -+ Removed `block` event. - Unit Testing ------------ -Unit tests where added with noUiSlider 6. Coverage of `$.Link` and value handling is near complete, but due to the sensitivity of events across browsers, event testing is a little lacking. +Unit tests where overhauled for noUiSlider 7. Most code is now covered, with events testing being slightly lacking due to it's browser dependant nature. Version numbering ------------------------------ @@ -41,34 +20,4 @@ Compression and Error checking ------------------------------ The plugin code is checked using ([JsLint](http://jslint.com/)). Any remaining errors and warnings are intentional. -The plugin is compressed using the ([Google Closure Compiler](http://closure-compiler.appspot.com/home)). The source was adapted to facilitate the `ADVANCED_OPTIMIZATIONS` level. `$.Link` is merged into the file. On Windows, the folling BAT script can be used to run the compiler. On OS X or Linux enviroments, simply run the `java -jar` command from the command line. - -```bat -@echo off - -:: Set all paths as variables -set "jquery=.\externs\jquery-1.8.js" -set "link=..\source\Link.js" -set "source=..\source\jquery.nouislider.js" -set "result=..\source\jquery.nouislider.min.js" - -:: Remove the existing file so we can be sure a new one is generated. -if exist %result% ( - del %result% -) - -echo "Removed %result%, ready." - -PAUSE - -java -jar .\compiler\compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --externs %jquery% --warning_level VERBOSE --js %link% --js %source% --js_output_file %result% - -echo "Done." -PAUSE -``` - -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 updates in the future. - -+ Firefox and Safari on Windows will emulate mouse-events on touch screens, but prefer scrolling to dragging the slider. +The plugin is compressed using the ([Google Closure Compiler](http://closure-compiler.appspot.com/home)). The source was initialy adapted to facilitate the `ADVANCED_OPTIMIZATIONS` level, but the risks this poses regarding to unintented renaming proved problematic. On Windows, the BAT script in this repository can be used to run the compiler. On OS X or Linux enviroments, simply run the `java -jar` command from the command line. From e6ab28d7da65db798f1359028fc049a2d0b51495 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:10:57 +0200 Subject: [PATCH 25/28] Added wNumb to the compile script --- compile.bat | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/compile.bat b/compile.bat index 915a610c..a033f66c 100644 --- a/compile.bat +++ b/compile.bat @@ -1,7 +1,8 @@ @echo off :: Set all paths as variables -set "wnumb=wnumb\wnumb.js" +set "wnumb=wnumb\wNumb.js" +set "wnumbMin=wnumb\wNumb.min.js" set "link=libLink\jquery.liblink.js" set "jquery=compiler\jquery-1.9.js" @@ -12,25 +13,30 @@ set "options=src\module.options.js" set "source=src\module.base.js" set "pips=src\optional.pips.js" -set "minresult=jquery.nouislider.min.js" -set "fullresult=jquery.nouislider.full.min.js" +set "nouislidermin=jquery.nouislider.min.js" +set "nouisliderfull=jquery.nouislider.full.min.js" + +set "compiler=compiler\compiler.jar" +set "level=--compilation_level SIMPLE_OPTIMIZATIONS" +set "verbose=--warning_level VERBOSE" :: Remove the existing file so we can be sure a new one is generated. -if exist %minresult% ( - del %minresult% - echo "Removed %minresult%." +if exist %nouislidermin% ( + del %nouislidermin% + echo "Removed %nouislidermin%." ) -if exist %fullresult% ( - del %fullresult% - echo "Removed %fullresult%," +if exist %nouisliderfull% ( + del %nouisliderfull% + echo "Removed %nouisliderfull%," ) echo "Ready." PAUSE -java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %range% --js %options% --js %source% --js_output_file %minresult% -java -jar compiler\compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --externs %jquery% --externs %zepto% --warning_level VERBOSE --js %wnumb% --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %fullresult% +java -jar %compiler% %level% %verbose% --js %wnumb% --js_output_file %wnumbMin% +java -jar %compiler% %level% %verbose% --externs %jquery% --externs %zepto% --js %range% --js %options% --js %source% --js_output_file %nouislidermin% +java -jar %compiler% %level% %verbose% --externs %jquery% --externs %zepto% --js %wnumb% --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %nouisliderfull% echo "Done." PAUSE From 7b41f0845402ff9ae294c5583d9ee5624e0db184 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:11:50 +0200 Subject: [PATCH 26/28] Update wNumb --- wnumb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wnumb b/wnumb index 3a5e1bb5..bdf2e244 160000 --- a/wnumb +++ b/wnumb @@ -1 +1 @@ -Subproject commit 3a5e1bb5d5993b8129e4f9f530c776156526d955 +Subproject commit bdf2e244cdf422c7343efe49607054b4fbc3fc95 From d702e1324407aa544a533191b8e583c75dbc1917 Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:12:33 +0200 Subject: [PATCH 27/28] Ignore the compiler --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..c954da8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/compiler \ No newline at end of file From 80d71e62713214345737555d1c46d7fba9e1291e Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Fri, 22 Aug 2014 20:46:10 +0200 Subject: [PATCH 28/28] Add new minified versions --- jquery.nouislider.full.min.js | 33 ++++++++++++++++++++++ jquery.nouislider.min.js | 53 +++++++++++++++-------------------- wnumb | 2 +- 3 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 jquery.nouislider.full.min.js diff --git a/jquery.nouislider.full.min.js b/jquery.nouislider.full.min.js new file mode 100644 index 00000000..215ac05a --- /dev/null +++ b/jquery.nouislider.full.min.js @@ -0,0 +1,33 @@ +(function(){function c(a){return a.split("").reverse().join("")}function l(a,b){return a.substring(0,b.length)===b}function q(a,b,d){if((a[b]||a[d])&&a[b]===a[d])throw Error(b);}function m(a,b,d,e,n,h,w,k,A,H,D,g){w=g;var l,s=D="";h&&(g=h(g));if("number"!==typeof g||!isFinite(g))return!1;a&&0===parseFloat(g.toFixed(a))&&(g=0);0>g&&(l=!0,g=Math.abs(g));!1!==a&&(h=g,g=Math.pow(10,a),g=(Math.round(h*g)/g).toFixed(a));g=g.toString();-1!==g.indexOf(".")?(a=g.split("."),h=a[0],d&&(D=d+a[1])):h=g;b&&(h= +c(h).match(/.{1,3}/g),h=c(h.join(c(b))));l&&k&&(s+=k);e&&(s+=e);l&&A&&(s+=A);s=s+h+D;n&&(s+=n);H&&(s=H(s,w));return s}function u(a,b,d,c,e,h,w,k,A,H,D,g){var m;a="";D&&(g=D(g));if(!g||"string"!==typeof g)return!1;k&&l(g,k)&&(g=g.replace(k,""),m=!0);c&&l(g,c)&&(g=g.replace(c,""));A&&l(g,A)&&(g=g.replace(A,""),m=!0);if(c=e)c=g.slice(-1*e.length)===e;c&&(g=g.slice(0,-1*e.length));b&&(g=g.split(b).join(""));d&&(g=g.replace(d,"."));m&&(a+="-");a=(a+g).replace(/[^0-9\.\-.]/g,"");if(""===a)return!1;a=Number(a); +w&&(a=w(a));return"number"===typeof a&&isFinite(a)?a:!1}function a(a){var b,d,c,n={};for(b=0;bc)n[d]=c;else throw Error(d);else if("encoder"===d||"decoder"===d||"edit"===d||"undo"===d)if("function"===typeof c)n[d]=c;else throw Error(d);else if("string"===typeof c)n[d]=c;else throw Error(d);q(n,"mark","thousand");q(n,"prefix","negative");q(n,"prefix", +"negativeBefore");return n}function b(a,b,d){var c,n=[];for(c=0;c"),!0},function(a){if("string"===typeof a&&0!==a.indexOf("-")){this.method="val";var b=document.createElement("input");b.name=a;b.type="hidden";this.target=this.el=c(b);return!0}},function(a){if("function"===typeof a)return this.target=!1,this.method=a,!0},function(a,b){if(l(a)&&!b)return a.is("input, select, textarea")?(this.method="val",this.target=a.on("change.liblink",this.changeHandler)): +(this.target=a,this.method="html"),!0},function(a,b){if(l(a)&&("function"===typeof b||"string"===typeof b&&a[b]))return this.method=b,this.target=a,!0}];q.prototype.set=function(a){var b=Array.prototype.slice.call(arguments).slice(1);this.lastSetValue=this.formatInstance.to(a);b.unshift(this.lastSetValue);("function"===typeof this.method?this.method:this.target[this.method]).apply(this.target,b)};m.prototype.push=function(a,b){this.items.push(a);b&&this.elements.push(b)};m.prototype.reconfirm=function(a){var b; +for(b=0;b=b[d];)d+=1;return d}function u(a,b,d,c){this.xPct=[];this.xVal=[];this.xSteps=[c||!1];this.xNumSteps=[!1];this.snap=b;this.direction=d;for(var f in a)if(a.hasOwnProperty(f)){b=f;d=a[f];c=void 0;"number"===typeof d&&(d=[d]);if("[object Array]"!==Object.prototype.toString.call(d))throw Error("noUiSlider: 'range' contains invalid value.");c="min"===b?0: +"max"===b?100:parseFloat(b);if(!l(c)||!l(d[0]))throw Error("noUiSlider: 'range' value isn't numeric.");this.xPct.push(c);this.xVal.push(d[0]);c?this.xSteps.push(isNaN(d[1])?!1:d[1]):isNaN(d[1])||(this.xSteps[0]=d[1])}this.xNumSteps=this.xSteps.slice(0);for(f in this.xNumSteps)this.xNumSteps.hasOwnProperty(f)&&(a=Number(f),(b=this.xNumSteps[f])&&(this.xSteps[a]=q([this.xVal[a],this.xVal[a+1]],b)/(100/(this.xPct[a+1]-this.xPct[a]))))}u.prototype.getMargin=function(a){return 2===this.xPct.length?q(this.xVal, +a):!1};u.prototype.toStepping=function(a){var b=this.xVal,c=this.xPct;if(a>=b.slice(-1)[0])a=100;else{var e=m(a,b),f,l;f=b[e-1];l=b[e];b=c[e-1];c=c[e];f=[f,l];a=q(f,0>f[0]?a+Math.abs(f[0]):a-f[0]);a=b+a/(100/(c-b))}this.direction&&(a=100-a);return a};u.prototype.fromStepping=function(a){this.direction&&(a=100-a);var b;var c=this.xVal;b=this.xPct;if(100<=a)b=c.slice(-1)[0];else{var e=m(a,b),f,l;f=c[e-1];l=c[e];c=b[e-1];f=[f,l];b=100/(b[e]-c)*(a-c)*(f[1]-f[0])/100+f[0]}a=Math.pow(10,7);return Number((Math.round(b* +a)/a).toFixed(7))};u.prototype.getStep=function(a){this.direction&&(a=100-a);var b=this.xPct,c=this.xSteps,e=this.snap;if(100!==a){var f=m(a,b);e?(c=b[f-1],b=b[f],a=a-c>(b-c)/2?b:c):(c[f-1]?(e=b[f-1],c=c[f-1],b=Math.round((a-b[f-1])/c)*c,b=e+b):b=a,a=b)}this.direction&&(a=100-a);return a};u.prototype.getApplicableStep=function(a){var b=m(a,this.xPct);a=100===a?2:1;return[this.xNumSteps[b-2],this.xVal[b-a],this.xNumSteps[b-a]]};u.prototype.convert=function(a){return this.getStep(this.toStepping(a))}; +c.noUiSlider={Spectrum:u}})(window.jQuery||window.Zepto);(function(c){function l(a){return"number"===typeof a&&!isNaN(a)&&isFinite(a)}function q(a,b){if(!l(b))throw Error("noUiSlider: 'step' is not numeric.");a.singleStep=b}function m(a,b){if("object"!==typeof b||c.isArray(b))throw Error("noUiSlider: 'range' is not an object.");if(void 0===b.min||void 0===b.max)throw Error("noUiSlider: Missing 'min' or 'max' in 'range'.");a.spectrum=new c.noUiSlider.Spectrum(b,a.snap,a.dir,a.singleStep)}function u(a,b){var d=b;b=c.isArray(d)?d:[d];if(!c.isArray(b)||!b.length|| +2
").addClass(h[2]),e=["-lower","-upper"];a&&e.reverse();d.children().addClass(h[3]+" "+h[3]+e[b]);return d}function u(a,b,c){switch(a){case 1:b.addClass(h[7]);c[0].addClass(h[6]);break;case 3:c[1].addClass(h[6]);case 2:c[0].addClass(h[7]);case 0:b.addClass(h[6])}}function a(a,b,c){var d,e=[];for(d=0;d").appendTo(d).addClass(h[1])}function d(d,k,e){function f(){return B[["width","height"][k.ort]]()}function m(a){var b,c=[v.val()];for(b=0;bd&&(e+=Math.abs(d)),100=c[1]?c[2]:c[0],c[2]]]});return g(a)};d.getInfo=function(){return[E,k.style,k.ort]};v.val(k.start)}function e(a){if(!this.length)throw Error("noUiSlider: Can't initialize slider on empty selection.");var b=c.noUiSlider.testOptions(a,this);return this.each(function(){d(this, +b,a)})}function f(a){return this.each(function(){if(this.destroy){var b=c(this).val(),d=this.destroy(),e=c.extend({},d,a);c(this).noUiSlider(e);this.reappend();d.start===e.start&&c(this).val(b)}else c(this).noUiSlider(a)})}function z(){return this[0][arguments.length?"vSet":"vGet"].apply(this[0],arguments)}var p=c(document),r=c.fn.val,n=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"},h="noUi-target noUi-base noUi-origin noUi-handle noUi-horizontal noUi-vertical noUi-background noUi-connect noUi-ltr noUi-rtl noUi-dragable noUi-state-drag noUi-state-tap noUi-active noUi-stacking".split(" ");c.fn.val=function(){var a=arguments,b=c(this[0]);return arguments.length?this.each(function(){(c(this).hasClass(h[0])?z:r).apply(c(this),a)}):(b.hasClass(h[0])?z:r).call(b)};c.fn.noUiSlider= +function(a,b){return(b?f:e).call(this,a)}})(window.jQuery||window.Zepto);(function(c){function l(a){return c.grep(a,function(b,d){return d===c.inArray(b,a)})}function q(a,b,d,e){if("range"===b||"steps"===b)return a.xVal;if("count"===b){b=100/(d-1);var f,l=0;for(d=[];100>=(f=l++*b);)d.push(f);b="positions"}if("positions"===b)return c.map(d,function(b){return a.fromStepping(e?a.getStep(b):b)});if("values"===b)return e?c.map(d,function(b){return a.fromStepping(a.getStep(a.toStepping(b)))}):d}function m(a,b,d,e){var f=a.direction,m={},p=a.xVal[0],r=a.xVal[a.xVal.length-1], +n=!1,h=!1,w=0;a.direction=0;e=l(e.slice().sort(function(a,b){return a-b}));e[0]!==p&&(e.unshift(p),n=!0);e[e.length-1]!==r&&(e.push(r),h=!0);c.each(e,function(f){var l,p,r,g=e[f],q=e[f+1],s,u,x,G;"steps"===d&&(l=a.xNumSteps[f]);l||(l=q-g);if(!1!==g&&void 0!==q)for(p=g;p<=q;p+=l){s=a.toStepping(p);r=s-w;x=r/b;x=Math.round(x);G=r/x;for(r=1;r<=x;r+=1)u=w+r*G,m[u.toFixed(5)]=["x",0];x=-1");m.addClass("noUi-pips noUi-pips-"+p);c.each(e,function(a,b){d&&(a=100-a);m.append("
");b[1]&&m.append("
"+Math.round(b[0])+"
")});return m}c.fn.noUiSlider_pips=function(a){var b=a.mode,d=a.density||1,e=a.filter||!1,f=a.values||!1, +l=a.stepped||!1;return this.each(function(){var a=this.getInfo(),r=q(a[0],b,f,l),r=m(a[0],d,b,r);return c(this).append(u(a[1],a[2],a[0].direction,r,e))})}})(window.jQuery||window.Zepto); diff --git a/jquery.nouislider.min.js b/jquery.nouislider.min.js index b9a63edd..850b0b32 100644 --- a/jquery.nouislider.min.js +++ b/jquery.nouislider.min.js @@ -1,31 +1,22 @@ -/* - -$.Link (part of noUiSlider) - WTFPL */ -(function(c){function m(a,c,d){if((a[c]||a[d])&&a[c]===a[d])throw Error("(Link) '"+c+"' can't match '"+d+"'.'");}function r(a){void 0===a&&(a={});if("object"!==typeof a)throw Error("(Format) 'format' option must be an object.");var h={};c(u).each(function(c,n){if(void 0===a[n])h[n]=A[c];else if(typeof a[n]===typeof A[c]){if("decimals"===n&&(0>a[n]||7a&&(n=this.a("negative"),k=this.a("negativeBefore"));a=Math.abs(a).toFixed(d).toString();a=a.split(".");this.a("thousand")?(m=c(a[0]).match(/.{1,3}/g),m=c(m.join(c(this.a("thousand"))))):m=a[0];this.a("mark")&&1")[0]};k.prototype.H=function(a){this.method="val";this.j=document.createElement("input");this.j.name=a;this.j.type="hidden"};k.prototype.G=function(a){function h(a,c){return[c?null:a,c?a:null]}var d=this;this.method="val";this.target=a.on("change",function(a){d.B.val(h(c(a.target).val(),d.t),{link:d,set:!0})})};k.prototype.p=function(a,h,d,k){this.g=d;this.update=!k;if("string"=== -typeof a&&0===a.indexOf("-tooltip-"))this.K(a,h);else if("string"===typeof a&&0!==a.indexOf("-"))this.H(a);else if("function"===typeof a)this.target=!1,this.method=a;else{if(a instanceof c||c.zepto&&c.zepto.isZ(a)){if(!h){if(a.is("input, select, textarea")){this.G(a);return}h="html"}if("function"===typeof h||"string"===typeof h&&a[h]){this.method=h;this.target=a;return}}throw new RangeError("(Link) Invalid Link.");}};k.prototype.write=function(a,c,d,k){if(!this.update||!1!==k)if(this.u=a,this.F=a= -this.format(a),"function"===typeof this.method)this.method.call(this.target[0]||d[0],a,c,d);else this.target[this.method](a,c,d)};k.prototype.q=function(a){this.g=new r(c.extend({},a,this.g instanceof r?this.g.r:this.g))};k.prototype.J=function(a){this.B=a};k.prototype.I=function(a){this.t=a};k.prototype.format=function(a){return this.g.L(a)};k.prototype.A=function(a){return this.g.w(a)};k.prototype.p.prototype=k.prototype;c.Link=k})(window.jQuery||window.Zepto);/* - -$.fn.noUiSlider - WTFPL - refreshless.com/nouislider/ */ -(function(c){function m(e){return"number"===typeof e&&!isNaN(e)&&isFinite(e)}function r(e){return c.isArray(e)?e:[e]}function k(e,b){e.addClass(b);setTimeout(function(){e.removeClass(b)},300)}function u(e,b){return 100*b/(e[1]-e[0])}function A(e,b){if(b>=e.d.slice(-1)[0])return 100;for(var a=1,c,f,d;b>=e.d[a];)a++;c=e.d[a-1];f=e.d[a];d=e.c[a-1];c=[c,f];return d+u(c,0>c[0]?b+Math.abs(c[0]):b-c[0])/(100/(e.c[a]-d))}function a(e,b){if(100<=b)return e.d.slice(-1)[0];for(var a=1,c,f,d;b>=e.c[a];)a++;c= -e.d[a-1];f=e.d[a];d=e.c[a-1];c=[c,f];return 100/(e.c[a]-d)*(b-d)*(c[1]-c[0])/100+c[0]}function h(a,b){for(var c=1,g;(a.dir?100-b:b)>=a.c[c];)c++;if(a.m)return g=a.c[c-1],c=a.c[c],b-g>(c-g)/2?c:g;a.h[c-1]?(g=a.h[c-1],c=a.c[c-1]+Math.round((b-a.c[c-1])/g)*g):c=b;return c}function d(a,b){if(!m(b))throw Error("noUiSlider: 'step' is not numeric.");a.h[0]=b}function n(a,b){if("object"!==typeof b||c.isArray(b))throw Error("noUiSlider: 'range' is not an object.");if(void 0===b.min||void 0===b.max)throw Error("noUiSlider: Missing 'min' or 'max' in 'range'."); -c.each(b,function(b,g){var d;"number"===typeof g&&(g=[g]);if(!c.isArray(g))throw Error("noUiSlider: 'range' contains invalid value.");d="min"===b?0:"max"===b?100:parseFloat(b);if(!m(d)||!m(g[0]))throw Error("noUiSlider: 'range' value isn't numeric.");a.c.push(d);a.d.push(g[0]);d?a.h.push(isNaN(g[1])?!1:g[1]):isNaN(g[1])||(a.h[0]=g[1])});c.each(a.h,function(b,c){if(!c)return!0;a.h[b]=u([a.d[b],a.d[b+1]],c)/(100/(a.c[b+1]-a.c[b]))})}function E(a,b){"number"===typeof b&&(b=[b]);if(!c.isArray(b)||!b.length|| -2
").addClass(f[2]),g=["-lower","-upper"];a.dir&&g.reverse();d.children().addClass(f[3]+" "+f[3]+g[b]);return d}function Q(a,b){b.j&&(b=new c.Link({target:c(b.j).clone().appendTo(a),method:b.method,format:b.g},!0));return b}function R(a,b){var d,f=[];for(d=0;d").appendTo(b).addClass(f[1])}function V(d,b,m){function g(){return t[["width","height"][b.k]]()}function n(a){var b,c=[q.val()];for(b=0;bp&&(p=h(b,p));p=Math.max(Math.min(parseFloat(p.toFixed(7)),100),0);if(p===x[g])return 1===l.length?!1:p===H||p===k?0:!1;d.css(b.style,p+"%");d.is(":first-child")&&d.toggleClass(f[17],50d&&(e+=Math.abs(d)),100=g[e];)e+=1;return e}function k(d,g,e,a){this.xPct=[];this.xVal=[];this.xSteps=[a||!1];this.xNumSteps=[!1];this.snap=g;this.direction=e;for(var f in d)if(d.hasOwnProperty(f)){g=f;e=d[f];a=void 0;"number"===typeof e&&(e=[e]);if("[object Array]"!==Object.prototype.toString.call(e))throw Error("noUiSlider: 'range' contains invalid value.");a="min"===g?0: +"max"===g?100:parseFloat(g);if(!q(a)||!q(e[0]))throw Error("noUiSlider: 'range' value isn't numeric.");this.xPct.push(a);this.xVal.push(e[0]);a?this.xSteps.push(isNaN(e[1])?!1:e[1]):isNaN(e[1])||(this.xSteps[0]=e[1])}this.xNumSteps=this.xSteps.slice(0);for(f in this.xNumSteps)this.xNumSteps.hasOwnProperty(f)&&(d=Number(f),(g=this.xNumSteps[f])&&(this.xSteps[d]=s([this.xVal[d],this.xVal[d+1]],g)/(100/(this.xPct[d+1]-this.xPct[d]))))}k.prototype.getMargin=function(d){return 2===this.xPct.length?s(this.xVal, +d):!1};k.prototype.toStepping=function(d){var g=this.xVal,a=this.xPct;if(d>=g.slice(-1)[0])d=100;else{var m=p(d,g),f,k;f=g[m-1];k=g[m];g=a[m-1];a=a[m];f=[f,k];d=s(f,0>f[0]?d+Math.abs(f[0]):d-f[0]);d=g+d/(100/(a-g))}this.direction&&(d=100-d);return d};k.prototype.fromStepping=function(d){this.direction&&(d=100-d);var a;var e=this.xVal;a=this.xPct;if(100<=d)a=e.slice(-1)[0];else{var m=p(d,a),f,k;f=e[m-1];k=e[m];e=a[m-1];f=[f,k];a=100/(a[m]-e)*(d-e)*(f[1]-f[0])/100+f[0]}d=Math.pow(10,7);return Number((Math.round(a* +d)/d).toFixed(7))};k.prototype.getStep=function(d){this.direction&&(d=100-d);var a=this.xPct,e=this.xSteps,k=this.snap;if(100!==d){var f=p(d,a);k?(e=a[f-1],a=a[f],d=d-e>(a-e)/2?a:e):(e[f-1]?(k=a[f-1],e=e[f-1],a=Math.round((d-a[f-1])/e)*e,a=k+a):a=d,d=a)}this.direction&&(d=100-d);return d};k.prototype.getApplicableStep=function(a){var g=p(a,this.xPct);a=100===a?2:1;return[this.xNumSteps[g-2],this.xVal[g-a],this.xNumSteps[g-a]]};k.prototype.convert=function(a){return this.getStep(this.toStepping(a))}; +a.noUiSlider={Spectrum:k}})(window.jQuery||window.Zepto);(function(a){function q(c){return"number"===typeof c&&!isNaN(c)&&isFinite(c)}function s(c,b){if(!q(b))throw Error("noUiSlider: 'step' is not numeric.");c.singleStep=b}function p(c,b){if("object"!==typeof b||a.isArray(b))throw Error("noUiSlider: 'range' is not an object.");if(void 0===b.min||void 0===b.max)throw Error("noUiSlider: Missing 'min' or 'max' in 'range'.");c.spectrum=new a.noUiSlider.Spectrum(b,c.snap,c.dir,c.singleStep)}function k(c,b){var d=b;b=a.isArray(d)?d:[d];if(!a.isArray(b)||!b.length|| +2
").addClass(h[2]),e=["-lower","-upper"];c&&e.reverse();d.children().addClass(h[3]+" "+h[3]+e[b]);return d}function k(a,b,d){switch(a){case 1:b.addClass(h[7]);d[0].addClass(h[6]);break;case 3:d[1].addClass(h[6]);case 2:d[0].addClass(h[7]);case 0:b.addClass(h[6])}}function d(a,b,d){var e,f=[];for(e=0;e").appendTo(d).addClass(h[1])}function e(c,b,e){function f(){return v[["width","height"][b.ort]]()}function m(b){var a,c=[n.val()];for(a=0;ad&&(e+=Math.abs(d)),100=c[1]?c[2]:c[0],c[2]]]});return p(b)};c.getInfo=function(){return[z,b.style,b.ort]};n.val(b.start)}function m(c){if(!this.length)throw Error("noUiSlider: Can't initialize slider on empty selection.");var b=a.noUiSlider.testOptions(c,this);return this.each(function(){e(this, +b,c)})}function f(c){return this.each(function(){if(this.destroy){var b=a(this).val(),d=this.destroy(),e=a.extend({},d,c);a(this).noUiSlider(e);this.reappend();d.start===e.start&&a(this).val(b)}else a(this).noUiSlider(c)})}function x(){return this[0][arguments.length?"vSet":"vGet"].apply(this[0],arguments)}var y=a(document),t=a.fn.val,u=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"},h="noUi-target noUi-base noUi-origin noUi-handle noUi-horizontal noUi-vertical noUi-background noUi-connect noUi-ltr noUi-rtl noUi-dragable noUi-state-drag noUi-state-tap noUi-active noUi-stacking".split(" ");a.fn.val=function(){var c=arguments,b=a(this[0]);return arguments.length?this.each(function(){(a(this).hasClass(h[0])?x:t).apply(a(this),c)}):(b.hasClass(h[0])?x:t).call(b)};a.fn.noUiSlider= +function(a,b){return(b?f:m).call(this,a)}})(window.jQuery||window.Zepto); diff --git a/wnumb b/wnumb index bdf2e244..2bf0af44 160000 --- a/wnumb +++ b/wnumb @@ -1 +1 @@ -Subproject commit bdf2e244cdf422c7343efe49607054b4fbc3fc95 +Subproject commit 2bf0af44c1e0ef316cc913236682ca4ef7ead548