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