jQuery plugin activation bug - javascript

I'm having some trouble identifying a bug on the website I'm developing. To be more specific, I'm using jPages twice on the same page.
The first instance of the plugin is used as a navigation through the website as it is a one page website. A
nd the second instance is used to browse through a bunch of products rather than scrolling.
You can find the website I'm building here : .
I'll also paste all the JavaScript, because I have no idea for now where the bug is and why is behaving like that :
$(document).ready(function() {
var default_cluster_options = {
environment : "Development",
local_storage_key : "Cluster",
plugin_navigation_class : ".navigation",
plugin_wrapper_id : "content-wrapper",
headings : ['.heading-first h1', '.heading-second h1'],
input_types : ['input', 'textarea'],
info_iqns_class : ".iqn",
preview_iqn_class : ".preview",
limits : [ { min: 1224, items: 8 }, { min: 954, items: 6 }, { min: 624, items: 4 }, { min: 0, items: 2 } ],
shop_local_storage_key : "Shop",
};
var default_plugin_options = {
containerID : "",
first : false,
previous : false,
next : false,
last : false,
startPage : 1,
perPage : 1,
midRange : 6,
startRange : 1,
endRange : 1,
keyBrowse : false,
scrollBrowse: false,
pause : 0,
clickStop : true,
delay : 50,
direction : "auto",
animation : "fadeIn",
links : "title",
fallback : 1000,
minHeight : true,
callback : function(pages, items) {}
};
var Cluster = function(cluster_options, plugin_options) {
var self = this;
this.options = $.extend({}, default_cluster_options, cluster_options);
this.plugin_options = $.extend({}, default_plugin_options, plugin_options);
this.environment = this.options.environment;
this.data_key = this.options.local_storage_key;
this.shop_data_key = this.options.shop_local_storage_key;
this.plugin_navigation_class = this.options.plugin_navigation_class;
this.plugin_wrapper_id = this.options.plugin_wrapper_id;
this.headings = this.options.headings;
this.input_types = this.options.input_types;
this.viewport = $(window);
this.body = $('body');
this.viewport_width = this.viewport.width();
this.viewport_height = this.viewport.height();
this.info_iqns_class = this.body.find(this.options.info_iqns_class);
this.preview_iqn_class = this.body.find(this.options.preview_iqn_class);
this.limits = this.options.limits;
this.current_shop_page = this.options.current_shop_page;
this.total_shop_pages = this.options.total_shop_pages;
this.initiate_cluster(self.plugin_navigation_class, {
containerID : self.plugin_wrapper_id,
startPage : +(self.get_local_storage_data(self.data_key) || 1),
callback : function(pages){
self.set_local_storage_data(self.data_key, pages.current);
}
});
this.inititate_shop();
this.initiate_shop_touch_events();
};
Cluster.prototype.set_environment = function() {
if(this.environment == "Development") {
less.env = "development";
less.watch();
}
};
Cluster.prototype.set_local_storage_data = function(data_key, data_val) {
return localStorage.setItem(data_key, data_val);
};
Cluster.prototype.get_local_storage_data = function(data_key) {
return localStorage.getItem(data_key);
};
Cluster.prototype.initiate_scalable_text = function() {
for(var i in this.headings) {
$(this.headings[i]).fitText(1.6);
}
};
Cluster.prototype.initiate_placeholder_support = function() {
for(var i in this.input_types) {
$(this.input_types[i]).placeholder();
}
};
Cluster.prototype.initiate_iqn_selected_class = function() {
if(this.viewport_width < 980) {
$(this.info_iqns_class).each(function(index, element) {
var iqn = $(element).parent();
$(iqn).on('click', function() {
if($(iqn).hasClass('selected')) {
$(iqn).removeClass('selected');
} else {
$(iqn).addClass('selected');
}
});
});
}
};
Cluster.prototype.initiate_preview_action = function() {
$(this.preview_iqn_class).each(function(index, element) {
var data = $(element).attr('data-image-link');
$(element).on('click', function(ev) {
$.lightbox(data, {
'modal' : true,
'autoresize' : true
});
ev.preventDefault();
});
});
};
Cluster.prototype.initiate_plugin = function(plugin_navigation, plugin_options) {
var options = $.extend({}, this.plugin_options, plugin_options);
return $(plugin_navigation).jPages(options);
};
Cluster.prototype.initiate_shop_touch_events = function() {
var self = this;
return $("#shop-items-wrapper").hammer({prevent_default: true, drag_min_distance: Math.round(this.viewport_width * 0.1)}).bind("drag", function(ev) {
var data = JSON.parse(self.get_local_storage_data(self.shop_data_key));
if (ev.direction == "left") {
var next_page = parseInt(data.current_page + 1);
if(next_page > 0 && next_page <= data.total_pages) {
$(".shop-items-navigation").jPages(next_page);
}
}
if(ev.direction == "right") {
var prev_page = parseInt(data.current_page - 1);
if(prev_page > 0 && prev_page <= data.total_pages) {
$(".shop-items-navigation").jPages(prev_page);
}
}
});
}
Cluster.prototype.inititate_shop = function() {
var self = this;
for(var i = 0; i < this.limits.length; i++) {
if(this.viewport_width >= this.limits[i].min) {
this.initiate_plugin('.shop-items-navigation', {
containerID : "shop-items-wrapper",
perPage : self.limits[i].items,
midRange : 8,
animation : "fadeIn",
links : "blank",
keyBrowse : true,
callback : function(pages) {
var data = {
current_page : pages.current,
total_pages : pages.count
}
self.set_local_storage_data(self.shop_data_key, JSON.stringify(data));
}
});
return false;
}
}
};
Cluster.prototype.initiate_cluster = function(plugin_navigation, plugin_options) {
this.set_environment();
this.initiate_scalable_text();
this.initiate_placeholder_support();
this.initiate_iqn_selected_class();
this.initiate_preview_action();
this.initiate_plugin(plugin_navigation, plugin_options);
};
var cluster = new Cluster();
});
And the bug I was talking about, when you are on the Home page and navigate to the Shop page you will notice the the second instance of the plugin doesn't activate as the items should only be 8 ( if the width of the screen is more than 1224px ) and you should be able to browse through with the keyboard left and right arrows, but you cannot.
But if you are on the Shop page, hit refresh and the plugin will now activate after page load.
So, I would like some help with that, tracking the bug, because I'm still learning JavaScript and I'm not very experienced with it.

