harness/app-assets/templates/assets/js/components/mobile-range-slider.js (147 lines of code) (raw):
/*
* Mobile Range Slider
* A Touch Slider for Webkit / Mobile Safari
*
* https://github.com/ubilabs/mobile-range-slider
*
* Full rewrite of https://github.com/alexgibson/WKSlider
*
* @author Ubilabs http://ubilabs.net, 2012
* @license MIT License http://www.opensource.org/licenses/mit-license.php
*/
// function.bind() polyfill
// taken from: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError(
"Function.prototype.bind - what is trying to be bound is not callable"
);
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() { },
fBound = function() {
return fToBind.apply(
this instanceof fNOP ? this : oThis || window,
aArgs.concat( Array.prototype.slice.call(arguments) )
);
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
(function(undefined) {
// mapping of event handlers
var events = {
start: ['touchstart', 'mousedown'],
move: ['touchmove', 'mousemove'],
end: ['touchend', 'touchcancel', 'mouseup']
};
// constructor
function MobileRangeSlider(element, options) {
this.element = element;
this.options = {};
options = options || {};
var property;
for (property in this.defaultOptions){
if (options[property] !== undefined){
// set options passed to constructor
this.options[property] = options[property];
} else {
// set default options
this.options[property] = this.defaultOptions[property];
}
}
// detect support for Webkit CSS 3d transforms
this.supportsWebkit3dTransform = (
'WebKitCSSMatrix' in window &&
'm11' in new WebKitCSSMatrix()
);
// store references to DOM elements
if (typeof element === 'string'){
this.element = document.getElementsByClassName(element)[0];
}
this.knob = this.element.getElementsByClassName('audio-player__slider__knob')[0];
this.track = this.element.getElementsByClassName('audio-player__slider__track')[0];
// set context for event handlers
this.start = this.start.bind(this);
this.move = this.move.bind(this);
this.end = this.end.bind(this);
// set the inital value
this.addEvents("start");
this.setValue(this.options.value);
// update postion on page resize
window.addEventListener("resize", this.update.bind(this));
}
// default options
MobileRangeSlider.prototype.defaultOptions = {
value: 0, // initial value
min: 0, // minimum value
max: 100, // maximum value
change: null // change callback
};
// add event handlers for a given name
MobileRangeSlider.prototype.addEvents = function(name){
var list = events[name],
handler = this[name],
all;
for (all in list){
this.element.addEventListener(list[all], handler, false);
}
};
// remove event handlers for a given name
MobileRangeSlider.prototype.removeEvents = function(name){
var list = events[name],
handler = this[name],
all;
for (all in list){
this.element.removeEventListener(list[all], handler, false);
}
};
// start to listen for move events
MobileRangeSlider.prototype.start = function(event) {
this.addEvents("move");
this.addEvents("end");
this.handle(event);
};
// handle move events
MobileRangeSlider.prototype.move = function(event) {
this.handle(event);
};
// stop listening for move events
MobileRangeSlider.prototype.end = function() {
this.removeEvents("move");
this.removeEvents("end");
};
// update the knob position
MobileRangeSlider.prototype.update = function() {
this.setValue(this.value);
};
// set the new value of the slider
MobileRangeSlider.prototype.setValue = function(value) {
if (value === undefined){ value = this.options.min; }
value = Math.min(value, this.options.max);
value = Math.max(value, this.options.min);
var
knobWidth = this.knob.offsetWidth,
trackWidth = this.track.offsetWidth,
range = this.options.max - this.options.min,
width = trackWidth - knobWidth,
position = Math.round((value - this.options.min) * width / range);
this.setKnobPosition(position);
this.value = value;
this.callback(value);
};
MobileRangeSlider.prototype.setKnobPosition = function(x){
// use Webkit CSS 3d transforms for hardware acceleration if available
if (this.supportsWebkit3dTransform) {
this.knob.style.webkitTransform = 'translate3d(' + x + 'px, 0, 0)';
} else {
this.knob.style.webkitTransform =
this.knob.style.MozTransform =
this.knob.style.msTransform =
this.knob.style.OTransform =
this.knob.style.transform = 'translateX(' + x + 'px)';
}
};
// handle a mouse event
MobileRangeSlider.prototype.handle = function(event){
event.preventDefault();
if (event.targetTouches){ event = event.targetTouches[0]; }
var position = event.pageX,
element,
knobWidth = this.knob.offsetWidth,
trackWidth = this.track.offsetWidth,
width = trackWidth - knobWidth,
range = this.options.max - this.options.min,
value;
for (element = this.element; element; element = element.offsetParent){
position -= element.offsetLeft;
}
// keep knob in the bounds
position += knobWidth / 2;
position = Math.min(position, trackWidth);
position = Math.max(position - knobWidth, 0);
this.setKnobPosition(position);
// update
value = this.options.min + Math.round(position * range / width);
this.setValue(value);
};
// call callback with new value
MobileRangeSlider.prototype.callback = function(value) {
if (this.options.change){
this.options.change(value);
}
};
//public function
window.MobileRangeSlider = MobileRangeSlider;
})();