Вход Регистрация
Файл: plugins/bower_components/vectormap/jquery-jvectormap-2.0.2.min.js
Строк: 3701
<?php
/**
 * jVectorMap version 2.0.2
 *
 * Copyright 2011-2014, Kirill Lebedev
 *
 */

(function( $ ){
  var 
apiParams = {
        
set: {
          
colors1,
          
values1,
          
backgroundColor1,
          
scaleColors1,
          
normalizeFunction1,
          
focus1
        
},
        
get: {
          
selectedRegions1,
          
selectedMarkers1,
          
mapObject1,
          
regionName1
        
}
      };

  $.
fn.vectorMap = function(options) {
    var 
map,
        
methodName,
        
map this.children('.jvectormap-container').data('mapObject');

    if (
options === 'addMap') {
      
jvm.Map.maps[arguments[1]] = arguments[2];
    } else if ((
options === 'set' || options === 'get') && apiParams[options][arguments[1]]) {
      
methodName arguments[1].charAt(0).toUpperCase()+arguments[1].substr(1);
      return 
map[options+methodName].apply(map, Array.prototype.slice.call(arguments2));
    } else {
      
options options || {};
      
options.container this;
      
map = new jvm.Map(options);
    }

    return 
this;
  };
})( 
jQuery );
/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
 * Licensed under the MIT License (LICENSE.txt).
 *
 * Version: 3.1.9
 *
 * Requires: jQuery 1.2.2+
 */

(function (factory) {
    if ( 
typeof define === 'function' && define.amd ) {
        
// AMD. Register as an anonymous module.
        
define(['jquery'], factory);
    } else if (
typeof exports === 'object') {
        
// Node/CommonJS style for Browserify
        
module.exports factory;
    } else {
        
// Browser globals
        
factory(jQuery);
    }
}(function ($) {

    var 
toFix  = ['wheel''mousewheel''DOMMouseScroll''MozMousePixelScroll'],
        
toBind = ( 'onwheel' in document || document.documentMode >= ) ?
                    [
'wheel'] : ['mousewheel''DomMouseScroll''MozMousePixelScroll'],
        
slice  = Array.prototype.slice,
        
nullLowestDeltaTimeoutlowestDelta;

    if ( $.
event.fixHooks ) {
        for ( var 
toFix.lengthi; ) {
            $.
event.fixHookstoFix[--i] ] = $.event.mouseHooks;
        }
    }

    var 
special = $.event.special.mousewheel = {
        
version'3.1.9',

        
setup: function() {
            if ( 
this.addEventListener ) {
                for ( var 
toBind.lengthi; ) {
                    
this.addEventListenertoBind[--i], handlerfalse );
                }
            } else {
                
this.onmousewheel handler;
            }
            
// Store the line height and page height for this particular element
            
$.data(this'mousewheel-line-height'special.getLineHeight(this));
            $.
data(this'mousewheel-page-height'special.getPageHeight(this));
        },

        
teardown: function() {
            if ( 
this.removeEventListener ) {
                for ( var 
toBind.lengthi; ) {
                    
this.removeEventListenertoBind[--i], handlerfalse );
                }
            } else {
                
this.onmousewheel null;
            }
        },

        
getLineHeight: function(elem) {
            return 
parseInt($(elem)['offsetParent' in $.fn 'offsetParent' 'parent']().css('fontSize'), 10);
        },

        
getPageHeight: function(elem) {
            return $(
elem).height();
        },

        
settings: {
            
adjustOldDeltastrue
        
}
    };

    $.
fn.extend({
        
mousewheel: function(fn) {
            return 
fn this.bind('mousewheel'fn) : this.trigger('mousewheel');
        },

        
unmousewheel: function(fn) {
            return 
this.unbind('mousewheel'fn);
        }
    });


    function 
handler(event) {
        var 
orgEvent   event || window.event,
            
args       slice.call(arguments1),
            
delta      0,
            
deltaX     0,
            
deltaY     0,
            
absDelta   0;
        
event = $.event.fix(orgEvent);
        
event.type 'mousewheel';

        
// Old school scrollwheel delta
        
if ( 'detail'      in orgEvent ) { deltaY orgEvent.detail * -1;      }
        if ( 
'wheelDelta'  in orgEvent ) { deltaY orgEvent.wheelDelta;       }
        if ( 
'wheelDeltaY' in orgEvent ) { deltaY orgEvent.wheelDeltaY;      }
        if ( 
'wheelDeltaX' in orgEvent ) { deltaX orgEvent.wheelDeltaX * -1; }

        
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
        
if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
            
deltaX deltaY * -1;
            
deltaY 0;
        }

        
// Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
        
delta deltaY === deltaX deltaY;

        
// New school wheel delta (wheel event)
        
if ( 'deltaY' in orgEvent ) {
            
deltaY orgEvent.deltaY * -1;
            
delta  deltaY;
        }
        if ( 
'deltaX' in orgEvent ) {
            
deltaX orgEvent.deltaX;
            if ( 
deltaY === ) { delta  deltaX * -1; }
        }

        
// No change actually happened, no reason to go any further
        
if ( deltaY === && deltaX === ) { return; }

        
// Need to convert lines and pages to pixels if we aren't already in pixels
        // There are three delta modes:
        //   * deltaMode 0 is by pixels, nothing to do
        //   * deltaMode 1 is by lines
        //   * deltaMode 2 is by pages
        
if ( orgEvent.deltaMode === ) {
            var 
lineHeight = $.data(this'mousewheel-line-height');
            
delta  *= lineHeight;
            
deltaY *= lineHeight;
            
deltaX *= lineHeight;
        } else if ( 
orgEvent.deltaMode === ) {
            var 
pageHeight = $.data(this'mousewheel-page-height');
            
delta  *= pageHeight;
            
deltaY *= pageHeight;
            
deltaX *= pageHeight;
        }

        
// Store lowest absolute delta to normalize the delta values
        
absDelta Math.maxMath.abs(deltaY), Math.abs(deltaX) );

        if ( !
lowestDelta || absDelta lowestDelta ) {
            
lowestDelta absDelta;

            
// Adjust older deltas if necessary
            
if ( shouldAdjustOldDeltas(orgEventabsDelta) ) {
                
lowestDelta /= 40;
            }
        }

        
// Adjust older deltas if necessary
        
if ( shouldAdjustOldDeltas(orgEventabsDelta) ) {
            
// Divide all the things by 40!
            
delta  /= 40;
            
deltaX /= 40;
            
deltaY /= 40;
        }

        
// Get a whole, normalized value for the deltas
        
delta  Mathdelta  >= 'floor' 'ceil' ](delta  lowestDelta);
        
deltaX MathdeltaX >= 'floor' 'ceil' ](deltaX lowestDelta);
        
deltaY MathdeltaY >= 'floor' 'ceil' ](deltaY lowestDelta);

        
// Add information to the event object
        
event.deltaX deltaX;
        
event.deltaY deltaY;
        
event.deltaFactor lowestDelta;
        
// Go ahead and set deltaMode to 0 since we converted to pixels
        // Although this is a little odd since we overwrite the deltaX/Y
        // properties with normalized deltas.
        
event.deltaMode 0;

        
// Add event and delta to the front of the arguments
        
args.unshift(eventdeltadeltaXdeltaY);

        
// Clearout lowestDelta after sometime to better
        // handle multiple device types that give different
        // a different lowestDelta
        // Ex: trackpad = 3 and mouse wheel = 120
        
if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
        
nullLowestDeltaTimeout setTimeout(nullLowestDelta200);

        return ($.
event.dispatch || $.event.handle).apply(thisargs);
    }

    function 
nullLowestDelta() {
        
lowestDelta null;
    }

    function 
shouldAdjustOldDeltas(orgEventabsDelta) {
        
// If this is an older event and the delta is divisable by 120,
        // then we are assuming that the browser is treating this as an
        // older mouse wheel event and that we should divide the deltas
        // by 40 to try and get a more usable deltaFactor.
        // Side note, this actually impacts the reported scroll distance
        // in older browsers and can cause scrolling to be slower than native.
        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
        
return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta 120 === 0;
    }

}));
/**
 * @namespace jvm Holds core methods and classes used by jVectorMap.
 */
var jvm = {

  
/**
   * Inherits child's prototype from the parent's one.
   * @param {Function} child
   * @param {Function} parent
   */
  
inherits: function(childparent) {
    function 
temp() {}
    
temp.prototype parent.prototype;
    
child.prototype = new temp();
    
child.prototype.constructor child;
    
child.parentClass parent;
  },

  
/**
   * Mixes in methods from the source constructor to the target one.
   * @param {Function} target
   * @param {Function} source
   */
  
mixin: function(targetsource){
    var 
prop;

    for (
prop in source.prototype) {
      if (
source.prototype.hasOwnProperty(prop)) {
        
target.prototype[prop] = source.prototype[prop];
      }
    }
  },

  
min: function(values){
    var 
min Number.MAX_VALUE,
        
i;

    if (
values instanceof Array) {
      for (
0values.lengthi++) {
        if (
values[i] < min) {
          
min values[i];
        }
      }
    } else {
      for (
i in values) {
        if (
values[i] < min) {
          
min values[i];
        }
      }
    }
    return 
min;
  },

  
max: function(values){
    var 
max Number.MIN_VALUE,
        
i;

    if (
values instanceof Array) {
      for (
0values.lengthi++) {
        if (
values[i] > max) {
          
max values[i];
        }
      }
    } else {
      for (
i in values) {
        if (
values[i] > max) {
          
max values[i];
        }
      }
    }
    return 
max;
  },

  
keys: function(object){
    var 
keys = [],
        
key;

    for (
key in object) {
      
keys.push(key);
    }
    return 
keys;
  },

  
values: function(object){
    var 
values = [],
        
key,
        
i;

    for (
0arguments.lengthi++) {
      
object arguments[i];
      for (
key in object) {
        
values.push(object[key]);
      }
    }
    return 
values;
  },

  
whenImageLoaded: function(url){
    var 
deferred = new jvm.$.Deferred(),
        
img jvm.$('<img/>');

    
img.error(function(){
      
deferred.reject();
    }).
load(function(){
      
deferred.resolve(img);
    });
    
img.attr('src'url);

    return 
deferred;
  },

  
isImageUrl: function(s){
    return /.
w{3,4}$/.test(s);
  }
};

jvm.$ = jQuery;

/**
 * indexOf polyfill for IE < 9
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
 */
