diff --git a/README.md b/README.md index c02ff18..5f37e89 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Check out the [demo](http://andreruffert.github.io/rangeslider.js/). ##Browser Support -* Tested in IE8+ +* Tested in IE8+ and all modern Browsers ##Installation diff --git a/bower.json b/bower.json index 2a04bd9..a0c3bd5 100644 --- a/bower.json +++ b/bower.json @@ -1,15 +1,18 @@ { "name": "rangeslider.js", - "version": "0.2.7", + "version": "0.2.8", "main": "dist/rangeslider.js", "homepage": "https://github.com/andreruffert/rangeslider.js", "author": "André Ruffert", "license": "MIT", + "description": "Simple, small and fast JavaScript/jQuery polyfill for the HTML5 slider element", "keywords": [ "input", "range", "slider", - "rangeslider" + "rangeslider", + "rangeslider.js", + "polyfill" ], "ignore": [ "**/.*", diff --git a/dist/rangeslider.js b/dist/rangeslider.js index 815a782..6049870 100644 --- a/dist/rangeslider.js +++ b/dist/rangeslider.js @@ -1,4 +1,4 @@ -/*! rangeslider.js - v0.2.7 | (c) 2014 @andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */ +/*! rangeslider.js - v0.2.8 | (c) 2014 @andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */ 'use strict'; (function (module) { @@ -21,31 +21,17 @@ return input.type !== 'text'; } - /** - * Touchscreen detection - * @return {Boolean} - */ - function isTouchScreen() { - var bool = false, - DocumentTouch = DocumentTouch || {}; - if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { - bool = true; - } - return bool; - } - var pluginName = 'rangeslider', pluginInstances = [], - touchevents = isTouchScreen(), inputrange = supportsRange(), defaults = { polyfill: true, rangeClass: 'rangeslider', fillClass: 'rangeslider__fill', handleClass: 'rangeslider__handle', - startEvent: ((!touchevents) ? 'mousedown' : 'touchstart') + '.' + pluginName, - moveEvent: ((!touchevents) ? 'mousemove' : 'touchmove') + '.' + pluginName, - endEvent: ((!touchevents) ? 'mouseup' : 'touchend') + '.' + pluginName + startEvent: ['mousedown', 'touchstart', 'pointerdown'], + moveEvent: ['mousemove', 'touchmove', 'pointermove'], + endEvent: ['mouseup', 'touchend', 'pointerup'] }; /** @@ -97,6 +83,9 @@ this.options = $.extend( {}, defaults, options ); this._defaults = defaults; this._name = pluginName; + this.startEvent = this.options.startEvent.join('.' + pluginName + ' ') + '.' + pluginName; + this.moveEvent = this.options.moveEvent.join('.' + pluginName + ' ') + '.' + pluginName; + this.endEvent = this.options.endEvent.join('.' + pluginName + ' ') + '.' + pluginName; this.polyfill = this.options.polyfill; this.onInit = this.options.onInit; this.onSlide = this.options.onSlide; @@ -109,9 +98,9 @@ } this.identifier = 'js-' + pluginName + '-' +(+new Date()); - this.value = parseFloat(this.$element[0].value) || 0; this.min = parseFloat(this.$element[0].getAttribute('min')) || 0; this.max = parseFloat(this.$element[0].getAttribute('max')) || 100; + this.value = parseFloat(this.$element[0].value) || this.min + (this.max-this.min)/2; this.step = parseFloat(this.$element[0].getAttribute('step')) || 1; this.$fill = $('
'); this.$handle = $('
'); @@ -140,7 +129,7 @@ delay(function() { _this.update(); }, 300); }, 20)); - this.$document.on(this.options.startEvent, '#' + this.identifier, this.handleDown); + this.$document.on(this.startEvent, '#' + this.identifier, this.handleDown); // Listen to programmatic value changes this.$element.on('change' + '.' + pluginName, function(e, data) { @@ -174,8 +163,8 @@ Plugin.prototype.handleDown = function(e) { e.preventDefault(); - this.$document.on(this.options.moveEvent, this.handleMove); - this.$document.on(this.options.endEvent, this.handleEnd); + this.$document.on(this.moveEvent, this.handleMove); + this.$document.on(this.endEvent, this.handleEnd); // If we click on the handle don't set the new position if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) { @@ -200,8 +189,8 @@ Plugin.prototype.handleEnd = function(e) { e.preventDefault(); - this.$document.off(this.options.moveEvent, this.handleMove); - this.$document.off(this.options.endEvent, this.handleEnd); + this.$document.off(this.moveEvent, this.handleMove); + this.$document.off(this.endEvent, this.handleEnd); var posX = this.getRelativePosition(this.$range[0], e); if (this.onSlideEnd && typeof this.onSlideEnd === 'function') { @@ -216,14 +205,11 @@ }; Plugin.prototype.setPosition = function(pos) { - var left = this.cap(pos, 0, this.maxHandleX), - value = this.getValueFromPosition(left); + var value, left; // Snapping steps - if (this.step !== 1) { - left = this.getPositionFromValue(value); - value = (this.getValueFromPosition(left) / this.step) * this.step; - } + value = (this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step; + left = this.getPositionFromValue(value); // Update ui this.$fill[0].style.width = (left + this.grabX) + 'px'; @@ -271,7 +257,7 @@ }; Plugin.prototype.destroy = function() { - this.$document.off(this.options.startEvent, '#' + this.identifier, this.handleDown); + this.$document.off(this.startEvent, '#' + this.identifier, this.handleDown); this.$element .off('.' + pluginName) .removeAttr('style') diff --git a/dist/rangeslider.min.js b/dist/rangeslider.min.js index c5c5e02..06239fa 100644 --- a/dist/rangeslider.min.js +++ b/dist/rangeslider.min.js @@ -1,2 +1,2 @@ -/*! rangeslider.js - v0.2.7 | (c) 2014 @andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */ -"use strict";!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(){var a=document.createElement("input");return a.setAttribute("type","range"),"text"!==a.type}function c(){var a=!1,b=b||{};return("ontouchstart"in window||window.DocumentTouch&&document instanceof b)&&(a=!0),a}function d(a,b){var c=Array.prototype.slice.call(arguments,2);return setTimeout(function(){return a.apply(null,c)},b)}function e(a,b){return b=b||100,function(){if(!a.debouncing){var c=Array.prototype.slice.apply(arguments);a.lastReturnVal=a.apply(window,c),a.debouncing=!0}return clearTimeout(a.debounceTimeout),a.debounceTimeout=setTimeout(function(){a.debouncing=!1},b),a.lastReturnVal}}function f(b,c){if(this.$window=a(window),this.$document=a(document),this.$element=a(b),this.options=a.extend({},k,c),this._defaults=k,this._name=g,this.polyfill=this.options.polyfill,this.onInit=this.options.onInit,this.onSlide=this.options.onSlide,this.onSlideEnd=this.options.onSlideEnd,this.polyfill&&j)return!1;this.identifier="js-"+g+"-"+ +new Date,this.value=parseFloat(this.$element[0].value)||0,this.min=parseFloat(this.$element[0].getAttribute("min"))||0,this.max=parseFloat(this.$element[0].getAttribute("max"))||100,this.step=parseFloat(this.$element[0].getAttribute("step"))||1,this.$fill=a('
'),this.$handle=a('
'),this.$range=a('
').insertBefore(this.$element).prepend(this.$fill,this.$handle),this.$element.css({position:"absolute",width:"1px",height:"1px",overflow:"hidden",visibility:"hidden"}),this.handleDown=a.proxy(this.handleDown,this),this.handleMove=a.proxy(this.handleMove,this),this.handleEnd=a.proxy(this.handleEnd,this),this.init();var f=this;this.$window.on("resize."+g,e(function(){d(function(){f.update()},300)},20)),this.$document.on(this.options.startEvent,"#"+this.identifier,this.handleDown),this.$element.on("change."+g,function(a,b){if(!b||b.origin!==g){var c=a.target.value,d=f.getPositionFromValue(c);f.setPosition(d)}})}var g="rangeslider",h=[],i=c(),j=b(),k={polyfill:!0,rangeClass:"rangeslider",fillClass:"rangeslider__fill",handleClass:"rangeslider__handle",startEvent:(i?"touchstart":"mousedown")+"."+g,moveEvent:(i?"touchmove":"mousemove")+"."+g,endEvent:(i?"touchend":"mouseup")+"."+g};f.prototype.init=function(){this.update(),this.onInit&&"function"==typeof this.onInit&&this.onInit()},f.prototype.update=function(){this.handleWidth=this.$handle[0].offsetWidth,this.rangeWidth=this.$range[0].offsetWidth,this.maxHandleX=this.rangeWidth-this.handleWidth,this.grabX=this.handleWidth/2,this.position=this.getPositionFromValue(this.value),this.setPosition(this.position)},f.prototype.handleDown=function(a){if(a.preventDefault(),this.$document.on(this.options.moveEvent,this.handleMove),this.$document.on(this.options.endEvent,this.handleEnd),!((" "+a.target.className+" ").replace(/[\n\t]/g," ").indexOf(this.options.handleClass)>-1)){var b=this.getRelativePosition(this.$range[0],a),c=this.getPositionFromNode(this.$handle[0])-this.getPositionFromNode(this.$range[0]);this.setPosition(b-this.grabX),b>=c&&ba?b:a>c?c:a},f.prototype.setPosition=function(a){var b=this.cap(a,0,this.maxHandleX),c=this.getValueFromPosition(b);1!==this.step&&(b=this.getPositionFromValue(c),c=this.getValueFromPosition(b)/this.step*this.step),this.$fill[0].style.width=b+this.grabX+"px",this.$handle[0].style.left=b+"px",this.setValue(c),this.position=b,this.value=c,this.onSlide&&"function"==typeof this.onSlide&&this.onSlide(b,c)},f.prototype.getPositionFromNode=function(a){for(var b=0;null!==a;)b+=a.offsetLeft,a=a.offsetParent;return b},f.prototype.getRelativePosition=function(a,b){return(b.pageX||b.originalEvent.clientX||b.originalEvent.touches[0].clientX||b.currentPoint.x)-this.getPositionFromNode(a)},f.prototype.getPositionFromValue=function(a){var b,c;return b=(a-this.min)/(this.max-this.min),c=b*this.maxHandleX},f.prototype.getValueFromPosition=function(a){var b,c;return b=a/this.maxHandleX,c=this.step*Math.ceil((b*(this.max-this.min)+this.min)/this.step),Number(c.toFixed(2))},f.prototype.setValue=function(a){this.$element.val(a).trigger("change",{origin:g})},f.prototype.destroy=function(){this.$document.off(this.options.startEvent,"#"+this.identifier,this.handleDown),this.$element.off("."+g).removeAttr("style").removeData("plugin_"+g),this.$range&&this.$range.length&&this.$range[0].parentNode.removeChild(this.$range[0]),h.splice(h.indexOf(this.$element[0]),1),h.length||this.$window.off("."+g)},a.fn[g]=function(b){return this.each(function(){var c=a(this),d=c.data("plugin_"+g);d||(c.data("plugin_"+g,d=new f(this,b)),h.push(this)),"string"==typeof b&&d[b]()})}}); \ No newline at end of file +/*! rangeslider.js - v0.2.8 | (c) 2014 @andreruffert | MIT license | https://github.com/andreruffert/rangeslider.js */ +"use strict";!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){function b(){var a=document.createElement("input");return a.setAttribute("type","range"),"text"!==a.type}function c(a,b){var c=Array.prototype.slice.call(arguments,2);return setTimeout(function(){return a.apply(null,c)},b)}function d(a,b){return b=b||100,function(){if(!a.debouncing){var c=Array.prototype.slice.apply(arguments);a.lastReturnVal=a.apply(window,c),a.debouncing=!0}return clearTimeout(a.debounceTimeout),a.debounceTimeout=setTimeout(function(){a.debouncing=!1},b),a.lastReturnVal}}function e(b,e){if(this.$window=a(window),this.$document=a(document),this.$element=a(b),this.options=a.extend({},i,e),this._defaults=i,this._name=f,this.startEvent=this.options.startEvent.join("."+f+" ")+"."+f,this.moveEvent=this.options.moveEvent.join("."+f+" ")+"."+f,this.endEvent=this.options.endEvent.join("."+f+" ")+"."+f,this.polyfill=this.options.polyfill,this.onInit=this.options.onInit,this.onSlide=this.options.onSlide,this.onSlideEnd=this.options.onSlideEnd,this.polyfill&&h)return!1;this.identifier="js-"+f+"-"+ +new Date,this.min=parseFloat(this.$element[0].getAttribute("min"))||0,this.max=parseFloat(this.$element[0].getAttribute("max"))||100,this.value=parseFloat(this.$element[0].value)||this.min+(this.max-this.min)/2,this.step=parseFloat(this.$element[0].getAttribute("step"))||1,this.$fill=a('
'),this.$handle=a('
'),this.$range=a('
').insertBefore(this.$element).prepend(this.$fill,this.$handle),this.$element.css({position:"absolute",width:"1px",height:"1px",overflow:"hidden",visibility:"hidden"}),this.handleDown=a.proxy(this.handleDown,this),this.handleMove=a.proxy(this.handleMove,this),this.handleEnd=a.proxy(this.handleEnd,this),this.init();var g=this;this.$window.on("resize."+f,d(function(){c(function(){g.update()},300)},20)),this.$document.on(this.startEvent,"#"+this.identifier,this.handleDown),this.$element.on("change."+f,function(a,b){if(!b||b.origin!==f){var c=a.target.value,d=g.getPositionFromValue(c);g.setPosition(d)}})}var f="rangeslider",g=[],h=b(),i={polyfill:!0,rangeClass:"rangeslider",fillClass:"rangeslider__fill",handleClass:"rangeslider__handle",startEvent:["mousedown","touchstart","pointerdown"],moveEvent:["mousemove","touchmove","pointermove"],endEvent:["mouseup","touchend","pointerup"]};e.prototype.init=function(){this.update(),this.onInit&&"function"==typeof this.onInit&&this.onInit()},e.prototype.update=function(){this.handleWidth=this.$handle[0].offsetWidth,this.rangeWidth=this.$range[0].offsetWidth,this.maxHandleX=this.rangeWidth-this.handleWidth,this.grabX=this.handleWidth/2,this.position=this.getPositionFromValue(this.value),this.setPosition(this.position)},e.prototype.handleDown=function(a){if(a.preventDefault(),this.$document.on(this.moveEvent,this.handleMove),this.$document.on(this.endEvent,this.handleEnd),!((" "+a.target.className+" ").replace(/[\n\t]/g," ").indexOf(this.options.handleClass)>-1)){var b=this.getRelativePosition(this.$range[0],a),c=this.getPositionFromNode(this.$handle[0])-this.getPositionFromNode(this.$range[0]);this.setPosition(b-this.grabX),b>=c&&ba?b:a>c?c:a},e.prototype.setPosition=function(a){var b,c;b=this.getValueFromPosition(this.cap(a,0,this.maxHandleX))/this.step*this.step,c=this.getPositionFromValue(b),this.$fill[0].style.width=c+this.grabX+"px",this.$handle[0].style.left=c+"px",this.setValue(b),this.position=c,this.value=b,this.onSlide&&"function"==typeof this.onSlide&&this.onSlide(c,b)},e.prototype.getPositionFromNode=function(a){for(var b=0;null!==a;)b+=a.offsetLeft,a=a.offsetParent;return b},e.prototype.getRelativePosition=function(a,b){return(b.pageX||b.originalEvent.clientX||b.originalEvent.touches[0].clientX||b.currentPoint.x)-this.getPositionFromNode(a)},e.prototype.getPositionFromValue=function(a){var b,c;return b=(a-this.min)/(this.max-this.min),c=b*this.maxHandleX},e.prototype.getValueFromPosition=function(a){var b,c;return b=a/this.maxHandleX,c=this.step*Math.ceil((b*(this.max-this.min)+this.min)/this.step),Number(c.toFixed(2))},e.prototype.setValue=function(a){this.$element.val(a).trigger("change",{origin:f})},e.prototype.destroy=function(){this.$document.off(this.startEvent,"#"+this.identifier,this.handleDown),this.$element.off("."+f).removeAttr("style").removeData("plugin_"+f),this.$range&&this.$range.length&&this.$range[0].parentNode.removeChild(this.$range[0]),g.splice(g.indexOf(this.$element[0]),1),g.length||this.$window.off("."+f)},a.fn[f]=function(b){return this.each(function(){var c=a(this),d=c.data("plugin_"+f);d||(c.data("plugin_"+f,d=new e(this,b)),g.push(this)),"string"==typeof b&&d[b]()})}}); \ No newline at end of file diff --git a/example/index.html b/example/index.html index 337af5c..ef2e507 100644 --- a/example/index.html +++ b/example/index.html @@ -29,9 +29,20 @@ +
+ +
+
+ + +
+ +
+
+

Programmatic value changes

- +
diff --git a/package.json b/package.json index 3c9a576..1cdfdbf 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,16 @@ "name": "rangeslider.js", "title": "rangeslider.js", "description": "Simple, small and fast JavaScript/jQuery polyfill for the HTML5 slider element", - "version": "0.2.7", + "version": "0.2.8", "homepage": "https://github.com/andreruffert/rangeslider.js", "license": "MIT", "keywords": [ "input", "range", "slider", - "rangeslider" + "rangeslider", + "rangeslider.js", + "polyfill" ], "author": { "name": "André Ruffert", diff --git a/src/rangeslider.js b/src/rangeslider.js index 93ad8db..fa7aa83 100644 --- a/src/rangeslider.js +++ b/src/rangeslider.js @@ -20,31 +20,17 @@ return input.type !== 'text'; } - /** - * Touchscreen detection - * @return {Boolean} - */ - function isTouchScreen() { - var bool = false, - DocumentTouch = DocumentTouch || {}; - if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { - bool = true; - } - return bool; - } - var pluginName = 'rangeslider', pluginInstances = [], - touchevents = isTouchScreen(), inputrange = supportsRange(), defaults = { polyfill: true, rangeClass: 'rangeslider', fillClass: 'rangeslider__fill', handleClass: 'rangeslider__handle', - startEvent: ((!touchevents) ? 'mousedown' : 'touchstart') + '.' + pluginName, - moveEvent: ((!touchevents) ? 'mousemove' : 'touchmove') + '.' + pluginName, - endEvent: ((!touchevents) ? 'mouseup' : 'touchend') + '.' + pluginName + startEvent: ['mousedown', 'touchstart', 'pointerdown'], + moveEvent: ['mousemove', 'touchmove', 'pointermove'], + endEvent: ['mouseup', 'touchend', 'pointerup'] }; /** @@ -96,6 +82,9 @@ this.options = $.extend( {}, defaults, options ); this._defaults = defaults; this._name = pluginName; + this.startEvent = this.options.startEvent.join('.' + pluginName + ' ') + '.' + pluginName; + this.moveEvent = this.options.moveEvent.join('.' + pluginName + ' ') + '.' + pluginName; + this.endEvent = this.options.endEvent.join('.' + pluginName + ' ') + '.' + pluginName; this.polyfill = this.options.polyfill; this.onInit = this.options.onInit; this.onSlide = this.options.onSlide; @@ -108,9 +97,9 @@ } this.identifier = 'js-' + pluginName + '-' +(+new Date()); - this.value = parseFloat(this.$element[0].value) || 0; this.min = parseFloat(this.$element[0].getAttribute('min')) || 0; this.max = parseFloat(this.$element[0].getAttribute('max')) || 100; + this.value = parseFloat(this.$element[0].value) || this.min + (this.max-this.min)/2; this.step = parseFloat(this.$element[0].getAttribute('step')) || 1; this.$fill = $('
'); this.$handle = $('
'); @@ -139,7 +128,7 @@ delay(function() { _this.update(); }, 300); }, 20)); - this.$document.on(this.options.startEvent, '#' + this.identifier, this.handleDown); + this.$document.on(this.startEvent, '#' + this.identifier, this.handleDown); // Listen to programmatic value changes this.$element.on('change' + '.' + pluginName, function(e, data) { @@ -173,8 +162,8 @@ Plugin.prototype.handleDown = function(e) { e.preventDefault(); - this.$document.on(this.options.moveEvent, this.handleMove); - this.$document.on(this.options.endEvent, this.handleEnd); + this.$document.on(this.moveEvent, this.handleMove); + this.$document.on(this.endEvent, this.handleEnd); // If we click on the handle don't set the new position if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) { @@ -199,8 +188,8 @@ Plugin.prototype.handleEnd = function(e) { e.preventDefault(); - this.$document.off(this.options.moveEvent, this.handleMove); - this.$document.off(this.options.endEvent, this.handleEnd); + this.$document.off(this.moveEvent, this.handleMove); + this.$document.off(this.endEvent, this.handleEnd); var posX = this.getRelativePosition(this.$range[0], e); if (this.onSlideEnd && typeof this.onSlideEnd === 'function') { @@ -215,14 +204,11 @@ }; Plugin.prototype.setPosition = function(pos) { - var left = this.cap(pos, 0, this.maxHandleX), - value = this.getValueFromPosition(left); + var value, left; // Snapping steps - if (this.step !== 1) { - left = this.getPositionFromValue(value); - value = (this.getValueFromPosition(left) / this.step) * this.step; - } + value = (this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step; + left = this.getPositionFromValue(value); // Update ui this.$fill[0].style.width = (left + this.grabX) + 'px'; @@ -270,7 +256,7 @@ }; Plugin.prototype.destroy = function() { - this.$document.off(this.options.startEvent, '#' + this.identifier, this.handleDown); + this.$document.off(this.startEvent, '#' + this.identifier, this.handleDown); this.$element .off('.' + pluginName) .removeAttr('style')