According to jPages source file this happens because at second plugin initialization plugin can't find :visible elements as they are hidden by first plugin initialization (line 60):
this._items = this._container.children(":visible");
To load your shop module with jPages plugin you need to initialize that plugin after shop items are shown. To do this you need to modify callback value in initiate_cluster function:
Lets say that Shop page index is 4:
Cluster.prototype.initiate_cluster = function(plugin_navigation, plugin_options) {
// ... your code
plugin_options.callback = function( pages ) {
if( pages.current == 4 ) {
this.inititate_shop();
}
};
this.initiate_plugin(plugin_navigation, plugin_options);
};
And remove this.inititate_shop(); function call from Cluster class constructor.
This should work.
Or you can try to swap plugin calls, but I'm not sure:
// first we initiate shop
this.inititate_shop();
// then main site navigation
this.initiate_cluster(self.plugin_navigation_class, {
containerID : self.plugin_wrapper_id,
startPage : +(self.get_local_storage_data(self.data_key) || 1),
callback : function(pages){
self.set_local_storage_data(self.data_key, pages.current);
}
});

Related

How to convert a javascript function to angular js?

I use a plugin that use this block of statements to display menus,Now I use it in angular js.
(function(window) {
'use strict';
var support = { animations : Modernizr.cssanimations },
animEndEventNames = { 'WebkitAnimation' : 'webkitAnimationEnd', 'OAnimation' : 'oAnimationEnd', 'msAnimation' : 'MSAnimationEnd', 'animation' : 'animationend' },
animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ],
onEndAnimation = function( el, callback ) {
var onEndCallbackFn = function( ev ) {
if( support.animations ) {
if( ev.target != this ) return;
this.removeEventListener( animEndEventName, onEndCallbackFn );
}
if( callback && typeof callback === 'function' ) { callback.call(); }
};
if( support.animations ) {
el.addEventListener( animEndEventName, onEndCallbackFn );
}
else {
onEndCallbackFn();
}
};
function extend( a, b ) {
for( var key in b ) {
if( b.hasOwnProperty( key ) ) {
a[key] = b[key];
}
}
return a;
}
function MLMenu(el, options) {
this.el = el;
this.options = extend( {}, this.options );
extend( this.options, options );
// the menus (<ul>´s)
this.menus = [].slice.call(this.el.querySelectorAll('.menu__level'));
// index of current menu
this.current = 0;
this._init();
}
MLMenu.prototype.options = {
// show breadcrumbs
breadcrumbsCtrl : true,
// initial breadcrumb text
initialBreadcrumb : 'all',
// show back button
backCtrl : true,
// delay between each menu item sliding animation
itemsDelayInterval : 60,
// direction
direction : 'r2l',
// callback: item that doesn´t have a submenu gets clicked
// onItemClick([event], [inner HTML of the clicked item])
onItemClick : function(ev, itemName) { return false; }
};
MLMenu.prototype._init = function() {
// iterate the existing menus and create an array of menus, more specifically an array of objects where each one holds the info of each menu element and its menu items
this.menusArr = [];
var self = this;
this.menus.forEach(function(menuEl, pos) {
var menu = {menuEl : menuEl, menuItems : [].slice.call(menuEl.querySelectorAll('.menu__item'))};
self.menusArr.push(menu);
// set current menu class
if( pos === self.current ) {
classie.add(menuEl, 'menu__level--current');
}
});
// create back button
if( this.options.backCtrl ) {
this.backCtrl = document.createElement('button');
this.backCtrl.className = 'menu__back menu__back--hidden';
this.backCtrl.setAttribute('aria-label', 'Go back');
this.backCtrl.innerHTML = '<span class="icon icon--arrow-left"></span>';
this.el.insertBefore(this.backCtrl, this.el.firstChild);
}
// create breadcrumbs
if( self.options.breadcrumbsCtrl ) {
this.breadcrumbsCtrl = document.createElement('nav');
this.breadcrumbsCtrl.className = 'menu__breadcrumbs';
this.el.insertBefore(this.breadcrumbsCtrl, this.el.firstChild);
// add initial breadcrumb
this._addBreadcrumb(0);
}
// event binding
this._initEvents();
};
MLMenu.prototype._initEvents = function() {
var self = this;
for(var i = 0, len = this.menusArr.length; i < len; ++i) {
this.menusArr[i].menuItems.forEach(function(item, pos) {
item.querySelector('a').addEventListener('click', function(ev) {
var submenu = ev.target.getAttribute('data-submenu'),
itemName = ev.target.innerHTML,
subMenuEl = self.el.querySelector('ul[data-menu="' + submenu + '"]');
// check if there's a sub menu for this item
if( submenu && subMenuEl ) {
ev.preventDefault();
// open it
self._openSubMenu(subMenuEl, pos, itemName);
}
else {
// add class current
var currentlink = self.el.querySelector('.menu__link--current');
if( currentlink ) {
classie.remove(self.el.querySelector('.menu__link--current'), 'menu__link--current');
}
classie.add(ev.target, 'menu__link--current');
// callback
self.options.onItemClick(ev, itemName);
}
});
});
}
// back navigation
if( this.options.backCtrl ) {
this.backCtrl.addEventListener('click', function() {
self._back();
});
}
};
MLMenu.prototype._openSubMenu = function(subMenuEl, clickPosition, subMenuName) {
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
// save "parent" menu index for back navigation
this.menusArr[this.menus.indexOf(subMenuEl)].backIdx = this.current;
// save "parent" menu´s name
this.menusArr[this.menus.indexOf(subMenuEl)].name = subMenuName;
// current menu slides out
this._menuOut(clickPosition);
// next menu (submenu) slides in
this._menuIn(subMenuEl, clickPosition);
};
MLMenu.prototype._back = function() {
if( this.isAnimating ) {
return false;
}
this.isAnimating = true;
// current menu slides out
this._menuOut();
// next menu (previous menu) slides in
var backMenu = this.menusArr[this.menusArr[this.current].backIdx].menuEl;
this._menuIn(backMenu);
// remove last breadcrumb
if( this.options.breadcrumbsCtrl ) {
this.breadcrumbsCtrl.removeChild(this.breadcrumbsCtrl.lastElementChild);
}
};
MLMenu.prototype._menuOut = function(clickPosition) {
// the current menu
var self = this,
currentMenu = this.menusArr[this.current].menuEl,
isBackNavigation = typeof clickPosition == 'undefined' ? true : false;
// slide out current menu items - first, set the delays for the items
this.menusArr[this.current].menuItems.forEach(function(item, pos) {
item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms';
});
// animation class
if( this.options.direction === 'r2l' ) {
classie.add(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
}
else {
classie.add(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
}
};
MLMenu.prototype._menuIn = function(nextMenuEl, clickPosition) {
var self = this,
// the current menu
currentMenu = this.menusArr[this.current].menuEl,
isBackNavigation = typeof clickPosition == 'undefined' ? true : false,
// index of the nextMenuEl
nextMenuIdx = this.menus.indexOf(nextMenuEl),
nextMenuItems = this.menusArr[nextMenuIdx].menuItems,
nextMenuItemsTotal = nextMenuItems.length;
// slide in next menu items - first, set the delays for the items
nextMenuItems.forEach(function(item, pos) {
item.style.WebkitAnimationDelay = item.style.animationDelay = isBackNavigation ? parseInt(pos * self.options.itemsDelayInterval) + 'ms' : parseInt(Math.abs(clickPosition - pos) * self.options.itemsDelayInterval) + 'ms';
// we need to reset the classes once the last item animates in
// the "last item" is the farthest from the clicked item
// let's calculate the index of the farthest item
var farthestIdx = clickPosition <= nextMenuItemsTotal/2 || isBackNavigation ? nextMenuItemsTotal - 1 : 0;
if( pos === farthestIdx ) {
onEndAnimation(item, function() {
// reset classes
if( self.options.direction === 'r2l' ) {
classie.remove(currentMenu, !isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
classie.remove(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
else {
classie.remove(currentMenu, isBackNavigation ? 'animate-outToLeft' : 'animate-outToRight');
classie.remove(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
classie.remove(currentMenu, 'menu__level--current');
classie.add(nextMenuEl, 'menu__level--current');
//reset current
self.current = nextMenuIdx;
// control back button and breadcrumbs navigation elements
if( !isBackNavigation ) {
// show back button
if( self.options.backCtrl ) {
classie.remove(self.backCtrl, 'menu__back--hidden');
}
// add breadcrumb
self._addBreadcrumb(nextMenuIdx);
}
else if( self.current === 0 && self.options.backCtrl ) {
// hide back button
classie.add(self.backCtrl, 'menu__back--hidden');
}
// we can navigate again..
self.isAnimating = false;
});
}
});
// animation class
if( this.options.direction === 'r2l' ) {
classie.add(nextMenuEl, !isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
else {
classie.add(nextMenuEl, isBackNavigation ? 'animate-inFromRight' : 'animate-inFromLeft');
}
};
MLMenu.prototype._addBreadcrumb = function(idx) {
if( !this.options.breadcrumbsCtrl ) {
return false;
}
var bc = document.createElement('a');
bc.innerHTML = idx ? this.menusArr[idx].name : this.options.initialBreadcrumb;
this.breadcrumbsCtrl.appendChild(bc);
var self = this;
bc.addEventListener('click', function(ev) {
ev.preventDefault();
// do nothing if this breadcrumb is the last one in the list of breadcrumbs
if( !bc.nextSibling || self.isAnimating ) {
return false;
}
self.isAnimating = true;
// current menu slides out
self._menuOut();
// next menu slides in
var nextMenu = self.menusArr[idx].menuEl;
self._menuIn(nextMenu);
// remove breadcrumbs that are ahead
var siblingNode;
while (siblingNode = bc.nextSibling) {
self.breadcrumbsCtrl.removeChild(siblingNode);
}
});
};
window.MLMenu = MLMenu;
})(window);
and customized java script :
(function() {
var menuEl = document.getElementById('ml-menu');
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl : false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
//mobile menu toggle
var openMenuCtrl = document.querySelector('.action--open'),
closeMenuCtrl = document.querySelector('.action--close');
openMenuCtrl.addEventListener('click', openMenu);
closeMenuCtrl.addEventListener('click', closeMenu);
function openMenu() {
classie.add(menuEl, 'menu--open');
}
function closeMenu() {
classie.remove(menuEl, 'menu--open');
}
// simulate grid content loading
var gridWrapper = document.querySelector('.content');
function loadDummyData(ev, itemName) {
ev.preventDefault();
closeMenu();
gridWrapper.innerHTML = '';
classie.add(gridWrapper, 'content--loading');
setTimeout(function() {
classie.remove(gridWrapper, 'content--loading');
gridWrapper.innerHTML = '<ul class="products">' + dummyData[itemName] + '<ul>';
}, 700);
}
})();
I want convert a part of this below customized script to angular js.
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl : false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
I use it as this in angular but it has error .
$scope.menuEl = document.getElementById('ml-menu');
var menuEl = document.getElementById('ml-menu'),
mlmenu = new MLMenu(menuEl, {
// breadcrumbsCtrl : true, // show breadcrumbs
// initialBreadcrumb : 'all', // initial breadcrumb text
backCtrl: false, // show back button
// itemsDelayInterval : 60, // delay between each menu item sliding animation
onItemClick: $scope.loadDummyData // callback: item that doesn´t have a submenu gets clicked - onItemClick([event], [inner HTML of the clicked item])
});
How can I do it ?
I think you should wrap your plugin with AngularJS directive https://docs.angularjs.org/guide/directive
I.e. create directive that uses your plugin and initialize it on marked DOM.
angular.module('yourModule')
.directive('mlMenu', function() {
return {
restrict: 'A',
link: function (scope, element) {
var mlmenu = new MLMenu(element,
...
}
};
});
Than in HTML
<div data-ml-menu></div>
And if you are going to modify scope in plugin listeners do not forget to use scope.$apply() wrapper.

JavaScript undefined object even it shows data when checking by F12

Can any one help with this error.
Here is the code:
$(document).ready(function(){
var certifications = {
nextCert : 0,
data : [
{
"imgSrc" : "http://res.cloudinary.com/sharek/image/upload/v1460011761/UC-PJNHKQ02_r1gjmj.jpg"
},
{
"imgSrc" : "http://res.cloudinary.com/sharek/image/upload/v1460011761/UC-KE5C95GA_aid30q.jpg"
}
],
getNextCert : function(){
if(this.nextCert === this.data.length){
this.nextCert = 0;
}
else{
this.nextCert += 1;
}
return this.nextCert;
},
getCurrentCert : function() {
return this.nextCert;
},
getImgCert : function() {
return this.data[this.getNextCert()];
}
};//certification object
var certContainer = $('#cert-container').html
var templateItem = $(certContainer);
templateItem.find('.img-responsive').attr('src',certifications.getImgCert()['imgSrc']);
$('#nextBtn').click( function(){
console.log(certifications.getImgCert()['imgSrc']);
templateItem.find('.img-responsive').attr('src',certifications.getImgCert()['imgSrc']);
});
});//document.ready
And here all of the home page I'm working on:
http://codepen.io/abomaged/pen/JXMXzV
Thanks a lot
It's because your getNextCert function is returning an index that's outside the bounds of your certifications array. When nextCert is 1, the check to see if it's out of bounds of the array passes, so your code then increments nextCert to 2 and returns data[2] which is undefined. You need to change your getNextCert function:
getNextCert : function(){
this.nextCert += 1;
if(this.nextCert === this.data.length){
this.nextCert = 0;
}
return this.nextCert;
},

How to "dump" points selected with the LinkedBrush plugin for mpld3?

I am trying to implement a plugin that allows the user to dump relevant information about points selected by the LinkedBrush plugin. I think my question is sort of related to this example. I have meta information tied to each point via the HTMLTooltip plugin. Ideally, I would somehow be able to dump this too. In the example I linked to, the information is outputted via a prompt. I wish to be able to save this information to a text file of some kind.
Put slightly differently: How do I determine which points in a scatter plot have been selected by the LinkedBrush tool so that I can save the information?
To solves this, I ended up just editing the LinkedBrush plugin code. I added a button that, when clicked, outputs the extent of the brush window by using brush.extent(). This prints the minimum and maximum x and y coordinates. I will basically use these coordinates to trace back to the input data set and determine which points fell within the bounds of the brush. If anyone has a better idea of how to solve this, I would welcome it.
class LinkedBrush(plugins.PluginBase):
JAVASCRIPT="""
mpld3.LinkedBrushPlugin = mpld3_LinkedBrushPlugin;
mpld3.register_plugin("linkedbrush", mpld3_LinkedBrushPlugin);
mpld3_LinkedBrushPlugin.prototype = Object.create(mpld3.Plugin.prototype);
mpld3_LinkedBrushPlugin.prototype.constructor = mpld3_LinkedBrushPlugin;
mpld3_LinkedBrushPlugin.prototype.requiredProps = [ "id" ];
mpld3_LinkedBrushPlugin.prototype.defaultProps = {
button: true,
enabled: null
};
function mpld3_LinkedBrushPlugin(fig, props) {
mpld3.Plugin.call(this, fig, props);
if (this.props.enabled === null) {
this.props.enabled = !this.props.button;
}
var enabled = this.props.enabled;
if (this.props.button) {
var BrushButton = mpld3.ButtonFactory({
buttonID: "linkedbrush",
sticky: true,
actions: [ "drag" ],
onActivate: this.activate.bind(this),
onDeactivate: this.deactivate.bind(this),
onDraw: function() {
this.setState(enabled);
},
icon: function() {
return mpld3.icons["brush"];
}
});
this.fig.buttons.push(BrushButton);
var my_icon = "data:image/png;base64,longstring_that_I_redacted";
var SaveButton = mpld3.ButtonFactory({
buttonID: "save",
sticky: false,
onActivate: this.get_selected.bind(this),
icon: function(){return my_icon;},
});
this.fig.buttons.push(SaveButton);
}
this.extentClass = "linkedbrush";
}
mpld3_LinkedBrushPlugin.prototype.activate = function() {
if (this.enable) this.enable();
};
mpld3_LinkedBrushPlugin.prototype.deactivate = function() {
if (this.disable) this.disable();
};
mpld3_LinkedBrushPlugin.prototype.get_selected = function() {
if (this.get_selected) this.get_selected();
};
mpld3_LinkedBrushPlugin.prototype.draw = function() {
var obj = mpld3.get_element(this.props.id);
if (obj === null) {
throw "LinkedBrush: no object with id='" + this.props.id + "' was found";
}
var fig = this.fig;
if (!("offsets" in obj.props)) {
throw "Plot object with id='" + this.props.id + "' is not a scatter plot";
}
var dataKey = "offsets" in obj.props ? "offsets" : "data";
mpld3.insert_css("#" + fig.figid + " rect.extent." + this.extentClass, {
fill: "#000",
"fill-opacity": .125,
stroke: "#fff"
});
mpld3.insert_css("#" + fig.figid + " path.mpld3-hidden", {
stroke: "#ccc !important",
fill: "#ccc !important"
});
var dataClass = "mpld3data-" + obj.props[dataKey];
var brush = fig.getBrush();
var dataByAx = [];
fig.axes.forEach(function(ax) {
var axData = [];
ax.elements.forEach(function(el) {
if (el.props[dataKey] === obj.props[dataKey]) {
el.group.classed(dataClass, true);
axData.push(el);
}
});
dataByAx.push(axData);
});
var allData = [];
var dataToBrush = fig.canvas.selectAll("." + dataClass);
var currentAxes;
function brushstart(d) {
if (currentAxes != this) {
d3.select(currentAxes).call(brush.clear());
currentAxes = this;
brush.x(d.xdom).y(d.ydom);
}
}
function brushmove(d) {
var data = dataByAx[d.axnum];
if (data.length > 0) {
var ix = data[0].props.xindex;
var iy = data[0].props.yindex;
var e = brush.extent();
if (brush.empty()) {
dataToBrush.selectAll("path").classed("mpld3-hidden", false);
} else {
dataToBrush.selectAll("path").classed("mpld3-hidden", function(p) {
return e[0][0] > p[ix] || e[1][0] < p[ix] || e[0][1] > p[iy] || e[1][1] < p[iy];
});
}
}
}
function brushend(d) {
if (brush.empty()) {
dataToBrush.selectAll("path").classed("mpld3-hidden", false);
}
}
this.get_selected = function(d) {
var brush = fig.getBrush();
var extent = brush.extent();
alert(extent);
}
this.enable = function() {
this.fig.showBrush(this.extentClass);
brush.on("brushstart", brushstart).on("brush", brushmove).on("brushend", brushend);
this.enabled = true;
};
this.disable = function() {
d3.select(currentAxes).call(brush.clear());
this.fig.hideBrush(this.extentClass);
this.enabled = false;
};
this.disable();
};
"""
def __init__(self, points, button=True, enabled=True):
if isinstance(points, mpl.lines.Line2D):
suffix = "pts"
else:
suffix = None
self.dict_ = {"type": "linkedbrush",
"button": button,
"enabled": False,
"id": utils.get_id(points, suffix)}

Type Error when using prototypes

I'm just getting started on Prototypes, and I'm trying to test different things on a website I'm developing. But I stumbled upon an error and I'm not sure why, because I was using that property before and it worked.
I ain't no experienced JavaScript developer, I'm still learning, but here's what I got:
var defaults = {
local_storage_key : "Cluster",
plugin_navigation : ".navigation",
plugin_wrapper : "content-wrapper",
iqns_class : ".iqn"
}
var Clusters = function(environment, options){
this.options = $.extend({}, defaults, options);
this.environment = environment;
this.viewport = $(window);
this.viewport_width = this.viewport.width();
this.viewport_height = this.viewport.height();
this.data_key = this.options.local_storage_key;
this.iqns_class = this.viewport.find(this.options.iqns_class);
this.iqn = this.iqns_class.parent();
this.shop_initiated = false;
this.plugin_navigation = this.options.plugin_navigation;
this.plugin_wrapper = this.options.plugin_wrapper;
this.initiate_plugin(this.plugin_navigation, {
containerID : this.plugin_wrapper,
first : false,
previous : false,
next : false,
last : false,
startPage : this.get_local_storage_data(),
perPage : 6,
midRange : 15,
startRange : 1,
endRange : 1,
keyBrowse : false,
scrollBrowse: false,
pause : 0,
clickStop : true,
delay : 50,
direction : "auto",
animation : "fadeInUp",
links : "title",
fallback : 1000,
minHeight : true,
callback : function(pages) {
this.set_local_storage_data(pages.current);
}
});
this.initiate_auxiliars();
this.set_environment();
};
Clusters.prototype.set_local_storage_data = function(data_val) {
return localStorage.setItem(this.data_key, data_val);
};
Clusters.prototype.get_local_storage_data = function() {
return +(localStorage.getItem(this.data_key) || 1);
};
Clusters.prototype.shop_iqns_selected_class = function() {
var self = this;
if (this.viewport_width < 980) {
$(this.iqns_class).each(function(index, element) {
var element = $(element);
$(self.iqn).on('click', function() {
if (element.hasClass('selected')) {
element.removeClass('selected');
} else {
element.addClass('selected');
}
});
});
}
}
Clusters.prototype.initiate_plugin = function(plugin_navigation, plugin_options) {
return $(plugin_navigation).jPages(plugin_options);
}
Clusters.prototype.initiate_auxiliars = function() {
return this.shop_iqns_selected_class();
}
Clusters.prototype.set_environment = function() {
if(this.environment == "Development") {
less.env = "development";
less.watch();
}
}
var cluster = new Clusters("Development");
I'm sure there's something I'm doing wrong or misunderstood about Prototyping, because otherwise I wouldn't get any errors. I'm just asking for some opinions or some directions on what I'm doing wrong, and what I should do or shouldn't do.
This is the error I get:
Uncaught TypeError: Object #<Object> has no method 'set_local_storage_data' on line 53
As you are using a callback function, the this keyword won't reference the cluster object any more. The callback will be executed in some other context, which "has no method 'set_local_storage_data'".
To avoid that, you can either bind() the function to your cluster object, or you use a local variable in the constructor's scope to reference the cluster object:
var that = this;
this.initiate_plugin(
...,
function(pages) {
that.set_local_storage_data(pages.current);
}
);

jQuery plugin data mixup with multiple instances

I'm trying to develop a jQuery plugin to display adaptive image galleries. I'm using .data() to store the information for each slide of the gallery.
Everything appears to be working okay when displaying a single gallery, but gets mixed up when trying to have multiple gallery instances on a single page. A rough demo can be viewed here.
Here is the code that is being run:
(function($) {
$.Gallery = function(element, options) {
this.$el = $(element);
this._init(options);
};
$.Gallery.defaults = {
showCarousel : true,
};
$.Gallery.prototype = {
_init : function(options) {
var $this = $(this);
var instance = this;
// Get settings from stored instance
var settings = $this.data('settings');
var slides = $this.data('slides');
// Create or update the settings of the current gallery instance
if (typeof(settings) == 'undefined') {
settings = $.extend(true, {}, $.Gallery.defaults, options);
}
else {
settings = $.extend(true, {}, settings, options);
}
$this.data('settings', settings);
if (typeof(slides) === 'undefined') {
slides = [];
}
$this.data('slides', slides);
instance.updateCarousel();
},
addItems : function(newItems) {
var $this = $(this),
slides = $this.data('slides');
for (var i=0; i<newItems.length; i++) {
slides.push(newItems[i]);
};
},
updateCarousel : function() {
var instance = this,
$this = $(this);
slides = $(instance).data('slides');
var gallery = instance.$el.attr('id');
/* Create carousel if it doesn't exist */
if (instance.$el.children('.carousel').length == 0) {
$('<div class="carousel"><ul></ul></div>').appendTo(instance.$el);
var image = instance.$el.find('img');
};
var carouselList = instance.$el.find('.carousel > ul'),
slideCount = slides.length,
displayCount = carouselList.find('li').length;
if (displayCount < slideCount) {
for (var i=displayCount; i<slides.length; i++) {
slides[i].id = gallery + '-slide-' + i;
slide = $('<img>').attr({src : slides[i].thumb}).appendTo(carouselList).wrap(function() {
return '<li id="' + slides[i].id + '" />'
});
slide.on({
click : function() {
instance.slideTo($(this).parent().attr('id'));
},
});
};
};
},
slideTo : function(slideID) {
var $this = $(this);
var slides = $this.data('slides');
var image;
var $instance = $(this.$el);
$(slides).each( function() {
if (this.id == slideID){
image = this.img;
};
});
$('<img/>').load( function() {
$instance.find('div.image').empty().append('<img src="' + image + '"/>');
}).attr( 'src', image );
},
};
$.fn.gallery = function(options) {
args = Array.prototype.slice.call(arguments, 1);
this.each(function() {
var instance = $(this).data('gallery');
if (typeof(options) === 'string') {
if (!instance) {
console.error('Methods cannot be called until jquery.responsiveGallery has been initialized.');
}
else if (!$.isFunction(instance[options])) {
console.error('The method "' + options + '" does not exist in jquery.responsiveGallery.');
}
else {
instance[options].apply(instance, args);
}
}
else {
if (!instance) {
$(this).data('gallery', new $.Gallery(this, options));
}
}
});
return this;
};
})(jQuery);
$(window).load(function() {
$('#gallery2').gallery();
$('#gallery3').gallery();
$('#gallery2').gallery('addItems', [
{
img: 'images/image2.jpg',
thumb: 'images/image2_thumb.jpg',
desc: 'Image of number 2'
},
{
img: 'images/image3.jpg',
thumb: 'images/image3_thumb.jpg',
desc: 'Image of number 3'
},
{
img: 'images/image4.jpg',
thumb: 'images/image4_thumb.jpg',
desc: 'Image of number 4'
},
{
img: 'images/image5.jpg',
thumb: 'images/image5_thumb.jpg',
desc: 'Image of number 5'
}
]);
$('.pGallery').gallery('addItems', [
{
img: 'images/2.jpg',
thumb: 'images/2_thumb.jpg',
desc: 'Image of number 0'
},
{
img: 'images/image1.jpg',
thumb: 'images/image1_thumb.jpg',
desc: 'The second image'
}
]);
$('.pGallery').gallery('updateCarousel');
});
On the surface it appears to create two galleries with the proper slide structure of:
gallery2
gallery2-slide-0
gallery2-slide-1
gallery2-slide-2
gallery2-slide-3
gallery2-slide-4
gallery2-slide-5
gallery3
gallery3-slide-0
gallery3-slide-1
However, the onclick action doesn't work for the last two slides of Gallery2 (the slides duplicated in both galleries). Upon closer inspection of the DOM, I can see that the slides stored in data for gallery2, have the following ids:
gallery2
gallery2-slide-0
gallery2-slide-1
gallery2-slide-2
gallery2-slide-3
gallery3-slide-0
gallery3-slide-1
I'm not sure what is causing the mix-up or how to fix, so any help would be greatly appreciated.
Thanks
Your problem is that calling $('.pGallery').gallery('addItems'... cause the same objects to be added to the internal structure of both galleries, when you call $('.pGallery').gallery('updateCarousel'); the ids stored in those objects are overwritten.
You need to put a copy of the original objects in there. Do:
addItems : function(newItems) {
var $this = $(this),
slides = $this.data('slides');
for (var i=0; i<newItems.length; i++) {
//slides.push(newItems[i]);
slides.push(jQuery.extend({}, newItems[i]));
};
},
With jQuery.extend({}, oldObject) you can perform a shallow copy;

Categories