Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 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'));
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
 
468
      if (map.selectedRegions.indexOf(code) !== -1) {
469
        map.deselect(code, path);
470
      } else {
471
        map.select(code, path);
472
      }
473
 
474
      //console.log(selectedRegions);
475
 
476
    });
477
 
478
    if (params.showTooltip) {
479
      params.container.mousemove(function (e) {
480
        if (map.label.is(':visible')) {
481
          map.label.css({
482
            left: e.pageX - 15 - map.labelWidth,
483
            top: e.pageY - 15 - map.labelHeight
484
          });
485
        }
486
      });
487
    }
488
 
489
    this.setColors(params.colors);
490
 
491
    this.canvas.canvas.appendChild(this.rootGroup);
492
 
493
    this.applyTransform();
494
 
495
    this.colorScale = new ColorScale(params.scaleColors, params.normalizeFunction, params.valueMin, params.valueMax);
496
 
497
    if (params.values) {
498
      this.values = params.values;
499
      this.setValues(params.values);
500
    }
501
 
502
    if (params.selectedRegions) {
503
      if (params.selectedRegions instanceof Array) {
504
        for(var k in params.selectedRegions) {
505
          this.select(params.selectedRegions[k].toLowerCase());
506
        }
507
      } else {
508
        this.select(params.selectedRegions.toLowerCase());
509
      }
510
    }
511
 
512
    this.bindZoomButtons();
513
 
514
    WorldMap.mapIndex++;
515
  };
516
 
