jquery-jvectormap.min.js 96 KB

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