jquery.vmap.js 33 KB

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