if (!Array.prototype.indexOf) {
  Array.
prototype.indexOf = function (searchElementfromIndex) {

    var 
k;

    
// 1. Let O be the result of calling ToObject passing
    //    the this value as the argument.
    
if (this == null) {
      throw new 
TypeError('"this" is null or not defined');
    }

    var 
Object(this);

    
// 2. Let lenValue be the result of calling the Get
    //    internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    
var len O.length >>> 0;

    
// 4. If len is 0, return -1.
    
if (len === 0) {
      return -
1;
    }

    
// 5. If argument fromIndex was passed let n be
    //    ToInteger(fromIndex); else let n be 0.
    
var = +fromIndex || 0;

    if (
Math.abs(n) === Infinity) {
      
0;
    }

    
// 6. If n >= len, return -1.
    
if (>= len) {
      return -
1;
    }

    
// 7. If n >= 0, then Let k be n.
    // 8. Else, n<0, Let k be len - abs(n).
    //    If k is less than 0, then let k be 0.
    
Math.max(>= len Math.abs(n), 0);

    
// 9. Repeat, while k < len
    
while (len) {
      
// a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the
      //    HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      //    i.  Let elementK be the result of calling the Get
      //        internal method of O with the argument ToString(k).
      //   ii.  Let same be the result of applying the
      //        Strict Equality Comparison Algorithm to
      //        searchElement and elementK.
      //  iii.  If same is true, return k.
      
if (k in O && O[k] === searchElement) {
        return 
k;
      }
      
k++;
    }
    return -
1;
  };
}
/**
 * Basic wrapper for DOM element.
 * @constructor
 * @param {String} name Tag name of the element
 * @param {Object} config Set of parameters to initialize element with
 */
jvm.AbstractElement = function(nameconfig){
  
/**
   * Underlying DOM element
   * @type {DOMElement}
   * @private
   */
  
this.node this.createElement(name);

  
/**
   * Name of underlying element
   * @type {String}
   * @private
   */
  
this.name name;

  
/**
   * Internal store of attributes
   * @type {Object}
   * @private
   */
  
this.properties = {};

  if (
config) {
    
this.set(config);
  }
};

/**
 * Set attribute of the underlying DOM element.
 * @param {String} name Name of attribute
 * @param {Number|String} config Set of parameters to initialize element with
 */
jvm.AbstractElement.prototype.set = function(propertyvalue){
  var 
key;

  if (
typeof property === 'object') {
    for (
key in property) {
      
this.properties[key] = property[key];
      
this.applyAttr(keyproperty[key]);
    }
  } else {
    
this.properties[property] = value;
    
this.applyAttr(propertyvalue);
  }
};

/**
 * Returns value of attribute.
 * @param {String} name Name of attribute
 */
jvm.AbstractElement.prototype.get = function(property){
  return 
this.properties[property];
};

/**
 * Applies attribute value to the underlying DOM element.
 * @param {String} name Name of attribute
 * @param {Number|String} config Value of attribute to apply
 * @private
 */
jvm.AbstractElement.prototype.applyAttr = function(propertyvalue){
  
this.node.setAttribute(propertyvalue);
};

jvm.AbstractElement.prototype.remove = function(){
  
jvm.$(this.node).remove();
};
/**
 * Implements abstract vector canvas.
 * @constructor
 * @param {HTMLElement} container Container to put element to.
 * @param {Number} width Width of canvas.
 * @param {Number} height Height of canvas.
 */
jvm.AbstractCanvasElement = function(containerwidthheight){
  
this.container container;
  
this.setSize(widthheight);
  
this.rootElement = new jvm[this.classPrefix+'GroupElement']();
  
this.node.appendChildthis.rootElement.node );
  
this.container.appendChild(this.node);
}

/**
 * Add element to the certain group inside of the canvas.
 * @param {HTMLElement} element Element to add to canvas.
 * @param {HTMLElement} group Group to add element into or into root group if not provided.
 */
jvm.AbstractCanvasElement.prototype.add = function(elementgroup){
  
group group || this.rootElement;
  
group.add(element);
  
element.canvas this;
}

/**
 * Create path and add it to the canvas.
 * @param {Object} config Parameters of path to create.
 * @param {Object} style Styles of the path to create.
 * @param {HTMLElement} group Group to add path into.
 */
jvm.AbstractCanvasElement.prototype.addPath = function(configstylegroup){
  var 
el = new jvm[this.classPrefix+'PathElement'](configstyle);

  
this.add(elgroup);
  return 
el;
};

/**
 * Create circle and add it to the canvas.
 * @param {Object} config Parameters of path to create.
 * @param {Object} style Styles of the path to create.
 * @param {HTMLElement} group Group to add circle into.
 */
jvm.AbstractCanvasElement.prototype.addCircle = function(configstylegroup){
  var 
el = new jvm[this.classPrefix+'CircleElement'](configstyle);

  
this.add(elgroup);
  return 
el;
};

/**
 * Create circle and add it to the canvas.
 * @param {Object} config Parameters of path to create.
 * @param {Object} style Styles of the path to create.
 * @param {HTMLElement} group Group to add circle into.
 */
jvm.AbstractCanvasElement.prototype.addImage = function(configstylegroup){
  var 
el = new jvm[this.classPrefix+'ImageElement'](configstyle);

  
this.add(elgroup);
  return 
el;
};

/**
 * Create text and add it to the canvas.
 * @param {Object} config Parameters of path to create.
 * @param {Object} style Styles of the path to create.
 * @param {HTMLElement} group Group to add circle into.
 */
jvm.AbstractCanvasElement.prototype.addText = function(configstylegroup){
  var 
el = new jvm[this.classPrefix+'TextElement'](configstyle);

  
this.add(elgroup);
  return 
el;
};

/**
 * Add group to the another group inside of the canvas.
 * @param {HTMLElement} group Group to add circle into or root group if not provided.
 */
jvm.AbstractCanvasElement.prototype.addGroup = function(parentGroup){
  var 
el = new jvm[this.classPrefix+'GroupElement']();

  if (
parentGroup) {
    
parentGroup.node.appendChild(el.node);
  } else {
    
this.node.appendChild(el.node);
  }
  
el.canvas this;
  return 
el;
};
/**
 * Abstract shape element. Shape element represents some visual vector or raster object.
 * @constructor
 * @param {String} name Tag name of the element.
 * @param {Object} config Set of parameters to initialize element with.
 * @param {Object} style Object with styles to set on element initialization.
 */
jvm.AbstractShapeElement = function(nameconfigstyle){
  
this.style style || {};
  
this.style.current this.style.current || {};
  
this.isHovered false;
  
this.isSelected false;
  
this.updateStyle();
};

/**
 * Set element's style.
 * @param {Object|String} property Could be string to set only one property or object to set several style properties at once.
 * @param {String} value Value to set in case only one property should be set.
 */
jvm.AbstractShapeElement.prototype.setStyle = function(propertyvalue){
  var 
styles = {};

  if (
typeof property === 'object') {
    
styles property;
  } else {
    
styles[property] = value;
  }
  
jvm.$.extend(this.style.currentstyles);
  
this.updateStyle();
};


jvm.AbstractShapeElement.prototype.updateStyle = function(){
  var 
attrs = {};

  
jvm.AbstractShapeElement.mergeStyles(attrsthis.style.initial);
  
jvm.AbstractShapeElement.mergeStyles(attrsthis.style.current);
  if (
this.isHovered) {
    
jvm.AbstractShapeElement.mergeStyles(attrsthis.style.hover);
  }
  if (
this.isSelected) {
    
jvm.AbstractShapeElement.mergeStyles(attrsthis.style.selected);
    if (
this.isHovered) {
      
jvm.AbstractShapeElement.mergeStyles(attrsthis.style.selectedHover);
    }
  }
  
this.set(attrs);
};

jvm.AbstractShapeElement.mergeStyles = function(stylesnewStyles){
  var 
key;

  
newStyles newStyles || {};
  for (
key in newStyles) {
    if (
newStyles[key] === null) {
      
delete styles[key];
    } else {
      
styles[key] = newStyles[key];
    }
  }
}
/**
 * Wrapper for SVG element.
 * @constructor
 * @extends jvm.AbstractElement
 * @param {String} name Tag name of the element
 * @param {Object} config Set of parameters to initialize element with
 */

jvm.SVGElement = function(nameconfig){
  
jvm.SVGElement.parentClass.apply(thisarguments);
}

jvm.inherits(jvm.SVGElementjvm.AbstractElement);

jvm.SVGElement.svgns "http://www.w3.org/2000/svg";

/**
 * Creates DOM element.
 * @param {String} tagName Name of element
 * @private
 * @returns DOMElement
 */
jvm.SVGElement.prototype.createElement = function( tagName ){
  return 
document.createElementNSjvm.SVGElement.svgnstagName );
};

/**
 * Adds CSS class for underlying DOM element.
 * @param {String} className Name of CSS class name
 */
jvm.SVGElement.prototype.addClass = function( className ){
  
this.node.setAttribute('class'className);
};

/**
 * Returns constructor for element by name prefixed with 'VML'.
 * @param {String} ctr Name of basic constructor to return
 * proper implementation for.
 * @returns Function
 * @private
 */
jvm.SVGElement.prototype.getElementCtr = function( ctr ){
  return 
jvm['SVG'+ctr];
};

jvm.SVGElement.prototype.getBBox = function(){
  return 
this.node.getBBox();
};
jvm.SVGGroupElement = function(){
  
jvm.SVGGroupElement.parentClass.call(this'g');
}

jvm.inherits(jvm.SVGGroupElementjvm.SVGElement);

jvm.SVGGroupElement.prototype.add = function(element){
  
this.node.appendChildelement.node );
};
jvm.SVGCanvasElement = function(containerwidthheight){
  
this.classPrefix 'SVG';
  
jvm.SVGCanvasElement.parentClass.call(this'svg');

  
this.defsElement = new jvm.SVGElement('defs');
  
this.node.appendChildthis.defsElement.node );

  
jvm.AbstractCanvasElement.apply(thisarguments);
}

jvm.inherits(jvm.SVGCanvasElementjvm.SVGElement);
jvm.mixin(jvm.SVGCanvasElementjvm.AbstractCanvasElement);

jvm.SVGCanvasElement.prototype.setSize = function(widthheight){
  
this.width width;
  
this.height height;
  
this.node.setAttribute('width'width);
  
this.node.setAttribute('height'height);
};

jvm.SVGCanvasElement.prototype.applyTransformParams = function(scaletransXtransY) {
  
this.scale scale;
  
this.transX transX;
  
this.transY transY;
  
this.rootElement.node.setAttribute('transform''scale('+scale+') translate('+transX+', '+transY+')');
};
jvm.SVGShapeElement = function(nameconfigstyle){
  
jvm.SVGShapeElement.parentClass.call(thisnameconfig);
  
jvm.AbstractShapeElement.apply(thisarguments);
};

