shop.balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

jquery.vmap.js (32675B)


      1 /*!
      2  * jQVMap Version 1.0
      3  *
      4  * http://jqvmap.com
      5  *
      6  * Copyright 2012, Peter Schmalfeldt <manifestinteractive@gmail.com>
      7  * Copyright 2011-2012, Kirill Lebedev
      8  * Licensed under the MIT license.
      9  *
     10  * Fork Me @ https://github.com/manifestinteractive/jqvmap
     11  */
     12 (function ($) {
     13 
     14   var apiParams = {
     15     colors: 1,
     16     values: 1,
     17     backgroundColor: 1,
     18     scaleColors: 1,
     19     normalizeFunction: 1,
     20     enableZoom: 1,
     21     showTooltip: 1,
     22     borderColor: 1,
     23     borderWidth: 1,
     24     borderOpacity: 1,
     25     selectedRegions: 1,
     26     multiSelectRegion: 1
     27   };
     28 
     29   var apiEvents = {
     30     onLabelShow: 'labelShow',
     31     onRegionOver: 'regionMouseOver',
     32     onRegionOut: 'regionMouseOut',
     33     onRegionClick: 'regionClick',
     34     onRegionSelect: 'regionSelect',
     35     onRegionDeselect: 'regionDeselect'
     36   };
     37 
     38   $.fn.vectorMap = function (options) {
     39 
     40     var defaultParams = {
     41       map: 'world_en',
     42       backgroundColor: '#a5bfdd',
     43       color: '#f4f3f0',
     44       hoverColor: '#c9dfaf',
     45       selectedColor: '#c9dfaf',
     46       scaleColors: ['#b6d6ff', '#005ace'],
     47       normalizeFunction: 'linear',
     48       enableZoom: true,
     49       showTooltip: true,
     50       borderColor: '#818181',
     51       borderWidth: 1,
     52       borderOpacity: 0.25,
     53       selectedRegions: null,
     54       multiSelectRegion: false
     55     }, map = this.data('mapObject');
     56 
     57     if (options === 'addMap') {
     58       WorldMap.maps[arguments[1]] = arguments[2];
     59     } else if (options === 'set' && apiParams[arguments[1]]) {
     60       map['set' + arguments[1].charAt(0).toUpperCase() + arguments[1].substr(1)].apply(map, Array.prototype.slice.call(arguments, 2));
     61     } else if (typeof options === 'string' &&
     62                typeof map[options] === 'function') {
     63       return map[options].apply(map, Array.prototype.slice.call(arguments, 1));
     64     } else {
     65       $.extend(defaultParams, options);
     66       defaultParams.container = this;
     67       this.css({ position: 'relative', overflow: 'hidden' });
     68 
     69       map = new WorldMap(defaultParams);
     70 
     71       this.data('mapObject', map);
     72 
     73       for (var e in apiEvents) {
     74         if (defaultParams[e]) {
     75           this.bind(apiEvents[e] + '.jqvmap', defaultParams[e]);
     76         }
     77       }
     78     }
     79   };
     80 
     81   var VectorCanvas = function (width, height, params) {
     82     this.mode = window.SVGAngle ? 'svg' : 'vml';
     83     this.params = params;
     84 
     85     if (this.mode == 'svg') {
     86       this.createSvgNode = function (nodeName) {
     87         return document.createElementNS(this.svgns, nodeName);
     88       };
     89     } else {
     90       try {
     91         if (!document.namespaces.rvml) {
     92           document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
     93         }
     94         this.createVmlNode = function (tagName) {
     95           return document.createElement('<rvml:' + tagName + ' class="rvml">');
     96         };
     97       } catch (e) {
     98         this.createVmlNode = function (tagName) {
     99           return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
    100         };
    101       }
    102 
    103       document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
    104     }
    105 
    106     if (this.mode == 'svg') {
    107       this.canvas = this.createSvgNode('svg');
    108     } else {
    109       this.canvas = this.createVmlNode('group');
    110       this.canvas.style.position = 'absolute';
    111     }
    112 
    113     this.setSize(width, height);
    114   };
    115 
    116   VectorCanvas.prototype = {
    117     svgns: "http://www.w3.org/2000/svg",
    118     mode: 'svg',
    119     width: 0,
    120     height: 0,
    121     canvas: null,
    122 
    123     setSize: function (width, height) {
    124       if (this.mode == 'svg') {
    125         this.canvas.setAttribute('width', width);
    126         this.canvas.setAttribute('height', height);
    127       } else {
    128         this.canvas.style.width = width + "px";
    129         this.canvas.style.height = height + "px";
    130         this.canvas.coordsize = width + ' ' + height;
    131         this.canvas.coordorigin = "0 0";
    132         if (this.rootGroup) {
    133           var pathes = this.rootGroup.getElementsByTagName('shape');
    134           for (var i = 0, l = pathes.length; i < l; i++) {
    135             pathes[i].coordsize = width + ' ' + height;
    136             pathes[i].style.width = width + 'px';
    137             pathes[i].style.height = height + 'px';
    138           }
    139           this.rootGroup.coordsize = width + ' ' + height;
    140           this.rootGroup.style.width = width + 'px';
    141           this.rootGroup.style.height = height + 'px';
    142         }
    143       }
    144       this.width = width;
    145       this.height = height;
    146     },
    147 
    148     createPath: function (config) {
    149       var node;
    150       if (this.mode == 'svg') {
    151         node = this.createSvgNode('path');
    152         node.setAttribute('d', config.path);
    153 
    154         if (this.params.borderColor !== null) {
    155           node.setAttribute('stroke', this.params.borderColor);
    156         }
    157         if (this.params.borderWidth > 0) {
    158           node.setAttribute('stroke-width', this.params.borderWidth);
    159           node.setAttribute('stroke-linecap', 'round');
    160           node.setAttribute('stroke-linejoin', 'round');
    161         }
    162         if (this.params.borderOpacity > 0) {
    163           node.setAttribute('stroke-opacity', this.params.borderOpacity);
    164         }
    165 
    166         node.setFill = function (color) {
    167           this.setAttribute("fill", color);
    168           if (this.getAttribute("original") === null) {
    169             this.setAttribute("original", color);
    170           }
    171         };
    172 
    173         node.getFill = function (color) {
    174           return this.getAttribute("fill");
    175         };
    176 
    177         node.getOriginalFill = function () {
    178           return this.getAttribute("original");
    179         };
    180 
    181         node.setOpacity = function (opacity) {
    182           this.setAttribute('fill-opacity', opacity);
    183         };
    184       } else {
    185         node = this.createVmlNode('shape');
    186         node.coordorigin = "0 0";
    187         node.coordsize = this.width + ' ' + this.height;
    188         node.style.width = this.width + 'px';
    189         node.style.height = this.height + 'px';
    190         node.fillcolor = WorldMap.defaultFillColor;
    191         node.stroked = false;
    192         node.path = VectorCanvas.pathSvgToVml(config.path);
    193 
    194         var scale = this.createVmlNode('skew');
    195         scale.on = true;
    196         scale.matrix = '0.01,0,0,0.01,0,0';
    197         scale.offset = '0,0';
    198 
    199         node.appendChild(scale);
    200 
    201         var fill = this.createVmlNode('fill');
    202         node.appendChild(fill);
    203 
    204         node.setFill = function (color) {
    205           this.getElementsByTagName('fill')[0].color = color;
    206           if (this.getAttribute("original") === null) {
    207             this.setAttribute("original", color);
    208           }
    209         };
    210 
    211         node.getFill = function (color) {
    212           return this.getElementsByTagName('fill')[0].color;
    213         };
    214         node.getOriginalFill = function () {
    215           return this.getAttribute("original");
    216         };
    217         node.setOpacity = function (opacity) {
    218           this.getElementsByTagName('fill')[0].opacity = parseInt(opacity * 100, 10) + '%';
    219         };
    220       }
    221       return node;
    222     },
    223 
    224     createGroup: function (isRoot) {
    225       var node;
    226       if (this.mode == 'svg') {
    227         node = this.createSvgNode('g');
    228       } else {
    229         node = this.createVmlNode('group');
    230         node.style.width = this.width + 'px';
    231         node.style.height = this.height + 'px';
    232         node.style.left = '0px';
    233         node.style.top = '0px';
    234         node.coordorigin = "0 0";
    235         node.coordsize = this.width + ' ' + this.height;
    236       }
    237 
    238       if (isRoot) {
    239         this.rootGroup = node;
    240       }
    241       return node;
    242     },
    243 
    244     applyTransformParams: function (scale, transX, transY) {
    245       if (this.mode == 'svg') {
    246         this.rootGroup.setAttribute('transform', 'scale(' + scale + ') translate(' + transX + ', ' + transY + ')');
    247       } else {
    248         this.rootGroup.coordorigin = (this.width - transX) + ',' + (this.height - transY);
    249         this.rootGroup.coordsize = this.width / scale + ',' + this.height / scale;
    250       }
    251     }
    252   };
    253 
    254   VectorCanvas.pathSvgToVml = function (path) {
    255     var result = '';
    256     var cx = 0, cy = 0, ctrlx, ctrly;
    257 
    258     return path.replace(/([MmLlHhVvCcSs])((?:-?(?:\d+)?(?:\.\d+)?,?\s?)+)/g, function (segment, letter, coords, index) {
    259       coords = coords.replace(/(\d)-/g, '$1,-').replace(/\s+/g, ',').split(',');
    260       if (!coords[0]) {
    261         coords.shift();
    262       }
    263 
    264       for (var i = 0, l = coords.length; i < l; i++) {
    265         coords[i] = Math.round(100 * coords[i]);
    266       }
    267 
    268       switch (letter) {
    269       case 'm':
    270         cx += coords[0];
    271         cy += coords[1];
    272         return 't' + coords.join(',');
    273         break;
    274 
    275       case 'M':
    276         cx = coords[0];
    277         cy = coords[1];
    278         return 'm' + coords.join(',');
    279         break;
    280 
    281       case 'l':
    282         cx += coords[0];
    283         cy += coords[1];
    284         return 'r' + coords.join(',');
    285         break;
    286 
    287       case 'L':
    288         cx = coords[0];
    289         cy = coords[1];
    290         return 'l' + coords.join(',');
    291         break;
    292 
    293       case 'h':
    294         cx += coords[0];
    295         return 'r' + coords[0] + ',0';
    296         break;
    297 
    298       case 'H':
    299         cx = coords[0];
    300         return 'l' + cx + ',' + cy;
    301         break;
    302 
    303       case 'v':
    304         cy += coords[0];
    305         return 'r0,' + coords[0];
    306         break;
    307 
    308       case 'V':
    309         cy = coords[0];
    310         return 'l' + cx + ',' + cy;
    311         break;
    312 
    313       case 'c':
    314         ctrlx = cx + coords[coords.length - 4];
    315         ctrly = cy + coords[coords.length - 3];
    316         cx += coords[coords.length - 2];
    317         cy += coords[coords.length - 1];
    318         return 'v' + coords.join(',');
    319         break;
    320 
    321       case 'C':
    322         ctrlx = coords[coords.length - 4];
    323         ctrly = coords[coords.length - 3];
    324         cx = coords[coords.length - 2];
    325         cy = coords[coords.length - 1];
    326         return 'c' + coords.join(',');
    327         break;
    328 
    329       case 's':
    330         coords.unshift(cy - ctrly);
    331         coords.unshift(cx - ctrlx);
    332         ctrlx = cx + coords[coords.length - 4];
    333         ctrly = cy + coords[coords.length - 3];
    334         cx += coords[coords.length - 2];
    335         cy += coords[coords.length - 1];
    336         return 'v' + coords.join(',');
    337         break;
    338 
    339       case 'S':
    340         coords.unshift(cy + cy - ctrly);
    341         coords.unshift(cx + cx - ctrlx);
    342         ctrlx = coords[coords.length - 4];
    343         ctrly = coords[coords.length - 3];
    344         cx = coords[coords.length - 2];
    345         cy = coords[coords.length - 1];
    346         return 'c' + coords.join(',');
    347         break;
    348 
    349       default:
    350         return false;
    351         break;
    352       }
    353 
    354       return '';
    355 
    356     }).replace(/z/g, '');
    357   };
    358 
    359   var WorldMap = function (params) {
    360     params = params || {};
    361     var map = this;
    362     var mapData = WorldMap.maps[params.map];
    363 
    364     this.selectedRegions = [];
    365     this.multiSelectRegion = params.multiSelectRegion;
    366 
    367     this.container = params.container;
    368 
    369     this.defaultWidth = mapData.width;
    370     this.defaultHeight = mapData.height;
    371 
    372     this.color = params.color;
    373     this.selectedColor = params.selectedColor;
    374     this.hoverColor = params.hoverColor;
    375     this.hoverOpacity = params.hoverOpacity;
    376     this.setBackgroundColor(params.backgroundColor);
    377 
    378     this.width = params.container.width();
    379     this.height = params.container.height();
    380 
    381     this.resize();
    382 
    383     jQuery(window).resize(function () {
    384       map.width = params.container.width();
    385       map.height = params.container.height();
    386       map.resize();
    387       map.canvas.setSize(map.width, map.height);
    388       map.applyTransform();
    389     });
    390 
    391     this.canvas = new VectorCanvas(this.width, this.height, params);
    392     params.container.append(this.canvas.canvas);
    393 
    394     this.makeDraggable();
    395 
    396     this.rootGroup = this.canvas.createGroup(true);
    397 
    398     this.index = WorldMap.mapIndex;
    399     this.label = jQuery('<div/>').addClass('jqvmap-label').appendTo(jQuery('body')).hide();
    400 
    401     if (params.enableZoom) {
    402       jQuery('<div/>').addClass('jqvmap-zoomin').text('+').appendTo(params.container);
    403       jQuery('<div/>').addClass('jqvmap-zoomout').html('&#x2212;').appendTo(params.container);
    404     }
    405 
    406     map.countries = [];
    407 
    408     for (var key in mapData.pathes) {
    409       var path = this.canvas.createPath({
    410         path: mapData.pathes[key].path
    411       });
    412 
    413       path.setFill(this.color);
    414       path.id = map.getCountryId(key);
    415       map.countries[key] = path;
    416 
    417       if (this.canvas.mode == 'svg') {
    418         path.setAttribute('class', 'jvectormap-region');
    419       } else {
    420         jQuery(path).addClass('jvectormap-region');
    421       }
    422 
    423       jQuery(this.rootGroup).append(path);
    424     }
    425 
    426     jQuery(params.container).delegate(this.canvas.mode == 'svg' ? 'path' : 'shape', 'mouseover mouseout', function (e) {
    427       var path = e.target,
    428       code = e.target.id.split('_').pop(),
    429       labelShowEvent = $.Event('labelShow.jqvmap'),
    430       regionMouseOverEvent = $.Event('regionMouseOver.jqvmap');
    431 
    432       if (e.type == 'mouseover') {
    433         jQuery(params.container).trigger(regionMouseOverEvent, [code, mapData.pathes[code].name]);
    434         if (!regionMouseOverEvent.isDefaultPrevented()) {
    435           map.highlight(code, path);
    436         }
    437         if (params.showTooltip) {
    438           map.label.text(mapData.pathes[code].name);
    439           jQuery(params.container).trigger(labelShowEvent, [map.label, code]);
    440 
    441           if (!labelShowEvent.isDefaultPrevented()) {
    442             map.label.show();
    443             map.labelWidth = map.label.width();
    444             map.labelHeight = map.label.height();
    445           }
    446         }
    447       } else {
    448         map.unhighlight(code, path);
    449 
    450         map.label.hide();
    451         jQuery(params.container).trigger('regionMouseOut.jqvmap', [code, mapData.pathes[code].name]);
    452       }
    453     });
    454 
    455     jQuery(params.container).delegate(this.canvas.mode == 'svg' ? 'path' : 'shape', 'click', function (e) {
    456       if (!params.multiSelectRegion) {
    457         for (var key in mapData.pathes) {
    458           map.countries[key].currentFillColor = map.countries[key].getOriginalFill();
    459           map.countries[key].setFill(map.countries[key].getOriginalFill());
    460         }
    461       }
    462 
    463       var path = e.target;
    464       var code = e.target.id.split('_').pop();
    465 
    466       jQuery(params.container).trigger('regionClick.jqvmap', [code, mapData.pathes[code].name]);
    467       if (!regionClickEvent.isDefaultPrevented()) {
    468         if (map.selectedRegions.indexOf(code) !== -1) {
    469           map.deselect(code, path);
    470         } else {
    471           map.select(code, path);
    472         }
    473       }
    474 
    475       //console.log(selectedRegions);
    476 
    477     });
    478 
    479     if (params.showTooltip) {
    480       params.container.mousemove(function (e) {
    481         if (map.label.is(':visible')) {
    482             var left = e.pageX - 15 - map.labelWidth;
    483             var top = e.pageY - 15 - map.labelHeight;
    484             
    485             if(left < 0)
    486                left = e.pageX + 15;
    487             if(top < 0)
    488                 top = e.pageY + 15;
    489             
    490             map.label.css({
    491                 left: left,
    492                 top: top
    493           });
    494         }
    495       });
    496     }
    497 
    498     this.setColors(params.colors);
    499 
    500     this.canvas.canvas.appendChild(this.rootGroup);
    501 
    502     this.applyTransform();
    503 
    504     this.colorScale = new ColorScale(params.scaleColors, params.normalizeFunction, params.valueMin, params.valueMax);
    505 
    506     if (params.values) {
    507       this.values = params.values;
    508       this.setValues(params.values);
    509     }
    510 
    511     if (params.selectedRegions) {
    512       if (params.selectedRegions instanceof Array) {
    513         for(var k in params.selectedRegions) {
    514           this.select(params.selectedRegions[k].toLowerCase());
    515         }
    516       } else {
    517         this.select(params.selectedRegions.toLowerCase());
    518       }
    519     }
    520 
    521     this.bindZoomButtons();
    522     
    523     if(params.pins) {
    524       /*if(params.pinMode) {
    525           if(params.pinMode != "id" && params.pinMode != "content") {
    526               params.pinMode = "content";
    527           }
    528       } else {
    529           params.pinMode = "content";
    530       }*/
    531       this.pinHandlers = false;
    532       this.placePins(params.pins, params.pinMode);
    533     }
    534 
    535     WorldMap.mapIndex++;
    536   };
    537 
    538   WorldMap.prototype = {
    539     transX: 0,
    540     transY: 0,
    541     scale: 1,
    542     baseTransX: 0,
    543     baseTransY: 0,
    544     baseScale: 1,
    545     width: 0,
    546     height: 0,
    547     countries: {},
    548     countriesColors: {},
    549     countriesData: {},
    550     zoomStep: 1.4,
    551     zoomMaxStep: 4,
    552     zoomCurStep: 1,
    553 
    554     setColors: function (key, color) {
    555       if (typeof key == 'string') {
    556         this.countries[key].setFill(color);
    557         this.countries[key].setAttribute("original", color);
    558       } else {
    559         var colors = key;
    560 
    561         for (var code in colors) {
    562           if (this.countries[code]) {
    563             this.countries[code].setFill(colors[code]);
    564             this.countries[code].setAttribute("original", colors[code]);
    565           }
    566         }
    567       }
    568     },
    569 
    570     setValues: function (values) {
    571       var max = 0,
    572       min = Number.MAX_VALUE,
    573       val;
    574 
    575       for (var cc in values) {
    576         val = parseFloat(values[cc]);
    577         if (val > max) {
    578           max = values[cc];
    579         }
    580         if (val && val < min) {
    581           min = val;
    582         }
    583       }
    584 
    585       this.colorScale.setMin(min);
    586       this.colorScale.setMax(max);
    587 
    588       var colors = {};
    589       for (cc in values) {
    590         val = parseFloat(values[cc]);
    591         if (val) {
    592           colors[cc] = this.colorScale.getColor(val);
    593         } else {
    594           colors[cc] = this.color;
    595         }
    596       }
    597       this.setColors(colors);
    598       this.values = values;
    599     },
    600 
    601     setBackgroundColor: function (backgroundColor) {
    602       this.container.css('background-color', backgroundColor);
    603     },
    604 
    605     setScaleColors: function (colors) {
    606       this.colorScale.setColors(colors);
    607 
    608       if (this.values) {
    609         this.setValues(this.values);
    610       }
    611     },
    612 
    613     setNormalizeFunction: function (f) {
    614       this.colorScale.setNormalizeFunction(f);
    615 
    616       if (this.values) {
    617         this.setValues(this.values);
    618       }
    619     },
    620 
    621     highlight: function (cc, path) {
    622       path = path || $('#' + this.getCountryId(cc))[0];
    623       if (this.hoverOpacity) {
    624         path.setOpacity(this.hoverOpacity);
    625       } else if (this.hoverColor) {
    626         path.currentFillColor = path.getFill() + '';
    627         path.setFill(this.hoverColor);
    628       }
    629     },
    630 
    631     unhighlight: function (cc, path) {
    632       path = path || $('#' + this.getCountryId(cc))[0];
    633       path.setOpacity(1);
    634       if (path.currentFillColor) {
    635         path.setFill(path.currentFillColor);
    636       }
    637     },
    638 
    639     select: function (cc, path) {
    640       path = path || $('#' + this.getCountryId(cc))[0];
    641       if(this.selectedRegions.indexOf(cc) < 0) {
    642         if (this.multiSelectRegion) {
    643           this.selectedRegions.push(cc);
    644         } else {
    645           this.selectedRegions = [cc];
    646         }
    647         // MUST BE after the change of selectedRegions
    648         // Otherwise, we might loop
    649         $(this.container).trigger('regionSelect.jqvmap', [cc]);
    650         if (this.selectedColor) {
    651           path.currentFillColor = this.selectedColor;
    652           path.setFill(this.selectedColor);
    653         }
    654       }
    655     },
    656 
    657     deselect: function (cc, path) {
    658       path = path || $('#' + this.getCountryId(cc))[0];
    659       if(this.selectedRegions.indexOf(cc) >= 0) {
    660         this.selectedRegions.splice(this.selectedRegions.indexOf(cc), 1);
    661         // MUST BE after the change of selectedRegions
    662         // Otherwise, we might loop
    663         $(this.container).trigger('regionDeselect.jqvmap', [cc]);
    664         path.currentFillColor = path.getOriginalFill();
    665         path.setFill(path.getOriginalFill());
    666       }
    667     },
    668 
    669     isSelected: function(cc) {
    670       return this.selectedRegions.indexOf(cc) >= 0;
    671     },
    672 
    673     resize: function () {
    674       var curBaseScale = this.baseScale;
    675       if (this.width / this.height > this.defaultWidth / this.defaultHeight) {
    676         this.baseScale = this.height / this.defaultHeight;
    677         this.baseTransX = Math.abs(this.width - this.defaultWidth * this.baseScale) / (2 * this.baseScale);
    678       } else {
    679         this.baseScale = this.width / this.defaultWidth;
    680         this.baseTransY = Math.abs(this.height - this.defaultHeight * this.baseScale) / (2 * this.baseScale);
    681       }
    682       this.scale *= this.baseScale / curBaseScale;
    683       this.transX *= this.baseScale / curBaseScale;
    684       this.transY *= this.baseScale / curBaseScale;
    685     },
    686 
    687     reset: function () {
    688       this.countryTitle.reset();
    689       for (var key in this.countries) {
    690         this.countries[key].setFill(WorldMap.defaultColor);
    691       }
    692       this.scale = this.baseScale;
    693       this.transX = this.baseTransX;
    694       this.transY = this.baseTransY;
    695       this.applyTransform();
    696     },
    697 
    698     applyTransform: function () {
    699       var maxTransX, maxTransY, minTransX, minTransY;
    700       if (this.defaultWidth * this.scale <= this.width) {
    701         maxTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
    702         minTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
    703       } else {
    704         maxTransX = 0;
    705         minTransX = (this.width - this.defaultWidth * this.scale) / this.scale;
    706       }
    707 
    708       if (this.defaultHeight * this.scale <= this.height) {
    709         maxTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
    710         minTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
    711       } else {
    712         maxTransY = 0;
    713         minTransY = (this.height - this.defaultHeight * this.scale) / this.scale;
    714       }
    715 
    716       if (this.transY > maxTransY) {
    717         this.transY = maxTransY;
    718       }
    719       else if (this.transY < minTransY) {
    720         this.transY = minTransY;
    721       }
    722       if (this.transX > maxTransX) {
    723         this.transX = maxTransX;
    724       }
    725       else if (this.transX < minTransX) {
    726         this.transX = minTransX;
    727       }
    728 
    729       this.canvas.applyTransformParams(this.scale, this.transX, this.transY);
    730     },
    731 
    732     makeDraggable: function () {
    733       var mouseDown = false;
    734       var oldPageX, oldPageY;
    735       var self = this;
    736 
    737       self.isMoving = false;
    738       self.isMovingTimeout = false;
    739 
    740       this.container.mousemove(function (e) {
    741 
    742         if (mouseDown) {
    743           var curTransX = self.transX;
    744           var curTransY = self.transY;
    745 
    746           self.transX -= (oldPageX - e.pageX) / self.scale;
    747           self.transY -= (oldPageY - e.pageY) / self.scale;
    748 
    749           self.applyTransform();
    750 
    751           oldPageX = e.pageX;
    752           oldPageY = e.pageY;
    753 
    754           self.isMoving = true;
    755           if (self.isMovingTimeout) {
    756             clearTimeout(self.isMovingTimeout);
    757           }
    758 
    759           self.container.trigger('drag');
    760         }
    761 
    762         return false;
    763 
    764       }).mousedown(function (e) {
    765 
    766         mouseDown = true;
    767         oldPageX = e.pageX;
    768         oldPageY = e.pageY;
    769 
    770         return false;
    771 
    772       }).mouseup(function () {
    773 
    774         mouseDown = false;
    775 
    776         self.isMovingTimeout = setTimeout(function () {
    777           self.isMoving = false;
    778         }, 100);
    779 
    780         return false;
    781 
    782       });
    783     },
    784 
    785     bindZoomButtons: function () {
    786       var map = this;
    787       this.container.find('.jqvmap-zoomin').click(function(){
    788         map.zoomIn();
    789       });
    790       this.container.find('.jqvmap-zoomout').click(function(){
    791         map.zoomOut();
    792       });
    793     },
    794     
    795     zoomIn: function () {
    796       var map = this;
    797       var sliderDelta = (jQuery('#zoom').innerHeight() - 6 * 2 - 15 * 2 - 3 * 2 - 7 - 6) / (this.zoomMaxStep - this.zoomCurStep);
    798 
    799       if (map.zoomCurStep < map.zoomMaxStep) {
    800         var curTransX = map.transX;
    801         var curTransY = map.transY;
    802         var curScale = map.scale;
    803 
    804         map.transX -= (map.width / map.scale - map.width / (map.scale * map.zoomStep)) / 2;
    805         map.transY -= (map.height / map.scale - map.height / (map.scale * map.zoomStep)) / 2;
    806         map.setScale(map.scale * map.zoomStep);
    807         map.zoomCurStep++;
    808 
    809         jQuery('#zoomSlider').css('top', parseInt(jQuery('#zoomSlider').css('top'), 10) - sliderDelta);
    810         
    811         map.container.trigger("zoomIn");
    812       }
    813     },
    814     
    815     zoomOut: function () {
    816       var map = this;
    817       var sliderDelta = (jQuery('#zoom').innerHeight() - 6 * 2 - 15 * 2 - 3 * 2 - 7 - 6) / (this.zoomMaxStep - this.zoomCurStep);
    818 
    819       if (map.zoomCurStep > 1) {
    820         var curTransX = map.transX;
    821         var curTransY = map.transY;
    822         var curScale = map.scale;
    823 
    824         map.transX += (map.width / (map.scale / map.zoomStep) - map.width / map.scale) / 2;
    825         map.transY += (map.height / (map.scale / map.zoomStep) - map.height / map.scale) / 2;
    826         map.setScale(map.scale / map.zoomStep);
    827         map.zoomCurStep--;
    828 
    829         jQuery('#zoomSlider').css('top', parseInt(jQuery('#zoomSlider').css('top'), 10) + sliderDelta);
    830         
    831         map.container.trigger("zoomOut");
    832       }
    833     },
    834 
    835     setScale: function (scale) {
    836       this.scale = scale;
    837       this.applyTransform();
    838     },
    839 
    840     getCountryId: function (cc) {
    841       return 'jqvmap' + this.index + '_' + cc;
    842     },
    843 
    844     getPinId: function (cc) {
    845       return this.getCountryId(cc)+'_pin';
    846     },
    847     
    848     placePins: function(pins, pinMode){
    849       var map = this;
    850 
    851       if(!pinMode || (pinMode != "content" && pinMode != "id")) {
    852         pinMode = "content";
    853       }
    854 
    855       if(pinMode == "content") {//treat pin as content
    856         jQuery.each(pins, function(index, pin){
    857           if(jQuery('#'+map.getCountryId(index)).length == 0){
    858               return;
    859           }
    860           //mapData.pathes[code].name
    861           var pinIndex = map.getPinId(index);
    862           if(jQuery('#'+pinIndex).length > 0){
    863             jQuery('#'+pinIndex).remove();
    864           }
    865           map.container.append('<div id="' + pinIndex + '" for="'+index+'" class="jqvmap_pin" style="position:absolute">' + pin + '</div>');
    866         });
    867       } else { //treat pin as id of an html content
    868         jQuery.each(pins, function(index, pin){
    869           if(jQuery('#'+map.getCountryId(index)).length == 0){
    870               return;
    871           }
    872           var pinIndex = map.getPinId(index);
    873           if(jQuery('#'+pinIndex).length > 0){
    874             jQuery('#'+pinIndex).remove();
    875           }
    876           map.container.append('<div id="' + pinIndex + '" for="'+index+'" class="jqvmap_pin" style="position:absolute"></div>');
    877           jQuery('#'+pinIndex).append(jQuery('#'+pin));
    878         });
    879       }
    880 
    881       this.positionPins();
    882       if(!this.pinHandlers){
    883         this.pinHandlers = true;//do only once
    884         var positionFix = function(){
    885           map.positionPins();
    886         };
    887         this.container.bind('zoomIn', positionFix)
    888         .bind('zoomOut', positionFix)
    889         .bind('drag', positionFix);
    890       }
    891     },
    892 
    893     positionPins: function(){
    894       var map = this;
    895       var pins = this.container.find('.jqvmap_pin');
    896       jQuery.each(pins, function(index, pinObj){
    897         pinObj = jQuery(pinObj);
    898         var countryId = map.getCountryId(pinObj.attr('for'));
    899         var countryObj = jQuery('#' + countryId);
    900 
    901         var bbox = document.getElementById(countryId).getBBox();
    902         var position = countryObj.position();
    903 
    904         var scale = map.scale;
    905 
    906         var left = position.left + (bbox.width / 2) * scale - pinObj.width() / 2,
    907         top = position.top + (bbox.height / 2) * scale - pinObj.height() / 2;
    908 
    909         pinObj.css('left',left).css('top',top);
    910       });
    911      },
    912 
    913      getPin: function(cc){
    914        var pinObj = jQuery('#'+this.getPinId(cc));
    915        return pinObj.html();
    916      },
    917 
    918      getPins: function(){
    919        var pins = this.container.find('.jqvmap_pin');
    920        var ret = new Object();
    921        jQuery.each(pins, function(index, pinObj){
    922          pinObj = jQuery(pinObj);
    923          var cc = pinObj.attr('for');
    924          var pinContent = pinObj.html();
    925          eval("ret." + cc + "=pinContent");
    926        });
    927        return JSON.stringify(ret);
    928      },
    929 
    930      removePin: function(cc) {
    931        jQuery('#'+this.getPinId(cc)).remove();
    932      },
    933 
    934      removePins: function(){
    935        this.container.find('.jqvmap_pin').remove();
    936      }
    937   };
    938 
    939   WorldMap.xlink = "http://www.w3.org/1999/xlink";
    940   WorldMap.mapIndex = 1;
    941   WorldMap.maps = {};
    942 
    943   var ColorScale = function (colors, normalizeFunction, minValue, maxValue) {
    944     if (colors) {
    945       this.setColors(colors);
    946     }
    947     if (normalizeFunction) {
    948       this.setNormalizeFunction(normalizeFunction);
    949     }
    950     if (minValue) {
    951       this.setMin(minValue);
    952     }
    953     if (minValue) {
    954       this.setMax(maxValue);
    955     }
    956   };
    957 
    958   ColorScale.prototype = {
    959     colors: [],
    960 
    961     setMin: function (min) {
    962       this.clearMinValue = min;
    963 
    964       if (typeof this.normalize === 'function') {
    965         this.minValue = this.normalize(min);
    966       } else {
    967         this.minValue = min;
    968       }
    969     },
    970 
    971     setMax: function (max) {
    972       this.clearMaxValue = max;
    973       if (typeof this.normalize === 'function') {
    974         this.maxValue = this.normalize(max);
    975       } else {
    976         this.maxValue = max;
    977       }
    978     },
    979 
    980     setColors: function (colors) {
    981       for (var i = 0; i < colors.length; i++) {
    982         colors[i] = ColorScale.rgbToArray(colors[i]);
    983       }
    984       this.colors = colors;
    985     },
    986 
    987     setNormalizeFunction: function (f) {
    988       if (f === 'polynomial') {
    989         this.normalize = function (value) {
    990           return Math.pow(value, 0.2);
    991         };
    992       }
    993       else if (f === 'linear') {
    994         delete this.normalize;
    995       } else {
    996         this.normalize = f;
    997       }
    998       this.setMin(this.clearMinValue);
    999       this.setMax(this.clearMaxValue);
   1000     },
   1001 
   1002     getColor: function (value) {
   1003       if (typeof this.normalize === 'function') {
   1004         value = this.normalize(value);
   1005       }
   1006 
   1007       var lengthes = [];
   1008       var fullLength = 0;
   1009       var l;
   1010 
   1011       for (var i = 0; i < this.colors.length - 1; i++) {
   1012         l = this.vectorLength(this.vectorSubtract(this.colors[i + 1], this.colors[i]));
   1013         lengthes.push(l);
   1014         fullLength += l;
   1015       }
   1016 
   1017       var c = (this.maxValue - this.minValue) / fullLength;
   1018 
   1019       for (i = 0; i < lengthes.length; i++) {
   1020         lengthes[i] *= c;
   1021       }
   1022 
   1023       i = 0;
   1024       value -= this.minValue;
   1025 
   1026       while (value - lengthes[i] >= 0) {
   1027         value -= lengthes[i];
   1028         i++;
   1029       }
   1030 
   1031       var color;
   1032       if (i == this.colors.length - 1) {
   1033         color = this.vectorToNum(this.colors[i]).toString(16);
   1034       } else {
   1035         color = (this.vectorToNum(this.vectorAdd(this.colors[i], this.vectorMult(this.vectorSubtract(this.colors[i + 1], this.colors[i]), (value) / (lengthes[i]))))).toString(16);
   1036       }
   1037 
   1038       while (color.length < 6) {
   1039         color = '0' + color;
   1040       }
   1041       return '#' + color;
   1042     },
   1043 
   1044     vectorToNum: function (vector) {
   1045       var num = 0;
   1046       for (var i = 0; i < vector.length; i++) {
   1047         num += Math.round(vector[i]) * Math.pow(256, vector.length - i - 1);
   1048       }
   1049       return num;
   1050     },
   1051 
   1052     vectorSubtract: function (vector1, vector2) {
   1053       var vector = [];
   1054       for (var i = 0; i < vector1.length; i++) {
   1055         vector[i] = vector1[i] - vector2[i];
   1056       }
   1057       return vector;
   1058     },
   1059 
   1060     vectorAdd: function (vector1, vector2) {
   1061       var vector = [];
   1062       for (var i = 0; i < vector1.length; i++) {
   1063         vector[i] = vector1[i] + vector2[i];
   1064       }
   1065       return vector;
   1066     },
   1067 
   1068     vectorMult: function (vector, num) {
   1069       var result = [];
   1070       for (var i = 0; i < vector.length; i++) {
   1071         result[i] = vector[i] * num;
   1072       }
   1073       return result;
   1074     },
   1075 
   1076     vectorLength: function (vector) {
   1077       var result = 0;
   1078       for (var i = 0; i < vector.length; i++) {
   1079         result += vector[i] * vector[i];
   1080       }
   1081       return Math.sqrt(result);
   1082     }
   1083   };
   1084 
   1085   ColorScale.arrayToRgb = function (ar) {
   1086     var rgb = '#';
   1087     var d;
   1088     for (var i = 0; i < ar.length; i++) {
   1089       d = ar[i].toString(16);
   1090       rgb += d.length == 1 ? '0' + d : d;
   1091     }
   1092     return rgb;
   1093   };
   1094 
   1095   ColorScale.rgbToArray = function (rgb) {
   1096     rgb = rgb.substr(1);
   1097     return [parseInt(rgb.substr(0, 2), 16), parseInt(rgb.substr(2, 2), 16), parseInt(rgb.substr(4, 2), 16)];
   1098   };
   1099 
   1100 })(jQuery);