UNPKG

equation-admin-template

Version:

Booststrap 4 admin template made by equation

1,109 lines (975 loc) 101 kB
/*! * * Jquery Mapael - Dynamic maps jQuery plugin (based on raphael.js) * Requires jQuery, raphael.js and jquery.mousewheel * * Version: 2.0.0 * * Copyright (c) 2015 Vincent Brouté (http://www.vincentbroute.fr/mapael) * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php). * * Thanks to Indigo744 * */ (function (factory) { if (typeof exports === 'object') { // CommonJS module.exports = factory(require('jquery'), require('raphael'), require('mousewheel')); } else if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery', 'raphael', 'mousewheel'], factory); } else { // Browser globals factory(jQuery, Raphael, jQuery.fn.mousewheel); } }(function ($, Raphael, mousewheel, undefined) { "use strict"; // The plugin name (used on several places) var pluginName = "mapael"; // Version number of jQuery Mapael. See http://semver.org/ for more information. var version = "2.0.0-dev"; /* * Mapael constructor * Init instance vars and call init() * @param container the DOM element on which to apply the plugin * @param options the complete options to use */ var Mapael = function (container, options) { var self = this; // the global container (DOM element object) self.container = container; // the global container (jQuery object) self.$container = $(container); // the global options self.options = self.extendDefaultOptions(options); // Save initial HTML content (used by destroy method) self.initialHTMLContent = self.$container.html(); // zoom TimeOut handler (used to set and clear) self.zoomTO = 0; // zoom center coordinate (set at touchstart) self.zoomCenterX = 0; self.zoomCenterY = 0; // Zoom pinch (set at touchstart and touchmove) self.previousPinchDist = 0; // Zoom data self.zoomData = { zoomLevel: 0, zoomX: 0, zoomY: 0, panX: 0, panY: 0 }; // resize TimeOut handler (used to set and clear) self.resizeTO = 0; // Panning: tell if panning action is in progress self.panning = false; // Panning TimeOut handler (used to set and clear) self.panningTO = 0; // Animate view box Interval handler (used to set and clear) self.animationIntervalID = null; // Map subcontainer jQuery object self.$map = {}; // The tooltip jQuery object self.$tooltip = {}; // The paper Raphael object self.paper = {}; // The areas object list self.areas = {}; // The plots object list self.plots = {}; // The links object list self.links = {}; // The map configuration object (taken from map file) self.mapConf = {}; // Let's start the initialization self.init(); }; /* * Mapael Prototype * Defines all methods and properties needed by Mapael * Each mapael object inherits their properties and methods from this prototype */ Mapael.prototype = { /* * Version number */ version: version, /* * Initialize the plugin * Called by the constructor */ init: function () { var self = this; // Init check for class existence if (self.options.map.cssClass === "" || $("." + self.options.map.cssClass, self.container).length === 0) { throw new Error("The map class `" + self.options.map.cssClass + "` doesn't exists"); } // Create the tooltip container self.$tooltip = $("<div>").addClass(self.options.map.tooltip.cssClass).css("display", "none"); // Get the map container, empty it then append tooltip self.$map = $("." + self.options.map.cssClass, self.container).empty().append(self.$tooltip); // Get the map from $.mapael or $.fn.mapael (backward compatibility) if ($[pluginName] && $[pluginName].maps && $[pluginName].maps[self.options.map.name]) { // Mapael version >= 2.x self.mapConf = $[pluginName].maps[self.options.map.name]; } else if ($.fn[pluginName] && $.fn[pluginName].maps && $.fn[pluginName].maps[self.options.map.name]) { // Mapael version <= 1.x - DEPRECATED self.mapConf = $.fn[pluginName].maps[self.options.map.name]; if (window.console && window.console.warn) { window.console.warn("Extending $.fn.mapael is deprecated (map '" + self.options.map.name + "')"); } } else { throw new Error("Unknown map '" + self.options.map.name + "'"); } // Create Raphael paper self.paper = new Raphael(self.$map[0], self.mapConf.width, self.mapConf.height); // issue #135: Check for Raphael bug on text element boundaries if (self.isRaphaelBBoxBugPresent() === true) { self.destroy(); throw new Error("Can't get boundary box for text (is your container hidden? See #135)"); } // add plugin class name on element self.$container.addClass(pluginName); if (self.options.map.tooltip.css) self.$tooltip.css(self.options.map.tooltip.css); self.paper.setViewBox(0, 0, self.mapConf.width, self.mapConf.height, false); // Handle map size if (self.options.map.width) { // NOT responsive: map has a fixed width self.paper.setSize(self.options.map.width, self.mapConf.height * (self.options.map.width / self.mapConf.width)); // Create the legends for plots taking into account the scale of the map self.createLegends("plot", self.plots, (self.options.map.width / self.mapConf.width)); } else { // Responsive: handle resizing of the map self.handleMapResizing(); } // Draw map areas $.each(self.mapConf.elems, function (id) { var elemOptions = self.getElemOptions( self.options.map.defaultArea, (self.options.areas[id] ? self.options.areas[id] : {}), self.options.legend.area ); self.areas[id] = {"mapElem": self.paper.path(self.mapConf.elems[id]).attr(elemOptions.attrs)}; }); // Hook that allows to add custom processing on the map if (self.options.map.beforeInit) self.options.map.beforeInit(self.$container, self.paper, self.options); // Init map areas in a second loop (prevent texts to be hidden by map elements) $.each(self.mapConf.elems, function (id) { var elemOptions = self.getElemOptions( self.options.map.defaultArea, (self.options.areas[id] ? self.options.areas[id] : {}), self.options.legend.area ); self.initElem(self.areas[id], elemOptions, id); }); // Draw links self.links = self.drawLinksCollection(self.options.links); // Draw plots $.each(self.options.plots, function (id) { self.plots[id] = self.drawPlot(id); }); // Attach zoom event self.$container.on("zoom." + pluginName, function (e, zoomOptions) { self.onZoomEvent(e, zoomOptions); }); if (self.options.map.zoom.enabled) { // Enable zoom self.initZoom(self.mapConf.width, self.mapConf.height, self.options.map.zoom); } // Set initial zoom if (self.options.map.zoom.init !== undefined) { if (self.options.map.zoom.init.animDuration === undefined) { self.options.map.zoom.init.animDuration = 0; } self.$container.trigger("zoom." + pluginName, self.options.map.zoom.init); } // Create the legends for areas self.createLegends("area", self.areas, 1); // Attach update event self.$container.on("update." + pluginName, function (e, opt) { self.onUpdateEvent(e, opt); }); // Attach showElementsInRange event self.$container.on("showElementsInRange." + pluginName, function (e, opt) { self.onShowElementsInRange(e, opt); }); // Hook that allows to add custom processing on the map if (self.options.map.afterInit) self.options.map.afterInit(self.$container, self.paper, self.areas, self.plots, self.options); $(self.paper.desc).append(" and Mapael " + self.version + " (http://www.vincentbroute.fr/mapael/)"); }, /* * Destroy mapael * This function effectively detach mapael from the container * - Set the container back to the way it was before mapael instanciation * - Remove all data associated to it (memory can then be free'ed by browser) * * This method can be call directly by user: * $(".mapcontainer").data("mapael").destroy(); * * This method is also automatically called if the user try to call mapael * on a container already containing a mapael instance */ destroy: function () { var self = this; // Detach all event listeners attached to the container self.$container.off("." + pluginName); // Empty the container (this will also detach all event listeners) self.$container.empty(); // Detach the global resize event handler if (self.onResizeEvent) $(window).off("resize." + pluginName, self.onResizeEvent); // Replace initial HTML content self.$container.html(self.initialHTMLContent); // Remove mapael class self.$container.removeClass(pluginName); // Remove the data self.$container.removeData(pluginName); // Remove all internal reference self.container = undefined; self.$container = undefined; self.options = undefined; self.paper = undefined; self.$map = undefined; self.$tooltip = undefined; self.mapConf = undefined; self.areas = undefined; self.plots = undefined; self.links = undefined; }, handleMapResizing: function () { var self = this; // onResizeEvent: call when the window element trigger the resize event // We create it inside this function (and not in the prototype) in order to have a closure // Otherwise, in the prototype, 'this' when triggered is *not* the mapael object but the global window self.onResizeEvent = function () { // Clear any previous setTimeout (avoid too much triggering) clearTimeout(self.resizeTO); // setTimeout to wait for the user to finish its resizing self.resizeTO = setTimeout(function () { self.$map.trigger("resizeEnd." + pluginName); }, 150); }; // Attach resize handler $(window).on("resize." + pluginName, self.onResizeEvent); // Attach resize end handler, and call it once self.$map.on("resizeEnd." + pluginName, function () { var containerWidth = self.$map.width(); if (self.paper.width != containerWidth) { var newScale = containerWidth / self.mapConf.width; // Set new size self.paper.setSize(containerWidth, self.mapConf.height * newScale); // Create plots legend again to take into account the new scale self.createLegends("plot", self.plots, newScale); } }).trigger("resizeEnd." + pluginName); }, /* * Extend the user option with the default one * @param options the user options * @return new options object */ extendDefaultOptions: function (options) { // Extend default options with user options options = $.extend(true, {}, Mapael.prototype.defaultOptions, options); // Extend legend default options $.each(options.legend, function (type) { if ($.isArray(options.legend[type])) { for (var i = 0; i < options.legend[type].length; ++i) options.legend[type][i] = $.extend(true, {}, Mapael.prototype.legendDefaultOptions[type], options.legend[type][i]); } else { options.legend[type] = $.extend(true, {}, Mapael.prototype.legendDefaultOptions[type], options.legend[type]); } }); return options; }, /* * Init the element "elem" on the map (drawing, setting attributes, events, tooltip, ...) */ initElem: function (elem, elemOptions, id) { var self = this; var bbox = {}; var textPosition = {}; // Assign value attribute to element if (elemOptions.value !== undefined){ elem.value = elemOptions.value; } // Init the label related to the element if (elemOptions.text && elemOptions.text.content !== undefined) { // Set a text label in the area bbox = elem.mapElem.getBBox(); textPosition = self.getTextPosition(bbox, elemOptions.text.position, elemOptions.text.margin); elemOptions.text.attrs["text-anchor"] = textPosition.textAnchor; elem.textElem = self.paper.text(textPosition.x, textPosition.y, elemOptions.text.content).attr(elemOptions.text.attrs); $(elem.textElem.node).attr("data-id", id); } // Set user event handlers if (elemOptions.eventHandlers) self.setEventHandlers(id, elemOptions, elem.mapElem, elem.textElem); // Set hover option for mapElem self.setHoverOptions(elem.mapElem, elemOptions.attrs, elemOptions.attrsHover); // Set hover option for textElem if (elem.textElem) self.setHoverOptions(elem.textElem, elemOptions.text.attrs, elemOptions.text.attrsHover); // Set hover behavior only if attrsHover is set for area or for text if (($.isEmptyObject(elemOptions.attrsHover) === false) || (elem.textElem && $.isEmptyObject(elemOptions.text.attrsHover) === false)) { // Set hover behavior self.setHover(elem.mapElem, elem.textElem); } // Init the tooltip if (elemOptions.tooltip) { elem.mapElem.tooltip = elemOptions.tooltip; self.setTooltip(elem.mapElem); if (elemOptions.text && elemOptions.text.content !== undefined) { elem.textElem.tooltip = elemOptions.tooltip; self.setTooltip(elem.textElem); } } // Init the link if (elemOptions.href) { elem.mapElem.href = elemOptions.href; elem.mapElem.target = elemOptions.target; self.setHref(elem.mapElem); if (elemOptions.text && elemOptions.text.content !== undefined) { elem.textElem.href = elemOptions.href; elem.textElem.target = elemOptions.target; self.setHref(elem.textElem); } } $(elem.mapElem.node).attr("data-id", id); }, /* * Init zoom and panning for the map * @param mapWidth * @param mapHeight * @param zoomOptions */ initZoom: function (mapWidth, mapHeight, zoomOptions) { var self = this; var mousedown = false; var previousX = 0; var previousY = 0; var fnZoomButtons = { "reset": function () { self.$container.trigger("zoom." + pluginName, {"level": 0}); }, "in": function () { self.$container.trigger("zoom." + pluginName, {"level": "+1"}); }, "out": function () { self.$container.trigger("zoom." + pluginName, {"level": -1}); } }; // init Zoom data $.extend(self.zoomData, { zoomLevel: 0, panX: 0, panY: 0 }); // init zoom buttons $.each(zoomOptions.buttons, function(type, opt) { if (fnZoomButtons[type] === undefined) throw new Error("Unknown zoom button '" + type + "'"); // Create div with classes, contents and title (for tooltip) var $button = $("<div>").addClass(opt.cssClass) .html(opt.content) .attr("title", opt.title); // Assign click event $button.on("click." + pluginName, fnZoomButtons[type]); // Append to map self.$map.append($button); }); // Update the zoom level of the map on mousewheel if (self.options.map.zoom.mousewheel) { self.$map.on("mousewheel." + pluginName, function (e) { var zoomLevel = (e.deltaY > 0) ? 1 : -1; var coord = self.mapPagePositionToXY(e.pageX, e.pageY); self.$container.trigger("zoom." + pluginName, { "fixedCenter": true, "level": self.zoomData.zoomLevel + zoomLevel, "x": coord.x, "y": coord.y }); return false; }); } // Update the zoom level of the map on touch pinch if (self.options.map.zoom.touch) { self.$map.on("touchstart." + pluginName, function (e) { if (e.originalEvent.touches.length === 2) { self.zoomCenterX = (e.originalEvent.touches[0].pageX + e.originalEvent.touches[1].pageX) / 2; self.zoomCenterY = (e.originalEvent.touches[0].pageY + e.originalEvent.touches[1].pageY) / 2; self.previousPinchDist = Math.sqrt(Math.pow((e.originalEvent.touches[1].pageX - e.originalEvent.touches[0].pageX), 2) + Math.pow((e.originalEvent.touches[1].pageY - e.originalEvent.touches[0].pageY), 2)); } }); self.$map.on("touchmove." + pluginName, function (e) { var pinchDist = 0; var zoomLevel = 0; if (e.originalEvent.touches.length === 2) { pinchDist = Math.sqrt(Math.pow((e.originalEvent.touches[1].pageX - e.originalEvent.touches[0].pageX), 2) + Math.pow((e.originalEvent.touches[1].pageY - e.originalEvent.touches[0].pageY), 2)); if (Math.abs(pinchDist - self.previousPinchDist) > 15) { var coord = self.mapPagePositionToXY(self.zoomCenterX, self.zoomCenterY); zoomLevel = (pinchDist - self.previousPinchDist) / Math.abs(pinchDist - self.previousPinchDist); self.$container.trigger("zoom." + pluginName, { "fixedCenter": true, "level": self.zoomData.zoomLevel + zoomLevel, "x": coord.x, "y": coord.y }); self.previousPinchDist = pinchDist; } return false; } }); } // Panning $("body").on("mouseup." + pluginName + (zoomOptions.touch ? " touchend" : ""), function () { mousedown = false; setTimeout(function () { self.panning = false; }, 50); }); self.$map.on("mousedown." + pluginName + (zoomOptions.touch ? " touchstart" : ""), function (e) { if (e.pageX !== undefined) { mousedown = true; previousX = e.pageX; previousY = e.pageY; } else { if (e.originalEvent.touches.length === 1) { mousedown = true; previousX = e.originalEvent.touches[0].pageX; previousY = e.originalEvent.touches[0].pageY; } } }).on("mousemove." + pluginName + (zoomOptions.touch ? " touchmove" : ""), function (e) { var currentLevel = self.zoomData.zoomLevel; var pageX = 0; var pageY = 0; if (e.pageX !== undefined) { pageX = e.pageX; pageY = e.pageY; } else { if (e.originalEvent.touches.length === 1) { pageX = e.originalEvent.touches[0].pageX; pageY = e.originalEvent.touches[0].pageY; } else { mousedown = false; } } if (mousedown && currentLevel !== 0) { var offsetX = (previousX - pageX) / (1 + (currentLevel * zoomOptions.step)) * (mapWidth / self.paper.width); var offsetY = (previousY - pageY) / (1 + (currentLevel * zoomOptions.step)) * (mapHeight / self.paper.height); var panX = Math.min(Math.max(0, self.paper._viewBox[0] + offsetX), (mapWidth - self.paper._viewBox[2])); var panY = Math.min(Math.max(0, self.paper._viewBox[1] + offsetY), (mapHeight - self.paper._viewBox[3])); if (Math.abs(offsetX) > 5 || Math.abs(offsetY) > 5) { $.extend(self.zoomData, { panX: panX, panY: panY, zoomX: panX + self.paper._viewBox[2] / 2, zoomY: panY + self.paper._viewBox[3] / 2 }); self.paper.setViewBox(panX, panY, self.paper._viewBox[2], self.paper._viewBox[3]); clearTimeout(self.panningTO); self.panningTO = setTimeout(function () { self.$map.trigger("afterPanning", { x1: panX, y1: panY, x2: (panX + self.paper._viewBox[2]), y2: (panY + self.paper._viewBox[3]) }); }, 150); previousX = pageX; previousY = pageY; self.panning = true; } return false; } }); }, /* * Map a mouse position to a map position * Transformation principle: * ** start with (pageX, pageY) absolute mouse coordinate * - Apply translation: take into accounts the map offset in the page * ** from this point, we have relative mouse coordinate * - Apply homothetic transformation: take into accounts initial factor of map sizing (fullWidth / actualWidth) * - Apply homothetic transformation: take into accounts the zoom factor * ** from this point, we have relative map coordinate * - Apply translation: take into accounts the current panning of the map * ** from this point, we have absolute map coordinate * @param pageX: mouse client coordinate on X * @param pageY: mouse client coordinate on Y * @return map coordinate {x, y} */ mapPagePositionToXY: function(pageX, pageY) { var self = this; var offset = self.$map.offset(); var initFactor = (self.options.map.width) ? (self.mapConf.width / self.options.map.width) : (self.mapConf.width / self.$map.width()); var zoomFactor = 1 / (1 + (self.zoomData.zoomLevel * self.options.map.zoom.step)); return { x: (zoomFactor * initFactor * (pageX - offset.left)) + self.zoomData.panX, y: (zoomFactor * initFactor * (pageY - offset.top)) + self.zoomData.panY }; }, /* * Zoom on the map at a specific level focused on specific coordinates * If no coordinates are specified, the zoom will be focused on the center of the map * options : * "level" : level of the zoom between minLevel and maxLevel * "x" or "latitude" : x coordinate or latitude of the point to focus on * "y" or "longitude" : y coordinate or longitude of the point to focus on * "fixedCenter" : set to true in order to preserve the position of x,y in the canvas when zoomed * "animDuration" : zoom duration */ onZoomEvent: function (e, zoomOptions) { var self = this; var newLevel = self.zoomData.zoomLevel; var panX = 0; var panY = 0; var previousZoomLevel = (1 + self.zoomData.zoomLevel * self.options.map.zoom.step); var zoomLevel = 0; var animDuration = (zoomOptions.animDuration !== undefined) ? zoomOptions.animDuration : self.options.map.zoom.animDuration; var offsetX = 0; var offsetY = 0; var coords = {}; // Get user defined zoom level if (zoomOptions.level !== undefined) { if (typeof zoomOptions.level === "string") { // level is a string, either "n", "+n" or "-n" if ((zoomOptions.level.slice(0, 1) === '+') || (zoomOptions.level.slice(0, 1) === '-')) { // zoomLevel is relative newLevel = self.zoomData.zoomLevel + parseInt(zoomOptions.level); } else { // zoomLevel is absolute newLevel = parseInt(zoomOptions.level); } } else { // level is integer if (zoomOptions.level < 0) { // zoomLevel is relative newLevel = self.zoomData.zoomLevel + zoomOptions.level; } else { // zoomLevel is absolute newLevel = zoomOptions.level; } } // Make sure we stay in the boundaries newLevel = Math.min(Math.max(newLevel, self.options.map.zoom.minLevel), self.options.map.zoom.maxLevel); } zoomLevel = (1 + newLevel * self.options.map.zoom.step); if (zoomOptions.latitude !== undefined && zoomOptions.longitude !== undefined) { coords = self.mapConf.getCoords(zoomOptions.latitude, zoomOptions.longitude); zoomOptions.x = coords.x; zoomOptions.y = coords.y; } if (zoomOptions.x === undefined) zoomOptions.x = self.paper._viewBox[0] + self.paper._viewBox[2] / 2; if (zoomOptions.y === undefined) zoomOptions.y = (self.paper._viewBox[1] + self.paper._viewBox[3] / 2); if (newLevel === 0) { panX = 0; panY = 0; } else if (zoomOptions.fixedCenter !== undefined && zoomOptions.fixedCenter === true) { offsetX = self.zoomData.panX + ((zoomOptions.x - self.zoomData.panX) * (zoomLevel - previousZoomLevel)) / zoomLevel; offsetY = self.zoomData.panY + ((zoomOptions.y - self.zoomData.panY) * (zoomLevel - previousZoomLevel)) / zoomLevel; panX = Math.min(Math.max(0, offsetX), (self.mapConf.width - (self.mapConf.width / zoomLevel))); panY = Math.min(Math.max(0, offsetY), (self.mapConf.height - (self.mapConf.height / zoomLevel))); } else { panX = Math.min(Math.max(0, zoomOptions.x - (self.mapConf.width / zoomLevel) / 2), (self.mapConf.width - (self.mapConf.width / zoomLevel))); panY = Math.min(Math.max(0, zoomOptions.y - (self.mapConf.height / zoomLevel) / 2), (self.mapConf.height - (self.mapConf.height / zoomLevel))); } // Update zoom level of the map if (zoomLevel == previousZoomLevel && panX == self.zoomData.panX && panY == self.zoomData.panY) return; if (animDuration > 0) { self.animateViewBox(panX, panY, self.mapConf.width / zoomLevel, self.mapConf.height / zoomLevel, animDuration, self.options.map.zoom.animEasing); } else { self.paper.setViewBox(panX, panY, self.mapConf.width / zoomLevel, self.mapConf.height / zoomLevel); clearTimeout(self.zoomTO); self.zoomTO = setTimeout(function () { self.$map.trigger("afterZoom", { x1: panX, y1: panY, x2: (panX + (self.mapConf.width / zoomLevel)), y2: (panY + (self.mapConf.height / zoomLevel)) }); }, 150); } $.extend(self.zoomData, { zoomLevel: newLevel, panX: panX, panY: panY, zoomX: panX + self.paper._viewBox[2] / 2, zoomY: panY + self.paper._viewBox[3] / 2 }); }, /* * Show some element in range defined by user * Triggered by user $(".mapcontainer").trigger("showElementsInRange", [opt]); * * @param opt the options * opt.hiddenOpacity opacity for hidden element (default = 0.3) * opt.animDuration animation duration in ms (default = 0) * opt.afterShowRange callback * opt.ranges the range to show: * Example: * opt.ranges = { * 'plot' : { * 0 : { // valueIndex * 'min': 1000, * 'max': 1200 * }, * 1 : { // valueIndex * 'min': 10, * 'max': 12 * } * }, * 'area' : { * {'min': 10, 'max': 20} // No valueIndex, only an object, use 0 as valueIndex (easy case) * } * } */ onShowElementsInRange: function(e, opt) { var self = this; // set animDuration to default if not defined if (opt.animDuration === undefined) { opt.animDuration = 0; } // set hiddenOpacity to default if not defined if (opt.hiddenOpacity === undefined) { opt.hiddenOpacity = 0.3; } // handle area if (opt.ranges && opt.ranges.area) { self.showElemByRange(opt.ranges.area, self.areas, opt.hiddenOpacity, opt.animDuration); } // handle plot if (opt.ranges && opt.ranges.plot) { self.showElemByRange(opt.ranges.plot, self.plots, opt.hiddenOpacity, opt.animDuration); } // handle link if (opt.ranges && opt.ranges.link) { self.showElemByRange(opt.ranges.link, self.links, opt.hiddenOpacity, opt.animDuration); } // Call user callback if (opt.afterShowRange) opt.afterShowRange(); }, /* * Show some element in range * @param ranges: the ranges * @param elems: list of element on which to check against previous range * @hiddenOpacity: the opacity when hidden * @animDuration: the animation duration */ showElemByRange: function(ranges, elems, hiddenOpacity, animDuration) { var self = this; // Hold the final opacity value for all elements consolidated after applying each ranges // This allow to set the opacity only once for each elements var elemsFinalOpacity = {}; // set object with one valueIndex to 0 if we have directly the min/max if (ranges.min !== undefined || ranges.max !== undefined) { ranges = {0: ranges}; } // Loop through each valueIndex $.each(ranges, function (valueIndex) { var range = ranges[valueIndex]; // Check if user defined at least a min or max value if (range.min === undefined && range.max === undefined) { return true; // skip this iteration (each loop), goto next range } // Loop through each elements $.each(elems, function (id) { var elemValue = elems[id].value; // set value with one valueIndex to 0 if not object if (typeof elemValue !== "object") { elemValue = [elemValue]; } // Check existence of this value index if (elemValue[valueIndex] === undefined) { return true; // skip this iteration (each loop), goto next element } // Check if in range if ((range.min !== undefined && elemValue[valueIndex] < range.min) || (range.max !== undefined && elemValue[valueIndex] > range.max)) { // Element not in range elemsFinalOpacity[id] = hiddenOpacity; } else { // Element in range elemsFinalOpacity[id] = 1; } }); }); // Now that we looped through all ranges, we can really assign the final opacity $.each(elemsFinalOpacity, function (id) { self.setElementOpacity(elems[id], elemsFinalOpacity[id], animDuration); }); }, /* * Set element opacity * Handle elem.mapElem and elem.textElem * @param elem the element * @param opacity the opacity to apply * @param animDuration the animation duration to use */ setElementOpacity: function(elem, opacity, animDuration) { // Ensure no animation is running //elem.mapElem.stop(); //if (elem.textElem) elem.textElem.stop(); // If final opacity is not null, ensure element is shown before proceeding if (opacity > 0) { elem.mapElem.show(); if (elem.textElem) elem.textElem.show(); } if (animDuration > 0) { // Animate attribute elem.mapElem.animate({"opacity": opacity}, animDuration, "linear", function () { // If final attribute is 0, hide if (opacity === 0) elem.mapElem.hide(); }); // Handle text element if (elem.textElem) { // Animate attribute elem.textElem.animate({"opacity": opacity}, animDuration, "linear", function () { // If final attribute is 0, hide if (opacity === 0) elem.textElem.hide(); }); } } else { // Set attribute elem.mapElem.attr({"opacity": opacity}); // For null opacity, hide it if (opacity === 0) elem.mapElem.hide(); // Handle text elemen if (elem.textElem) { // Set attribute elem.textElem.attr({"opacity": opacity}); // For null opacity, hide it if (opacity === 0) elem.textElem.hide(); } } }, /* * * Update the current map * Refresh attributes and tooltips for areas and plots * @param opt option for the refresh : * opt.mapOptions: options to update for plots and areas * opt.replaceOptions: whether mapsOptions should entirely replace current map options, or just extend it * opt.opt.newPlots new plots to add to the map * opt.newLinks new links to add to the map * opt.deletePlotKeys plots to delete from the map (array, or "all" to remove all plots) * opt.deleteLinkKeys links to remove from the map (array, or "all" to remove all links) * opt.setLegendElemsState the state of legend elements to be set : show (default) or hide * opt.animDuration animation duration in ms (default = 0) * opt.afterUpdate Hook that allows to add custom processing on the map */ onUpdateEvent: function (e, opt) { var self = this; // Abort if opt is undefined if (typeof opt !== "object") return; var i = 0; var animDuration = (opt.animDuration) ? opt.animDuration : 0; // This function remove an element using animation (or not, depending on animDuration) // Used for deletePlotKeys and deleteLinkKeys var fnRemoveElement = function (elem) { // Unset all event handlers self.unsetHover(elem.mapElem, elem.textElem); if (animDuration > 0) { elem.mapElem.animate({"opacity": 0}, animDuration, "linear", function () { elem.mapElem.remove(); }); if (elem.textElem) { elem.textElem.animate({"opacity": 0}, animDuration, "linear", function () { elem.textElem.remove(); }); } } else { elem.mapElem.remove(); if (elem.textElem) { elem.textElem.remove(); } } }; // This function show an element using animation // Used for newPlots and newLinks var fnShowElement = function (elem) { // Starts with hidden elements elem.mapElem.attr({opacity: 0}); if (elem.textElem) elem.textElem.attr({opacity: 0}); // Set final element opacity self.setElementOpacity( elem, (elem.mapElem.originalAttrs.opacity !== undefined) ? elem.mapElem.originalAttrs.opacity : 1, animDuration ); }; if (typeof opt.mapOptions === "object") { if (opt.replaceOptions === true) self.options = self.extendDefaultOptions(opt.mapOptions); else $.extend(true, self.options, opt.mapOptions); // IF we update areas, plots or legend, then reset all legend state to "show" if (opt.mapOptions.areas !== undefined || opt.mapOptions.plots !== undefined || opt.mapOptions.legend !== undefined) { $("[data-type='elem']", self.$container).each(function (id, elem) { if ($(elem).attr('data-hidden') === "1") { // Toggle state of element by clicking $(elem).trigger("click." + pluginName, [false, animDuration]); } }); } } // Delete plots by name if deletePlotKeys is array if (typeof opt.deletePlotKeys === "object") { for (; i < opt.deletePlotKeys.length; i++) { if (self.plots[opt.deletePlotKeys[i]] !== undefined) { fnRemoveElement(self.plots[opt.deletePlotKeys[i]]); delete self.plots[opt.deletePlotKeys[i]]; } } // Delete ALL plots if deletePlotKeys is set to "all" } else if (opt.deletePlotKeys === "all") { $.each(self.plots, function (id, elem) { fnRemoveElement(elem); }); // Empty plots object self.plots = {}; } // Delete links by name if deleteLinkKeys is array if (typeof opt.deleteLinkKeys === "object") { for (i = 0; i < opt.deleteLinkKeys.length; i++) { if (self.links[opt.deleteLinkKeys[i]] !== undefined) { fnRemoveElement(self.links[opt.deleteLinkKeys[i]]); delete self.links[opt.deleteLinkKeys[i]]; } } // Delete ALL links if deleteLinkKeys is set to "all" } else if (opt.deleteLinkKeys === "all") { $.each(self.links, function (id, elem) { fnRemoveElement(elem); }); // Empty links object self.links = {}; } // New plots if (typeof opt.newPlots === "object") { $.each(opt.newPlots, function (id) { if (self.plots[id] === undefined) { self.options.plots[id] = opt.newPlots[id]; self.plots[id] = self.drawPlot(id); if (animDuration > 0) { fnShowElement(self.plots[id]); } } }); } // New links if (typeof opt.newLinks === "object") { var newLinks = self.drawLinksCollection(opt.newLinks); $.extend(self.links, newLinks); $.extend(self.options.links, opt.newLinks); if (animDuration > 0) { $.each(newLinks, function (id) { fnShowElement(newLinks[id]); }); } } // Update areas attributes and tooltips $.each(self.areas, function (id) { // Avoid updating unchanged elements if ((typeof opt.mapOptions === "object" && ( (typeof opt.mapOptions.map === "object" && typeof opt.mapOptions.map.defaultArea === "object") || (typeof opt.mapOptions.areas === "object" && typeof opt.mapOptions.areas[id] === "object") || (typeof opt.mapOptions.legend === "object" && typeof opt.mapOptions.legend.area === "object") )) || opt.replaceOptions === true ) { var elemOptions = self.getElemOptions( self.options.map.defaultArea, (self.options.areas[id] ? self.options.areas[id] : {}), self.options.legend.area ); self.updateElem(elemOptions, self.areas[id], animDuration); } }); // Update plots attributes and tooltips $.each(self.plots, function (id) { // Avoid updating unchanged elements if ((typeof opt.mapOptions ==="object" && ( (typeof opt.mapOptions.map === "object" && typeof opt.mapOptions.map.defaultPlot === "object") || (typeof opt.mapOptions.plots === "object" && typeof opt.mapOptions.plots[id] === "object") || (typeof opt.mapOptions.legend === "object" && typeof opt.mapOptions.legend.plot === "object") )) || opt.replaceOptions === true ) { var elemOptions = self.getElemOptions( self.options.map.defaultPlot, (self.options.plots[id] ? self.options.plots[id] : {}), self.options.legend.plot ); if (elemOptions.type == "square") { elemOptions.attrs.width = elemOptions.size; elemOptions.attrs.height = elemOptions.size; elemOptions.attrs.x = self.plots[id].mapElem.attrs.x - (elemOptions.size - self.plots[id].mapElem.attrs.width) / 2; elemOptions.attrs.y = self.plots[id].mapElem.attrs.y - (elemOptions.size - self.plots[id].mapElem.attrs.height) / 2; } else if (elemOptions.type == "image") { elemOptions.attrs.width = elemOptions.width; elemOptions.attrs.height = elemOptions.height; elemOptions.attrs.x = self.plots[id].mapElem.attrs.x - (elemOptions.width - self.plots[id].mapElem.attrs.width) / 2; elemOptions.attrs.y = self.plots[id].mapElem.attrs.y - (elemOptions.height - self.plots[id].mapElem.attrs.height) / 2; } else if (elemOptions.type == "svg") { if (elemOptions.attrs.transform !== undefined) { elemOptions.attrs.transform = self.plots[id].mapElem.baseTransform + elemOptions.attrs.transform; } }else { // Default : circle elemOptions.attrs.r = elemOptions.size / 2; } self.updateElem(elemOptions, self.plots[id], animDuration); } }); // Update links attributes and tooltips $.each(self.links, function (id) { // Avoid updating unchanged elements if ((typeof opt.mapOptions === "object" && ( (typeof opt.mapOptions.map === "object" && typeof opt.mapOptions.map.defaultLink === "object") || (typeof opt.mapOptions.links === "object" && typeof opt.mapOptions.links[id] === "object") )) || opt.replaceOptions === true ) { var elemOptions = self.getElemOptions( self.options.map.defaultLink, (self.options.links[id] ? self.options.links[id] : {}), {} ); self.updateElem(elemOptions, self.links[id], animDuration); } }); // Update legends if (opt.mapOptions && ( (typeof opt.mapOptions.legend === "object") || (typeof opt.mapOptions.map === "object" && typeof opt.mapOptions.map.defaultArea === "object") || (typeof opt.mapOptions.map === "object" && typeof opt.mapOptions.map.defaultPlot === "object") )) { // Show all elements on the map before updating the legends $("[data-type='elem']", self.$container).each(function (id, elem) { if ($(elem).attr('data-hidden') === "1") { $(elem).trigger("click." + pluginName, [false, animDuration]); } }); self.createLegends("area", self.areas, 1); if (self.options.map.width) { self.createLegends("plot", self.plots, (self.options.map.width / self.mapConf.width)); } else { self.createLegends("plot", self.plots, (self.$map.width() / self.mapConf.width)); } } // Hide/Show all elements based on showlegendElems // Toggle (i.e. click) only if: // - slice legend is shown AND we want to hide // - slice legend is hidden AND we want to show if (typeof opt.setLegendElemsState === "object") { // setLegendElemsState is an object listing the legend we want to hide/show $.each(opt.setLegendElemsState, function (legendCSSClass, action) { // Search for the legend var $legend = self.$container.find("." + legendCSSClass)[0]; if ($legend !== undefined) { // Select all elem inside this legend $("[data-type='elem']", $legend).each(function (id, elem) { if (($(elem).attr('data-hidden') === "0" && action === "hide") || ($(elem).attr('data-hidden') === "1" && action === "show")) { // Toggle state of element by clicking