jvm.inherits(jvm.SVGShapeElementjvm.SVGElement);
jvm.mixin(jvm.SVGShapeElementjvm.AbstractShapeElement);

jvm.SVGShapeElement.prototype.applyAttr = function(attrvalue){
  var 
patternEl,
      
imageEl,
      
that this;

  if (
attr === 'fill' && jvm.isImageUrl(value)) {
    if (!
jvm.SVGShapeElement.images[value]) {
      
jvm.whenImageLoaded(value).then(function(img){
        
imageEl = new jvm.SVGElement('image');
        
imageEl.node.setAttributeNS('http://www.w3.org/1999/xlink''href'value);
        
imageEl.applyAttr('x''0');
        
imageEl.applyAttr('y''0');
        
imageEl.applyAttr('width'img[0].width);
        
imageEl.applyAttr('height'img[0].height);

        
patternEl = new jvm.SVGElement('pattern');
        
patternEl.applyAttr('id''image'+jvm.SVGShapeElement.imageCounter);
        
patternEl.applyAttr('x'0);
        
patternEl.applyAttr('y'0);
        
patternEl.applyAttr('width'img[0].width 2);
        
patternEl.applyAttr('height'img[0].height 2);
        
patternEl.applyAttr('viewBox''0 0 '+img[0].width+' '+img[0].height);
        
patternEl.applyAttr('patternUnits''userSpaceOnUse');
        
patternEl.node.appendChildimageEl.node );

        
that.canvas.defsElement.node.appendChildpatternEl.node );

        
jvm.SVGShapeElement.images[value] = jvm.SVGShapeElement.imageCounter++;

        
that.applyAttr('fill''url(#image'+jvm.SVGShapeElement.images[value]+')');
      });
    } else {
      
this.applyAttr('fill''url(#image'+jvm.SVGShapeElement.images[value]+')');
    }
  } else {
    
jvm.SVGShapeElement.parentClass.prototype.applyAttr.apply(thisarguments);
  }
};

jvm.SVGShapeElement.imageCounter 1;
jvm.SVGShapeElement.images = {};jvm.SVGPathElement = function(configstyle){
  
jvm.SVGPathElement.parentClass.call(this'path'configstyle);
  
this.node.setAttribute('fill-rule''evenodd');
}

jvm.inherits(jvm.SVGPathElementjvm.SVGShapeElement);jvm.SVGCircleElement = function(configstyle){
  
jvm.SVGCircleElement.parentClass.call(this'circle'configstyle);
};

jvm.inherits(jvm.SVGCircleElementjvm.SVGShapeElement);jvm.SVGImageElement = function(configstyle){
  
jvm.SVGImageElement.parentClass.call(this'image'configstyle);
};

jvm.inherits(jvm.SVGImageElementjvm.SVGShapeElement);

jvm.SVGImageElement.prototype.applyAttr = function(attrvalue){
  var 
that this;

  if (
attr == 'image') {
    
jvm.whenImageLoaded(value).then(function(img){
      
that.node.setAttributeNS('http://www.w3.org/1999/xlink''href'value);
      
that.width img[0].width;
      
that.height img[0].height;
      
that.applyAttr('width'that.width);
      
that.applyAttr('height'that.height);

      
that.applyAttr('x'that.cx that.width 2);
      
that.applyAttr('y'that.cy that.height 2);

      
jvm.$(that.node).trigger('imageloaded', [img]);
    });
  } else if(
attr == 'cx') {
    
this.cx value;
    if (
this.width) {
      
this.applyAttr('x'value this.width 2);
    }
  } else if(
attr == 'cy') {
    
this.cy value;
    if (
this.height) {
      
this.applyAttr('y'value this.height 2);
    }
  } else {
    
jvm.SVGImageElement.parentClass.prototype.applyAttr.apply(thisarguments);
  }
};
jvm.SVGTextElement = function(configstyle){
  
jvm.SVGTextElement.parentClass.call(this'text'configstyle);
}

jvm.inherits(jvm.SVGTextElementjvm.SVGShapeElement);

jvm.SVGTextElement.prototype.applyAttr = function(attrvalue){
  if (
attr === 'text') {
    
this.node.textContent value;
  } else {
    
jvm.SVGTextElement.parentClass.prototype.applyAttr.apply(thisarguments);
  }
};
/**
 * Wrapper for VML element.
 * @constructor
 * @extends jvm.AbstractElement
 * @param {String} name Tag name of the element
 * @param {Object} config Set of parameters to initialize element with
 */

jvm.VMLElement = function(nameconfig){
  if (!
jvm.VMLElement.VMLInitialized) {
    
jvm.VMLElement.initializeVML();
  }

  
jvm.VMLElement.parentClass.apply(thisarguments);
};

jvm.inherits(jvm.VMLElementjvm.AbstractElement);

/**
 * Shows if VML was already initialized for the current document or not.
 * @static
 * @private
 * @type {Boolean}
 */
jvm.VMLElement.VMLInitialized false;

/**
 * Initializes VML handling before creating the first element
 * (adds CSS class and creates namespace). Adds one of two forms
 * of createElement method depending of support by browser.
 * @static
 * @private
 */

 // The following method of VML handling is borrowed from the
 // Raphael library by Dmitry Baranovsky.

jvm.VMLElement.initializeVML = function(){
  try {
    if (!
document.namespaces.rvml) {
      
document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
    }
    
/**
     * Creates DOM element.
     * @param {String} tagName Name of element
     * @private
     * @returns DOMElement
     */
    
jvm.VMLElement.prototype.createElement = function (tagName) {
      return 
document.createElement('<rvml:' tagName ' class="rvml">');
    };
  } catch (
e) {
    
/**
     * @private
     */
    
jvm.VMLElement.prototype.createElement = function (tagName) {
      return 
document.createElement('<' tagName ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
    };
  }
  
document.createStyleSheet().addRule(".rvml""behavior:url(#default#VML)");
  
jvm.VMLElement.VMLInitialized true;
};

/**
 * Returns constructor for element by name prefixed with 'VML'.
 * @param {String} ctr Name of basic constructor to return
 * proper implementation for.
 * @returns Function
 * @private
 */
jvm.VMLElement.prototype.getElementCtr = function( ctr ){
  return 
jvm['VML'+ctr];
};

/**
 * Adds CSS class for underlying DOM element.
 * @param {String} className Name of CSS class name
 */
jvm.VMLElement.prototype.addClass = function( className ){
  
jvm.$(this.node).addClass(className);
};

/**
 * Applies attribute value to the underlying DOM element.
 * @param {String} name Name of attribute
 * @param {Number|String} config Value of attribute to apply
 * @private
 */
jvm.VMLElement.prototype.applyAttr = function( attrvalue ){
  
this.node[attr] = value;
};

/**
 * Returns boundary box for the element.
 * @returns {Object} Boundary box with numeric fields: x, y, width, height
 * @override
 */
jvm.VMLElement.prototype.getBBox = function(){
  var 
node jvm.$(this.node);

  return {
    
xnode.position().left this.canvas.scale,
    
ynode.position().top this.canvas.scale,
    
widthnode.width() / this.canvas.scale,
    
heightnode.height() / this.canvas.scale
  
};
};
jvm.VMLGroupElement = function(){
  
jvm.VMLGroupElement.parentClass.call(this'group');

  
this.node.style.left '0px';
  
this.node.style.top '0px';
  
this.node.coordorigin "0 0";
};

jvm.inherits(jvm.VMLGroupElementjvm.VMLElement);

jvm.VMLGroupElement.prototype.add = function(element){
  
this.node.appendChildelement.node );
};
jvm.VMLCanvasElement = function(containerwidthheight){
  
this.classPrefix 'VML';
  
jvm.VMLCanvasElement.parentClass.call(this'group');
  
jvm.AbstractCanvasElement.apply(thisarguments);
  
this.node.style.position 'absolute';
};

jvm.inherits(jvm.VMLCanvasElementjvm.VMLElement);
jvm.mixin(jvm.VMLCanvasElementjvm.AbstractCanvasElement);

jvm.VMLCanvasElement.prototype.setSize = function(widthheight){
  var 
paths,
      
groups,
      
i,
      
l;

  
this.width width;
  
this.height height;
  
this.node.style.width width "px";
  
this.node.style.height height "px";
  
this.node.coordsize width+' '+height;
  
this.node.coordorigin "0 0";
  if (
this.rootElement) {
    
paths this.rootElement.node.getElementsByTagName('shape');
    for(
0paths.lengthli++) {
      
paths[i].coordsize width+' '+height;
      
paths[i].style.width width+'px';
      
paths[i].style.height height+'px';
    }
    
groups this.node.getElementsByTagName('group');
    for(
0groups.lengthli++) {
      
groups[i].coordsize width+' '+height;
      
groups[i].style.width width+'px';
      
groups[i].style.height height+'px';
    }
  }
};

jvm.VMLCanvasElement.prototype.applyTransformParams = function(scaletransXtransY) {
  
this.scale scale;
  
this.transX transX;
  
this.transY transY;
  
this.rootElement.node.coordorigin = (this.width-transX-this.width/100)+','+(this.height-transY-this.height/100);
  
this.rootElement.node.coordsize this.width/scale+','+this.height/scale;
};
jvm.VMLShapeElement = function(nameconfig){
  
jvm.VMLShapeElement.parentClass.call(thisnameconfig);

  
this.fillElement = new jvm.VMLElement('fill');
  
this.strokeElement = new jvm.VMLElement('stroke');
  
this.node.appendChild(this.fillElement.node);
  
this.node.appendChild(this.strokeElement.node);
  
this.node.stroked false;

  
jvm.AbstractShapeElement.apply(thisarguments);
};

jvm.inherits(jvm.VMLShapeElementjvm.VMLElement);
jvm.mixin(jvm.VMLShapeElementjvm.AbstractShapeElement);

jvm.VMLShapeElement.prototype.applyAttr = function(attrvalue){
  switch (
attr) {
    case 
'fill':
      
this.node.fillcolor value;
      break;
    case 
'fill-opacity':
      
this.fillElement.node.opacity Math.round(value*100)+'%';
      break;
    case 
'stroke':
      if (
value === 'none') {
        
this.node.stroked false;
      } else {
        
this.node.stroked true;
      }
      
this.node.strokecolor value;
      break;
    case 
'stroke-opacity':
      
this.strokeElement.node.opacity Math.round(value*100)+'%';
      break;
    case 
'stroke-width':
      if (
parseInt(value10) === 0) {
        
this.node.stroked false;
      } else {
        
this.node.stroked true;
      }
      
this.node.strokeweight value;
      break;
    case 
'd':
      
this.node.path jvm.VMLPathElement.pathSvgToVml(value);
      break;
    default:
      
jvm.VMLShapeElement.parentClass.prototype.applyAttr.apply(thisarguments);
  }
};
jvm.VMLPathElement = function(configstyle){
  var 
scale = new jvm.VMLElement('skew');

  
jvm.VMLPathElement.parentClass.call(this'shape'configstyle);

  
this.node.coordorigin "0 0";

  
scale.node.on true;
  
scale.node.matrix '0.01,0,0,0.01,0,0';
  
scale.node.offset '0,0';

  
this.node.appendChild(scale.node);
};

