Subversion-Projekte lars-tiefland.webanos.zeldi.de

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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