Related
I do have slider that outputs a value as text. Now I need also to build an input Textfield that does change the slider value and the slider value change the input Textfield value. I did find couple solutions, but that does not work for me, because I have comparison boxes that work hand in hand, when I try those solutions everything crashes.
Here my code:
// Term Slider
function comparisons_term_slider($settings) {
$term = str_replace('[term]',$settings['terminitial'],$settings['termoutput']);
$term = str_replace('[period]',$settings['plural'],$term);
$output = '<div class="comparisons-range comparisons-slider-term">
<div class="comparisons_slider_output">';
$output .= '<div class="output-pad"><span class="circle-down circle-control"></span><span class="output-number"><label for="loan-term">'.$settings['termlabel'].'</label> <output>'.$term.'</output><span class="circle-up circle-control"></span></div>';
$output .= '</div>
<input type="range" class="loan_term" name="loan-term" id="loan-term" min="'.$settings['termmin'].'" max="'.$settings['termmax'].'" value="'.$settings['terminitial'].'" step="'.$settings['termstep'].'" data-comparisons>
</div>';
return $output;
}
and in javascript
// Select all relevant loan slider forms
$(comparisons_loan_selector).each(function() {
// Initialize sliders
var sliders = $(this).find('[data-comparisons]'), x = $(this);
sliders.change(comparisonsCalculate);
sliders.comparisons({polyfill:false});
$('#lc_show_more').click(function() {
comparisonsShowMore.apply(this);
//comparisonsCalculate;
});
$('#sortby').change(comparisonsCalculate);
// Up and down buttons
x.find('.circle-control').click(function() {
var holder = $(this).closest('.comparisons-range'),
range = holder.find('input'),
newVal = parseFloat(range.val()),
step = parseFloat(range.attr('step')),
min = parseFloat(range.attr('min')),
max = parseFloat(range.attr('max'));
if ($(this).hasClass('circle-down')) { // reduce the slider value
newVal = newVal - step;
if (newVal > min) range.val(newVal);
else range.val(min);
} else { // raise the slider value
newVal = newVal + step;
if (newVal < max) range.val(newVal);
else range.val(max);
}
range.change();
});
// Sliders
jQuery(document).ready(function($) {
(function(factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
factory(require('jquery'));
} else {
factory(jQuery);
}
} (function($) {
'use strict';
function supportsRange() {
var input = document.createElement('input');
input.setAttribute('type', 'range');
return input.type !== 'text';
}
var pluginName = 'comparisons',
pluginInstances = [],
inputrange = supportsRange(),
defaults = {
polyfill: true,
rangeClass: 'comparisons',
disabledClass: 'comparisons--disabled',
fillClass: 'comparisons__fill',
handleClass: 'comparisons__handle',
startEvent: ['mousedown', 'touchstart', 'pointerdown'],
moveEvent: ['mousemove', 'touchmove', 'pointermove'],
endEvent: ['mouseup', 'touchend', 'pointerup']
};
function delay(fn, wait) {
var args = Array.prototype.slice.call(arguments, 2);
return setTimeout(function(){ return fn.apply(null, args); }, wait);
}
function debounce(fn, debounceDuration) {
debounceDuration = debounceDuration || 100;
return function() {
if (!fn.debouncing) {
var args = Array.prototype.slice.apply(arguments);
fn.lastReturnVal = fn.apply(window, args);
fn.debouncing = true;
}
clearTimeout(fn.debounceTimeout);
fn.debounceTimeout = setTimeout(function(){
fn.debouncing = false;
}, debounceDuration);
return fn.lastReturnVal;
};
}
function Plugin(element, options) {
this.$window = $(window);
this.$document = $(document);
this.$element = $(element);
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;
this.onSlideEnd = this.options.onSlideEnd;
if (this.polyfill) {
if (inputrange) { return false; }
}
this.identifier = 'js-' + pluginName + '-' +(+new Date())+'-'+(pluginInstances.length + 1);
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 = $('<div class="' + this.options.fillClass + '" />');
this.$handle = $('<div class="' + this.options.handleClass + '" />');
this.$range = $('<div class="' + this.options.rangeClass + '" id="' + this.identifier + '" />').insertAfter(this.$element).prepend(this.$fill, this.$handle);
this.$element.css({
'position': 'absolute',
'width': '1px',
'height': '1px',
'overflow': 'hidden',
'opacity': '0'
});
this.handleDown = $.proxy(this.handleDown, this);
this.handleMove = $.proxy(this.handleMove, this);
this.handleEnd = $.proxy(this.handleEnd, this);
this.init();
var _this = this;
this.$window.on('resize' + '.' + pluginName, debounce(function() {
delay(function() { _this.update(); }, 300);
}, 20));
this.$document.on(this.startEvent, '#' + this.identifier + ':not(.' + this.options.disabledClass + ')', this.handleDown);
this.$element.on('change' + '.' + pluginName, function(e, data) {
if (data && data.origin === pluginName) {
return;
}
var value = e.target.value,
pos = _this.getPositionFromValue(value);
_this.setPosition(pos);
});
}
Plugin.prototype.init = function() {
if (this.onInit && typeof this.onInit === 'function') {
this.onInit();
}
this.update();
};
Plugin.prototype.attributes = function() {
var e = this.$element;
this.min = parseFloat(e.attr('min'));
this.max = parseFloat(e.attr('max'));
this.value = parseFloat(e.attr('value'));
if (this.value > this.max) this.setValue(this.max);
if (this.value < this.min) this.setValue(this.min);
if (this.value > this.min && this.value > this.min) this.setValue(this.value);
this.update();
};
Plugin.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);
if (this.$element[0].disabled) {
this.$range.addClass(this.options.disabledClass);
} else {
this.$range.removeClass(this.options.disabledClass);
}
this.setPosition(this.position);
};
Plugin.prototype.handleDown = function(e) {
e.preventDefault();
this.$document.on(this.moveEvent, this.handleMove);
this.$document.on(this.endEvent, this.handleEnd);
if ((' ' + e.target.className + ' ').replace(/[\n\t]/g, ' ').indexOf(this.options.handleClass) > -1) {
return;
}
var posX = this.getRelativePosition(this.$range[0], e),
handleX = this.getPositionFromNode(this.$handle[0]) - this.getPositionFromNode(this.$range[0]);
this.setPosition(posX - this.grabX);
if (posX >= handleX && posX < handleX + this.handleWidth) {
this.grabX = posX - handleX;
}
};
Plugin.prototype.handleMove = function(e) {
e.preventDefault();
var posX = this.getRelativePosition(this.$range[0], e);
this.setPosition(posX - this.grabX);
};
Plugin.prototype.handleEnd = function(e) {
e.preventDefault();
this.$document.off(this.moveEvent, this.handleMove);
this.$document.off(this.endEvent, this.handleEnd);
if (this.onSlideEnd && typeof this.onSlideEnd === 'function') {
this.onSlideEnd(this.position, this.value);
}
};
Plugin.prototype.cap = function(pos, min, max) {
if (pos < min) { return min; }
if (pos > max) { return max; }
return pos;
};
Plugin.prototype.setPosition = function(pos) {
var value, left;
value = (this.getValueFromPosition(this.cap(pos, 0, this.maxHandleX)) / this.step) * this.step;
left = this.getPositionFromValue(value);
this.$fill[0].style.width = (left + this.grabX) + 'px';
this.$handle[0].style.left = left + 'px';
this.setValue(value);
this.position = left;
this.value = value;
};
Plugin.prototype.getPositionFromNode = function(node) {
var i = 0;
while (node !== null) {
i += node.offsetLeft;
node = node.offsetParent;
}
return i;
};
Plugin.prototype.getRelativePosition = function(node, e) {
return (e.pageX || e.originalEvent.clientX || e.originalEvent.touches[0].clientX || e.currentPoint.x) - this.getPositionFromNode(node);
};
Plugin.prototype.getPositionFromValue = function(value) {
var percentage, pos;
percentage = (value - this.min)/(this.max - this.min);
pos = percentage * this.maxHandleX;
return pos;
};
Plugin.prototype.getValueFromPosition = function(pos) {
var percentage, value;
percentage = ((pos) / (this.maxHandleX || 1));
value = this.step * Math.round((((percentage) * (this.max - this.min)) + this.min) / this.step);
return Number((value).toFixed(2));
};
Plugin.prototype.setValue = function(value) {
if (value !== this.value) {
this.$element.val(value).trigger('change', {origin: pluginName});
}
};
Plugin.prototype.destroy = function() {
this.$document.off(this.startEvent, '#' + this.identifier, this.handleDown);
this.$element
.off('.' + pluginName)
.removeAttr('style')
.removeData('plugin_' + pluginName);
if (this.$range && this.$range.length) {
this.$range[0].parentNode.removeChild(this.$range[0]);
}
pluginInstances.splice(pluginInstances.indexOf(this.$element[0]),1);
if (!pluginInstances.length) {
this.$window.off('.' + pluginName);
}
};
$.fn[pluginName] = function(options) {
return this.each(function() {
var $this = $(this),
data = $this.data('plugin_' + pluginName);
if (!data) {
$this.data('plugin_' + pluginName, (data = new Plugin(this, options)));
pluginInstances.push(this);
}
if (typeof options === 'string') {
data[options]();
}
});
};
}));
});
any solutions possible? Do you need more code?
Already did try this but it doesnt work aswell..
<div>
<input id="rangeInput" type="range" min="0" max="200" oninput="amount.value=rangeInput.value" />
<input id="amount" type="number" value="100" min="0" max="200" oninput="rangeInput.value=amount.value" />
</div>
I have a javascript function for a slider in the JS file of my site. When we are in a page where the slider is not called or there is no trigger element, it displays an error in the console "Uncaught TypeError: Cannot read properties of null (reading 'querySelector') ", This error is not displayed when the slider is in the page.
I would like to know how to avoid this error, and how to prevent this function from loading in pages where this slider is not present?
function slider(set) {
const sliderContainer = document.querySelector(set.name),
slider = sliderContainer.querySelector('.slider'),
sliderItem = slider.querySelectorAll('.slider__item'),
sliderArrows = sliderContainer.querySelectorAll('.arrows__item');
let dotsCreate,
dotsClass,
dotsFunk,
numberSlider,
numberSliderWork,
sliderExecutionLine,
sliderExecutionLineWork;
// calculate the maximum width of all slides
function forSliderItem(t) {
t = 0;
for(let i = 0; i < sliderItem.length - 1; i++) {
t += sliderItem[i].clientWidth;
}
return t;
}
let maxWidth = forSliderItem(sliderItem), // maximum width of all slides
slidWidth = 0, // main variable for calculating the movement of the slider
count = 0; // counter
//===== flip left
sliderArrows[0].addEventListener('click', function() {
if(count !== 0) {
count--;
slidWidth -= sliderItem[count].clientWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
} else {
count = sliderItem.length - 1;
slidWidth = maxWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
}
if(set.dots) {
dotsFunk();
}
if(set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
//===== flip right
sliderArrows[1].addEventListener('click', function() {
if(count < sliderItem.length - 1) {
count++;
slidWidth += sliderItem[count].clientWidth;
slider.style.transform = `translateX(-${slidWidth}px)`;
} else {
count = 0;
slidWidth = 0;
slider.style.transform = `translateX(-${slidWidth}px)`;
}
if(set.dots) {
dotsFunk();
}
if(set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
//===== dots
if(set.dots) {
dotsCreate = function() {
const dotContainer = document.createElement('div'); // create dots container
dotContainer.classList.add('dots');
// create the required number of dots and insert a container into the dots
sliderItem.forEach(() => {
let dotsItem = document.createElement('span');
dotContainer.append(dotsItem);
});
sliderContainer.append(dotContainer);
};
dotsCreate();
// add the class to the desired dots, and remove from the rest
dotsClass = function(remove, add) {
remove.classList.remove('dots_active');
add.classList.add('dots_active');
};
// move slides by clicking on the dot
dotsFunk = function() {
const dotsWork = sliderContainer.querySelectorAll('.dots span'); // we get dots
dotsWork.forEach((item, i) => {
dotsClass(dotsWork[i], dotsWork[count]);
item.addEventListener('click', function() {
count = i;
// multiply the slide size by the number of the dots, and get the number by which you need to move the slider
slidWidth = sliderItem[0].clientWidth * i;
slider.style.transform = `translateX(-${slidWidth}px)`;
for(let j = 0; j < dotsWork.length; j++) {
dotsClass(dotsWork[j], dotsWork[count]);
}
if(set.dots && set.numberSlid) {
numberSliderWork(count);
}
if(set.line) {
sliderExecutionLineWork(count);
}
});
});
};
dotsFunk();
}
//===== count slider
if(set.numberSlid) {
numberSlider = function(item) {
const countContainer = document.createElement('div'),
sliderNumber = document.createElement('span'),
slash = document.createElement('span'),
allSliderNumber = document.createElement('span');
casClient = document.createElement('p');
sliderNumber.innerHTML = item + 1;
casClient.innerHTML = 'Cas client N°';
slash.innerHTML = '/';
allSliderNumber.innerHTML = sliderItem.length;
countContainer.classList.add('count-slides');
countContainer.append(casClient, sliderNumber, slash, allSliderNumber);
sliderContainer.append(countContainer);
};
numberSlider(0);
numberSliderWork = function(item) {
const sliderNumberNow = sliderContainer.querySelector('.count-slides span');
sliderNumberNow.innerHTML = item + 1;
if(set.line) {
sliderExecutionLineWork(item);
}
};
}
}
slider({
name: ".video_users",
numberSlid: true
});
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Example of a page without the slider</title>
</head>
<body class="error_404">
<h1>Example of a page without the slider</h1>
<script src="media/js/faveod.js" async></script>
</body>
</html>
Just return if the element isn't present if you don't want it to show an error.
const sliderContainer = document.querySelector(set.name);
if (!sliderContainer) return;
const slider = sliderContainer.querySelector('.slider'),
sliderItem = slider.querySelectorAll('.slider__item'),
sliderArrows = sliderContainer.querySelectorAll('.arrows__item');
If you want it to only execute on certain pages, then just add:
if (location.pathname !== '/the/page/with/slider') return;
to the beginning of the function
/*
* SCROLLBAR 2 COLUMN / SOLUTIONS
*/
var ssb = {
aConts : [],
mouseY : 0,
N : 0,
asd : 0, /*active scrollbar element*/
sc : 0,
sp : 0,
to : 0,
// constructor
scrollbar : function (cont_id) {
var cont = document.getElementById(cont_id);
// perform initialization
if (! ssb.init()) return false;
var cont_clone = cont.cloneNode(false);
cont_clone.style.overflow = "hidden";
cont.parentNode.appendChild(cont_clone);
cont_clone.appendChild(cont);
cont.style.position = 'absolute';
cont.style.left = cont.style.top = '0px';
cont.style.width = cont.style.height = '100%';
// adding new container into array
ssb.aConts[ssb.N++] = cont;
cont.sg = false;
//creating scrollbar child elements
cont.st = this.create_div('ssb_st', cont, cont_clone);
cont.sb = this.create_div('ssb_sb', cont, cont_clone);
cont.su = this.create_div('ssb_up', cont, cont_clone);
cont.sd = this.create_div('ssb_down', cont, cont_clone);
// on mouse down processing
cont.sb.onmousedown = function (e) {
if (! this.cont.sg) {
if (! e) e = window.event;
ssb.asd = this.cont;
this.cont.yZ = e.screenY;
this.cont.sZ = cont.scrollTop;
this.cont.sg = true;
// new class name
this.className = 'ssb_sb ssb_sb_down';
}
return false;
}
// on mouse down on free track area - move our scroll element too
cont.st.onmousedown = function (e) {
if (! e) e = window.event;
ssb.asd = this.cont;
ssb.mouseY = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
for (var o = this.cont, y = 0; o != null; o = o.offsetParent) y += o.offsetTop;
this.cont.scrollTop = (ssb.mouseY - y - (this.cont.ratio * this.cont.offsetHeight / 2) - this.cont.sw) / this.cont.ratio;
this.cont.sb.onmousedown(e);
}
// onmousedown events
cont.su.onmousedown = cont.su.ondblclick = function (e) { ssb.mousedown(this, -1); return false; }
cont.sd.onmousedown = cont.sd.ondblclick = function (e) { ssb.mousedown(this, 1); return false; }
//onmouseout events
cont.su.onmouseout = cont.su.onmouseup = ssb.clear;
cont.sd.onmouseout = cont.sd.onmouseup = ssb.clear;
// on mouse over - apply custom class name: ssb_sb_over
cont.sb.onmouseover = function (e) {
if (! this.cont.sg) this.className = 'ssb_sb ssb_sb_over';
return false;
}
// on mouse out - revert back our usual class name 'ssb_sb'
cont.sb.onmouseout = function (e) {
if (! this.cont.sg) this.className = 'ssb_sb';
return false;
}
// onscroll - change positions of scroll element
cont.ssb_onscroll = function () {
this.ratio = (this.offsetHeight - 2 * this.sw) / this.scrollHeight;
this.sb.style.top = Math.floor(this.sw + this.scrollTop * this.ratio) + 'px';
}
// scrollbar width
cont.sw = 16;
// start scrolling
cont.ssb_onscroll();
ssb.refresh();
// binding own onscroll event
cont.onscroll = cont.ssb_onscroll;
return cont;
},
// initialization
init : function () {
if (window.oper || (! window.addEventListener && ! window.attachEvent)) { return false; }
// temp inner function for event registration
function addEvent (o, e, f) {
if (window.addEventListener) { o.addEventListener(e, f, false); ssb.w3c = true; return true; }
if (window.attachEvent) return o.attachEvent('on' + e, f);
return false;
}
// binding events
addEvent(window.document, 'mousemove', ssb.onmousemove);
addEvent(window.document, 'mouseup', ssb.onmouseup);
addEvent(window, 'resize', ssb.refresh);
return true;
},
// create and append div finc
create_div : function(c, cont, cont_clone) {
var o = document.createElement('div');
o.cont = cont;
o.className = c;
cont_clone.appendChild(o);
return o;
},
// do clear of controls
clear : function () {
clearTimeout(ssb.to);
ssb.sc = 0;
return false;
},
// refresh scrollbar
refresh : function () {
for (var i = 0, N = ssb.N; i < N; i++) {
var o = ssb.aConts[i];
o.ssb_onscroll();
o.sb.style.width = o.st.style.width = o.su.style.width = o.su.style.height = o.sd.style.width = o.sd.style.height = o.sw + 'px';
o.sb.style.height = Math.ceil(Math.max(o.sw * .5, o.ratio * o.offsetHeight) + 1) + 'px';
}
},
// arrow scrolling
arrow_scroll : function () {
if (ssb.sc != 0) {
ssb.asd.scrollTop += 6 * ssb.sc / ssb.asd.ratio;
ssb.to = setTimeout(ssb.arrow_scroll, ssb.sp);
ssb.sp = 32;
}
},
/* event binded functions : */
// scroll on mouse down
mousedown : function (o, s) {
if (ssb.sc == 0) {
// new class name
o.cont.sb.className = 'ssb_sb ssb_sb_down';
ssb.asd = o.cont;
ssb.sc = s;
ssb.sp = 100;
ssb.arrow_scroll();
}
},
// on mouseMove binded event
onmousemove : function(e) {
if (! e) e = window.event;
// get vertical mouse position
ssb.mouseY = e.screenY;
if (ssb.asd.sg) ssb.asd.scrollTop = ssb.asd.sZ + (ssb.mouseY - ssb.asd.yZ) / ssb.asd.ratio;
},
// on mouseUp binded event
onmouseup : function (e) {
if (! e) e = window.event;
var tg = (e.target) ? e.target : e.srcElement;
if (ssb.asd && document.releaseCapture) ssb.asd.releaseCapture();
// new class name
if (ssb.asd) ssb.asd.sb.className = (tg.className.indexOf('scrollbar') > 0) ? 'ssb_sb ssb_sb_over' : 'ssb_sb';
document.onselectstart = '';
ssb.clear();
ssb.asd.sg = false;
}
}
window.onload = function() {
ssb.scrollbar('container'); // scrollbar initialization
}
I am using videojs in my react application. I have added rangeslider to it. There is a button near my video player which triggers the rangeslider to show up on the video player. Every thing works fine, but the rangeslider's start arrow is not at the point (time) where I clicked the button. The starting button is always at time 0:0. What I want is: say the video is playing at the current time 30:00 seconds and I clicked the show button, then rangeslider should show it's starting arrow at 30 sec only and not at o sec. I can get my current time of videojs player and pass it to rangeslider.js plugin, but I don't know where to pass it.
This is my rangeslider.js (I know it's a long code but i don't know which part of it to use to achieve the result)
//----------------Load Plugin----------------//
(function () {
var videojsAddClass = function (element, className) {
element.classList.add(className);
};
var videojsRemoveClass = function (element, className) {
element.classList.remove(className);
};
var videojsFindPosition = function (element) {
return element.getBoundingClientRect();
};
var videojsRound = function (n, precision) {
return parseFloat(n.toFixed(precision));
};
var videojsFormatTime = function (totalSeconds) {
var minutes = Math.floor(totalSeconds / 60).toFixed(0);
var seconds = (totalSeconds % 60).toFixed(0);
if (seconds.length === 1) {
seconds = "0" + seconds;
}
return minutes + ':' + seconds;
};
var videojsBlockTextSelection = function () {
// TODO
};
//-- Load RangeSlider plugin in videojs
function RangeSlider_(options) {
var player = this;
player.rangeslider = new RangeSlider(player, options);
//When the DOM and the video media is loaded
function initialVideoFinished(event) {
var plugin = player.rangeslider;
//All components will be initialize after they have been loaded by videojs
for (var index in plugin.components) {
plugin.components[index].init_();
}
if (plugin.options.hidden)
plugin.hide(); //Hide the Range Slider
if (plugin.options.locked)
plugin.lock(); //Lock the Range Slider
if (plugin.options.panel == false)
plugin.hidePanel(); //Hide the second Panel
if (plugin.options.controlTime == false)
plugin.hidecontrolTime(); //Hide the control time panel
plugin._reset();
player.trigger('loadedRangeSlider'); //Let know if the Range Slider DOM is ready
}
if (player.techName == 'Youtube') {
//Detect youtube problems
player.one('error', function (e) {
switch (player.error) {
case 2:
alert("The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.");
case 5:
alert("The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.");
case 100:
alert("The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.");
break;
case 101:
alert("The owner of the requested video does not allow it to be played in embedded players.");
break;
case 150:
alert("The owner of the requested video does not allow it to be played in embedded players.");
break;
default:
alert("Unknown Error");
break;
}
});
player.on('firstplay', initialVideoFinished);
} else {
player.one('playing', initialVideoFinished);
}
}
videojs.plugin('rangeslider', RangeSlider_);
//-- Plugin
function RangeSlider(player, options) {
var player = player || this;
this.player = player;
this.components = {}; // holds any custom components we add to the player
options = options || {}; // plugin options
if (!options.hasOwnProperty('locked'))
options.locked = false; // lock slider handles
if (!options.hasOwnProperty('hidden'))
options.hidden = true; // hide slider handles
if (!options.hasOwnProperty('panel'))
options.panel = true; // Show Second Panel
if (!options.hasOwnProperty('controlTime'))
options.controlTime = true; // Show Control Time to set the arrows in the edition
this.options = options;
this.init();
}
//-- Methods
RangeSlider.prototype = {
/*Constructor*/
init: function () {
var player = this.player || {};
this.updatePrecision = 3;
//position in second of the arrows
this.start = 0;
this.end = 0;
//components of the plugin
var controlBar = player.controlBar;
var seekBar = controlBar.progressControl.seekBar;
this.components.RSTimeBar = seekBar.RSTimeBar;
this.components.ControlTimePanel = controlBar.ControlTimePanel;
//Save local component
this.rstb = this.components.RSTimeBar;
this.box = this.components.SeekRSBar = this.rstb.SeekRSBar;
this.bar = this.components.SelectionBar = this.box.SelectionBar;
this.left = this.components.SelectionBarLeft = this.box.SelectionBarLeft;
this.right = this.components.SelectionBarRight = this.box.SelectionBarRight;
this.tp = this.components.TimePanel = this.box.TimePanel;
this.tpl = this.components.TimePanelLeft = this.tp.TimePanelLeft;
this.tpr = this.components.TimePanelRight = this.tp.TimePanelRight;
this.ctp = this.components.ControlTimePanel;
this.ctpl = this.components.ControlTimePanelLeft = this.ctp.ControlTimePanelLeft;
this.ctpr = this.components.ControlTimePanelRight = this.ctp.ControlTimePanelRight;
},
show: function () {
this.options.hidden = false;
if (typeof this.rstb != 'undefined') {
this.rstb.show();
if (this.options.controlTime)
this.showcontrolTime();
}
},
hide: function () {
this.options.hidden = true;
if (typeof this.rstb != 'undefined') {
this.rstb.hide();
this.ctp.hide();
}
},
showPanel: function () {
this.options.panel = true;
if (typeof this.tp != 'undefined')
videojsRemoveClass(this.tp.el_, 'disable');
},
showcontrolTime: function () {
this.options.controlTime = true;
if (typeof this.ctp != 'undefined')
this.ctp.show();
},
hidecontrolTime: function () {
this.options.controlTime = false;
if (typeof this.ctp != 'undefined')
this.ctp.hide();
},
setValue: function (index, seconds, writeControlTime) {
//index = 0 for the left Arrow and 1 for the right Arrow. Value in seconds
var writeControlTime = typeof writeControlTime != 'undefined' ? writeControlTime : true;
var percent = this._percent(seconds);
var isValidIndex = (index === 0 || index === 1);
var isChangeable = !this.locked;
if (isChangeable && isValidIndex)
this.box.setPosition(index, percent, writeControlTime);
},
setValues: function (start, end, writeControlTime) {
//index = 0 for the left Arrow and 1 for the right Arrow. Value in seconds
var writeControlTime = typeof writeControlTime != 'undefined' ? writeControlTime : true;
this._reset();
this._setValuesLocked(start, end, writeControlTime);
},
getValues: function () { //get values in seconds
var values = {}, start, end;
start = this.start || this._getArrowValue(0);
end = this.end || this._getArrowValue(1);
return {start: start, end: end};
},
_getArrowValue: function (index) {
var index = index || 0;
var duration = this.player.duration();
duration = typeof duration == 'undefined' ? 0 : duration;
var percentage = this[index === 0 ? "left" : "right"].el_.style.left.replace("%", "");
if (percentage == "")
percentage = index === 0 ? 0 : 100;
return videojsRound(this._seconds(percentage / 100), this.updatePrecision - 1);
},
_percent: function (seconds) {
var duration = this.player.duration();
if (isNaN(duration)) {
return 0;
}
return Math.min(1, Math.max(0, seconds / duration));
},
_seconds: function (percent) {
var duration = this.player.duration();
if (isNaN(duration)) {
return 0;
}
return Math.min(duration, Math.max(0, percent * duration));
},
_reset: function () {
var duration = this.player.duration();
this.tpl.el_.style.left = '0%';
this.tpr.el_.style.left = '100%';
this._setValuesLocked(0, duration);
},
_setValuesLocked: function (start, end, writeControlTime) {
var triggerSliderChange = typeof writeControlTime != 'undefined';
var writeControlTime = typeof writeControlTime != 'undefined' ? writeControlTime : true;
if (this.options.locked) {
this.unlock();//It is unlocked to change the bar position. In the end it will return the value.
this.setValue(0, start, writeControlTime);
this.setValue(1, end, writeControlTime);
this.lock();
} else {
this.setValue(0, start, writeControlTime);
this.setValue(1, end, writeControlTime);
}
// Trigger slider change
if (triggerSliderChange) {
this._triggerSliderChange();
}
},
_checkControlTime: function (index, TextInput, timeOld) {
var h = TextInput[0],
m = TextInput[1],
s = TextInput[2],
newHour = h.value,
newMin = m.value,
newSec = s.value,
obj, objNew, objOld;
index = index || 0;
if (newHour != timeOld[0]) {
obj = h;
objNew = newHour;
objOld = timeOld[0];
} else if (newMin != timeOld[1]) {
obj = m;
objNew = newMin;
objOld = timeOld[1];
} else if (newSec != timeOld[2]) {
obj = s;
objNew = newSec;
objOld = timeOld[2];
} else {
return false;
}
var duration = this.player.duration() || 0,
durationSel;
var intRegex = /^\d+$/;//check if the objNew is an integer
if (!intRegex.test(objNew) || objNew > 60) {
objNew = objNew == "" ? "" : objOld;
}
newHour = newHour == "" ? 0 : newHour;
newMin = newMin == "" ? 0 : newMin;
newSec = newSec == "" ? 0 : newSec;
durationSel = videojs.TextTrack.prototype.parseCueTime(newHour + ":" + newMin + ":" + newSec);
if (durationSel > duration) {
obj.value = objOld;
obj.style.border = "1px solid red";
} else {
obj.value = objNew;
h.style.border = m.style.border = s.style.border = "1px solid transparent";
this.setValue(index, durationSel, false);
// Trigger slider change
this._triggerSliderChange();
}
if (index === 1) {
var oldTimeLeft = this.ctpl.el_.children,
durationSelLeft = videojs.TextTrack.prototype.parseCueTime(oldTimeLeft[0].value + ":" + oldTimeLeft[1].value + ":" + oldTimeLeft[2].value);
if (durationSel < durationSelLeft) {
obj.style.border = "1px solid red";
}
} else {
var oldTimeRight = this.ctpr.el_.children,
durationSelRight = videojs.TextTrack.prototype.parseCueTime(oldTimeRight[0].value + ":" + oldTimeRight[1].value + ":" + oldTimeRight[2].value);
if (durationSel > durationSelRight) {
obj.style.border = "1px solid red";
}
}
},
_triggerSliderChange: function () {
this.player.trigger("sliderchange");
}
};
//----------------Public Functions----------------//
//-- Public Functions added to video-js
var videojsPlayer = videojs.getComponent('Player');
//Lock the Slider bar and it will not be possible to change the arrow positions
videojsPlayer.prototype.lockSlider = function () {
return this.rangeslider.lock();
};
//Unlock the Slider bar and it will be possible to change the arrow positions
videojsPlayer.prototype.unlockSlider = function () {
return this.rangeslider.unlock();
};
//Show the Slider Bar Component
videojsPlayer.prototype.showSlider = function () {
return this.rangeslider.show();
};
//Hide the Slider Bar Component
videojsPlayer.prototype.hideSlider = function () {
return this.rangeslider.hide();
};
//Show the Panel with the seconds of the selection
videojsPlayer.prototype.showSliderPanel = function () {
return this.rangeslider.showPanel();
};
//Hide the Panel with the seconds of the selection
videojsPlayer.prototype.hideSliderPanel = function () {
return this.rangeslider.hidePanel();
};
//Show the control Time to edit the position of the arrows
videojsPlayer.prototype.showControlTime = function () {
return this.rangeslider.showcontrolTime();
};
//Hide the control Time to edit the position of the arrows
videojsPlayer.prototype.hideControlTime = function () {
return this.rangeslider.hidecontrolTime();
};
//Set a Value in second for both arrows
videojsPlayer.prototype.setValueSlider = function (start, end) {
return this.rangeslider.setValues(start, end);
};
//The video will be played in a selected section
videojsPlayer.prototype.playBetween = function (start, end) {
return this.rangeslider.playBetween(start, end);
};
//The video will loop between to values
videojsPlayer.prototype.loopBetween = function (start, end) {
return this.rangeslider.loop(start, end);
};
//Set a Value in second for the arrows
videojsPlayer.prototype.getValueSlider = function () {
return this.rangeslider.getValues();
};
//----------------Create new Components----------------//
//--Charge the new Component into videojs
var videojsSeekBar = videojs.getComponent('SeekBar');
videojsSeekBar.prototype.options_.children.push('RSTimeBar'); //Range Slider Time Bar
var videojsControlBar = videojs.getComponent('ControlBar');
videojsControlBar.prototype.options_.children.push('ControlTimePanel'); //Panel with the time of the range slider
//-- Design the new components
var videojsComponent = videojs.getComponent('Component');
/**
* Range Slider Time Bar
* #param {videojs.Player|Object} player
* #param {Object=} options
* #constructor
*/
var videojsRSTimeBar = videojs.extend(videojsComponent, {
/** #constructor */
constructor: function (player, options) {
videojsComponent.call(this, player, options);
}
});
videojsRSTimeBar.prototype.init_ = function () {
this.rs = this.player_.rangeslider;
};
videojsRSTimeBar.prototype.options_ = {
children: {
'SeekRSBar': {}
}
};
videojsRSTimeBar.prototype.createEl = function () {
return videojsComponent.prototype.createEl.call(this, 'div', {
className: 'vjs-timebar-RS',
innerHTML: ''
});
};
videojs.registerComponent('RSTimeBar', videojsRSTimeBar);
/**
* Seek Range Slider Bar and holder for the selection bars
* #param {videojs.Player|Object} player
* #param {Object=} options
* #constructor
*/
var videojsSeekRSBar = videojs.extend(videojsSeekBar, {
/** #constructor */
constructor: function (player, options) {
videojsComponent.call(this, player, options);
this.on('mousedown', this.onMouseDown);
this.on('touchstart', this.onMouseDown);
}
});
videojsSeekRSBar.prototype.init_ = function () {
this.rs = this.player_.rangeslider;
};
videojsSeekRSBar.prototype.options_ = {
children: {
'SelectionBar': {},
'SelectionBarLeft': {},
'SelectionBarRight': {},
'TimePanel': {},
}
};
videojsSeekRSBar.prototype.createEl = function () {
return videojsComponent.prototype.createEl.call(this, 'div', {
className: 'vjs-rangeslider-holder'
});
};
videojsSeekRSBar.prototype.onMouseDown = function (event) {
event.preventDefault();
videojsBlockTextSelection();
if (!this.rs.options.locked) {
this.on(document, "mousemove", videojs.bind(this, this.onMouseMove));
this.on(document, "mouseup", videojs.bind(this, this.onMouseUp));
this.on(document, "touchmove", videojs.bind(this, this.onMouseMove));
this.on(document, "touchend", videojs.bind(this, this.onMouseUp));
}
};
videojsSeekRSBar.prototype.onMouseUp = function (event) {
this.off(document, "mousemove", videojs.bind(this, this.onMouseMove), false);
this.off(document, "mouseup", videojs.bind(this, this.onMouseUp), false);
this.off(document, "touchmove", videojs.bind(this, this.onMouseMove), false);
this.off(document, "touchend", videojs.bind(this, this.onMouseUp), false);
};
videojsSeekRSBar.prototype.onMouseMove = function (event) {
var left = this.calculateDistance(event);
if (this.rs.left.pressed)
this.setPosition(0, left);
else if (this.rs.right.pressed)
this.setPosition(1, left);
//Fix a problem with the presition in the display time
var ctd = this.player_.controlBar.currentTimeDisplay;
ctd.contentEl_.innerHTML = '<span class="vjs-control-text">' + ctd.localize('Current Time') + '</span>' + videojsFormatTime(this.rs._seconds(left), this.player_.duration());
// Trigger slider change
if (this.rs.left.pressed || this.rs.right.pressed) {
this.rs._triggerSliderChange();
}
};
videojsSeekRSBar.prototype.setPosition = function (index, left, writeControlTime) {
var writeControlTime = typeof writeControlTime != 'undefined' ? writeControlTime : true;
//index = 0 for left side, index = 1 for right side
var index = index || 0;
// Position shouldn't change when handle is locked
if (this.rs.options.locked)
return false;
// Check for invalid position
if (isNaN(left))
return false;
// Check index between 0 and 1
if (!(index === 0 || index === 1))
return false;
// Alias
var ObjLeft = this.rs.left.el_,
ObjRight = this.rs.right.el_,
Obj = this.rs[index === 0 ? 'left' : 'right'].el_,
tpr = this.rs.tpr.el_,
tpl = this.rs.tpl.el_,
bar = this.rs.bar,
ctp = this.rs[index === 0 ? 'ctpl' : 'ctpr'].el_;
//Check if left arrow is passing the right arrow
if ((index === 0 ? bar.updateLeft(left) : bar.updateRight(left))) {
Obj.style.left = (left * 100) + '%';
index === 0 ? bar.updateLeft(left) : bar.updateRight(left);
this.rs[index === 0 ? 'start' : 'end'] = this.rs._seconds(left);
//Fix the problem when you press the button and the two arrow are underhand
//left.zIndex = 10 and right.zIndex=20. This is always less in this case:
if (index === 0) {
if ((left) >= 0.9)
ObjLeft.style.zIndex = 25;
else
ObjLeft.style.zIndex = 10;
}
//-- Panel
var TimeText = videojsFormatTime(this.rs._seconds(left)),
tplTextLegth = tpl.children[0].innerHTML.length;
var MaxP, MinP, MaxDisP;
if (tplTextLegth <= 4) //0:00
MaxDisP = this.player_.isFullScreen ? 3.25 : 6.5;
else if (tplTextLegth <= 5)//00:00
MaxDisP = this.player_.isFullScreen ? 4 : 8;
else//0:00:00
MaxDisP = this.player_.isFullScreen ? 5 : 10;
if (TimeText.length <= 4) { //0:00
MaxP = this.player_.isFullScreen ? 97 : 93;
MinP = this.player_.isFullScreen ? 0.1 : 0.5;
} else if (TimeText.length <= 5) {//00:00
MaxP = this.player_.isFullScreen ? 96 : 92;
MinP = this.player_.isFullScreen ? 0.1 : 0.5;
} else {//0:00:00
MaxP = this.player_.isFullScreen ? 95 : 91;
MinP = this.player_.isFullScreen ? 0.1 : 0.5;
}
if (index === 0) {
tpl.style.left = Math.max(MinP, Math.min(MaxP, (left * 100 - MaxDisP / 2))) + '%';
if ((tpr.style.left.replace("%", "") - tpl.style.left.replace("%", "")) <= MaxDisP)
tpl.style.left = Math.max(MinP, Math.min(MaxP, tpr.style.left.replace("%", "") - MaxDisP)) + '%';
tpl.children[0].innerHTML = TimeText;
} else {
tpr.style.left = Math.max(MinP, Math.min(MaxP, (left * 100 - MaxDisP / 2))) + '%';
if (((tpr.style.left.replace("%", "") || 100) - tpl.style.left.replace("%", "")) <= MaxDisP)
tpr.style.left = Math.max(MinP, Math.min(MaxP, tpl.style.left.replace("%", "") - 0 + MaxDisP)) + '%';
tpr.children[0].innerHTML = TimeText;
}
//-- Control Time
if (writeControlTime) {
var time = TimeText.split(":"),
h, m, s;
if (time.length == 2) {
h = 0;
m = time[0];
s = time[1];
} else {
h = time[0];
m = time[1];
s = time[2];
}
ctp.children[0].value = h;
ctp.children[1].value = m;
ctp.children[2].value = s;
}
}
return true;
};
videojs.registerComponent('SeekRSBar', videojsSeekRSBar);
/**
* This is the bar with the selection of the RangeSlider
* #param {videojs.Player|Object} player
* #param {Object=} options
* #constructor
*/
var videojsSelectionBar = videojs.extend(videojsComponent, {
/** #constructor */
constructor: function (player, options) {
videojsComponent.call(this, player, options);
this.on('mouseup', this.onMouseUp);
this.on('touchend', this.onMouseUp);
this.fired = false;
}
});
videojsSelectionBar.prototype.init_ = function () {
this.rs = this.player_.rangeslider;
};
videojsSelectionBar.prototype.createEl = function () {
return videojsComponent.prototype.createEl.call(this, 'div', {
className: 'vjs-selectionbar-RS'
});
};
videojsControlTimePanelRight.prototype.init_ = function () {
this.rs = this.player_.rangeslider;
this.timeOld = {};
};
videojsControlTimePanelRight.prototype.createEl = function () {
return videojsComponent.prototype.createEl.call(this, 'div', {
className: 'vjs-controltimepanel-right-RS',
innerHTML: 'End: <input type="text" id="controltimepanel" maxlength="2" value="00"/>:<input type="text" id="controltimepanel" maxlength="2" value="00"/>:<input type="text" id="controltimepanel" maxlength="2" value="00"/>'
});
};
videojsControlTimePanelRight.prototype.onKeyDown = function (event) {
this.timeOld[0] = this.el_.children[0].value;
this.timeOld[1] = this.el_.children[1].value;
this.timeOld[2] = this.el_.children[2].value;
};
videojsControlTimePanelRight.prototype.onKeyUp = function (event) {
this.rs._checkControlTime(1, this.el_.children, this.timeOld);
};
videojs.registerComponent('ControlTimePanelRight', videojsControlTimePanelRight);
})();
And this is how rangeslider looks everytime it shows up
I use
video.currentTime
But it looks like rangeslider is using
e.currentPoint
Before I get into details, please take a look at the live example where this problem occurs - http://jsfiddle.net/66HFU/ (Script code at the bottom of this post)
Now if you would click on any image at last row, it would display these. However if you would click on upper row images, below row images are shown.
Further investigation shows that for somewhat reason the letter called function selector elements only get binded with event listener while the firstly called functions selector elements do not.
So, I would like to know is there are any ways to make the function call independent so the latter function call does not override first one (if that would fix the problem, of-course)?
The place where the event function gets bind on the element can be found in function f_AddEvents first lines.
And the the main function calls that are used to initialize Light Box is at the bottom of the code like this:
LightBox.init({
selector: "[data-simplbox='demo1']",
boxId: "simplbox"
});
LightBox.init({
selector: "[data-simplbox='demo2']",
boxId: "simplbox",
imageLoadStart: activityIndicatorOn,
imageLoadEnd: activityIndicatorOff
});
All code:
;(function (window, document, undefined) {
var docElem = document.documentElement;
var DomM = (function() {
var f_ToDOMStyle = function (p_Style) {
return p_Style.replace(/\-[a-z]/g, function (p_Style) {
return p_Style.charAt(1).toUpperCase();
});
};
return {
event: {
set: function (p_Element, p_Events, p_Function) {
var i = 0,
j = 0;
p_Events = p_Events.split(" ");
if (!p_Element.length) {
for (i = 0; i < p_Events.length; i++) {
p_Element.addEventListener(p_Events[i], p_Function, false);
}
} else {
for (i = 0; i < p_Element.length; i++) {
for (j = 0; j < p_Events.length; j++) {
p_Element[i].addEventListener(p_Events[j], p_Function, false);
}
}
}
}
},
css: {
set: function (p_Element, p_Style) {
var j;
if (!p_Element.length) {
for (j in p_Style) {
if (p_Style.hasOwnProperty(j)) {
j = f_ToDOMStyle(j);
p_Element.style[j] = p_Style[j];
}
}
} else {
for (var i = 0; i < p_Element.length; i++) {
for (j in p_Style) {
if (p_Style.hasOwnProperty(j)) {
j = f_ToDOMStyle(j);
p_Element[i].style[j] = p_Style[j];
}
}
}
}
}
}
};
}());
var _LightBox = {
f_MergeObjects: function (p_Original, p_Updates) {
for (var i in p_Updates) {
if (p_Updates.hasOwnProperty(i)) {
p_Original[i] = p_Updates[i];
}
}
return p_Original;
},
f_isFunction: function (p_Function) {
return !!(p_Function && p_Function.constructor && p_Function.call && p_Function.apply);
},
f_Initialize: function (p_Options) {
var base = this;
base.m_Options = base.f_MergeObjects(_LightBox.options, p_Options || {});
base.m_Elements = document.querySelectorAll(base.m_Options.selector);
base.m_ElementsLength = base.m_Elements.length - 1;
base.m_Body = document.getElementsByTagName("body")[0];
base.m_CurrentImageElement = false;
base.m_CurrentImageNumber = 0;
base.m_Direction = 1;
base.m_InProgress = false;
base.m_InstalledImageBox = false;
console.log(base.m_Elements);
// Check if hardware acceleration is supported and check if touch is enabled.
base.f_CheckBrowser();
// Adds events.
base.f_AddEvents();
},
f_CheckBrowser: function () {
var base = this,
isTouch = "ontouchstart" in window || window.navigator.msMaxTouchPoints || navigator.maxTouchPoints || false,
vendors = ["ms", "O", "Moz", "Webkit", "Khtml"],
rootStyle = docElem.style,
hardwareAccelerated = false;
if ("transform" in rootStyle) {
hardwareAccelerated = true;
} else {
while (vendors.length) {
if (vendors.pop() + "Transform" in rootStyle) {
hardwareAccelerated = true;
}
}
}
base.browser = {
"isHardwareAccelerated": hardwareAccelerated,
"isTouch": isTouch
};
},
f_AddEvents: function () {
var base = this;
// Add open image event on images.
for (var i = 0; i < base.m_Elements.length; i++) {
(function (i) {
base.m_Elements[i].addEventListener("click", function (event) {
event.preventDefault();
console.log(base.m_Elements[i]);
if (base.f_isFunction(base.m_Options.onImageStart)) {
base.m_Options.onImageStart();
}
base.f_OpenImage(i);
}, false);
})(i);
}
// Resize event for window.
window.addEventListener("resize", function (event) {
event.preventDefault();
base.f_SetImage();
}, false);
// Add keyboard support.
if (base.m_Options.enableKeyboard) {
var keyBoard = {
left: 37,
right: 39,
esc: 27
};
window.addEventListener("keydown", function (event) {
event.preventDefault();
if (base.m_CurrentImageElement) {
if (base.m_InProgress) {
return false;
}
switch (event.keyCode) {
case keyBoard.left:
// If the previous one is out of target range then go to the last image.
if ((base.m_CurrentImageNumber - 1) < 0) {
base.f_OpenImage(base.m_ElementsLength, "left");
} else {
base.f_OpenImage(base.m_CurrentImageNumber - 1, "left");
}
return false;
case keyBoard.right:
// If the next one is out of target range then go to the first image.
if ((base.m_CurrentImageNumber + 1) > base.m_ElementsLength) {
base.f_OpenImage(0, "right");
} else {
base.f_OpenImage(base.m_CurrentImageNumber + 1, "right");
}
return false;
case keyBoard.esc:
base.f_QuitImage();
return false;
}
}
return false;
}, false);
}
// Add document click event.
if (base.m_Options.quitOnDocumentClick) {
document.body.addEventListener("click", function (event) {
var target = event.target ? event.target : event.srcElement;
event.preventDefault();
if (target && target.id != "imagelightbox" && base.m_CurrentImageElement && !base.m_InProgress && base.m_InstalledImageBox) {
base.f_QuitImage();
return false;
}
return false;
}, false);
}
},
f_OpenImage: function (p_WhichOne, p_Direction) {
var base = this,
newFragment = document.createDocumentFragment(),
newImageElement = document.createElement("img"),
target = base.m_Elements[p_WhichOne].getAttribute("href");
if (base.m_CurrentImageElement) {
base.f_RemoveImage();
}
if (base.f_isFunction(base.m_Options.imageLoadStart)) {
base.m_Options.imageLoadStart();
}
base.m_InProgress = true;
base.m_InstalledImageBox = false;
base.m_Direction = typeof p_Direction === "undefined" ? 1 : p_Direction == "left" ? -1 : 1;
newImageElement.setAttribute("src", target);
newImageElement.setAttribute("alt", "LightBox");
newImageElement.setAttribute("id", base.m_Options.boxId);
newFragment.appendChild(newImageElement);
base.m_Body.appendChild(newFragment);
base.m_CurrentImageElement = document.getElementById(base.m_Options.boxId);
base.m_CurrentImageElement.style.opacity = "0";
base.m_CurrentImageNumber = p_WhichOne;
if (base.m_Options.quitOnImageClick) {
base.f_ImageClickEvent = function (event) {
event.preventDefault();
base.f_QuitImage();
};
base.m_CurrentImageElement.addEventListener("click", base.f_ImageClickEvent, false);
}
if (base.browser.isHardwareAccelerated) {
DomM.css.set(base.m_CurrentImageElement, base.f_AddTransitionSpeed(base.m_Options.animationSpeed));
}
base.f_SetImage();
DomM.css.set(base.m_CurrentImageElement, base.f_doTranslateX(50 * base.m_Direction + "px"));
setTimeout(function () {
if (base.browser.isHardwareAccelerated) {
setTimeout(function () {
DomM.css.set(base.m_CurrentImageElement, base.f_doTranslateX("0px"));
}, 50);
}
if (base.f_isFunction(base.m_Options.imageLoadEnd)) {
base.m_Options.imageLoadEnd();
}
}, 20);
setTimeout(function () {
base.m_InProgress = false;
base.m_InstalledImageBox = true;
}, base.m_Options.animationSpeed - 200);
},
f_SetImage: function () {
var base = this,
screenHeight = window.innerHeight || docElem.offsetHeight,
screenWidth = window.innerWidth || docElem.offsetWidth,
tmpImage = new Image(),
imageWidth, imageHeight, imageSizeRatio;
if (!base.m_CurrentImageElement) {
return;
}
tmpImage.onload = function () {
imageWidth = this.width;
imageHeight = this.height;
imageSizeRatio = imageWidth / imageHeight;
if (Math.floor(screenWidth/imageSizeRatio) > screenHeight) {
imageWidth = screenHeight * imageSizeRatio * 0.7;
imageHeight = screenHeight * 0.7;
} else {
imageWidth = screenWidth * 0.7;
imageHeight = screenWidth / imageSizeRatio * 0.7;
}
DomM.css.set(base.m_CurrentImageElement, {
"top": ((screenHeight - imageHeight) / 2) + "px",
"left": ((screenWidth - imageWidth) / 2) + "px",
"width": Math.floor(imageWidth) + "px",
"height": Math.floor(imageHeight) + "px",
"opacity": 1
});
};
tmpImage.src = base.m_CurrentImageElement.getAttribute("src");
},
f_RemoveImage: function () {
var base = this;
if (base.m_CurrentImageElement) {
if (base.f_isFunction(base.m_Options.quitOnImageClick)) {
base.m_CurrentImageElement.removeEventListener("click", base.f_ImageClickEvent, false);
}
base.m_CurrentImageElement.parentNode.removeChild(base.m_CurrentImageElement);
base.m_CurrentImageElement = false;
}
return false;
},
f_QuitImage: function () {
var base = this;
if (base.m_CurrentImageElement) {
setTimeout(function () {
DomM.css.set(base.m_CurrentImageElement, {
"opacity": 0,
"transition": ("opacity " + base.m_Options.fadeOutSpeed + "ms ease")
});
setTimeout(function () {
base.f_RemoveImage();
if (base.f_isFunction(base.m_Options.onImageQuit)) {
base.m_Options.onImageQuit();
}
}, base.m_Options.fadeOutSpeed);
}, 20);
}
},
f_IsValidSource: function (p_Src) {
return new RegExp().test(p_Src);
},
f_doTranslateX: function (p_Pixels) {
return {
"-webkit-transform": "translateX(" + p_Pixels + ")",
"-moz-transform": "translateX(" + p_Pixels + ")",
"-o-transform": "translateX(" + p_Pixels + ")",
"-ms-transform": "translateX(" + p_Pixels + ")",
"transform": "translateX(" + p_Pixels + ")"
};
},
f_AddTransitionSpeed: function (p_Speed) {
var base = this;
return {
"-webkit-transition": "transform " + p_Speed + "ms ease, opacity " + base.m_Options.fadeInSpeed + "ms ease",
"-moz-transition": "transform " + p_Speed + "ms ease, opacity " + base.m_Options.fadeInSpeed + "ms ease",
"-o-transition": "transform " + p_Speed + "ms ease, opacity " + base.m_Options.fadeInSpeed + "ms ease",
"transition": "transform " + p_Speed + "ms ease, opacity " + base.m_Options.fadeInSpeed + "ms ease"
};
}
};
_LightBox.options = {
selector: "[data-imagelightbox]",
boxId: "imagelightbox",
allowedTypes: "png|jpg|jpeg|gif",
quitOnImageClick: true,
quitOnDocumentClick: true,
enableKeyboard: true,
animationSpeed: 750,
fadeInSpeed: 500,
fadeOutSpeed: 200,
imageLoadStart: function () {},
imageLoadEnd: function () {},
onImageQuit: function () {},
onImageStart: function () {}
};
LightBox.init = function (p_Options) {
_LightBox.f_Initialize(p_Options);
};
})(window, document, window.LightBox = window.LightBox || {});
var activityIndicatorOn = function () {
var newE = document.createElement("div"),
newB = document.createElement("div");
newE.setAttribute("id", "imagelightbox-loading");
newE.appendChild(newB);
document.body.appendChild(newE);
},
activityIndicatorOff = function () {
var elE = document.getElementById("imagelightbox-loading");
elE.parentNode.removeChild(elE);
};
LightBox.init({
selector: "[data-simplbox='demo1']",
boxId: "simplbox"
});
LightBox.init({
selector: "[data-simplbox='demo2']",
boxId: "simplbox",
imageLoadStart: activityIndicatorOn,
imageLoadEnd: activityIndicatorOff
});
Your code is almost working. What you handled badly is the fact that you can perform several init. On each init, you overwrite some items, especially with this line :
base.m_Elements = document.querySelectorAll(base.m_Options.selector);
So base.m_Elements will only have the elements of the last init.
( Based on the name 'init' i wonder if the real use case wouldn't be to allow just one call of init... )
Quick-fix is to do one single init with :
LightBox.init({
selector: "[data-simplbox='demo1'],[data-simplbox='demo2']",
boxId: "simplbox"
});
(erase the two calls to init)
And here it works.
http://jsfiddle.net/gamealchemist/66HFU/1/
So i guess either you want to support several init, or (easier to maintain in fact), throw exception on multiple init and expect the lib user to write the right selector in the single init call.
Edit : Super quick fix for your issue : rather than having LightBox as a singleton, have it as a Class :
function LightBox( initArguments ) {
// something like what was done in init
}
LightBox.prototype = {
f_RemoveImage : function() {
} ,
f_OpenImage : function( ..., ... ) {
} ,
...
}
then you can call with no issue :
var demo1LB = new LightBox({ selector: "[data-simplbox='demo1']",
boxId: "simplbox" });
var demo2LB = new LightBox({ selector: "[data-simplbox='demo2']",
boxId: "simplbox" });
Can somebody recommend an easy to use Lightbox plug-in for ExtJS 4?
The best I can do at the momemnt is http://dev.sencha.com/playpen/ext-core-latest/examples/lightbox/. This relates to ExtJS 3 and I cannot find an equivalent in ExtJS 4.
I found adapted version for ExtJS4 here .
/*!
* Based on the lightbox implementation in Ext Core Library 3.0
* http://www.lyquidity.com/
* Copyright(c) 2012, Lyquidity Solutions Limited.
*
* MIT Licensed - http://extjs.com/license/mit.txt
*/
Ext.define('Ext.ux.Lightbox', {
extend: 'Ext.Component',
alias: 'widget.lightbox',
id: 'ux-lightbox',
/**
* #cfg {int} activeImage The index of the current image.
*/
activeImage: undefined,
/**
* #cfg {boolean} animate True if the image change transition is to be animated.
*/
animate: true,
/**
* #cfg {int} borderSize The size of the image border.
*/
borderSize: 10,
/**
* #cfg {string} labelImage The term for 'image'.
*/
labelImage: "Image",
/**
* #cfg {string} labelOf The term for 'of' as in '3 of 7'.
*/
labelOf: "of",
/**
* #cfg {int} overlayOpacity The relativ opacity of the overlay (background). Default: 0.85.
*/
overlayOpacity: 0.85,
/**
* #cfg {HtmlElement} renderTo The element to which the lightbox will be rendered. Defaults to the body.
*/
renderTo: Ext.getBody(),
/**
* #cfg {int} resizeSpeed Time in milliseconds for the lightbox to resize to a new image. Default: 600ms.
*/
resizeSpeed: 600,
/**
* #cfg {string} containerTag The tag which contains the <img> tag. Default: 'a'.
*/
containerTag: 'a',
/**
* #cfg {string} missingImage The image to use to represent a missing image. Default: missing.png.
*/
missingImage: 'missing.png',
childEls: ['outerImageContainer', 'imageContainer', 'image', 'hoverNav', 'navPrev', 'navNext',
'outerDataContainer', 'dataContainer', 'data', 'details', 'caption', 'imageNumber', 'bottomNav', 'navClose'],
initComponent: function()
{
var me = this;
// This weirdness is here because I'm simulating what happens in the Designer
// which has a bug because renderTpl is set to null by ExtJS so Ext.applyIf()
// has no effect. Setting renderTpl to undefined solves the problem.
// You can remove this by using Ext.apply() instead or by moving the renderTpl
// out of the initComponent function.
this.renderTpl = undefined;
Ext.applyIf(me, {
renderTpl: [
'<div id="ux-lightbox-outerImageContainer">',
' <div id="ux-lightbox-imageContainer">',
' <img id="ux-lightbox-image">',
' <div id="ux-lightbox-hoverNav">',
' ',
' ',
' </div>',
' </div>',
'</div>',
'<div id="ux-lightbox-outerDataContainer">',
' <div id="ux-lightbox-dataContainer">',
' <div id="ux-lightbox-data">',
' <div id="ux-lightbox-details">',
' <span id="ux-lightbox-caption"></span>',
' <span id="ux-lightbox-imageNumber"></span>',
' </div>',
' <div id="ux-lightbox-bottomNav">',
' ',
' </div>',
' </div>',
' </div>',
'</div>'
],
images: [],
selectors: [],
listeners: {
beforerender: {
fn: me.onComponentBeforeRender,
scope: me
},
afterrender: {
fn: me.onComponentAfterRender,
scope: me
}
}
});
this.resizeDuration = this.animate ? this.resizeSpeed : 0;
this.overlayDuration = this.animate ? 200 : 0;
this.addEvents('open', 'close');
this.callParent(arguments);
},
onComponentBeforeRender: function () {
var me = this;
this.shim = Ext.DomHelper.append( this.renderTo,
{
tag: 'iframe',
id: 'ux-lightbox-shim'
}, true);
this.overlay = Ext.DomHelper.append(this.renderTo, {
id: 'ux-lightbox-overlay'
}, true);
},
onComponentAfterRender: function (container, position) {
var me = this;
Ext.each([this.overlay, this.shim, this.el], function(el){
el.setVisibilityMode(Ext.Element.DISPLAY).hide();
});
this.setVisible(false).setPosition(0, 20);
var size = (this.animate ? 250 : 1);
this.outerImageContainer.setSize(size,size);
this.outerDataContainer.setWidth(size).hide();
// Initialize events
var close = function(ev)
{
ev.preventDefault();
this.close();
};
this.overlay.on('click', close, this);
this.navClose.on('click', close, this);
this.el.on('click', function(ev)
{
if(ev.getTarget().id == this.getId())
this.close();
}, this);
this.navPrev.on('click', function(ev)
{
ev.preventDefault();
this.setImage(this.activeImage - 1);
}, this);
this.navNext.on('click', function(ev)
{
ev.preventDefault();
this.setImage(this.activeImage + 1);
}, this);
},
/**
* Called by user to register the images to include in the lightbox
* #property register
* #type Function
* #param {String} The selector to find the images in 'container' to register
* #param {boolean} True if the images are a group
* #param {String} The container which holds the images to use. Default: body
*/
register: function(sel, group, container)
{
this.thumbnailContainer = container
? container
: Ext.getBody();
if (this.renderTo != Ext.getBody())
{
this.renderTo.setStyle({position:'relative'});
}
var selector = {sel: sel, group: group};
var length = Ext.Array.filter(this.selectors, function(item) { return item.sel === sel; }, this).length;
if(length === 0)
{
this.selectors.push(selector);
// This ability to use functions like 'on' against the result of Ext.fly() or Ext.select() is good
Ext.select(sel, true, this.thumbnailContainer.dom).on('click', this.click, this);
}
},
/**
* Private function to handle the image click event
* #property click
* #type Function
* #param {HTMLElement} ev The contained item pressed
*/
click: function(ev)
{
try
{
// Get the parent of the clicked element which is the container
var up = Ext.get(ev.target).up(this.containerTag);
var target = undefined;
var selector = undefined;
Ext.each(this.selectors, function(item)
{
var els = Ext.select(item.sel, true);
var i = els.indexOf(up);
if (i === -1) return;
target = ev.getTarget(item.sel);
selector = item;
}, true );
if (target)
{
ev.preventDefault();
this.open(target, selector.sel, selector.group);
}
}
catch (e)
{
console.log(e.message);
}
},
remove: function()
{
this.clear();
Ext.enableNestedListenerRemoval = true;
Ext.each([this.overlay, this.shim], function(node)
{
if (!node) return;
Ext.removeNode(node.dom);
delete node;
});
this.destroy();
},
/**
* Clears the click events on all registered images
* #property clear
* #type Function
*/
clear: function()
{
this.close();
if (!this.selectors) return;
Ext.each(this.selectors, function(item)
{
Ext.select(item.sel, true, this.thumbnailContainer.dom).un('click', this.click, this);
},this);
this.selectors = [];
this.images = [];
},
/**
* Called automatically when the page has completely loaded. This is an empty function that should be
* overridden by each application that needs to take action on page load
* #property launch
* #type Function
* #param {HTMLElement} image The element containing the item to display
* #param {String} sel The selector for the image
* #param {Boolean} group True if this image is part of a group
*/
open: function(image, sel, group)
{
// Close the image if on is already open
if (this.isVisible())
{
Ext.callback( this.close, this, [this.open,image, sel, group] );
return;
}
group = group || false;
this.setViewSize();
this.overlay.setTop(0);
this.overlay.setLeft(0);
this.overlay.show();
this.overlay.setOpacity(0);
this.overlay.fadeIn({
duration: this.overlayDuration,
opacity: this.overlayOpacity,
callback: function() {
this.images = [];
var index = 0;
if(!group) {
this.images.push([image.href, image.title]);
}
else {
var setItems = Ext.query(sel,this.thumbnailContainer);
Ext.each(setItems, function(item){
var href = item.href;
href = item.getAttribute('href');
if(href) {
this.images.push([href, item.title]);
}
}, this);
while (index < this.images.length && this.images[index][0] != image.getAttribute('href')) {
index++;
}
}
// calculate top and left offset for the lightbox
var pageScroll = Ext.fly(this.renderTo.dom).getScroll();
var size = this.getViewSize();
var lightboxLeft = pageScroll.left;
this.setHeight(size.height);
this.setWidth(size.width);
this.setVisible(true);
this.setImage(index);
this.fireEvent('open', index == this.images.length ? undefined : this.images[index]);
},
scope: this
});
},
setViewSize: function()
{
var viewSize = this.getViewSize();
this.overlay.setStyle({
width: viewSize.width + 'px',
height: viewSize.height + 'px'
});
this.shim.setStyle({
width: viewSize.width + 'px',
height: viewSize.height + 'px'
}).show();
},
setImage: function(index)
{
this.activeImage = index;
this.disableKeyNav();
if (this.animate)
this.showMask();
this.image.fadeOut({
duration: this.resizeDuration,
scope: this,
callback: function()
{
this.image.hide();
delete this.lastImage;
this.hoverNav.hide();
this.navPrev.hide();
this.navNext.hide();
this.dataContainer.setOpacity(0.0001);
this.imageNumber.hide();
var imageSrc = (this.activeImage >= this.images.length)
? this.missingImage
: this.images[this.activeImage][0];
this.preview = Ext.create('Ext.Img',
{
src: imageSrc,
hidden: true,
renderTo: Ext.getBody(),
listeners : {
load : {
element : 'el', //the rendered img element
fn : function()
{
this.image.dom.src = this.preview.src;
this.resizeImage(this.preview.el.dom.width, this.preview.el.dom.height);
Ext.removeNode(this.preview.el.dom);
delete this.preview;
},
scope: this
}
}
});
}
});
},
resizeImage: function(w, h)
{
this.hideMask();
var wCur = this.outerImageContainer.getWidth();
var hCur = this.outerImageContainer.getHeight();
// The w and h could exceed the displayable area so this
// should be checked and the w/h changed accordingly.
var guesstimateDetailsSize = 60;
var size = this.getViewSize();
while ((w+this.borderSize * 2 > size.width) || (h+this.borderSize * 2 + guesstimateDetailsSize > size.height))
{
if (w+this.borderSize * 2 > size.width)
{
// Compute the new width
var wtemp = size.width - this.borderSize * 2;
// Compute the corresponding height so the aspect ratio remains the same
var htemp = Math.abs(wtemp/w*h);
w = wtemp;
h = htemp;
}
if (h+this.borderSize * 2 + guesstimateDetailsSize > size.height)
{
// Compute the new height
var htemp = size.height - guesstimateDetailsSize - this.borderSize * 2;
// Compute the corresponding width so the aspect ratio remains the same
var wtemp = Math.abs(htemp/h*w);
w = wtemp;
h = htemp;
}
}
var wNew = (w + this.borderSize * 2);
var hNew = (h + this.borderSize * 2);
var wDiff = wCur - wNew;
var hDiff = hCur - hNew;
var afterResize = function(){
this.hoverNav.setWidth(this.imageContainer.getWidth() + 'px');
this.navPrev.setHeight(h + 'px');
this.navNext.setHeight(h + 'px');
this.outerDataContainer.setWidth(wNew + 'px');
this.outerDataContainer.show();
this.showImage(w,h);
};
if (hDiff != 0 || wDiff != 0)
{
this.outerImageContainer.animate(
{
height: hNew,
width: wNew,
duration: this.resizeDuration,
scope: this,
callback: afterResize,
delay: 50
});
}
else
{
afterResize.call(this);
}
},
hideMask: function()
{
if (Ext.getVersion().major === 4 && Ext.getVersion().minor === 0)
{
if (!this.mask) return
this.mask.hide();
}
else
this.imageContainer.unmask();
},
showMask: function()
{
if (Ext.getVersion().major === 4 && Ext.getVersion().minor === 0)
{
if (!this.mask)
{
this.mask = new Ext.LoadMask(this.imageContainer, {});
}
this.mask.show();
}
else
this.imageContainer.mask('Loading image...');
},
showImage: function(w,h)
{
// Is it already displayed?
if (this.lastImage && this.lastImage == this.image.dom.src)
return;
this.lastImage = this.image.dom.src;
this.image.setWidth(w);
this.image.setHeight(h);
this.image.setOpacity(0);
this.image.show({
duration: this.resizeDuration,
scope: this,
callback: this.updateDetails()
});
this.preloadImages();
},
updateDetails: function()
{
var detailsWidth = this.data.getWidth(true) - this.navClose.getWidth() - 10;
this.details.setWidth((detailsWidth > 0 ? detailsWidth : 0) + 'px');
this.caption.update((this.activeImage >= this.images.length) ? '' : this.images[this.activeImage][1]);
this.caption.show();
if (this.images.length > 1) {
this.imageNumber.update(this.labelImage + ' ' + (this.activeImage + 1) + ' ' + this.labelOf + ' ' + this.images.length);
this.imageNumber.show();
}
this.dataContainer.fadeIn({
duration: this.resizeDuration/2,
scope: this,
callback: function() {
var viewSize = this.getViewSize();
this.overlay.setHeight(viewSize.height + 'px');
var total = this.outerDataContainer.getHeight() + this.outerImageContainer.getHeight();
this.setHeight(total);
var position = this.getPosition(true);
var distance = Math.abs((this.getViewSize().height - total)/2) - position[1];
this.el.move("b", distance, {
duration: this.resizeDuration,
scope: this,
callback: this.updateNav()
});
}
});
},
updateNav: function()
{
this.enableKeyNav();
this.hoverNav.show();
// if not first image in set, display prev image button
if (this.activeImage > 0)
this.navPrev.show();
// if not last image in set, display next image button
if (this.activeImage < (this.images.length - 1))
this.navNext.show();
},
enableKeyNav: function()
{
Ext.fly(this.renderTo.dom).on('keydown', this.keyNavAction, this);
},
disableKeyNav: function()
{
Ext.fly(this.renderTo.dom).un('keydown', this.keyNavAction, this);
},
keyNavAction: function(ev)
{
var keyCode = ev.getKey();
if (
keyCode == 88 || // x
keyCode == 67 || // c
keyCode == 27
) {
this.close();
}
else if (keyCode == 80 || keyCode == 37){ // display previous image
if (this.activeImage != 0){
this.setImage(this.activeImage - 1);
}
}
else if (keyCode == 78 || keyCode == 39){ // display next image
if (this.activeImage != (this.images.length - 1)){
this.setImage(this.activeImage + 1);
}
}
},
preloadImages: function()
{
var next, prev;
if (this.images.length > this.activeImage + 1)
{
next = new Image();
next.src = this.images[this.activeImage + 1][0];
}
if (this.activeImage > 0)
{
prev = new Image();
prev.src = this.images[this.activeImage - 1][0];
}
},
close: function(callback,image, sel, group)
{
if (!this.isVisible()) return;
this.hideMask();
this.disableKeyNav();
this.setVisible(false);
this.shim.hide();
this.overlay.fadeOut({
duration: this.overlayDuration,
stopAnimation: true,
scope: this,
callback: function()
{
if (this.overlay)
this.overlay.hide();
if (callback)
Ext.callback(callback, this, [image, sel, group], 1);
}
});
this.fireEvent('close', this.activeImage);
},
getViewSize: function()
{
return {width: this.renderTo.getWidth(), height: this.renderTo.getHeight()};
}
});
I'm not able to leave a comment, but some people on the Sencha forums have already tried to build off the example you linked to: http://www.sencha.com/forum/showthread.php?138296-Ext.ux.Lightbox
I think your best bet is to build off their existing work to adapt it for Ext 4.