jvm.inherits(jvm.VMLPathElementjvm.VMLShapeElement);

jvm.VMLPathElement.prototype.applyAttr = function(attrvalue){
  if (
attr === 'd') {
    
this.node.path jvm.VMLPathElement.pathSvgToVml(value);
  } else {
    
jvm.VMLShapeElement.prototype.applyAttr.call(thisattrvalue);
  }
};

jvm.VMLPathElement.pathSvgToVml = function(path) {
  var 
cx 0cy 0ctrlxctrly;

  
path path.replace(/(-?d+)e(-?d+)/g'0');
  return 
path.replace(/([MmLlHhVvCcSs])s*((?:-?d*(?:.d+)?s*,?s*)+)/g, function(segmentlettercoordsindex){
    
coords coords.replace(/(d)-/g'$1,-')
            .
replace(/^s+/g'')
            .
replace(/s+$/g'')
            .
replace(/s+/g',').split(',');
    if (!
coords[0]) coords.shift();
    for (var 
i=0l=coords.lengthi<li++) {
      
coords[i] = Math.round(100*coords[i]);
    }
    switch (
letter) {
      case 
'm':
        
cx += coords[0];
        
cy += coords[1];
        return 
't'+coords.join(',');
      case 
'M':
        
cx coords[0];
        
cy coords[1];
        return 
'm'+coords.join(',');
      case 
'l':
        
cx += coords[0];
        
cy += coords[1];
        return 
'r'+coords.join(',');
      case 
'L':
        
cx coords[0];
        
cy coords[1];
        return 
'l'+coords.join(',');
      case 
'h':
        
cx += coords[0];
        return 
'r'+coords[0]+',0';
      case 
'H':
        
cx coords[0];
        return 
'l'+cx+','+cy;
      case 
'v':
        
cy += coords[0];
        return 
'r0,'+coords[0];
      case 
'V':
        
cy coords[0];
        return 
'l'+cx+','+cy;
      case 
'c':
        
ctrlx cx coords[coords.length-4];
        
ctrly cy coords[coords.length-3];
        
cx += coords[coords.length-2];
        
cy += coords[coords.length-1];
        return 
'v'+coords.join(',');
      case 
'C':
        
ctrlx coords[coords.length-4];
        
ctrly coords[coords.length-3];
        
cx coords[coords.length-2];
        
cy coords[coords.length-1];
        return 
'c'+coords.join(',');
      case 
's':
        
coords.unshift(cy-ctrly);
        
coords.unshift(cx-ctrlx);
        
ctrlx cx coords[coords.length-4];
        
ctrly cy coords[coords.length-3];
        
cx += coords[coords.length-2];
        
cy += coords[coords.length-1];
        return 
'v'+coords.join(',');
      case 
'S':
        
coords.unshift(cy+cy-ctrly);
        
coords.unshift(cx+cx-ctrlx);
        
ctrlx coords[coords.length-4];
        
ctrly coords[coords.length-3];
        
cx coords[coords.length-2];
        
cy coords[coords.length-1];
        return 
'c'+coords.join(',');
    }
    return 
'';
  }).
replace(/z/g'e');
};
jvm.VMLCircleElement = function(configstyle){
  
jvm.VMLCircleElement.parentClass.call(this'oval'configstyle);
};

jvm.inherits(jvm.VMLCircleElementjvm.VMLShapeElement);

jvm.VMLCircleElement.prototype.applyAttr = function(attrvalue){
  switch (
attr) {
    case 
'r':
      
this.node.style.width value*2+'px';
      
this.node.style.height value*2+'px';
      
this.applyAttr('cx'this.get('cx') || 0);
      
this.applyAttr('cy'this.get('cy') || 0);
      break;
    case 
'cx':
      if (!
value) return;
      
this.node.style.left value - (this.get('r') || 0) + 'px';
      break;
    case 
'cy':
      if (!
value) return;
      
this.node.style.top value - (this.get('r') || 0) + 'px';
      break;
    default:
      
jvm.VMLCircleElement.parentClass.prototype.applyAttr.call(thisattrvalue);
  }
};
/**
 * Class for vector images manipulations.
 * @constructor
 * @param {DOMElement} container to place canvas to
 * @param {Number} width
 * @param {Number} height
 */
jvm.VectorCanvas = function(containerwidthheight) {
  
this.mode window.SVGAngle 'svg' 'vml';

  if (
this.mode == 'svg') {
    
this.impl = new jvm.SVGCanvasElement(containerwidthheight);
  } else {
    
this.impl = new jvm.VMLCanvasElement(containerwidthheight);
  }
  
this.impl.mode this.mode;
  return 
this.impl;
};
jvm.SimpleScale = function(scale){
  
this.scale scale;
};

jvm.SimpleScale.prototype.getValue = function(value){
  return 
value;
};
jvm.OrdinalScale = function(scale){
  
this.scale scale;
};

jvm.OrdinalScale.prototype.getValue = function(value){
  return 
this.scale[value];
};

