Related
I am using Leaflet and it works well. I am using leaflet.label as well and that works nicely as well. The problem is that I would like to display two labels to the right of the marker. If I call bindLabel twice, then the second overrides the first. How could I make sure that I have two labels, both to the right of the marker and the second label is above the first one?
This is how I tried:
newMarker.bindLabel(result, { noHide: true }).bindLabel("secondlabel", { noHide: true });
Thanks
EDIT:
I have managed to display the texts using a single call to bindLabel, like this:
newMarker.bindLabel(result + "<br>secondLabel", { noHide: true });
but this seems to be a too hacky solution.
Here they say it is not possible, but that was written in 2014. It might be possible since then.
I have managed to solve it. I am lazy to push it to their repo right now, but I will probably do it in the future. The logic of the solution is as follows:
this.label -> this.labels
this.labels is an array of LeafletLabels
apply the change for methods containing this.label
move this._labelNoHide = options.noHide into the if to prevent bugs
The labels will act similarly for the subset of options which is handled singularly / marker. Sorry, folks, singularizing noHide or opacity to label level instead of marker level is beyond the scope of this question. I might resolve those later though.
The code is as follows:
/*
Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps.
(c) 2012-2013, Jacob Toye, Smartrak
https://github.com/Leaflet/Leaflet.label
http://leafletjs.com
https://github.com/jacobtoye
*/
(function (factory, window) {
// define an AMD module that relies on 'leaflet'
if (typeof define === 'function' && define.amd) {
define(['leaflet'], factory);
// define a Common JS module that relies on 'leaflet'
} else if (typeof exports === 'object') {
module.exports = factory(require('leaflet'));
}
// attach your plugin to the global 'L' variable
if (typeof window !== 'undefined' && window.L) {
window.LeafletLabel = factory(L);
}
}(function (L) {
L.labelVersion = '0.2.4';
var LeafletLabel = L.Class.extend({
includes: L.Mixin.Events,
options: {
className: '',
clickable: false,
direction: 'right',
noHide: false,
offset: [12, -15], // 6 (width of the label triangle) + 6 (padding)
opacity: 1,
zoomAnimation: true
},
initialize: function (options, source) {
L.setOptions(this, options);
this._source = source;
this._animated = L.Browser.any3d && this.options.zoomAnimation;
this._isOpen = false;
},
onAdd: function (map) {
this._map = map;
this._pane = this.options.pane ? map._panes[this.options.pane] :
this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;
if (!this._container) {
this._initLayout();
}
this._pane.appendChild(this._container);
this._initInteraction();
this._update();
this.setOpacity(this.options.opacity);
map
.on('moveend', this._onMoveEnd, this)
.on('viewreset', this._onViewReset, this);
if (this._animated) {
map.on('zoomanim', this._zoomAnimation, this);
}
if (L.Browser.touch && !this.options.noHide) {
L.DomEvent.on(this._container, 'click', this.close, this);
map.on('click', this.close, this);
}
},
onRemove: function (map) {
this._pane.removeChild(this._container);
map.off({
zoomanim: this._zoomAnimation,
moveend: this._onMoveEnd,
viewreset: this._onViewReset
}, this);
this._removeInteraction();
this._map = null;
},
setLatLng: function (latlng) {
this._latlng = L.latLng(latlng);
if (this._map) {
this._updatePosition();
}
return this;
},
setContent: function (content) {
// Backup previous content and store new content
this._previousContent = this._content;
this._content = content;
this._updateContent();
return this;
},
close: function () {
var map = this._map;
if (map) {
if (L.Browser.touch && !this.options.noHide) {
L.DomEvent.off(this._container, 'click', this.close);
map.off('click', this.close, this);
}
map.removeLayer(this);
}
},
updateZIndex: function (zIndex) {
this._zIndex = zIndex;
if (this._container && this._zIndex) {
this._container.style.zIndex = zIndex;
}
},
setOpacity: function (opacity) {
this.options.opacity = opacity;
if (this._container) {
L.DomUtil.setOpacity(this._container, opacity);
}
},
_initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
this.updateZIndex(this._zIndex);
},
_update: function () {
if (!this._map) { return; }
this._container.style.visibility = 'hidden';
this._updateContent();
this._updatePosition();
this._container.style.visibility = '';
},
_updateContent: function () {
if (!this._content || !this._map || this._prevContent === this._content) {
return;
}
if (typeof this._content === 'string') {
this._container.innerHTML = this._content;
this._prevContent = this._content;
this._labelWidth = this._container.offsetWidth;
}
},
_updatePosition: function () {
var pos = this._map.latLngToLayerPoint(this._latlng);
this._setPosition(pos);
},
_setPosition: function (pos) {
var map = this._map,
container = this._container,
centerPoint = map.latLngToContainerPoint(map.getCenter()),
labelPoint = map.layerPointToContainerPoint(pos),
direction = this.options.direction,
labelWidth = this._labelWidth,
offset = L.point(this.options.offset);
// position to the right (right or auto & needs to)
if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) {
L.DomUtil.addClass(container, 'leaflet-label-right');
L.DomUtil.removeClass(container, 'leaflet-label-left');
pos = pos.add(offset);
} else { // position to the left
L.DomUtil.addClass(container, 'leaflet-label-left');
L.DomUtil.removeClass(container, 'leaflet-label-right');
pos = pos.add(L.point(-offset.x - labelWidth, offset.y));
}
L.DomUtil.setPosition(container, pos);
},
_zoomAnimation: function (opt) {
var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
this._setPosition(pos);
},
_onMoveEnd: function () {
if (!this._animated || this.options.direction === 'auto') {
this._updatePosition();
}
},
_onViewReset: function (e) {
/* if map resets hard, we must update the label */
if (e && e.hard) {
this._update();
}
},
_initInteraction: function () {
if (!this.options.clickable) { return; }
var container = this._container,
events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
L.DomUtil.addClass(container, 'leaflet-clickable');
L.DomEvent.on(container, 'click', this._onMouseClick, this);
for (var i = 0; i < events.length; i++) {
L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
}
},
_removeInteraction: function () {
if (!this.options.clickable) { return; }
var container = this._container,
events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
L.DomUtil.removeClass(container, 'leaflet-clickable');
L.DomEvent.off(container, 'click', this._onMouseClick, this);
for (var i = 0; i < events.length; i++) {
L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
}
},
_onMouseClick: function (e) {
if (this.hasEventListeners(e.type)) {
L.DomEvent.stopPropagation(e);
}
this.fire(e.type, {
originalEvent: e
});
},
_fireMouseEvent: function (e) {
this.fire(e.type, {
originalEvent: e
});
// TODO proper custom event propagation
// this line will always be called if marker is in a FeatureGroup
if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
L.DomEvent.preventDefault(e);
}
if (e.type !== 'mousedown') {
L.DomEvent.stopPropagation(e);
} else {
L.DomEvent.preventDefault(e);
}
}
});
/*global LeafletLabel */
// This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents.
L.BaseMarkerMethods = {
showLabel: function () {
if (this.labels && this._map) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].setLatLng(this._latlng);
this._map.showLabel(this.labels[labelIndex]);
}
}
return this;
},
hideLabel: function () {
if (this.labels) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].close();
}
}
return this;
},
setLabelNoHide: function (noHide) {
if (this._labelNoHide === noHide) {
return;
}
this._labelNoHide = noHide;
if (noHide) {
this._removeLabelRevealHandlers();
this.showLabel();
} else {
this._addLabelRevealHandlers();
this.hideLabel();
}
},
bindLabel: function (content, options) {
var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor,
anchor = L.point(labelAnchor) || L.point(0, 0);
anchor = anchor.add(LeafletLabel.prototype.options.offset);
if (options && options.offset) {
anchor = anchor.add(options.offset);
}
options = L.Util.extend({ offset: anchor }, options);
if (!this.labels) {
this._labelNoHide = options.noHide;
this.labels = [];
if (!this._labelNoHide) {
this._addLabelRevealHandlers();
}
this
.on('remove', this.hideLabel, this)
.on('move', this._moveLabel, this)
.on('add', this._onMarkerAdd, this);
this._hasLabelHandlers = true;
}
this.labels.push(new LeafletLabel(options, this).setContent(content));
return this;
},
unbindLabel: function () {
if (this.labels) {
this.hideLabel();
this.labels = null;
if (this._hasLabelHandlers) {
if (!this._labelNoHide) {
this._removeLabelRevealHandlers();
}
this
.off('remove', this.hideLabel, this)
.off('move', this._moveLabel, this)
.off('add', this._onMarkerAdd, this);
}
this._hasLabelHandlers = false;
}
return this;
},
updateLabelContent: function (content, index) {
if ((this.labels) && (index < this.labels.length)) {
this.labels[index].setContent(content);
}
},
getLabels: function () {
return this.labels;
},
_onMarkerAdd: function () {
if (this._labelNoHide) {
this.showLabel();
}
},
_addLabelRevealHandlers: function () {
this
.on('mouseover', this.showLabel, this)
.on('mouseout', this.hideLabel, this);
if (L.Browser.touch) {
this.on('click', this.showLabel, this);
}
},
_removeLabelRevealHandlers: function () {
this
.off('mouseover', this.showLabel, this)
.off('mouseout', this.hideLabel, this);
if (L.Browser.touch) {
this.off('click', this.showLabel, this);
}
},
_moveLabel: function (e) {
this.label.setLatLng(e.latlng);
}
};
// Add in an option to icon that is used to set where the label anchor is
L.Icon.Default.mergeOptions({
labelAnchor: new L.Point(9, -20)
});
// Have to do this since Leaflet is loaded before this plugin and initializes
// L.Marker.options.icon therefore missing our mixin above.
L.Marker.mergeOptions({
icon: new L.Icon.Default()
});
L.Marker.include(L.BaseMarkerMethods);
L.Marker.include({
_originalUpdateZIndex: L.Marker.prototype._updateZIndex,
_updateZIndex: function (offset) {
var zIndex = this._zIndex + offset;
this._originalUpdateZIndex(offset);
if (this.labels) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].updateZIndex(zIndex);
}
}
},
_originalSetOpacity: L.Marker.prototype.setOpacity,
setOpacity: function (opacity, labelHasSemiTransparency) {
this.options.labelHasSemiTransparency = labelHasSemiTransparency;
this._originalSetOpacity(opacity);
},
_originalUpdateOpacity: L.Marker.prototype._updateOpacity,
_updateOpacity: function () {
var absoluteOpacity = this.options.opacity === 0 ? 0 : 1;
this._originalUpdateOpacity();
if (this.labels) {
for (var labelIndex in labels) {
this.labels[labelIndex].setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity);
}
}
},
_originalSetLatLng: L.Marker.prototype.setLatLng,
setLatLng: function (latlng) {
if (this.labels && !this._labelNoHide) {
this.hideLabel();
}
return this._originalSetLatLng(latlng);
}
});
// Add in an option to icon that is used to set where the label anchor is
L.CircleMarker.mergeOptions({
labelAnchor: new L.Point(0, 0)
});
L.CircleMarker.include(L.BaseMarkerMethods);
/*global LeafletLabel */
L.Path.include({
bindLabel: function (content, options) {
if (!this.label || this.label.options !== options) {
this.label = new LeafletLabel(options, this);
}
this.label.setContent(content);
if (!this._showLabelAdded) {
this
.on('mouseover', this._showLabel, this)
.on('mousemove', this._moveLabel, this)
.on('mouseout remove', this._hideLabel, this);
if (L.Browser.touch) {
this.on('click', this._showLabel, this);
}
this._showLabelAdded = true;
}
return this;
},
unbindLabel: function () {
if (this.label) {
this._hideLabel();
this.label = null;
this._showLabelAdded = false;
this
.off('mouseover', this._showLabel, this)
.off('mousemove', this._moveLabel, this)
.off('mouseout remove', this._hideLabel, this);
}
return this;
},
updateLabelContent: function (content) {
if (this.label) {
this.label.setContent(content);
}
},
_showLabel: function (e) {
this.label.setLatLng(e.latlng);
this._map.showLabel(this.label);
},
_moveLabel: function (e) {
this.label.setLatLng(e.latlng);
},
_hideLabel: function () {
this.label.close();
}
});
L.Map.include({
showLabel: function (label) {
return this.addLayer(label);
}
});
L.FeatureGroup.include({
// TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer()
clearLayers: function () {
this.unbindLabel();
this.eachLayer(this.removeLayer, this);
return this;
},
bindLabel: function (content, options) {
return this.invoke('bindLabel', content, options);
},
unbindLabel: function () {
return this.invoke('unbindLabel');
},
updateLabelContent: function (content) {
this.invoke('updateLabelContent', content);
}
});
return LeafletLabel;
}, window));
This is a simple script for horizontal fortune roulette, created using slick slider. Problem: function rollout() executed twice (and all helpers too) because template rendered twice. I'm using iron:router for routing, code:
Router.configure({
loadingTemplate: "loading",
layoutTemplate: "layout"
});
Router.map(function() {
this.route("hello", {
path: "/",
waitOn: function() {
return Meteor.subscribe("games");
}
});
});
hello.js:
Session.setDefault('currentSlide', 0);
Template.hello.events({
'click .create': function(e) {
e.preventDefault();
var ins = {
gameStartTime: new Date(),
finished: false
}
Games.insert({
gameStartTime: new Date(),
finished: "false"
});
Session.set("gameStatus", "waiting");
}
})
Template.hello.helpers({
currentSlide: function() {
var value = Session.get('currentSlide');
if (value === 25) {
return 0;
}
return value;
},
games: function() {
var game = Games.find();
return game;
},
gameHandler: function() {
var game = Games.findOne();
var gameStatus = Session.get("gameStatus");
if (game && game.finished === "false" && gameStatus === "waiting") {
Games.update({_id: game._id}, {
$set: {running: true}
});
} else if (gameStatus === "finished") {
Meteor.call('updateGame', game._id); //remove item from collection after game over; Error here: TypeError: Cannot read property '_id' of undefined;
console.log('game finished');
}
if (game && game.running === true) {
//Set Session variable and launch roulette
Session.set("gameStatus", "running");
rollout(game._id);
}
}
});
var rollout = function(gameid) {
Session.set("gameStatus", "running");
//make div's visible. They are have display:none by default.
document.getElementById("roulette").style.display = "block";
document.getElementById("result").style.display = "block";
document.getElementById("wrapper").style.display = "none";
var speed = 500; //default slider speed
Session.setDefault('speed', speed);
var r = $('#roulette').slick({
centerMode: true,
slidesToShow: 7,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 0,
speed: speed,
draggable: false,
pauseOnHover: false,
cssEase:'linear'
});
var maximum = 14;
var minimum = 0;
var randomnumber = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
var winResult = randomnumber;
var speed = 80;
var currentSlide = Session.get('currentSlide');
function roll(callback) {
Session.set('currentSlide', r.slickCurrentSlide());
if (speed < 800) {
speed += 20;
r.slickSetOption("speed", speed, false);
setTimeout(function() {
roll(callback);
}, 500);
} else if (speed >= 600 && speed < 1300) {
speed += 40;
r.slickSetOption("speed", speed, false);
setTimeout(function() {
roll(callback);
}, 300);
} else if (speed >= 1300 && speed < 20000) {
setTimeout(function() {
speed += 3;
r.slickSetOption('speed', speed, false);
if (Session.get('currentSlide') === winResult) {
r.slickPause();
setTimeout(function() {
//hide roulette div's
document.getElementById("roulette").style.display = "none";
document.getElementById("result").style.display = "none";
document.getElementById("wrapper").style.display = "block";
r.unslick();
Session.set('currentSlide', 0);
Session.set("gameStatus", "finished");
callback(true);
return
}, 10000);
} else {
roll(callback);
}
}, 30)
} else {
callback(true);
}
}
roll(function(callback) {
Meteor.call('updateGame', gameid); //remove item from collection
Session.set("gameStatus", "finished");
});
}
In template i'm using spinner by sacha:spin package and it working fine.
The gameHandler function should not be in your template helpers, which are only meant to supply information to the template. Instead, that logic needs to be moved to an autorun function. This autorun statement will re-run anytime a reactive datasource within it changes.
ex:
Template.hello.onCreated(function() {
this.autorun(function() {
var game = Games.findOne();
var gameStatus = Session.get("gameStatus");
// ...etc
});
}
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" });
For some reason my gallery isn't working on Mobile devices including iPad, works fine on desktop. Instead of allowing a user to click through, all images appear stacked. The link to my site. The code is
located here
// scroll gallery init
function initCarousel() {
var isTouchDevice = /MSIE 10.*Touch/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
jQuery('div.view-gallery').scrollGallery({
mask: 'div.frame',
slider: '>ul',
slides: '>li',
btnPrev: 'a.btn-prev',
btnNext: 'a.btn-next',
pagerLinks: '.pagination li',
circularRotation: false,
autoRotation: false,
switchTime: 3000,
animSpeed: 500,
onInit: function(obj){
obj.resizeFlag = true;
obj.win = jQuery(window);
//obj.win.unbind('resize orientationchange load', obj.onWindowResize);
obj.resizeSlides = function(){
obj.slideOffset = obj.slides.eq(0).outerWidth(true) - obj.slides.eq(0).width();
if(!obj.resizeFlag) obj.slides.css({width: ''});
else obj.slides.css({width: obj.mask.width()/2 - obj.slideOffset});
obj.calculateOffsets();
obj.refreshPosition();
obj.refreshState();
}
if(isTouchDevice){
ResponsiveHelper.addRange({
'..767': {
on: function(){
setTimeout(function(){
obj.resizeFlag = true;
obj.resizeSlides();
obj.win.bind('resize orientationchange load', obj.resizeSlides);
}, 100);
}
},
'768..': {
on: function(){
obj.resizeFlag = false;
obj.win.unbind('resize orientationchange load', obj.resizeSlides);
obj.resizeSlides();
}
}
});
}
}
});
jQuery('.scrollable-gallery').scrollableGallery();
}
/*
* scrollableGallery
*/
;(function($) {
function ScrollableGallery(options) {
this.options = {
scrollableArea: '.frame',
listItems: '.list-items',
btnPrev: '.btn-prev',
btnNext: '.btn-next',
animSpeed: 500
}
$.extend(this.options, options);
this.init();
}
ScrollableGallery.prototype = {
init: function() {
this.findElements()
this.setStructure();
this.addEvents();
},
findElements: function() {
this.holder = $(this.options.holder);
this.scrollableArea = this.holder.find(this.options.scrollableArea);
this.listItems = this.scrollableArea.find(this.options.listItems);
this.items = this.listItems.children();
this.lastItem = this.items.last();
this.btnPrev = this.holder.find(this.options.btnPrev);
this.btnNext = this.holder.find(this.options.btnNext);
this.scrollAPI = new jcf.modules.customscroll({
replaces: this.scrollableArea[0]
});
},
setStructure: function() {
var that = this;
if (that.listItems.css('position') === 'static') {
that.listItems.css('position', 'relative');
}
setTimeout(function() {
that.refreshState();
}, 50);
},
refreshState: function() {
this.listItems.css('width', 32700);
this.listItems.css('width', this.lastItem.position().left + this.lastItem.outerWidth(true) + 1);
this.scrollableArea.add(this.scrollableArea.parent()).css({
width: '',
height: ''
});
this.scrollAPI.refreshState();
},
addEvents: function() {
var that = this;
that.btnPrev.bind('click', function(e) {
e.preventDefault();
that.prevSlide();
});
that.btnNext.bind('click', function(e) {
e.preventDefault();
that.nextSlide();
});
win.bind('resize orientationchange load', function() {
that.refreshState();
});
},
nextSlide: function() {
var that = this;
var curPos = this.scrollableArea.scrollLeft();
var pos;
for (var i = 0; i < that.items.length; i++) {
pos = that.items.eq(i).position().left;
if (pos > curPos) {
that.scrollAnimate(curPos, pos);
break;
}
}
},
prevSlide: function() {
var that = this;
var curPos = this.scrollableArea.scrollLeft();
var pos;
for (var i = that.items.length - 1; i >= 0; i--) {
pos = that.items.eq(i).position().left;
if (pos < curPos) {
that.scrollAnimate(curPos, pos);
break;
}
}
},
scrollAnimate: function(from, to) {
var that = this;
var start = new Date().getTime();
setTimeout(function() {
var now = (new Date().getTime()) - start;
var progress = now / that.options.animSpeed;
var result = (to - from) * progress + from;
that.scrollAPI.hScrollBar.scrollTo(result);
if (progress < 1) {
setTimeout(arguments.callee, 10);
} else {
that.scrollAPI.hScrollBar.scrollTo(to);
}
}, 10);
}
}
var win = $(window);
$.fn.scrollableGallery = function(options) {
return this.each(function() {
if (!$(this).data('ScrollableGallery')) {
$(this).data('ScrollableGallery', new ScrollableGallery($.extend({}, {holder: this}, options)));
}
});
}
}(jQuery));
After looking through your code, there were numerous errors with syntax. I have cleaned them up as best as I could, this should help you out.
http://jsfiddle.net/wvWrY/1/
For example, this area was missing a semicolon (no way to call the findElements function, as JS will simply skip to the next line without a semicolon there.)
init: function() {
this.findElements()
this.setStructure();
this.addEvents();
Run your code through a linter, it will greatly improve your syntax structure and ensure little leave out errors like semicolons and commas and brackets aren't omitted.
EDIT: Ok, having looked at your code it appears this is actually due to the !importants in your allmobile.css file. The width and height are set to max-width: 100% (this breaks it because the way the slider works is to extend the gallery as far off screen as possible) and the height to auto (this breaks it because it allows the images to just keep piling on). Once you remove those for the page, it become much much much better and actually works.
I hope that somebody can help me.
I want to redeclare js function by extension.
For example, there is the basic js function on website:
function foo(){
..something here..
}
i want to redeclare it by own function with the same name. how it will be easiest to do?
edit 1. i'll try to explain better.
there is a native code in website:
Notifier = {
debug: false,
init: function (options) {
curNotifier = extend({
q_events: [],
q_shown: [],
q_closed: [],
q_max: 3,
q_idle_max: 5,
done_events: {},
addQueues: curNotifier.addQueues || {},
recvClbks: curNotifier.recvClbks || {},
error_timeout: 1,
sound: new Sound('mp3/bb1'),
sound_im: new Sound('mp3/bb2')
}, options);
if (!this.initFrameTransport() && !this.initFlashTransport(options)) {
return false;
}
this.initIdleMan();
if (!(curNotifier.cont = ge('notifiers_wrap'))) {
bodyNode.insertBefore(curNotifier.cont = ce('div', {id: 'notifiers_wrap', className: 'fixed'}), ge('page_wrap'));
}
},
destroy: function () {
Notifier.hideAllEvents();
curNotifier.idle_manager.stop();
curNotifier = {};
re('notifiers_wrap');
re('queue_transport_wrap');
},
reinit: function () {
ajax.post('notifier.php?act=a_get_params', {}, {
onDone: function (options) {
if (options) {
curNotifier.error_timeout = 1;
this.init(options);
} else {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
}
}.bind(this),
onFail: function () {
curNotifier.error_timeout = curNotifier.error_timeout || 1;
setTimeout(this.reinit.bind(this), curNotifier.error_timeout * 1000);
if (curNotifier.error_timeout < 256) {
curNotifier.error_timeout *= 2;
}
return true;
}.bind(this)
});
}
}
and function Sound
function Sound(filename) {
var audioObjSupport = false, audioTagSupport = false, self = this, ext;
if (!filename) throw 'Undefined filename';
try {
var audioObj = ce('audio');
audioObjSupport = !!(audioObj.canPlayType);
if (('no' != audioObj.canPlayType('audio/mpeg')) && ('' != audioObj.canPlayType('audio/mpeg')))
ext = '.mp3?1';
else if (('no' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')) && ('' != audioObj.canPlayType('audio/ogg; codecs="vorbis"')))
ext = '.ogg?1';
else
audioObjSupport = false;
} catch (e) {}
// audioObjSupport = false;
if (audioObjSupport) {
audioObj.src = filename + ext;
var ended = false;
audioObj.addEventListener('ended', function(){ended = true;}, true);
audioObj.load();
this.playSound = function() {
if (ended) {
audioObj.load();
}
audioObj.play();
ended = false;
};
this.pauseSound = function() {
audioObj.pause();
};
} else {
cur.__sound_guid = cur.__sound_guid || 0;
var wrap = ge('flash_sounds_wrap') || utilsNode.appendChild(ce('span', {id: 'flash_sounds_wrap'})),
guid = 'flash_sound_' + (cur.__sound_guid++);
var opts = {
url: '/swf/audio_lite.swf?4',
id: guid
}
var params = {
swliveconnect: 'true',
allowscriptaccess: 'always',
wmode: 'opaque'
}
if (renderFlash(wrap, opts, params, {})) {
var swfObj = browser.msie ? window[guid] : document[guid],
inited = false,
checkLoadInt = setInterval(function () {
if (swfObj && swfObj.paused) {
try {
swfObj.setVolume(1);
swfObj.loadAudio(filename + ext);
swfObj.pauseAudio();
} catch (e) {debugLog(e);}
}
inited = true;
clearInterval(checkLoadInt);
}, 300);
self.playSound = function() {
if (!inited) return;
swfObj.playAudio(0);
};
self.pauseSound = function() {
if (!inited) return;
swfObj.pauseAudio();
};
}
}
}
Sound.prototype = {
play: function() {
try {this.playSound();} catch(e){}
},
pause: function() {
try {this.pauseSound();} catch(e){}
}
};
when i try to add injection with redeclaration function Sound it doesn't work.
if i create my own function, for example, xSound and сall it this way:
cur.sound = new xSound('mp3/bb1');
it's working.
You can do it like this, for example:
foo = function(args) {
// method body...
}
JavaScript is a programming language where functions are first-class citizens so you can manipulate them like other types.
UPDATE:
Make sure that this piece of code actually does the redefinition and not the first definition. (thanks to #jmort253)
function foo(){
// ..something else here..
}
Remember that an extension's Content Script code and the webpage code run in different execution contexts.
So if you want to redefine a function that exists in the webpage context, you'll have to inject your code into the webpage. Take a look at this answer by Rob W for different methods of doing that:
Insert code into the page context using a content script