var on = function(items, scope) { items.forEach(function(item) { return item.e.map(function(e) { item.context.addEventListener(e,item.fn,false) }) }) } var off = function(items) { items.forEach(function(item) { return item.e.map(function(e) { item.context.removeEventListener(e,item.fn,false) }) }) } // move the element to a percentage point var to = function(el, percent) { // This is for sanity but also for visual. The playhead shouldn't leave the progress bar. var clamp = Math.min(Math.max(percent, 0.2), 99.8); // Convert this into matrices stuff later ['webkitTransform', 'MozTransform', 'msTransform', 'transform'].forEach(function(v) { el.style[v] = 'translateX(' + clamp + '%)'; }); } // calculate the percentage point var percentage = function(el, event) { var x = (event.touches) ? event.touches[0].clientX : event.clientX; var position = x - el.getBoundingClientRect().left; var percentage = 100 * position / el.offsetWidth; return Math.min(Math.max(percentage, 0), 100); } /** * Range slider * Creates a simple UI slider for scrubbing video or volume * @param {Element} el */ function Range(el) { // store passed element this._el = el; // current percentage of the range element this._position = 0; // amount of move events in the current range adjustment this._count = 0; // create elements, and the event configuration this.create(); }; // Enable the range slider events Range.prototype.enable = function() { on([this._e.start]); }; // Disable the range slider events Range.prototype.disable = function() { off([this._e.start]); }; // Get the range current position Range.prototype.get = function() { return this._position; }; // Set the range position Range.prototype.set = function() { this._position = percent; to(this._time, this._position); }; // Handle touchstart and mousedown events Range.prototype._start = function() { event.stopPropagation(); on([this._e.move, this._e.stop]); this.trigger('begin', {type: 'begin', srcEvent: event, percent: this._position}); if (this._position !== percentage(this._bar, event)) { this.move(event); } }; // Handle touchmove and mousemove events Range.prototype._move = function() { event.stopPropagation(); this._position = percentage(this._bar, event); to(this._time, this._position); this.trigger('move', {type: 'move', srcEvent: event, count: ++this._count, percent: this._position}); }; // Handle touchend and mouseup events Range.prototype._stop = function() { event.stopPropagation(); off([this._e.move, this._e.stop]); this.trigger('end', {type: 'end', srcEvent: event, percent: this._position }); this._count = 0; }; // Destroy the Range slider elements and remove event listeners Range.prototype.destroy = function() { off([this._e.start, this._e.move, this._e.stop]); this._bar.removeChild(this._time); this._el.removeChild(this._bar); this._el.classList.remove('ivp-control-progress'); }; // Create the Range markup and event configuration Range.prototype.create = function() { // Bar is the outer element always visibile this._bar = document.createElement('div'); this._bar.className = 'ivp-progress-bar'; // Time is the inner element which slides this._time = document.createElement('div'); this._time.className = 'ivp-progress-time'; this._bar.appendChild(this._time); // @NOTE modifying passed element here - not sure if I like this or not this._el.classList.add('ivp-control-progress'); this._el.appendChild(this._bar); var _this = this; this._e = { start: { context: this._bar, e: ['mousedown', 'touchstart'], fn: function() { _this._start.apply(_this, arguments); } }, move: { context: document, e: ['mousemove', 'touchmove'], fn: function() { _this._move.apply(_this, arguments); } }, stop: { context: document, e: ['mouseup', 'touchend'], fn: function() { _this._stop.apply(_this, arguments) } } } }; // Export the Range class as default export default Range;