jvm.OrdinalScale.prototype.getTicks = function(){
  var 
ticks = [],
      
key;

  for (
key in this.scale) {
    
ticks.push({
      
labelkey,
      
valuethis.scale[key]
    });
  }

  return 
ticks;
};
jvm.NumericScale = function(scalenormalizeFunctionminValuemaxValue) {
  
this.scale = [];

  
normalizeFunction normalizeFunction || 'linear';

  if (
scalethis.setScale(scale);
  if (
normalizeFunctionthis.setNormalizeFunction(normalizeFunction);
  if (
typeof minValue !== 'undefined' this.setMin(minValue);
  if (
typeof maxValue !== 'undefined' this.setMax(maxValue);
};

jvm.NumericScale.prototype = {
  
setMin: function(min) {
    
this.clearMinValue min;
    if (
typeof this.normalize === 'function') {
      
this.minValue this.normalize(min);
    } else {
      
this.minValue min;
    }
  },

  
setMax: function(max) {
    
this.clearMaxValue max;
    if (
typeof this.normalize === 'function') {
      
this.maxValue this.normalize(max);
    } else {
      
this.maxValue max;
    }
  },

  
setScale: function(scale) {
    var 
i;

    
this.scale = [];
    for (
0scale.lengthi++) {
      
this.scale[i] = [scale[i]];
    }
  },

  
setNormalizeFunction: function(f) {
    if (
=== 'polynomial') {
      
this.normalize = function(value) {
        return 
Math.pow(value0.2);
      }
    } else if (
=== 'linear') {
      
delete this.normalize;
    } else {
      
this.normalize f;
    }
    
this.setMin(this.clearMinValue);
    
this.setMax(this.clearMaxValue);
  },

  
getValue: function(value) {
    var 
lengthes = [],
        
fullLength 0,
        
l,
        
0,
        
c;

    if (
typeof this.normalize === 'function') {
      
value this.normalize(value);
    }
    for (
0this.scale.length-1i++) {
      
this.vectorLength(this.vectorSubtract(this.scale[i+1], this.scale[i]));
      
lengthes.push(l);
      
fullLength += l;
    }

    
= (this.maxValue this.minValue) / fullLength;
    for (
i=0i<lengthes.lengthi++) {
      
lengthes[i] *= c;
    }

    
0;
    
value -= this.minValue;
    while (
value lengthes[i] >= 0) {
      
value -= lengthes[i];
      
i++;
    }

    if (
== this.scale.length 1) {
      
value this.vectorToNum(this.scale[i])
    } else {
      
value = (
        
this.vectorToNum(
          
this.vectorAdd(this.scale[i],
            
this.vectorMult(
              
this.vectorSubtract(this.scale[i+1], this.scale[i]),
              (
value) / (lengthes[i])
            )
          )
        )
      );
    }

    return 
value;
  },

  
vectorToNum: function(vector) {
    var 
num 0,
        
i;

    for (
0vector.lengthi++) {
      
num += Math.round(vector[i])*Math.pow(256vector.length-i-1);
    }
    return 
num;
  },

  
vectorSubtract: function(vector1vector2) {
    var 
vector = [],
        
i;

    for (
0vector1.lengthi++) {
      
vector[i] = vector1[i] - vector2[i];
    }
    return 
vector;
  },

  
vectorAdd: function(vector1vector2) {
    var 
vector = [],
        
i;

    for (
0vector1.lengthi++) {
      
vector[i] = vector1[i] + vector2[i];
    }
    return 
vector;
  },

  
vectorMult: function(vectornum) {
    var 
result = [],
        
i;

    for (
0vector.lengthi++) {
      
result[i] = vector[i] * num;
    }
    return 
result;
  },

  
vectorLength: function(vector) {
    var 
result 0,
        
i;
    for (
0vector.lengthi++) {
      
result += vector[i] * vector[i];
    }
    return 
Math.sqrt(result);
  },

  
/* Derived from d3 implementation https://github.com/mbostock/d3/blob/master/src/scale/linear.js#L94 */
  
getTicks: function(){
    var 
5,
        
extent = [this.clearMinValuethis.clearMaxValue],
        
span extent[1] - extent[0],
        
step Math.pow(10Math.floor(Math.log(span m) / Math.LN10)),
        
err span step,
        
ticks = [],
        
tick,
        
v;

    if (
err <= .15step *= 10;
    else if (
err <= .35step *= 5;
    else if (
err <= .75step *= 2;

    
extent[0] = Math.floor(extent[0] / step) * step;
    
extent[1] = Math.ceil(extent[1] / step) * step;

    
tick extent[0];
    while (
tick <= extent[1]) {
      if (
tick == extent[0]) {
        
this.clearMinValue;
      } else if (
tick == extent[1]) {
        
this.clearMaxValue;
      } else {
        
tick;
      }
      
ticks.push({
        
labeltick,
        
valuethis.getValue(v)
      });
      
tick += step;
    }

    return 
ticks;
  }
};
jvm.ColorScale = function(colorsnormalizeFunctionminValuemaxValue) {
  
jvm.ColorScale.parentClass.apply(thisarguments);
}

jvm.inherits(jvm.ColorScalejvm.NumericScale);

jvm.ColorScale.prototype.setScale = function(scale) {
  var 
i;

  for (
0scale.lengthi++) {
    
this.scale[i] = jvm.ColorScale.rgbToArray(scale[i]);
  }
};

jvm.ColorScale.prototype.getValue = function(value) {
  return 
jvm.ColorScale.numToRgb(jvm.ColorScale.parentClass.prototype.getValue.call(thisvalue));
};

jvm.ColorScale.arrayToRgb = function(ar) {
  var 
rgb '#',
      
d,
      
i;

  for (
0ar.lengthi++) {
    
ar[i].toString(16);
    
rgb += d.length == '0'+d;
  }
  return 
rgb;
};

jvm.ColorScale.numToRgb = function(num) {
  
num num.toString(16);

  while (
num.length 6) {
    
num '0' num;
  }

  return 
'#'+num;
};

jvm.ColorScale.rgbToArray = function(rgb) {
  
rgb rgb.substr(1);
  return [
parseInt(rgb.substr(02), 16), parseInt(rgb.substr(22), 16), parseInt(rgb.substr(42), 16)];
};
/**
 * Represents map legend.
 * @constructor
 * @param {Object} params Configuration parameters.
 * @param {String} params.cssClass Additional CSS class to apply to legend element.
 * @param {Boolean} params.vertical If <code>true</code> legend will be rendered as vertical.
 * @param {String} params.title Legend title.
 * @param {Function} params.labelRender Method to convert series values to legend labels.
 */
jvm.Legend = function(params) {
  
this.params params || {};
  
this.map this.params.map;
  
this.series this.params.series;
  
this.body jvm.$('<div/>');
  
this.body.addClass('jvectormap-legend');
  if (
this.params.cssClass) {
    
this.body.addClass(this.params.cssClass);
  }

  if (
params.vertical) {
    
this.map.legendCntVertical.appendthis.body );
  } else {
    
this.map.legendCntHorizontal.appendthis.body );
  }

  
this.render();
}

jvm.Legend.prototype.render = function(){
  var 
ticks this.series.scale.getTicks(),
      
i,
      
inner jvm.$('<div/>').addClass('jvectormap-legend-inner'),
      
tick,
      
sample,
      
label;

  
this.body.html('');
  if (
this.params.title) {
    
this.body.append(
      
jvm.$('<div/>').addClass('jvectormap-legend-title').html(this.params.title)
    );
  }
  
this.body.append(inner);

  for (
0ticks.lengthi++) {
    
tick jvm.$('<div/>').addClass('jvectormap-legend-tick');
    
sample jvm.$('<div/>').addClass('jvectormap-legend-tick-sample');

    switch (
this.series.params.attribute) {
      case 
'fill':
        if (
jvm.isImageUrl(ticks[i].value)) {
          
sample.css('background''url('+ticks[i].value+')');
        } else {
          
sample.css('background'ticks[i].value);
        }
        break;
      case 
'stroke':
        
sample.css('background'ticks[i].value);
        break;
      case 
'image':
        
sample.css('background''url('+ticks[i].value+') no-repeat center center');
        break;
      case 
'r':
        
jvm.$('<div/>').css({
          
'border-radius'ticks[i].value,
          
borderthis.map.params.markerStyle.initial['stroke-width']+'px '+
                  
this.map.params.markerStyle.initial['stroke']+' solid',
          
widthticks[i].value 'px',
          
heightticks[i].value 'px',
          
backgroundthis.map.params.markerStyle.initial['fill']
        }).
appendTo(sample);
        break;
    }
    
tick.appendsample );
    
label ticks[i].label;
    if (
this.params.labelRender) {
      
label this.params.labelRender(label);
    }
    
tick.appendjvm.$('<div>'+label+' </div>').addClass('jvectormap-legend-tick-text') );
    
inner.append(tick);
  }
  
inner.appendjvm.$('<div/>').css('clear''both') );
}
/**
 * Creates data series.
 * @constructor
 * @param {Object} params Parameters to initialize series with.
 * @param {Array} params.values The data set to visualize.
 * @param {String} params.attribute Numberic or color attribute to use for data visualization. This could be: <code>fill</code>, <code>stroke</code>, <code>fill-opacity</code>, <code>stroke-opacity</code> for markers and regions and <code>r</code> (radius) for markers only.
 * @param {Array} params.scale Values used to map a dimension of data to a visual representation. The first value sets visualization for minimum value from the data set and the last value sets visualization for the maximum value. There also could be intermidiate values. Default value is <code>['#C8EEFF', '#0071A4']</code>
 * @param {Function|String} params.normalizeFunction The function used to map input values to the provided scale. This parameter could be provided as function or one of the strings: <code>'linear'</code> or <code>'polynomial'</code>, while <code>'linear'</code> is used by default. The function provided takes value from the data set as an input and returns corresponding value from the scale.
 * @param {Number} params.min Minimum value of the data set. Could be calculated automatically if not provided.
 * @param {Number} params.min Maximum value of the data set. Could be calculated automatically if not provided.
 */
jvm.DataSeries = function(paramselementsmap) {
  var 
scaleConstructor;

  
params params || {};
  
params.attribute params.attribute || 'fill';

  
this.elements elements;
  
this.params params;
  
this.map map;

  if (
params.attributes) {
    
this.setAttributes(params.attributes);
  }

  if (
jvm.$.isArray(params.scale)) {
    
scaleConstructor = (params.attribute === 'fill' || params.attribute === 'stroke') ? jvm.ColorScale jvm.NumericScale;
    
this.scale = new scaleConstructor(params.scaleparams.normalizeFunctionparams.minparams.max);
  } else if (
params.scale) {
    
this.scale = new jvm.OrdinalScale(params.scale);
  } else {
    
this.scale = new jvm.SimpleScale(params.scale);
  }

  
this.values params.values || {};
  
this.setValues(this.values);

  if (
this.params.legend) {
    
this.legend = new jvm.Legend($.extend({
      
mapthis.map,
      
seriesthis
    
}, this.params.legend))
  }
};

jvm.DataSeries.prototype = {
  
setAttributes: function(keyattr){
    var 
attrs key,
        
code;

    if (
typeof key == 'string') {
      if (
this.elements[key]) {
        
this.elements[key].setStyle(this.params.attributeattr);
      }
    } else {
      for (
code in attrs) {
        if (
this.elements[code]) {
          
this.elements[code].element.setStyle(this.params.attributeattrs[code]);
        }
      }
    }
  },

  
/**
   * Set values for the data set.
   * @param {Object} values Object which maps codes of regions or markers to values.
   */
  
setValues: function(values) {
    var 
max = -Number.MAX_VALUE,
        
min Number.MAX_VALUE,
        
val,
        
cc,
        
attrs = {};

    if (!(
this.scale instanceof jvm.OrdinalScale) && !(this.scale instanceof jvm.SimpleScale)) {
      
// we have a color scale as an array
      
if (typeof this.params.min === 'undefined' || typeof this.params.max === 'undefined') {
        
// min and/or max are not defined, so calculate them
        
for (cc in values) {
          
val parseFloat(values[cc]);
          if (
val maxmax val;
          if (
val minmin val;
        }
      }

      if (
typeof this.params.min === 'undefined') {
        
this.scale.setMin(min);
        
this.params.min min;
      } else {
        
this.scale.setMin(this.params.min);
      }

      if (
typeof this.params.max === 'undefined') {
        
this.scale.setMax(max);
        
this.params.max max;
      } else {
        
this.scale.setMax(this.params.max);
      }

      for (
cc in values) {
        if (
cc != 'indexOf') {
          
val parseFloat(values[cc]);
          if (!
isNaN(val)) {
            
attrs[cc] = this.scale.getValue(val);
          } else {
            
attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
          }
        }
      }
    } else {
      for (
cc in values) {
        if (
values[cc]) {
          
attrs[cc] = this.scale.getValue(values[cc]);
        } else {
          
attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
        }
      }
    }

    
this.setAttributes(attrs);
    
jvm.$.extend(this.valuesvalues);
  },

  
clear: function(){
    var 
key,
        
attrs = {};

    for (
key in this.values) {
      if (
this.elements[key]) {
        
attrs[key] = this.elements[key].element.shape.style.initial[this.params.attribute];
      }
    }
    
this.setAttributes(attrs);
    
this.values = {};
  },

  
/**
   * Set scale of the data series.
   * @param {Array} scale Values representing scale.
   */
  
setScale: function(scale) {
    
this.scale.setScale(scale);
    if (
this.values) {
      
this.setValues(this.values);
    }
  },

  
/**
   * Set normalize function of the data series.
   * @param {Function|String} normilizeFunction.
   */
  
setNormalizeFunction: function(f) {
    
this.scale.setNormalizeFunction(f);
    if (
this.values) {
      
this.setValues(this.values);
    }
  }
};
/**
 * Contains methods for transforming point on sphere to
 * Cartesian coordinates using various projections.
 * @class
 */
jvm.Proj = {
  
degRad180 Math.PI,
  
radDegMath.PI 180,
  
radius6381372,

  
sgn: function(n){
    if (
0) {
      return 
1;
    } else if (
0) {
      return -
1;
    } else {
      return 
n;
    }
  },

  
/**
   * Converts point on sphere to the Cartesian coordinates using Miller projection
   * @param {Number} lat Latitude in degrees
   * @param {Number} lng Longitude in degrees
   * @param {Number} c Central meridian in degrees
   */
  
mill: function(latlngc){
    return {
      
xthis.radius * (lng c) * this.radDeg,
      
y: - this.radius Math.log(Math.tan((45 0.4 lat) * this.radDeg)) / 0.8
    
};
  },

  
/**
   * Inverse function of mill()
   * Converts Cartesian coordinates to point on sphere using Miller projection
   * @param {Number} x X of point in Cartesian system as integer
   * @param {Number} y Y of point in Cartesian system as integer
   * @param {Number} c Central meridian in degrees
   */
  
mill_inv: function(xyc){
    return {
      
lat: (2.5 Math.atan(Math.exp(0.8 this.radius)) - Math.PI 8) * this.degRad,
      
lng: (this.radDeg this.radius) * this.degRad
    
};
  },

  
/**
   * Converts point on sphere to the Cartesian coordinates using Mercator projection
   * @param {Number} lat Latitude in degrees
   * @param {Number} lng Longitude in degrees
   * @param {Number} c Central meridian in degrees
   */
  
merc: function(latlngc){
    return {
      
xthis.radius * (lng c) * this.radDeg,
      
y: - this.radius Math.log(Math.tan(Math.PI lat Math.PI 360))
    };
  },

  
/**
   * Inverse function of merc()
   * Converts Cartesian coordinates to point on sphere using Mercator projection
   * @param {Number} x X of point in Cartesian system as integer
   * @param {Number} y Y of point in Cartesian system as integer
   * @param {Number} c Central meridian in degrees
   */
  
merc_inv: function(xyc){
    return {
      
lat: (Math.atan(Math.exp(this.radius)) - Math.PI 2) * this.degRad,
      
lng: (this.radDeg this.radius) * this.degRad
    
};
  },

  
/**
   * Converts point on sphere to the Cartesian coordinates using Albers Equal-Area Conic
   * projection
   * @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
   * @param {Number} lat Latitude in degrees
   * @param {Number} lng Longitude in degrees
   * @param {Number} c Central meridian in degrees
   */
  
aea: function(latlngc){
    var 
fi0 0,
        
lambda0 this.radDeg,
        
fi1 29.5 this.radDeg,
        
fi2 45.5 this.radDeg,
        
fi lat this.radDeg,
        
lambda lng this.radDeg,
        
= (Math.sin(fi1)+Math.sin(fi2)) / 2,
        
Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
        
theta n*(lambda-lambda0),
        
ro Math.sqrt(C-2*n*Math.sin(fi))/n,
        
ro0 Math.sqrt(C-2*n*Math.sin(fi0))/n;

    return {
      
xro Math.sin(theta) * this.radius,
      
y: - (ro0 ro Math.cos(theta)) * this.radius
    
};
  },

  
/**
   * Converts Cartesian coordinates to the point on sphere using Albers Equal-Area Conic
   * projection
   * @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
   * @param {Number} x X of point in Cartesian system as integer
   * @param {Number} y Y of point in Cartesian system as integer
   * @param {Number} c Central meridian in degrees
   */
  
aea_inv: function(xCoordyCoordc){
    var 
xCoord this.radius,
        
yCoord this.radius,
        
fi0 0,
        
lambda0 this.radDeg,
        
fi1 29.5 this.radDeg,
        
fi2 45.5 this.radDeg,
        
= (Math.sin(fi1)+Math.sin(fi2)) / 2,
        
Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
        
ro0 Math.sqrt(C-2*n*Math.sin(fi0))/n,
        
ro Math.sqrt(x*x+(ro0-y)*(ro0-y)),
        
theta Math.atan/ (ro0 y) );

    return {
      
lat: (Math.asin((ro ro n) / (n))) * this.degRad,
      
lng: (lambda0 theta n) * this.degRad
    
};
  },

  
/**
   * Converts point on sphere to the Cartesian coordinates using Lambert conformal
   * conic projection
   * @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
   * @param {Number} lat Latitude in degrees
   * @param {Number} lng Longitude in degrees
   * @param {Number} c Central meridian in degrees
   */
  
lcc: function(latlngc){
    var 
fi0 0,
        
lambda0 this.radDeg,
        
lambda lng this.radDeg,
        
fi1 33 this.radDeg,
        
fi2 45 this.radDeg,
        
fi lat this.radDeg,
        
Math.logMath.cos(fi1) * (Math.cos(fi2)) ) / Math.logMath.tanMath.PI fi2 2) * (Math.tanMath.PI fi1 2) ) ),
        
= ( Math.cos(fi1) * Math.powMath.tanMath.PI fi1 ), ) ) / n,
        
