PageSourceSearch

https://mdscripts.com/js/jquery-jvectormap-2.0.4.min.js

js mdscripts.com collected 2026-10-04 02:39:42 UTC 106,282 bytes, 3,375 lines download raw bytes

vendor: 25,764 bytes, lines 1-862
1/**
2 * jVectorMap version 2.0.4
3 *
4 * Copyright 2011-2014, Kirill Lebedev
5 *
6 */
7
8(function( $ ){
9  var apiParams = {
10        set: {
11          colors: 1,
12          values: 1,
13          backgroundColor: 1,
14          scaleColors: 1,
15          normalizeFunction: 1,
16          focus: 1
17        },
18        get: {
19          selectedRegions: 1,
20          selectedMarkers: 1,
21          mapObject: 1,
22          regionName: 1
23        }
24      };
25
26  $.fn.vectorMap = function(options) {
27    var map,
28        methodName,
29        map = this.children('.jvectormap-container').data('mapObject');
30
31    if (options === 'addMap') {
32      jvm.Map.maps[arguments[1]] = arguments[2];
33    } else if ((options === 'set' || options === 'get') && apiParams[options][arguments[1]]) {
34      methodName = arguments[1].charAt(0).toUpperCase()+arguments[1].substr(1);
35      return map[options+methodName].apply(map, Array.prototype.slice.call(arguments, 2));
36    } else {
37      options = options || {};
38      options.container = this;
39      map = new jvm.Map(options);
40    }
41
42    return this;
43  };
44})( jQuery );
45/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
46 * Licensed under the MIT License (LICENSE.txt).
47 *
48 * Version: 3.1.9
49 *
50 * Requires: jQuery 1.2.2+
51 */
52
53(function (factory) {
54    if ( typeof define === 'function' && define.amd ) {
55        // AMD. Register as an anonymous module.
56        define(['jquery'], factory);
57    } else if (typeof exports === 'object') {
58        // Node/CommonJS style for Browserify
59        module.exports = factory;
60    } else {
61        // Browser globals
62        factory(jQuery);
63    }
64}(function ($) {
65
66    var toFix  = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
67        toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
68                    ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
69        slice  = Array.prototype.slice,
70        nullLowestDeltaTimeout, lowestDelta;
71
72    if ( $.event.fixHooks ) {
73        for ( var i = toFix.length; i; ) {
74            $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
75        }
76    }
77
78    var special = $.event.special.mousewheel = {
79        version: '3.1.9',
80
81        setup: function() {
82            if ( this.addEventListener ) {
83                for ( var i = toBind.length; i; ) {
84                    this.addEventListener( toBind[--i], handler, false );
85                }
86            } else {
87                this.onmousewheel = handler;
88            }
89            // Store the line height and page height for this particular element
90            $.data(this, 'mousewheel-line-height', special.getLineHeight(this));
91            $.data(this, 'mousewheel-page-height', special.getPageHeight(this));
92        },
93
94        teardown: function() {
95            if ( this.removeEventListener ) {
96                for ( var i = toBind.length; i; ) {
97                    this.removeEventListener( toBind[--i], handler, false );
98                }
99            } else {
100                this.onmousewheel = null;
101            }
102        },
103
104        getLineHeight: function(elem) {
105            return parseInt($(elem)['offsetParent' in $.fn ? 'offsetParent' : 'parent']().css('fontSize'), 10);
106        },
107
108        getPageHeight: function(elem) {
109            return $(elem).height();
110        },
111
112        settings: {
113            adjustOldDeltas: true
114        }
115    };
116
117    $.fn.extend({
118        mousewheel: function(fn) {
119            return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
120        },
121
122        unmousewheel: function(fn) {
123            return this.unbind('mousewheel', fn);
124        }
125    });
126
127
128    function handler(event) {
129        var orgEvent   = event || window.event,
130            args       = slice.call(arguments, 1),
131            delta      = 0,
132            deltaX     = 0,
133            deltaY     = 0,
134            absDelta   = 0;
135        event = $.event.fix(orgEvent);
136        event.type = 'mousewheel';
137
138        // Old school scrollwheel delta
139        if ( 'detail'      in orgEvent ) { deltaY = orgEvent.detail * -1;      }
140        if ( 'wheelDelta'  in orgEvent ) { deltaY = orgEvent.wheelDelta;       }
141        if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY;      }
142        if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
143
144        // Firefox < 17 horizontal scrolling related to DOMMouseScroll event
145        if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
146            deltaX = deltaY * -1;
147            deltaY = 0;
148        }
149
150        // Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
151        delta = deltaY === 0 ? deltaX : deltaY;
152
153        // New school wheel delta (wheel event)
154        if ( 'deltaY' in orgEvent ) {
155            deltaY = orgEvent.deltaY * -1;
156            delta  = deltaY;
157        }
158        if ( 'deltaX' in orgEvent ) {
159            deltaX = orgEvent.deltaX;
160            if ( deltaY === 0 ) { delta  = deltaX * -1; }
161        }
162
163        // No change actually happened, no reason to go any further
164        if ( deltaY === 0 && deltaX === 0 ) { return; }
165
166        // Need to convert lines and pages to pixels if we aren't already in pixels
167        // There are three delta modes:
168        //   * deltaMode 0 is by pixels, nothing to do
169        //   * deltaMode 1 is by lines
170        //   * deltaMode 2 is by pages
171        if ( orgEvent.deltaMode === 1 ) {
172            var lineHeight = $.data(this, 'mousewheel-line-height');
173            delta  *= lineHeight;
174            deltaY *= lineHeight;
175            deltaX *= lineHeight;
176        } else if ( orgEvent.deltaMode === 2 ) {
177            var pageHeight = $.data(this, 'mousewheel-page-height');
178            delta  *= pageHeight;
179            deltaY *= pageHeight;
180            deltaX *= pageHeight;
181        }
182
183        // Store lowest absolute delta to normalize the delta values
184        absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
185
186        if ( !lowestDelta || absDelta < lowestDelta ) {
187            lowestDelta = absDelta;
188
189            // Adjust older deltas if necessary
190            if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
191                lowestDelta /= 40;
192            }
193        }
194
195        // Adjust older deltas if necessary
196        if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
197            // Divide all the things by 40!
198            delta  /= 40;
199            deltaX /= 40;
200            deltaY /= 40;
201        }
202
203        // Get a whole, normalized value for the deltas
204        delta  = Math[ delta  >= 1 ? 'floor' : 'ceil' ](delta  / lowestDelta);
205        deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
206        deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
207
208        // Add information to the event object
209        event.deltaX = deltaX;
210        event.deltaY = deltaY;
211        event.deltaFactor = lowestDelta;
212        // Go ahead and set deltaMode to 0 since we converted to pixels
213        // Although this is a little odd since we overwrite the deltaX/Y
214        // properties with normalized deltas.
215        event.deltaMode = 0;
216
217        // Add event and delta to the front of the arguments
218        args.unshift(event, delta, deltaX, deltaY);
219
220        // Clearout lowestDelta after sometime to better
221        // handle multiple device types that give different
222        // a different lowestDelta
223        // Ex: trackpad = 3 and mouse wheel = 120
224        if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
225        nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
226
227        return ($.event.dispatch || $.event.handle).apply(this, args);
228    }
229
230    function nullLowestDelta() {
231        lowestDelta = null;
232    }
233
234    function shouldAdjustOldDeltas(orgEvent, absDelta) {
235        // If this is an older event and the delta is divisable by 120,
236        // then we are assuming that the browser is treating this as an
237        // older mouse wheel event and that we should divide the deltas
238        // by 40 to try and get a more usable deltaFactor.
239        // Side note, this actually impacts the reported scroll distance
240        // in older browsers and can cause scrolling to be slower than native.
241        // Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
242        return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
243    }
244
245}));/**
246 * @namespace jvm Holds core methods and classes used by jVectorMap.
247 */
248var jvm = {
249
250  /**
251   * Inherits child's prototype from the parent's one.
252   * @param {Function} child
253   * @param {Function} parent
254   */
255  inherits: function(child, parent) {
256    function temp() {}
257    temp.prototype = parent.prototype;
258    child.prototype = new temp();
259    child.prototype.constructor = child;
260    child.parentClass = parent;
261  },
262
263  /**
264   * Mixes in methods from the source constructor to the target one.
265   * @param {Function} target
266   * @param {Function} source
267   */
268  mixin: function(target, source){
269    var prop;
270
271    for (prop in source.prototype) {
272      if (source.prototype.hasOwnProperty(prop)) {
273        target.prototype[prop] = source.prototype[prop];
274      }
275    }
276  },
277
278  min: function(values){
279    var min = Number.MAX_VALUE,
280        i;
281
282    if (values instanceof Array) {
283      for (i = 0; i < values.length; i++) {
284        if (values[i] < min) {
285          min = values[i];
286        }
287      }
288    } else {
289      for (i in values) {
290        if (values[i] < min) {
291          min = values[i];
292        }
293      }
294    }
295    return min;
296  },
297
298  max: function(values){
299    var max = Number.MIN_VALUE,
300        i;
301
302    if (values instanceof Array) {
303      for (i = 0; i < values.length; i++) {
304        if (values[i] > max) {
305          max = values[i];
306        }
307      }
308    } else {
309      for (i in values) {
310        if (values[i] > max) {
311          max = values[i];
312        }
313      }
314    }
315    return max;
316  },
317
318  keys: function(object){
319    var keys = [],
320        key;
321
322    for (key in object) {
323      keys.push(key);
324    }
325    return keys;
326  },
327
328  values: function(object){
329    var values = [],
330        key,
331        i;
332
333    for (i = 0; i < arguments.length; i++) {
334      object = arguments[i];
335      for (key in object) {
336        values.push(object[key]);
337      }
338    }
339    return values;
340  },
341
342  whenImageLoaded: function(url){
343    var deferred = new jvm.$.Deferred(),
344        img = jvm.$('<img/>');
345
346    img.error(function(){
347      deferred.reject();
348    }).load(function(){
349      deferred.resolve(img);
350    });
351    img.attr('src', url);
352
353    return deferred;
354  },
355
356  isImageUrl: function(s){
357    return /\.\w{3,4}$/.test(s);
358  }
359};
360
361jvm.$ = jQuery;
362
363/**
364 * indexOf polyfill for IE < 9
365 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
366 */
367if (!Array.prototype.indexOf) {
368  Array.prototype.indexOf = function (searchElement, fromIndex) {
369
370    var k;
371
372    // 1. Let O be the result of calling ToObject passing
373    //    the this value as the argument.
374    if (this == null) {
375      throw new TypeError('"this" is null or not defined');
376    }
377
378    var O = Object(this);
379
380    // 2. Let lenValue be the result of calling the Get
381    //    internal method of O with the argument "length".
382    // 3. Let len be ToUint32(lenValue).
383    var len = O.length >>> 0;
384
385    // 4. If len is 0, return -1.
386    if (len === 0) {
387      return -1;
388    }
389
390    // 5. If argument fromIndex was passed let n be
391    //    ToInteger(fromIndex); else let n be 0.
392    var n = +fromIndex || 0;
393
394    if (Math.abs(n) === Infinity) {
395      n = 0;
396    }
397
398    // 6. If n >= len, return -1.
399    if (n >= len) {
400      return -1;
401    }
402
403    // 7. If n >= 0, then Let k be n.
404    // 8. Else, n<0, Let k be len - abs(n).
405    //    If k is less than 0, then let k be 0.
406    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
407
408    // 9. Repeat, while k < len
409    while (k < len) {
410      // a. Let Pk be ToString(k).
411      //   This is implicit for LHS operands of the in operator
412      // b. Let kPresent be the result of calling the
413      //    HasProperty internal method of O with argument Pk.
414      //   This step can be combined with c
415      // c. If kPresent is true, then
416      //    i.  Let elementK be the result of calling the Get
417      //        internal method of O with the argument ToString(k).
418      //   ii.  Let same be the result of applying the
419      //        Strict Equality Comparison Algorithm to
420      //        searchElement and elementK.
421      //  iii.  If same is true, return k.
422      if (k in O && O[k] === searchElement) {
423        return k;
424      }
425      k++;
426    }
427    return -1;
428  };
429}/**
430 * Basic wrapper for DOM element.
431 * @constructor
432 * @param {String} name Tag name of the element
433 * @param {Object} config Set of parameters to initialize element with
434 */
435jvm.AbstractElement = function(name, config){
436  /**
437   * Underlying DOM element
438   * @type {DOMElement}
439   * @private
440   */
441  this.node = this.createElement(name);
442
443  /**
444   * Name of underlying element
445   * @type {String}
446   * @private
447   */
448  this.name = name;
449
450  /**
451   * Internal store of attributes
452   * @type {Object}
453   * @private
454   */
455  this.properties = {};
456
457  if (config) {
458    this.set(config);
459  }
460};
461
462/**
463 * Set attribute of the underlying DOM element.
464 * @param {String} name Name of attribute
465 * @param {Number|String} config Set of parameters to initialize element with
466 */
467jvm.AbstractElement.prototype.set = function(property, value){
468  var key;
469
470  if (typeof property === 'object') {
471    for (key in property) {
472      this.properties[key] = property[key];
473      this.applyAttr(key, property[key]);
474    }
475  } else {
476    this.properties[property] = value;
477    this.applyAttr(property, value);
478  }
479};
480
481/**
482 * Returns value of attribute.
483 * @param {String} name Name of attribute
484 */
485jvm.AbstractElement.prototype.get = function(property){
486  return this.properties[property];
487};
488
489/**
490 * Applies attribute value to the underlying DOM element.
491 * @param {String} name Name of attribute
492 * @param {Number|String} config Value of attribute to apply
493 * @private
494 */
495jvm.AbstractElement.prototype.applyAttr = function(property, value){
496  this.node.setAttribute(property, value);
497};
498
499jvm.AbstractElement.prototype.remove = function(){
500  jvm.$(this.node).remove();
501};/**
502 * Implements abstract vector canvas.
503 * @constructor
504 * @param {HTMLElement} container Container to put element to.
505 * @param {Number} width Width of canvas.
506 * @param {Number} height Height of canvas.
507 */
508jvm.AbstractCanvasElement = function(container, width, height){
509  this.container = container;
510  this.setSize(width, height);
511  this.rootElement = new jvm[this.classPrefix+'GroupElement']();
512  this.node.appendChild( this.rootElement.node );
513  this.container.appendChild(this.node);
514}
515
516/**
517 * Add element to the certain group inside of the canvas.
518 * @param {HTMLElement} element Element to add to canvas.
519 * @param {HTMLElement} group Group to add element into or into root group if not provided.
520 */
521jvm.AbstractCanvasElement.prototype.add = function(element, group){
522  group = group || this.rootElement;
523  group.add(element);
524  element.canvas = this;
525}
526
527/**
528 * Create path and add it to the canvas.
529 * @param {Object} config Parameters of path to create.
530 * @param {Object} style Styles of the path to create.
531 * @param {HTMLElement} group Group to add path into.
532 */
533jvm.AbstractCanvasElement.prototype.addPath = function(config, style, group){
534  var el = new jvm[this.classPrefix+'PathElement'](config, style);
535
536  this.add(el, group);
537  return el;
538};
539
540/**
541 * Create circle and add it to the canvas.
542 * @param {Object} config Parameters of path to create.
543 * @param {Object} style Styles of the path to create.
544 * @param {HTMLElement} group Group to add circle into.
545 */
546jvm.AbstractCanvasElement.prototype.addCircle = function(config, style, group){
547  var el = new jvm[this.classPrefix+'CircleElement'](config, style);
548
549  this.add(el, group);
550  return el;
551};
552
553/**
554 * Create circle and add it to the canvas.
555 * @param {Object} config Parameters of path to create.
556 * @param {Object} style Styles of the path to create.
557 * @param {HTMLElement} group Group to add circle into.
558 */
559jvm.AbstractCanvasElement.prototype.addImage = function(config, style, group){
560  var el = new jvm[this.classPrefix+'ImageElement'](config, style);
561
562  this.add(el, group);
563  return el;
564};
565
566/**
567 * Create text and add it to the canvas.
568 * @param {Object} config Parameters of path to create.
569 * @param {Object} style Styles of the path to create.
570 * @param {HTMLElement} group Group to add circle into.
571 */
572jvm.AbstractCanvasElement.prototype.addText = function(config, style, group){
573  var el = new jvm[this.classPrefix+'TextElement'](config, style);
574
575  this.add(el, group);
576  return el;
577};
578
579/**
580 * Add group to the another group inside of the canvas.
581 * @param {HTMLElement} group Group to add circle into or root group if not provided.
582 */
583jvm.AbstractCanvasElement.prototype.addGroup = function(parentGroup){
584  var el = new jvm[this.classPrefix+'GroupElement']();
585
586  if (parentGroup) {
587    parentGroup.node.appendChild(el.node);
588  } else {
589    this.node.appendChild(el.node);
590  }
591  el.canvas = this;
592  return el;
593};/**
594 * Abstract shape element. Shape element represents some visual vector or raster object.
595 * @constructor
596 * @param {String} name Tag name of the element.
597 * @param {Object} config Set of parameters to initialize element with.
598 * @param {Object} style Object with styles to set on element initialization.
599 */
600jvm.AbstractShapeElement = function(name, config, style){
601  this.style = style || {};
602  this.style.current = this.style.current || {};
603  this.isHovered = false;
604  this.isSelected = false;
605  this.updateStyle();
606};
607
608/**
609 * Set element's style.
610 * @param {Object|String} property Could be string to set only one property or object to set several style properties at once.
611 * @param {String} value Value to set in case only one property should be set.
612 */
613jvm.AbstractShapeElement.prototype.setStyle = function(property, value){
614  var styles = {};
615
616  if (typeof property === 'object') {
617    styles = property;
618  } else {
619    styles[property] = value;
620  }
621  jvm.$.extend(this.style.current, styles);
622  this.updateStyle();
623};
624
625
626jvm.AbstractShapeElement.prototype.updateStyle = function(){
627  var attrs = {};
628
629  jvm.AbstractShapeElement.mergeStyles(attrs, this.style.initial);
630  jvm.AbstractShapeElement.mergeStyles(attrs, this.style.current);
631  if (this.isHovered) {
632    jvm.AbstractShapeElement.mergeStyles(attrs, this.style.hover);
633  }
634  if (this.isSelected) {
635    jvm.AbstractShapeElement.mergeStyles(attrs, this.style.selected);
636    if (this.isHovered) {
637      jvm.AbstractShapeElement.mergeStyles(attrs, this.style.selectedHover);
638    }
639  }
640  this.set(attrs);
641};
642
643jvm.AbstractShapeElement.mergeStyles = function(styles, newStyles){
644  var key;
645
646  newStyles = newStyles || {};
647  for (key in newStyles) {
648    if (newStyles[key] === null) {
649      delete styles[key];
650    } else {
651      styles[key] = newStyles[key];
652    }
653  }
654}/**
655 * Wrapper for SVG element.
656 * @constructor
657 * @extends jvm.AbstractElement
658 * @param {String} name Tag name of the element
659 * @param {Object} config Set of parameters to initialize element with
660 */
661
662jvm.SVGElement = function(name, config){
663  jvm.SVGElement.parentClass.apply(this, arguments);
664}
665
666jvm.inherits(jvm.SVGElement, jvm.AbstractElement);
667
668jvm.SVGElement.svgns = "http://www.w3.org/2000/svg";
669
670/**
671 * Creates DOM element.
672 * @param {String} tagName Name of element
673 * @private
674 * @returns DOMElement
675 */
676jvm.SVGElement.prototype.createElement = function( tagName ){
677  return document.createElementNS( jvm.SVGElement.svgns, tagName );
678};
679
680/**
681 * Adds CSS class for underlying DOM element.
682 * @param {String} className Name of CSS class name
683 */
684jvm.SVGElement.prototype.addClass = function( className ){
685  this.node.setAttribute('class', className);
686};
687
688/**
689 * Returns constructor for element by name prefixed with 'VML'.
690 * @param {String} ctr Name of basic constructor to return
691 * proper implementation for.
692 * @returns Function
693 * @private
694 */
695jvm.SVGElement.prototype.getElementCtr = function( ctr ){
696  return jvm['SVG'+ctr];
697};
698
699jvm.SVGElement.prototype.getBBox = function(){
700  return this.node.getBBox();
701};jvm.SVGGroupElement = function(){
702  jvm.SVGGroupElement.parentClass.call(this, 'g');
703}
704
705jvm.inherits(jvm.SVGGroupElement, jvm.SVGElement);
706
707jvm.SVGGroupElement.prototype.add = function(element){
708  this.node.appendChild( element.node );
709};jvm.SVGCanvasElement = function(container, width, height){
710  this.classPrefix = 'SVG';
711  jvm.SVGCanvasElement.parentClass.call(this, 'svg');
712
713  this.defsElement = new jvm.SVGElement('defs');
714  this.node.appendChild( this.defsElement.node );
715
716  jvm.AbstractCanvasElement.apply(this, arguments);
717}
718
719jvm.inherits(jvm.SVGCanvasElement, jvm.SVGElement);
720jvm.mixin(jvm.SVGCanvasElement, jvm.AbstractCanvasElement);
721
722jvm.SVGCanvasElement.prototype.setSize = function(width, height){
723  this.width = width;
724  this.height = height;
725  this.node.setAttribute('width', width);
726  this.node.setAttribute('height', height);
727};
728
729jvm.SVGCanvasElement.prototype.applyTransformParams = function(scale, transX, transY) {
730  this.scale = scale;
731  this.transX = transX;
732  this.transY = transY;
733  this.rootElement.node.setAttribute('transform', 'scale('+scale+') translate('+transX+', '+transY+')');
734};jvm.SVGShapeElement = function(name, config, style){
735  jvm.SVGShapeElement.parentClass.call(this, name, config);
736  jvm.AbstractShapeElement.apply(this, arguments);
737};
738
739jvm.inherits(jvm.SVGShapeElement, jvm.SVGElement);
740jvm.mixin(jvm.SVGShapeElement, jvm.AbstractShapeElement);
741
742jvm.SVGShapeElement.prototype.applyAttr = function(attr, value){
743  var patternEl,
744      imageEl,
745      that = this;
746
747  if (attr === 'fill' && jvm.isImageUrl(value)) {
748    if (!jvm.SVGShapeElement.images[value]) {
749      jvm.whenImageLoaded(value).then(function(img){
750        imageEl = new jvm.SVGElement('image');
751        imageEl.node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
752        imageEl.applyAttr('x', '0');
753        imageEl.applyAttr('y', '0');
754        imageEl.applyAttr('width', img[0].width);
755        imageEl.applyAttr('height', img[0].height);
756
757        patternEl = new jvm.SVGElement('pattern');
758        patternEl.applyAttr('id', 'image'+jvm.SVGShapeElement.imageCounter);
759        patternEl.applyAttr('x', 0);
760        patternEl.applyAttr('y', 0);
761        patternEl.applyAttr('width', img[0].width / 2);
762        patternEl.applyAttr('height', img[0].height / 2);
763        patternEl.applyAttr('viewBox', '0 0 '+img[0].width+' '+img[0].height);
764        patternEl.applyAttr('patternUnits', 'userSpaceOnUse');
765        patternEl.node.appendChild( imageEl.node );
766
767        that.canvas.defsElement.node.appendChild( patternEl.node );
768
769        jvm.SVGShapeElement.images[value] = jvm.SVGShapeElement.imageCounter++;
770
771        that.applyAttr('fill', 'url(#image'+jvm.SVGShapeElement.images[value]+')');
772      });
773    } else {
774      this.applyAttr('fill', 'url(#image'+jvm.SVGShapeElement.images[value]+')');
775    }
776  } else {
777    jvm.SVGShapeElement.parentClass.prototype.applyAttr.apply(this, arguments);
778  }
779};
780
781jvm.SVGShapeElement.imageCounter = 1;
782jvm.SVGShapeElement.images = {};jvm.SVGPathElement = function(config, style){
783  jvm.SVGPathElement.parentClass.call(this, 'path', config, style);
784  this.node.setAttribute('fill-rule', 'evenodd');
785}
786
787jvm.inherits(jvm.SVGPathElement, jvm.SVGShapeElement);jvm.SVGCircleElement = function(config, style){
788  jvm.SVGCircleElement.parentClass.call(this, 'circle', config, style);
789};
790
791jvm.inherits(jvm.SVGCircleElement, jvm.SVGShapeElement);jvm.SVGImageElement = function(config, style){
792  jvm.SVGImageElement.parentClass.call(this, 'image', config, style);
793};
794
795jvm.inherits(jvm.SVGImageElement, jvm.SVGShapeElement);
796
797jvm.SVGImageElement.prototype.applyAttr = function(attr, value){
798  var that = this;
799
800  if (attr == 'image') {
801    jvm.whenImageLoaded(value).then(function(img){
802      that.node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
803      that.width = img[0].width;
804      that.height = img[0].height;
805      that.applyAttr('width', that.width);
806      that.applyAttr('height', that.height);
807
808      that.applyAttr('x', that.cx - that.width / 2);
809      that.applyAttr('y', that.cy - that.height / 2);
810
811      jvm.$(that.node).trigger('imageloaded', [img]);
812    });
813  } else if(attr == 'cx') {
814    this.cx = value;
815    if (this.width) {
816      this.applyAttr('x', value - this.width / 2);
817    }
818  } else if(attr == 'cy') {
819    this.cy = value;
820    if (this.height) {
821      this.applyAttr('y', value - this.height / 2);
822    }
823  } else {
824    jvm.SVGImageElement.parentClass.prototype.applyAttr.apply(this, arguments);
825  }
826};jvm.SVGTextElement = function(config, style){
827  jvm.SVGTextElement.parentClass.call(this, 'text', config, style);
828}
829
830jvm.inherits(jvm.SVGTextElement, jvm.SVGShapeElement);
831
832jvm.SVGTextElement.prototype.applyAttr = function(attr, value){
833  if (attr === 'text') {
834    this.node.textContent = value;
835  } else {
836    jvm.SVGTextElement.parentClass.prototype.applyAttr.apply(this, arguments);
837  }
838};/**
839 * Wrapper for VML element.
840 * @constructor
841 * @extends jvm.AbstractElement
842 * @param {String} name Tag name of the element
843 * @param {Object} config Set of parameters to initialize element with
844 */
845
846jvm.VMLElement = function(name, config){
847  if (!jvm.VMLElement.VMLInitialized) {
848    jvm.VMLElement.initializeVML();
849  }
850
851  jvm.VMLElement.parentClass.apply(this, arguments);
852};
853
854jvm.inherits(jvm.VMLElement, jvm.AbstractElement);
855
856/**
857 * Shows if VML was already initialized for the current document or not.
858 * @static
859 * @private
860 * @type {Boolean}
861 */
862jvm.VMLElement.VMLInitialized = false;
863
864/**
865 * Initializes VML handling before creating the first element
866 * (adds CSS class and creates namespace). Adds one of two forms
867 * of createElement method depending of support by browser.
868 * @static
869 * @private
870 */
871
872 // The following method of VML handling is borrowed from the
873 // Raphael library by Dmitry Baranovsky.
874
875jvm.VMLElement.initializeVML = function(){
876  try {
877    if (!document.namespaces.rvml) {
878      document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
879    }
880    /**
881     * Creates DOM element.
882     * @param {String} tagName Name of element
883     * @private
884     * @returns DOMElement
885     */
886    jvm.VMLElement.prototype.createElement = function (tagName) {
887      return document.createElement('<rvml:' + tagName + ' class="rvml">');
888    };
889  } catch (e) {
890    /**
891     * @private
892     */
893    jvm.VMLElement.prototype.createElement = function (tagName) {
894      return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
895    };
896  }
897  document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
898  jvm.VMLElement.VMLInitialized = true;
899};
900
901/**
902 * Returns constructor for element by name prefixed with 'VML'.
903 * @param {String} ctr Name of basic constructor to return
904 * proper implementation for.
905 * @returns Function
906 * @private
907 */
908jvm.VMLElement.prototype.getElementCtr = function( ctr ){
vendor: 17,160 bytes, lines 909-1528
909  return jvm['VML'+ctr];
910};
911
912/**
913 * Adds CSS class for underlying DOM element.
914 * @param {String} className Name of CSS class name
915 */
916jvm.VMLElement.prototype.addClass = function( className ){
917  jvm.$(this.node).addClass(className);
918};
919
920/**
921 * Applies attribute value to the underlying DOM element.
922 * @param {String} name Name of attribute
923 * @param {Number|String} config Value of attribute to apply
924 * @private
925 */
926jvm.VMLElement.prototype.applyAttr = function( attr, value ){
927  this.node[attr] = value;
928};
929
930/**
931 * Returns boundary box for the element.
932 * @returns {Object} Boundary box with numeric fields: x, y, width, height
933 * @override
934 */
935jvm.VMLElement.prototype.getBBox = function(){
936  var node = jvm.$(this.node);
937
938  return {
939    x: node.position().left / this.canvas.scale,
940    y: node.position().top / this.canvas.scale,
941    width: node.width() / this.canvas.scale,
942    height: node.height() / this.canvas.scale
943  };
944};jvm.VMLGroupElement = function(){
945  jvm.VMLGroupElement.parentClass.call(this, 'group');
946
947  this.node.style.left = '0px';
948  this.node.style.top = '0px';
949  this.node.coordorigin = "0 0";
950};
951
952jvm.inherits(jvm.VMLGroupElement, jvm.VMLElement);
953
954jvm.VMLGroupElement.prototype.add = function(element){
955  this.node.appendChild( element.node );
956};jvm.VMLCanvasElement = function(container, width, height){
957  this.classPrefix = 'VML';
958  jvm.VMLCanvasElement.parentClass.call(this, 'group');
959  jvm.AbstractCanvasElement.apply(this, arguments);
960  this.node.style.position = 'absolute';
961};
962
963jvm.inherits(jvm.VMLCanvasElement, jvm.VMLElement);
964jvm.mixin(jvm.VMLCanvasElement, jvm.AbstractCanvasElement);
965
966jvm.VMLCanvasElement.prototype.setSize = function(width, height){
967  var paths,
968      groups,
969      i,
970      l;
971
972  this.width = width;
973  this.height = height;
974  this.node.style.width = width + "px";
975  this.node.style.height = height + "px";
976  this.node.coordsize = width+' '+height;
977  this.node.coordorigin = "0 0";
978  if (this.rootElement) {
979    paths = this.rootElement.node.getElementsByTagName('shape');
980    for(i = 0, l = paths.length; i < l; i++) {
981      paths[i].coordsize = width+' '+height;
982      paths[i].style.width = width+'px';
983      paths[i].style.height = height+'px';
984    }
985    groups = this.node.getElementsByTagName('group');
986    for(i = 0, l = groups.length; i < l; i++) {
987      groups[i].coordsize = width+' '+height;
988      groups[i].style.width = width+'px';
989      groups[i].style.height = height+'px';
990    }
991  }
992};
993
994jvm.VMLCanvasElement.prototype.applyTransformParams = function(scale, transX, transY) {
995  this.scale = scale;
996  this.transX = transX;
997  this.transY = transY;
998  this.rootElement.node.coordorigin = (this.width-transX-this.width/100)+','+(this.height-transY-this.height/100);
999  this.rootElement.node.coordsize = this.width/scale+','+this.height/scale;
1000};jvm.VMLShapeElement = function(name, config){
1001  jvm.VMLShapeElement.parentClass.call(this, name, config);
1002
1003  this.fillElement = new jvm.VMLElement('fill');
1004  this.strokeElement = new jvm.VMLElement('stroke');
1005  this.node.appendChild(this.fillElement.node);
1006  this.node.appendChild(this.strokeElement.node);
1007  this.node.stroked = false;
1008
1009  jvm.AbstractShapeElement.apply(this, arguments);
1010};
1011
1012jvm.inherits(jvm.VMLShapeElement, jvm.VMLElement);
1013jvm.mixin(jvm.VMLShapeElement, jvm.AbstractShapeElement);
1014
1015jvm.VMLShapeElement.prototype.applyAttr = function(attr, value){
1016  switch (attr) {
1017    case 'fill':
1018      this.node.fillcolor = value;
1019      break;
1020    case 'fill-opacity':
1021      this.fillElement.node.opacity = Math.round(value*100)+'%';
1022      break;
1023    case 'stroke':
1024      if (value === 'none') {
1025        this.node.stroked = false;
1026      } else {
1027        this.node.stroked = true;
1028      }
1029      this.node.strokecolor = value;
1030      break;
1031    case 'stroke-opacity':
1032      this.strokeElement.node.opacity = Math.round(value*100)+'%';
1033      break;
1034    case 'stroke-width':
1035      if (parseInt(value, 10) === 0) {
1036        this.node.stroked = false;
1037      } else {
1038        this.node.stroked = true;
1039      }
1040      this.node.strokeweight = value;
1041      break;
1042    case 'd':
1043      this.node.path = jvm.VMLPathElement.pathSvgToVml(value);
1044      break;
1045    default:
1046      jvm.VMLShapeElement.parentClass.prototype.applyAttr.apply(this, arguments);
1047  }
1048};jvm.VMLPathElement = function(config, style){
1049  var scale = new jvm.VMLElement('skew');
1050
1051  jvm.VMLPathElement.parentClass.call(this, 'shape', config, style);
1052
1053  this.node.coordorigin = "0 0";
1054
1055  scale.node.on = true;
1056  scale.node.matrix = '0.01,0,0,0.01,0,0';
1057  scale.node.offset = '0,0';
1058
1059  this.node.appendChild(scale.node);
1060};
1061
1062jvm.inherits(jvm.VMLPathElement, jvm.VMLShapeElement);
1063
1064jvm.VMLPathElement.prototype.applyAttr = function(attr, value){
1065  if (attr === 'd') {
1066    this.node.path = jvm.VMLPathElement.pathSvgToVml(value);
1067  } else {
1068    jvm.VMLShapeElement.prototype.applyAttr.call(this, attr, value);
1069  }
1070};
1071
1072jvm.VMLPathElement.pathSvgToVml = function(path) {
1073  var cx = 0, cy = 0, ctrlx, ctrly;
1074
1075  path = path.replace(/(-?\d+)e(-?\d+)/g, '0');
1076  return path.replace(/([MmLlHhVvCcSs])\s*((?:-?\d*(?:\.\d+)?\s*,?\s*)+)/g, function(segment, letter, coords, index){
1077    coords = coords.replace(/(\d)-/g, '$1,-')
1078            .replace(/^\s+/g, '')
1079            .replace(/\s+$/g, '')
1080            .replace(/\s+/g, ',').split(',');
1081    if (!coords[0]) coords.shift();
1082    for (var i=0, l=coords.length; i<l; i++) {
1083      coords[i] = Math.round(100*coords[i]);
1084    }
1085    switch (letter) {
1086      case 'm':
1087        cx += coords[0];
1088        cy += coords[1];
1089        return 't'+coords.join(',');
1090      case 'M':
1091        cx = coords[0];
1092        cy = coords[1];
1093        return 'm'+coords.join(',');
1094      case 'l':
1095        cx += coords[0];
1096        cy += coords[1];
1097        return 'r'+coords.join(',');
1098      case 'L':
1099        cx = coords[0];
1100        cy = coords[1];
1101        return 'l'+coords.join(',');
1102      case 'h':
1103        cx += coords[0];
1104        return 'r'+coords[0]+',0';
1105      case 'H':
1106        cx = coords[0];
1107        return 'l'+cx+','+cy;
1108      case 'v':
1109        cy += coords[0];
1110        return 'r0,'+coords[0];
1111      case 'V':
1112        cy = coords[0];
1113        return 'l'+cx+','+cy;
1114      case 'c':
1115        ctrlx = cx + coords[coords.length-4];
1116        ctrly = cy + coords[coords.length-3];
1117        cx += coords[coords.length-2];
1118        cy += coords[coords.length-1];
1119        return 'v'+coords.join(',');
1120      case 'C':
1121        ctrlx = coords[coords.length-4];
1122        ctrly = coords[coords.length-3];
1123        cx = coords[coords.length-2];
1124        cy = coords[coords.length-1];
1125        return 'c'+coords.join(',');
1126      case 's':
1127        coords.unshift(cy-ctrly);
1128        coords.unshift(cx-ctrlx);
1129        ctrlx = cx + coords[coords.length-4];
1130        ctrly = cy + coords[coords.length-3];
1131        cx += coords[coords.length-2];
1132        cy += coords[coords.length-1];
1133        return 'v'+coords.join(',');
1134      case 'S':
1135        coords.unshift(cy+cy-ctrly);
1136        coords.unshift(cx+cx-ctrlx);
1137        ctrlx = coords[coords.length-4];
1138        ctrly = coords[coords.length-3];
1139        cx = coords[coords.length-2];
1140        cy = coords[coords.length-1];
1141        return 'c'+coords.join(',');
1142    }
1143    return '';
1144  }).replace(/z/g, 'e');
1145};jvm.VMLCircleElement = function(config, style){
1146  jvm.VMLCircleElement.parentClass.call(this, 'oval', config, style);
1147};
1148
1149jvm.inherits(jvm.VMLCircleElement, jvm.VMLShapeElement);
1150
1151jvm.VMLCircleElement.prototype.applyAttr = function(attr, value){
1152  switch (attr) {
1153    case 'r':
1154      this.node.style.width = value*2+'px';
1155      this.node.style.height = value*2+'px';
1156      this.applyAttr('cx', this.get('cx') || 0);
1157      this.applyAttr('cy', this.get('cy') || 0);
1158      break;
1159    case 'cx':
1160      if (!value) return;
1161      this.node.style.left = value - (this.get('r') || 0) + 'px';
1162      break;
1163    case 'cy':
1164      if (!value) return;
1165      this.node.style.top = value - (this.get('r') || 0) + 'px';
1166      break;
1167    default:
1168      jvm.VMLCircleElement.parentClass.prototype.applyAttr.call(this, attr, value);
1169  }
1170};/**
1171 * Class for vector images manipulations.
1172 * @constructor
1173 * @param {DOMElement} container to place canvas to
1174 * @param {Number} width
1175 * @param {Number} height
1176 */
1177jvm.VectorCanvas = function(container, width, height) {
1178  this.mode = window.SVGAngle ? 'svg' : 'vml';
1179
1180  if (this.mode == 'svg') {
1181    this.impl = new jvm.SVGCanvasElement(container, width, height);
1182  } else {
1183    this.impl = new jvm.VMLCanvasElement(container, width, height);
1184  }
1185  this.impl.mode = this.mode;
1186  return this.impl;
1187};jvm.SimpleScale = function(scale){
1188  this.scale = scale;
1189};
1190
1191jvm.SimpleScale.prototype.getValue = function(value){
1192  return value;
1193};jvm.OrdinalScale = function(scale){
1194  this.scale = scale;
1195};
1196
1197jvm.OrdinalScale.prototype.getValue = function(value){
1198  return this.scale[value];
1199};
1200
1201jvm.OrdinalScale.prototype.getTicks = function(){
1202  var ticks = [],
1203      key;
1204
1205  for (key in this.scale) {
1206    ticks.push({
1207      label: key,
1208      value: this.scale[key]
1209    });
1210  }
1211
1212  return ticks;
1213};jvm.NumericScale = function(scale, normalizeFunction, minValue, maxValue) {
1214  this.scale = [];
1215
1216  normalizeFunction = normalizeFunction || 'linear';
1217
1218  if (scale) this.setScale(scale);
1219  if (normalizeFunction) this.setNormalizeFunction(normalizeFunction);
1220  if (typeof minValue !== 'undefined' ) this.setMin(minValue);
1221  if (typeof maxValue !== 'undefined' ) this.setMax(maxValue);
1222};
1223
1224jvm.NumericScale.prototype = {
1225  setMin: function(min) {
1226    this.clearMinValue = min;
1227    if (typeof this.normalize === 'function') {
1228      this.minValue = this.normalize(min);
1229    } else {
1230      this.minValue = min;
1231    }
1232  },
1233
1234  setMax: function(max) {
1235    this.clearMaxValue = max;
1236    if (typeof this.normalize === 'function') {
1237      this.maxValue = this.normalize(max);
1238    } else {
1239      this.maxValue = max;
1240    }
1241  },
1242
1243  setScale: function(scale) {
1244    var i;
1245
1246    this.scale = [];
1247    for (i = 0; i < scale.length; i++) {
1248      this.scale[i] = [scale[i]];
1249    }
1250  },
1251
1252  setNormalizeFunction: function(f) {
1253    if (f === 'polynomial') {
1254      this.normalize = function(value) {
1255        return Math.pow(value, 0.2);
1256      }
1257    } else if (f === 'linear') {
1258      delete this.normalize;
1259    } else {
1260      this.normalize = f;
1261    }
1262    this.setMin(this.clearMinValue);
1263    this.setMax(this.clearMaxValue);
1264  },
1265
1266  getValue: function(value) {
1267    var lengthes = [],
1268        fullLength = 0,
1269        l,
1270        i = 0,
1271        c;
1272
1273    if (typeof this.normalize === 'function') {
1274      value = this.normalize(value);
1275    }
1276    for (i = 0; i < this.scale.length-1; i++) {
1277      l = this.vectorLength(this.vectorSubtract(this.scale[i+1], this.scale[i]));
1278      lengthes.push(l);
1279      fullLength += l;
1280    }
1281
1282    c = (this.maxValue - this.minValue) / fullLength;
1283    for (i=0; i<lengthes.length; i++) {
1284      lengthes[i] *= c;
1285    }
1286
1287    i = 0;
1288    value -= this.minValue;
1289    while (value - lengthes[i] >= 0) {
1290      value -= lengthes[i];
1291      i++;
1292    }
1293
1294    if (i == this.scale.length - 1) {
1295      value = this.vectorToNum(this.scale[i])
1296    } else {
1297      value = (
1298        this.vectorToNum(
1299          this.vectorAdd(this.scale[i],
1300            this.vectorMult(
1301              this.vectorSubtract(this.scale[i+1], this.scale[i]),
1302              (value) / (lengthes[i])
1303            )
1304          )
1305        )
1306      );
1307    }
1308
1309    return value;
1310  },
1311
1312  vectorToNum: function(vector) {
1313    var num = 0,
1314        i;
1315
1316    for (i = 0; i < vector.length; i++) {
1317      num += Math.round(vector[i])*Math.pow(256, vector.length-i-1);
1318    }
1319    return num;
1320  },
1321
1322  vectorSubtract: function(vector1, vector2) {
1323    var vector = [],
1324        i;
1325
1326    for (i = 0; i < vector1.length; i++) {
1327      vector[i] = vector1[i] - vector2[i];
1328    }
1329    return vector;
1330  },
1331
1332  vectorAdd: function(vector1, vector2) {
1333    var vector = [],
1334        i;
1335
1336    for (i = 0; i < vector1.length; i++) {
1337      vector[i] = vector1[i] + vector2[i];
1338    }
1339    return vector;
1340  },
1341
1342  vectorMult: function(vector, num) {
1343    var result = [],
1344        i;
1345
1346    for (i = 0; i < vector.length; i++) {
1347      result[i] = vector[i] * num;
1348    }
1349    return result;
1350  },
1351
1352  vectorLength: function(vector) {
1353    var result = 0,
1354        i;
1355    for (i = 0; i < vector.length; i++) {
1356      result += vector[i] * vector[i];
1357    }
1358    return Math.sqrt(result);
1359  },
1360
1361  /* Derived from d3 implementation https://github.com/mbostock/d3/blob/master/src/scale/linear.js#L94 */
1362  getTicks: function(){
1363    var m = 5,
1364        extent = [this.clearMinValue, this.clearMaxValue],
1365        span = extent[1] - extent[0],
1366        step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)),
1367        err = m / span * step,
1368        ticks = [],
1369        tick,
1370        v;
1371
1372    if (err <= .15) step *= 10;
1373    else if (err <= .35) step *= 5;
1374    else if (err <= .75) step *= 2;
1375
1376    extent[0] = Math.floor(extent[0] / step) * step;
1377    extent[1] = Math.ceil(extent[1] / step) * step;
1378
1379    tick = extent[0];
1380    while (tick <= extent[1]) {
1381      if (tick == extent[0]) {
1382        v = this.clearMinValue;
1383      } else if (tick == extent[1]) {
1384        v = this.clearMaxValue;
1385      } else {
1386        v = tick;
1387      }
1388      ticks.push({
1389        label: tick,
1390        value: this.getValue(v)
1391      });
1392      tick += step;
1393    }
1394
1395    return ticks;
1396  }
1397};
1398jvm.ColorScale = function(colors, normalizeFunction, minValue, maxValue) {
1399  jvm.ColorScale.parentClass.apply(this, arguments);
1400}
1401
1402jvm.inherits(jvm.ColorScale, jvm.NumericScale);
1403
1404jvm.ColorScale.prototype.setScale = function(scale) {
1405  var i;
1406
1407  for (i = 0; i < scale.length; i++) {
1408    this.scale[i] = jvm.ColorScale.rgbToArray(scale[i]);
1409  }
1410};
1411
1412jvm.ColorScale.prototype.getValue = function(value) {
1413  return jvm.ColorScale.numToRgb(jvm.ColorScale.parentClass.prototype.getValue.call(this, value));
1414};
1415
1416jvm.ColorScale.arrayToRgb = function(ar) {
1417  var rgb = '#',
1418      d,
1419      i;
1420
1421  for (i = 0; i < ar.length; i++) {
1422    d = ar[i].toString(16);
1423    rgb += d.length == 1 ? '0'+d : d;
1424  }
1425  return rgb;
1426};
1427
1428jvm.ColorScale.numToRgb = function(num) {
1429  num = num.toString(16);
1430
1431  while (num.length < 6) {
1432    num = '0' + num;
1433  }
1434
1435  return '#'+num;
1436};
1437
1438jvm.ColorScale.rgbToArray = function(rgb) {
1439  rgb = rgb.substr(1);
1440  return [parseInt(rgb.substr(0, 2), 16), parseInt(rgb.substr(2, 2), 16), parseInt(rgb.substr(4, 2), 16)];
1441};/**
1442 * Represents map legend.
1443 * @constructor
1444 * @param {Object} params Configuration parameters.
1445 * @param {String} params.cssClass Additional CSS class to apply to legend element.
1446 * @param {Boolean} params.vertical If <code>true</code> legend will be rendered as vertical.
1447 * @param {String} params.title Legend title.
1448 * @param {Function} params.labelRender Method to convert series values to legend labels.
1449 */
1450jvm.Legend = function(params) {
1451  this.params = params || {};
1452  this.map = this.params.map;
1453  this.series = this.params.series;
1454  this.body = jvm.$('<div/>');
1455  this.body.addClass('jvectormap-legend');
1456  if (this.params.cssClass) {
1457    this.body.addClass(this.params.cssClass);
1458  }
1459
1460  if (params.vertical) {
1461    this.map.legendCntVertical.append( this.body );
1462  } else {
1463    this.map.legendCntHorizontal.append( this.body );
1464  }
1465
1466  this.render();
1467}
1468
1469jvm.Legend.prototype.render = function(){
1470  var ticks = this.series.scale.getTicks(),
1471      i,
1472      inner = jvm.$('<div/>').addClass('jvectormap-legend-inner'),
1473      tick,
1474      sample,
1475      label;
1476
1477  this.body.html('');
1478  if (this.params.title) {
1479    this.body.append(
1480      jvm.$('<div/>').addClass('jvectormap-legend-title').html(this.params.title)
1481    );
1482  }
1483  this.body.append(inner);
1484
1485  for (i = 0; i < ticks.length; i++) {
1486    tick = jvm.$('<div/>').addClass('jvectormap-legend-tick');
1487    sample = jvm.$('<div/>').addClass('jvectormap-legend-tick-sample');
1488
1489    switch (this.series.params.attribute) {
1490      case 'fill':
1491        if (jvm.isImageUrl(ticks[i].value)) {
1492          sample.css('background', 'url('+ticks[i].value+')');
1493        } else {
1494          sample.css('background', ticks[i].value);
1495        }
1496        break;
1497      case 'stroke':
1498        sample.css('background', ticks[i].value);
1499        break;
1500      case 'image':
1501        sample.css('background', 'url('+ticks[i].value+') no-repeat center center');
1502        break;
1503      case 'r':
1504        jvm.$('<div/>').css({
1505          'border-radius': ticks[i].value,
1506          border: this.map.params.markerStyle.initial['stroke-width']+'px '+
1507                  this.map.params.markerStyle.initial['stroke']+' solid',
1508          width: ticks[i].value * 2 + 'px',
1509          height: ticks[i].value * 2 + 'px',
1510          background: this.map.params.markerStyle.initial['fill']
1511        }).appendTo(sample);
1512        break;
1513    }
1514    tick.append( sample );
1515    label = ticks[i].label;
1516    if (this.params.labelRender) {
1517      label = this.params.labelRender(label);
1518    }
1519    tick.append( jvm.$('<div>'+label+' </div>').addClass('jvectormap-legend-tick-text') );
1520    inner.append(tick);
1521  }
1522  inner.append( jvm.$('<div/>').css('clear', 'both') );
1523}/**
1524 * Creates data series.
1525 * @constructor
1526 * @param {Object} params Parameters to initialize series with.
1527 * @param {Array} params.values The data set to visualize.
1528 * @param {String}
1528 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.
1529 * @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>
1530 * @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.
1531 * @param {Number} params.min Minimum value of the data set. Could be calculated automatically if not provided.
1532 * @param {Number} params.min Maximum value of the data set. Could be calculated automatically if not provided.
1533 */
1534jvm.DataSeries = function(params, elements, map) {
1535  var scaleConstructor;
1536
1537  params = params || {};
1538  params.attribute = params.attribute || 'fill';
1539
1540  this.elements = elements;
1541  this.params = params;
1542  this.map = map;
1543
1544  if (params.attributes) {
1545    this.setAttributes(params.attributes);
1546  }
1547
1548  if (jvm.$.isArray(params.scale)) {
1549    scaleConstructor = (params.attribute === 'fill' || params.attribute === 'stroke') ? jvm.ColorScale : jvm.NumericScale;
1550    this.scale = new scaleConstructor(params.scale, params.normalizeFunction, params.min, params.max);
1551  } else if (params.scale) {
1552    this.scale = new jvm.OrdinalScale(params.scale);
1553  } else {
1554    this.scale = new jvm.SimpleScale(params.scale);
1555  }
1556
1557  this.values = params.values || {};
1558  this.setValues(this.values);
1559
1560  if (this.params.legend) {
1561    this.legend = new jvm.Legend($.extend({
1562      map: this.map,
1563      series: this
1564    }, this.params.legend))
1565  }
1566};
1567
1568jvm.DataSeries.prototype = {
1569  setAttributes: function(key, attr){
1570    var attrs = key,
1571        code;
1572
1573    if (typeof key == 'string') {
1574      if (this.elements[key]) {
1575        this.elements[key].setStyle(this.params.attribute, attr);
1576      }
1577    } else {
1578      for (code in attrs) {
1579        if (this.elements[code]) {
1580          this.elements[code].element.setStyle(this.params.attribute, attrs[code]);
1581        }
1582      }
1583    }
1584  },
1585
1586  /**
1587   * Set values for the data set.
1588   * @param {Object} values Object which maps codes of regions or markers to values.
1589   */
1590  setValues: function(values) {
1591    var max = -Number.MAX_VALUE,
1592        min = Number.MAX_VALUE,
1593        val,
1594        cc,
1595        attrs = {};
1596
1597    if (!(this.scale instanceof jvm.OrdinalScale) && !(this.scale instanceof jvm.SimpleScale)) {
1598      // we have a color scale as an array
1599      if (typeof this.params.min === 'undefined' || typeof this.params.max === 'undefined') {
1600        // min and/or max are not defined, so calculate them
1601        for (cc in values) {
1602          val = parseFloat(values[cc]);
1603          if (val > max) max = val;
1604          if (val < min) min = val;
1605        }
1606      }
1607
1608      if (typeof this.params.min === 'undefined') {
1609        this.scale.setMin(min);
1610        this.params.min = min;
1611      } else {
1612        this.scale.setMin(this.params.min);
1613      }
1614
1615      if (typeof this.params.max === 'undefined') {
1616        this.scale.setMax(max);
1617        this.params.max = max;
1618      } else {
1619        this.scale.setMax(this.params.max);
1620      }
1621
1622      for (cc in values) {
1623        if (cc != 'indexOf') {
1624          val = parseFloat(values[cc]);
1625          if (!isNaN(val)) {
1626            attrs[cc] = this.scale.getValue(val);
1627          } else {
1628            attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
1629          }
1630        }
1631      }
1632    } else {
1633      for (cc in values) {
1634        if (values[cc]) {
1635          attrs[cc] = this.scale.getValue(values[cc]);
1636        } else {
vendor: 13,464 bytes, lines 1637-2057
1637          attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
1638        }
1639      }
1640    }
1641
1642    this.setAttributes(attrs);
1643    jvm.$.extend(this.values, values);
1644  },
1645
1646  clear: function(){
1647    var key,
1648        attrs = {};
1649
1650    for (key in this.values) {
1651      if (this.elements[key]) {
1652        attrs[key] = this.elements[key].element.shape.style.initial[this.params.attribute];
1653      }
1654    }
1655    this.setAttributes(attrs);
1656    this.values = {};
1657  },
1658
1659  /**
1660   * Set scale of the data series.
1661   * @param {Array} scale Values representing scale.
1662   */
1663  setScale: function(scale) {
1664    this.scale.setScale(scale);
1665    if (this.values) {
1666      this.setValues(this.values);
1667    }
1668  },
1669
1670  /**
1671   * Set normalize function of the data series.
1672   * @param {Function|String} normilizeFunction.
1673   */
1674  setNormalizeFunction: function(f) {
1675    this.scale.setNormalizeFunction(f);
1676    if (this.values) {
1677      this.setValues(this.values);
1678    }
1679  }
1680};
1681/**
1682 * Contains methods for transforming point on sphere to
1683 * Cartesian coordinates using various projections.
1684 * @class
1685 */
1686jvm.Proj = {
1687  degRad: 180 / Math.PI,
1688  radDeg: Math.PI / 180,
1689  radius: 6381372,
1690
1691  sgn: function(n){
1692    if (n > 0) {
1693      return 1;
1694    } else if (n < 0) {
1695      return -1;
1696    } else {
1697      return n;
1698    }
1699  },
1700
1701  /**
1702   * Converts point on sphere to the Cartesian coordinates using Miller projection
1703   * @param {Number} lat Latitude in degrees
1704   * @param {Number} lng Longitude in degrees
1705   * @param {Number} c Central meridian in degrees
1706   */
1707  mill: function(lat, lng, c){
1708    return {
1709      x: this.radius * (lng - c) * this.radDeg,
1710      y: - this.radius * Math.log(Math.tan((45 + 0.4 * lat) * this.radDeg)) / 0.8
1711    };
1712  },
1713
1714  /**
1715   * Inverse function of mill()
1716   * Converts Cartesian coordinates to point on sphere using Miller projection
1717   * @param {Number} x X of point in Cartesian system as integer
1718   * @param {Number} y Y of point in Cartesian system as integer
1719   * @param {Number} c Central meridian in degrees
1720   */
1721  mill_inv: function(x, y, c){
1722    return {
1723      lat: (2.5 * Math.atan(Math.exp(0.8 * y / this.radius)) - 5 * Math.PI / 8) * this.degRad,
1724      lng: (c * this.radDeg + x / this.radius) * this.degRad
1725    };
1726  },
1727
1728  /**
1729   * Converts point on sphere to the Cartesian coordinates using Mercator projection
1730   * @param {Number} lat Latitude in degrees
1731   * @param {Number} lng Longitude in degrees
1732   * @param {Number} c Central meridian in degrees
1733   */
1734  merc: function(lat, lng, c){
1735    return {
1736      x: this.radius * (lng - c) * this.radDeg,
1737      y: - this.radius * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360))
1738    };
1739  },
1740
1741  /**
1742   * Inverse function of merc()
1743   * Converts Cartesian coordinates to point on sphere using Mercator projection
1744   * @param {Number} x X of point in Cartesian system as integer
1745   * @param {Number} y Y of point in Cartesian system as integer
1746   * @param {Number} c Central meridian in degrees
1747   */
1748  merc_inv: function(x, y, c){
1749    return {
1750      lat: (2 * Math.atan(Math.exp(y / this.radius)) - Math.PI / 2) * this.degRad,
1751      lng: (c * this.radDeg + x / this.radius) * this.degRad
1752    };
1753  },
1754
1755  /**
1756   * Converts point on sphere to the Cartesian coordinates using Albers Equal-Area Conic
1757   * projection
1758   * @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
1759   * @param {Number} lat Latitude in degrees
1760   * @param {Number} lng Longitude in degrees
1761   * @param {Number} c Central meridian in degrees
1762   */
1763  aea: function(lat, lng, c){
1764    var fi0 = 0,
1765        lambda0 = c * this.radDeg,
1766        fi1 = 29.5 * this.radDeg,
1767        fi2 = 45.5 * this.radDeg,
1768        fi = lat * this.radDeg,
1769        lambda = lng * this.radDeg,
1770        n = (Math.sin(fi1)+Math.sin(fi2)) / 2,
1771        C = Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
1772        theta = n*(lambda-lambda0),
1773        ro = Math.sqrt(C-2*n*Math.sin(fi))/n,
1774        ro0 = Math.sqrt(C-2*n*Math.sin(fi0))/n;
1775
1776    return {
1777      x: ro * Math.sin(theta) * this.radius,
1778      y: - (ro0 - ro * Math.cos(theta)) * this.radius
1779    };
1780  },
1781
1782  /**
1783   * Converts Cartesian coordinates to the point on sphere using Albers Equal-Area Conic
1784   * projection
1785   * @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
1786   * @param {Number} x X of point in Cartesian system as integer
1787   * @param {Number} y Y of point in Cartesian system as integer
1788   * @param {Number} c Central meridian in degrees
1789   */
1790  aea_inv: function(xCoord, yCoord, c){
1791    var x = xCoord / this.radius,
1792        y = yCoord / this.radius,
1793        fi0 = 0,
1794        lambda0 = c * this.radDeg,
1795        fi1 = 29.5 * this.radDeg,
1796        fi2 = 45.5 * this.radDeg,
1797        n = (Math.sin(fi1)+Math.sin(fi2)) / 2,
1798        C = Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
1799        ro0 = Math.sqrt(C-2*n*Math.sin(fi0))/n,
1800        ro = Math.sqrt(x*x+(ro0-y)*(ro0-y)),
1801        theta = Math.atan( x / (ro0 - y) );
1802
1803    return {
1804      lat: (Math.asin((C - ro * ro * n * n) / (2 * n))) * this.degRad,
1805      lng: (lambda0 + theta / n) * this.degRad
1806    };
1807  },
1808
1809  /**
1810   * Converts point on sphere to the Cartesian coordinates using Lambert conformal
1811   * conic projection
1812   * @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
1813   * @param {Number} lat Latitude in degrees
1814   * @param {Number} lng Longitude in degrees
1815   * @param {Number} c Central meridian in degrees
1816   */
1817  lcc: function(lat, lng, c){
1818    var fi0 = 0,
1819        lambda0 = c * this.radDeg,
1820        lambda = lng * this.radDeg,
1821        fi1 = 33 * this.radDeg,
1822        fi2 = 45 * this.radDeg,
1823        fi = lat * this.radDeg,
1824        n = Math.log( Math.cos(fi1) * (1 / Math.cos(fi2)) ) / Math.log( Math.tan( Math.PI / 4 + fi2 / 2) * (1 / Math.tan( Math.PI / 4 + fi1 / 2) ) ),
1825        F = ( Math.cos(fi1) * Math.pow( Math.tan( Math.PI / 4 + fi1 / 2 ), n ) ) / n,
1826        ro = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi / 2 ), n ),
1827        ro0 = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi0 / 2 ), n );
1828
1829    return {
1830      x: ro * Math.sin( n * (lambda - lambda0) ) * this.radius,
1831      y: - (ro0 - ro * Math.cos( n * (lambda - lambda0) ) ) * this.radius
1832    };
1833  },
1834
1835  /**
1836   * Converts Cartesian coordinates to the point on sphere using Lambert conformal conic
1837   * projection
1838   * @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
1839   * @param {Number} x X of point in Cartesian system as integer
1840   * @param {Number} y Y of point in Cartesian system as integer
1841   * @param {Number} c Central meridian in degrees
1842   */
1843  lcc_inv: function(xCoord, yCoord, c){
1844    var x = xCoord / this.radius,
1845        y = yCoord / this.radius,
1846        fi0 = 0,
1847        lambda0 = c * this.radDeg,
1848        fi1 = 33 * this.radDeg,
1849        fi2 = 45 * this.radDeg,
1850        n = Math.log( Math.cos(fi1) * (1 / Math.cos(fi2)) ) / Math.log( Math.tan( Math.PI / 4 + fi2 / 2) * (1 / Math.tan( Math.PI / 4 + fi1 / 2) ) ),
1851        F = ( Math.cos(fi1) * Math.pow( Math.tan( Math.PI / 4 + fi1 / 2 ), n ) ) / n,
1852        ro0 = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi0 / 2 ), n ),
1853        ro = this.sgn(n) * Math.sqrt(x*x+(ro0-y)*(ro0-y)),
1854        theta = Math.atan( x / (ro0 - y) );
1855
1856    return {
1857      lat: (2 * Math.atan(Math.pow(F/ro, 1/n)) - Math.PI / 2) * this.degRad,
1858      lng: (lambda0 + theta / n) * this.degRad
1859    };
1860  }
1861};jvm.MapObject = function(config){};
1862
1863jvm.MapObject.prototype.getLabelText = function(key){
1864  var text;
1865
1866  if (this.config.label) {
1867    if (typeof this.config.label.render === 'function') {
1868      text = this.config.label.render(key);
1869    } else {
1870      text = key;
1871    }
1872  } else {
1873    text = null;
1874  }
1875  return text;
1876}
1877
1878jvm.MapObject.prototype.getLabelOffsets = function(key){
1879  var offsets;
1880
1881  if (this.config.label) {
1882    if (typeof this.config.label.offsets === 'function') {
1883      offsets = this.config.label.offsets(key);
1884    } else if (typeof this.config.label.offsets === 'object') {
1885      offsets = this.config.label.offsets[key];
1886    }
1887  }
1888  return offsets || [0, 0];
1889}
1890
1891/**
1892 * Set hovered state to the element. Hovered state means mouse cursor is over element. Styles will be updates respectively.
1893 * @param {Boolean} isHovered <code>true</code> to make element hovered, <code>false</code> otherwise.
1894 */
1895jvm.MapObject.prototype.setHovered = function(isHovered){
1896  if (this.isHovered !== isHovered) {
1897    this.isHovered = isHovered;
1898    this.shape.isHovered = isHovered;
1899    this.shape.updateStyle();
1900    if (this.label) {
1901      this.label.isHovered = isHovered;
1902      this.label.updateStyle();
1903    }
1904  }
1905};
1906
1907/**
1908 * Set selected state to the element. Styles will be updates respectively.
1909 * @param {Boolean} isSelected <code>true</code> to make element selected, <code>false</code> otherwise.
1910 */
1911jvm.MapObject.prototype.setSelected = function(isSelected){
1912  if (this.isSelected !== isSelected) {
1913    this.isSelected = isSelected;
1914    this.shape.isSelected = isSelected;
1915    this.shape.updateStyle();
1916    if (this.label) {
1917      this.label.isSelected = isSelected;
1918      this.label.updateStyle();
1919    }
1920    jvm.$(this.shape).trigger('selected', [isSelected]);
1921  }
1922};
1923
1924jvm.MapObject.prototype.setStyle = function(){
1925	this.shape.setStyle.apply(this.shape, arguments);
1926};
1927
1928jvm.MapObject.prototype.remove = function(){
1929  this.shape.remove();
1930  if (this.label) {
1931    this.label.remove();
1932  }
1933};jvm.Region = function(config){
1934  var bbox,
1935      text,
1936      offsets,
1937      labelDx,
1938      labelDy;
1939
1940  this.config = config;
1941  this.map = this.config.map;
1942
1943  this.shape = config.canvas.addPath({
1944    d: config.path,
1945    'data-code': config.code
1946  }, config.style, config.canvas.rootElement);
1947  this.shape.addClass('jvectormap-region jvectormap-element');
1948
1949  bbox = this.shape.getBBox();
1950
1951  text = this.getLabelText(config.code);
1952  if (this.config.label && text) {
1953    offsets = this.getLabelOffsets(config.code);
1954    this.labelX = bbox.x + bbox.width / 2 + offsets[0];
1955    this.labelY = bbox.y + bbox.height / 2 + offsets[1];
1956    this.label = config.canvas.addText({
1957      text: text,
1958      'text-anchor': 'middle',
1959      'alignment-baseline': 'central',
1960      x: this.labelX,
1961      y: this.labelY,
1962      'data-code': config.code
1963    }, config.labelStyle, config.labelsGroup);
1964    this.label.addClass('jvectormap-region jvectormap-element');
1965  }
1966};
1967
1968jvm.inherits(jvm.Region, jvm.MapObject);
1969
1970jvm.Region.prototype.updateLabelPosition = function(){
1971  if (this.label) {
1972    this.label.set({
1973      x: this.labelX * this.map.scale + this.map.transX * this.map.scale,
1974      y: this.labelY * this.map.scale + this.map.transY * this.map.scale
1975    });
1976  }
1977};jvm.Marker = function(config){
1978  var text,
1979      offsets;
1980
1981  this.config = config;
1982  this.map = this.config.map;
1983
1984  this.isImage = !!this.config.style.initial.image;
1985  this.createShape();
1986
1987  text = this.getLabelText(config.index);
1988  if (this.config.label && text) {
1989    this.offsets = this.getLabelOffsets(config.index);
1990    this.labelX = config.cx / this.map.scale - this.map.transX;
1991    this.labelY = config.cy / this.map.scale - this.map.transY;
1992    this.label = config.canvas.addText({
1993      text: text,
1994      'data-index': config.index,
1995      dy: "0.6ex",
1996      x: this.labelX,
1997      y: this.labelY
1998    }, config.labelStyle, config.labelsGroup);
1999
2000    this.label.addClass('jvectormap-marker jvectormap-element');
2001  }
2002};
2003
2004jvm.inherits(jvm.Marker, jvm.MapObject);
2005
2006jvm.Marker.prototype.createShape = function(){
2007  var that = this;
2008
2009  if (this.shape) {
2010    this.shape.remove();
2011  }
2012  this.shape = this.config.canvas[this.isImage ? 'addImage' : 'addCircle']({
2013    "data-index": this.config.index,
2014    cx: this.config.cx,
2015    cy: this.config.cy
2016  }, this.config.style, this.config.group);
2017
2018  this.shape.addClass('jvectormap-marker jvectormap-element');
2019
2020  if (this.isImage) {
2021    jvm.$(this.shape.node).on('imageloaded', function(){
2022      that.updateLabelPosition();
2023    });
2024  }
2025};
2026
2027jvm.Marker.prototype.updateLabelPosition = function(){
2028  if (this.label) {
2029    this.label.set({
2030      x: this.labelX * this.map.scale + this.offsets[0] +
2031         this.map.transX * this.map.scale + 5 + (this.isImage ? (this.shape.width || 0) / 2 : this.shape.properties.r),
2032      y: this.labelY * this.map.scale + this.map.transY * this.map.scale + this.offsets[1]
2033    });
2034  }
2035};
2036
2037jvm.Marker.prototype.setStyle = function(property, value){
2038  var isImage;
2039
2040  jvm.Marker.parentClass.prototype.setStyle.apply(this, arguments);
2041
2042  if (property === 'r') {
2043    this.updateLabelPosition();
2044  }
2045
2046  isImage = !!this.shape.get('image');
2047  if (isImage != this.isImage) {
2048    this.isImage = isImage;
2049    this.config.style = jvm.$.extend(true, {}, this.shape.style);
2050    this.createShape();
2051  }
2052};/**
2053 * Creates map, draws paths, binds events.
2054 * @constructor
2055 * @param {Object} params Parameters to initialize map with.
2056 * @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.
2057 * @param {String}
2057 params.backgroundColor Background color of the map in CSS format.
2058 * @param {Boolean} params.zoomOnScroll When set to true map could be zoomed using mouse scroll. Default value is <code>true</code>.
2059 * @param {Boolean} params.zoomOnScrollSpeed Mouse scroll speed. Number from 1 to 10. Default value is <code>3</code>.
2060 * @param {Boolean} params.panOnDrag When set to true, the map pans when being dragged. Default value is <code>true</code>.
2061 * @param {Number} params.zoomMax Indicates the maximum zoom ratio which could be reached zooming the map. Default value is <code>8</code>.
2062 * @param {Number} params.zoomMin Indicates the minimum zoom ratio which could be reached zooming the map. Default value is <code>1</code>.
2063 * @param {Number} params.zoomStep Indicates the multiplier used to zoom map with +/- buttons. Default value is <code>1.6</code>.
2064 * @param {Boolean} params.zoomAnimate Indicates whether or not to animate changing of map zoom with zoom buttons.
2065 * @param {Boolean} params.regionsSelectable When set to true regions of the map could be selected. Default value is <code>false</code>.
2066 * @param {Boolean} params.regionsSelectableOne Allow only one region to be selected at the moment. Default value is <code>false</code>.
2067 * @param {Boolean} params.markersSelectable When set to true markers on the map could be selected. Default value is <code>false</code>.
2068 * @param {Boolean} params.markersSelectableOne Allow only one marker to be selected at the moment. Default value is <code>false</code>.
2069 * @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>
2069 (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:
2070<pre>{
2071  initial: {
2072    fill: 'white',
2073    "fill-opacity": 1,
2074    stroke: 'none',
2075    "stroke-width": 0,
2076    "stroke-opacity": 1
2077  },
2078  hover: {
2079    "fill-opacity": 0.8,
2080    cursor: 'pointer'
2081  },
2082  selected: {
2083    fill: 'yellow'
2084  },
2085  selectedHover: {
2086  }
2087}</pre>
2088* @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:
2089<pre>{
2090  initial: {
2091    'font-family': 'Verdana',
2092    'font-size': '12',
2093    'font-weight': 'bold',
2094    cursor: 'default',
2095    fill: 'black'
2096  },
2097  hover: {
2098    cursor: 'pointer'
2099  }
2100}</pre>
2101 * @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:
2102<pre>{
2103  initial: {
2104    fill: 'grey',
2105    stroke: '#505050',
2106    "fill-opacity": 1,
2107    "stroke-width": 1,
2108    "stroke-opacity": 1,
2109    r: 5
2110  },
2111  hover: {
2112    stroke: 'black',
2113    "stroke-width": 2,
2114    cursor: 'pointer'
2115  },
2116  selected: {
2117    fill: 'blue'
2118  },
2119  selectedHover: {
2120  }
2121}</pre>
2122 * @param {Object} params.markerLabelStyle Set the styles for the markers' labels. Default value for that parameter is:
2123<pre>{
2124  initial: {
2125    'font-family': 'Verdana',
2126    'font-size': '12',
2127    'font-weight': 'bold',
2128    cursor: 'default',
2129    fill: 'black'
2130  },
2131  hover: {
2132    cursor: 'pointer'
2133  }
2134}</pre>
2135 * @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.
2136 * @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.
2137 * @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.
2138 * @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:
2139<ul>
2140  <li><code>render {Function}</code> - defines method for converting region code or marker index to actual label value.</li>
2141  <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>
2142</ul>
2143<b>Plase note: static labels feature is not supported in Internet Explorer 8 and below.</b>
2144 * @param {Array|Object|String} params.selectedRegions Set initially selected regions.
2145 * @param {Array|Object|String} params.selectedMarkers Set initially selected markers.
2146 * @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.
2147 * @param {Function} params.onRegionOver <code>(Event e, String code)</code> Will be called on region mouse over event.
2148 * @param {Function} params.onRegionOut <code>(Event e, String code)</code> Will be called on region mouse out event.
2149 * @param {Function} params.onRegionClick <code>(Event e, String code)</code> Will be called on region click event.
2150 * @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>
2150 contains codes of all currently selected regions.
2151 * @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.
2152 * @param {Function} params.onMarkerOver <code>(Event e, String code)</code> Will be called on marker mouse over event.
2153 * @param {Function} params.onMarkerOut <code>(Event e, String code)</code> Will be called on marker mouse out event.
2154 * @param {Function} params.onMarkerClick <code>(Event e, String code)</code> Will be called on marker click event.
2155 * @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.
2156 * @param {Function} params.onViewportChange <code>(Event e, Number scale)</code> Triggered when the map's viewport is changed (map was panned or zoomed).
2157 */
2158jvm.Map = function(params) {
2159  var map = this,
2160      e;
2161
2162  this.params = jvm.$.extend(true, {}, jvm.Map.defaultParams, params);
2163
2164  if (!jvm.Map.maps[this.params.map]) {
2165    throw new Error('Attempt to use map which was not loaded: '+this.params.map);
2166  }
2167
2168  this.mapData = jvm.Map.maps[this.params.map];
2169  this.markers = {};
2170  this.regions = {};
2171  this.regionsColors = {};
2172  this.regionsData = {};
2173
2174  this.container = jvm.$('<div>').addClass('jvectormap-container');
2175  if (this.params.container) {
2176    this.params.container.append( this.container );
2177  }
2178  this.container.data('mapObject', this);
2179
2180  this.defaultWidth = this.mapData.width;
2181  this.defaultHeight = this.mapData.height;
2182
2183  this.setBackgroundColor(this.params.backgroundColor);
2184
2185  this.onResize = function(){
2186    map.updateSize();
2187  }
2188  jvm.$(window).resize(this.onResize);
2189
2190  for (e in jvm.Map.apiEvents) {
2191    if (this.params[e]) {
2192      this.container.bind(jvm.Map.apiEvents[e]+'.jvectormap', this.params[e]);
2193    }
2194  }
2195
2196  this.canvas = new jvm.VectorCanvas(this.container[0], this.width, this.height);
2197
2198  if (this.params.bindTouchEvents) {
2199    if (('ontouchstart' in window) || (window.DocumentTouch && document instanceof DocumentTouch)) {
2200      this.bindContainerTouchEvents();
2201    } else if (window.MSGesture) {
2202      this.bindContainerPointerEvents();
2203    }
2204  }
2205  this.bindContainerEvents();
2206  this.bindElementEvents();
2207  this.createTip();
2208  if (this.params.zoomButtons) {
2209    this.bindZoomButtons();
2210  }
2211
2212  this.createRegions();
2213  this.createMarkers(this.params.markers || {});
2214
2215  this.updateSize();
2216
2217  if (this.params.focusOn) {
2218    if (typeof this.params.focusOn === 'string') {
2219      this.params.focusOn = {region: this.params.focusOn};
2220    } else if (jvm.$.isArray(this.params.focusOn)) {
2221      this.params.focusOn = {regions: this.params.focusOn};
2222    }
2223    this.setFocus(this.params.focusOn);
2224  }
2225
2226  if (this.params.selectedRegions) {
2227    this.setSelectedRegions(this.params.selectedRegions);
2228  }
2229  if (this.params.selectedMarkers) {
2230    this.setSelectedMarkers(this.params.selectedMarkers);
2231  }
2232
2233  this.legendCntHorizontal = jvm.$('<div/>').addClass('jvectormap-legend-cnt jvectormap-legend-cnt-h');
2234  this.legendCntVertical = jvm.$('<div/>').addClass('jvectormap-legend-cnt jvectormap-legend-cnt-v');
2235  this.container.append(this.legendCntHorizontal);
2236  this.container.append(this.legendCntVertical);
2237
2238  if (this.params.series) {
2239    this.createSeries();
2240  }
2241};
2242
2243jvm.Map.prototype = {
2244  transX: 0,
2245  transY: 0,
2246  scale: 1,
2247  baseTransX: 0,
2248  baseTransY: 0,
2249  baseScale: 1,
2250
2251  width: 0,
2252  height: 0,
2253
2254  /**
2255   * Set background color of the map.
2256   * @param {String}
vendor: 34,470 bytes, lines 2256-3375
2256 backgroundColor Background color in CSS format.
2257   */
2258  setBackgroundColor: function(backgroundColor) {
2259    this.container.css('background-color', backgroundColor);
2260  },
2261
2262  resize: function() {
2263    var curBaseScale = this.baseScale;
2264    if (this.width / this.height > this.defaultWidth / this.defaultHeight) {
2265      this.baseScale = this.height / this.defaultHeight;
2266      this.baseTransX = Math.abs(this.width - this.defaultWidth * this.baseScale) / (2 * this.baseScale);
2267    } else {
2268      this.baseScale = this.width / this.defaultWidth;
2269      this.baseTransY = Math.abs(this.height - this.defaultHeight * this.baseScale) / (2 * this.baseScale);
2270    }
2271    this.scale *= this.baseScale / curBaseScale;
2272    this.transX *= this.baseScale / curBaseScale;
2273    this.transY *= this.baseScale / curBaseScale;
2274  },
2275
2276  /**
2277   * 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.
2278   */
2279  updateSize: function(){
2280    this.width = this.container.width();
2281    this.height = this.container.height();
2282    this.resize();
2283    this.canvas.setSize(this.width, this.height);
2284    this.applyTransform();
2285  },
2286
2287  /**
2288   * Reset all the series and show the map with the initial zoom.
2289   */
2290  reset: function() {
2291    var key,
2292        i;
2293
2294    for (key in this.series) {
2295      for (i = 0; i < this.series[key].length; i++) {
2296        this.series[key][i].clear();
2297      }
2298    }
2299    this.scale = this.baseScale;
2300    this.transX = this.baseTransX;
2301    this.transY = this.baseTransY;
2302    this.applyTransform();
2303  },
2304
2305  applyTransform: function() {
2306    var maxTransX,
2307        maxTransY,
2308        minTransX,
2309        minTransY;
2310
2311    if (this.defaultWidth * this.scale <= this.width) {
2312      maxTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
2313      minTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
2314    } else {
2315      maxTransX = 0;
2316      minTransX = (this.width - this.defaultWidth * this.scale) / this.scale;
2317    }
2318
2319    if (this.defaultHeight * this.scale <= this.height) {
2320      maxTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
2321      minTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
2322    } else {
2323      maxTransY = 0;
2324      minTransY = (this.height - this.defaultHeight * this.scale) / this.scale;
2325    }
2326
2327    if (this.transY > maxTransY) {
2328      this.transY = maxTransY;
2329    } else if (this.transY < minTransY) {
2330      this.transY = minTransY;
2331    }
2332    if (this.transX > maxTransX) {
2333      this.transX = maxTransX;
2334    } else if (this.transX < minTransX) {
2335      this.transX = minTransX;
2336    }
2337
2338    this.canvas.applyTransformParams(this.scale, this.transX, this.transY);
2339
2340    if (this.markers) {
2341      this.repositionMarkers();
2342    }
2343
2344    this.repositionLabels();
2345
2346    this.container.trigger('viewportChange', [this.scale/this.baseScale, this.transX, this.transY]);
2347  },
2348
2349  bindContainerEvents: function(){
2350    var mouseDown = false,
2351        oldPageX,
2352        oldPageY,
2353        map = this;
2354
2355    if (this.params.panOnDrag) {
2356      this.container.mousemove(function(e){
2357        if (mouseDown) {
2358          map.transX -= (oldPageX - e.pageX) / map.scale;
2359          map.transY -= (oldPageY - e.pageY) / map.scale;
2360
2361          map.applyTransform();
2362
2363          oldPageX = e.pageX;
2364          oldPageY = e.pageY;
2365        }
2366        return false;
2367      }).mousedown(function(e){
2368        mouseDown = true;
2369        oldPageX = e.pageX;
2370        oldPageY = e.pageY;
2371        return false;
2372      });
2373
2374      this.onContainerMouseUp = function(){
2375        mouseDown = false;
2376      };
2377      jvm.$('body').mouseup(this.onContainerMouseUp);
2378    }
2379
2380    if (this.params.zoomOnScroll) {
2381      this.container.mousewheel(function(event, delta, deltaX, deltaY) {
2382        var offset = jvm.$(map.container).offset(),
2383            centerX = event.pageX - offset.left,
2384            centerY = event.pageY - offset.top,
2385            zoomStep = Math.pow(1 + map.params.zoomOnScrollSpeed / 1000, event.deltaFactor * event.deltaY);
2386
2387        map.tip.hide();
2388
2389        map.setScale(map.scale * zoomStep, centerX, centerY);
2390        event.preventDefault();
2391      });
2392    }
2393  },
2394
2395  bindContainerTouchEvents: function(){
2396    var touchStartScale,
2397        touchStartDistance,
2398        map = this,
2399        touchX,
2400        touchY,
2401        centerTouchX,
2402        centerTouchY,
2403        lastTouchesLength,
2404        handleTouchEvent = function(e){
2405          var touches = e.originalEvent.touches,
2406              offset,
2407              scale,
2408              transXOld,
2409              transYOld;
2410
2411          if (e.type == 'touchstart') {
2412            lastTouchesLength = 0;
2413          }
2414
2415          if (touches.length == 1) {
2416            if (lastTouchesLength == 1) {
2417              transXOld = map.transX;
2418              transYOld = map.transY;
2419              map.transX -= (touchX - touches[0].pageX) / map.scale;
2420              map.transY -= (touchY - touches[0].pageY) / map.scale;
2421              map.applyTransform();
2422              map.tip.hide();
2423              if (transXOld != map.transX || transYOld != map.transY) {
2424                e.preventDefault();
2425              }
2426            }
2427            touchX = touches[0].pageX;
2428            touchY = touches[0].pageY;
2429          } else if (touches.length == 2) {
2430            if (lastTouchesLength == 2) {
2431              scale = Math.sqrt(
2432                Math.pow(touches[0].pageX - touches[1].pageX, 2) +
2433                Math.pow(touches[0].pageY - touches[1].pageY, 2)
2434              ) / touchStartDistance;
2435              map.setScale(
2436                touchStartScale * scale,
2437                centerTouchX,
2438                centerTouchY
2439              )
2440              map.tip.hide();
2441              e.preventDefault();
2442            } else {
2443              offset = jvm.$(map.container).offset();
2444              if (touches[0].pageX > touches[1].pageX) {
2445                centerTouchX = touches[1].pageX + (touches[0].pageX - touches[1].pageX) / 2;
2446              } else {
2447                centerTouchX = touches[0].pageX + (touches[1].pageX - touches[0].pageX) / 2;
2448              }
2449              if (touches[0].pageY > touches[1].pageY) {
2450                centerTouchY = touches[1].pageY + (touches[0].pageY - touches[1].pageY) / 2;
2451              } else {
2452                centerTouchY = touches[0].pageY + (touches[1].pageY - touches[0].pageY) / 2;
2453              }
2454              centerTouchX -= offset.left;
2455              centerTouchY -= offset.top;
2456              touchStartScale = map.scale;
2457              touchStartDistance = Math.sqrt(
2458                Math.pow(touches[0].pageX - touches[1].pageX, 2) +
2459                Math.pow(touches[0].pageY - touches[1].pageY, 2)
2460              );
2461            }
2462          }
2463
2464          lastTouchesLength = touches.length;
2465        };
2466
2467    jvm.$(this.container).bind('touchstart', handleTouchEvent);
2468    jvm.$(this.container).bind('touchmove', handleTouchEvent);
2469  },
2470
2471  bindContainerPointerEvents: function(){
2472    var map = this,
2473        gesture = new MSGesture(),
2474        element = this.container[0],
2475        handlePointerDownEvent = function(e){
2476          gesture.addPointer(e.pointerId);
2477        },
2478        handleGestureEvent = function(e){
2479          var offset,
2480              scale,
2481              transXOld,
2482              transYOld;
2483
2484          if (e.translationX != 0 || e.translationY != 0) {
2485            transXOld = map.transX;
2486            transYOld = map.transY;
2487            map.transX += e.translationX / map.scale;
2488            map.transY += e.translationY / map.scale;
2489            map.applyTransform();
2490            map.tip.hide();
2491            if (transXOld != map.transX || transYOld != map.transY) {
2492              e.preventDefault();
2493            }
2494          }
2495          if (e.scale != 1) {
2496            map.setScale(
2497              map.scale * e.scale,
2498              e.offsetX,
2499              e.offsetY
2500            )
2501            map.tip.hide();
2502            e.preventDefault();
2503          }
2504        };
2505
2506    gesture.target = element;
2507    element.addEventListener("MSGestureChange", handleGestureEvent, false);
2508    element.addEventListener("pointerdown", handlePointerDownEvent, false);
2509  },
2510
2511  bindElementEvents: function(){
2512    var map = this,
2513        pageX,
2514        pageY,
2515        mouseMoved;
2516
2517    this.container.mousemove(function(e){
2518      if (Math.abs(pageX - e.pageX) + Math.abs(pageY - e.pageY) > 2) {
2519        mouseMoved = true;
2520      }
2521    });
2522
2523    /* Can not use common class selectors here because of the bug in jQuery
2524       SVG handling, use with caution. */
2525    this.container.delegate("[class~='jvectormap-element']", 'mouseover mouseout', function(e){
2526      var baseVal = jvm.$(this).attr('class').baseVal || jvm.$(this).attr('class'),
2527          type = baseVal.indexOf('jvectormap-region') === -1 ? 'marker' : 'region',
2528          code = type == 'region' ? jvm.$(this).attr('data-code') : jvm.$(this).attr('data-index'),
2529          element = type == 'region' ? map.regions[code].element : map.markers[code].element,
2530          tipText = type == 'region' ? map.mapData.paths[code].name : (map.markers[code].config.name || ''),
2531          tipShowEvent = jvm.$.Event(type+'TipShow.jvectormap'),
2532          overEvent = jvm.$.Event(type+'Over.jvectormap');
2533
2534      if (e.type == 'mouseover') {
2535        map.container.trigger(overEvent, [code]);
2536        if (!overEvent.isDefaultPrevented()) {
2537          element.setHovered(true);
2538        }
2539
2540        map.tip.text(tipText);
2541        map.container.trigger(tipShowEvent, [map.tip, code]);
2542        if (!tipShowEvent.isDefaultPrevented()) {
2543          map.tip.show();
2544          map.tipWidth = map.tip.width();
2545          map.tipHeight = map.tip.height();
2546        }
2547      } else {
2548        element.setHovered(false);
2549        map.tip.hide();
2550        map.container.trigger(type+'Out.jvectormap', [code]);
2551      }
2552    });
2553
2554    /* Can not use common class selectors here because of the bug in jQuery
2555       SVG handling, use with caution. */
2556    this.container.delegate("[class~='jvectormap-element']", 'mousedown', function(e){
2557      pageX = e.pageX;
2558      pageY = e.pageY;
2559      mouseMoved = false;
2560    });
2561
2562    /* Can not use common class selectors here because of the bug in jQuery
2563       SVG handling, use with caution. */
2564    this.container.delegate("[class~='jvectormap-element']", 'mouseup', function(){
2565      var baseVal = jvm.$(this).attr('class').baseVal ? jvm.$(this).attr('class').baseVal : jvm.$(this).attr('class'),
2566          type = baseVal.indexOf('jvectormap-region') === -1 ? 'marker' : 'region',
2567          code = type == 'region' ? jvm.$(this).attr('data-code') : jvm.$(this).attr('data-index'),
2568          clickEvent = jvm.$.Event(type+'Click.jvectormap'),
2569          element = type == 'region' ? map.regions[code].element : map.markers[code].element;
2570
2571      if (!mouseMoved) {
2572        map.container.trigger(clickEvent, [code]);
2573        if ((type === 'region' && map.params.regionsSelectable) || (type === 'marker' && map.params.markersSelectable)) {
2574          if (!clickEvent.isDefaultPrevented()) {
2575            if (map.params[type+'sSelectableOne']) {
2576              map.clearSelected(type+'s');
2577            }
2578            element.setSelected(!element.isSelected);
2579          }
2580        }
2581      }
2582    });
2583  },
2584
2585  bindZoomButtons: function() {
2586    var map = this;
2587
2588    jvm.$('<div/>').addClass('jvectormap-zoomin').text('+').appendTo(this.container);
2589    jvm.$('<div/>').addClass('jvectormap-zoomout').html('&#x2212;').appendTo(this.container);
2590
2591    this.container.find('.jvectormap-zoomin').click(function(){
2592      map.setScale(map.scale * map.params.zoomStep, map.width / 2, map.height / 2, false, map.params.zoomAnimate);
2593    });
2594    this.container.find('.jvectormap-zoomout').click(function(){
2595      map.setScale(map.scale / map.params.zoomStep, map.width / 2, map.height / 2, false, map.params.zoomAnimate);
2596    });
2597  },
2598
2599  createTip: function(){
2600    var map = this;
2601
2602    this.tip = jvm.$('<div/>').addClass('jvectormap-tip').appendTo(jvm.$('body'));
2603
2604    this.container.mousemove(function(e){
2605      var left = e.pageX-15-map.tipWidth,
2606          top = e.pageY-15-map.tipHeight;
2607
2608      if (left < 5) {
2609        left = e.pageX + 15;
2610      }
2611      if (top < 5) {
2612        top = e.pageY + 15;
2613      }
2614
2615      map.tip.css({
2616        left: left,
2617        top: top
2618      });
2619    });
2620  },
2621
2622  setScale: function(scale, anchorX, anchorY, isCentered, animate) {
2623    var viewportChangeEvent = jvm.$.Event('zoom.jvectormap'),
2624        interval,
2625        that = this,
2626        i = 0,
2627        count = Math.abs(Math.round((scale - this.scale) * 60 / Math.max(scale, this.scale))),
2628        scaleStart,
2629        scaleDiff,
2630        transXStart,
2631        transXDiff,
2632        transYStart,
2633        transYDiff,
2634        transX,
2635        transY,
2636        deferred = new jvm.$.Deferred();
2637
2638    if (scale > this.params.zoomMax * this.baseScale) {
2639      scale = this.params.zoomMax * this.baseScale;
2640    } else if (scale < this.params.zoomMin * this.baseScale) {
2641      scale = this.params.zoomMin * this.baseScale;
2642    }
2643
2644    if (typeof anchorX != 'undefined' && typeof anchorY != 'undefined') {
2645      zoomStep = scale / this.scale;
2646      if (isCentered) {
2647        transX = anchorX + this.defaultWidth * (this.width / (this.defaultWidth * scale)) / 2;
2648        transY = anchorY + this.defaultHeight * (this.height / (this.defaultHeight * scale)) / 2;
2649      } else {
2650        transX = this.transX - (zoomStep - 1) / scale * anchorX;
2651        transY = this.transY - (zoomStep - 1) / scale * anchorY;
2652      }
2653    }
2654
2655    if (animate && count > 0)  {
2656      scaleStart = this.scale;
2657      scaleDiff = (scale - scaleStart) / count;
2658      transXStart = this.transX * this.scale;
2659      transYStart = this.transY * this.scale;
2660      transXDiff = (transX * scale - transXStart) / count;
2661      transYDiff = (transY * scale - transYStart) / count;
2662      interval = setInterval(function(){
2663        i += 1;
2664        that.scale = scaleStart + scaleDiff * i;
2665        that.transX = (transXStart + transXDiff * i) / that.scale;
2666        that.transY = (transYStart + transYDiff * i) / that.scale;
2667        that.applyTransform();
2668        if (i == count) {
2669          clearInterval(interval);
2670          that.container.trigger(viewportChangeEvent, [scale/that.baseScale]);
2671          deferred.resolve();
2672        }
2673      }, 10);
2674    } else {
2675      this.transX = transX;
2676      this.transY = transY;
2677      this.scale = scale;
2678      this.applyTransform();
2679      this.container.trigger(viewportChangeEvent, [scale/this.baseScale]);
2680      deferred.resolve();
2681    }
2682
2683    return deferred;
2684  },
2685
2686  /**
2687   * 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.
2688   * @param This method takes a configuration object as the single argument. The options passed to it are the following:
2689   * @param {Array} params.regions Array of region codes to zoom to.
2690   * @param {String} params.region Region code to zoom to.
2691   * @param {Number} params.scale Map scale to set.
2692   * @param {Number} params.lat Latitude to set viewport to.
2693   * @param {Number} params.lng Longitude to set viewport to.
2694   * @param {Number} params.x Number from 0 to 1 specifying the horizontal coordinate of the central point of the viewport.
2695   * @param {Number} params.y Number from 0 to 1 specifying the vertical coordinate of the central point of the viewport.
2696   * @param {Boolean} params.animate Indicates whether or not to animate the scale change and transition.
2697   */
2698  setFocus: function(config){
2699    var bbox,
2700        itemBbox,
2701        newBbox,
2702        codes,
2703        i,
2704        point;
2705
2706    config = config || {};
2707
2708    if (config.region) {
2709      codes = [config.region];
2710    } else if (config.regions) {
2711      codes = config.regions;
2712    }
2713
2714    if (codes) {
2715      for (i = 0; i < codes.length; i++) {
2716        if (this.regions[codes[i]]) {
2717          itemBbox = this.regions[codes[i]].element.shape.getBBox();
2718          if (itemBbox) {
2719            if (typeof bbox == 'undefined') {
2720              bbox = itemBbox;
2721            } else {
2722              newBbox = {
2723                x: Math.min(bbox.x, itemBbox.x),
2724                y: Math.min(bbox.y, itemBbox.y),
2725                width: Math.max(bbox.x + bbox.width, itemBbox.x + itemBbox.width) - Math.min(bbox.x, itemBbox.x),
2726                height: Math.max(bbox.y + bbox.height, itemBbox.y + itemBbox.height) - Math.min(bbox.y, itemBbox.y)
2727              }
2728              bbox = newBbox;
2729            }
2730          }
2731        }
2732      }
2733      return this.setScale(
2734        Math.min(this.width / bbox.width, this.height / bbox.height),
2735        - (bbox.x + bbox.width / 2),
2736        - (bbox.y + bbox.height / 2),
2737        true,
2738        config.animate
2739      );
2740    } else {
2741      if (config.lat && config.lng) {
2742        point = this.latLngToPoint(config.lat, config.lng);
2743        config.x = this.transX - point.x / this.scale;
2744        config.y = this.transY - point.y / this.scale;
2745      } else if (config.x && config.y) {
2746        config.x *= -this.defaultWidth;
2747        config.y *= -this.defaultHeight;
2748      }
2749      return this.setScale(config.scale * this.baseScale, config.x, config.y, true, config.animate);
2750    }
2751  },
2752
2753  getSelected: function(type){
2754    var key,
2755        selected = [];
2756
2757    for (key in this[type]) {
2758      if (this[type][key].element.isSelected) {
2759        selected.push(key);
2760      }
2761    }
2762    return selected;
2763  },
2764
2765  /**
2766   * Return the codes of currently selected regions.
2767   * @returns {Array}
2768   */
2769  getSelectedRegions: function(){
2770    return this.getSelected('regions');
2771  },
2772
2773  /**
2774   * Return the codes of currently selected markers.
2775   * @returns {Array}
2776   */
2777  getSelectedMarkers: function(){
2778    return this.getSelected('markers');
2779  },
2780
2781  setSelected: function(type, keys){
2782    var i;
2783
2784    if (typeof keys != 'object') {
2785      keys = [keys];
2786    }
2787
2788    if (jvm.$.isArray(keys)) {
2789      for (i = 0; i < keys.length; i++) {
2790        this[type][keys[i]].element.setSelected(true);
2791      }
2792    } else {
2793      for (i in keys) {
2794        this[type][i].element.setSelected(!!keys[i]);
2795      }
2796    }
2797  },
2798
2799  /**
2800   * Set or remove selected state for the regions.
2801   * @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.
2802   */
2803  setSelectedRegions: function(keys){
2804    this.setSelected('regions', keys);
2805  },
2806
2807  /**
2808   * Set or remove selected state for the markers.
2809   * @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.
2810   */
2811  setSelectedMarkers: function(keys){
2812    this.setSelected('markers', keys);
2813  },
2814
2815  clearSelected: function(type){
2816    var select = {},
2817        selected = this.getSelected(type),
2818        i;
2819
2820    for (i = 0; i < selected.length; i++) {
2821      select[selected[i]] = false;
2822    };
2823
2824    this.setSelected(type, select);
2825  },
2826
2827  /**
2828   * Remove the selected state from all the currently selected regions.
2829   */
2830  clearSelectedRegions: function(){
2831    this.clearSelected('regions');
2832  },
2833
2834  /**
2835   * Remove the selected state from all the currently selected markers.
2836   */
2837  clearSelectedMarkers: function(){
2838    this.clearSelected('markers');
2839  },
2840
2841  /**
2842   * Return the instance of Map. Useful when instantiated as a jQuery plug-in.
2843   * @returns {Map}
2844   */
2845  getMapObject: function(){
2846    return this;
2847  },
2848
2849  /**
2850   * Return the name of the region by region code.
2851   * @returns {String}
2852   */
2853  getRegionName: function(code){
2854    return this.mapData.paths[code].name;
2855  },
2856
2857  createRegions: function(){
2858    var key,
2859        region,
2860        map = this;
2861
2862    this.regionLabelsGroup = this.regionLabelsGroup || this.canvas.addGroup();
2863
2864    for (key in this.mapData.paths) {
2865      region = new jvm.Region({
2866        map: this,
2867        path: this.mapData.paths[key].path,
2868        code: key,
2869        style: jvm.$.extend(true, {}, this.params.regionStyle),
2870        labelStyle: jvm.$.extend(true, {}, this.params.regionLabelStyle),
2871        canvas: this.canvas,
2872        labelsGroup: this.regionLabelsGroup,
2873        label: this.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.regions) : null
2874      });
2875
2876      jvm.$(region.shape).bind('selected', function(e, isSelected){
2877        map.container.trigger('regionSelected.jvectormap', [jvm.$(this.node).attr('data-code'), isSelected, map.getSelectedRegions()]);
2878      });
2879      this.regions[key] = {
2880        element: region,
2881        config: this.mapData.paths[key]
2882      };
2883    }
2884  },
2885
2886  createMarkers: function(markers) {
2887    var i,
2888        marker,
2889        point,
2890        markerConfig,
2891        markersArray,
2892        map = this;
2893
2894    this.markersGroup = this.markersGroup || this.canvas.addGroup();
2895    this.markerLabelsGroup = this.markerLabelsGroup || this.canvas.addGroup();
2896
2897    if (jvm.$.isArray(markers)) {
2898      markersArray = markers.slice();
2899      markers = {};
2900      for (i = 0; i < markersArray.length; i++) {
2901        markers[i] = markersArray[i];
2902      }
2903    }
2904
2905    for (i in markers) {
2906      markerConfig = markers[i] instanceof Array ? {latLng: markers[i]} : markers[i];
2907      point = this.getMarkerPosition( markerConfig );
2908
2909      if (point !== false) {
2910        marker = new jvm.Marker({
2911          map: this,
2912          style: jvm.$.extend(true, {}, this.params.markerStyle, {initial: markerConfig.style || {}}),
2913          labelStyle: jvm.$.extend(true, {}, this.params.markerLabelStyle),
2914          index: i,
2915          cx: point.x,
2916          cy: point.y,
2917          group: this.markersGroup,
2918          canvas: this.canvas,
2919          labelsGroup: this.markerLabelsGroup,
2920          label: this.canvas.mode != 'vml' ? (this.params.labels && this.params.labels.markers) : null
2921        });
2922
2923        jvm.$(marker.shape).bind('selected', function(e, isSelected){
2924          map.container.trigger('markerSelected.jvectormap', [jvm.$(this.node).attr('data-index'), isSelected, map.getSelectedMarkers()]);
2925        });
2926        if (this.markers[i]) {
2927          this.removeMarkers([i]);
2928        }
2929        this.markers[i] = {element: marker, config: markerConfig};
2930      }
2931    }
2932  },
2933
2934  repositionMarkers: function() {
2935    var i,
2936        point;
2937
2938    for (i in this.markers) {
2939      point = this.getMarkerPosition( this.markers[i].config );
2940      if (point !== false) {
2941        this.markers[i].element.setStyle({cx: point.x, cy: point.y});
2942      }
2943    }
2944  },
2945
2946  repositionLabels: function() {
2947    var key;
2948
2949    for (key in this.regions) {
2950      this.regions[key].element.updateLabelPosition();
2951    }
2952
2953    for (key in this.markers) {
2954      this.markers[key].element.updateLabelPosition();
2955    }
2956  },
2957
2958  getMarkerPosition: function(markerConfig) {
2959    if (jvm.Map.maps[this.params.map].projection) {
2960      return this.latLngToPoint.apply(this, markerConfig.latLng || [0, 0]);
2961    } else {
2962      return {
2963        x: markerConfig.coords[0]*this.scale + this.transX*this.scale,
2964        y: markerConfig.coords[1]*this.scale + this.transY*this.scale
2965      };
2966    }
2967  },
2968
2969  /**
2970   * Add one marker to the map.
2971   * @param {String} key Marker unique code.
2972   * @param {Object} marker Marker configuration parameters.
2973   * @param {Array} seriesData Values to add to the data series.
2974   */
2975  addMarker: function(key, marker, seriesData){
2976    var markers = {},
2977        data = [],
2978        values,
2979        i,
2980        seriesData = seriesData || [];
2981
2982    markers[key] = marker;
2983
2984    for (i = 0; i < seriesData.length; i++) {
2985      values = {};
2986      if (typeof seriesData[i] !== 'undefined') {
2987        values[key] = seriesData[i];
2988      }
2989      data.push(values);
2990    }
2991    this.addMarkers(markers, data);
2992  },
2993
2994  /**
2995   * Add set of marker to the map.
2996   * @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.
2997   * @param {Array} seriesData Values to add to the data series.
2998   */
2999  addMarkers: function(markers, seriesData){
3000    var i;
3001
3002    seriesData = seriesData || [];
3003
3004    this.createMarkers(markers);
3005    for (i = 0; i < seriesData.length; i++) {
3006      this.series.markers[i].setValues(seriesData[i] || {});
3007    };
3008  },
3009
3010  /**
3011   * Remove some markers from the map.
3012   * @param {Array} markers Array of marker codes to be removed.
3013   */
3014  removeMarkers: function(markers){
3015    var i;
3016
3017    for (i = 0; i < markers.length; i++) {
3018      this.markers[ markers[i] ].element.remove();
3019      delete this.markers[ markers[i] ];
3020    };
3021  },
3022
3023  /**
3024   * Remove all markers from the map.
3025   */
3026  removeAllMarkers: function(){
3027    var i,
3028        markers = [];
3029
3030    for (i in this.markers) {
3031      markers.push(i);
3032    }
3033    this.removeMarkers(markers)
3034  },
3035
3036  /**
3037   * Converts coordinates expressed as latitude and longitude to the coordinates in pixels on the map.
3038   * @param {Number} lat Latitide of point in degrees.
3039   * @param {Number} lng Longitude of point in degrees.
3040   */
3041  latLngToPoint: function(lat, lng) {
3042    var point,
3043        proj = jvm.Map.maps[this.params.map].projection,
3044        centralMeridian = proj.centralMeridian,
3045        inset,
3046        bbox;
3047
3048    if (lng < (-180 + centralMeridian)) {
3049      lng += 360;
3050    }
3051
3052    point = jvm.Proj[proj.type](lat, lng, centralMeridian);
3053
3054    inset = this.getInsetForPoint(point.x, point.y);
3055    if (inset) {
3056      bbox = inset.bbox;
3057
3058      point.x = (point.x - bbox[0].x) / (bbox[1].x - bbox[0].x) * inset.width * this.scale;
3059      point.y = (point.y - bbox[0].y) / (bbox[1].y - bbox[0].y) * inset.height * this.scale;
3060
3061      return {
3062        x: point.x + this.transX*this.scale + inset.left*this.scale,
3063        y: point.y + this.transY*this.scale + inset.top*this.scale
3064      };
3065     } else {
3066       return false;
3067     }
3068  },
3069
3070  /**
3071   * Converts cartesian coordinates into coordinates expressed as latitude and longitude.
3072   * @param {Number} x X-axis of point on map in pixels.
3073   * @param {Number} y Y-axis of point on map in pixels.
3074   */
3075  pointToLatLng: function(x, y) {
3076    var proj = jvm.Map.maps[this.params.map].projection,
3077        centralMeridian = proj.centralMeridian,
3078        insets = jvm.Map.maps[this.params.map].insets,
3079        i,
3080        inset,
3081        bbox,
3082        nx,
3083        ny;
3084
3085    for (i = 0; i < insets.length; i++) {
3086      inset = insets[i];
3087      bbox = inset.bbox;
3088
3089      nx = x - (this.transX*this.scale + inset.left*this.scale);
3090      ny = y - (this.transY*this.scale + inset.top*this.scale);
3091
3092      nx = (nx / (inset.width * this.scale)) * (bbox[1].x - bbox[0].x) + bbox[0].x;
3093      ny = (ny / (inset.height * this.scale)) * (bbox[1].y - bbox[0].y) + bbox[0].y;
3094
3095      if (nx > bbox[0].x && nx < bbox[1].x && ny > bbox[0].y && ny < bbox[1].y) {
3096        return jvm.Proj[proj.type + '_inv'](nx, -ny, centralMeridian);
3097      }
3098    }
3099
3100    return false;
3101  },
3102
3103  getInsetForPoint: function(x, y){
3104    var insets = jvm.Map.maps[this.params.map].insets,
3105        i,
3106        bbox;
3107
3108    for (i = 0; i < insets.length; i++) {
3109      bbox = insets[i].bbox;
3110      if (x > bbox[0].x && x < bbox[1].x && y > bbox[0].y && y < bbox[1].y) {
3111        return insets[i];
3112      }
3113    }
3114  },
3115
3116  createSeries: function(){
3117    var i,
3118        key;
3119
3120    this.series = {
3121      markers: [],
3122      regions: []
3123    };
3124
3125    for (key in this.params.series) {
3126      for (i = 0; i < this.params.series[key].length; i++) {
3127        this.series[key][i] = new jvm.DataSeries(
3128          this.params.series[key][i],
3129          this[key],
3130          this
3131        );
3132      }
3133    }
3134  },
3135
3136  /**
3137   * Gracefully remove the map and and all its accessories, unbind event handlers.
3138   */
3139  remove: function(){
3140    this.tip.remove();
3141    this.container.remove();
3142    jvm.$(window).unbind('resize', this.onResize);
3143    jvm.$('body').unbind('mouseup', this.onContainerMouseUp);
3144  }
3145};
3146
3147jvm.Map.maps = {};
3148jvm.Map.defaultParams = {
3149  map: 'world_mill_en',
3150  backgroundColor: '#505050',
3151  zoomButtons: true,
3152  zoomOnScroll: true,
3153  zoomOnScrollSpeed: 3,
3154  panOnDrag: true,
3155  zoomMax: 8,
3156  zoomMin: 1,
3157  zoomStep: 1.6,
3158  zoomAnimate: true,
3159  regionsSelectable: false,
3160  markersSelectable: false,
3161  bindTouchEvents: true,
3162  regionStyle: {
3163    initial: {
3164      fill: 'white',
3165      "fill-opacity": 1,
3166      stroke: 'none',
3167      "stroke-width": 0,
3168      "stroke-opacity": 1
3169    },
3170    hover: {
3171      "fill-opacity": 0.8,
3172      cursor: 'pointer'
3173    },
3174    selected: {
3175      fill: 'yellow'
3176    },
3177    selectedHover: {
3178    }
3179  },
3180  regionLabelStyle: {
3181    initial: {
3182      'font-family': 'Verdana',
3183      'font-size': '12',
3184      'font-weight': 'bold',
3185      cursor: 'default',
3186      fill: 'black'
3187    },
3188    hover: {
3189      cursor: 'pointer'
3190    }
3191  },
3192  markerStyle: {
3193    initial: {
3194      fill: 'grey',
3195      stroke: '#505050',
3196      "fill-opacity": 1,
3197      "stroke-width": 1,
3198      "stroke-opacity": 1,
3199      r: 5
3200    },
3201    hover: {
3202      stroke: 'black',
3203      "stroke-width": 2,
3204      cursor: 'pointer'
3205    },
3206    selected: {
3207      fill: 'blue'
3208    },
3209    selectedHover: {
3210    }
3211  },
3212  markerLabelStyle: {
3213    initial: {
3214      'font-family': 'Verdana',
3215      'font-size': '12',
3216      'font-weight': 'bold',
3217      cursor: 'default',
3218      fill: 'black'
3219    },
3220    hover: {
3221      cursor: 'pointer'
3222    }
3223  }
3224};
3225jvm.Map.apiEvents = {
3226  onRegionTipShow: 'regionTipShow',
3227  onRegionOver: 'regionOver',
3228  onRegionOut: 'regionOut',
3229  onRegionClick: 'regionClick',
3230  onRegionSelected: 'regionSelected',
3231  onMarkerTipShow: 'markerTipShow',
3232  onMarkerOver: 'markerOver',
3233  onMarkerOut: 'markerOut',
3234  onMarkerClick: 'markerClick',
3235  onMarkerSelected: 'markerSelected',
3236  onViewportChange: 'viewportChange'
3237};/**
3238 * Creates map with drill-down functionality.
3239 * @constructor
3240 * @param {Object} params Parameters to initialize map with.
3241 * @param {Number} params.maxLevel Maximum number of levels user can go through
3242 * @param {Object} params.main Config of the main map. See <a href="./jvm-map/">jvm.Map</a> for more information.
3243 * @param {Function} params.mapNameByCode Function go generate map name by region code. Default value is:
3244<pre>
3245function(code, multiMap) {
3246  return code.toLowerCase()+'_'+
3247         multiMap.defaultProjection+'_en';
3248}
3249</pre>
3250 * @param {Function} params.mapUrlByCode Function to generate map url by region code. Default value is:
3251<pre>
3252function(code, multiMap){
3253  return 'jquery-jvectormap-data-'+
3254         code.toLowerCase()+'-'+
3255         multiMap.defaultProjection+'-en.js';
3256}
3257</pre>
3258 */
3259jvm.MultiMap = function(params) {
3260  var that = this;
3261
3262  this.maps = {};
3263  this.params = jvm.$.extend(true, {}, jvm.MultiMap.defaultParams, params);
3264  this.params.maxLevel = this.params.maxLevel || Number.MAX_VALUE;
3265  this.params.main = this.params.main || {};
3266  this.params.main.multiMapLevel = 0;
3267  this.history = [ this.addMap(this.params.main.map, this.params.main) ];
3268  this.defaultProjection = this.history[0].mapData.projection.type;
3269  this.mapsLoaded = {};
3270
3271  this.params.container.css({position: 'relative'});
3272  this.backButton = jvm.$('<div/>').addClass('jvectormap-goback').text('Back').appendTo(this.params.container);
3273  this.backButton.hide();
3274  this.backButton.click(function(){
3275    that.goBack();
3276  });
3277
3278  this.spinner = jvm.$('<div/>').addClass('jvectormap-spinner').appendTo(this.params.container);
3279  this.spinner.hide();
3280};
3281
3282jvm.MultiMap.prototype = {
3283  addMap: function(name, config){
3284    var cnt = jvm.$('<div/>').css({
3285      width: '100%',
3286      height: '100%'
3287    });
3288
3289    this.params.container.append(cnt);
3290
3291    this.maps[name] = new jvm.Map(jvm.$.extend(config, {container: cnt}));
3292    if (this.params.maxLevel > config.multiMapLevel) {
3293      this.maps[name].container.on('regionClick.jvectormap', {scope: this}, function(e, code){
3294        var multimap = e.data.scope,
3295            mapName = multimap.params.mapNameByCode(code, multimap);
3296
3297        if (!multimap.drillDownPromise || multimap.drillDownPromise.state() !== 'pending') {
3298          multimap.drillDown(mapName, code);
3299        }
3300      });
3301    }
3302
3303
3304    return this.maps[name];
3305  },
3306
3307  downloadMap: function(code){
3308    var that = this,
3309        deferred = jvm.$.Deferred();
3310
3311    if (!this.mapsLoaded[code]) {
3312      jvm.$.get(this.params.mapUrlByCode(code, this)).then(function(){
3313        that.mapsLoaded[code] = true;
3314        deferred.resolve();
3315      }, function(){
3316        deferred.reject();
3317      });
3318    } else {
3319      deferred.resolve();
3320    }
3321    return deferred;
3322  },
3323
3324  drillDown: function(name, code){
3325    var currentMap = this.history[this.history.length - 1],
3326        that = this,
3327        focusPromise = currentMap.setFocus({region: code, animate: true}),
3328        downloadPromise = this.downloadMap(code);
3329
3330    focusPromise.then(function(){
3331      if (downloadPromise.state() === 'pending') {
3332        that.spinner.show();
3333      }
3334    });
3335    downloadPromise.always(function(){
3336      that.spinner.hide();
3337    });
3338    this.drillDownPromise = jvm.$.when(downloadPromise, focusPromise);
3339    this.drillDownPromise.then(function(){
3340      currentMap.params.container.hide();
3341      if (!that.maps[name]) {
3342        that.addMap(name, {map: name, multiMapLevel: currentMap.params.multiMapLevel + 1});
3343      } else {
3344        that.maps[name].params.container.show();
3345      }
3346      that.history.push( that.maps[name] );
3347      that.backButton.show();
3348    });
3349  },
3350
3351  goBack: function(){
3352    var currentMap = this.history.pop(),
3353        prevMap = this.history[this.history.length - 1],
3354        that = this;
3355
3356    currentMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true}).then(function(){
3357      currentMap.params.container.hide();
3358      prevMap.params.container.show();
3359      prevMap.updateSize();
3360      if (that.history.length === 1) {
3361        that.backButton.hide();
3362      }
3363      prevMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true});
3364    });
3365  }
3366};
3367
3368jvm.MultiMap.defaultParams = {
3369  mapNameByCode: function(code, multiMap){
3370    return code.toLowerCase()+'_'+multiMap.defaultProjection+'_en';
3371  },
3372  mapUrlByCode: function(code, multiMap){
3373    return 'jquery-jvectormap-data-'+code.toLowerCase()+'-'+multiMap.defaultProjection+'-en.js';
3374  }
3375}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.