517
  WorldMap.prototype = {
518
    transX: 0,
519
    transY: 0,
520
    scale: 1,
521
    baseTransX: 0,
522
    baseTransY: 0,
523
    baseScale: 1,
524
    width: 0,
525
    height: 0,
526
    countries: {},
527
    countriesColors: {},
528
    countriesData: {},
529
    zoomStep: 1.4,
530
    zoomMaxStep: 4,
531
    zoomCurStep: 1,
532
 
533
    setColors: function (key, color) {
534
      if (typeof key == 'string') {
535
        this.countries[key].setFill(color);
536
        this.countries[key].setAttribute("original", color);
537
      } else {
538
        var colors = key;
539
 
540
        for (var code in colors) {
541
          if (this.countries[code]) {
542
            this.countries[code].setFill(colors[code]);
543
            this.countries[code].setAttribute("original", colors[code]);
544
          }
545
        }
546
      }
547
    },
548
 
549
    setValues: function (values) {
550
      var max = 0,
551
      min = Number.MAX_VALUE,
552
      val;
553
 
554
      for (var cc in values) {
555
        val = parseFloat(values[cc]);
556
        if (val > max) {
557
          max = values[cc];
558
        }
559
        if (val && val < min) {
560
          min = val;
561
        }
562
      }
563
 
564
      this.colorScale.setMin(min);
565
      this.colorScale.setMax(max);
566
 
567
      var colors = {};
568
      for (cc in values) {
569
        val = parseFloat(values[cc]);
570
        if (val) {
571
          colors[cc] = this.colorScale.getColor(val);
572
        } else {
573
          colors[cc] = this.color;
574
        }
575
      }
576
      this.setColors(colors);
577
      this.values = values;
578
    },
579
 
580
    setBackgroundColor: function (backgroundColor) {
581
      this.container.css('background-color', backgroundColor);
582
    },
583
 
584
    setScaleColors: function (colors) {
585
      this.colorScale.setColors(colors);
586
 
587
      if (this.values) {
588
        this.setValues(this.values);
589
      }
590
    },
591
 
592
    setNormalizeFunction: function (f) {
593
      this.colorScale.setNormalizeFunction(f);
594
 
595
      if (this.values) {
596
        this.setValues(this.values);
597
      }
598
    },
599
 
600
    highlight: function (cc, path) {
601
      path = path || $('#' + this.getCountryId(cc))[0];
602
      if (this.hoverOpacity) {
603
        path.setOpacity(this.hoverOpacity);
604
      } else if (this.hoverColor) {
605
        path.currentFillColor = path.getFill() + '';
606
        path.setFill(this.hoverColor);
607
      }
608
    },
609
 
610
    unhighlight: function (cc, path) {
611
      path = path || $('#' + this.getCountryId(cc))[0];
612
      path.setOpacity(1);
613
      if (path.currentFillColor) {
614
        path.setFill(path.currentFillColor);
615
      }
616
    },
617
 
618
    select: function (cc, path) {
619
      path = path || $('#' + this.getCountryId(cc))[0];
620
      if(this.selectedRegions.indexOf(cc) < 0) {
621
        if (this.multiSelectRegion) {
622
          this.selectedRegions.push(cc);
623
        } else {
624
          this.selectedRegions = [cc];
625
        }
626
        // MUST BE after the change of selectedRegions
627
        // Otherwise, we might loop
628
        $(this.container).trigger('regionSelect.jqvmap', [cc]);
629
        if (this.selectedColor) {
630
          path.currentFillColor = this.selectedColor;
631
          path.setFill(this.selectedColor);
632
        }
633
      }
634
    },
635
 
636
    deselect: function (cc, path) {
637
      path = path || $('#' + this.getCountryId(cc))[0];
638
      if(this.selectedRegions.indexOf(cc) >= 0) {
639
        this.selectedRegions.splice(this.selectedRegions.indexOf(cc), 1);
640
        // MUST BE after the change of selectedRegions
641
        // Otherwise, we might loop
642
        $(this.container).trigger('regionDeselect.jqvmap', [cc]);
643
        path.currentFillColor = path.getOriginalFill();
644
        path.setFill(path.getOriginalFill());
645
      }
646
    },
647
 
648
    isSelected: function(cc) {
649
      return this.selectedRegions.indexOf(cc) >= 0;
650
    },
651
 
652
    resize: function () {
653
      var curBaseScale = this.baseScale;
654
      if (this.width / this.height > this.defaultWidth / this.defaultHeight) {
655
        this.baseScale = this.height / this.defaultHeight;
656
        this.baseTransX = Math.abs(this.width - this.defaultWidth * this.baseScale) / (2 * this.baseScale);
657
      } else {
658
        this.baseScale = this.width / this.defaultWidth;
659
        this.baseTransY = Math.abs(this.height - this.defaultHeight * this.baseScale) / (2 * this.baseScale);
660
      }
661
      this.scale *= this.baseScale / curBaseScale;
662
      this.transX *= this.baseScale / curBaseScale;
663
      this.transY *= this.baseScale / curBaseScale;
664
    },
665
 
666
    reset: function () {
667
      this.countryTitle.reset();
668
      for (var key in this.countries) {
669
        this.countries[key].setFill(WorldMap.defaultColor);
670
      }
671
      this.scale = this.baseScale;
672
      this.transX = this.baseTransX;
673
      this.transY = this.baseTransY;
674
      this.applyTransform();
675
    },
676
 
677
    applyTransform: function () {
678
      var maxTransX, maxTransY, minTransX, minTransY;
679
      if (this.defaultWidth * this.scale <= this.width) {
680
        maxTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
681
        minTransX = (this.width - this.defaultWidth * this.scale) / (2 * this.scale);
682
      } else {
683
        maxTransX = 0;
684
        minTransX = (this.width - this.defaultWidth * this.scale) / this.scale;
685
      }
686
 
687
      if (this.defaultHeight * this.scale <= this.height) {
688
        maxTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
689
        minTransY = (this.height - this.defaultHeight * this.scale) / (2 * this.scale);
690
      } else {
691
        maxTransY = 0;
692
        minTransY = (this.height - this.defaultHeight * this.scale) / this.scale;
693
      }
694
 
695
      if (this.transY > maxTransY) {
696
        this.transY = maxTransY;
697
      }
698
      else if (this.transY < minTransY) {
699
        this.transY = minTransY;
700
      }
701
      if (this.transX > maxTransX) {
702
        this.transX = maxTransX;
703
      }
704
      else if (this.transX < minTransX) {
705
        this.transX = minTransX;
706
      }
707
 
708
      this.canvas.applyTransformParams(this.scale, this.transX, this.transY);
709
    },
710
 
711
    makeDraggable: function () {
712
      var mouseDown = false;
713
      var oldPageX, oldPageY;
714
      var self = this;
715
 
716
      self.isMoving = false;
717
      self.isMovingTimeout = false;
718
 
719
      this.container.mousemove(function (e) {
720
 
721
        if (mouseDown) {
722
          var curTransX = self.transX;
723
          var curTransY = self.transY;
724
 
725
          self.transX -= (oldPageX - e.pageX) / self.scale;
726
          self.transY -= (oldPageY - e.pageY) / self.scale;
727
 
728
          self.applyTransform();
729
 
730
          oldPageX = e.pageX;
731
          oldPageY = e.pageY;
732
 
733
          self.isMoving = true;
734
          if (self.isMovingTimeout) {
735
            clearTimeout(self.isMovingTimeout);
736
          }
737
        }
738
 
739
        return false;
740
 
741
      }).mousedown(function (e) {
742
 
743
        mouseDown = true;
744
        oldPageX = e.pageX;
745
        oldPageY = e.pageY;
746
 
747
        return false;
748
 
749
      }).mouseup(function () {
750
 
751
        mouseDown = false;
752
 
753
        self.isMovingTimeout = setTimeout(function () {
754
          self.isMoving = false;
755
        }, 100);
756
 
757
        return false;
758
 
759
      });
760
    },
761
 
762
    bindZoomButtons: function () {
763
      var map = this;
764
      var sliderDelta = (jQuery('#zoom').innerHeight() - 6 * 2 - 15 * 2 - 3 * 2 - 7 - 6) / (this.zoomMaxStep - this.zoomCurStep);
765
 
766
      this.container.find('.jqvmap-zoomin').click(function () {
767
        if (map.zoomCurStep < map.zoomMaxStep) {
768
          var curTransX = map.transX;
769
          var curTransY = map.transY;
770
          var curScale = map.scale;
771
 
772
          map.transX -= (map.width / map.scale - map.width / (map.scale * map.zoomStep)) / 2;
773
          map.transY -= (map.height / map.scale - map.height / (map.scale * map.zoomStep)) / 2;
774
          map.setScale(map.scale * map.zoomStep);
775
          map.zoomCurStep++;
776
 
777
          jQuery('#zoomSlider').css('top', parseInt(jQuery('#zoomSlider').css('top'), 10) - sliderDelta);
778
        }
779
      });
780
 
781
      this.container.find('.jqvmap-zoomout').click(function () {
782
        if (map.zoomCurStep > 1) {
783
          var curTransX = map.transX;
784
          var curTransY = map.transY;
785
          var curScale = map.scale;
786
 
787
          map.transX += (map.width / (map.scale / map.zoomStep) - map.width / map.scale) / 2;
788
          map.transY += (map.height / (map.scale / map.zoomStep) - map.height / map.scale) / 2;
789
          map.setScale(map.scale / map.zoomStep);
790
          map.zoomCurStep--;
791
 
792
          jQuery('#zoomSlider').css('top', parseInt(jQuery('#zoomSlider').css('top'), 10) + sliderDelta);
793
        }
794
      });
795
    },
796
 
797
    setScale: function (scale) {
798
      this.scale = scale;
799
      this.applyTransform();
800
    },
801
 
802
    getCountryId: function (cc) {
803
      return 'jqvmap' + this.index + '_' + cc;
804
    }
805
  };