ro Math.powMath.tanMath.PI fi ), ),
        
ro0 Math.powMath.tanMath.PI fi0 ), );

    return {
      
xro Math.sin* (lambda lambda0) ) * this.radius,
      
y: - (ro0 ro Math.cos* (lambda lambda0) ) ) * this.radius
    
};
  },

  
/**
   * Converts Cartesian coordinates to the point on sphere using Lambert conformal conic
   * projection
   * @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
   * @param {Number} x X of point in Cartesian system as integer
   * @param {Number} y Y of point in Cartesian system as integer
   * @param {Number} c Central meridian in degrees
   */
  
lcc_inv: function(xCoordyCoordc){
    var 
xCoord this.radius,
        
yCoord this.radius,
        
fi0 0,
        
lambda0 this.radDeg,
        
fi1 33 this.radDeg,
        
fi2 45 this.radDeg,
        
Math.logMath.cos(fi1) * (Math.cos(fi2)) ) / Math.logMath.tanMath.PI fi2 2) * (Math.tanMath.PI fi1 2) ) ),
        
= ( Math.cos(fi1) * Math.powMath.tanMath.PI fi1 ), ) ) / n,
        
ro0 Math.powMath.tanMath.PI fi0 ), ),
        
ro this.sgn(n) * Math.sqrt(x*x+(ro0-y)*(ro0-y)),
        
theta Math.atan/ (ro0 y) );

    return {
      
lat: (Math.atan(Math.pow(F/ro1/n)) - Math.PI 2) * this.degRad,
      
lng: (lambda0 theta n) * this.degRad
    
};
  }
};
jvm.MapObject = function(config){};

jvm.MapObject.prototype.getLabelText = function(key){
  var 
text;

  if (
this.config.label) {
    if (
typeof this.config.label.render === 'function') {
      
text this.config.label.render(key);
    } else {
      
text key;
    }
  } else {
    
text null;
  }
  return 
text;
}

jvm.MapObject.prototype.getLabelOffsets = function(key){
  var 
offsets;

  if (
this.config.label) {
    if (
typeof this.config.label.offsets === 'function') {
      
offsets this.config.label.offsets(key);
    } else if (
typeof this.config.label.offsets === 'object') {
      
offsets this.config.label.offsets[key];
    }
  }
  return 
offsets || [00];
}

/**
 * Set hovered state to the element. Hovered state means mouse cursor is over element. Styles will be updates respectively.
 * @param {Boolean} isHovered <code>true</code> to make element hovered, <code>false</code> otherwise.
 */
jvm.MapObject.prototype.setHovered = function(isHovered){
  if (
this.isHovered !== isHovered) {
    
this.isHovered isHovered;
    
this.shape.isHovered isHovered;
    
this.shape.updateStyle();
    if (
this.label) {
      
this.label.isHovered isHovered;
      
this.label.updateStyle();
    }
  }
};

/**
 * Set selected state to the element. Styles will be updates respectively.
 * @param {Boolean} isSelected <code>true</code> to make element selected, <code>false</code> otherwise.
 */
jvm.MapObject.prototype.setSelected = function(isSelected){
  if (
this.isSelected !== isSelected) {
    
this.isSelected isSelected;
    
this.shape.isSelected isSelected;
    
this.shape.updateStyle();
    if (
this.label) {
      
this.label.isSelected isSelected;
      
this.label.updateStyle();
    }
    
jvm.$(this.shape).trigger('selected', [isSelected]);
  }
};

jvm.MapObject.prototype.setStyle = function(){
    
this.shape.setStyle.apply(this.shapearguments);
};

jvm.MapObject.prototype.remove = function(){
  
this.shape.remove();
  if (
this.label) {
    
this.label.remove();
  }
};
jvm.Region = function(config){
  var 
bbox,
      
text,
      
offsets,
      
labelDx,
      
labelDy;

  
this.config config;
  
this.map this.config.map;

  
this.shape config.canvas.addPath({
    
dconfig.path,
    
'data-code'config.code
  
}, config.styleconfig.canvas.rootElement);
  
this.shape.addClass('jvectormap-region jvectormap-element');

  
bbox this.shape.getBBox();

  
text this.getLabelText(config.code);
  if (
this.config.label && text) {
    
offsets this.getLabelOffsets(config.code);
    
this.labelX bbox.bbox.width offsets[0];
    
this.labelY bbox.bbox.height offsets[1];
    
this.label config.canvas.addText({
      
texttext,
      
'text-anchor''middle',
      
'alignment-baseline''central',
      
xthis.labelX,
      
ythis.labelY,
      
'data-code'config.code
    
}, config.labelStyleconfig.labelsGroup);
    
this.label.addClass('jvectormap-region jvectormap-element');
  }
};

jvm.inherits(jvm.Regionjvm.MapObject);

jvm.Region.prototype.updateLabelPosition = function(){
  if (
this.label) {
    
this.label.set({
      
xthis.labelX this.map.scale this.map.transX this.map.scale,
      
ythis.labelY this.map.scale this.map.transY this.map.scale
    
});
  }
};
jvm.Marker = function(config){
  var 
text,
      
offsets;

  
this.config config;
  
this.map this.config.map;

  
this.isImage = !!this.config.style.initial.image;
  
this.createShape();

  
text this.getLabelText(config.index);
  if (
this.config.label && text) {
    
this.offsets this.getLabelOffsets(config.index);
    
this.labelX config.cx this.map.scale this.map.transX;
    
this.labelY config.cy this.map.scale this.map.transY;
    
this.label config.canvas.addText({
      
texttext,
      
'data-index'config.index,
      
dy"0.6ex",
      
xthis.labelX,
      
ythis.labelY
    
}, config.labelStyleconfig.labelsGroup);

    
this.label.addClass('jvectormap-marker jvectormap-element');
  }
};

jvm.inherits(jvm.Markerjvm.MapObject);

jvm.Marker.prototype.createShape = function(){
  var 
that this;

  if (
this.shape) {
    
this.shape.remove();
  }
  
this.shape this.config.canvas[this.isImage 'addImage' 'addCircle']({
    
"data-index"this.config.index,
    
cxthis.config.cx,
    
cythis.config.cy
  
}, this.config.stylethis.config.group);

  
this.shape.addClass('jvectormap-marker jvectormap-element');

  if (
this.isImage) {
    
jvm.$(this.shape.node).on('imageloaded', function(){
      
that.updateLabelPosition();
    });
  }
};

jvm.Marker.prototype.updateLabelPosition = function(){
  if (
this.label) {
    
this.label.set({
      
xthis.labelX this.map.scale this.offsets[0] +
         
this.map.transX this.map.scale + (this.isImage ? (this.shape.width || 0) / this.shape.properties.r),
      
ythis.labelY this.map.scale this.map.transY this.map.scale this.offsets[1]
    });
  }
};

