From 6dfbfd9cedd3d2e259b0b1a12699ed165e92a7ac Mon Sep 17 00:00:00 2001 From: Leon Gersen Date: Sun, 21 Jan 2018 14:07:27 +0100 Subject: [PATCH] Refactor - Split scope files - Fix shadowing with jshint - Try some eslint options - Rename odd variable naming choices - Move 'already initialized' error --- Gruntfile.js | 34 ++- README.md | 3 + distribute/nouislider.css | 2 +- distribute/nouislider.js | 404 ++++++++++++++++---------------- distribute/nouislider.min.css | 2 +- distribute/nouislider.min.js | 4 +- eslint.json | 8 + package.json | 7 +- src/js/aria.js | 12 +- src/js/interface.js | 9 +- src/js/options.js | 34 +-- src/js/range.js | 41 ++-- src/js/scope.js | 421 ---------------------------------- src/js/scope_api.js | 201 ++++++++++++++++ src/js/scope_core.js | 203 ++++++++++++++++ src/js/scope_event_binding.js | 60 +++++ src/js/scope_events.js | 178 ++++++++++++++ src/js/scope_helpers.js | 262 --------------------- src/js/scope_run.js | 41 ++++ src/js/scope_start.js | 5 +- src/js/structure.js | 19 +- 21 files changed, 997 insertions(+), 953 deletions(-) create mode 100644 eslint.json delete mode 100644 src/js/scope.js create mode 100644 src/js/scope_api.js create mode 100644 src/js/scope_core.js create mode 100644 src/js/scope_event_binding.js delete mode 100644 src/js/scope_helpers.js create mode 100644 src/js/scope_run.js diff --git a/Gruntfile.js b/Gruntfile.js index f22c0d23..6f6c7030 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,9 +17,11 @@ module.exports = function(grunt) { 'src/js/tooltips.js', 'src/js/aria.js', 'src/js/pips.js', - 'src/js/scope_helpers.js', 'src/js/scope_events.js', - 'src/js/scope.js', + 'src/js/scope_event_binding.js', + 'src/js/scope_core.js', + 'src/js/scope_api.js', + 'src/js/scope_run.js', 'src/js/scope_end.js', 'src/js/interface.js', 'src/js/outro.js' @@ -78,10 +80,35 @@ module.exports = function(grunt) { latedef: true, undef: true, unused: true, + shadow: "outer", + eqeqeq: true, + forin: true, + freeze: true, globals: { module: true, define: true, __dirname: true, require: true } }, basic: ['distribute/nouislider.js'] }, + eslint: { + options: { + configFile: 'eslint.json' + }, + + // Only lint files containing solely function definitions + target: [ + 'src/js/helpers.js', + 'src/js/constants.js', + 'src/js/range.js', + 'src/js/options.js', + 'src/js/structure.js', + 'src/js/tooltips.js', + 'src/js/aria.js', + 'src/js/pips.js', + 'src/js/scope_events.js', + 'src/js/scope_event_binding.js', + 'src/js/scope_core.js', + 'src/js/scope_api.js' + ] + }, uglify: { all: { options: { @@ -131,6 +158,9 @@ module.exports = function(grunt) { // https://github.com/gruntjs/grunt-contrib-qunit grunt.loadNpmTasks('grunt-contrib-qunit'); + // https://www.npmjs.com/package/grunt-eslint + grunt.loadNpmTasks('grunt-eslint'); + grunt.registerTask('default', ['concat', 'less', 'jshint']); grunt.registerTask('test', ['concat', 'less', 'jshint', 'qunit']); grunt.registerTask('create', ['concat', 'less', 'uglify', 'cssmin']); diff --git a/README.md b/README.md index d083b928..5d390130 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ npm [(package)](https://www.npmjs.com/package/nouislider) Changelog --------- +### 11.0.3 (*2018-01-21*) +Refactor of source code. There are no meaningful changes in the distributed files; + ### 11.0.2 (*2018-01-20*) - Fixed: Slider ignores clicks on `.noUi-target` outside of `.noUi-base` (#842); - Fixed: `.noUi-origin` moving out of the page causes horizontal scrolling (#852); diff --git a/distribute/nouislider.css b/distribute/nouislider.css index c233a628..f68fa5e3 100644 --- a/distribute/nouislider.css +++ b/distribute/nouislider.css @@ -1,4 +1,4 @@ -/*! nouislider - 11.0.2 - 2018-01-20 18:05:29 */ +/*! nouislider - 11.0.3 - 2018-01-21 14:04:07 */ /* Functional styling; * These styles are required for noUiSlider to function. * You don't need to change these rules to apply your design. diff --git a/distribute/nouislider.js b/distribute/nouislider.js index 170ad2fd..120eaf8e 100644 --- a/distribute/nouislider.js +++ b/distribute/nouislider.js @@ -1,4 +1,4 @@ -/*! nouislider - 11.0.2 - 2018-01-20 18:05:29 */ +/*! nouislider - 11.0.3 - 2018-01-21 14:04:07 */ (function (factory) { @@ -22,7 +22,7 @@ 'use strict'; - var VERSION = '11.0.2'; + var VERSION = '11.0.3'; function isValidFormatter ( entry ) { @@ -231,12 +231,11 @@ return 100; } - var j = getJ( value, xVal ), va, vb, pa, pb; - - va = xVal[j-1]; - vb = xVal[j]; - pa = xPct[j-1]; - pb = xPct[j]; + var j = getJ( value, xVal ); + var va = xVal[j-1]; + var vb = xVal[j]; + var pa = xPct[j-1]; + var pb = xPct[j]; return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); } @@ -249,12 +248,11 @@ return xVal.slice(-1)[0]; } - var j = getJ( value, xPct ), va, vb, pa, pb; - - va = xVal[j-1]; - vb = xVal[j]; - pa = xPct[j-1]; - pb = xPct[j]; + var j = getJ( value, xPct ); + var va = xVal[j-1]; + var vb = xVal[j]; + var pa = xPct[j-1]; + var pb = xPct[j]; return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); } @@ -266,14 +264,13 @@ return value; } - var j = getJ( value, xPct ), a, b; + var j = getJ( value, xPct ); + var a = xPct[j-1]; + var b = xPct[j]; // 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; @@ -349,12 +346,7 @@ } // Factor to range ratio - that.xSteps[i] = fromPercentage([ - that.xVal[i] - ,that.xVal[i+1] - ], n) / subRangeRatio ( - that.xPct[i], - that.xPct[i+1] ); + that.xSteps[i] = fromPercentage([that.xVal[i], that.xVal[i+1]], n) / subRangeRatio(that.xPct[i], that.xPct[i+1]); var totalSteps = (that.xVal[i+1] - that.xVal[i]) / that.xNumSteps[i]; var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1); @@ -376,7 +368,8 @@ this.snap = snap; - var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ]; + var index; + var ordered = []; // [0, 'min'], [1, '50%'], [2, 'max'] // Map the object keys to an array. for ( index in entry ) { @@ -452,7 +445,7 @@ Spectrum.prototype.countStepDecimals = function () { var stepDecimals = this.xNumSteps.map(countDecimals); return Math.max.apply(null, stepDecimals); - }; + }; // Outside testing Spectrum.prototype.convert = function ( value ) { @@ -605,14 +598,14 @@ // 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 (" + VERSION + "): 'orientation' option is invalid."); + case 'horizontal': + parsed.ort = 0; + break; + case 'vertical': + parsed.ort = 1; + break; + default: + throw new Error("noUiSlider (" + VERSION + "): 'orientation' option is invalid."); } } @@ -653,7 +646,7 @@ throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers."); } - if ( Array.isArray(entry) && !(entry.length == 2 || isNumeric(entry[0]) || isNumeric(entry[1])) ) { + if ( Array.isArray(entry) && !(entry.length === 2 || isNumeric(entry[0]) || isNumeric(entry[1])) ) { throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers."); } @@ -687,14 +680,14 @@ // 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; - break; - default: - throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized."); + case 'ltr': + parsed.dir = 0; + break; + case 'rtl': + parsed.dir = 1; + break; + default: + throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized."); } } @@ -928,13 +921,13 @@ } -function closure ( target, options, originalOptions ){ +function scope ( target, options, originalOptions ){ var actions = getActions(); var supportsTouchActionNone = getSupportsTouchActionNone(); var supportsPassive = supportsTouchActionNone && getSupportsPassive(); - // All variables local to 'closure' are prefixed with 'scope_' + // All variables local to 'scope' are prefixed with 'scope_' var scope_Target = target; var scope_Locations = []; var scope_Base; @@ -951,13 +944,15 @@ function closure ( target, options, originalOptions ){ var scope_DocumentElement = scope_Document.documentElement; var scope_Body = scope_Document.body; + // For horizontal sliders in standard ltr documents, // make .noUi-origin overflow to the left so the document doesn't scroll. var scope_DirOffset = (scope_Document.dir === 'rtl') || (options.ort === 1) ? 0 : 100; +/*! In this file: Construction of DOM elements; */ // Creates a node, adds it to target, returns the new node. - function addNodeTo ( target, className ) { + function addNodeTo ( addTarget, className ) { var div = scope_Document.createElement('div'); @@ -965,7 +960,7 @@ function closure ( target, options, originalOptions ){ addClass(div, className); } - target.appendChild(div); + addTarget.appendChild(div); return div; } @@ -1027,24 +1022,24 @@ function closure ( target, options, originalOptions ){ } // Initialize a single slider. - function addSlider ( target ) { + function addSlider ( addTarget ) { // Apply classes and data to the target. - addClass(target, options.cssClasses.target); + addClass(addTarget, options.cssClasses.target); if ( options.dir === 0 ) { - addClass(target, options.cssClasses.ltr); + addClass(addTarget, options.cssClasses.ltr); } else { - addClass(target, options.cssClasses.rtl); + addClass(addTarget, options.cssClasses.rtl); } if ( options.ort === 0 ) { - addClass(target, options.cssClasses.horizontal); + addClass(addTarget, options.cssClasses.horizontal); } else { - addClass(target, options.cssClasses.vertical); + addClass(addTarget, options.cssClasses.vertical); } - scope_Base = addNodeTo(target, options.cssClasses.base); + scope_Base = addNodeTo(addTarget, options.cssClasses.base); } @@ -1085,15 +1080,15 @@ function closure ( target, options, originalOptions ){ bindEvent('update', function ( values, handleNumber, unencoded, tap, positions ) { // Update Aria Values for all handles, as a change in one changes min and max values for the next. - scope_HandleNumbers.forEach(function( handleNumber ){ + scope_HandleNumbers.forEach(function( index ){ - var handle = scope_Handles[handleNumber]; + var handle = scope_Handles[index]; - var min = checkHandlePosition(scope_Locations, handleNumber, 0, true, true, true); - var max = checkHandlePosition(scope_Locations, handleNumber, 100, true, true, true); + var min = checkHandlePosition(scope_Locations, index, 0, true, true, true); + var max = checkHandlePosition(scope_Locations, index, 100, true, true, true); - var now = positions[handleNumber]; - var text = options.ariaFormat.to(unencoded[handleNumber]); + var now = positions[index]; + var text = options.ariaFormat.to(unencoded[index]); handle.children[0].setAttribute('aria-valuemin', min.toFixed(1)); handle.children[0].setAttribute('aria-valuemax', max.toFixed(1)); @@ -1368,10 +1363,12 @@ function closure ( target, options, originalOptions ){ return scope_Pips; } +/*! In this file: Browser events (not slider events like slide, change); */ // Shorthand for base dimensions. function baseSize ( ) { - var rect = scope_Base.getBoundingClientRect(), alt = 'offset' + ['Width', 'Height'][options.ort]; + var rect = scope_Base.getBoundingClientRect(); + var alt = 'offset' + ['Width', 'Height'][options.ort]; return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]); } @@ -1439,7 +1436,7 @@ function closure ( target, options, originalOptions ){ } // Provide a clean event with standardized offset values. - function fixEvent ( e, pageOffset, target ) { + function fixEvent ( e, pageOffset, eventTarget ) { // Filter the event to register the type, which can be // touch, mouse or pointer. Offset changes need to be @@ -1456,14 +1453,13 @@ function closure ( target, options, originalOptions ){ pointer = true; } - // In the event that multitouch is activated, the only thing one handle should be concerned // about is the touches that originated on top of it. if ( touch ) { // Returns true if a touch originated on the target. - var isTouchOnTarget = function (touch) { - return touch.target === target || target.contains(touch.target); + var isTouchOnTarget = function (checkTouch) { + return checkTouch.target === eventTarget || eventTarget.contains(checkTouch.target); }; // In the case of touchstart events, we need to make sure there is still no more than one @@ -1515,7 +1511,7 @@ function closure ( target, options, originalOptions ){ var proposal = ( location * 100 ) / baseSize(); // Clamp proposal between 0% and 100% - // Out-of-bound coordinates may occur when .noUi-base pseudo-elements + // Out-of-bound coordinates may occur when .noUi-base pseudo-elements // are used (e.g. contained handles feature) proposal = limit(proposal); @@ -1546,92 +1542,6 @@ function closure ( target, options, originalOptions ){ return handleNumber; } - // Moves handle(s) by a percentage - // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...]) - function moveHandles ( upward, proposal, locations, handleNumbers ) { - - var proposals = locations.slice(); - - var b = [!upward, upward]; - var f = [upward, !upward]; - - // Copy handleNumbers so we don't change the dataset - handleNumbers = handleNumbers.slice(); - - // Check to see which handle is 'leading'. - // If that one can't move the second can't either. - if ( upward ) { - handleNumbers.reverse(); - } - - // Step 1: get the maximum percentage that any of the handles can move - if ( handleNumbers.length > 1 ) { - - handleNumbers.forEach(function(handleNumber, o) { - - var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false); - - // Stop if one of the handles can't move. - if ( to === false ) { - proposal = 0; - } else { - proposal = to - proposals[handleNumber]; - proposals[handleNumber] = to; - } - }); - } - - // If using one handle, check backward AND forward - else { - b = f = [true]; - } - - var state = false; - - // Step 2: Try to set the handles with the found percentage - handleNumbers.forEach(function(handleNumber, o) { - state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state; - }); - - // Step 3: If a handle moved, fire events - if ( state ) { - handleNumbers.forEach(function(handleNumber){ - fireEvent('update', handleNumber); - fireEvent('slide', handleNumber); - }); - } - } - - // External event handling - function fireEvent ( eventName, handleNumber, tap ) { - - Object.keys(scope_Events).forEach(function( targetEvent ) { - - var eventType = targetEvent.split('.')[0]; - - if ( eventName === eventType ) { - scope_Events[targetEvent].forEach(function( callback ) { - - callback.call( - // Use the slider public API as the scope ('this') - scope_Self, - // Return values as array, so arg_1[arg_2] is always valid. - scope_Values.map(options.format.to), - // Handle index, 0 or 1 - handleNumber, - // Unformatted slider values - scope_Values.slice(), - // Event is fired by tap, true or false - tap || false, - // Left offset of the handle, in relation to the slider - scope_Locations.slice() - ); - }); - } - }); - } - - // Fire 'end' when a mouse or pen leaves the document. function documentLeave ( event, data ) { if ( event.type === "mouseout" && event.target.nodeName === "HTML" && event.relatedTarget === null ){ @@ -1891,6 +1801,72 @@ function closure ( target, options, originalOptions ){ } } +/*! In this file: Slider events (not browser events); */ + + // Attach an event to this slider, possibly including a namespace + function bindEvent ( namespacedEvent, callback ) { + scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || []; + scope_Events[namespacedEvent].push(callback); + + // If the event bound is 'update,' fire it immediately for all handles. + if ( namespacedEvent.split('.')[0] === 'update' ) { + scope_Handles.forEach(function(a, index){ + fireEvent('update', index); + }); + } + } + + // Undo attachment of event + function removeEvent ( namespacedEvent ) { + + var event = namespacedEvent && namespacedEvent.split('.')[0]; + var namespace = event && namespacedEvent.substring(event.length); + + Object.keys(scope_Events).forEach(function( bind ){ + + var tEvent = bind.split('.')[0]; + var tNamespace = bind.substring(tEvent.length); + + if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) { + delete scope_Events[bind]; + } + }); + } + + // External event handling + function fireEvent ( eventName, handleNumber, tap ) { + + Object.keys(scope_Events).forEach(function( targetEvent ) { + + var eventType = targetEvent.split('.')[0]; + + if ( eventName === eventType ) { + scope_Events[targetEvent].forEach(function( callback ) { + + callback.call( + // Use the slider public API as the scope ('this') + scope_Self, + // Return values as array, so arg_1[arg_2] is always valid. + scope_Values.map(options.format.to), + // Handle index, 0 or 1 + handleNumber, + // Unformatted slider values + scope_Values.slice(), + // Event is fired by tap, true or false + tap || false, + // Left offset of the handle, in relation to the slider + scope_Locations.slice() + ); + }); + } + }); + } + +/*! In this file: Mechanics for slider operation */ + + function toPct ( pct ) { + return pct + '%'; + } // Split out the handle positioning logic so the Move event can use it, too function checkHandlePosition ( reference, handleNumber, to, lookBackward, lookForward, getValue ) { @@ -1954,8 +1930,60 @@ function closure ( target, options, originalOptions ){ return (o?a:v) + ', ' + (o?v:a); } - function toPct ( pct ) { - return pct + '%'; + // Moves handle(s) by a percentage + // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...]) + function moveHandles ( upward, proposal, locations, handleNumbers ) { + + var proposals = locations.slice(); + + var b = [!upward, upward]; + var f = [upward, !upward]; + + // Copy handleNumbers so we don't change the dataset + handleNumbers = handleNumbers.slice(); + + // Check to see which handle is 'leading'. + // If that one can't move the second can't either. + if ( upward ) { + handleNumbers.reverse(); + } + + // Step 1: get the maximum percentage that any of the handles can move + if ( handleNumbers.length > 1 ) { + + handleNumbers.forEach(function(handleNumber, o) { + + var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false); + + // Stop if one of the handles can't move. + if ( to === false ) { + proposal = 0; + } else { + proposal = to - proposals[handleNumber]; + proposals[handleNumber] = to; + } + }); + } + + // If using one handle, check backward AND forward + else { + b = f = [true]; + } + + var state = false; + + // Step 2: Try to set the handles with the found percentage + handleNumbers.forEach(function(handleNumber, o) { + state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state; + }); + + // Step 3: If a handle moved, fire events + if ( state ) { + handleNumbers.forEach(function(handleNumber){ + fireEvent('update', handleNumber); + fireEvent('slide', handleNumber); + }); + } } // Takes a base value and an offset. This offset is used for the connect bar size. @@ -1982,12 +2010,12 @@ function closure ( target, options, originalOptions ){ updateConnect(handleNumber + 1); } + // Handles before the slider middle are stacked later = higher, + // Handles after the middle later is lower + // [[7] [8] .......... | .......... [5] [4] function setZindex ( ) { scope_HandleNumbers.forEach(function(handleNumber){ - // Handles before the slider middle are stacked later = higher, - // Handles after the middle later is lower - // [[7] [8] .......... | .......... [5] [4] var dir = (scope_Locations[handleNumber] > 50 ? -1 : 1); var zIndex = 3 + (scope_Handles.length + (dir * handleNumber)); scope_Handles[handleNumber].style.zIndex = zIndex; @@ -2038,7 +2066,9 @@ function closure ( target, options, originalOptions ){ scope_Connects[index].style[options.transformRule] = translateRule + ' ' + scaleRule; } - // Parses value passed to .set method. Returns current value if not parseable. +/*! In this file: All methods eventually exposed in slider.noUiSlider... */ + + // Parses value passed to .set method. Returns current value if not parse-able. function resolveToValue ( to, handleNumber ) { // Setting with null indicates an 'ignore'. @@ -2195,36 +2225,6 @@ function closure ( target, options, originalOptions ){ }); } - // Attach an event to this slider, possibly including a namespace - function bindEvent ( namespacedEvent, callback ) { - scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || []; - scope_Events[namespacedEvent].push(callback); - - // If the event bound is 'update,' fire it immediately for all handles. - if ( namespacedEvent.split('.')[0] === 'update' ) { - scope_Handles.forEach(function(a, index){ - fireEvent('update', index); - }); - } - } - - // Undo attachment of event - function removeEvent ( namespacedEvent ) { - - var event = namespacedEvent && namespacedEvent.split('.')[0]; - var namespace = event && namespacedEvent.substring(event.length); - - Object.keys(scope_Events).forEach(function( bind ){ - - var tEvent = bind.split('.')[0], - tNamespace = bind.substring(tEvent.length); - - if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) { - delete scope_Events[bind]; - } - }); - } - // Updateable: margin, limit, padding, step, range, animate, snap function updateOptions ( optionsToUpdate, fireSetEvent ) { @@ -2268,16 +2268,19 @@ function closure ( target, options, originalOptions ){ valueSet(optionsToUpdate.start || v, fireSetEvent); } - // Throw an error if the slider was already initialized. - if ( scope_Target.noUiSlider ) { - throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized."); - } +/*! In this file: Calls to functions. All other scope_ files define functions only; */ - // Create the base element, initialise HTML and set classes. + // Create the base element, initialize HTML and set classes. // Add handles and connect elements. addSlider(scope_Target); addElements(options.connect, scope_Base); + // Attach user events. + bindSliderEvents(options.events); + + // Use the public value method to set the start values. + valueSet(options.start); + scope_Self = { destroy: destroy, steps: getCurrentStep, @@ -2295,12 +2298,6 @@ function closure ( target, options, originalOptions ){ pips: pips // Issue #594 }; - // Attach user events. - bindSliderEvents(options.events); - - // Use the public value method to set the start values. - valueSet(options.start); - if ( options.pips ) { pips(options.pips); } @@ -2323,16 +2320,21 @@ function closure ( target, options, originalOptions ){ throw new Error("noUiSlider (" + VERSION + "): create requires a single element, got: " + target); } + // Throw an error if the slider was already initialized. + if ( target.noUiSlider ) { + throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized."); + } + // Test the options and create the slider environment; var options = testOptions( originalOptions, target ); - var api = closure( target, options, originalOptions ); + var api = scope( target, options, originalOptions ); target.noUiSlider = api; return api; } - // Use an object instead of a function for future expansibility; + // Use an object instead of a function for future expandability; return { version: VERSION, create: initialize diff --git a/distribute/nouislider.min.css b/distribute/nouislider.min.css index f98e2f66..45f0eee5 100644 --- a/distribute/nouislider.min.css +++ b/distribute/nouislider.min.css @@ -1 +1 @@ -/*! nouislider - 11.0.2 - 2018-01-20 18:05:29 */.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative;direction:ltr}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;left:0;height:100%;width:100%;-webkit-transform-origin:0 0;transform-origin:0 0}html:not([dir=rtl]) .noUi-horizontal .noUi-origin{left:auto;right:0}.noUi-vertical .noUi-origin{width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{position:absolute}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px}html:not([dir=rtl]) .noUi-horizontal .noUi-handle{right:-17px;left:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-large,.noUi-marker-sub{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.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}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%,0);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.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}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%} \ No newline at end of file +/*! nouislider - 11.0.3 - 2018-01-21 14:04:07 */.noUi-target,.noUi-target *{-webkit-touch-callout:none;-webkit-tap-highlight-color:transparent;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box}.noUi-target{position:relative;direction:ltr}.noUi-base,.noUi-connects{width:100%;height:100%;position:relative;z-index:1}.noUi-connects{overflow:hidden;z-index:0}.noUi-connect,.noUi-origin{will-change:transform;position:absolute;z-index:1;top:0;left:0;height:100%;width:100%;-webkit-transform-origin:0 0;transform-origin:0 0}html:not([dir=rtl]) .noUi-horizontal .noUi-origin{left:auto;right:0}.noUi-vertical .noUi-origin{width:0}.noUi-horizontal .noUi-origin{height:0}.noUi-handle{position:absolute}.noUi-state-tap .noUi-connect,.noUi-state-tap .noUi-origin{-webkit-transition:transform .3s;transition:transform .3s}.noUi-state-drag *{cursor:inherit!important}.noUi-horizontal{height:18px}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px}.noUi-vertical{width:18px}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px}html:not([dir=rtl]) .noUi-horizontal .noUi-handle{right:-17px;left:auto}.noUi-target{background:#FAFAFA;border-radius:4px;border:1px solid #D3D3D3;box-shadow:inset 0 1px 1px #F0F0F0,0 3px 6px -5px #BBB}.noUi-connects{border-radius:3px}.noUi-connect{background:#3FB8AF}.noUi-draggable{cursor:ew-resize}.noUi-vertical .noUi-draggable{cursor:ns-resize}.noUi-handle{border:1px solid #D9D9D9;border-radius:3px;background:#FFF;cursor:default;box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #EBEBEB,0 3px 6px -3px #BBB}.noUi-active{box-shadow:inset 0 0 1px #FFF,inset 0 1px 7px #DDD,0 3px 6px -3px #BBB}.noUi-handle:after,.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#E8E7E6;left:14px;top:6px}.noUi-handle:after{left:17px}.noUi-vertical .noUi-handle:after,.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px}.noUi-vertical .noUi-handle:after{top:17px}[disabled] .noUi-connect{background:#B8B8B8}[disabled] .noUi-handle,[disabled].noUi-handle,[disabled].noUi-target{cursor:not-allowed}.noUi-pips,.noUi-pips *{-moz-box-sizing:border-box;box-sizing:border-box}.noUi-pips{position:absolute;color:#999}.noUi-value{position:absolute;white-space:nowrap;text-align:center}.noUi-value-sub{color:#ccc;font-size:10px}.noUi-marker{position:absolute;background:#CCC}.noUi-marker-large,.noUi-marker-sub{background:#AAA}.noUi-pips-horizontal{padding:10px 0;height:80px;top:100%;left:0;width:100%}.noUi-value-horizontal{-webkit-transform:translate(-50%,50%);transform:translate(-50%,50%)}.noUi-rtl .noUi-value-horizontal{-webkit-transform:translate(50%,50%);transform:translate(50%,50%)}.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}.noUi-pips-vertical{padding:0 10px;height:100%;top:0;left:100%}.noUi-value-vertical{-webkit-transform:translate(0,-50%);transform:translate(0,-50%,0);padding-left:25px}.noUi-rtl .noUi-value-vertical{-webkit-transform:translate(0,50%);transform:translate(0,50%)}.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}.noUi-tooltip{display:block;position:absolute;border:1px solid #D9D9D9;border-radius:3px;background:#fff;color:#000;padding:5px;text-align:center;white-space:nowrap}.noUi-horizontal .noUi-tooltip{-webkit-transform:translate(-50%,0);transform:translate(-50%,0);left:50%;bottom:120%}.noUi-vertical .noUi-tooltip{-webkit-transform:translate(0,-50%);transform:translate(0,-50%);top:50%;right:120%} \ No newline at end of file diff --git a/distribute/nouislider.min.js b/distribute/nouislider.min.js index 1c81c147..f40c4358 100644 --- a/distribute/nouislider.min.js +++ b/distribute/nouislider.min.js @@ -1,3 +1,3 @@ -/*! nouislider - 11.0.2 - 2018-01-20 18:05:29 */ +/*! nouislider - 11.0.3 - 2018-01-21 14:04:07 */ -!function(a){"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?module.exports=a():window.noUiSlider=a()}(function(){"use strict";function a(a){return"object"==typeof a&&"function"==typeof a.to&&"function"==typeof a.from}function b(a){a.parentElement.removeChild(a)}function c(a){a.preventDefault()}function d(a){return a.filter(function(a){return!this[a]&&(this[a]=!0)},{})}function e(a,b){return Math.round(a/b)*b}function f(a,b){var c=a.getBoundingClientRect(),d=a.ownerDocument,e=d.documentElement,f=o(d);return/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)&&(f.x=0),b?c.top+f.y-e.clientTop:c.left+f.x-e.clientLeft}function g(a){return"number"==typeof a&&!isNaN(a)&&isFinite(a)}function h(a,b,c){c>0&&(l(a,b),setTimeout(function(){m(a,b)},c))}function i(a){return Math.max(Math.min(a,100),0)}function j(a){return Array.isArray(a)?a:[a]}function k(a){a=String(a);var b=a.split(".");return b.length>1?b[1].length:0}function l(a,b){a.classList?a.classList.add(b):a.className+=" "+b}function m(a,b){a.classList?a.classList.remove(b):a.className=a.className.replace(new RegExp("(^|\\b)"+b.split(" ").join("|")+"(\\b|$)","gi")," ")}function n(a,b){return a.classList?a.classList.contains(b):new RegExp("\\b"+b+"\\b").test(a.className)}function o(a){var b=void 0!==window.pageXOffset,c="CSS1Compat"===(a.compatMode||"");return{x:b?window.pageXOffset:c?a.documentElement.scrollLeft:a.body.scrollLeft,y:b?window.pageYOffset:c?a.documentElement.scrollTop:a.body.scrollTop}}function p(){return 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"}}function q(){var a=!1;try{var b=Object.defineProperty({},"passive",{get:function(){a=!0}});window.addEventListener("test",null,b)}catch(a){}return a}function r(){return window.CSS&&CSS.supports&&CSS.supports("touch-action","none")}function s(a,b){return 100/(b-a)}function t(a,b){return 100*b/(a[1]-a[0])}function u(a,b){return t(a,a[0]<0?b+Math.abs(a[0]):b-a[0])}function v(a,b){return b*(a[1]-a[0])/100+a[0]}function w(a,b){for(var c=1;a>=b[c];)c+=1;return c}function x(a,b,c){if(c>=a.slice(-1)[0])return 100;var d,e,f,g,h=w(c,a);return d=a[h-1],e=a[h],f=b[h-1],g=b[h],f+u([d,e],c)/s(f,g)}function y(a,b,c){if(c>=100)return a.slice(-1)[0];var d,e,f,g,h=w(c,b);return d=a[h-1],e=a[h],f=b[h-1],g=b[h],v([d,e],(c-f)*s(f,g))}function z(a,b,c,d){if(100===d)return d;var f,g,h=w(d,a);return c?(f=a[h-1],g=a[h],d-f>(g-f)/2?g:f):b[h-1]?a[h-1]+e(d-a[h-1],b[h-1]):d}function A(a,b,c){var d;if("number"==typeof b&&(b=[b]),!Array.isArray(b))throw new Error("noUiSlider ("+Z+"): 'range' contains invalid value.");if(d="min"===a?0:"max"===a?100:parseFloat(a),!g(d)||!g(b[0]))throw new Error("noUiSlider ("+Z+"): 'range' value isn't numeric.");c.xPct.push(d),c.xVal.push(b[0]),d?c.xSteps.push(!isNaN(b[1])&&b[1]):isNaN(b[1])||(c.xSteps[0]=b[1]),c.xHighestCompleteStep.push(0)}function B(a,b,c){if(!b)return!0;c.xSteps[a]=t([c.xVal[a],c.xVal[a+1]],b)/s(c.xPct[a],c.xPct[a+1]);var d=(c.xVal[a+1]-c.xVal[a])/c.xNumSteps[a],e=Math.ceil(Number(d.toFixed(3))-1),f=c.xVal[a]+c.xNumSteps[a]*e;c.xHighestCompleteStep[a]=f}function C(a,b,c){this.xPct=[],this.xVal=[],this.xSteps=[c||!1],this.xNumSteps=[!1],this.xHighestCompleteStep=[],this.snap=b;var d,e=[];for(d in a)a.hasOwnProperty(d)&&e.push([a[d],d]);for(e.length&&"object"==typeof e[0][0]?e.sort(function(a,b){return a[0][0]-b[0][0]}):e.sort(function(a,b){return a[0]-b[0]}),d=0;d=50||a.padding[1]>=50)throw new Error("noUiSlider ("+Z+"): 'padding' option must be less than half the range.")}}function P(a,b){switch(b){case"ltr":a.dir=0;break;case"rtl":a.dir=1;break;default:throw new Error("noUiSlider ("+Z+"): 'direction' option was not recognized.")}}function Q(a,b){if("string"!=typeof b)throw new Error("noUiSlider ("+Z+"): 'behaviour' must be a string containing options.");var c=b.indexOf("tap")>=0,d=b.indexOf("drag")>=0,e=b.indexOf("fixed")>=0,f=b.indexOf("snap")>=0,g=b.indexOf("hover")>=0;if(e){if(2!==a.handles)throw new Error("noUiSlider ("+Z+"): 'fixed' behaviour must be used with 2 handles");M(a,a.start[1]-a.start[0])}a.events={tap:c||f,drag:d,fixed:e,snap:f,hover:g}}function R(a,b){if(!1!==b)if(!0===b){a.tooltips=[];for(var c=0;c= 2) required for mode 'count'.");var d=b-1,e=100/d;for(b=[];d--;)b[d]=d*e;b.push(100),a="positions"}return"positions"===a?b.map(function(a){return va.fromStepping(c?va.getStep(a):a)}):"values"===a?c?b.map(function(a){return va.fromStepping(va.getStep(va.toStepping(a)))}):b:void 0}function A(a,b,c){function e(a,b){return(a+b).toFixed(7)/1}var f={},g=va.xVal[0],h=va.xVal[va.xVal.length-1],i=!1,j=!1,k=0;return c=d(c.slice().sort(function(a,b){return a-b})),c[0]!==g&&(c.unshift(g),i=!0),c[c.length-1]!==h&&(c.push(h),j=!0),c.forEach(function(d,g){var h,l,m,n,o,p,q,r,s,t,u=d,v=c[g+1];if("steps"===b&&(h=va.xNumSteps[g]),h||(h=v-u),!1!==u&&void 0!==v)for(h=Math.max(h,1e-7),l=u;l<=v;l=e(l,h)){for(n=va.toStepping(l),o=n-k,r=o/a,s=Math.round(r),t=o/s,m=1;m<=s;m+=1)p=k+m*t,f[p.toFixed(5)]=["x",0];q=c.indexOf(l)>-1?1:"steps"===b?2:0,!g&&i&&(q=0),l===v&&j||(f[n.toFixed(5)]=[l,q]),k=n}}),f}function B(a,b,c){function d(a,b){var c=b===e.cssClasses.value,d=c?j:m,f=c?h:i;return b+" "+d[e.ort]+" "+f[a]}function f(a,f){f[1]=f[1]&&b?b(f[0],f[1]):f[1];var h=k(g,!1);h.className=d(f[1],e.cssClasses.marker),h.style[e.style]=a+"%",f[1]&&(h=k(g,!1),h.className=d(f[1],e.cssClasses.value),h.setAttribute("data-value",f[0]),h.style[e.style]=a+"%",h.innerText=c.to(f[0]))}var g=ya.createElement("div"),h=[e.cssClasses.valueNormal,e.cssClasses.valueLarge,e.cssClasses.valueSub],i=[e.cssClasses.markerNormal,e.cssClasses.markerLarge,e.cssClasses.markerSub],j=[e.cssClasses.valueHorizontal,e.cssClasses.valueVertical],m=[e.cssClasses.markerHorizontal,e.cssClasses.markerVertical];return l(g,e.cssClasses.pips),l(g,0===e.ort?e.cssClasses.pipsHorizontal:e.cssClasses.pipsVertical),Object.keys(a).forEach(function(b){f(b,a[b])}),g}function C(){na&&(b(na),na=null)}function D(a){C();var b=a.mode,c=a.density||1,d=a.filter||!1,e=a.values||!1,f=a.stepped||!1,g=z(b,e,f),h=A(c,b,g),i=a.format||{to:Math.round};return na=ra.appendChild(B(h,d,i))}function E(){var a=ja.getBoundingClientRect(),b="offset"+["Width","Height"][e.ort];return 0===e.ort?a.width||ja[b]:a.height||ja[b]}function F(a,b,c,d){var f=function(f){return!!(f=G(f,d.pageOffset,d.target||b))&&(!(ra.hasAttribute("disabled")&&!d.doNotReject)&&(!(n(ra,e.cssClasses.tap)&&!d.doNotReject)&&(!(a===oa.start&&void 0!==f.buttons&&f.buttons>1)&&((!d.hover||!f.buttons)&&(qa||f.preventDefault(),f.calcPoint=f.points[e.ort],void c(f,d))))))},g=[];return a.split(" ").forEach(function(a){b.addEventListener(a,f,!!qa&&{passive:!0}),g.push([a,f])}),g}function G(a,b,c){var d,e,f=0===a.type.indexOf("touch"),g=0===a.type.indexOf("mouse"),h=0===a.type.indexOf("pointer");if(0===a.type.indexOf("MSPointer")&&(h=!0),f){var i=function(a){return a.target===c||c.contains(a.target)};if("touchstart"===a.type){var j=Array.prototype.filter.call(a.touches,i);if(j.length>1)return!1;d=j[0].pageX,e=j[0].pageY}else{var k=Array.prototype.find.call(a.changedTouches,i);if(!k)return!1;d=k.pageX,e=k.pageY}}return b=b||o(ya),(g||h)&&(d=a.clientX+b.x,e=a.clientY+b.y),a.pageOffset=b,a.points=[d,e],a.cursor=g||h,a}function H(a){var b=a-f(ja,e.ort),c=100*b/E();return c=i(c),e.dir?100-c:c}function I(a){var b=100,c=!1;return ka.forEach(function(d,e){if(!d.hasAttribute("disabled")){var f=Math.abs(sa[e]-a);(f1?d.forEach(function(a,c){var d=S(e,a,e[a]+b,f[c],g[c],!1);!1===d?b=0:(b=d-e[a],e[a]=d)}):f=g=[!0];var h=!1;d.forEach(function(a,d){h=$(a,c[a]+b,f[d],g[d])||h}),h&&d.forEach(function(a){K("update",a),K("slide",a)})}function K(a,b,c){Object.keys(xa).forEach(function(d){var f=d.split(".")[0];a===f&&xa[d].forEach(function(a){a.call(ma,wa.map(e.format.to),b,wa.slice(),c||!1,sa.slice())})})}function L(a,b){"mouseout"===a.type&&"HTML"===a.target.nodeName&&null===a.relatedTarget&&N(a,b)}function M(a,b){if(-1===navigator.appVersion.indexOf("MSIE 9")&&0===a.buttons&&0!==b.buttonsProperty)return N(a,b);var c=(e.dir?-1:1)*(a.calcPoint-b.startCalcPoint);J(c>0,100*c/b.baseSize,b.locations,b.handleNumbers)}function N(a,b){b.handle&&(m(b.handle,e.cssClasses.active),ua-=1),b.listeners.forEach(function(a){za.removeEventListener(a[0],a[1])}),0===ua&&(m(ra,e.cssClasses.drag),Y(),a.cursor&&(Aa.style.cursor="",Aa.removeEventListener("selectstart",c))),b.handleNumbers.forEach(function(a){K("change",a),K("set",a),K("end",a)})}function O(a,b){var d;if(1===b.handleNumbers.length){var f=ka[b.handleNumbers[0]];if(f.hasAttribute("disabled"))return!1;d=f.children[0],ua+=1,l(d,e.cssClasses.active)}a.stopPropagation();var g=[],h=F(oa.move,za,M,{target:a.target,handle:d,listeners:g,startCalcPoint:a.calcPoint,baseSize:E(),pageOffset:a.pageOffset,handleNumbers:b.handleNumbers,buttonsProperty:a.buttons,locations:sa.slice()}),i=F(oa.end,za,N,{target:a.target,handle:d,listeners:g,doNotReject:!0,handleNumbers:b.handleNumbers}),j=F("mouseout",za,L,{target:a.target,handle:d,listeners:g,doNotReject:!0,handleNumbers:b.handleNumbers});g.push.apply(g,h.concat(i,j)),a.cursor&&(Aa.style.cursor=getComputedStyle(a.target).cursor,ka.length>1&&l(ra,e.cssClasses.drag),Aa.addEventListener("selectstart",c,!1)),b.handleNumbers.forEach(function(a){K("start",a)})}function P(a){a.stopPropagation();var b=H(a.calcPoint),c=I(b);if(!1===c)return!1;e.events.snap||h(ra,e.cssClasses.tap,e.animationDuration),$(c,b,!0,!0),Y(),K("slide",c,!0),K("update",c,!0),K("change",c,!0),K("set",c,!0),e.events.snap&&O(a,{handleNumbers:[c]})}function Q(a){var b=H(a.calcPoint),c=va.getStep(b),d=va.fromStepping(c);Object.keys(xa).forEach(function(a){"hover"===a.split(".")[0]&&xa[a].forEach(function(a){a.call(ma,d)})})}function R(a){a.fixed||ka.forEach(function(a,b){F(oa.start,a.children[0],O,{handleNumbers:[b]})}),a.tap&&F(oa.start,ja,P,{}),a.hover&&F(oa.move,ja,Q,{hover:!0}),a.drag&&la.forEach(function(b,c){if(!1!==b&&0!==c&&c!==la.length-1){var d=ka[c-1],f=ka[c],g=[b];l(b,e.cssClasses.draggable),a.fixed&&(g.push(d.children[0]),g.push(f.children[0])),g.forEach(function(a){F(oa.start,a,O,{handles:[d,f],handleNumbers:[c-1,c]})})}})}function S(a,b,c,d,f,g){return ka.length>1&&(d&&b>0&&(c=Math.max(c,a[b-1]+e.margin)),f&&b1&&e.limit&&(d&&b>0&&(c=Math.min(c,a[b-1]+e.limit)),f&&b50?-1:1,c=3+(ka.length+b*a);ka[a].style.zIndex=c})}function $(a,b,c,d){return!1!==(b=S(sa,a,b,c,d,!1))&&(X(a,b),!0)}function _(a){if(la[a]){var b=0,c=100;0!==a&&(b=sa[a-1]),a!==la.length-1&&(c=sa[a]);var d=c-b,f="translate("+T(U(V(b,d)),"0")+")",g="scale("+T(d/100,"1")+")";la[a].style[e.transformRule]=f+" "+g}}function aa(a,b){return null===a||!1===a||void 0===a?sa[b]:("number"==typeof a&&(a=String(a)),a=e.format.from(a),a=va.toStepping(a),!1===a||isNaN(a)?sa[b]:a)}function ba(a,b){var c=j(a),d=void 0===sa[0];b=void 0===b||!!b,e.animate&&!d&&h(ra,e.cssClasses.tap,e.animationDuration),ta.forEach(function(a){$(a,aa(c[a],a),!0,!1)}),ta.forEach(function(a){$(a,sa[a],!0,!0)}),Y(),ta.forEach(function(a){K("update",a),null!==c[a]&&b&&K("set",a)})}function ca(a){ba(e.start,a)}function da(){var a=wa.map(e.format.to);return 1===a.length?a[0]:a}function ea(){for(var a in e.cssClasses)e.cssClasses.hasOwnProperty(a)&&m(ra,e.cssClasses[a]);for(;ra.firstChild;)ra.removeChild(ra.firstChild);delete ra.noUiSlider}function fa(){return sa.map(function(a,b){var c=va.getNearbySteps(a),d=wa[b],e=c.thisStep.step,f=null;!1!==e&&d+e>c.stepAfter.startValue&&(e=c.stepAfter.startValue-d),f=d>c.thisStep.startValue?c.thisStep.step:!1!==c.stepBefore.step&&d-c.stepBefore.highestStep,100===a?e=null:0===a&&(f=null);var g=va.countStepDecimals();return null!==e&&!1!==e&&(e=Number(e.toFixed(g))),null!==f&&!1!==f&&(f=Number(f.toFixed(g))),[f,e]})}function ga(a,b){xa[a]=xa[a]||[],xa[a].push(b),"update"===a.split(".")[0]&&ka.forEach(function(a,b){K("update",b)})}function ha(a){var b=a&&a.split(".")[0],c=b&&a.substring(b.length);Object.keys(xa).forEach(function(a){var d=a.split(".")[0],e=a.substring(d.length);b&&b!==d||c&&c!==e||delete xa[a]})}function ia(a,b){var c=da(),d=["margin","limit","padding","range","animate","snap","step","format"];d.forEach(function(b){void 0!==a[b]&&(g[b]=a[b])});var f=W(g);d.forEach(function(b){void 0!==a[b]&&(e[b]=f[b])}),va=f.spectrum,e.margin=f.margin,e.limit=f.limit,e.padding=f.padding,e.pips&&D(e.pips),sa=[],ba(a.start||c,b)}var ja,ka,la,ma,na,oa=p(),pa=r(),qa=pa&&q(),ra=a,sa=[],ta=[],ua=0,va=e.spectrum,wa=[],xa={},ya=a.ownerDocument,za=ya.documentElement,Aa=ya.body,Ba="rtl"===ya.dir||1===e.ort?0:100;if(ra.noUiSlider)throw new Error("noUiSlider ("+Z+"): Slider was already initialized.");return v(ra),u(e.connect,ja),ma={destroy:ea,steps:fa,on:ga,off:ha,get:da,set:ba,reset:ca,__moveHandles:function(a,b,c){J(a,b,sa,c)},options:g,updateOptions:ia,target:ra,removePips:C,pips:D},R(e.events),ba(e.start),e.pips&&D(e.pips),e.tooltips&&x(),y(),ma}function Y(a,b){if(!a||!a.nodeName)throw new Error("noUiSlider ("+Z+"): create requires a single element, got: "+a);var c=W(b,a),d=X(a,c,b);return a.noUiSlider=d,d}var Z="11.0.2";C.prototype.getMargin=function(a){var b=this.xNumSteps[0];if(b&&a/b%1!=0)throw new Error("noUiSlider ("+Z+"): 'limit', 'margin' and 'padding' must be divisible by step.");return 2===this.xPct.length&&t(this.xVal,a)},C.prototype.toStepping=function(a){return a=x(this.xVal,this.xPct,a)},C.prototype.fromStepping=function(a){return y(this.xVal,this.xPct,a)},C.prototype.getStep=function(a){return a=z(this.xPct,this.xSteps,this.snap,a)},C.prototype.getNearbySteps=function(a){var b=w(a,this.xPct);return{stepBefore:{startValue:this.xVal[b-2],step:this.xNumSteps[b-2],highestStep:this.xHighestCompleteStep[b-2]},thisStep:{startValue:this.xVal[b-1],step:this.xNumSteps[b-1],highestStep:this.xHighestCompleteStep[b-1]},stepAfter:{startValue:this.xVal[b-0],step:this.xNumSteps[b-0],highestStep:this.xHighestCompleteStep[b-0]}}},C.prototype.countStepDecimals=function(){var a=this.xNumSteps.map(k);return Math.max.apply(null,a)},C.prototype.convert=function(a){return this.getStep(this.toStepping(a))};var $={to:function(a){return void 0!==a&&a.toFixed(2)},from:Number};return{version:Z,create:Y}}); \ No newline at end of file +!function(a){"function"==typeof define&&define.amd?define([],a):"object"==typeof exports?module.exports=a():window.noUiSlider=a()}(function(){"use strict";function a(a){return"object"==typeof a&&"function"==typeof a.to&&"function"==typeof a.from}function b(a){a.parentElement.removeChild(a)}function c(a){a.preventDefault()}function d(a){return a.filter(function(a){return!this[a]&&(this[a]=!0)},{})}function e(a,b){return Math.round(a/b)*b}function f(a,b){var c=a.getBoundingClientRect(),d=a.ownerDocument,e=d.documentElement,f=o(d);return/webkit.*Chrome.*Mobile/i.test(navigator.userAgent)&&(f.x=0),b?c.top+f.y-e.clientTop:c.left+f.x-e.clientLeft}function g(a){return"number"==typeof a&&!isNaN(a)&&isFinite(a)}function h(a,b,c){c>0&&(l(a,b),setTimeout(function(){m(a,b)},c))}function i(a){return Math.max(Math.min(a,100),0)}function j(a){return Array.isArray(a)?a:[a]}function k(a){a=String(a);var b=a.split(".");return b.length>1?b[1].length:0}function l(a,b){a.classList?a.classList.add(b):a.className+=" "+b}function m(a,b){a.classList?a.classList.remove(b):a.className=a.className.replace(new RegExp("(^|\\b)"+b.split(" ").join("|")+"(\\b|$)","gi")," ")}function n(a,b){return a.classList?a.classList.contains(b):new RegExp("\\b"+b+"\\b").test(a.className)}function o(a){var b=void 0!==window.pageXOffset,c="CSS1Compat"===(a.compatMode||"");return{x:b?window.pageXOffset:c?a.documentElement.scrollLeft:a.body.scrollLeft,y:b?window.pageYOffset:c?a.documentElement.scrollTop:a.body.scrollTop}}function p(){return 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"}}function q(){var a=!1;try{var b=Object.defineProperty({},"passive",{get:function(){a=!0}});window.addEventListener("test",null,b)}catch(a){}return a}function r(){return window.CSS&&CSS.supports&&CSS.supports("touch-action","none")}function s(a,b){return 100/(b-a)}function t(a,b){return 100*b/(a[1]-a[0])}function u(a,b){return t(a,a[0]<0?b+Math.abs(a[0]):b-a[0])}function v(a,b){return b*(a[1]-a[0])/100+a[0]}function w(a,b){for(var c=1;a>=b[c];)c+=1;return c}function x(a,b,c){if(c>=a.slice(-1)[0])return 100;var d=w(c,a),e=a[d-1],f=a[d],g=b[d-1],h=b[d];return g+u([e,f],c)/s(g,h)}function y(a,b,c){if(c>=100)return a.slice(-1)[0];var d=w(c,b),e=a[d-1],f=a[d],g=b[d-1];return v([e,f],(c-g)*s(g,b[d]))}function z(a,b,c,d){if(100===d)return d;var f=w(d,a),g=a[f-1],h=a[f];return c?d-g>(h-g)/2?h:g:b[f-1]?a[f-1]+e(d-a[f-1],b[f-1]):d}function A(a,b,c){var d;if("number"==typeof b&&(b=[b]),!Array.isArray(b))throw new Error("noUiSlider ("+Z+"): 'range' contains invalid value.");if(d="min"===a?0:"max"===a?100:parseFloat(a),!g(d)||!g(b[0]))throw new Error("noUiSlider ("+Z+"): 'range' value isn't numeric.");c.xPct.push(d),c.xVal.push(b[0]),d?c.xSteps.push(!isNaN(b[1])&&b[1]):isNaN(b[1])||(c.xSteps[0]=b[1]),c.xHighestCompleteStep.push(0)}function B(a,b,c){if(!b)return!0;c.xSteps[a]=t([c.xVal[a],c.xVal[a+1]],b)/s(c.xPct[a],c.xPct[a+1]);var d=(c.xVal[a+1]-c.xVal[a])/c.xNumSteps[a],e=Math.ceil(Number(d.toFixed(3))-1),f=c.xVal[a]+c.xNumSteps[a]*e;c.xHighestCompleteStep[a]=f}function C(a,b,c){this.xPct=[],this.xVal=[],this.xSteps=[c||!1],this.xNumSteps=[!1],this.xHighestCompleteStep=[],this.snap=b;var d,e=[];for(d in a)a.hasOwnProperty(d)&&e.push([a[d],d]);for(e.length&&"object"==typeof e[0][0]?e.sort(function(a,b){return a[0][0]-b[0][0]}):e.sort(function(a,b){return a[0]-b[0]}),d=0;d=50||a.padding[1]>=50)throw new Error("noUiSlider ("+Z+"): 'padding' option must be less than half the range.")}}function P(a,b){switch(b){case"ltr":a.dir=0;break;case"rtl":a.dir=1;break;default:throw new Error("noUiSlider ("+Z+"): 'direction' option was not recognized.")}}function Q(a,b){if("string"!=typeof b)throw new Error("noUiSlider ("+Z+"): 'behaviour' must be a string containing options.");var c=b.indexOf("tap")>=0,d=b.indexOf("drag")>=0,e=b.indexOf("fixed")>=0,f=b.indexOf("snap")>=0,g=b.indexOf("hover")>=0;if(e){if(2!==a.handles)throw new Error("noUiSlider ("+Z+"): 'fixed' behaviour must be used with 2 handles");M(a,a.start[1]-a.start[0])}a.events={tap:c||f,drag:d,fixed:e,snap:f,hover:g}}function R(a,b){if(!1!==b)if(!0===b){a.tooltips=[];for(var c=0;c= 2) required for mode 'count'.");var d=b-1,e=100/d;for(b=[];d--;)b[d]=d*e;b.push(100),a="positions"}return"positions"===a?b.map(function(a){return va.fromStepping(c?va.getStep(a):a)}):"values"===a?c?b.map(function(a){return va.fromStepping(va.getStep(va.toStepping(a)))}):b:void 0}function A(a,b,c){function e(a,b){return(a+b).toFixed(7)/1}var f={},g=va.xVal[0],h=va.xVal[va.xVal.length-1],i=!1,j=!1,k=0;return c=d(c.slice().sort(function(a,b){return a-b})),c[0]!==g&&(c.unshift(g),i=!0),c[c.length-1]!==h&&(c.push(h),j=!0),c.forEach(function(d,g){var h,l,m,n,o,p,q,r,s,t,u=d,v=c[g+1];if("steps"===b&&(h=va.xNumSteps[g]),h||(h=v-u),!1!==u&&void 0!==v)for(h=Math.max(h,1e-7),l=u;l<=v;l=e(l,h)){for(n=va.toStepping(l),o=n-k,r=o/a,s=Math.round(r),t=o/s,m=1;m<=s;m+=1)p=k+m*t,f[p.toFixed(5)]=["x",0];q=c.indexOf(l)>-1?1:"steps"===b?2:0,!g&&i&&(q=0),l===v&&j||(f[n.toFixed(5)]=[l,q]),k=n}}),f}function B(a,b,c){function d(a,b){var c=b===e.cssClasses.value,d=c?j:m,f=c?h:i;return b+" "+d[e.ort]+" "+f[a]}function f(a,f){f[1]=f[1]&&b?b(f[0],f[1]):f[1];var h=k(g,!1);h.className=d(f[1],e.cssClasses.marker),h.style[e.style]=a+"%",f[1]&&(h=k(g,!1),h.className=d(f[1],e.cssClasses.value),h.setAttribute("data-value",f[0]),h.style[e.style]=a+"%",h.innerText=c.to(f[0]))}var g=ya.createElement("div"),h=[e.cssClasses.valueNormal,e.cssClasses.valueLarge,e.cssClasses.valueSub],i=[e.cssClasses.markerNormal,e.cssClasses.markerLarge,e.cssClasses.markerSub],j=[e.cssClasses.valueHorizontal,e.cssClasses.valueVertical],m=[e.cssClasses.markerHorizontal,e.cssClasses.markerVertical];return l(g,e.cssClasses.pips),l(g,0===e.ort?e.cssClasses.pipsHorizontal:e.cssClasses.pipsVertical),Object.keys(a).forEach(function(b){f(b,a[b])}),g}function C(){na&&(b(na),na=null)}function D(a){C();var b=a.mode,c=a.density||1,d=a.filter||!1,e=a.values||!1,f=a.stepped||!1,g=z(b,e,f),h=A(c,b,g),i=a.format||{to:Math.round};return na=ra.appendChild(B(h,d,i))}function E(){var a=ja.getBoundingClientRect(),b="offset"+["Width","Height"][e.ort];return 0===e.ort?a.width||ja[b]:a.height||ja[b]}function F(a,b,c,d){var f=function(f){return!!(f=G(f,d.pageOffset,d.target||b))&&(!(ra.hasAttribute("disabled")&&!d.doNotReject)&&(!(n(ra,e.cssClasses.tap)&&!d.doNotReject)&&(!(a===oa.start&&void 0!==f.buttons&&f.buttons>1)&&((!d.hover||!f.buttons)&&(qa||f.preventDefault(),f.calcPoint=f.points[e.ort],void c(f,d))))))},g=[];return a.split(" ").forEach(function(a){b.addEventListener(a,f,!!qa&&{passive:!0}),g.push([a,f])}),g}function G(a,b,c){var d,e,f=0===a.type.indexOf("touch"),g=0===a.type.indexOf("mouse"),h=0===a.type.indexOf("pointer");if(0===a.type.indexOf("MSPointer")&&(h=!0),f){var i=function(a){return a.target===c||c.contains(a.target)};if("touchstart"===a.type){var j=Array.prototype.filter.call(a.touches,i);if(j.length>1)return!1;d=j[0].pageX,e=j[0].pageY}else{var k=Array.prototype.find.call(a.changedTouches,i);if(!k)return!1;d=k.pageX,e=k.pageY}}return b=b||o(ya),(g||h)&&(d=a.clientX+b.x,e=a.clientY+b.y),a.pageOffset=b,a.points=[d,e],a.cursor=g||h,a}function H(a){var b=a-f(ja,e.ort),c=100*b/E();return c=i(c),e.dir?100-c:c}function I(a){var b=100,c=!1;return ka.forEach(function(d,e){if(!d.hasAttribute("disabled")){var f=Math.abs(sa[e]-a);(f0,100*c/b.baseSize,b.locations,b.handleNumbers)}function L(a,b){b.handle&&(m(b.handle,e.cssClasses.active),ua-=1),b.listeners.forEach(function(a){za.removeEventListener(a[0],a[1])}),0===ua&&(m(ra,e.cssClasses.drag),_(),a.cursor&&(Aa.style.cursor="",Aa.removeEventListener("selectstart",c))),b.handleNumbers.forEach(function(a){S("change",a),S("set",a),S("end",a)})}function M(a,b){var d;if(1===b.handleNumbers.length){var f=ka[b.handleNumbers[0]];if(f.hasAttribute("disabled"))return!1;d=f.children[0],ua+=1,l(d,e.cssClasses.active)}a.stopPropagation();var g=[],h=F(oa.move,za,K,{target:a.target,handle:d,listeners:g,startCalcPoint:a.calcPoint,baseSize:E(),pageOffset:a.pageOffset,handleNumbers:b.handleNumbers,buttonsProperty:a.buttons,locations:sa.slice()}),i=F(oa.end,za,L,{target:a.target,handle:d,listeners:g,doNotReject:!0,handleNumbers:b.handleNumbers}),j=F("mouseout",za,J,{target:a.target,handle:d,listeners:g,doNotReject:!0,handleNumbers:b.handleNumbers});g.push.apply(g,h.concat(i,j)),a.cursor&&(Aa.style.cursor=getComputedStyle(a.target).cursor,ka.length>1&&l(ra,e.cssClasses.drag),Aa.addEventListener("selectstart",c,!1)),b.handleNumbers.forEach(function(a){S("start",a)})}function N(a){a.stopPropagation();var b=H(a.calcPoint),c=I(b);if(!1===c)return!1;e.events.snap||h(ra,e.cssClasses.tap,e.animationDuration),aa(c,b,!0,!0),_(),S("slide",c,!0),S("update",c,!0),S("change",c,!0),S("set",c,!0),e.events.snap&&M(a,{handleNumbers:[c]})}function O(a){var b=H(a.calcPoint),c=va.getStep(b),d=va.fromStepping(c);Object.keys(xa).forEach(function(a){"hover"===a.split(".")[0]&&xa[a].forEach(function(a){a.call(ma,d)})})}function P(a){a.fixed||ka.forEach(function(a,b){F(oa.start,a.children[0],M,{handleNumbers:[b]})}),a.tap&&F(oa.start,ja,N,{}),a.hover&&F(oa.move,ja,O,{hover:!0}),a.drag&&la.forEach(function(b,c){if(!1!==b&&0!==c&&c!==la.length-1){var d=ka[c-1],f=ka[c],g=[b];l(b,e.cssClasses.draggable),a.fixed&&(g.push(d.children[0]),g.push(f.children[0])),g.forEach(function(a){F(oa.start,a,M,{handles:[d,f],handleNumbers:[c-1,c]})})}})}function Q(a,b){xa[a]=xa[a]||[],xa[a].push(b),"update"===a.split(".")[0]&&ka.forEach(function(a,b){S("update",b)})}function R(a){var b=a&&a.split(".")[0],c=b&&a.substring(b.length);Object.keys(xa).forEach(function(a){var d=a.split(".")[0],e=a.substring(d.length);b&&b!==d||c&&c!==e||delete xa[a]})}function S(a,b,c){Object.keys(xa).forEach(function(d){var f=d.split(".")[0];a===f&&xa[d].forEach(function(a){a.call(ma,wa.map(e.format.to),b,wa.slice(),c||!1,sa.slice())})})}function T(a){return a+"%"}function U(a,b,c,d,f,g){return ka.length>1&&(d&&b>0&&(c=Math.max(c,a[b-1]+e.margin)),f&&b1&&e.limit&&(d&&b>0&&(c=Math.min(c,a[b-1]+e.limit)),f&&b1?d.forEach(function(a,c){var d=U(e,a,e[a]+b,f[c],g[c],!1);!1===d?b=0:(b=d-e[a],e[a]=d)}):f=g=[!0];var h=!1;d.forEach(function(a,d){h=aa(a,c[a]+b,f[d],g[d])||h}),h&&d.forEach(function(a){S("update",a),S("slide",a)})}function Y(a,b){return e.dir?100-a-b:a}function $(a,b){sa[a]=b,wa[a]=va.fromStepping(b);var c="translate("+V(T(Y(b,0)-Ba),"0")+")";ka[a].style[e.transformRule]=c,ba(a),ba(a+1)}function _(){ta.forEach(function(a){var b=sa[a]>50?-1:1,c=3+(ka.length+b*a);ka[a].style.zIndex=c})}function aa(a,b,c,d){return!1!==(b=U(sa,a,b,c,d,!1))&&($(a,b),!0)}function ba(a){if(la[a]){var b=0,c=100;0!==a&&(b=sa[a-1]),a!==la.length-1&&(c=sa[a]);var d=c-b,f="translate("+V(T(Y(b,d)),"0")+")",g="scale("+V(d/100,"1")+")";la[a].style[e.transformRule]=f+" "+g}}function ca(a,b){return null===a||!1===a||void 0===a?sa[b]:("number"==typeof a&&(a=String(a)),a=e.format.from(a),a=va.toStepping(a),!1===a||isNaN(a)?sa[b]:a)}function da(a,b){var c=j(a),d=void 0===sa[0];b=void 0===b||!!b,e.animate&&!d&&h(ra,e.cssClasses.tap,e.animationDuration),ta.forEach(function(a){aa(a,ca(c[a],a),!0,!1)}),ta.forEach(function(a){aa(a,sa[a],!0,!0)}),_(),ta.forEach(function(a){S("update",a),null!==c[a]&&b&&S("set",a)})}function ea(a){da(e.start,a)}function fa(){var a=wa.map(e.format.to);return 1===a.length?a[0]:a}function ga(){for(var a in e.cssClasses)e.cssClasses.hasOwnProperty(a)&&m(ra,e.cssClasses[a]);for(;ra.firstChild;)ra.removeChild(ra.firstChild);delete ra.noUiSlider}function ha(){return sa.map(function(a,b){var c=va.getNearbySteps(a),d=wa[b],e=c.thisStep.step,f=null;!1!==e&&d+e>c.stepAfter.startValue&&(e=c.stepAfter.startValue-d),f=d>c.thisStep.startValue?c.thisStep.step:!1!==c.stepBefore.step&&d-c.stepBefore.highestStep,100===a?e=null:0===a&&(f=null);var g=va.countStepDecimals();return null!==e&&!1!==e&&(e=Number(e.toFixed(g))),null!==f&&!1!==f&&(f=Number(f.toFixed(g))),[f,e]})}function ia(a,b){var c=fa(),d=["margin","limit","padding","range","animate","snap","step","format"];d.forEach(function(b){void 0!==a[b]&&(g[b]=a[b])});var f=W(g);d.forEach(function(b){void 0!==a[b]&&(e[b]=f[b])}),va=f.spectrum,e.margin=f.margin,e.limit=f.limit,e.padding=f.padding,e.pips&&D(e.pips),sa=[],da(a.start||c,b)}var ja,ka,la,ma,na,oa=p(),pa=r(),qa=pa&&q(),ra=a,sa=[],ta=[],ua=0,va=e.spectrum,wa=[],xa={},ya=a.ownerDocument,za=ya.documentElement,Aa=ya.body,Ba="rtl"===ya.dir||1===e.ort?0:100;return v(ra),u(e.connect,ja),P(e.events),da(e.start),ma={destroy:ga,steps:ha,on:Q,off:R,get:fa,set:da,reset:ea,__moveHandles:function(a,b,c){X(a,b,sa,c)},options:g,updateOptions:ia,target:ra,removePips:C,pips:D},e.pips&&D(e.pips),e.tooltips&&x(),y(),ma}function Y(a,b){if(!a||!a.nodeName)throw new Error("noUiSlider ("+Z+"): create requires a single element, got: "+a);if(a.noUiSlider)throw new Error("noUiSlider ("+Z+"): Slider was already initialized.");var c=W(b,a),d=X(a,c,b);return a.noUiSlider=d,d}var Z="11.0.3";C.prototype.getMargin=function(a){var b=this.xNumSteps[0];if(b&&a/b%1!=0)throw new Error("noUiSlider ("+Z+"): 'limit', 'margin' and 'padding' must be divisible by step.");return 2===this.xPct.length&&t(this.xVal,a)},C.prototype.toStepping=function(a){return a=x(this.xVal,this.xPct,a)},C.prototype.fromStepping=function(a){return y(this.xVal,this.xPct,a)},C.prototype.getStep=function(a){return a=z(this.xPct,this.xSteps,this.snap,a)},C.prototype.getNearbySteps=function(a){var b=w(a,this.xPct);return{stepBefore:{startValue:this.xVal[b-2],step:this.xNumSteps[b-2],highestStep:this.xHighestCompleteStep[b-2]},thisStep:{startValue:this.xVal[b-1],step:this.xNumSteps[b-1],highestStep:this.xHighestCompleteStep[b-1]},stepAfter:{startValue:this.xVal[b-0],step:this.xNumSteps[b-0],highestStep:this.xHighestCompleteStep[b-0]}}},C.prototype.countStepDecimals=function(){var a=this.xNumSteps.map(k);return Math.max.apply(null,a)},C.prototype.convert=function(a){return this.getStep(this.toStepping(a))};var $={to:function(a){return void 0!==a&&a.toFixed(2)},from:Number};return{version:Z,create:Y}}); \ No newline at end of file diff --git a/eslint.json b/eslint.json new file mode 100644 index 00000000..c7f112b2 --- /dev/null +++ b/eslint.json @@ -0,0 +1,8 @@ +{ + "extends": "eslint:recommended", + "rules": { + "no-unused-vars": "off", + "no-undef": "off", + "one-var": ["error", "never"] + } +} diff --git a/package.json b/package.json index 1dea3dbd..5a7d844b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nouislider", - "version": "11.0.2", + "version": "11.0.3", "main": "distribute/nouislider", "style": "distribute/nouislider.min.css", "license": "WTFPL", @@ -19,11 +19,12 @@ "grunt-contrib-compress": "^0.11.0", "grunt-contrib-concat": "^0.5.0", "grunt-contrib-copy": "^0.5.0", - "grunt-contrib-less": "^1.4.0", "grunt-contrib-cssmin": "^0.10.0", "grunt-contrib-jshint": "^0.11.3", - "grunt-contrib-uglify": "^0.5.1", + "grunt-contrib-less": "^1.4.0", "grunt-contrib-qunit": "^1.2.0", + "grunt-contrib-uglify": "^0.5.1", + "grunt-eslint": "^20.1.0", "onchange": "2.0.0", "simulant": "0.1.5" }, diff --git a/src/js/aria.js b/src/js/aria.js index 8639dd96..b8946c1a 100644 --- a/src/js/aria.js +++ b/src/js/aria.js @@ -4,15 +4,15 @@ bindEvent('update', function ( values, handleNumber, unencoded, tap, positions ) { // Update Aria Values for all handles, as a change in one changes min and max values for the next. - scope_HandleNumbers.forEach(function( handleNumber ){ + scope_HandleNumbers.forEach(function( index ){ - var handle = scope_Handles[handleNumber]; + var handle = scope_Handles[index]; - var min = checkHandlePosition(scope_Locations, handleNumber, 0, true, true, true); - var max = checkHandlePosition(scope_Locations, handleNumber, 100, true, true, true); + var min = checkHandlePosition(scope_Locations, index, 0, true, true, true); + var max = checkHandlePosition(scope_Locations, index, 100, true, true, true); - var now = positions[handleNumber]; - var text = options.ariaFormat.to(unencoded[handleNumber]); + var now = positions[index]; + var text = options.ariaFormat.to(unencoded[index]); handle.children[0].setAttribute('aria-valuemin', min.toFixed(1)); handle.children[0].setAttribute('aria-valuemax', max.toFixed(1)); diff --git a/src/js/interface.js b/src/js/interface.js index 979724cf..d00cc914 100644 --- a/src/js/interface.js +++ b/src/js/interface.js @@ -6,16 +6,21 @@ throw new Error("noUiSlider (" + VERSION + "): create requires a single element, got: " + target); } + // Throw an error if the slider was already initialized. + if ( target.noUiSlider ) { + throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized."); + } + // Test the options and create the slider environment; var options = testOptions( originalOptions, target ); - var api = closure( target, options, originalOptions ); + var api = scope( target, options, originalOptions ); target.noUiSlider = api; return api; } - // Use an object instead of a function for future expansibility; + // Use an object instead of a function for future expandability; return { version: VERSION, create: initialize diff --git a/src/js/options.js b/src/js/options.js index 1e05c21f..223589a8 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -144,14 +144,14 @@ // 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 (" + VERSION + "): 'orientation' option is invalid."); + case 'horizontal': + parsed.ort = 0; + break; + case 'vertical': + parsed.ort = 1; + break; + default: + throw new Error("noUiSlider (" + VERSION + "): 'orientation' option is invalid."); } } @@ -192,7 +192,7 @@ throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers."); } - if ( Array.isArray(entry) && !(entry.length == 2 || isNumeric(entry[0]) || isNumeric(entry[1])) ) { + if ( Array.isArray(entry) && !(entry.length === 2 || isNumeric(entry[0]) || isNumeric(entry[1])) ) { throw new Error("noUiSlider (" + VERSION + "): 'padding' option must be numeric or array of exactly 2 numbers."); } @@ -226,14 +226,14 @@ // 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; - break; - default: - throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized."); + case 'ltr': + parsed.dir = 0; + break; + case 'rtl': + parsed.dir = 1; + break; + default: + throw new Error("noUiSlider (" + VERSION + "): 'direction' option was not recognized."); } } diff --git a/src/js/range.js b/src/js/range.js index 1b4dc9c8..d462ac98 100644 --- a/src/js/range.js +++ b/src/js/range.js @@ -44,12 +44,11 @@ return 100; } - var j = getJ( value, xVal ), va, vb, pa, pb; - - va = xVal[j-1]; - vb = xVal[j]; - pa = xPct[j-1]; - pb = xPct[j]; + var j = getJ( value, xVal ); + var va = xVal[j-1]; + var vb = xVal[j]; + var pa = xPct[j-1]; + var pb = xPct[j]; return pa + (toPercentage([va, vb], value) / subRangeRatio (pa, pb)); } @@ -62,12 +61,11 @@ return xVal.slice(-1)[0]; } - var j = getJ( value, xPct ), va, vb, pa, pb; - - va = xVal[j-1]; - vb = xVal[j]; - pa = xPct[j-1]; - pb = xPct[j]; + var j = getJ( value, xPct ); + var va = xVal[j-1]; + var vb = xVal[j]; + var pa = xPct[j-1]; + var pb = xPct[j]; return isPercentage([va, vb], (value - pa) * subRangeRatio (pa, pb)); } @@ -79,14 +77,13 @@ return value; } - var j = getJ( value, xPct ), a, b; + var j = getJ( value, xPct ); + var a = xPct[j-1]; + var b = xPct[j]; // 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; @@ -162,12 +159,7 @@ } // Factor to range ratio - that.xSteps[i] = fromPercentage([ - that.xVal[i] - ,that.xVal[i+1] - ], n) / subRangeRatio ( - that.xPct[i], - that.xPct[i+1] ); + that.xSteps[i] = fromPercentage([that.xVal[i], that.xVal[i+1]], n) / subRangeRatio(that.xPct[i], that.xPct[i+1]); var totalSteps = (that.xVal[i+1] - that.xVal[i]) / that.xNumSteps[i]; var highestStep = Math.ceil(Number(totalSteps.toFixed(3)) - 1); @@ -189,7 +181,8 @@ this.snap = snap; - var index, ordered = [ /* [0, 'min'], [1, '50%'], [2, 'max'] */ ]; + var index; + var ordered = []; // [0, 'min'], [1, '50%'], [2, 'max'] // Map the object keys to an array. for ( index in entry ) { @@ -265,7 +258,7 @@ Spectrum.prototype.countStepDecimals = function () { var stepDecimals = this.xNumSteps.map(countDecimals); return Math.max.apply(null, stepDecimals); - }; + }; // Outside testing Spectrum.prototype.convert = function ( value ) { diff --git a/src/js/scope.js b/src/js/scope.js deleted file mode 100644 index 6643f462..00000000 --- a/src/js/scope.js +++ /dev/null @@ -1,421 +0,0 @@ - - // Split out the handle positioning logic so the Move event can use it, too - function checkHandlePosition ( reference, handleNumber, to, lookBackward, lookForward, getValue ) { - - // For sliders with multiple handles, limit movement to the other handle. - // Apply the margin option by adding it to the handle positions. - if ( scope_Handles.length > 1 ) { - - if ( lookBackward && handleNumber > 0 ) { - to = Math.max(to, reference[handleNumber - 1] + options.margin); - } - - if ( lookForward && handleNumber < scope_Handles.length - 1 ) { - to = Math.min(to, reference[handleNumber + 1] - options.margin); - } - } - - // 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. - if ( scope_Handles.length > 1 && options.limit ) { - - if ( lookBackward && handleNumber > 0 ) { - to = Math.min(to, reference[handleNumber - 1] + options.limit); - } - - if ( lookForward && handleNumber < scope_Handles.length - 1 ) { - to = Math.max(to, reference[handleNumber + 1] - options.limit); - } - } - - // The padding option keeps the handles a certain distance from the - // edges of the slider. Padding must be > 0. - if ( options.padding ) { - - if ( handleNumber === 0 ) { - to = Math.max(to, options.padding[0]); - } - - if ( handleNumber === scope_Handles.length - 1 ) { - to = Math.min(to, 100 - options.padding[1]); - } - } - - to = scope_Spectrum.getStep(to); - - // Limit percentage to the 0 - 100 range - to = limit(to); - - // Return false if handle can't move - if ( to === reference[handleNumber] && !getValue ) { - return false; - } - - return to; - } - - // Uses slider orientation to create CSS rules. a = base value; - function inRuleOrder ( v, a ) { - var o = options.ort; - return (o?a:v) + ', ' + (o?v:a); - } - - function toPct ( pct ) { - return pct + '%'; - } - - // Takes a base value and an offset. This offset is used for the connect bar size. - // In the initial design for this feature, the origin element was 1% wide. - // Unfortunately, a rounding bug in Chrome makes it impossible to implement this feature - // in this manner: https://bugs.chromium.org/p/chromium/issues/detail?id=798223 - function transformDirection ( a, b ) { - return options.dir ? 100 - a - b : a; - } - - // Updates scope_Locations and scope_Values, updates visual state - function updateHandlePosition ( handleNumber, to ) { - - // Update locations. - scope_Locations[handleNumber] = to; - - // Convert the value to the slider stepping/range. - scope_Values[handleNumber] = scope_Spectrum.fromStepping(to); - - var rule = 'translate(' + inRuleOrder(toPct(transformDirection(to, 0) - scope_DirOffset), '0') + ')'; - scope_Handles[handleNumber].style[options.transformRule] = rule; - - updateConnect(handleNumber); - updateConnect(handleNumber + 1); - } - - function setZindex ( ) { - - scope_HandleNumbers.forEach(function(handleNumber){ - // Handles before the slider middle are stacked later = higher, - // Handles after the middle later is lower - // [[7] [8] .......... | .......... [5] [4] - var dir = (scope_Locations[handleNumber] > 50 ? -1 : 1); - var zIndex = 3 + (scope_Handles.length + (dir * handleNumber)); - scope_Handles[handleNumber].style.zIndex = zIndex; - }); - } - - // Test suggested values and apply margin, step. - function setHandle ( handleNumber, to, lookBackward, lookForward ) { - - to = checkHandlePosition(scope_Locations, handleNumber, to, lookBackward, lookForward, false); - - if ( to === false ) { - return false; - } - - updateHandlePosition(handleNumber, to); - - return true; - } - - // Updates style attribute for connect nodes - function updateConnect ( index ) { - - // Skip connects set to false - if ( !scope_Connects[index] ) { - return; - } - - var l = 0; - var h = 100; - - if ( index !== 0 ) { - l = scope_Locations[index - 1]; - } - - if ( index !== scope_Connects.length - 1 ) { - h = scope_Locations[index]; - } - - // We use two rules: - // 'translate' to change the left/top offset; - // 'scale' to change the width of the element; - // As the element has a width of 100%, a translation of 100% is equal to 100% of the parent (.noUi-base) - var connectWidth = h - l; - var translateRule = 'translate(' + inRuleOrder(toPct(transformDirection(l, connectWidth)), '0') + ')'; - var scaleRule = 'scale(' + inRuleOrder(connectWidth / 100, '1') + ')'; - - scope_Connects[index].style[options.transformRule] = translateRule + ' ' + scaleRule; - } - - // Parses value passed to .set method. Returns current value if not parseable. - function resolveToValue ( to, handleNumber ) { - - // Setting with null indicates an 'ignore'. - // Inputting 'false' is invalid. - if ( to === null || to === false || to === undefined ) { - return scope_Locations[handleNumber]; - } - - // If a formatted number was passed, attempt to decode it. - if ( typeof to === 'number' ) { - to = String(to); - } - - to = options.format.from(to); - to = scope_Spectrum.toStepping(to); - - // If parsing the number failed, use the current value. - if ( to === false || isNaN(to) ) { - return scope_Locations[handleNumber]; - } - - return to; - } - - // Set the slider value. - function valueSet ( input, fireSetEvent ) { - - var values = asArray(input); - var isInit = scope_Locations[0] === undefined; - - // Event fires by default - fireSetEvent = (fireSetEvent === undefined ? true : !!fireSetEvent); - - // Animation is optional. - // Make sure the initial values were set before using animated placement. - if ( options.animate && !isInit ) { - addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration); - } - - // First pass, without lookAhead but with lookBackward. Values are set from left to right. - scope_HandleNumbers.forEach(function(handleNumber){ - setHandle(handleNumber, resolveToValue(values[handleNumber], handleNumber), true, false); - }); - - // Second pass. Now that all base values are set, apply constraints - scope_HandleNumbers.forEach(function(handleNumber){ - setHandle(handleNumber, scope_Locations[handleNumber], true, true); - }); - - setZindex(); - - scope_HandleNumbers.forEach(function(handleNumber){ - - fireEvent('update', handleNumber); - - // Fire the event only for handles that received a new value, as per #579 - if ( values[handleNumber] !== null && fireSetEvent ) { - fireEvent('set', handleNumber); - } - }); - } - - // Reset slider to initial values - function valueReset ( fireSetEvent ) { - valueSet(options.start, fireSetEvent); - } - - // Get the slider value. - function valueGet ( ) { - - var values = scope_Values.map(options.format.to); - - // If only one handle is used, return a single value. - if ( values.length === 1 ){ - return values[0]; - } - - return values; - } - - // Removes classes from the root and empties it. - function destroy ( ) { - - for ( var key in options.cssClasses ) { - if ( !options.cssClasses.hasOwnProperty(key) ) { continue; } - removeClass(scope_Target, options.cssClasses[key]); - } - - while (scope_Target.firstChild) { - scope_Target.removeChild(scope_Target.firstChild); - } - - delete scope_Target.noUiSlider; - } - - // Get the current step size for the slider. - function getCurrentStep ( ) { - - // Check all locations, map them to their stepping point. - // Get the step point, then find it in the input list. - return scope_Locations.map(function( location, index ){ - - var nearbySteps = scope_Spectrum.getNearbySteps( location ); - var value = scope_Values[index]; - var increment = nearbySteps.thisStep.step; - var decrement = null; - - // If the next value in this step moves into the next step, - // the increment is the start of the next step - the current value - if ( increment !== false ) { - if ( value + increment > nearbySteps.stepAfter.startValue ) { - increment = nearbySteps.stepAfter.startValue - value; - } - } - - - // If the value is beyond the starting point - if ( value > nearbySteps.thisStep.startValue ) { - decrement = nearbySteps.thisStep.step; - } - - else if ( nearbySteps.stepBefore.step === false ) { - decrement = false; - } - - // If a handle is at the start of a step, it always steps back into the previous step first - else { - decrement = value - nearbySteps.stepBefore.highestStep; - } - - - // Now, if at the slider edges, there is not in/decrement - if ( location === 100 ) { - increment = null; - } - - else if ( location === 0 ) { - decrement = null; - } - - // As per #391, the comparison for the decrement step can have some rounding issues. - var stepDecimals = scope_Spectrum.countStepDecimals(); - - // Round per #391 - if ( increment !== null && increment !== false ) { - increment = Number(increment.toFixed(stepDecimals)); - } - - if ( decrement !== null && decrement !== false ) { - decrement = Number(decrement.toFixed(stepDecimals)); - } - - return [decrement, increment]; - }); - } - - // Attach an event to this slider, possibly including a namespace - function bindEvent ( namespacedEvent, callback ) { - scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || []; - scope_Events[namespacedEvent].push(callback); - - // If the event bound is 'update,' fire it immediately for all handles. - if ( namespacedEvent.split('.')[0] === 'update' ) { - scope_Handles.forEach(function(a, index){ - fireEvent('update', index); - }); - } - } - - // Undo attachment of event - function removeEvent ( namespacedEvent ) { - - var event = namespacedEvent && namespacedEvent.split('.')[0]; - var namespace = event && namespacedEvent.substring(event.length); - - Object.keys(scope_Events).forEach(function( bind ){ - - var tEvent = bind.split('.')[0], - tNamespace = bind.substring(tEvent.length); - - if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) { - delete scope_Events[bind]; - } - }); - } - - // Updateable: margin, limit, padding, step, range, animate, snap - function updateOptions ( optionsToUpdate, fireSetEvent ) { - - // Spectrum is created using the range, snap, direction and step options. - // 'snap' and 'step' can be updated. - // If 'snap' and 'step' are not passed, they should remain unchanged. - var v = valueGet(); - - var updateAble = ['margin', 'limit', 'padding', 'range', 'animate', 'snap', 'step', 'format']; - - // Only change options that we're actually passed to update. - updateAble.forEach(function(name){ - if ( optionsToUpdate[name] !== undefined ) { - originalOptions[name] = optionsToUpdate[name]; - } - }); - - var newOptions = testOptions(originalOptions); - - // Load new options into the slider state - updateAble.forEach(function(name){ - if ( optionsToUpdate[name] !== undefined ) { - options[name] = newOptions[name]; - } - }); - - scope_Spectrum = newOptions.spectrum; - - // Limit, margin and padding depend on the spectrum but are stored outside of it. (#677) - options.margin = newOptions.margin; - options.limit = newOptions.limit; - options.padding = newOptions.padding; - - // Update pips, removes existing. - if ( options.pips ) { - pips(options.pips); - } - - // Invalidate the current positioning so valueSet forces an update. - scope_Locations = []; - valueSet(optionsToUpdate.start || v, fireSetEvent); - } - - // Throw an error if the slider was already initialized. - if ( scope_Target.noUiSlider ) { - throw new Error("noUiSlider (" + VERSION + "): Slider was already initialized."); - } - - // Create the base element, initialise HTML and set classes. - // Add handles and connect elements. - addSlider(scope_Target); - addElements(options.connect, scope_Base); - - scope_Self = { - destroy: destroy, - steps: getCurrentStep, - on: bindEvent, - off: removeEvent, - get: valueGet, - set: valueSet, - reset: valueReset, - // Exposed for unit testing, don't use this in your application. - __moveHandles: function(a, b, c) { moveHandles(a, b, scope_Locations, c); }, - options: originalOptions, // Issue #600, #678 - updateOptions: updateOptions, - target: scope_Target, // Issue #597 - removePips: removePips, - pips: pips // Issue #594 - }; - - // Attach user events. - bindSliderEvents(options.events); - - // Use the public value method to set the start values. - valueSet(options.start); - - if ( options.pips ) { - pips(options.pips); - } - - if ( options.tooltips ) { - tooltips(); - } - - aria(); - - return scope_Self; diff --git a/src/js/scope_api.js b/src/js/scope_api.js new file mode 100644 index 00000000..26d5ec50 --- /dev/null +++ b/src/js/scope_api.js @@ -0,0 +1,201 @@ +/*! In this file: All methods eventually exposed in slider.noUiSlider... */ + + // Parses value passed to .set method. Returns current value if not parse-able. + function resolveToValue ( to, handleNumber ) { + + // Setting with null indicates an 'ignore'. + // Inputting 'false' is invalid. + if ( to === null || to === false || to === undefined ) { + return scope_Locations[handleNumber]; + } + + // If a formatted number was passed, attempt to decode it. + if ( typeof to === 'number' ) { + to = String(to); + } + + to = options.format.from(to); + to = scope_Spectrum.toStepping(to); + + // If parsing the number failed, use the current value. + if ( to === false || isNaN(to) ) { + return scope_Locations[handleNumber]; + } + + return to; + } + + // Set the slider value. + function valueSet ( input, fireSetEvent ) { + + var values = asArray(input); + var isInit = scope_Locations[0] === undefined; + + // Event fires by default + fireSetEvent = (fireSetEvent === undefined ? true : !!fireSetEvent); + + // Animation is optional. + // Make sure the initial values were set before using animated placement. + if ( options.animate && !isInit ) { + addClassFor(scope_Target, options.cssClasses.tap, options.animationDuration); + } + + // First pass, without lookAhead but with lookBackward. Values are set from left to right. + scope_HandleNumbers.forEach(function(handleNumber){ + setHandle(handleNumber, resolveToValue(values[handleNumber], handleNumber), true, false); + }); + + // Second pass. Now that all base values are set, apply constraints + scope_HandleNumbers.forEach(function(handleNumber){ + setHandle(handleNumber, scope_Locations[handleNumber], true, true); + }); + + setZindex(); + + scope_HandleNumbers.forEach(function(handleNumber){ + + fireEvent('update', handleNumber); + + // Fire the event only for handles that received a new value, as per #579 + if ( values[handleNumber] !== null && fireSetEvent ) { + fireEvent('set', handleNumber); + } + }); + } + + // Reset slider to initial values + function valueReset ( fireSetEvent ) { + valueSet(options.start, fireSetEvent); + } + + // Get the slider value. + function valueGet ( ) { + + var values = scope_Values.map(options.format.to); + + // If only one handle is used, return a single value. + if ( values.length === 1 ){ + return values[0]; + } + + return values; + } + + // Removes classes from the root and empties it. + function destroy ( ) { + + for ( var key in options.cssClasses ) { + if ( !options.cssClasses.hasOwnProperty(key) ) { continue; } + removeClass(scope_Target, options.cssClasses[key]); + } + + while (scope_Target.firstChild) { + scope_Target.removeChild(scope_Target.firstChild); + } + + delete scope_Target.noUiSlider; + } + + // Get the current step size for the slider. + function getCurrentStep ( ) { + + // Check all locations, map them to their stepping point. + // Get the step point, then find it in the input list. + return scope_Locations.map(function( location, index ){ + + var nearbySteps = scope_Spectrum.getNearbySteps( location ); + var value = scope_Values[index]; + var increment = nearbySteps.thisStep.step; + var decrement = null; + + // If the next value in this step moves into the next step, + // the increment is the start of the next step - the current value + if ( increment !== false ) { + if ( value + increment > nearbySteps.stepAfter.startValue ) { + increment = nearbySteps.stepAfter.startValue - value; + } + } + + + // If the value is beyond the starting point + if ( value > nearbySteps.thisStep.startValue ) { + decrement = nearbySteps.thisStep.step; + } + + else if ( nearbySteps.stepBefore.step === false ) { + decrement = false; + } + + // If a handle is at the start of a step, it always steps back into the previous step first + else { + decrement = value - nearbySteps.stepBefore.highestStep; + } + + + // Now, if at the slider edges, there is not in/decrement + if ( location === 100 ) { + increment = null; + } + + else if ( location === 0 ) { + decrement = null; + } + + // As per #391, the comparison for the decrement step can have some rounding issues. + var stepDecimals = scope_Spectrum.countStepDecimals(); + + // Round per #391 + if ( increment !== null && increment !== false ) { + increment = Number(increment.toFixed(stepDecimals)); + } + + if ( decrement !== null && decrement !== false ) { + decrement = Number(decrement.toFixed(stepDecimals)); + } + + return [decrement, increment]; + }); + } + + // Updateable: margin, limit, padding, step, range, animate, snap + function updateOptions ( optionsToUpdate, fireSetEvent ) { + + // Spectrum is created using the range, snap, direction and step options. + // 'snap' and 'step' can be updated. + // If 'snap' and 'step' are not passed, they should remain unchanged. + var v = valueGet(); + + var updateAble = ['margin', 'limit', 'padding', 'range', 'animate', 'snap', 'step', 'format']; + + // Only change options that we're actually passed to update. + updateAble.forEach(function(name){ + if ( optionsToUpdate[name] !== undefined ) { + originalOptions[name] = optionsToUpdate[name]; + } + }); + + var newOptions = testOptions(originalOptions); + + // Load new options into the slider state + updateAble.forEach(function(name){ + if ( optionsToUpdate[name] !== undefined ) { + options[name] = newOptions[name]; + } + }); + + scope_Spectrum = newOptions.spectrum; + + // Limit, margin and padding depend on the spectrum but are stored outside of it. (#677) + options.margin = newOptions.margin; + options.limit = newOptions.limit; + options.padding = newOptions.padding; + + // Update pips, removes existing. + if ( options.pips ) { + pips(options.pips); + } + + // Invalidate the current positioning so valueSet forces an update. + scope_Locations = []; + valueSet(optionsToUpdate.start || v, fireSetEvent); + } diff --git a/src/js/scope_core.js b/src/js/scope_core.js new file mode 100644 index 00000000..c3a707ba --- /dev/null +++ b/src/js/scope_core.js @@ -0,0 +1,203 @@ +/*! In this file: Mechanics for slider operation */ + + function toPct ( pct ) { + return pct + '%'; + } + + // Split out the handle positioning logic so the Move event can use it, too + function checkHandlePosition ( reference, handleNumber, to, lookBackward, lookForward, getValue ) { + + // For sliders with multiple handles, limit movement to the other handle. + // Apply the margin option by adding it to the handle positions. + if ( scope_Handles.length > 1 ) { + + if ( lookBackward && handleNumber > 0 ) { + to = Math.max(to, reference[handleNumber - 1] + options.margin); + } + + if ( lookForward && handleNumber < scope_Handles.length - 1 ) { + to = Math.min(to, reference[handleNumber + 1] - options.margin); + } + } + + // 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. + if ( scope_Handles.length > 1 && options.limit ) { + + if ( lookBackward && handleNumber > 0 ) { + to = Math.min(to, reference[handleNumber - 1] + options.limit); + } + + if ( lookForward && handleNumber < scope_Handles.length - 1 ) { + to = Math.max(to, reference[handleNumber + 1] - options.limit); + } + } + + // The padding option keeps the handles a certain distance from the + // edges of the slider. Padding must be > 0. + if ( options.padding ) { + + if ( handleNumber === 0 ) { + to = Math.max(to, options.padding[0]); + } + + if ( handleNumber === scope_Handles.length - 1 ) { + to = Math.min(to, 100 - options.padding[1]); + } + } + + to = scope_Spectrum.getStep(to); + + // Limit percentage to the 0 - 100 range + to = limit(to); + + // Return false if handle can't move + if ( to === reference[handleNumber] && !getValue ) { + return false; + } + + return to; + } + + // Uses slider orientation to create CSS rules. a = base value; + function inRuleOrder ( v, a ) { + var o = options.ort; + return (o?a:v) + ', ' + (o?v:a); + } + + // Moves handle(s) by a percentage + // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...]) + function moveHandles ( upward, proposal, locations, handleNumbers ) { + + var proposals = locations.slice(); + + var b = [!upward, upward]; + var f = [upward, !upward]; + + // Copy handleNumbers so we don't change the dataset + handleNumbers = handleNumbers.slice(); + + // Check to see which handle is 'leading'. + // If that one can't move the second can't either. + if ( upward ) { + handleNumbers.reverse(); + } + + // Step 1: get the maximum percentage that any of the handles can move + if ( handleNumbers.length > 1 ) { + + handleNumbers.forEach(function(handleNumber, o) { + + var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false); + + // Stop if one of the handles can't move. + if ( to === false ) { + proposal = 0; + } else { + proposal = to - proposals[handleNumber]; + proposals[handleNumber] = to; + } + }); + } + + // If using one handle, check backward AND forward + else { + b = f = [true]; + } + + var state = false; + + // Step 2: Try to set the handles with the found percentage + handleNumbers.forEach(function(handleNumber, o) { + state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state; + }); + + // Step 3: If a handle moved, fire events + if ( state ) { + handleNumbers.forEach(function(handleNumber){ + fireEvent('update', handleNumber); + fireEvent('slide', handleNumber); + }); + } + } + + // Takes a base value and an offset. This offset is used for the connect bar size. + // In the initial design for this feature, the origin element was 1% wide. + // Unfortunately, a rounding bug in Chrome makes it impossible to implement this feature + // in this manner: https://bugs.chromium.org/p/chromium/issues/detail?id=798223 + function transformDirection ( a, b ) { + return options.dir ? 100 - a - b : a; + } + + // Updates scope_Locations and scope_Values, updates visual state + function updateHandlePosition ( handleNumber, to ) { + + // Update locations. + scope_Locations[handleNumber] = to; + + // Convert the value to the slider stepping/range. + scope_Values[handleNumber] = scope_Spectrum.fromStepping(to); + + var rule = 'translate(' + inRuleOrder(toPct(transformDirection(to, 0) - scope_DirOffset), '0') + ')'; + scope_Handles[handleNumber].style[options.transformRule] = rule; + + updateConnect(handleNumber); + updateConnect(handleNumber + 1); + } + + // Handles before the slider middle are stacked later = higher, + // Handles after the middle later is lower + // [[7] [8] .......... | .......... [5] [4] + function setZindex ( ) { + + scope_HandleNumbers.forEach(function(handleNumber){ + var dir = (scope_Locations[handleNumber] > 50 ? -1 : 1); + var zIndex = 3 + (scope_Handles.length + (dir * handleNumber)); + scope_Handles[handleNumber].style.zIndex = zIndex; + }); + } + + // Test suggested values and apply margin, step. + function setHandle ( handleNumber, to, lookBackward, lookForward ) { + + to = checkHandlePosition(scope_Locations, handleNumber, to, lookBackward, lookForward, false); + + if ( to === false ) { + return false; + } + + updateHandlePosition(handleNumber, to); + + return true; + } + + // Updates style attribute for connect nodes + function updateConnect ( index ) { + + // Skip connects set to false + if ( !scope_Connects[index] ) { + return; + } + + var l = 0; + var h = 100; + + if ( index !== 0 ) { + l = scope_Locations[index - 1]; + } + + if ( index !== scope_Connects.length - 1 ) { + h = scope_Locations[index]; + } + + // We use two rules: + // 'translate' to change the left/top offset; + // 'scale' to change the width of the element; + // As the element has a width of 100%, a translation of 100% is equal to 100% of the parent (.noUi-base) + var connectWidth = h - l; + var translateRule = 'translate(' + inRuleOrder(toPct(transformDirection(l, connectWidth)), '0') + ')'; + var scaleRule = 'scale(' + inRuleOrder(connectWidth / 100, '1') + ')'; + + scope_Connects[index].style[options.transformRule] = translateRule + ' ' + scaleRule; + } diff --git a/src/js/scope_event_binding.js b/src/js/scope_event_binding.js new file mode 100644 index 00000000..72f68b85 --- /dev/null +++ b/src/js/scope_event_binding.js @@ -0,0 +1,60 @@ +/*! In this file: Slider events (not browser events); */ + + // Attach an event to this slider, possibly including a namespace + function bindEvent ( namespacedEvent, callback ) { + scope_Events[namespacedEvent] = scope_Events[namespacedEvent] || []; + scope_Events[namespacedEvent].push(callback); + + // If the event bound is 'update,' fire it immediately for all handles. + if ( namespacedEvent.split('.')[0] === 'update' ) { + scope_Handles.forEach(function(a, index){ + fireEvent('update', index); + }); + } + } + + // Undo attachment of event + function removeEvent ( namespacedEvent ) { + + var event = namespacedEvent && namespacedEvent.split('.')[0]; + var namespace = event && namespacedEvent.substring(event.length); + + Object.keys(scope_Events).forEach(function( bind ){ + + var tEvent = bind.split('.')[0]; + var tNamespace = bind.substring(tEvent.length); + + if ( (!event || event === tEvent) && (!namespace || namespace === tNamespace) ) { + delete scope_Events[bind]; + } + }); + } + + // External event handling + function fireEvent ( eventName, handleNumber, tap ) { + + Object.keys(scope_Events).forEach(function( targetEvent ) { + + var eventType = targetEvent.split('.')[0]; + + if ( eventName === eventType ) { + scope_Events[targetEvent].forEach(function( callback ) { + + callback.call( + // Use the slider public API as the scope ('this') + scope_Self, + // Return values as array, so arg_1[arg_2] is always valid. + scope_Values.map(options.format.to), + // Handle index, 0 or 1 + handleNumber, + // Unformatted slider values + scope_Values.slice(), + // Event is fired by tap, true or false + tap || false, + // Left offset of the handle, in relation to the slider + scope_Locations.slice() + ); + }); + } + }); + } diff --git a/src/js/scope_events.js b/src/js/scope_events.js index 307bb248..11739f34 100644 --- a/src/js/scope_events.js +++ b/src/js/scope_events.js @@ -1,3 +1,181 @@ +/*! In this file: Browser events (not slider events like slide, change); */ + + // Shorthand for base dimensions. + function baseSize ( ) { + var rect = scope_Base.getBoundingClientRect(); + var alt = 'offset' + ['Width', 'Height'][options.ort]; + return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]); + } + + // Handler for attaching events trough a proxy. + function attachEvent ( events, element, callback, data ) { + + // This function can be used to 'filter' events to the slider. + // element is a node, not a nodeList + + var method = function ( e ){ + + e = fixEvent(e, data.pageOffset, data.target || element); + + // fixEvent returns false if this event has a different target + // when handling (multi-) touch events; + if ( !e ) { + return false; + } + + // doNotReject is passed by all end events to make sure released touches + // are not rejected, leaving the slider "stuck" to the cursor; + if ( scope_Target.hasAttribute('disabled') && !data.doNotReject ) { + return false; + } + + // Stop if an active 'tap' transition is taking place. + if ( hasClass(scope_Target, options.cssClasses.tap) && !data.doNotReject ) { + return false; + } + + // Ignore right or middle clicks on start #454 + if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) { + return false; + } + + // Ignore right or middle clicks on start #454 + if ( data.hover && e.buttons ) { + return false; + } + + // 'supportsPassive' is only true if a browser also supports touch-action: none in CSS. + // iOS safari does not, so it doesn't get to benefit from passive scrolling. iOS does support + // touch-action: manipulation, but that allows panning, which breaks + // sliders after zooming/on non-responsive pages. + // See: https://bugs.webkit.org/show_bug.cgi?id=133112 + if ( !supportsPassive ) { + e.preventDefault(); + } + + e.calcPoint = e.points[ options.ort ]; + + // Call the event handler with the event [ and additional data ]. + callback ( e, data ); + }; + + var methods = []; + + // Bind a closure on the target for every event type. + events.split(' ').forEach(function( eventName ){ + element.addEventListener(eventName, method, supportsPassive ? { passive: true } : false); + methods.push([eventName, method]); + }); + + return methods; + } + + // Provide a clean event with standardized offset values. + function fixEvent ( e, pageOffset, eventTarget ) { + + // Filter the event to register the type, which can be + // touch, mouse or pointer. Offset changes need to be + // made on an event specific basis. + var touch = e.type.indexOf('touch') === 0; + var mouse = e.type.indexOf('mouse') === 0; + var pointer = e.type.indexOf('pointer') === 0; + + var x; + var y; + + // IE10 implemented pointer events with a prefix; + if ( e.type.indexOf('MSPointer') === 0 ) { + pointer = true; + } + + // In the event that multitouch is activated, the only thing one handle should be concerned + // about is the touches that originated on top of it. + if ( touch ) { + + // Returns true if a touch originated on the target. + var isTouchOnTarget = function (checkTouch) { + return checkTouch.target === eventTarget || eventTarget.contains(checkTouch.target); + }; + + // In the case of touchstart events, we need to make sure there is still no more than one + // touch on the target so we look amongst all touches. + if (e.type === 'touchstart') { + + var targetTouches = Array.prototype.filter.call(e.touches, isTouchOnTarget); + + // Do not support more than one touch per handle. + if ( targetTouches.length > 1 ) { + return false; + } + + x = targetTouches[0].pageX; + y = targetTouches[0].pageY; + + } else { + + // In the other cases, find on changedTouches is enough. + var targetTouch = Array.prototype.find.call(e.changedTouches, isTouchOnTarget); + + // Cancel if the target touch has not moved. + if ( !targetTouch ) { + return false; + } + + x = targetTouch.pageX; + y = targetTouch.pageY; + } + } + + pageOffset = pageOffset || getPageOffset(scope_Document); + + if ( mouse || pointer ) { + x = e.clientX + pageOffset.x; + y = e.clientY + pageOffset.y; + } + + e.pageOffset = pageOffset; + e.points = [x, y]; + e.cursor = mouse || pointer; // Fix #435 + + return e; + } + + // Translate a coordinate in the document to a percentage on the slider + function calcPointToPercentage ( calcPoint ) { + var location = calcPoint - offset(scope_Base, options.ort); + var proposal = ( location * 100 ) / baseSize(); + + // Clamp proposal between 0% and 100% + // Out-of-bound coordinates may occur when .noUi-base pseudo-elements + // are used (e.g. contained handles feature) + proposal = limit(proposal); + + return options.dir ? 100 - proposal : proposal; + } + + // Find handle closest to a certain percentage on the slider + function getClosestHandle ( proposal ) { + + var closest = 100; + var handleNumber = false; + + scope_Handles.forEach(function(handle, index){ + + // Disabled handles are ignored + if ( handle.hasAttribute('disabled') ) { + return; + } + + var pos = Math.abs(scope_Locations[index] - proposal); + + if ( pos < closest || (pos === 100 && closest === 100) ) { + handleNumber = index; + closest = pos; + } + }); + + return handleNumber; + } // Fire 'end' when a mouse or pen leaves the document. function documentLeave ( event, data ) { diff --git a/src/js/scope_helpers.js b/src/js/scope_helpers.js deleted file mode 100644 index 200257d9..00000000 --- a/src/js/scope_helpers.js +++ /dev/null @@ -1,262 +0,0 @@ - - // Shorthand for base dimensions. - function baseSize ( ) { - var rect = scope_Base.getBoundingClientRect(), alt = 'offset' + ['Width', 'Height'][options.ort]; - return options.ort === 0 ? (rect.width||scope_Base[alt]) : (rect.height||scope_Base[alt]); - } - - // Handler for attaching events trough a proxy. - function attachEvent ( events, element, callback, data ) { - - // This function can be used to 'filter' events to the slider. - // element is a node, not a nodeList - - var method = function ( e ){ - - e = fixEvent(e, data.pageOffset, data.target || element); - - // fixEvent returns false if this event has a different target - // when handling (multi-) touch events; - if ( !e ) { - return false; - } - - // doNotReject is passed by all end events to make sure released touches - // are not rejected, leaving the slider "stuck" to the cursor; - if ( scope_Target.hasAttribute('disabled') && !data.doNotReject ) { - return false; - } - - // Stop if an active 'tap' transition is taking place. - if ( hasClass(scope_Target, options.cssClasses.tap) && !data.doNotReject ) { - return false; - } - - // Ignore right or middle clicks on start #454 - if ( events === actions.start && e.buttons !== undefined && e.buttons > 1 ) { - return false; - } - - // Ignore right or middle clicks on start #454 - if ( data.hover && e.buttons ) { - return false; - } - - // 'supportsPassive' is only true if a browser also supports touch-action: none in CSS. - // iOS safari does not, so it doesn't get to benefit from passive scrolling. iOS does support - // touch-action: manipulation, but that allows panning, which breaks - // sliders after zooming/on non-responsive pages. - // See: https://bugs.webkit.org/show_bug.cgi?id=133112 - if ( !supportsPassive ) { - e.preventDefault(); - } - - e.calcPoint = e.points[ options.ort ]; - - // Call the event handler with the event [ and additional data ]. - callback ( e, data ); - }; - - var methods = []; - - // Bind a closure on the target for every event type. - events.split(' ').forEach(function( eventName ){ - element.addEventListener(eventName, method, supportsPassive ? { passive: true } : false); - methods.push([eventName, method]); - }); - - return methods; - } - - // Provide a clean event with standardized offset values. - function fixEvent ( e, pageOffset, target ) { - - // Filter the event to register the type, which can be - // touch, mouse or pointer. Offset changes need to be - // made on an event specific basis. - var touch = e.type.indexOf('touch') === 0; - var mouse = e.type.indexOf('mouse') === 0; - var pointer = e.type.indexOf('pointer') === 0; - - var x; - var y; - - // IE10 implemented pointer events with a prefix; - if ( e.type.indexOf('MSPointer') === 0 ) { - pointer = true; - } - - - // In the event that multitouch is activated, the only thing one handle should be concerned - // about is the touches that originated on top of it. - if ( touch ) { - - // Returns true if a touch originated on the target. - var isTouchOnTarget = function (touch) { - return touch.target === target || target.contains(touch.target); - }; - - // In the case of touchstart events, we need to make sure there is still no more than one - // touch on the target so we look amongst all touches. - if (e.type === 'touchstart') { - - var targetTouches = Array.prototype.filter.call(e.touches, isTouchOnTarget); - - // Do not support more than one touch per handle. - if ( targetTouches.length > 1 ) { - return false; - } - - x = targetTouches[0].pageX; - y = targetTouches[0].pageY; - - } else { - - // In the other cases, find on changedTouches is enough. - var targetTouch = Array.prototype.find.call(e.changedTouches, isTouchOnTarget); - - // Cancel if the target touch has not moved. - if ( !targetTouch ) { - return false; - } - - x = targetTouch.pageX; - y = targetTouch.pageY; - } - } - - pageOffset = pageOffset || getPageOffset(scope_Document); - - if ( mouse || pointer ) { - x = e.clientX + pageOffset.x; - y = e.clientY + pageOffset.y; - } - - e.pageOffset = pageOffset; - e.points = [x, y]; - e.cursor = mouse || pointer; // Fix #435 - - return e; - } - - // Translate a coordinate in the document to a percentage on the slider - function calcPointToPercentage ( calcPoint ) { - var location = calcPoint - offset(scope_Base, options.ort); - var proposal = ( location * 100 ) / baseSize(); - - // Clamp proposal between 0% and 100% - // Out-of-bound coordinates may occur when .noUi-base pseudo-elements - // are used (e.g. contained handles feature) - proposal = limit(proposal); - - return options.dir ? 100 - proposal : proposal; - } - - // Find handle closest to a certain percentage on the slider - function getClosestHandle ( proposal ) { - - var closest = 100; - var handleNumber = false; - - scope_Handles.forEach(function(handle, index){ - - // Disabled handles are ignored - if ( handle.hasAttribute('disabled') ) { - return; - } - - var pos = Math.abs(scope_Locations[index] - proposal); - - if ( pos < closest || (pos === 100 && closest === 100) ) { - handleNumber = index; - closest = pos; - } - }); - - return handleNumber; - } - - // Moves handle(s) by a percentage - // (bool, % to move, [% where handle started, ...], [index in scope_Handles, ...]) - function moveHandles ( upward, proposal, locations, handleNumbers ) { - - var proposals = locations.slice(); - - var b = [!upward, upward]; - var f = [upward, !upward]; - - // Copy handleNumbers so we don't change the dataset - handleNumbers = handleNumbers.slice(); - - // Check to see which handle is 'leading'. - // If that one can't move the second can't either. - if ( upward ) { - handleNumbers.reverse(); - } - - // Step 1: get the maximum percentage that any of the handles can move - if ( handleNumbers.length > 1 ) { - - handleNumbers.forEach(function(handleNumber, o) { - - var to = checkHandlePosition(proposals, handleNumber, proposals[handleNumber] + proposal, b[o], f[o], false); - - // Stop if one of the handles can't move. - if ( to === false ) { - proposal = 0; - } else { - proposal = to - proposals[handleNumber]; - proposals[handleNumber] = to; - } - }); - } - - // If using one handle, check backward AND forward - else { - b = f = [true]; - } - - var state = false; - - // Step 2: Try to set the handles with the found percentage - handleNumbers.forEach(function(handleNumber, o) { - state = setHandle(handleNumber, locations[handleNumber] + proposal, b[o], f[o]) || state; - }); - - // Step 3: If a handle moved, fire events - if ( state ) { - handleNumbers.forEach(function(handleNumber){ - fireEvent('update', handleNumber); - fireEvent('slide', handleNumber); - }); - } - } - - // External event handling - function fireEvent ( eventName, handleNumber, tap ) { - - Object.keys(scope_Events).forEach(function( targetEvent ) { - - var eventType = targetEvent.split('.')[0]; - - if ( eventName === eventType ) { - scope_Events[targetEvent].forEach(function( callback ) { - - callback.call( - // Use the slider public API as the scope ('this') - scope_Self, - // Return values as array, so arg_1[arg_2] is always valid. - scope_Values.map(options.format.to), - // Handle index, 0 or 1 - handleNumber, - // Unformatted slider values - scope_Values.slice(), - // Event is fired by tap, true or false - tap || false, - // Left offset of the handle, in relation to the slider - scope_Locations.slice() - ); - }); - } - }); - } diff --git a/src/js/scope_run.js b/src/js/scope_run.js new file mode 100644 index 00000000..977b8349 --- /dev/null +++ b/src/js/scope_run.js @@ -0,0 +1,41 @@ +/*! In this file: Calls to functions. All other scope_ files define functions only; */ + + // Create the base element, initialize HTML and set classes. + // Add handles and connect elements. + addSlider(scope_Target); + addElements(options.connect, scope_Base); + + // Attach user events. + bindSliderEvents(options.events); + + // Use the public value method to set the start values. + valueSet(options.start); + + scope_Self = { + destroy: destroy, + steps: getCurrentStep, + on: bindEvent, + off: removeEvent, + get: valueGet, + set: valueSet, + reset: valueReset, + // Exposed for unit testing, don't use this in your application. + __moveHandles: function(a, b, c) { moveHandles(a, b, scope_Locations, c); }, + options: originalOptions, // Issue #600, #678 + updateOptions: updateOptions, + target: scope_Target, // Issue #597 + removePips: removePips, + pips: pips // Issue #594 + }; + + if ( options.pips ) { + pips(options.pips); + } + + if ( options.tooltips ) { + tooltips(); + } + + aria(); + + return scope_Self; diff --git a/src/js/scope_start.js b/src/js/scope_start.js index f72f5419..58db5a5d 100644 --- a/src/js/scope_start.js +++ b/src/js/scope_start.js @@ -1,11 +1,11 @@ -function closure ( target, options, originalOptions ){ +function scope ( target, options, originalOptions ){ var actions = getActions(); var supportsTouchActionNone = getSupportsTouchActionNone(); var supportsPassive = supportsTouchActionNone && getSupportsPassive(); - // All variables local to 'closure' are prefixed with 'scope_' + // All variables local to 'scope' are prefixed with 'scope_' var scope_Target = target; var scope_Locations = []; var scope_Base; @@ -22,6 +22,7 @@ function closure ( target, options, originalOptions ){ var scope_DocumentElement = scope_Document.documentElement; var scope_Body = scope_Document.body; + // For horizontal sliders in standard ltr documents, // make .noUi-origin overflow to the left so the document doesn't scroll. var scope_DirOffset = (scope_Document.dir === 'rtl') || (options.ort === 1) ? 0 : 100; diff --git a/src/js/structure.js b/src/js/structure.js index 1a311e10..2706afd4 100644 --- a/src/js/structure.js +++ b/src/js/structure.js @@ -1,6 +1,7 @@ +/*! In this file: Construction of DOM elements; */ // Creates a node, adds it to target, returns the new node. - function addNodeTo ( target, className ) { + function addNodeTo ( addTarget, className ) { var div = scope_Document.createElement('div'); @@ -8,7 +9,7 @@ addClass(div, className); } - target.appendChild(div); + addTarget.appendChild(div); return div; } @@ -70,22 +71,22 @@ } // Initialize a single slider. - function addSlider ( target ) { + function addSlider ( addTarget ) { // Apply classes and data to the target. - addClass(target, options.cssClasses.target); + addClass(addTarget, options.cssClasses.target); if ( options.dir === 0 ) { - addClass(target, options.cssClasses.ltr); + addClass(addTarget, options.cssClasses.ltr); } else { - addClass(target, options.cssClasses.rtl); + addClass(addTarget, options.cssClasses.rtl); } if ( options.ort === 0 ) { - addClass(target, options.cssClasses.horizontal); + addClass(addTarget, options.cssClasses.horizontal); } else { - addClass(target, options.cssClasses.vertical); + addClass(addTarget, options.cssClasses.vertical); } - scope_Base = addNodeTo(target, options.cssClasses.base); + scope_Base = addNodeTo(addTarget, options.cssClasses.base); }