806
 
807
  WorldMap.xlink = "http://www.w3.org/1999/xlink";
808
  WorldMap.mapIndex = 1;
809
  WorldMap.maps = {};
810
 
811
  var ColorScale = function (colors, normalizeFunction, minValue, maxValue) {
812
    if (colors) {
813
      this.setColors(colors);
814
    }
815
    if (normalizeFunction) {
816
      this.setNormalizeFunction(normalizeFunction);
817
    }
818
    if (minValue) {
819
      this.setMin(minValue);
820
    }
821
    if (minValue) {
822
      this.setMax(maxValue);
823
    }
824
  };
825
 
826
  ColorScale.prototype = {
827
    colors: [],
828
 
829
    setMin: function (min) {
830
      this.clearMinValue = min;
831
 
832
      if (typeof this.normalize === 'function') {
833
        this.minValue = this.normalize(min);
834
      } else {
835
        this.minValue = min;
836
      }
837
    },
838
 
839
    setMax: function (max) {
840
      this.clearMaxValue = max;
841
      if (typeof this.normalize === 'function') {
842
        this.maxValue = this.normalize(max);
843
      } else {
844
        this.maxValue = max;
845
      }
846
    },
847
 
848
    setColors: function (colors) {
849
      for (var i = 0; i < colors.length; i++) {
850
        colors[i] = ColorScale.rgbToArray(colors[i]);
851
      }
852
      this.colors = colors;
853
    },
854
 
855
    setNormalizeFunction: function (f) {
856
      if (f === 'polynomial') {
857
        this.normalize = function (value) {
858
          return Math.pow(value, 0.2);
859
        };
860
      }
861
      else if (f === 'linear') {
862
        delete this.normalize;
863
      } else {
864
        this.normalize = f;
865
      }
866
      this.setMin(this.clearMinValue);
867
      this.setMax(this.clearMaxValue);
868
    },
869
 
870
    getColor: function (value) {
871
      if (typeof this.normalize === 'function') {
872
        value = this.normalize(value);
873
      }
874
 
875
      var lengthes = [];
876
      var fullLength = 0;
877
      var l;
878
 
879
      for (var i = 0; i < this.colors.length - 1; i++) {
880
        l = this.vectorLength(this.vectorSubtract(this.colors[i + 1], this.colors[i]));
881
        lengthes.push(l);
882
        fullLength += l;
883
      }
884
 
885
      var c = (this.maxValue - this.minValue) / fullLength;
886
 
887
      for (i = 0; i < lengthes.length; i++) {
888
        lengthes[i] *= c;
889
      }
890
 
891
      i = 0;
892
      value -= this.minValue;
893
 
894
      while (value - lengthes[i] >= 0) {
895
        value -= lengthes[i];
896
        i++;
897
      }
898
 
899
      var color;
900
      if (i == this.colors.length - 1) {
901
        color = this.vectorToNum(this.colors[i]).toString(16);
902
      } else {
903
        color = (this.vectorToNum(this.vectorAdd(this.colors[i], this.vectorMult(this.vectorSubtract(this.colors[i + 1], this.colors[i]), (value) / (lengthes[i]))))).toString(16);
904
      }
905
 
906
      while (color.length < 6) {
907
        color = '0' + color;
908
      }
909
      return '#' + color;
910
    },
911
 
912
    vectorToNum: function (vector) {
913
      var num = 0;
914
      for (var i = 0; i < vector.length; i++) {
915
        num += Math.round(vector[i]) * Math.pow(256, vector.length - i - 1);
916
      }
917
      return num;
918
    },
919
 
920
    vectorSubtract: function (vector1, vector2) {
921
      var vector = [];
922
      for (var i = 0; i < vector1.length; i++) {
923
        vector[i] = vector1[i] - vector2[i];
924
      }
925
      return vector;
926
    },
927
 
928
    vectorAdd: function (vector1, vector2) {
929
      var vector = [];
930
      for (var i = 0; i < vector1.length; i++) {
931
        vector[i] = vector1[i] + vector2[i];
932
      }
933
      return vector;
934
    },
935
 
936
    vectorMult: function (vector, num) {
937
      var result = [];
938
      for (var i = 0; i < vector.length; i++) {
939
        result[i] = vector[i] * num;
940
      }
941
      return result;
942
    },
943
 
944
    vectorLength: function (vector) {
945
      var result = 0;
946
      for (var i = 0; i < vector.length; i++) {
947
        result += vector[i] * vector[i];
948
      }
949
      return Math.sqrt(result);
950
    }
951
  };
952
 
953
  ColorScale.arrayToRgb = function (ar) {
954
    var rgb = '#';
955
    var d;
956
    for (var i = 0; i < ar.length; i++) {
957
      d = ar[i].toString(16);
958
      rgb += d.length == 1 ? '0' + d : d;
959
    }
960
    return rgb;
961
  };
962
 
963
  ColorScale.rgbToArray = function (rgb) {
964
    rgb = rgb.substr(1);
965
    return [parseInt(rgb.substr(0, 2), 16), parseInt(rgb.substr(2, 2), 16), parseInt(rgb.substr(4, 2), 16)];
966
  };
967
 
968
})(jQuery);