jvm.Marker.prototype.setStyle = function(propertyvalue){
  var 
isImage;

  
jvm.Marker.parentClass.prototype.setStyle.apply(thisarguments);

  if (
property === 'r') {
    
this.updateLabelPosition();
  }

  
isImage = !!this.shape.get('image');
  if (
isImage != this.isImage) {
    
this.isImage isImage;
    
this.config.style jvm.$.extend(true, {}, this.shape.style);
    
this.createShape();
  }
};
/**
 * Creates map, draws paths, binds events.
 * @constructor
 * @param {Object} params Parameters to initialize map with.
 * @param {String} params.map Name of the map in the format <code>territory_proj_lang</code> where <code>territory</code> is a unique code or name of the territory which the map represents (ISO 3166 standard is used where possible), <code>proj</code> is a name of projection used to generate representation of the map on the plane (projections are named according to the conventions of proj4 utility) and <code>lang</code> is a code of the language, used for the names of regions.
 * @param {String} params.backgroundColor Background color of the map in CSS format.
 * @param {Boolean} params.zoomOnScroll When set to true map could be zoomed using mouse scroll. Default value is <code>true</code>.
 * @param {Boolean} params.zoomOnScrollSpeed Mouse scroll speed. Number from 1 to 10. Default value is <code>3</code>.
 * @param {Boolean} params.panOnDrag When set to true, the map pans when being dragged. Default value is <code>true</code>.
 * @param {Number} params.zoomMax Indicates the maximum zoom ratio which could be reached zooming the map. Default value is <code>8</code>.
 * @param {Number} params.zoomMin Indicates the minimum zoom ratio which could be reached zooming the map. Default value is <code>1</code>.
 * @param {Number} params.zoomStep Indicates the multiplier used to zoom map with +/- buttons. Default value is <code>1.6</code>.
 * @param {Boolean} params.zoomAnimate Indicates whether or not to animate changing of map zoom with zoom buttons.
 * @param {Boolean} params.regionsSelectable When set to true regions of the map could be selected. Default value is <code>false</code>.
 * @param {Boolean} params.regionsSelectableOne Allow only one region to be selected at the moment. Default value is <code>false</code>.
 * @param {Boolean} params.markersSelectable When set to true markers on the map could be selected. Default value is <code>false</code>.
 * @param {Boolean} params.markersSelectableOne Allow only one marker to be selected at the moment. Default value is <code>false</code>.
 * @param {Object} params.regionStyle Set the styles for the map's regions. Each region or marker has four states: <code>initial</code> (default state), <code>hover</code> (when the mouse cursor is over the region or marker), <code>selected</code> (when region or marker is selected), <code>selectedHover</code> (when the mouse cursor is over the region or marker and it's selected simultaneously). Styles could be set for each of this states. Default value for that parameter is:
<pre>{
  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: {
  }
}</pre>
* @param {Object} params.regionLabelStyle Set the styles for the regions' labels. Each region or marker has four states: <code>initial</code> (default state), <code>hover</code> (when the mouse cursor is over the region or marker), <code>selected</code> (when region or marker is selected), <code>selectedHover</code> (when the mouse cursor is over the region or marker and it's selected simultaneously). Styles could be set for each of this states. Default value for that parameter is:
<pre>{
  initial: {
    'font-family': 'Verdana',
    'font-size': '12',
    'font-weight': 'bold',
    cursor: 'default',
    fill: 'black'
  },
  hover: {
    cursor: 'pointer'
  }
}</pre>
 * @param {Object} params.markerStyle Set the styles for the map's markers. Any parameter suitable for <code>regionStyle</code> could be used as well as numeric parameter <code>r</code> to set the marker's radius. Default value for that parameter is:
<pre>{
  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: {
  }
}</pre>
 * @param {Object} params.markerLabelStyle Set the styles for the markers' labels. Default value for that parameter is:
<pre>{
  initial: {
    'font-family': 'Verdana',
    'font-size': '12',
    'font-weight': 'bold',
    cursor: 'default',
    fill: 'black'
  },
  hover: {
    cursor: 'pointer'
  }
}</pre>
 * @param {Object|Array} params.markers Set of markers to add to the map during initialization. In case of array is provided, codes of markers will be set as string representations of array indexes. Each marker is represented by <code>latLng</code> (array of two numeric values), <code>name</code> (string which will be show on marker's tip) and any marker styles.
 * @param {Object} params.series Object with two keys: <code>markers</code> and <code>regions</code>. Each of which is an array of series configs to be applied to the respective map elements. See <a href="jvm.DataSeries.html">DataSeries</a> description for a list of parameters available.
 * @param {Object|String} params.focusOn This parameter sets the initial position and scale of the map viewport. See <code>setFocus</code> docuemntation for possible parameters.
 * @param {Object} params.labels Defines parameters for rendering static labels. Object could contain two keys: <code>regions</code> and <code>markers</code>. Each key value defines configuration object with the following possible options:
<ul>
  <li><code>render {Function}</code> - defines method for converting region code or marker index to actual label value.</li>
  <li><code>offsets {Object|Function}</code> - provides method or object which could be used to define label offset by region code or marker index.</li>
</ul>
<b>Plase note: static labels feature is not supported in Internet Explorer 8 and below.</b>
 * @param {Array|Object|String} params.selectedRegions Set initially selected regions.
 * @param {Array|Object|String} params.selectedMarkers Set initially selected markers.
 * @param {Function} params.onRegionTipShow <code>(Event e, Object tip, String code)</code> Will be called right before the region tip is going to be shown.
 * @param {Function} params.onRegionOver <code>(Event e, String code)</code> Will be called on region mouse over event.
 * @param {Function} params.onRegionOut <code>(Event e, String code)</code> Will be called on region mouse out event.
 * @param {Function} params.onRegionClick <code>(Event e, String code)</code> Will be called on region click event.
 * @param {Function} params.onRegionSelected <code>(Event e, String code, Boolean isSelected, Array selectedRegions)</code> Will be called when region is (de)selected. <code>isSelected</code> parameter of the callback indicates whether region is selected or not. <code>selectedRegions</code> contains codes of all currently selected regions.
 * @param {Function} params.onMarkerTipShow <code>(Event e, Object tip, String code)</code> Will be called right before the marker tip is going to be shown.
 * @param {Function} params.onMarkerOver <code>(Event e, String code)</code> Will be called on marker mouse over event.
 * @param {Function} params.onMarkerOut <code>(Event e, String code)</code> Will be called on marker mouse out event.
 * @param {Function} params.onMarkerClick <code>(Event e, String code)</code> Will be called on marker click event.
 * @param {Function} params.onMarkerSelected <code>(Event e, String code, Boolean isSelected, Array selectedMarkers)</code> Will be called when marker is (de)selected. <code>isSelected</code> parameter of the callback indicates whether marker is selected or not. <code>selectedMarkers</code> contains codes of all currently selected markers.
 * @param {Function} params.onViewportChange <code>(Event e, Number scale)</code> Triggered when the map's viewport is changed (map was panned or zoomed).
 */
