').addClass('jvectormap-container');
if (this.params.container) {
this.params.container.append( this.container );
}
this.container.data('mapObject', this);
this.defaultWidth = this.mapData.width;
this.defaultHeight = this.mapData.height;
this.setBackgroundColor(this.params.backgroundColor);
this.onResize = function(){
map.updateSize();
}
jvm.$(window).resize(this.onResize);
for (e in jvm.Map.apiEvents) {
if (this.params[e]) {
this.container.bind(jvm.Map.apiEvents[e]+'.jvectormap', this.params[e]);
}
}
this.canvas = new jvm.VectorCanvas(this.container[0], this.width, this.height);
if ( ('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch) ) {
if (this.params.bindTouchEvents) {
this.bindContainerTouchEvents();
}
}
this.bindContainerEvents();
this.bindElementEvents();
this.createTip();
if (this.params.zoomButtons) {
this.bindZoomButtons();
}
this.createRegions();
this.createMarkers(this.params.markers || {});
this.updateSize();
if (this.params.focusOn) {
if (typeof this.params.focusOn === 'string') {
this.params.focusOn = {region: this.params.focusOn};
} else if (jvm.$.isArray(this.params.focusOn)) {
this.params.focusOn = {regions: this.params.focusOn};
}
this.setFocus(this.params.focusOn);
}
if (this.params.selectedRegions) {
this.setSelectedRegions(this.params.selectedRegions);
}
if (this.params.selectedMarkers) {
this.setSelectedMarkers(this.params.selectedMarkers);
}
this.legendCntHorizontal = jvm.$('
').addClass('jvectormap-legend-cnt jvectormap-legend-cnt-h');
this.legendCntVertical = jvm.$('
').addClass('jvectormap-legend-cnt jvectormap-legend-cnt-v');
this.container.append(this.legendCntHorizontal);
this.container.append(this.legendCntVertical);
if (this.params.series) {
this.createSeries();
}
};
jvm.Map.prototype = {
transX: 0,
transY: 0,
scale: 1,
baseTransX: 0,
baseTransY: 0,
baseScale: 1,
width: 0,
height: 0,
/**
* Set background color of the map.
* @param {String} backgroundColor Background color in CSS format.
*/
setBackgroundColor: function(backgroundColor) {
this.container.css('background-color', backgroundColor);
},
resize: function() {
var curBaseScale = this.baseScale;
if (this.width / this.height > this.defaultWidth / this.defaultHeight) {
this.baseScale = this.height / this.defaultHeight;
this.baseTransX = Math.abs(this.width - this.defaultWidth * this.baseScale) / (2 * this.baseScale);
} else {
this.baseScale = this.width / this.defaultWidth;
this.baseTransY = Math.abs(this.height - this.defaultHeight * this.baseScale) / (2 * this.baseScale);
}
this.scale *= this.baseScale / curBaseScale;
this.transX *= this.baseScale / curBaseScale;
this.transY *= this.baseScale / curBaseScale;
},
/**
* Synchronize the size of the map with the size of the container. Suitable in situations where the size of the container is changed programmatically or container is shown after it became visible.
*/
updateSize: function(){
this.width = this.container.width();
this.height = this.container.height();
this.resize();
this.canvas.setSize(this.width, this.height);
this.applyTransform();
},
/**
* Reset all the series and show the map with the initial zoom.
*/
reset: function() {
var key,
i;
for (key in this.series) {
for (i = 0; i < this.series[key].length; i++) {
this.series[key][i].clear();
}
}
this.scale = this.baseScale;
this.transX = this.baseTransX;
this.transY = this.baseTransY;
this.applyTransform();
},
applyTransform: function() {
var maxTransX,
maxTransY,
minTransX,
minTransY;
if (this.defaultWidth * this.scale <= this.width) {
maxTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
minTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
} else {
maxTransX = 0;
minTransX = (this.width - this.defaultWidth * this.scale) / this.scale;
}
if (this.defaultHeight * this.scale <= this.height) {
maxTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
minTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
} else {
maxTransY = 0;
minTransY = (this.height - this.defaultHeight * this.scale) / this.scale;
}
if (this.transY > maxTransY) {
this.transY = maxTransY;
} else if (this.transY < minTransY) {
this.transY = minTransY;
}
if (this.transX > maxTransX) {
this.transX = maxTransX;
} else if (this.transX < minTransX) {
this.transX = minTransX;
}
this.canvas.applyTransformParams(this.scale, this.transX, this.transY);
if (this.markers) {
this.repositionMarkers();
}
this.repositionLabels();
this.container.trigger('viewportChange', [this.scale/this.baseScale, this.transX, this.transY]);
},
bindContainerEvents: function(){
var mouseDown = false,
oldPageX,
oldPageY,
map = this;
if (this.params.panOnDrag) {
this.container.mousemove(function(e){
if (mouseDown) {
map.transX -= (oldPageX - e.pageX) / map.scale;
map.transY -= (oldPageY - e.pageY) / map.scale;
map.applyTransform();
oldPageX = e.pageX;
oldPageY = e.pageY;
}
return false;
}).mousedown(function(e){
mouseDown = true;
oldPageX = e.pageX;
oldPageY = e.pageY;
return false;
});
this.onContainerMouseUp = function(){
mouseDown = false;
};
jvm.$('body').mouseup(this.onContainerMouseUp);
}
if (this.params.zoomOnScroll) {
this.container.mousewheel(function(event, delta, deltaX, deltaY) {
var offset = jvm.$(map.container).offset(),
centerX = event.pageX - offset.left,
centerY = event.pageY - offset.top,
zoomStep = Math.pow(1.003, event.deltaY);
map.tip.hide();
map.setScale(map.scale * zoomStep, centerX, centerY);
event.preventDefault();
});
}
},
bindContainerTouchEvents: function(){
var touchStartScale,
touchStartDistance,
map = this,
touchX,
touchY,
centerTouchX,
centerTouchY,
lastTouchesLength,
handleTouchEvent = function(e){
var touches = e.originalEvent.touches,
offset,
scale,
transXOld,
transYOld;
if (e.type == 'touchstart') {
lastTouchesLength = 0;
}
if (touches.length == 1) {
if (lastTouchesLength == 1) {
transXOld = map.transX;
transYOld = map.transY;
map.transX -= (touchX - touches[0].pageX) / map.scale;
map.transY -= (touchY - touches[0].pageY) / map.scale;
map.applyTransform();
map.tip.hide();
if (transXOld != map.transX || transYOld != map.transY) {
e.preventDefault();
}
}
touchX = touches[0].pageX;
touchY = touches[0].pageY;
} else if (touches.length == 2) {
if (lastTouchesLength == 2) {
scale = Math.sqrt(
Math.pow(touches[0].pageX - touches[1].pageX, 2) +
Math.pow(touches[0].pageY - touches[1].pageY, 2)
) / touchStartDistance;
map.setScale(
touchStartScale * scale,
centerTouchX,
centerTouchY
)
map.tip.hide();
e.preventDefault();
} else {
offset = jvm.$(map.container).offset();
if (touches[0].pageX > touches[1].pageX) {
centerTouchX = touches[1].pageX + (touches[0].pageX - touches[1].pageX) / 2;
} else {
centerTouchX = touches[0].pageX + (touches[1].pageX - touches[0].pageX) / 2;
}
if (touches[0].pageY > touches[1].pageY) {
centerTouchY = touches[1].pageY + (touches[0].pageY - touches[1].pageY) / 2;
} else {
centerTouchY = touches[0].pageY + (touches[1].pageY - touches[0].pageY) / 2;
}
centerTouchX -= offset.left;
centerTouchY -= offset.top;
touchStartScale = map.scale;
touchStartDistance = Math.sqrt(
Math.pow(touches[0].pageX - touches[1].pageX, 2) +
Math.pow(touches[0].pageY - touches[1].pageY, 2)
);
}
}
lastTouchesLength = touches.length;
};
jvm.$(this.container).bind('touchstart', handleTouchEvent);
jvm.$(this.container).bind('touchmove', handleTouchEvent);
},
bindElementEvents: function(){
var map = this,
mouseMoved;
this.container.mousemove(function(){
mouseMoved = true;
});
/* Can not use common class selectors here because of the bug in jQuery
SVG handling, use with caution. */
this.container.delegate("[class~='jvectormap-element']", 'mouseover mouseout', function(e){
var baseVal = jvm.$(this).attr('class').baseVal || jvm.$(this).attr('class'),
type = baseVal.indexOf('jvectormap-region') === -1 ? 'marker' : 'region',
code = type == 'region' ? jvm.$(this).attr('data-code') : jvm.$(this).attr('data-index'),
element = type == 'region' ? map.regions[code].element : map.markers[code].element,
tipText = type == 'region' ? map.mapData.paths[code].name : (map.markers[code].config.name || ''),
tipShowEvent = jvm.$.Event(type+'TipShow.jvectormap'),
overEvent = jvm.$.Event(type+'Over.jvectormap');
if (e.type == 'mouseover') {
map.container.trigger(overEvent, [code]);
if (!overEvent.isDefaultPrevented()) {
element.setHovered(true);
}
map.tip.text(tipText);
map.container.trigger(tipShowEvent, [map.tip, code]);
if (!tipShowEvent.isDefaultPrevented()) {
map.tip.show();
map.tipWidth = map.tip.width();
map.tipHeight = map.tip.height();
}
} else {
element.setHovered(false);
map.tip.hide();
map.container.trigger(type+'Out.jvectormap', [code]);
}
});
/* Can not use common class selectors here because of the bug in jQuery
SVG handling, use with caution. */
this.container.delegate("[class~='jvectormap-element']", 'mousedown', function(){
mouseMoved = false;
});
/* Can not use common class selectors here because of the bug in jQuery
SVG handling, use with caution. */
this.container.delegate("[class~='jvectormap-element']", 'mouseup', function(){
var baseVal = jvm.$(this).attr('class').baseVal ? jvm.$(this).attr('class').baseVal : jvm.$(this).attr('class'),
type = baseVal.indexOf('jvectormap-region') === -1 ? 'marker' : 'region',
code = type == 'region' ? jvm.$(this).attr('data-code') : jvm.$(this).attr('data-index'),
clickEvent = jvm.$.Event(type+'Click.jvectormap'),
element = type == 'region' ? map.regions[code].element : map.markers[code].element;
if (!mouseMoved) {
map.container.trigger(clickEvent, [code]);
if ((type === 'region' && map.params.regionsSelectable) || (type === 'marker' && map.params.markersSelectable)) {
if (!clickEvent.isDefaultPrevented()) {
if (map.params[type+'sSelectableOne']) {
map.clearSelected(type+'s');
}
element.setSelected(!element.isSelected);
}
}
}
});
},
bindZoomButtons: function() {
var map = this;
jvm.$('
').addClass('jvectormap-zoomin').text('+').appendTo(this.container);
jvm.$('
').addClass('jvectormap-zoomout').html('−').appendTo(this.container);
this.container.find('.jvectormap-zoomin').click(function(){
map.setScale(map.scale * map.params.zoomStep, map.width / 2, map.height / 2, false, map.params.zoomAnimate);
});
this.container.find('.jvectormap-zoomout').click(function(){
map.setScale(map.scale / map.params.zoomStep, map.width / 2, map.height / 2, false, map.params.zoomAnimate);
});
},
createTip: function(){
var map = this;
this.tip = jvm.$('
').addClass('jvectormap-tip').appendTo(jvm.$('body'));
this.container.mousemove(function(e){
var left = e.pageX-15-map.tipWidth,
top = e.pageY-15-map.tipHeight;
if (left < 5) {
left = e.pageX + 15;
}
if (top < 5) {
top = e.pageY + 15;
}
if (map.tip.is(':visible')) {
map.tip.css({
left: left,
top: top
})
}
});
},
setScale: function(scale, anchorX, anchorY, isCentered, animate) {
var viewportChangeEvent = jvm.$.Event('zoom.jvectormap'),
interval,
that = this,
i = 0,
count = Math.abs(Math.round((scale - this.scale) * 60 / Math.max(scale, this.scale))),
scaleStart,
scaleDiff,
transXStart,
transXDiff,
transYStart,
transYDiff,
transX,
transY,
deferred = new jvm.$.Deferred();
if (scale > this.params.zoomMax * this.baseScale) {
scale = this.params.zoomMax * this.baseScale;
} else if (scale < this.params.zoomMin * this.baseScale) {
scale = this.params.zoomMin * this.baseScale;
}
if (typeof anchorX != 'undefined' && typeof anchorY != 'undefined') {
zoomStep = scale / this.scale;
if (isCentered) {
transX = anchorX + this.defaultWidth * (this.width / (this.defaultWidth * scale)) / 2;
transY = anchorY + this.defaultHeight * (this.height / (this.defaultHeight * scale)) / 2;
} else {
transX = this.transX - (zoomStep - 1) / scale * anchorX;
transY = this.transY - (zoomStep - 1) / scale * anchorY;
}
}
if (animate && count > 0) {
scaleStart = this.scale;
scaleDiff = (scale - scaleStart) / count;
transXStart = this.transX * this.scale;
transYStart = this.transY * this.scale;
transXDiff = (transX * scale - transXStart) / count;
transYDiff = (transY * scale - transYStart) / count;
interval = setInterval(function(){
i += 1;
that.scale = scaleStart + scaleDiff * i;
that.transX = (transXStart + transXDiff * i) / that.scale;
that.transY = (transYStart + transYDiff * i) / that.scale;
that.applyTransform();
if (i == count) {
clearInterval(interval);
that.container.trigger(viewportChangeEvent, [scale/that.baseScale]);
deferred.resolve();
}
}, 10);
} else {
this.transX = transX;
this.transY = transY;
this.scale = scale;
this.applyTransform();
this.container.trigger(viewportChangeEvent, [scale/this.baseScale]);
deferred.resolve();
}
return deferred;
},
/**
* Set the map's viewport to the specific point and set zoom of the map to the specific level. Point and zoom level could be defined in two ways: using the code of some region to focus on or a central point and zoom level as numbers.
* @param This method takes a configuration object as the single argument. The options passed to it are the following:
* @param {Array} params.regions Array of region codes to zoom to.
* @param {String} params.region Region code to zoom to.
* @param {Number} params.scale Map scale to set.
* @param {Number} params.lat Latitude to set viewport to.
* @param {Number} params.lng Longitude to set viewport to.
* @param {Number} params.x Number from 0 to 1 specifying the horizontal coordinate of the central point of the viewport.
* @param {Number} params.y Number from 0 to 1 specifying the vertical coordinate of the central point of the viewport.
* @param {Boolean} params.animate Indicates whether or not to animate the scale change and transition.
*/
setFocus: function(config){
var bbox,
itemBbox,
newBbox,
codes,
i,
point;
config = config || {};
if (config.region) {
codes = [config.region];
} else if (config.regions) {
codes = config.regions;
}
if (codes) {
for (i = 0; i < codes.length; i++) {
if (this.regions[codes[i]]) {
itemBbox = this.regions[codes[i]].element.shape.getBBox();
if (itemBbox) {
if (typeof bbox == 'undefined') {
bbox = itemBbox;
} else {
newBbox = {
x: Math.min(bbox.x, itemBbox.x),
y: Math.min(bbox.y, itemBbox.y),
width: Math.max(bbox.x + bbox.width, itemBbox.x + itemBbox.width) - Math.min(bbox.x, itemBbox.x),
height: Math.max(bbox.y + bbox.height, itemBbox.y + itemBbox.height) - Math.min(bbox.y, itemBbox.y)
}
bbox = newBbox;
}
}
}
}
return this.setScale(
Math.min(this.width / bbox.width, this.height / bbox.height),
- (bbox.x + bbox.width / 2),
- (bbox.y + bbox.height / 2),
true,
config.animate
);
} else {
if (config.lat && config.lng) {
point = this.latLngToPoint(config.lat, config.lng);
config.x = this.transX - point.x / this.scale;
config.y = this.transY - point.y / this.scale;
} else if (config.x && config.y) {
config.x *= -this.defaultWidth;
config.y *= -this.defaultHeight;
}
return this.setScale(config.scale * this.baseScale, config.x, config.y, true, config.animate);
}
},
getSelected: function(type){
var key,
selected = [];
for (key in this[type]) {
if (this[type][key].element.isSelected) {
selected.push(key);
}
}
return selected;
},
/**
* Return the codes of currently selected regions.
* @returns {Array}
*/
getSelectedRegions: function(){
return this.getSelected('regions');
},
/**
* Return the codes of currently selected markers.
* @returns {Array}
*/
getSelectedMarkers: function(){
return this.getSelected('markers');
},
setSelected: function(type, keys){
var i;
if (typeof keys != 'object') {
keys = [keys];
}
if (jvm.$.isArray(keys)) {
for (i = 0; i < keys.length; i++) {
this[type][keys[i]].element.setSelected(true);
}
} else {
for (i in keys) {
this[type][i].element.setSelected(!!keys[i]);
}
}
},
/**
* Set or remove selected state for the regions.
* @param {String|Array|Object} keys If
String
or
Array
the region(s) with the corresponding code(s) will be selected. If
Object
was provided its keys are codes of regions, state of which should be changed. Selected state will be set if value is true, removed otherwise.
*/
setSelectedRegions: function(keys){
this.setSelected('regions', keys);
},
/**
* Set or remove selected state for the markers.
* @param {String|Array|Object} keys If
String
or
Array
the marker(s) with the corresponding code(s) will be selected. If
Object
was provided its keys are codes of markers, state of which should be changed. Selected state will be set if value is true, removed otherwise.
*/
setSelectedMarkers: function(keys){
this.setSelected('markers', keys);
},
clearSelected: function(type){
var select = {},
selected = this.getSelected(type),
i;
for (i = 0; i < selected.length; i++) {
select[selected[i]] = false;
};
this.setSelected(type, select);
},
/**
* Remove the selected state from all the currently selected regions.
*/
clearSelectedRegions: function(){
this.clearSelected('regions');
},
/**
* Remove the selected state from all the currently selected markers.
*/
clearSelectedMarkers: function(){
this.clearSelected('markers');
},
/**
* Return the instance of Map. Useful when instantiated as a jQuery plug-in.
* @returns {Map}
*/
getMapObject: function(){
return this;
},
/**
* Return the name of the region by region code.
* @returns {String}
*/
getRegionName: function(code){
return this.mapData.paths[code].name;
},
createRegions: function(){
var key,
region,
map = this;
this.regionLabelsGroup = this.regionLabelsGroup || this.canvas.addGroup();
for (key in this.mapData.paths) {
region = new jvm.Region({
map: this,
path: this.mapData.paths[key].path,
code: key,
style: jvm.$.extend(true, {}, this.params.regionStyle),
labelStyle: jvm.$.extend(true, {}, this.params.regionLabelStyle),
canvas: this.canvas,
labelsGroup: this.regionLabelsGroup,
label: this.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.regions) : null
});
jvm.$(region.shape).bind('selected', function(e, isSelected){
map.container.trigger('regionSelected.jvectormap', [jvm.$(this.node).attr('data-code'), isSelected, map.getSelectedRegions()]);
});
this.regions[key] = {
element: region,
config: this.mapData.paths[key]
};
}
},
createMarkers: function(markers) {
var i,
marker,
point,
markerConfig,
markersArray,
map = this;
this.markersGroup = this.markersGroup || this.canvas.addGroup();
this.markerLabelsGroup = this.markerLabelsGroup || this.canvas.addGroup();
if (jvm.$.isArray(markers)) {
markersArray = markers.slice();
markers = {};
for (i = 0; i < markersArray.length; i++) {
markers[i] = markersArray[i];
}
}
for (i in markers) {
markerConfig = markers[i] instanceof Array ? {latLng: markers[i]} : markers[i];
point = this.getMarkerPosition( markerConfig );
if (point !== false) {
marker = new jvm.Marker({
map: this,
style: jvm.$.extend(true, {}, this.params.markerStyle, {initial: markerConfig.style || {}}),
labelStyle: jvm.$.extend(true, {}, this.params.markerLabelStyle),
index: i,
cx: point.x,
cy: point.y,
group: this.markersGroup,
canvas: this.canvas,
labelsGroup: this.markerLabelsGroup,
label: this.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.markers) : null
});
jvm.$(marker.shape).bind('selected', function(e, isSelected){
map.container.trigger('markerSelected.jvectormap', [jvm.$(this.node).attr('data-index'), isSelected, map.getSelectedMarkers()]);
});
if (this.markers[i]) {
this.removeMarkers([i]);
}
this.markers[i] = {element: marker, config: markerConfig};
}
}
},
repositionMarkers: function() {
var i,
point;
for (i in this.markers) {
point = this.getMarkerPosition( this.markers[i].config );
if (point !== false) {
this.markers[i].element.setStyle({cx: point.x, cy: point.y});
}
}
},
repositionLabels: function() {
var key;
for (key in this.regions) {
this.regions[key].element.updateLabelPosition();
}
for (key in this.markers) {
this.markers[key].element.updateLabelPosition();
}
},
getMarkerPosition: function(markerConfig) {
if (jvm.Map.maps[this.params.map].projection) {
return this.latLngToPoint.apply(this, markerConfig.latLng || [0, 0]);
} else {
return {
x: markerConfig.coords[0]*this.scale + this.transX*this.scale,
y: markerConfig.coords[1]*this.scale + this.transY*this.scale
};
}
},
/**
* Add one marker to the map.
* @param {String} key Marker unique code.
* @param {Object} marker Marker configuration parameters.
* @param {Array} seriesData Values to add to the data series.
*/
addMarker: function(key, marker, seriesData){
var markers = {},
data = [],
values,
i,
seriesData = seriesData || [];
markers[key] = marker;
for (i = 0; i < seriesData.length; i++) {
values = {};
if (typeof seriesData[i] !== 'undefined') {
values[key] = seriesData[i];
}
data.push(values);
}
this.addMarkers(markers, data);
},
/**
* Add set of marker to the map.
* @param {Object|Array} markers Markers to add to the map. In case of array is provided, codes of markers will be set as string representations of array indexes.
* @param {Array} seriesData Values to add to the data series.
*/
addMarkers: function(markers, seriesData){
var i;
seriesData = seriesData || [];
this.createMarkers(markers);
for (i = 0; i < seriesData.length; i++) {
this.series.markers[i].setValues(seriesData[i] || {});
};
},
/**
* Remove some markers from the map.
* @param {Array} markers Array of marker codes to be removed.
*/
removeMarkers: function(markers){
var i;
for (i = 0; i < markers.length; i++) {
this.markers[ markers[i] ].element.remove();
delete this.markers[ markers[i] ];
};
},
/**
* Remove all markers from the map.
*/
removeAllMarkers: function(){
var i,
markers = [];
for (i in this.markers) {
markers.push(i);
}
this.removeMarkers(markers)
},
/**
* Converts coordinates expressed as latitude and longitude to the coordinates in pixels on the map.
* @param {Number} lat Latitide of point in degrees.
* @param {Number} lng Longitude of point in degrees.
*/
latLngToPoint: function(lat, lng) {
var point,
proj = jvm.Map.maps[this.params.map].projection,
centralMeridian = proj.centralMeridian,
inset,
bbox;
if (lng < (-180 + centralMeridian)) {
lng += 360;
}
point = jvm.Proj[proj.type](lat, lng, centralMeridian);
inset = this.getInsetForPoint(point.x, point.y);
if (inset) {
bbox = inset.bbox;
point.x = (point.x - bbox[0].x) / (bbox[1].x - bbox[0].x) * inset.width * this.scale;
point.y = (point.y - bbox[0].y) / (bbox[1].y - bbox[0].y) * inset.height * this.scale;
return {
x: point.x + this.transX*this.scale + inset.left*this.scale,
y: point.y + this.transY*this.scale + inset.top*this.scale
};
} else {
return false;
}
},
/**
* Converts cartesian coordinates into coordinates expressed as latitude and longitude.
* @param {Number} x X-axis of point on map in pixels.
* @param {Number} y Y-axis of point on map in pixels.
*/
pointToLatLng: function(x, y) {
var proj = jvm.Map.maps[this.params.map].projection,
centralMeridian = proj.centralMeridian,
insets = jvm.Map.maps[this.params.map].insets,
i,
inset,
bbox,
nx,
ny;
for (i = 0; i < insets.length; i++) {
inset = insets[i];
bbox = inset.bbox;
nx = x - (this.transX*this.scale + inset.left*this.scale);
ny = y - (this.transY*this.scale + inset.top*this.scale);
nx = (nx / (inset.width * this.scale)) * (bbox[1].x - bbox[0].x) + bbox[0].x;
ny = (ny / (inset.height * this.scale)) * (bbox[1].y - bbox[0].y) + bbox[0].y;
if (nx > bbox[0].x && nx < bbox[1].x && ny > bbox[0].y && ny < bbox[1].y) {
return jvm.Proj[proj.type + '_inv'](nx, -ny, centralMeridian);
}
}
return false;
},
getInsetForPoint: function(x, y){
var insets = jvm.Map.maps[this.params.map].insets,
i,
bbox;
for (i = 0; i < insets.length; i++) {
bbox = insets[i].bbox;
if (x > bbox[0].x && x < bbox[1].x && y > bbox[0].y && y < bbox[1].y) {
return insets[i];
}
}
},
createSeries: function(){
var i,
key;
this.series = {
markers: [],
regions: []
};
for (key in this.params.series) {
for (i = 0; i < this.params.series[key].length; i++) {
this.series[key][i] = new jvm.DataSeries(
this.params.series[key][i],
this[key],
this
);
}
}
},
/**
* Gracefully remove the map and and all its accessories, unbind event handlers.
*/
remove: function(){
this.tip.remove();
this.container.remove();
jvm.$(window).unbind('resize', this.onResize);
jvm.$('body').unbind('mouseup', this.onContainerMouseUp);
}
};
jvm.Map.maps = {};
jvm.Map.defaultParams = {
map: 'world_mill_en',
backgroundColor: '#505050',
zoomButtons: true,
zoomOnScroll: true,
panOnDrag: true,
zoomMax: 8,
zoomMin: 1,
zoomStep: 1.6,
zoomAnimate: true,
regionsSelectable: false,
markersSelectable: false,
bindTouchEvents: true,
regionStyle: {
initial: {
fill: 'white',
"fill-opacity": 1,
stroke: 'none',
"stroke-width": 0,
"stroke-opacity": 1
},
hover: {
"fill-opacity": 0.8,
cursor: 'pointer'
},
selected: {
fill: 'yellow'
},
selectedHover: {
}
},
regionLabelStyle: {
initial: {
'font-family': 'Verdana',
'font-size': '12',
'font-weight': 'bold',
cursor: 'default',
fill: 'black'
},
hover: {
cursor: 'pointer'
}
},
markerStyle: {
initial: {
fill: 'grey',
stroke: '#505050',
"fill-opacity": 1,
"stroke-width": 1,
"stroke-opacity": 1,
r: 5
},
hover: {
stroke: 'black',
"stroke-width": 2,
cursor: 'pointer'
},
selected: {
fill: 'blue'
},
selectedHover: {
}
},
markerLabelStyle: {
initial: {
'font-family': 'Verdana',
'font-size': '12',
'font-weight': 'bold',
cursor: 'default',
fill: 'black'
},
hover: {
cursor: 'pointer'
}
}
};
jvm.Map.apiEvents = {
onRegionTipShow: 'regionTipShow',
onRegionOver: 'regionOver',
onRegionOut: 'regionOut',
onRegionClick: 'regionClick',
onRegionSelected: 'regionSelected',
onMarkerTipShow: 'markerTipShow',
onMarkerOver: 'markerOver',
onMarkerOut: 'markerOut',
onMarkerClick: 'markerClick',
onMarkerSelected: 'markerSelected',
onViewportChange: 'viewportChange'
};
/**
* Creates map with drill-down functionality.
* @constructor
* @param {Object} params Parameters to initialize map with.
* @param {Number} params.maxLevel Maximum number of levels user can go through
* @param {Object} params.main Config of the main map. See
jvm.Map for more information.
* @param {Function} params.mapNameByCode Function go generate map name by region code. Default value is:
function(code, multiMap) {
return code.toLowerCase()+'_'+
multiMap.defaultProjection+'_en';
}
* @param {Function} params.mapUrlByCode Function to generate map url by region code. Default value is:
function(code, multiMap){
return 'jquery-jvectormap-data-'+
code.toLowerCase()+'-'+
multiMap.defaultProjection+'-en.js';
}
*/
jvm.MultiMap = function(params) {
var that = this;
this.maps = {};
this.params = jvm.$.extend(true, {}, jvm.MultiMap.defaultParams, params);
this.params.maxLevel = this.params.maxLevel || Number.MAX_VALUE;
this.params.main = this.params.main || {};
this.params.main.multiMapLevel = 0;
this.history = [ this.addMap(this.params.main.map, this.params.main) ];
this.defaultProjection = this.history[0].mapData.projection.type;
this.mapsLoaded = {};
this.params.container.css({position: 'relative'});
this.backButton = jvm.$('
').addClass('jvectormap-goback').text('Back').appendTo(this.params.container);
this.backButton.hide();
this.backButton.click(function(){
that.goBack();
});
this.spinner = jvm.$('
').addClass('jvectormap-spinner').appendTo(this.params.container);
this.spinner.hide();
};
jvm.MultiMap.prototype = {
addMap: function(name, config){
var cnt = jvm.$('
').css({
width: '100%',
height: '100%'
});
this.params.container.append(cnt);
this.maps[name] = new jvm.Map(jvm.$.extend(config, {container: cnt}));
if (this.params.maxLevel > config.multiMapLevel) {
this.maps[name].container.on('regionClick.jvectormap', {scope: this}, function(e, code){
var multimap = e.data.scope,
mapName = multimap.params.mapNameByCode(code, multimap);
if (!multimap.drillDownPromise || multimap.drillDownPromise.state() !== 'pending') {
multimap.drillDown(mapName, code);
}
});
}
return this.maps[name];
},
downloadMap: function(code){
var that = this,
deferred = jvm.$.Deferred();
if (!this.mapsLoaded[code]) {
jvm.$.get(this.params.mapUrlByCode(code, this)).then(function(){
that.mapsLoaded[code] = true;
deferred.resolve();
}, function(){
deferred.reject();
});
} else {
deferred.resolve();
}
return deferred;
},
drillDown: function(name, code){
var currentMap = this.history[this.history.length - 1],
that = this,
focusPromise = currentMap.setFocus({region: code, animate: true}),
downloadPromise = this.downloadMap(code);
focusPromise.then(function(){
if (downloadPromise.state() === 'pending') {
that.spinner.show();
}
});
downloadPromise.always(function(){
that.spinner.hide();
});
this.drillDownPromise = jvm.$.when(downloadPromise, focusPromise);
this.drillDownPromise.then(function(){
currentMap.params.container.hide();
if (!that.maps[name]) {
that.addMap(name, {map: name, multiMapLevel: currentMap.params.multiMapLevel + 1});
} else {
that.maps[name].params.container.show();
}
that.history.push( that.maps[name] );
that.backButton.show();
});
},
goBack: function(){
var currentMap = this.history.pop(),
prevMap = this.history[this.history.length - 1],
that = this;
currentMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true}).then(function(){
currentMap.params.container.hide();
prevMap.params.container.show();
prevMap.updateSize();
if (that.history.length === 1) {
that.backButton.hide();
}
prevMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true});
});
}
};
jvm.MultiMap.defaultParams = {
mapNameByCode: function(code, multiMap){
return code.toLowerCase()+'_'+multiMap.defaultProjection+'_en';
},
mapUrlByCode: function(code, multiMap){
return 'jquery-jvectormap-data-'+code.toLowerCase()+'-'+multiMap.defaultProjection+'-en.js';
}
}