diff --git a/.gitignore b/.gitignore index c954da8d..e93fb70c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -/compiler \ No newline at end of file +/distribute +/node_modules +*.log +*.zip diff --git a/.gitmodules b/.gitmodules index c95f97b5..28e29886 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "libLink"] - path = libLink - url = https://github.com/leongersen/libLink -[submodule "wnumb"] - path = wnumb +[submodule "submodules/wNumb"] + path = submodules/wNumb url = https://github.com/leongersen/wnumb +[submodule "submodules/libLink"] + path = submodules/libLink + url = https://github.com/leongersen/libLink diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..663f1456 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,130 @@ +module.exports = function(grunt) { + + var VERSION_TEMPLATE = '/*! <%= pkg.name %> - <%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd HH:MM:ss") %> */' + + '\n\n'; + + function getFiles ( append ) { + + var files = [ + 'src/js/helpers/intro.js', + 'src/js/helpers.js', + 'src/js/constants.js', + 'src/js/range.js', + 'src/js/options.js', + 'src/js/structure.js', + 'src/js/scope_start.js', + 'src/js/scope_helpers.js', + 'src/js/scope_link.js', + 'src/js/scope_events.js', + 'src/js/scope.js', + 'src/js/scope_end.js', + 'src/js/interface.js' + ]; + + if ( append ) { + files = files.concat(append); + } + + files.push('src/js/helpers/outro.js'); + + return files; + } + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + concat: { + options: { + banner: VERSION_TEMPLATE + }, + basic: { + src: getFiles(), + dest: 'distribute/jquery.nouislider.js', + nonull: true + }, + all: { + src: ['submodules/wNumb/wNumb.js', 'submodules/libLink/jquery.libLink.js'].concat(getFiles('src/js/pips.js')), + dest: 'distribute/jquery.nouislider.all.js', + nonull: true + }, + }, + cssmin: { + all: { + options: { + banner: VERSION_TEMPLATE + }, + files: { + 'distribute/jquery.nouislider.min.css': ['src/jquery.nouislider.css'], + 'distribute/jquery.nouislider.pips.min.css': ['src/jquery.nouislider.pips.css'] + } + } + }, + 'string-replace': { + version: { + files: { + 'nouislider.jquery.json': 'src/jquery.json' + }, + options: { + replacements: [{ + pattern: /{{VERSION}}/g, + replacement: '<%= pkg.version %>' + }] + } + } + }, + jshint: { + options: { + browser: true, + indent: false, + laxbreak: true, + laxcomma: true, + validthis: true, + newcap: false + }, + basic: ['distribute/jquery.nouislider.js'], + all: ['distribute/jquery.nouislider.all.js'] + }, + uglify: { + all: { + files: { + 'distribute/jquery.nouislider.min.js': 'distribute/jquery.nouislider.js', + 'distribute/jquery.nouislider.all.min.js': 'distribute/jquery.nouislider.all.js' + } + } + }, + compress: { + all: { + options: { + archive: 'noUiSlider.<%= pkg.version %>.zip' + }, + files: [ + { src: ['**/*'], dest: '', cwd: 'distribute/', expand: true }, + { src: ['**/*.css'], dest: '', cwd: 'src/', expand: true }, + { src: ['**/archive.md'], rename: function(){ return 'README.md'; }, dest: '', cwd: 'src/', expand: true } + ] + } + } + }); + + // https://github.com/gruntjs/grunt-contrib-concat + grunt.loadNpmTasks('grunt-contrib-concat'); + + // https://github.com/gruntjs/grunt-contrib-uglify + grunt.loadNpmTasks('grunt-contrib-uglify'); + + // https://github.com/gruntjs/grunt-contrib-jshint + grunt.loadNpmTasks('grunt-contrib-jshint'); + + // https://github.com/erickrdch/grunt-string-replace + grunt.loadNpmTasks('grunt-string-replace'); + + // https://github.com/gruntjs/grunt-contrib-cssmin + grunt.loadNpmTasks('grunt-contrib-cssmin'); + + // https://github.com/gruntjs/grunt-contrib-compress + grunt.loadNpmTasks('grunt-contrib-compress'); + + grunt.registerTask('default', ['concat', 'jshint']); + grunt.registerTask('create', ['concat', 'jshint', 'uglify', 'cssmin']); + grunt.registerTask('release', ['string-replace', 'compress']); +}; diff --git a/README.md b/README.md index 50803042..ab4247a8 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,21 @@ noUiSlider is lightweight plugin, developed to be a jQuery UI alternative. It features cross-browser support, a `just-another-input-type` style of getting and setting values, a wide range of options and support for a bunch of touch devices. It works wonders on Android phones, iPhone & iPad, Windows phone and touch-screen laptops and tablets. It works excellent on the desktop too; All modern browsers and IE7+ are supported. The end result? A lean, extendible and bloat-less plugin that'll just do its job. To add even more flexibility, noUiSlider is compatible with both jQuery and Zepto.js. Oh, and the licensing terms are simple: [just do what you want](http://www.wtfpl.net/about/). +Documentation +------- +An extensive documentation, including **examples**, **options** and **configuration details**, is available here: [noUiSlider documentation](http://refreshless.com/nouislider/). + Changelog --------- +Latest changes: ++ Fixed an issue with the handle `z-index`. (#333) ++ Added pips formatting. (#330) ++ Added Grunt-based tasks. + +**Note for Bower users:** +The repository no longers contains any minified files. As you are using npm anyway, simply run `npm install` & `grunt create` to generate them. -noUiSlider is currently on version 7.0. This version contains significant changes from 6.2, improving various aspects and moving some features in their own module. +noUiSlider is currently on version 7. This version contains significant changes from 6, improving various aspects and moving some features in their own module. + All serialization features are now supported by my new project, [libLink](http://refreshless.com/liblink/). + All number formatting features have been moved into the [wNumb formatting library](http://refreshless.com/wnumb/). + The val method now only takes values, as all additional options are now automaticly detected. @@ -18,11 +29,6 @@ noUiSlider is currently on version 7.0. This version contains significant change + Added generation of pips/range points (#254, #260). + Fixed `tap` ignoring `margin` (#265). - -Documentation -------- -An extensive documentation, including **examples**, **options** and **configuration details**, is available here: [noUiSlider documentation](http://refreshless.com/nouislider/). - Unit Testing ------------ Unit tests where overhauled for noUiSlider 7. Most code is now covered, with events testing being slightly lacking due to it's browser dependant nature. @@ -32,8 +38,7 @@ Version numbering Version numbering follows the 'Semantic versioning' style. You'll find an excellent documentation at [Semver.org](http://semver.org/). -Compression and error checking +Contributing ------------------------------ -The plugin code is checked using ([JsLint](http://jslint.com/)). Any remaining errors and warnings are intentional. - -The plugin is compressed using the ([Google Closure Compiler](http://closure-compiler.appspot.com/home)). The source was initialy adapted to facilitate the `ADVANCED_OPTIMIZATIONS` level, but the risks this poses regarding to unintented renaming proved problematic. On Windows, the BAT script in this repository can be used to run the compiler. On OS X or Linux enviroments, simply run the `java -jar` command from the command line. +The plugin code can be managed using a Grunt-based task runner. +Use `npm install` to fetch all dependancies, then `grunt concat` to merge all files. diff --git a/bower.json b/bower.json index 63aeb2f0..86476bd9 100644 --- a/bower.json +++ b/bower.json @@ -11,10 +11,6 @@ "input", "slide" ], - "main": [ - "jquery.nouislider.min.js", - "jquery.nouislider.css" - ], "dependencies": { "jquery": ">= 1.7.0" }, diff --git a/compile.bat b/compile.bat deleted file mode 100644 index a033f66c..00000000 --- a/compile.bat +++ /dev/null @@ -1,42 +0,0 @@ -@echo off - -:: Set all paths as variables -set "wnumb=wnumb\wNumb.js" -set "wnumbMin=wnumb\wNumb.min.js" -set "link=libLink\jquery.liblink.js" - -set "jquery=compiler\jquery-1.9.js" -set "zepto=compiler\zepto.js" - -set "range=src\module.range.js" -set "options=src\module.options.js" -set "source=src\module.base.js" -set "pips=src\optional.pips.js" - -set "nouislidermin=jquery.nouislider.min.js" -set "nouisliderfull=jquery.nouislider.full.min.js" - -set "compiler=compiler\compiler.jar" -set "level=--compilation_level SIMPLE_OPTIMIZATIONS" -set "verbose=--warning_level VERBOSE" - -:: Remove the existing file so we can be sure a new one is generated. -if exist %nouislidermin% ( - del %nouislidermin% - echo "Removed %nouislidermin%." -) -if exist %nouisliderfull% ( - del %nouisliderfull% - echo "Removed %nouisliderfull%," -) - -echo "Ready." - -PAUSE - -java -jar %compiler% %level% %verbose% --js %wnumb% --js_output_file %wnumbMin% -java -jar %compiler% %level% %verbose% --externs %jquery% --externs %zepto% --js %range% --js %options% --js %source% --js_output_file %nouislidermin% -java -jar %compiler% %level% %verbose% --externs %jquery% --externs %zepto% --js %wnumb% --js %link% --js %range% --js %options% --js %source% --js %pips% --js_output_file %nouisliderfull% - -echo "Done." -PAUSE diff --git a/jquery.nouislider.full.min.js b/jquery.nouislider.full.min.js deleted file mode 100644 index 215ac05a..00000000 --- a/jquery.nouislider.full.min.js +++ /dev/null @@ -1,33 +0,0 @@ -(function(){function c(a){return a.split("").reverse().join("")}function l(a,b){return a.substring(0,b.length)===b}function q(a,b,d){if((a[b]||a[d])&&a[b]===a[d])throw Error(b);}function m(a,b,d,e,n,h,w,k,A,H,D,g){w=g;var l,s=D="";h&&(g=h(g));if("number"!==typeof g||!isFinite(g))return!1;a&&0===parseFloat(g.toFixed(a))&&(g=0);0>g&&(l=!0,g=Math.abs(g));!1!==a&&(h=g,g=Math.pow(10,a),g=(Math.round(h*g)/g).toFixed(a));g=g.toString();-1!==g.indexOf(".")?(a=g.split("."),h=a[0],d&&(D=d+a[1])):h=g;b&&(h= -c(h).match(/.{1,3}/g),h=c(h.join(c(b))));l&&k&&(s+=k);e&&(s+=e);l&&A&&(s+=A);s=s+h+D;n&&(s+=n);H&&(s=H(s,w));return s}function u(a,b,d,c,e,h,w,k,A,H,D,g){var m;a="";D&&(g=D(g));if(!g||"string"!==typeof g)return!1;k&&l(g,k)&&(g=g.replace(k,""),m=!0);c&&l(g,c)&&(g=g.replace(c,""));A&&l(g,A)&&(g=g.replace(A,""),m=!0);if(c=e)c=g.slice(-1*e.length)===e;c&&(g=g.slice(0,-1*e.length));b&&(g=g.split(b).join(""));d&&(g=g.replace(d,"."));m&&(a+="-");a=(a+g).replace(/[^0-9\.\-.]/g,"");if(""===a)return!1;a=Number(a); -w&&(a=w(a));return"number"===typeof a&&isFinite(a)?a:!1}function a(a){var b,d,c,n={};for(b=0;bc)n[d]=c;else throw Error(d);else if("encoder"===d||"decoder"===d||"edit"===d||"undo"===d)if("function"===typeof c)n[d]=c;else throw Error(d);else if("string"===typeof c)n[d]=c;else throw Error(d);q(n,"mark","thousand");q(n,"prefix","negative");q(n,"prefix", -"negativeBefore");return n}function b(a,b,d){var c,n=[];for(c=0;c"),!0},function(a){if("string"===typeof a&&0!==a.indexOf("-")){this.method="val";var b=document.createElement("input");b.name=a;b.type="hidden";this.target=this.el=c(b);return!0}},function(a){if("function"===typeof a)return this.target=!1,this.method=a,!0},function(a,b){if(l(a)&&!b)return a.is("input, select, textarea")?(this.method="val",this.target=a.on("change.liblink",this.changeHandler)): -(this.target=a,this.method="html"),!0},function(a,b){if(l(a)&&("function"===typeof b||"string"===typeof b&&a[b]))return this.method=b,this.target=a,!0}];q.prototype.set=function(a){var b=Array.prototype.slice.call(arguments).slice(1);this.lastSetValue=this.formatInstance.to(a);b.unshift(this.lastSetValue);("function"===typeof this.method?this.method:this.target[this.method]).apply(this.target,b)};m.prototype.push=function(a,b){this.items.push(a);b&&this.elements.push(b)};m.prototype.reconfirm=function(a){var b; -for(b=0;b=b[d];)d+=1;return d}function u(a,b,d,c){this.xPct=[];this.xVal=[];this.xSteps=[c||!1];this.xNumSteps=[!1];this.snap=b;this.direction=d;for(var f in a)if(a.hasOwnProperty(f)){b=f;d=a[f];c=void 0;"number"===typeof d&&(d=[d]);if("[object Array]"!==Object.prototype.toString.call(d))throw Error("noUiSlider: 'range' contains invalid value.");c="min"===b?0: -"max"===b?100:parseFloat(b);if(!l(c)||!l(d[0]))throw Error("noUiSlider: 'range' value isn't numeric.");this.xPct.push(c);this.xVal.push(d[0]);c?this.xSteps.push(isNaN(d[1])?!1:d[1]):isNaN(d[1])||(this.xSteps[0]=d[1])}this.xNumSteps=this.xSteps.slice(0);for(f in this.xNumSteps)this.xNumSteps.hasOwnProperty(f)&&(a=Number(f),(b=this.xNumSteps[f])&&(this.xSteps[a]=q([this.xVal[a],this.xVal[a+1]],b)/(100/(this.xPct[a+1]-this.xPct[a]))))}u.prototype.getMargin=function(a){return 2===this.xPct.length?q(this.xVal, -a):!1};u.prototype.toStepping=function(a){var b=this.xVal,c=this.xPct;if(a>=b.slice(-1)[0])a=100;else{var e=m(a,b),f,l;f=b[e-1];l=b[e];b=c[e-1];c=c[e];f=[f,l];a=q(f,0>f[0]?a+Math.abs(f[0]):a-f[0]);a=b+a/(100/(c-b))}this.direction&&(a=100-a);return a};u.prototype.fromStepping=function(a){this.direction&&(a=100-a);var b;var c=this.xVal;b=this.xPct;if(100<=a)b=c.slice(-1)[0];else{var e=m(a,b),f,l;f=c[e-1];l=c[e];c=b[e-1];f=[f,l];b=100/(b[e]-c)*(a-c)*(f[1]-f[0])/100+f[0]}a=Math.pow(10,7);return Number((Math.round(b* -a)/a).toFixed(7))};u.prototype.getStep=function(a){this.direction&&(a=100-a);var b=this.xPct,c=this.xSteps,e=this.snap;if(100!==a){var f=m(a,b);e?(c=b[f-1],b=b[f],a=a-c>(b-c)/2?b:c):(c[f-1]?(e=b[f-1],c=c[f-1],b=Math.round((a-b[f-1])/c)*c,b=e+b):b=a,a=b)}this.direction&&(a=100-a);return a};u.prototype.getApplicableStep=function(a){var b=m(a,this.xPct);a=100===a?2:1;return[this.xNumSteps[b-2],this.xVal[b-a],this.xNumSteps[b-a]]};u.prototype.convert=function(a){return this.getStep(this.toStepping(a))}; -c.noUiSlider={Spectrum:u}})(window.jQuery||window.Zepto);(function(c){function l(a){return"number"===typeof a&&!isNaN(a)&&isFinite(a)}function q(a,b){if(!l(b))throw Error("noUiSlider: 'step' is not numeric.");a.singleStep=b}function m(a,b){if("object"!==typeof b||c.isArray(b))throw Error("noUiSlider: 'range' is not an object.");if(void 0===b.min||void 0===b.max)throw Error("noUiSlider: Missing 'min' or 'max' in 'range'.");a.spectrum=new c.noUiSlider.Spectrum(b,a.snap,a.dir,a.singleStep)}function u(a,b){var d=b;b=c.isArray(d)?d:[d];if(!c.isArray(b)||!b.length|| -2
").addClass(h[2]),e=["-lower","-upper"];a&&e.reverse();d.children().addClass(h[3]+" "+h[3]+e[b]);return d}function u(a,b,c){switch(a){case 1:b.addClass(h[7]);c[0].addClass(h[6]);break;case 3:c[1].addClass(h[6]);case 2:c[0].addClass(h[7]);case 0:b.addClass(h[6])}}function a(a,b,c){var d,e=[];for(d=0;d").appendTo(d).addClass(h[1])}function d(d,k,e){function f(){return B[["width","height"][k.ort]]()}function m(a){var b,c=[v.val()];for(b=0;bd&&(e+=Math.abs(d)),100=c[1]?c[2]:c[0],c[2]]]});return g(a)};d.getInfo=function(){return[E,k.style,k.ort]};v.val(k.start)}function e(a){if(!this.length)throw Error("noUiSlider: Can't initialize slider on empty selection.");var b=c.noUiSlider.testOptions(a,this);return this.each(function(){d(this, -b,a)})}function f(a){return this.each(function(){if(this.destroy){var b=c(this).val(),d=this.destroy(),e=c.extend({},d,a);c(this).noUiSlider(e);this.reappend();d.start===e.start&&c(this).val(b)}else c(this).noUiSlider(a)})}function z(){return this[0][arguments.length?"vSet":"vGet"].apply(this[0],arguments)}var p=c(document),r=c.fn.val,n=window.navigator.pointerEnabled?{start:"pointerdown",move:"pointermove",end:"pointerup"}:window.navigator.msPointerEnabled?{start:"MSPointerDown",move:"MSPointerMove", -end:"MSPointerUp"}:{start:"mousedown touchstart",move:"mousemove touchmove",end:"mouseup touchend"},h="noUi-target noUi-base noUi-origin noUi-handle noUi-horizontal noUi-vertical noUi-background noUi-connect noUi-ltr noUi-rtl noUi-dragable noUi-state-drag noUi-state-tap noUi-active noUi-stacking".split(" ");c.fn.val=function(){var a=arguments,b=c(this[0]);return arguments.length?this.each(function(){(c(this).hasClass(h[0])?z:r).apply(c(this),a)}):(b.hasClass(h[0])?z:r).call(b)};c.fn.noUiSlider= -function(a,b){return(b?f:e).call(this,a)}})(window.jQuery||window.Zepto);(function(c){function l(a){return c.grep(a,function(b,d){return d===c.inArray(b,a)})}function q(a,b,d,e){if("range"===b||"steps"===b)return a.xVal;if("count"===b){b=100/(d-1);var f,l=0;for(d=[];100>=(f=l++*b);)d.push(f);b="positions"}if("positions"===b)return c.map(d,function(b){return a.fromStepping(e?a.getStep(b):b)});if("values"===b)return e?c.map(d,function(b){return a.fromStepping(a.getStep(a.toStepping(b)))}):d}function m(a,b,d,e){var f=a.direction,m={},p=a.xVal[0],r=a.xVal[a.xVal.length-1], -n=!1,h=!1,w=0;a.direction=0;e=l(e.slice().sort(function(a,b){return a-b}));e[0]!==p&&(e.unshift(p),n=!0);e[e.length-1]!==r&&(e.push(r),h=!0);c.each(e,function(f){var l,p,r,g=e[f],q=e[f+1],s,u,x,G;"steps"===d&&(l=a.xNumSteps[f]);l||(l=q-g);if(!1!==g&&void 0!==q)for(p=g;p<=q;p+=l){s=a.toStepping(p);r=s-w;x=r/b;x=Math.round(x);G=r/x;for(r=1;r<=x;r+=1)u=w+r*G,m[u.toFixed(5)]=["x",0];x=-1");m.addClass("noUi-pips noUi-pips-"+p);c.each(e,function(a,b){d&&(a=100-a);m.append("
");b[1]&&m.append("
"+Math.round(b[0])+"
")});return m}c.fn.noUiSlider_pips=function(a){var b=a.mode,d=a.density||1,e=a.filter||!1,f=a.values||!1, -l=a.stepped||!1;return this.each(function(){var a=this.getInfo(),r=q(a[0],b,f,l),r=m(a[0],d,b,r);return c(this).append(u(a[1],a[2],a[0].direction,r,e))})}})(window.jQuery||window.Zepto); diff --git a/jquery.nouislider.min.js b/jquery.nouislider.min.js deleted file mode 100644 index 850b0b32..00000000 --- a/jquery.nouislider.min.js +++ /dev/null @@ -1,22 +0,0 @@ -(function(a){function q(d){return"number"===typeof d&&!isNaN(d)&&isFinite(d)}function s(d,g){return 100*g/(d[1]-d[0])}function p(d,g){for(var e=1;d>=g[e];)e+=1;return e}function k(d,g,e,a){this.xPct=[];this.xVal=[];this.xSteps=[a||!1];this.xNumSteps=[!1];this.snap=g;this.direction=e;for(var f in d)if(d.hasOwnProperty(f)){g=f;e=d[f];a=void 0;"number"===typeof e&&(e=[e]);if("[object Array]"!==Object.prototype.toString.call(e))throw Error("noUiSlider: 'range' contains invalid value.");a="min"===g?0: -"max"===g?100:parseFloat(g);if(!q(a)||!q(e[0]))throw Error("noUiSlider: 'range' value isn't numeric.");this.xPct.push(a);this.xVal.push(e[0]);a?this.xSteps.push(isNaN(e[1])?!1:e[1]):isNaN(e[1])||(this.xSteps[0]=e[1])}this.xNumSteps=this.xSteps.slice(0);for(f in this.xNumSteps)this.xNumSteps.hasOwnProperty(f)&&(d=Number(f),(g=this.xNumSteps[f])&&(this.xSteps[d]=s([this.xVal[d],this.xVal[d+1]],g)/(100/(this.xPct[d+1]-this.xPct[d]))))}k.prototype.getMargin=function(d){return 2===this.xPct.length?s(this.xVal, -d):!1};k.prototype.toStepping=function(d){var g=this.xVal,a=this.xPct;if(d>=g.slice(-1)[0])d=100;else{var m=p(d,g),f,k;f=g[m-1];k=g[m];g=a[m-1];a=a[m];f=[f,k];d=s(f,0>f[0]?d+Math.abs(f[0]):d-f[0]);d=g+d/(100/(a-g))}this.direction&&(d=100-d);return d};k.prototype.fromStepping=function(d){this.direction&&(d=100-d);var a;var e=this.xVal;a=this.xPct;if(100<=d)a=e.slice(-1)[0];else{var m=p(d,a),f,k;f=e[m-1];k=e[m];e=a[m-1];f=[f,k];a=100/(a[m]-e)*(d-e)*(f[1]-f[0])/100+f[0]}d=Math.pow(10,7);return Number((Math.round(a* -d)/d).toFixed(7))};k.prototype.getStep=function(d){this.direction&&(d=100-d);var a=this.xPct,e=this.xSteps,k=this.snap;if(100!==d){var f=p(d,a);k?(e=a[f-1],a=a[f],d=d-e>(a-e)/2?a:e):(e[f-1]?(k=a[f-1],e=e[f-1],a=Math.round((d-a[f-1])/e)*e,a=k+a):a=d,d=a)}this.direction&&(d=100-d);return d};k.prototype.getApplicableStep=function(a){var g=p(a,this.xPct);a=100===a?2:1;return[this.xNumSteps[g-2],this.xVal[g-a],this.xNumSteps[g-a]]};k.prototype.convert=function(a){return this.getStep(this.toStepping(a))}; -a.noUiSlider={Spectrum:k}})(window.jQuery||window.Zepto);(function(a){function q(c){return"number"===typeof c&&!isNaN(c)&&isFinite(c)}function s(c,b){if(!q(b))throw Error("noUiSlider: 'step' is not numeric.");c.singleStep=b}function p(c,b){if("object"!==typeof b||a.isArray(b))throw Error("noUiSlider: 'range' is not an object.");if(void 0===b.min||void 0===b.max)throw Error("noUiSlider: Missing 'min' or 'max' in 'range'.");c.spectrum=new a.noUiSlider.Spectrum(b,c.snap,c.dir,c.singleStep)}function k(c,b){var d=b;b=a.isArray(d)?d:[d];if(!a.isArray(b)||!b.length|| -2
").addClass(h[2]),e=["-lower","-upper"];c&&e.reverse();d.children().addClass(h[3]+" "+h[3]+e[b]);return d}function k(a,b,d){switch(a){case 1:b.addClass(h[7]);d[0].addClass(h[6]);break;case 3:d[1].addClass(h[6]);case 2:d[0].addClass(h[7]);case 0:b.addClass(h[6])}}function d(a,b,d){var e,f=[];for(e=0;e").appendTo(d).addClass(h[1])}function e(c,b,e){function f(){return v[["width","height"][b.ort]]()}function m(b){var a,c=[n.val()];for(a=0;ad&&(e+=Math.abs(d)),100=c[1]?c[2]:c[0],c[2]]]});return p(b)};c.getInfo=function(){return[z,b.style,b.ort]};n.val(b.start)}function m(c){if(!this.length)throw Error("noUiSlider: Can't initialize slider on empty selection.");var b=a.noUiSlider.testOptions(c,this);return this.each(function(){e(this, -b,c)})}function f(c){return this.each(function(){if(this.destroy){var b=a(this).val(),d=this.destroy(),e=a.extend({},d,c);a(this).noUiSlider(e);this.reappend();d.start===e.start&&a(this).val(b)}else a(this).noUiSlider(c)})}function x(){return this[0][arguments.length?"vSet":"vGet"].apply(this[0],arguments)}var y=a(document),t=a.fn.val,u=window.navigator.pointerEnabled?{start:"pointerdown",move:"pointermove",end:"pointerup"}:window.navigator.msPointerEnabled?{start:"MSPointerDown",move:"MSPointerMove", -end:"MSPointerUp"}:{start:"mousedown touchstart",move:"mousemove touchmove",end:"mouseup touchend"},h="noUi-target noUi-base noUi-origin noUi-handle noUi-horizontal noUi-vertical noUi-background noUi-connect noUi-ltr noUi-rtl noUi-dragable noUi-state-drag noUi-state-tap noUi-active noUi-stacking".split(" ");a.fn.val=function(){var c=arguments,b=a(this[0]);return arguments.length?this.each(function(){(a(this).hasClass(h[0])?x:t).apply(a(this),c)}):(b.hasClass(h[0])?x:t).call(b)};a.fn.noUiSlider= -function(a,b){return(b?f:m).call(this,a)}})(window.jQuery||window.Zepto); diff --git a/nouislider.jquery.json b/nouislider.jquery.json index a62c2f1a..beb32d7c 100644 --- a/nouislider.jquery.json +++ b/nouislider.jquery.json @@ -1,4 +1,4 @@ -{ +{ "name": "nouislider", "title": "noUiSlider - jQuery Range Slider", "description": "A lightweight, highly customizable range slider without bloat.", @@ -16,7 +16,7 @@ "url": "http://www.wtfpl.net/" } ], - "version": "7.0.1", + "version": "7.0.2", "author": { "name": "Léon Gersen", "url": "https://twitter.com/LeonGersen" @@ -28,4 +28,4 @@ "dependencies": { "jquery": ">=1.7" } -} \ No newline at end of file +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..0380ff4a --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "noUiSlider", + "version": "7.0.2", + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-compress": "^0.11.0", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-cssmin": "^0.10.0", + "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-uglify": "^0.5.1", + "grunt-string-replace": "^0.2.7" + }, + "repository": { + "type": "git", + "url": "git://github.com/leongersen/noUiSlider.git" + } +} diff --git a/src/archive.md b/src/archive.md new file mode 100644 index 00000000..214703ae --- /dev/null +++ b/src/archive.md @@ -0,0 +1,7 @@ +Use the following files: + +(RECOMMENDED) jquery.nouislider.all.min.js - If you want to use libLink, wNumb and the Pips add-on. Includes noUiSlider. +(ALTERNATIVE) jquery.nouislider.min.js - noUiSlider without any additions + +(REQUIRED) jquery.nouislider.min.css - Styling (REQUIRED) +(RECOMMENDED) jquery.nouislider.pips.min.css - If you are using the Pips add-on. diff --git a/src/jquery.json b/src/jquery.json new file mode 100644 index 00000000..20280c1f --- /dev/null +++ b/src/jquery.json @@ -0,0 +1,31 @@ +{ + "name": "nouislider", + "title": "noUiSlider - jQuery Range Slider", + "description": "A lightweight, highly customizable range slider without bloat.", + "keywords": [ + "range", + "slider", + "form", + "input", + "serialize", + "handle" + ], + "licenses": [ + { + "type": "WTFPL", + "url": "http://www.wtfpl.net/" + } + ], + "version": "{{VERSION}}", + "author": { + "name": "Léon Gersen", + "url": "https://twitter.com/LeonGersen" + }, + "bugs": "https://github.com/leongersen/noUiSlider/issues", + "homepage": "http://refreshless.com/nouislider/", + "docs": "http://refreshless.com/nouislider/", + "download": "http://refreshless.com/nouislider/download/", + "dependencies": { + "jquery": ">=1.7" + } +} diff --git a/jquery.nouislider.css b/src/jquery.nouislider.css similarity index 99% rename from jquery.nouislider.css rename to src/jquery.nouislider.css index 1e34ac94..f0a53f22 100644 --- a/jquery.nouislider.css +++ b/src/jquery.nouislider.css @@ -53,8 +53,7 @@ /* Painting and performance; * Browsers can paint handles in their own layer. */ -.noUi-origin, -.noUi-handle { +.noUi-base { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } diff --git a/jquery.nouislider.pips.css b/src/jquery.nouislider.pips.css similarity index 100% rename from jquery.nouislider.pips.css rename to src/jquery.nouislider.pips.css diff --git a/src/js/constants.js b/src/js/constants.js new file mode 100644 index 00000000..df2b2cf9 --- /dev/null +++ b/src/js/constants.js @@ -0,0 +1,49 @@ + + var + // Cache the document selector; + /** @const */ + doc = $(document), + // Make a backup of the original jQuery/Zepto .val() method. + /** @const */ + $val = $.fn.val, + // Namespace for binding and unbinding slider events; + /** @const */ + namespace = '.nui', + // Determine the events to bind. IE11 implements pointerEvents without + // a prefix, which breaks compatibility with the IE10 implementation. + /** @const */ + actions = window.navigator.pointerEnabled ? { + start: 'pointerdown', + move: 'pointermove', + end: 'pointerup' + } : window.navigator.msPointerEnabled ? { + start: 'MSPointerDown', + move: 'MSPointerMove', + end: 'MSPointerUp' + } : { + start: 'mousedown touchstart', + move: 'mousemove touchmove', + end: 'mouseup touchend' + }, + // Re-usable list of classes; + /** @const */ + Classes = [ +/* 0 */ 'noUi-target' +/* 1 */ ,'noUi-base' +/* 2 */ ,'noUi-origin' +/* 3 */ ,'noUi-handle' +/* 4 */ ,'noUi-horizontal' +/* 5 */ ,'noUi-vertical' +/* 6 */ ,'noUi-background' +/* 7 */ ,'noUi-connect' +/* 8 */ ,'noUi-ltr' +/* 9 */ ,'noUi-rtl' +/* 10 */ ,'noUi-dragable' +/* 11 */ ,'' +/* 12 */ ,'noUi-state-drag' +/* 13 */ ,'' +/* 14 */ ,'noUi-state-tap' +/* 15 */ ,'noUi-active' +/* 16 */ ,'' +/* 17 */ ,'noUi-stacking' + ]; diff --git a/src/js/helpers.js b/src/js/helpers.js new file mode 100644 index 00000000..c7153512 --- /dev/null +++ b/src/js/helpers.js @@ -0,0 +1,41 @@ + + // Removes duplicates from an array. + function unique(array) { + return $.grep(array, function(el, index) { + return index === $.inArray(el, array); + }); + } + + // Round a value to the closest 'to'. + function closest ( value, to ) { + return Math.round(value / to) * to; + } + + // Checks whether a value is numerical. + function isNumeric ( a ) { + return typeof a === 'number' && !isNaN( a ) && isFinite( a ); + } + + // Rounds a number to 7 supported decimals. + function accurateNumber( number ) { + var p = Math.pow(10, 7); + return Number((Math.round(number*p)/p).toFixed(7)); + } + + // Sets a class and removes it after [duration] ms. + function addClassFor ( element, className, duration ) { + element.addClass(className); + setTimeout(function(){ + element.removeClass(className); + }, duration); + } + + // Limits a value to 0 - 100 + function limit ( a ) { + return Math.max(Math.min(a, 100), 0); + } + + // Wraps a variable as an array, if it isn't one yet. + function asArray ( a ) { + return $.isArray(a) ? a : [a]; + } diff --git a/src/js/helpers/intro.js b/src/js/helpers/intro.js new file mode 100644 index 00000000..b412a6cf --- /dev/null +++ b/src/js/helpers/intro.js @@ -0,0 +1,6 @@ +/*jslint browser: true */ +/*jslint white: true */ + +(function( $ ){ + + 'use strict'; diff --git a/src/js/helpers/outro.js b/src/js/helpers/outro.js new file mode 100644 index 00000000..95e20113 --- /dev/null +++ b/src/js/helpers/outro.js @@ -0,0 +1 @@ +}( window.jQuery || window.Zepto )); diff --git a/src/js/interface.js b/src/js/interface.js new file mode 100644 index 00000000..db47f18e --- /dev/null +++ b/src/js/interface.js @@ -0,0 +1,90 @@ + + // Run the standard initializer + function initialize ( originalOptions ) { + + // Throw error if group is empty. + if ( !this.length ){ + throw new Error("noUiSlider: Can't initialize slider on empty selection."); + } + + // Test the options once, not for every slider. + var options = testOptions( originalOptions, this ); + + // Loop all items, and provide a new closed-scope environment. + return this.each(function(){ + closure(this, options, originalOptions); + }); + } + + // Destroy the slider, then re-enter initialization. + function rebuild ( options ) { + + return this.each(function(){ + + // The rebuild flag can be used if the slider wasn't initialized yet. + if ( !this.destroy ) { + $(this).noUiSlider( options ); + return; + } + + // Get the current values from the slider, + // including the initialization options. + var values = $(this).val(), originalOptions = this.destroy(), + + // Extend the previous options with the newly provided ones. + newOptions = $.extend( {}, originalOptions, options ); + + // Run the standard initializer. + $(this).noUiSlider( newOptions ); + + // Place Link elements back. + this.reappend(); + + // If the start option hasn't changed, + // reset the previous values. + if ( originalOptions.start === newOptions.start ) { + $(this).val(values); + } + }); + } + + // Access the internal getting and setting methods based on argument count. + function value ( ) { + return this[0][ !arguments.length ? 'vGet' : 'vSet' ].apply(this[0], arguments); + } + + // Override the .val() method. Test every element. Is it a slider? Go to + // the slider value handling. No? Use the standard method. + // Note how $.fn.val expects 'this' to be an instance of $. For convenience, + // the above 'value' function does too. + $.fn.val = function ( ) { + + // this === instanceof $ + + function valMethod( a ){ + return a.hasClass(Classes[0]) ? value : $val; + } + + var args = arguments, + first = $(this[0]); + + if ( !arguments.length ) { + return valMethod(first).call(first); + } + + // Return the set so it remains chainable + return this.each(function(){ + valMethod($(this)).apply($(this), args); + }); + }; + +// Extend jQuery/Zepto with the noUiSlider method. + $.fn.noUiSlider = function ( options, rebuildFlag ) { + + switch ( options ) { + case 'step': return this[0].getCurrentStep(); + case 'options': return this[0].getOriginalOptions(); + } + + return ( rebuildFlag ? rebuild : initialize ).call(this, options); + }; diff --git a/src/module.options.js b/src/js/options.js similarity index 92% rename from src/module.options.js rename to src/js/options.js index 20d64aa8..54e3bc9a 100644 --- a/src/module.options.js +++ b/src/js/options.js @@ -1,6 +1,3 @@ -/*jslint browser: true */ -/*jslint white: true */ - /* Every input option is tested and parsed. This'll prevent endless validation in internal methods. These tests are structured with an item for every option available. An @@ -14,20 +11,6 @@ or true when everything is OK. It can also modify the option object, to make sure all values can be correctly looped elsewhere. */ -(function( $ ){ - - 'use strict'; - - // Wraps a variable as an array, if it isn't one yet. - function asArray ( a ) { - return $.isArray(a) ? a : [a]; - } - - // Checks whether a value is numerical. - function isNumeric ( a ) { - return typeof a === 'number' && !isNaN( a ) && isFinite( a ); - } - /** @const */ var defaultFormatter = { 'to': function( value ){ return value.toFixed(2); @@ -56,7 +39,7 @@ throw new Error("noUiSlider: Missing 'min' or 'max' in 'range'."); } - parsed.spectrum = new $.noUiSlider.Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); + parsed.spectrum = new Spectrum(entry, parsed.snap, parsed.dir, parsed.singleStep); } function testStart ( parsed, entry ) { @@ -206,6 +189,7 @@ throw new Error( "noUiSlider: 'format' requires 'to' and 'from' methods."); } + // Test all developer settings and parse to assumption-safe values. function testOptions ( options ) { var parsed = { @@ -262,8 +246,3 @@ return parsed; } - - // Test all developer settings and parse to assumption-safe values. - $.noUiSlider.testOptions = testOptions; - -}( window.jQuery || window.Zepto )); diff --git a/src/optional.pips.js b/src/js/pips.js similarity index 94% rename from src/optional.pips.js rename to src/js/pips.js index a7c862d0..f5137ddb 100644 --- a/src/optional.pips.js +++ b/src/js/pips.js @@ -1,19 +1,3 @@ -/*jslint browser: true */ -/*jslint white: true */ - -(function( $ ){ - - 'use strict'; - - // Removes duplicates from an array. - function unique(array) { - return $.grep(array, function(el, index) { - return index === $.inArray(el, array); - }); - } - -// Pips - function getGroup ( $Spectrum, mode, values, stepped ) { // Use the range. @@ -170,7 +154,7 @@ return indexes; } - function addMarking ( CSSstyle, orientation, direction, spread, filterFunc ) { + function addMarking ( CSSstyle, orientation, direction, spread, filterFunc, formatter ) { var style = ['horizontal', 'vertical'][orientation], element = $('
'); @@ -197,7 +181,7 @@ // Values are only appended for points marked '1' or '2'. if ( values[1] ) { - element.append('
' + Math.round(values[0]) + '
'); + element.append('
' + formatter.to(values[0]) + '
'); } } @@ -213,6 +197,9 @@ density = grid.density || 1, filter = grid.filter || false, values = grid.values || false, + format = grid.format || { + to: Math.round + }, stepped = grid.stepped || false; return this.each(function(){ @@ -226,9 +213,8 @@ info[2], info[0].direction, spread, - filter + filter, + format )); }); }; - -}( window.jQuery || window.Zepto )); diff --git a/src/module.range.js b/src/js/range.js similarity index 90% rename from src/module.range.js rename to src/js/range.js index 9c44754d..c398c9fb 100644 --- a/src/module.range.js +++ b/src/js/range.js @@ -1,36 +1,11 @@ -/*jslint browser: true */ -/*jslint white: true */ -(function( $ ){ - - 'use strict'; - -// Helpers +// Value calculation // Determine the size of a sub-range in relation to a full range. function subRangeRatio ( pa, pb ) { return (100 / (pb - pa)); } - // Round a value to the closest 'to'. - function closest ( value, to ) { - return Math.round(value / to) * to; - } - - // Checks whether a value is numerical. - function isNumeric ( a ) { - return typeof a === 'number' && !isNaN( a ) && isFinite( a ); - } - - // Rounds a number to 7 supported decimals. - function accurateNumber( number ) { - var p = Math.pow(10, 7); - return Number((Math.round(number*p)/p).toFixed(7)); - } - - -// Value calculation - // (percentage) How many percent is this value of this range? function fromPercentage ( range, value ) { return (value * 100) / ( range[1] - range[0] ); @@ -282,9 +257,3 @@ Spectrum.prototype.convert = function ( value ) { return this.getStep(this.toStepping(value)); }; - - $.noUiSlider = { - Spectrum: Spectrum - }; - -}( window.jQuery || window.Zepto )); diff --git a/src/js/scope.js b/src/js/scope.js new file mode 100644 index 00000000..5b03dd2e --- /dev/null +++ b/src/js/scope.js @@ -0,0 +1,234 @@ + + // Test suggested values and apply margin, step. + function setHandle ( handle, to, noLimitOption ) { + + var trigger = handle[0] !== $Handles[0][0] ? 1 : 0, + lowerMargin = $Locations[0] + options.margin, + upperMargin = $Locations[1] - options.margin, + lowerLimit = $Locations[0] + options.limit, + upperLimit = $Locations[1] - options.limit; + + // For sliders with multiple handles, + // limit movement to the other handle. + // Apply the margin option by adding it to the handle positions. + if ( $Handles.length > 1 ) { + to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin ); + } + + // The limit option has the opposite effect, limiting handles to a + // maximum distance from another. Limit must be > 0, as otherwise + // handles would be unmoveable. 'noLimitOption' is set to 'false' + // for the .val() method, except for pass 4/4. + if ( noLimitOption !== false && options.limit && $Handles.length > 1 ) { + to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit ); + } + + // Handle the step option. + to = $Spectrum.getStep( to ); + + // Limit to 0/100 for .val input, trim anything beyond 7 digits, as + // JavaScript has some issues in its floating point implementation. + to = limit(parseFloat(to.toFixed(7))); + + // Return false if handle can't move. + if ( to === $Locations[trigger] ) { + return false; + } + + // Set the handle to the new position. + handle.css( options.style, to + '%' ); + + // Force proper handle stacking + if ( handle.is(':first-child') ) { + handle.toggleClass(Classes[17], to > 50 ); + } + + // Update locations. + $Locations[trigger] = to; + + // Convert the value to the slider stepping/range. + $Values[trigger] = $Spectrum.fromStepping( to ); + + linkUpdate(triggerPos[trigger]); + + return true; + } + + // Loop values from value method and apply them. + function setValues ( count, values ) { + + var i, trigger, to; + + // With the limit option, we'll need another limiting pass. + if ( options.limit ) { + count += 1; + } + + // If there are multiple handles to be set run the setting + // mechanism twice for the first handle, to make sure it + // can be bounced of the second one properly. + for ( i = 0; i < count; i += 1 ) { + + trigger = i%2; + + // Get the current argument from the array. + to = values[trigger]; + + // Setting with null indicates an 'ignore'. + // Inputting 'false' is invalid. + if ( to !== null && to !== false ) { + + // If a formatted number was passed, attemt to decode it. + if ( typeof to === 'number' ) { + to = String(to); + } + + to = options.format.from( to ); + + // Request an update for all links if the value was invalid. + // Do so too if setting the handle fails. + if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) { + + linkUpdate(triggerPos[trigger]); + } + } + } + } + + // Set the slider value. + function valueSet ( input ) { + + // LibLink: don't accept new values when currently emitting changes. + if ( $Target[0].LinkIsEmitting ) { + return this; + } + + var count, values = asArray( input ); + + // The RTL settings is implemented by reversing the front-end, + // internal mechanisms are the same. + if ( options.dir && options.handles > 1 ) { + values.reverse(); + } + + // Animation is optional. + // Make sure the initial values where set before using animated + // placement. (no report, unit testing); + if ( options.animate && $Locations[0] !== -1 ) { + addClassFor( $Target, Classes[14], 300 ); + } + + // Determine how often to set the handles. + count = $Handles.length > 1 ? 3 : 1; + + if ( values.length === 1 ) { + count = 1; + } + + setValues ( count, values ); + + // Fire the 'set' event. As of noUiSlider 7, + // this is no longer optional. + fireEvents(['set']); + + return this; + } + + // Get the slider value. + function valueGet ( ) { + + var i, retour = []; + + // Get the value from all handles. + for ( i = 0; i < options.handles; i += 1 ){ + retour[i] = options.format.to( $Values[i] ); + } + + return inSliderOrder( retour ); + } + + // Destroy the slider and unbind all events. + function destroyTarget ( ) { + + // Unbind events on the slider, remove all classes and child elements. + $(this).off(namespace) + .removeClass(Classes.join(' ')) + .empty(); + + delete this.LinkUpdate; + delete this.LinkConfirm; + delete this.LinkDefaultFormatter; + delete this.LinkDefaultFlag; + delete this.reappend; + delete this.vGet; + delete this.vSet; + delete this.getCurrentStep; + delete this.getInfo; + delete this.destroy; + + // Return the original options from the closure. + return originalOptions; + } + + // 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. + var retour = $.map($Locations, function( location, index ){ + + var step = $Spectrum.getApplicableStep( location ), + value = $Values[index], + increment = step[2], + decrement = (value - step[2]) >= step[1] ? step[2] : step[0]; + + return [[decrement, increment]]; + }); + + // Return values in the proper order. + return inSliderOrder( retour ); + } + + // Get the original set of options. + function getOriginalOptions ( ) { + return originalOptions; + } + + +// Initialize slider + + // Throw an error if the slider was already initialized. + if ( $Target.hasClass(Classes[0]) ) { + throw new Error('Slider was already initialized.'); + } + + // Create the base element, initialise HTML and set classes. + // Add handles and links. + $Base = addSlider( options.dir, options.ort, $Target ); + $Handles = addHandles( options.handles, options.dir, $Base ); + + // Set the connect classes. + addConnection ( options.connect, $Target, $Handles ); + + // Attach user events. + events( options.events ); + +// Methods + + target.vSet = valueSet; + target.vGet = valueGet; + target.destroy = destroyTarget; + + target.getCurrentStep = getCurrentStep; + target.getOriginalOptions = getOriginalOptions; + + target.getInfo = function(){ + return [ + $Spectrum, + options.style, + options.ort + ]; + }; + + // Use the public value method to set the start values. + $Target.val( options.start ); diff --git a/src/js/scope_end.js b/src/js/scope_end.js new file mode 100644 index 00000000..5c34318c --- /dev/null +++ b/src/js/scope_end.js @@ -0,0 +1 @@ +} diff --git a/src/js/scope_events.js b/src/js/scope_events.js new file mode 100644 index 00000000..e9a43ba7 --- /dev/null +++ b/src/js/scope_events.js @@ -0,0 +1,197 @@ + + // Handler for attaching events trough a proxy. + function attach ( events, element, callback, data ) { + + // This function can be used to 'filter' events to the slider. + + // Add the noUiSlider namespace to all events. + events = events.replace( /\s/g, namespace + ' ' ) + namespace; + + // Bind a closure on the target. + return element.on( events, function( e ){ + + // jQuery and Zepto (1) handle unset attributes differently, + // but always falsy; #208 + if ( !!$Target.attr('disabled') ) { + return false; + } + + // Stop if an active 'tap' transition is taking place. + if ( $Target.hasClass( Classes[14] ) ) { + return false; + } + + e = fixEvent(e); + e.calcPoint = e.points[ options.ort ]; + + // Call the event handler with the event [ and additional data ]. + callback ( e, data ); + }); + } + + // Handle movement on document for handle and range drag. + function move ( event, data ) { + + var handles = data.handles || $Handles, positions, state = false, + proposal = ((event.calcPoint - data.start) * 100) / baseSize(), + h = handles[0][0] !== $Handles[0][0] ? 1 : 0; + + // Calculate relative positions for the handles. + positions = getPositions( proposal, data.positions, handles.length > 1); + + state = setHandle ( handles[0], positions[h], handles.length === 1 ); + + if ( handles.length > 1 ) { + state = setHandle ( handles[1], positions[h?0:1], false ) || state; + } + + // Fire the 'slide' event if any handle moved. + if ( state ) { + fireEvents(['slide']); + } + } + + // Unbind move events on document, call callbacks. + function end ( event ) { + + // The handle is no longer active, so remove the class. + $('.' + Classes[15]).removeClass(Classes[15]); + + // Remove cursor styles and text-selection events bound to the body. + if ( event.cursor ) { + $('body').css('cursor', '').off( namespace ); + } + + // Unbind the move and end events, which are added on 'start'. + doc.off( namespace ); + + // Remove dragging class. + $Target.removeClass(Classes[12]); + + // Fire the change and set events. + fireEvents(['set', 'change']); + } + + // Bind move events on document. + function start ( event, data ) { + + // Mark the handle as 'active' so it can be styled. + if( data.handles.length === 1 ) { + data.handles[0].children().addClass(Classes[15]); + } + + // A drag should never propagate up to the 'tap' event. + event.stopPropagation(); + + // Attach the move event. + attach ( actions.move, doc, move, { + start: event.calcPoint, + handles: data.handles, + positions: [ + $Locations[0], + $Locations[$Handles.length - 1] + ] + }); + + // Unbind all movement when the drag ends. + attach ( actions.end, doc, end, null ); + + // Text selection isn't an issue on touch devices, + // so adding cursor styles can be skipped. + if ( event.cursor ) { + + // Prevent the 'I' cursor and extend the range-drag cursor. + $('body').css('cursor', $(event.target).css('cursor')); + + // Mark the target with a dragging state. + if ( $Handles.length > 1 ) { + $Target.addClass(Classes[12]); + } + + // Prevent text selection when dragging the handles. + $('body').on('selectstart' + namespace, false); + } + } + + // Move closest handle to tapped location. + function tap ( event ) { + + var location = event.calcPoint, total = 0, to; + + // The tap event shouldn't propagate up and cause 'edge' to run. + event.stopPropagation(); + + // Add up the handle offsets. + $.each( $Handles, function(){ + total += this.offset()[ options.style ]; + }); + + // Find the handle closest to the tapped position. + total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1; + + location -= $Base.offset()[ options.style ]; + + // Calculate the new position. + to = ( location * 100 ) / baseSize(); + + if ( !options.events.snap ) { + // Flag the slider as it is now in a transitional state. + // Transition takes 300 ms, so re-enable the slider afterwards. + addClassFor( $Target, Classes[14], 300 ); + } + + // Find the closest handle and calculate the tapped point. + // The set handle to the new position. + setHandle( $Handles[total], to ); + + fireEvents(['slide', 'set', 'change']); + + if ( options.events.snap ) { + start(event, { handles: [$Handles[total]] }); + } + } + + // Attach events to several slider parts. + function events ( behaviour ) { + + var i, drag; + + // Attach the standard drag event to the handles. + if ( !behaviour.fixed ) { + + for ( i = 0; i < $Handles.length; i += 1 ) { + + // These events are only bound to the visual handle + // element, not the 'real' origin element. + attach ( actions.start, $Handles[i].children(), start, { + handles: [ $Handles[i] ] + }); + } + } + + // Attach the tap event to the slider base. + if ( behaviour.tap ) { + + attach ( actions.start, $Base, tap, { + handles: $Handles + }); + } + + // Make the range dragable. + if ( behaviour.drag ){ + + drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] ); + + // When the range is fixed, the entire range can + // be dragged by the handles. The handle in the first + // origin will propagate the start event upward, + // but it needs to be bound manually on the other. + if ( behaviour.fixed ) { + drag = drag.add($Base.children().not( drag ).children()); + } + + attach ( actions.start, drag, start, { + handles: $Handles + }); + } + } diff --git a/src/js/scope_helpers.js b/src/js/scope_helpers.js new file mode 100644 index 00000000..54bc8e83 --- /dev/null +++ b/src/js/scope_helpers.js @@ -0,0 +1,34 @@ +// Helpers + + // Shorthand for base dimensions. + function baseSize ( ) { + return $Base[['width', 'height'][options.ort]](); + } + + // External event handling + function fireEvents ( events ) { + + // Use the external api to get the values. + // Wrap the values in an array, as .trigger takes + // only one additional argument. + var index, values = [ $Target.val() ]; + + for ( index = 0; index < events.length; index += 1 ){ + $Target.trigger(events[index], values); + } + } + + // Returns the input array, respecting the slider direction configuration. + function inSliderOrder ( values ) { + + // If only one handle is used, return a single value. + if ( values.length === 1 ){ + return values[0]; + } + + if ( options.dir ) { + return values.reverse(); + } + + return values; + } diff --git a/src/js/scope_link.js b/src/js/scope_link.js new file mode 100644 index 00000000..38b9cf1c --- /dev/null +++ b/src/js/scope_link.js @@ -0,0 +1,64 @@ +// libLink integration + + // Create a new function which calls .val on input change. + function createChangeHandler ( trigger ) { + return function ( ignore, value ){ + // Determine which array position to 'null' based on 'trigger'. + $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); + }; + } + + // Called by libLink when it wants a set of links updated. + function linkUpdate ( flag ) { + + var trigger = $.inArray(flag, triggerPos); + + // The API might not have been set yet. + if ( $Target[0].linkAPI && $Target[0].linkAPI[flag] ) { + $Target[0].linkAPI[flag].change( + $Values[trigger], + $Handles[trigger].children(), + $Target + ); + } + } + + // Called by libLink to append an element to the slider. + function linkConfirm ( flag, element ) { + + // Find the trigger for the passed flag. + var trigger = $.inArray(flag, triggerPos); + + // If set, append the element to the handle it belongs to. + if ( element ) { + element.appendTo( $Handles[trigger].children() ); + } + + // The public API is reversed for rtl sliders, so the changeHandler + // should not be aware of the inverted trigger positions. + if ( options.dir ) { + trigger = trigger === 1 ? 0 : 1; + } + + return createChangeHandler( trigger ); + } + + // Place elements back on the slider. + function reAppendLink ( ) { + + var i, flag; + + // The API keeps a list of elements: we can re-append them on rebuild. + for ( i = 0; i < triggerPos.length; i += 1 ) { + if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { + this.linkAPI[flag].reconfirm(flag); + } + } + } + + target.LinkUpdate = linkUpdate; + target.LinkConfirm = linkConfirm; + target.LinkDefaultFormatter = options.format; + target.LinkDefaultFlag = 'lower'; + + target.reappend = reAppendLink; diff --git a/src/js/scope_start.js b/src/js/scope_start.js new file mode 100644 index 00000000..7e05ae37 --- /dev/null +++ b/src/js/scope_start.js @@ -0,0 +1,19 @@ +function closure ( target, options, originalOptions ){ + +// Internal variables + + // All variables local to 'closure' are marked $. + var $Target = $(target), + $Locations = [-1, -1], + $Base, + $Handles, + $Spectrum = options.spectrum, + $Values = [], + // libLink. For rtl sliders, 'lower' and 'upper' should not be inverted + // for one-handle sliders, so trim 'upper' it that case. + triggerPos = ['lower', 'upper'].slice(0, options.handles); + + // Invert the libLink connection for rtl sliders. + if ( options.dir ) { + triggerPos.reverse(); + } diff --git a/src/js/structure.js b/src/js/structure.js new file mode 100644 index 00000000..b58c1be9 --- /dev/null +++ b/src/js/structure.js @@ -0,0 +1,147 @@ +// Class handling + + // Delimit proposed values for handle positions. + function getPositions ( a, b, delimit ) { + + // Add movement to current position. + var c = a + b[0], d = a + b[1]; + + // Only alter the other position on drag, + // not on standard sliding. + if ( delimit ) { + if ( c < 0 ) { + d += Math.abs(c); + } + if ( d > 100 ) { + c -= ( d - 100 ); + } + + // Limit values to 0 and 100. + return [limit(c), limit(d)]; + } + + return [c,d]; + } + + +// Event handling + + // Provide a clean event with standardized offset values. + function fixEvent ( e ) { + + // Prevent scrolling and panning on touch events, while + // attempting to slide. The tap event also depends on this. + e.preventDefault(); + + // Filter the event to register the type, which can be + // touch, mouse or pointer. Offset changes need to be + // made on an event specific basis. + var touch = e.type.indexOf('touch') === 0 + ,mouse = e.type.indexOf('mouse') === 0 + ,pointer = e.type.indexOf('pointer') === 0 + ,x,y, event = e; + + // IE10 implemented pointer events with a prefix; + if ( e.type.indexOf('MSPointer') === 0 ) { + pointer = true; + } + + // Get the originalEvent, if the event has been wrapped + // by jQuery. Zepto doesn't wrap the event. + if ( e.originalEvent ) { + e = e.originalEvent; + } + + if ( touch ) { + // noUiSlider supports one movement at a time, + // so we can select the first 'changedTouch'. + x = e.changedTouches[0].pageX; + y = e.changedTouches[0].pageY; + } + + if ( mouse || pointer ) { + + // Polyfill the pageXOffset and pageYOffset + // variables for IE7 and IE8; + if( !pointer && window.pageXOffset === undefined ){ + window.pageXOffset = document.documentElement.scrollLeft; + window.pageYOffset = document.documentElement.scrollTop; + } + + x = e.clientX + window.pageXOffset; + y = e.clientY + window.pageYOffset; + } + + event.points = [x, y]; + event.cursor = mouse; + + return event; + } + + +// DOM additions + + // Append a handle to the base. + function addHandle ( direction, index ) { + + var handle = $('
').addClass( Classes[2] ), + additions = [ '-lower', '-upper' ]; + + if ( direction ) { + additions.reverse(); + } + + handle.children().addClass( + Classes[3] + " " + Classes[3]+additions[index] + ); + + return handle; + } + + // Add the proper connection classes. + function addConnection ( connect, target, handles ) { + + // Apply the required connection classes to the elements + // that need them. Some classes are made up for several + // segments listed in the class list, to allow easy + // renaming and provide a minor compression benefit. + switch ( connect ) { + case 1: target.addClass( Classes[7] ); + handles[0].addClass( Classes[6] ); + break; + case 3: handles[1].addClass( Classes[6] ); + /* falls through */ + case 2: handles[0].addClass( Classes[7] ); + /* falls through */ + case 0: target.addClass(Classes[6]); + break; + } + } + + // Add handles to the slider base. + function addHandles ( nrHandles, direction, base ) { + + var index, handles = []; + + // Append handles. + for ( index = 0; index < nrHandles; index += 1 ) { + + // Keep a list of all added handles. + handles.push( addHandle( direction, index ).appendTo(base) ); + } + + return handles; + } + + // Initialize a single slider. + function addSlider ( direction, orientation, target ) { + + // Apply classes and data to the target. + target.addClass([ + Classes[0], + Classes[8 + direction], + Classes[4 + orientation] + ].join(' ')); + + return $('
').appendTo(target).addClass( Classes[1] ); + } diff --git a/src/module.base.js b/src/module.base.js deleted file mode 100644 index b71a9c1e..00000000 --- a/src/module.base.js +++ /dev/null @@ -1,872 +0,0 @@ -/*jslint browser: true */ -/*jslint white: true */ - -(function( $ ){ - - 'use strict'; - - var - // Cache the document selector; - /** @const */ - doc = $(document), - // Make a backup of the original jQuery/Zepto .val() method. - /** @const */ - $val = $.fn.val, - // Namespace for binding and unbinding slider events; - /** @const */ - namespace = '.nui', - // Determine the events to bind. IE11 implements pointerEvents without - // a prefix, which breaks compatibility with the IE10 implementation. - /** @const */ - actions = window.navigator.pointerEnabled ? { - start: 'pointerdown', - move: 'pointermove', - end: 'pointerup' - } : window.navigator.msPointerEnabled ? { - start: 'MSPointerDown', - move: 'MSPointerMove', - end: 'MSPointerUp' - } : { - start: 'mousedown touchstart', - move: 'mousemove touchmove', - end: 'mouseup touchend' - }, - // Re-usable list of classes; - /** @const */ - Classes = [ -/* 0 */ 'noUi-target' -/* 1 */ ,'noUi-base' -/* 2 */ ,'noUi-origin' -/* 3 */ ,'noUi-handle' -/* 4 */ ,'noUi-horizontal' -/* 5 */ ,'noUi-vertical' -/* 6 */ ,'noUi-background' -/* 7 */ ,'noUi-connect' -/* 8 */ ,'noUi-ltr' -/* 9 */ ,'noUi-rtl' -/* 10 */ ,'noUi-dragable' -/* 11 */ ,'' -/* 12 */ ,'noUi-state-drag' -/* 13 */ ,'' -/* 14 */ ,'noUi-state-tap' -/* 15 */ ,'noUi-active' -/* 16 */ ,'' -/* 17 */ ,'noUi-stacking' - ]; - - -// General helpers - - // Limits a value to 0 - 100 - function limit ( a ) { - return Math.max(Math.min(a, 100), 0); - } - - // Wraps a variable as an array, if it isn't one yet. - function asArray ( a ) { - return $.isArray(a) ? a : [a]; - } - - -// Class handling - - // Sets a class and removes it after [duration] ms. - function addClassFor ( element, className, duration ) { - element.addClass(className); - setTimeout(function(){ - element.removeClass(className); - }, duration); - } - - // Delimit proposed values for handle positions. - function getPositions ( a, b, delimit ) { - - // Add movement to current position. - var c = a + b[0], d = a + b[1]; - - // Only alter the other position on drag, - // not on standard sliding. - if ( delimit ) { - if ( c < 0 ) { - d += Math.abs(c); - } - if ( d > 100 ) { - c -= ( d - 100 ); - } - - // Limit values to 0 and 100. - return [limit(c), limit(d)]; - } - - return [c,d]; - } - - - -// Event handling - - // Provide a clean event with standardized offset values. - function fixEvent ( e ) { - - // Prevent scrolling and panning on touch events, while - // attempting to slide. The tap event also depends on this. - e.preventDefault(); - - // Filter the event to register the type, which can be - // touch, mouse or pointer. Offset changes need to be - // made on an event specific basis. - var touch = e.type.indexOf('touch') === 0 - ,mouse = e.type.indexOf('mouse') === 0 - ,pointer = e.type.indexOf('pointer') === 0 - ,x,y, event = e; - - // IE10 implemented pointer events with a prefix; - if ( e.type.indexOf('MSPointer') === 0 ) { - pointer = true; - } - - // Get the originalEvent, if the event has been wrapped - // by jQuery. Zepto doesn't wrap the event. - if ( e.originalEvent ) { - e = e.originalEvent; - } - - if ( touch ) { - // noUiSlider supports one movement at a time, - // so we can select the first 'changedTouch'. - x = e.changedTouches[0].pageX; - y = e.changedTouches[0].pageY; - } - - if ( mouse || pointer ) { - - // Polyfill the pageXOffset and pageYOffset - // variables for IE7 and IE8; - if( !pointer && window.pageXOffset === undefined ){ - window.pageXOffset = document.documentElement.scrollLeft; - window.pageYOffset = document.documentElement.scrollTop; - } - - x = e.clientX + window.pageXOffset; - y = e.clientY + window.pageYOffset; - } - - event.points = [x, y]; - event.cursor = mouse; - - return event; - } - - -// DOM additions - - // Append a handle to the base. - function addHandle ( direction, index ) { - - var handle = $('
').addClass( Classes[2] ), - additions = [ '-lower', '-upper' ]; - - if ( direction ) { - additions.reverse(); - } - - handle.children().addClass( - Classes[3] + " " + Classes[3]+additions[index] - ); - - return handle; - } - - // Add the proper connection classes. - function addConnection ( connect, target, handles ) { - - // Apply the required connection classes to the elements - // that need them. Some classes are made up for several - // segments listed in the class list, to allow easy - // renaming and provide a minor compression benefit. - switch ( connect ) { - case 1: target.addClass( Classes[7] ); - handles[0].addClass( Classes[6] ); - break; - case 3: handles[1].addClass( Classes[6] ); - /* falls through */ - case 2: handles[0].addClass( Classes[7] ); - /* falls through */ - case 0: target.addClass(Classes[6]); - break; - } - } - - // Add handles to the slider base. - function addHandles ( nrHandles, direction, base ) { - - var index, handles = []; - - // Append handles. - for ( index = 0; index < nrHandles; index += 1 ) { - - // Keep a list of all added handles. - handles.push( addHandle( direction, index ).appendTo(base) ); - } - - return handles; - } - - // Initialize a single slider. - function addSlider ( direction, orientation, target ) { - - // Apply classes and data to the target. - target.addClass([ - Classes[0], - Classes[8 + direction], - Classes[4 + orientation] - ].join(' ')); - - return $('
').appendTo(target).addClass( Classes[1] ); - } - - -// Slider scope - -function closure ( target, options, originalOptions ){ - -// Internal variables - - // All variables local to 'closure' are marked $. - var $Target = $(target), - $Locations = [-1, -1], - $Base, - $Handles, - $Spectrum = options.spectrum, - $Values = [], - // libLink. For rtl sliders, 'lower' and 'upper' should not be inverted - // for one-handle sliders, so trim 'upper' it that case. - triggerPos = ['lower', 'upper'].slice(0, options.handles); - - // Invert the libLink connection for rtl sliders. - if ( options.dir ) { - triggerPos.reverse(); - } - - -// Helpers - - // Shorthand for base dimensions. - function baseSize ( ) { - return $Base[['width', 'height'][options.ort]](); - } - - // External event handling - function fireEvents ( events ) { - - // Use the external api to get the values. - // Wrap the values in an array, as .trigger takes - // only one additional argument. - var index, values = [ $Target.val() ]; - - for ( index = 0; index < events.length; index += 1 ){ - $Target.trigger(events[index], values); - } - } - - // Returns the input array, respecting the slider direction configuration. - function inSliderOrder ( values ) { - - // If only one handle is used, return a single value. - if ( values.length === 1 ){ - return values[0]; - } - - if ( options.dir ) { - return values.reverse(); - } - - return values; - } - - -// libLink integration - - // Create a new function which calls .val on input change. - function createChangeHandler ( trigger ) { - return function ( ignore, value ){ - // Determine which array position to 'null' based on 'trigger'. - $Target.val( [ trigger ? null : value, trigger ? value : null ], true ); - }; - } - - // Called by libLink when it wants a set of links updated. - function linkUpdate ( flag ) { - - var trigger = $.inArray(flag, triggerPos); - - // The API might not have been set yet. - if ( $Target[0].linkAPI && $Target[0].linkAPI[flag] ) { - $Target[0].linkAPI[flag].change( - $Values[trigger], - $Handles[trigger].children(), - $Target - ); - } - } - - // Called by libLink to append an element to the slider. - function linkConfirm ( flag, element ) { - - // Find the trigger for the passed flag. - var trigger = $.inArray(flag, triggerPos); - - // If set, append the element to the handle it belongs to. - if ( element ) { - element.appendTo( $Handles[trigger].children() ); - } - - // The public API is reversed for rtl sliders, so the changeHandler - // should not be aware of the inverted trigger positions. - if ( options.dir ) { - trigger = trigger === 1 ? 0 : 1; - } - - return createChangeHandler( trigger ); - } - - // Place elements back on the slider. - function reAppendLink ( ) { - - var i, flag; - - // The API keeps a list of elements: we can re-append them on rebuild. - for ( i = 0; i < triggerPos.length; i += 1 ) { - if ( this.linkAPI && this.linkAPI[(flag = triggerPos[i])] ) { - this.linkAPI[flag].reconfirm(flag); - } - } - } - - target.LinkUpdate = linkUpdate; - target.LinkConfirm = linkConfirm; - target.LinkDefaultFormatter = options.format; - target.LinkDefaultFlag = 'lower'; - - target.reappend = reAppendLink; - - - // Test suggested values and apply margin, step. - function setHandle ( handle, to, noLimitOption ) { - - var trigger = handle[0] !== $Handles[0][0] ? 1 : 0, - lowerMargin = $Locations[0] + options.margin, - upperMargin = $Locations[1] - options.margin, - lowerLimit = $Locations[0] + options.limit, - upperLimit = $Locations[1] - options.limit; - - // For sliders with multiple handles, - // limit movement to the other handle. - // Apply the margin option by adding it to the handle positions. - if ( $Handles.length > 1 ) { - to = trigger ? Math.max( to, lowerMargin ) : Math.min( to, upperMargin ); - } - - // The limit option has the opposite effect, limiting handles to a - // maximum distance from another. Limit must be > 0, as otherwise - // handles would be unmoveable. 'noLimitOption' is set to 'false' - // for the .val() method, except for pass 4/4. - if ( noLimitOption !== false && options.limit && $Handles.length > 1 ) { - to = trigger ? Math.min ( to, lowerLimit ) : Math.max( to, upperLimit ); - } - - // Handle the step option. - to = $Spectrum.getStep( to ); - - // Limit to 0/100 for .val input, trim anything beyond 7 digits, as - // JavaScript has some issues in its floating point implementation. - to = limit(parseFloat(to.toFixed(7))); - - // Return false if handle can't move. - if ( to === $Locations[trigger] ) { - return false; - } - - // Set the handle to the new position. - handle.css( options.style, to + '%' ); - - // Force proper handle stacking - if ( handle.is(':first-child') ) { - handle.toggleClass(Classes[17], to > 50 ); - } - - // Update locations. - $Locations[trigger] = to; - - // Convert the value to the slider stepping/range. - $Values[trigger] = $Spectrum.fromStepping( to ); - - linkUpdate(triggerPos[trigger]); - - return true; - } - - // Loop values from value method and apply them. - function setValues ( count, values ) { - - var i, trigger, to; - - // With the limit option, we'll need another limiting pass. - if ( options.limit ) { - count += 1; - } - - // If there are multiple handles to be set run the setting - // mechanism twice for the first handle, to make sure it - // can be bounced of the second one properly. - for ( i = 0; i < count; i += 1 ) { - - trigger = i%2; - - // Get the current argument from the array. - to = values[trigger]; - - // Setting with null indicates an 'ignore'. - // Inputting 'false' is invalid. - if ( to !== null && to !== false ) { - - // If a formatted number was passed, attemt to decode it. - if ( typeof to === 'number' ) { - to = String(to); - } - - to = options.format.from( to ); - - // Request an update for all links if the value was invalid. - // Do so too if setting the handle fails. - if ( to === false || isNaN(to) || setHandle( $Handles[trigger], $Spectrum.toStepping( to ), i === (3 - options.dir) ) === false ) { - - linkUpdate(triggerPos[trigger]); - } - } - } - } - - - - // Handler for attaching events trough a proxy. - function attach ( events, element, callback, data ) { - - // This function can be used to 'filter' events to the slider. - - // Add the noUiSlider namespace to all events. - events = events.replace( /\s/g, namespace + ' ' ) + namespace; - - // Bind a closure on the target. - return element.on( events, function( e ){ - - // jQuery and Zepto (1) handle unset attributes differently, - // but always falsy; #208 - if ( !!$Target.attr('disabled') ) { - return false; - } - - // Stop if an active 'tap' transition is taking place. - if ( $Target.hasClass( Classes[14] ) ) { - return false; - } - - e = fixEvent(e); - e.calcPoint = e.points[ options.ort ]; - - // Call the event handler with the event [ and additional data ]. - callback ( e, data ); - }); - } - - // Handle movement on document for handle and range drag. - function move ( event, data ) { - - var handles = data.handles || $Handles, positions, state = false, - proposal = ((event.calcPoint - data.start) * 100) / baseSize(), - h = handles[0][0] !== $Handles[0][0] ? 1 : 0; - - // Calculate relative positions for the handles. - positions = getPositions( proposal, data.positions, handles.length > 1); - - state = setHandle ( handles[0], positions[h], handles.length === 1 ); - - if ( handles.length > 1 ) { - state = setHandle ( handles[1], positions[h?0:1], false ) || state; - } - - // Fire the 'slide' event if any handle moved. - if ( state ) { - fireEvents(['slide']); - } - } - - // Unbind move events on document, call callbacks. - function end ( event ) { - - // The handle is no longer active, so remove the class. - $('.' + Classes[15]).removeClass(Classes[15]); - - // Remove cursor styles and text-selection events bound to the body. - if ( event.cursor ) { - $('body').css('cursor', '').off( namespace ); - } - - // Unbind the move and end events, which are added on 'start'. - doc.off( namespace ); - - // Remove dragging class. - $Target.removeClass(Classes[12]); - - // Fire the change and set events. - fireEvents(['set', 'change']); - } - - // Bind move events on document. - function start ( event, data ) { - - // Mark the handle as 'active' so it can be styled. - if( data.handles.length === 1 ) { - data.handles[0].children().addClass(Classes[15]); - } - - // A drag should never propagate up to the 'tap' event. - event.stopPropagation(); - - // Attach the move event. - attach ( actions.move, doc, move, { - start: event.calcPoint, - handles: data.handles, - positions: [ - $Locations[0], - $Locations[$Handles.length - 1] - ] - }); - - // Unbind all movement when the drag ends. - attach ( actions.end, doc, end, null ); - - // Text selection isn't an issue on touch devices, - // so adding cursor styles can be skipped. - if ( event.cursor ) { - - // Prevent the 'I' cursor and extend the range-drag cursor. - $('body').css('cursor', $(event.target).css('cursor')); - - // Mark the target with a dragging state. - if ( $Handles.length > 1 ) { - $Target.addClass(Classes[12]); - } - - // Prevent text selection when dragging the handles. - $('body').on('selectstart' + namespace, false); - } - } - - // Move closest handle to tapped location. - function tap ( event ) { - - var location = event.calcPoint, total = 0, to; - - // The tap event shouldn't propagate up and cause 'edge' to run. - event.stopPropagation(); - - // Add up the handle offsets. - $.each( $Handles, function(){ - total += this.offset()[ options.style ]; - }); - - // Find the handle closest to the tapped position. - total = ( location < total/2 || $Handles.length === 1 ) ? 0 : 1; - - location -= $Base.offset()[ options.style ]; - - // Calculate the new position. - to = ( location * 100 ) / baseSize(); - - if ( !options.events.snap ) { - // Flag the slider as it is now in a transitional state. - // Transition takes 300 ms, so re-enable the slider afterwards. - addClassFor( $Target, Classes[14], 300 ); - } - - // Find the closest handle and calculate the tapped point. - // The set handle to the new position. - setHandle( $Handles[total], to ); - - fireEvents(['slide', 'set', 'change']); - - if ( options.events.snap ) { - start(event, { handles: [$Handles[total]] }); - } - } - - // Attach events to several slider parts. - function events ( behaviour ) { - - var i, drag; - - // Attach the standard drag event to the handles. - if ( !behaviour.fixed ) { - - for ( i = 0; i < $Handles.length; i += 1 ) { - - // These events are only bound to the visual handle - // element, not the 'real' origin element. - attach ( actions.start, $Handles[i].children(), start, { - handles: [ $Handles[i] ] - }); - } - } - - // Attach the tap event to the slider base. - if ( behaviour.tap ) { - - attach ( actions.start, $Base, tap, { - handles: $Handles - }); - } - - // Make the range dragable. - if ( behaviour.drag ){ - - drag = $Base.find( '.' + Classes[7] ).addClass( Classes[10] ); - - // When the range is fixed, the entire range can - // be dragged by the handles. The handle in the first - // origin will propagate the start event upward, - // but it needs to be bound manually on the other. - if ( behaviour.fixed ) { - drag = drag.add($Base.children().not( drag ).children()); - } - - attach ( actions.start, drag, start, { - handles: $Handles - }); - } - } - - - - // Set the slider value. - function valueSet ( input ) { - - // LibLink: don't accept new values when currently emitting changes. - if ( $Target[0].LinkIsEmitting ) { - return this; - } - - var count, values = asArray( input ); - - // The RTL settings is implemented by reversing the front-end, - // internal mechanisms are the same. - if ( options.dir && options.handles > 1 ) { - values.reverse(); - } - - // Animation is optional. - // Make sure the initial values where set before using animated - // placement. (no report, unit testing); - if ( options.animate && $Locations[0] !== -1 ) { - addClassFor( $Target, Classes[14], 300 ); - } - - // Determine how often to set the handles. - count = $Handles.length > 1 ? 3 : 1; - - if ( values.length === 1 ) { - count = 1; - } - - setValues ( count, values ); - - // Fire the 'set' event. As of noUiSlider 7, - // this is no longer optional. - fireEvents(['set']); - - return this; - } - - // Get the slider value. - function valueGet ( ) { - - var i, retour = []; - - // Get the value from all handles. - for ( i = 0; i < options.handles; i += 1 ){ - retour[i] = options.format.to( $Values[i] ); - } - - return inSliderOrder( retour ); - } - - - - // Destroy the slider and unbind all events. - function destroyTarget ( ) { - - // Unbind events on the slider, remove all classes and child elements. - $(this).off(namespace) - .removeClass(Classes.join(' ')) - .empty(); - - delete this.LinkUpdate; - delete this.LinkConfirm; - delete this.LinkDefaultFormatter; - delete this.LinkDefaultFlag; - delete this.reappend; - delete this.vGet; - delete this.vSet; - delete this.getCurrentStep; - delete this.getInfo; - delete this.destroy; - - // Return the original options from the closure. - return originalOptions; - } - - // 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. - var retour = $.map($Locations, function( location, index ){ - - var step = $Spectrum.getApplicableStep( location ), - value = $Values[index], - increment = step[2], - decrement = (value - step[2]) >= step[1] ? step[2] : step[0]; - - return [[decrement, increment]]; - }); - - // Return values in the proper order. - return inSliderOrder( retour ); - } - - - -// Initialize slider - - // Throw an error if the slider was already initialized. - if ( $Target.hasClass(Classes[0]) ) { - throw new Error('Slider was already initialized.'); - } - - // Create the base element, initialise HTML and set classes. - // Add handles and links. - $Base = addSlider( options.dir, options.ort, $Target ); - $Handles = addHandles( options.handles, options.dir, $Base ); - - // Set the connect classes. - addConnection ( options.connect, $Target, $Handles ); - - // Attach user events. - events( options.events ); - -// Methods - - target.vSet = valueSet; - target.vGet = valueGet; - target.destroy = destroyTarget; - target.getCurrentStep = getCurrentStep; - target.getInfo = function(){ - return [ - $Spectrum, - options.style, - options.ort - ]; - }; - - // Use the public value method to set the start values. - $Target.val( options.start ); -} - - -// Access points - - // Run the standard initializer - function initialize ( originalOptions ) { - - // Throw error if group is empty. - if ( !this.length ){ - throw new Error("noUiSlider: Can't initialize slider on empty selection."); - } - - // Test the options once, not for every slider. - var options = $.noUiSlider.testOptions( originalOptions, this ); - - // Loop all items, and provide a new closed-scope environment. - return this.each(function(){ - closure(this, options, originalOptions); - }); - } - - // Destroy the slider, then re-enter initialization. - function rebuild ( options ) { - - return this.each(function(){ - - // The rebuild flag can be used if the slider wasn't initialized yet. - if ( !this.destroy ) { - $(this).noUiSlider( options ); - return; - } - - // Get the current values from the slider, - // including the initialization options. - var values = $(this).val(), originalOptions = this.destroy(), - - // Extend the previous options with the newly provided ones. - newOptions = $.extend( {}, originalOptions, options ); - - // Run the standard initializer. - $(this).noUiSlider( newOptions ); - - // Place Link elements back. - this.reappend(); - - // If the start option hasn't changed, - // reset the previous values. - if ( originalOptions.start === newOptions.start ) { - $(this).val(values); - } - }); - } - - // Access the internal getting and setting methods based on argument count. - function value ( ) { - return this[0][ !arguments.length ? 'vGet' : 'vSet' ].apply(this[0], arguments); - } - - // Override the .val() method. Test every element. Is it a slider? Go to - // the slider value handling. No? Use the standard method. - // Note how $.fn.val expects 'this' to be an instance of $. For convenience, - // the above 'value' function does too. - $.fn.val = function ( ) { - - // this === instanceof $ - - function valMethod( a ){ - return a.hasClass(Classes[0]) ? value : $val; - } - - var args = arguments, - first = $(this[0]); - - if ( !arguments.length ) { - return valMethod(first).call(first); - } - - // Return the set so it remains chainable - return this.each(function(){ - valMethod($(this)).apply($(this), args); - }); - }; - -// Extend jQuery/Zepto with the noUiSlider method. - $.fn.noUiSlider = function ( options, rebuildFlag ) { - return ( rebuildFlag ? rebuild : initialize ).call(this, options); - }; - -}( window.jQuery || window.Zepto )); diff --git a/libLink b/submodules/libLink similarity index 100% rename from libLink rename to submodules/libLink diff --git a/submodules/wNumb b/submodules/wNumb new file mode 160000 index 00000000..cda69307 --- /dev/null +++ b/submodules/wNumb @@ -0,0 +1 @@ +Subproject commit cda693078a839bbc3bf90efdd981d4c03ca44c4a diff --git a/tests/addon_pips.js b/tests/addon_pips.js index dac94735..522e1654 100644 --- a/tests/addon_pips.js +++ b/tests/addon_pips.js @@ -28,14 +28,19 @@ slider.noUiSlider_pips({ mode: 'range', - density: 3 + density: 3, + format: wNumb({ + decimals: 2 + }) }); ok( Q.find('.noUi-pips'), 'Pips where created' ); - var markers = Q.find('.noUi-marker').length; - ok( markers >= 32 && markers <= 34, 'Density of 1/3 was applied' ); - + var markers = Q.find('.noUi-marker'); + ok( markers.length >= 32 && markers.length <= 34, 'Density of 1/3 was applied' ); + + // Test formatter + equal( Q.find('.noUi-value').first().text(), '0.00' ); }); test( "Steps", function(){ @@ -81,7 +86,7 @@ test( "Positions, stepped", function(){ expect(0); // TODO - + var slider = test_slider(); // POSITIONS (STEPPED) @@ -119,7 +124,7 @@ test( "Count, stepped", function(){ expect(0); // TODO - + var slider = test_slider(); // COUNT (STEPPED) diff --git a/tests/range.html b/tests/range.html index d3e91f97..e5511d52 100644 --- a/tests/range.html +++ b/tests/range.html @@ -4,7 +4,9 @@ Testing - + + + diff --git a/tests/slider.html b/tests/slider.html index 221b5ff1..8926fe9f 100644 --- a/tests/slider.html +++ b/tests/slider.html @@ -14,20 +14,14 @@ - + - - - - - - - + diff --git a/wnumb b/wnumb deleted file mode 160000 index 2bf0af44..00000000 --- a/wnumb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2bf0af44c1e0ef316cc913236682ca4ef7ead548