jvm.Map = function(params) {
  var 
map this,
      
e;

  
this.params jvm.$.extend(true, {}, jvm.Map.defaultParamsparams);

  if (!
jvm.Map.maps[this.params.map]) {
    throw new 
Error('Attempt to use map which was not loaded: '+this.params.map);
  }

  
this.mapData jvm.Map.maps[this.params.map];
  
this.markers = {};
  
this.regions = {};
  
this.regionsColors = {};
  
this.regionsData = {};

  
this.container jvm.$('<div>').addClass('jvectormap-container');
  if (
this.params.container) {
    
this.params.container.appendthis.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.widththis.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 = {regionthis.params.focusOn};
    } else if (
jvm.$.isArray(this.params.focusOn)) {
      
this.params.focusOn = {regionsthis.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.$('<div/>').addClass('jvectormap-legend-cnt jvectormap-legend-cnt-h');
  
this.legendCntVertical jvm.$('<div/>').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 = {
  
transX0,
  
transY0,
  
scale1,
  
baseTransX0,
  
baseTransY0,
  
baseScale1,

  
width0,
  
height0,

  
/**
   * 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) / (this.baseScale);
    } else {
      
this.baseScale this.width this.defaultWidth;
      
this.baseTransY Math.abs(this.height this.defaultHeight this.baseScale) / (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.widththis.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 (
0this.series[key].lengthi++) {
        
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) / (this.scale);
      
minTransX = (this.width this.defaultWidth this.scale) / (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) / (this.scale);
      
minTransY = (this.height this.defaultHeight this.scale) / (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.scalethis.transXthis.transY);

    if (
this.markers) {
      
this.repositionMarkers();
    }

    
this.repositionLabels();

    
this.container.trigger('viewportChange', [this.scale/this.baseScalethis.transXthis.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(eventdeltadeltaXdeltaY) {
        var 
offset jvm.$(map.container).offset(),
            
centerX event.pageX offset.left,
            
centerY event.pageY offset.top,
            
zoomStep Math.pow(map.params.zoomOnScrollSpeed 1000event.deltaFactor event.deltaY);

        
map.tip.hide();

        
map.setScale(map.scale zoomStepcenterXcenterY);
        
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].pageX2) +
                
Math.pow(touches[0].pageY touches[1].pageY2)
              ) / 
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].pageX2) +
                
Math.pow(touches[0].pageY touches[1].pageY2)
              );
            }
          }

          
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') === -'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.tipcode]);
        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') === -'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.$('<div/>').addClass('jvectormap-zoomin').text('+').appendTo(this.container);
    
jvm.$('<div/>').addClass('jvectormap-zoomout').html('&#x2212;').appendTo(this.container);

    
this.container.find('.jvectormap-zoomin').click(function(){
      
map.setScale(map.scale map.params.zoomStepmap.width 2map.height 2falsemap.params.zoomAnimate);
    });
    
this.container.find('.jvectormap-zoomout').click(function(){
      
map.setScale(map.scale map.params.zoomStepmap.width 2map.height 2falsemap.params.zoomAnimate);
    });
  },

  
createTip: function(){
    var 
map this;

    
this.tip jvm.$('<div/>').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;
      }

      
map.tip.css({
        
leftleft,
        
toptop
      
});
    });
  },

  
setScale: function(scaleanchorXanchorYisCenteredanimate) {
    var 
viewportChangeEvent jvm.$.Event('zoom.jvectormap'),
        
interval,
        
that this,
        
0,
        
count Math.abs(Math.round((scale this.scale) * 60 Math.max(scalethis.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(){
        
+= 1;
        
that.scale scaleStart scaleDiff i;
        
that.transX = (transXStart transXDiff i) / that.scale;
        
that.transY = (transYStart transYDiff i) / that.scale;
        
that.applyTransform();
        if (
== 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 (
0codes.lengthi++) {
        if (
this.regions[codes[i]]) {
          
itemBbox this.regions[codes[i]].element.shape.getBBox();
          if (
itemBbox) {
            if (
typeof bbox == 'undefined') {
              
bbox itemBbox;
            } else {
              
newBbox = {
                
xMath.min(bbox.xitemBbox.x),
                
yMath.min(bbox.yitemBbox.y),
                
widthMath.max(bbox.bbox.widthitemBbox.itemBbox.width) - Math.min(bbox.xitemBbox.x),
                
heightMath.max(bbox.bbox.heightitemBbox.itemBbox.height) - Math.min(bbox.yitemBbox.y)
              }
              
bbox newBbox;
            }
          }
        }
      }
      return 
this.setScale(
        
Math.min(this.width bbox.widththis.height bbox.height),
        - (
bbox.bbox.width 2),
        - (
bbox.bbox.height 2),
        
true,
        
config.animate
      
);
    } else {
      if (
config.lat && config.lng) {
        
point this.latLngToPoint(config.latconfig.lng);
        
config.this.transX point.this.scale;
        
config.this.transY point.this.scale;
      } else if (
config.&& config.y) {
        
config.*= -this.defaultWidth;
        
config.*= -this.defaultHeight;
      }
      return 
this.setScale(config.scale this.baseScaleconfig.xconfig.ytrueconfig.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(typekeys){
    var 
i;

    if (
typeof keys != 'object') {
      
keys = [keys];
    }

    if (
jvm.$.isArray(keys)) {
      for (
0keys.lengthi++) {
        
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 <code>String</code> or <code>Array</code> the region(s) with the corresponding code(s) will be selected. If <code>Object</code> 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 <code>String</code> or <code>Array</code> the marker(s) with the corresponding code(s) will be selected. If <code>Object</code> 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 (
0selected.lengthi++) {
      
select[selected[i]] = false;
    };

    
this.setSelected(typeselect);
  },

  
/**
   * 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({
        
mapthis,
        
paththis.mapData.paths[key].path,
        
codekey,
        
stylejvm.$.extend(true, {}, this.params.regionStyle),
        
labelStylejvm.$.extend(true, {}, this.params.regionLabelStyle),
        
canvasthis.canvas,
        
labelsGroupthis.regionLabelsGroup,
        
labelthis.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.regions) : null
      
});

      
jvm.$(region.shape).bind('selected', function(eisSelected){
        
map.container.trigger('regionSelected.jvectormap', [jvm.$(this.node).attr('data-code'), isSelectedmap.getSelectedRegions()]);
      });
      
this.regions[key] = {
        
elementregion,
        
configthis.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 (
0markersArray.lengthi++) {
        
markers[i] = markersArray[i];
      }
    }

    for (
i in markers) {
      
markerConfig markers[i] instanceof Array ? {latLngmarkers[i]} : markers[i];
      
point this.getMarkerPositionmarkerConfig );

      if (
point !== false) {
        
marker = new jvm.Marker({
          
mapthis,
          
stylejvm.$.extend(true, {}, this.params.markerStyle, {initialmarkerConfig.style || {}}),
          
labelStylejvm.$.extend(true, {}, this.params.markerLabelStyle),
          
indexi,
          
cxpoint.x,
          
cypoint.y,
          
groupthis.markersGroup,
          
canvasthis.canvas,
          
labelsGroupthis.markerLabelsGroup,
          
labelthis.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.markers) : null
        
});

        
jvm.$(marker.shape).bind('selected', function(eisSelected){
          
map.container.trigger('markerSelected.jvectormap', [jvm.$(this.node).attr('data-index'), isSelectedmap.getSelectedMarkers()]);
        });
        if (
this.markers[i]) {
          
this.removeMarkers([i]);
        }
        
this.markers[i] = {elementmarkerconfigmarkerConfig};
      }
    }
  },

  
repositionMarkers: function() {
    var 
i,
        
point;

    for (
i in this.markers) {
      
point this.getMarkerPositionthis.markers[i].config );
      if (
point !== false) {
        
this.markers[i].element.setStyle({cxpoint.xcypoint.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(thismarkerConfig.latLng || [00]);
    } else {
      return {
        
xmarkerConfig.coords[0]*this.scale this.transX*this.scale,
        
ymarkerConfig.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(keymarkerseriesData){
    var 
markers = {},
        
data = [],
        
values,
        
i,
        
seriesData seriesData || [];

    
markers[key] = marker;

    for (
0seriesData.lengthi++) {
      
values = {};
      if (
typeof seriesData[i] !== 'undefined') {
        
values[key] = seriesData[i];
      }
      
data.push(values);
    }
    
this.addMarkers(markersdata);
  },

  
/**
   * 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(markersseriesData){
    var 
i;

    
seriesData seriesData || [];

    
this.createMarkers(markers);
    for (
0seriesData.lengthi++) {
      
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 (
0markers.lengthi++) {
      
this.markersmarkers[i] ].element.remove();
      
delete this.markersmarkers[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(latlng) {
    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](latlngcentralMeridian);

    
inset this.getInsetForPoint(point.xpoint.y);
    if (
inset) {
      
bbox inset.bbox;

      
point.= (point.bbox[0].x) / (bbox[1].bbox[0].x) * inset.width this.scale;
      
point.= (point.bbox[0].y) / (bbox[1].bbox[0].y) * inset.height this.scale;

      return {
        
xpoint.this.transX*this.scale inset.left*this.scale,
        
ypoint.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(xy) {
    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 (
0insets.lengthi++) {
      
inset insets[i];
      
bbox inset.bbox;

      
nx - (this.transX*this.scale inset.left*this.scale);
      
ny - (this.transY*this.scale inset.top*this.scale);

      
nx = (nx / (inset.width this.scale)) * (bbox[1].bbox[0].x) + bbox[0].x;
      
ny = (ny / (inset.height this.scale)) * (bbox[1].bbox[0].y) + bbox[0].y;

      if (
nx bbox[0].&& nx bbox[1].&& ny bbox[0].&& ny bbox[1].y) {
        return 
jvm.Proj[proj.type '_inv'](nx, -nycentralMeridian);
      }
    }

    return 
false;
  },

  
getInsetForPoint: function(xy){
    var 
insets jvm.Map.maps[this.params.map].insets,
        
i,
        
bbox;

    for (
0insets.lengthi++) {
      
bbox insets[i].bbox;
      if (
bbox[0].&& bbox[1].&& bbox[0].&& bbox[1].y) {
        return 
insets[i];
      }
    }
  },

  
createSeries: function(){
    var 
i,
        
key;

    
this.series = {
      
markers: [],
      
regions: []
    };

    for (
key in this.params.series) {
      for (
0this.params.series[key].lengthi++) {
        
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',
  
zoomButtonstrue,
  
zoomOnScrolltrue,
  
zoomOnScrollSpeed3,
  
panOnDragtrue,
  
zoomMax8,
  
zoomMin1,
  
zoomStep1.6,
  
zoomAnimatetrue,
  
regionsSelectablefalse,
  
markersSelectablefalse,
  
bindTouchEventstrue,
  
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,
      
r5
    
},
    
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 <a href="./jvm-map/">jvm.Map</a> for more information.
 * @param {Function} params.mapNameByCode Function go generate map name by region code. Default value is:
<pre>
function(code, multiMap) {
  return code.toLowerCase()+'_'+
         multiMap.defaultProjection+'_en';
}
</pre>
 * @param {Function} params.mapUrlByCode Function to generate map url by region code. Default value is:
<pre>
function(code, multiMap){
  return 'jquery-jvectormap-data-'+
         code.toLowerCase()+'-'+
         multiMap.defaultProjection+'-en.js';
}
</pre>
 */
jvm.MultiMap = function(params) {
  var 
that this;

  
this.maps = {};
  
this.params jvm.$.extend(true, {}, jvm.MultiMap.defaultParamsparams);
  
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.mapthis.params.main) ];
  
this.defaultProjection this.history[0].mapData.projection.type;
  
this.mapsLoaded = {};

  
this.params.container.css({position'relative'});
  
this.backButton jvm.$('<div/>').addClass('jvectormap-goback').text('Back').appendTo(this.params.container);
  
this.backButton.hide();
  
this.backButton.click(function(){
    
that.goBack();
  });

  
this.spinner jvm.$('<div/>').addClass('jvectormap-spinner').appendTo(this.params.container);
  
this.spinner.hide();
};

jvm.MultiMap.prototype = {
  
addMap: function(nameconfig){
    var 
cnt jvm.$('<div/>').css({
      
width'100%',
      
height'100%'
    
});

    
this.params.container.append(cnt);

    
this.maps[name] = new jvm.Map(jvm.$.extend(config, {containercnt}));
    if (
this.params.maxLevel config.multiMapLevel) {
      
this.maps[name].container.on('regionClick.jvectormap', {scopethis}, function(ecode){
        var 
multimap e.data.scope,
            
mapName multimap.params.mapNameByCode(codemultimap);

        if (!
multimap.drillDownPromise || multimap.drillDownPromise.state() !== 'pending') {
          
multimap.drillDown(mapNamecode);
        }
      });
    }


    return 
this.maps[name];
  },

  
downloadMap: function(code){
    var 
that this,
        
deferred jvm.$.Deferred();

    if (!
this.mapsLoaded[code]) {
      
jvm.$.get(this.params.mapUrlByCode(codethis)).then(function(){
        
that.mapsLoaded[code] = true;
        
deferred.resolve();
      }, function(){
        
deferred.reject();
      });
    } else {
      
deferred.resolve();
    }
    return 
deferred;
  },

  
drillDown: function(namecode){
    var 
currentMap this.history[this.history.length 1],
        
that this,
        
focusPromise currentMap.setFocus({regioncodeanimatetrue}),
        
downloadPromise this.downloadMap(code);

    
focusPromise.then(function(){
      if (
downloadPromise.state() === 'pending') {
        
that.spinner.show();
      }
    });
    
downloadPromise.always(function(){
      
that.spinner.hide();
    });
    
this.drillDownPromise jvm.$.when(downloadPromisefocusPromise);
    
this.drillDownPromise.then(function(){
      
currentMap.params.container.hide();
      if (!
that.maps[name]) {
        
that.addMap(name, {mapnamemultiMapLevelcurrentMap.params.multiMapLevel 1});
      } else {
        
that.maps[name].params.container.show();
      }
      
that.history.pushthat.maps[name] );
      
that.backButton.show();
    });
  },

  
goBack: function(){
    var 
currentMap this.history.pop(),
        
prevMap this.history[this.history.length 1],
        
that this;

    
currentMap.setFocus({scale1x0.5y0.5animatetrue}).then(function(){
      
currentMap.params.container.hide();
      
prevMap.params.container.show();
      
prevMap.updateSize();
      if (
that.history.length === 1) {
        
that.backButton.hide();
      }
      
prevMap.setFocus({scale1x0.5y0.5animatetrue});
    });
  }
};

jvm.MultiMap.defaultParams = {
  
mapNameByCode: function(codemultiMap){
    return 
code.toLowerCase()+'_'+multiMap.defaultProjection+'_en';
  },
  
mapUrlByCode: function(codemultiMap){
    return 
'jquery-jvectormap-data-'+code.toLowerCase()+'-'+multiMap.defaultProjection+'-en.js';
  }
}
?>
Онлайн: 1
Реклама