CR/LF correction

This commit is contained in:
pucherot
2021-01-05 14:51:52 +01:00
parent c6f11a428c
commit 766671ad6d
7156 changed files with 671400 additions and 1 deletions

View File

@@ -0,0 +1,93 @@
/**
* Implements abstract vector canvas.
* @constructor
* @param {HTMLElement} container Container to put element to.
* @param {Number} width Width of canvas.
* @param {Number} height Height of canvas.
*/
jvm.AbstractCanvasElement = function(container, width, height){
this.container = container;
this.setSize(width, height);
this.rootElement = new jvm[this.classPrefix+'GroupElement']();
this.node.appendChild( this.rootElement.node );
this.container.appendChild(this.node);
}
/**
* Add element to the certain group inside of the canvas.
* @param {HTMLElement} element Element to add to canvas.
* @param {HTMLElement} group Group to add element into or into root group if not provided.
*/
jvm.AbstractCanvasElement.prototype.add = function(element, group){
group = group || this.rootElement;
group.add(element);
element.canvas = this;
}
/**
* Create path and add it to the canvas.
* @param {Object} config Parameters of path to create.
* @param {Object} style Styles of the path to create.
* @param {HTMLElement} group Group to add path into.
*/
jvm.AbstractCanvasElement.prototype.addPath = function(config, style, group){
var el = new jvm[this.classPrefix+'PathElement'](config, style);
this.add(el, group);
return el;
};
/**
* Create circle and add it to the canvas.
* @param {Object} config Parameters of path to create.
* @param {Object} style Styles of the path to create.
* @param {HTMLElement} group Group to add circle into.
*/
jvm.AbstractCanvasElement.prototype.addCircle = function(config, style, group){
var el = new jvm[this.classPrefix+'CircleElement'](config, style);
this.add(el, group);
return el;
};
/**
* Create circle and add it to the canvas.
* @param {Object} config Parameters of path to create.
* @param {Object} style Styles of the path to create.
* @param {HTMLElement} group Group to add circle into.
*/
jvm.AbstractCanvasElement.prototype.addImage = function(config, style, group){
var el = new jvm[this.classPrefix+'ImageElement'](config, style);
this.add(el, group);
return el;
};
/**
* Create text and add it to the canvas.
* @param {Object} config Parameters of path to create.
* @param {Object} style Styles of the path to create.
* @param {HTMLElement} group Group to add circle into.
*/
jvm.AbstractCanvasElement.prototype.addText = function(config, style, group){
var el = new jvm[this.classPrefix+'TextElement'](config, style);
this.add(el, group);
return el;
};
/**
* Add group to the another group inside of the canvas.
* @param {HTMLElement} group Group to add circle into or root group if not provided.
*/
jvm.AbstractCanvasElement.prototype.addGroup = function(parentGroup){
var el = new jvm[this.classPrefix+'GroupElement']();
if (parentGroup) {
parentGroup.node.appendChild(el.node);
} else {
this.node.appendChild(el.node);
}
el.canvas = this;
return el;
};

View File

@@ -0,0 +1,73 @@
/**
* Basic wrapper for DOM element.
* @constructor
* @param {String} name Tag name of the element
* @param {Object} config Set of parameters to initialize element with
*/
jvm.AbstractElement = function(name, config){
/**
* Underlying DOM element
* @type {DOMElement}
* @private
*/
this.node = this.createElement(name);
/**
* Name of underlying element
* @type {String}
* @private
*/
this.name = name;
/**
* Internal store of attributes
* @type {Object}
* @private
*/
this.properties = {};
if (config) {
this.set(config);
}
};
/**
* Set attribute of the underlying DOM element.
* @param {String} name Name of attribute
* @param {Number|String} config Set of parameters to initialize element with
*/
jvm.AbstractElement.prototype.set = function(property, value){
var key;
if (typeof property === 'object') {
for (key in property) {
this.properties[key] = property[key];
this.applyAttr(key, property[key]);
}
} else {
this.properties[property] = value;
this.applyAttr(property, value);
}
};
/**
* Returns value of attribute.
* @param {String} name Name of attribute
*/
jvm.AbstractElement.prototype.get = function(property){
return this.properties[property];
};
/**
* Applies attribute value to the underlying DOM element.
* @param {String} name Name of attribute
* @param {Number|String} config Value of attribute to apply
* @private
*/
jvm.AbstractElement.prototype.applyAttr = function(property, value){
this.node.setAttribute(property, value);
};
jvm.AbstractElement.prototype.remove = function(){
jvm.$(this.node).remove();
};

View File

@@ -0,0 +1,62 @@
/**
* Abstract shape element. Shape element represents some visual vector or raster object.
* @constructor
* @param {String} name Tag name of the element.
* @param {Object} config Set of parameters to initialize element with.
* @param {Object} style Object with styles to set on element initialization.
*/
jvm.AbstractShapeElement = function(name, config, style){
this.style = style || {};
this.style.current = this.style.current || {};
this.isHovered = false;
this.isSelected = false;
this.updateStyle();
};
/**
* Set element's style.
* @param {Object|String} property Could be string to set only one property or object to set several style properties at once.
* @param {String} value Value to set in case only one property should be set.
*/
jvm.AbstractShapeElement.prototype.setStyle = function(property, value){
var styles = {};
if (typeof property === 'object') {
styles = property;
} else {
styles[property] = value;
}
jvm.$.extend(this.style.current, styles);
this.updateStyle();
};
jvm.AbstractShapeElement.prototype.updateStyle = function(){
var attrs = {};
jvm.AbstractShapeElement.mergeStyles(attrs, this.style.initial);
jvm.AbstractShapeElement.mergeStyles(attrs, this.style.current);
if (this.isHovered) {
jvm.AbstractShapeElement.mergeStyles(attrs, this.style.hover);
}
if (this.isSelected) {
jvm.AbstractShapeElement.mergeStyles(attrs, this.style.selected);
if (this.isHovered) {
jvm.AbstractShapeElement.mergeStyles(attrs, this.style.selectedHover);
}
}
this.set(attrs);
};
jvm.AbstractShapeElement.mergeStyles = function(styles, newStyles){
var key;
newStyles = newStyles || {};
for (key in newStyles) {
if (newStyles[key] === null) {
delete styles[key];
} else {
styles[key] = newStyles[key];
}
}
}

View File

@@ -0,0 +1,44 @@
jvm.ColorScale = function(colors, normalizeFunction, minValue, maxValue) {
jvm.ColorScale.parentClass.apply(this, arguments);
}
jvm.inherits(jvm.ColorScale, jvm.NumericScale);
jvm.ColorScale.prototype.setScale = function(scale) {
var i;
for (i = 0; i < scale.length; i++) {
this.scale[i] = jvm.ColorScale.rgbToArray(scale[i]);
}
};
jvm.ColorScale.prototype.getValue = function(value) {
return jvm.ColorScale.numToRgb(jvm.ColorScale.parentClass.prototype.getValue.call(this, value));
};
jvm.ColorScale.arrayToRgb = function(ar) {
var rgb = '#',
d,
i;
for (i = 0; i < ar.length; i++) {
d = ar[i].toString(16);
rgb += d.length == 1 ? '0'+d : d;
}
return rgb;
};
jvm.ColorScale.numToRgb = function(num) {
num = num.toString(16);
while (num.length < 6) {
num = '0' + num;
}
return '#'+num;
};
jvm.ColorScale.rgbToArray = function(rgb) {
rgb = rgb.substr(1);
return [parseInt(rgb.substr(0, 2), 16), parseInt(rgb.substr(2, 2), 16), parseInt(rgb.substr(4, 2), 16)];
};

View File

@@ -0,0 +1,158 @@
/**
* Creates data series.
* @constructor
* @param {Object} params Parameters to initialize series with.
* @param {Array} params.values The data set to visualize.
* @param {String} params.attribute Numberic or color attribute to use for data visualization. This could be: <code>fill</code>, <code>stroke</code>, <code>fill-opacity</code>, <code>stroke-opacity</code> for markers and regions and <code>r</code> (radius) for markers only.
* @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>
* @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.
* @param {Number} params.min Minimum value of the data set. Could be calculated automatically if not provided.
* @param {Number} params.min Maximum value of the data set. Could be calculated automatically if not provided.
*/
jvm.DataSeries = function(params, elements, map) {
var scaleConstructor;
params = params || {};
params.attribute = params.attribute || 'fill';
this.elements = elements;
this.params = params;
this.map = map;
if (params.attributes) {
this.setAttributes(params.attributes);
}
if (jvm.$.isArray(params.scale)) {
scaleConstructor = (params.attribute === 'fill' || params.attribute === 'stroke') ? jvm.ColorScale : jvm.NumericScale;
this.scale = new scaleConstructor(params.scale, params.normalizeFunction, params.min, params.max);
} else if (params.scale) {
this.scale = new jvm.OrdinalScale(params.scale);
} else {
this.scale = new jvm.SimpleScale(params.scale);
}
this.values = params.values || {};
this.setValues(this.values);
if (this.params.legend) {
this.legend = new jvm.Legend($.extend({
map: this.map,
series: this
}, this.params.legend))
}
};
jvm.DataSeries.prototype = {
setAttributes: function(key, attr){
var attrs = key,
code;
if (typeof key == 'string') {
if (this.elements[key]) {
this.elements[key].setStyle(this.params.attribute, attr);
}
} else {
for (code in attrs) {
if (this.elements[code]) {
this.elements[code].element.setStyle(this.params.attribute, attrs[code]);
}
}
}
},
/**
* Set values for the data set.
* @param {Object} values Object which maps codes of regions or markers to values.
*/
setValues: function(values) {
var max = -Number.MAX_VALUE,
min = Number.MAX_VALUE,
val,
cc,
attrs = {};
if (!(this.scale instanceof jvm.OrdinalScale) && !(this.scale instanceof jvm.SimpleScale)) {
// we have a color scale as an array
if (typeof this.params.min === 'undefined' || typeof this.params.max === 'undefined') {
// min and/or max are not defined, so calculate them
for (cc in values) {
val = parseFloat(values[cc]);
if (val > max) max = val;
if (val < min) min = val;
}
}
if (typeof this.params.min === 'undefined') {
this.scale.setMin(min);
this.params.min = min;
} else {
this.scale.setMin(this.params.min);
}
if (typeof this.params.max === 'undefined') {
this.scale.setMax(max);
this.params.max = max;
} else {
this.scale.setMax(this.params.max);
}
for (cc in values) {
if (cc != 'indexOf') {
val = parseFloat(values[cc]);
if (!isNaN(val)) {
attrs[cc] = this.scale.getValue(val);
} else {
attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
}
}
}
} else {
for (cc in values) {
if (values[cc]) {
attrs[cc] = this.scale.getValue(values[cc]);
} else {
attrs[cc] = this.elements[cc].element.style.initial[this.params.attribute];
}
}
}
this.setAttributes(attrs);
jvm.$.extend(this.values, values);
},
clear: function(){
var key,
attrs = {};
for (key in this.values) {
if (this.elements[key]) {
attrs[key] = this.elements[key].element.shape.style.initial[this.params.attribute];
}
}
this.setAttributes(attrs);
this.values = {};
},
/**
* Set scale of the data series.
* @param {Array} scale Values representing scale.
*/
setScale: function(scale) {
this.scale.setScale(scale);
if (this.values) {
this.setValues(this.values);
}
},
/**
* Set normalize function of the data series.
* @param {Function|String} normilizeFunction.
*/
setNormalizeFunction: function(f) {
this.scale.setNormalizeFunction(f);
if (this.values) {
this.setValues(this.values);
}
}
};

View File

@@ -0,0 +1,185 @@
/**
* @namespace jvm Holds core methods and classes used by jVectorMap.
*/
var jvm = {
/**
* Inherits child's prototype from the parent's one.
* @param {Function} child
* @param {Function} parent
*/
inherits: function(child, parent) {
function temp() {}
temp.prototype = parent.prototype;
child.prototype = new temp();
child.prototype.constructor = child;
child.parentClass = parent;
},
/**
* Mixes in methods from the source constructor to the target one.
* @param {Function} target
* @param {Function} source
*/
mixin: function(target, source){
var prop;
for (prop in source.prototype) {
if (source.prototype.hasOwnProperty(prop)) {
target.prototype[prop] = source.prototype[prop];
}
}
},
min: function(values){
var min = Number.MAX_VALUE,
i;
if (values instanceof Array) {
for (i = 0; i < values.length; i++) {
if (values[i] < min) {
min = values[i];
}
}
} else {
for (i in values) {
if (values[i] < min) {
min = values[i];
}
}
}
return min;
},
max: function(values){
var max = Number.MIN_VALUE,
i;
if (values instanceof Array) {
for (i = 0; i < values.length; i++) {
if (values[i] > max) {
max = values[i];
}
}
} else {
for (i in values) {
if (values[i] > max) {
max = values[i];
}
}
}
return max;
},
keys: function(object){
var keys = [],
key;
for (key in object) {
keys.push(key);
}
return keys;
},
values: function(object){
var values = [],
key,
i;
for (i = 0; i < arguments.length; i++) {
object = arguments[i];
for (key in object) {
values.push(object[key]);
}
}
return values;
},
whenImageLoaded: function(url){
var deferred = new jvm.$.Deferred(),
img = jvm.$('<img/>');
img.error(function(){
deferred.reject();
}).load(function(){
deferred.resolve(img);
});
img.attr('src', url);
return deferred;
},
isImageUrl: function(s){
return /\.\w{3,4}$/.test(s);
}
};
jvm.$ = jQuery;
/**
* indexOf polyfill for IE < 9
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
*/
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
var k;
// 1. Let O be the result of calling ToObject passing
// the this value as the argument.
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var O = Object(this);
// 2. Let lenValue be the result of calling the Get
// internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If len is 0, return -1.
if (len === 0) {
return -1;
}
// 5. If argument fromIndex was passed let n be
// ToInteger(fromIndex); else let n be 0.
var n = +fromIndex || 0;
if (Math.abs(n) === Infinity) {
n = 0;
}
// 6. If n >= len, return -1.
if (n >= len) {
return -1;
}
// 7. If n >= 0, then Let k be n.
// 8. Else, n<0, Let k be len - abs(n).
// If k is less than 0, then let k be 0.
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
// 9. Repeat, while k < len
while (k < len) {
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the
// HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
// i. Let elementK be the result of calling the Get
// internal method of O with the argument ToString(k).
// ii. Let same be the result of applying the
// Strict Equality Comparison Algorithm to
// searchElement and elementK.
// iii. If same is true, return k.
if (k in O && O[k] === searchElement) {
return k;
}
k++;
}
return -1;
};
}

View File

@@ -0,0 +1,83 @@
/**
* Represents map legend.
* @constructor
* @param {Object} params Configuration parameters.
* @param {String} params.cssClass Additional CSS class to apply to legend element.
* @param {Boolean} params.vertical If <code>true</code> legend will be rendered as vertical.
* @param {String} params.title Legend title.
* @param {Function} params.labelRender Method to convert series values to legend labels.
*/
jvm.Legend = function(params) {
this.params = params || {};
this.map = this.params.map;
this.series = this.params.series;
this.body = jvm.$('<div/>');
this.body.addClass('jvectormap-legend');
if (this.params.cssClass) {
this.body.addClass(this.params.cssClass);
}
if (params.vertical) {
this.map.legendCntVertical.append( this.body );
} else {
this.map.legendCntHorizontal.append( this.body );
}
this.render();
}
jvm.Legend.prototype.render = function(){
var ticks = this.series.scale.getTicks(),
i,
inner = jvm.$('<div/>').addClass('jvectormap-legend-inner'),
tick,
sample,
label;
this.body.html('');
if (this.params.title) {
this.body.append(
jvm.$('<div/>').addClass('jvectormap-legend-title').html(this.params.title)
);
}
this.body.append(inner);
for (i = 0; i < ticks.length; i++) {
tick = jvm.$('<div/>').addClass('jvectormap-legend-tick');
sample = jvm.$('<div/>').addClass('jvectormap-legend-tick-sample');
switch (this.series.params.attribute) {
case 'fill':
if (jvm.isImageUrl(ticks[i].value)) {
sample.css('background', 'url('+ticks[i].value+')');
} else {
sample.css('background', ticks[i].value);
}
break;
case 'stroke':
sample.css('background', ticks[i].value);
break;
case 'image':
sample.css('background', 'url('+ticks[i].value+') no-repeat center center');
break;
case 'r':
jvm.$('<div/>').css({
'border-radius': ticks[i].value,
border: this.map.params.markerStyle.initial['stroke-width']+'px '+
this.map.params.markerStyle.initial['stroke']+' solid',
width: ticks[i].value * 2 + 'px',
height: ticks[i].value * 2 + 'px',
background: this.map.params.markerStyle.initial['fill']
}).appendTo(sample);
break;
}
tick.append( sample );
label = ticks[i].label;
if (this.params.labelRender) {
label = this.params.labelRender(label);
}
tick.append( jvm.$('<div>'+label+' </div>').addClass('jvectormap-legend-tick-text') );
inner.append(tick);
}
inner.append( jvm.$('<div/>').css('clear', 'both') );
}

View File

@@ -0,0 +1,73 @@
jvm.MapObject = function(config){};
jvm.MapObject.prototype.getLabelText = function(key){
var text;
if (this.config.label) {
if (typeof this.config.label.render === 'function') {
text = this.config.label.render(key);
} else {
text = key;
}
} else {
text = null;
}
return text;
}
jvm.MapObject.prototype.getLabelOffsets = function(key){
var offsets;
if (this.config.label) {
if (typeof this.config.label.offsets === 'function') {
offsets = this.config.label.offsets(key);
} else if (typeof this.config.label.offsets === 'object') {
offsets = this.config.label.offsets[key];
}
}
return offsets || [0, 0];
}
/**
* Set hovered state to the element. Hovered state means mouse cursor is over element. Styles will be updates respectively.
* @param {Boolean} isHovered <code>true</code> to make element hovered, <code>false</code> otherwise.
*/
jvm.MapObject.prototype.setHovered = function(isHovered){
if (this.isHovered !== isHovered) {
this.isHovered = isHovered;
this.shape.isHovered = isHovered;
this.shape.updateStyle();
if (this.label) {
this.label.isHovered = isHovered;
this.label.updateStyle();
}
}
};
/**
* Set selected state to the element. Styles will be updates respectively.
* @param {Boolean} isSelected <code>true</code> to make element selected, <code>false</code> otherwise.
*/
jvm.MapObject.prototype.setSelected = function(isSelected){
if (this.isSelected !== isSelected) {
this.isSelected = isSelected;
this.shape.isSelected = isSelected;
this.shape.updateStyle();
if (this.label) {
this.label.isSelected = isSelected;
this.label.updateStyle();
}
jvm.$(this.shape).trigger('selected', [isSelected]);
}
};
jvm.MapObject.prototype.setStyle = function(){
this.shape.setStyle.apply(this.shape, arguments);
};
jvm.MapObject.prototype.remove = function(){
this.shape.remove();
if (this.label) {
this.label.remove();
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
jvm.Marker = function(config){
var text,
offsets;
this.config = config;
this.map = this.config.map;
this.isImage = !!this.config.style.initial.image;
this.createShape();
text = this.getLabelText(config.index);
if (this.config.label && text) {
this.offsets = this.getLabelOffsets(config.index);
this.labelX = config.cx / this.map.scale - this.map.transX;
this.labelY = config.cy / this.map.scale - this.map.transY;
this.label = config.canvas.addText({
text: text,
'data-index': config.index,
dy: "0.6ex",
x: this.labelX,
y: this.labelY
}, config.labelStyle, config.labelsGroup);
this.label.addClass('jvectormap-marker jvectormap-element');
}
};
jvm.inherits(jvm.Marker, jvm.MapObject);
jvm.Marker.prototype.createShape = function(){
var that = this;
if (this.shape) {
this.shape.remove();
}
this.shape = this.config.canvas[this.isImage ? 'addImage' : 'addCircle']({
"data-index": this.config.index,
cx: this.config.cx,
cy: this.config.cy
}, this.config.style, this.config.group);
this.shape.addClass('jvectormap-marker jvectormap-element');
if (this.isImage) {
jvm.$(this.shape.node).on('imageloaded', function(){
that.updateLabelPosition();
});
}
};
jvm.Marker.prototype.updateLabelPosition = function(){
if (this.label) {
this.label.set({
x: this.labelX * this.map.scale + this.offsets[0] +
this.map.transX * this.map.scale + 5 + (this.isImage ? (this.shape.width || 0) / 2 : this.shape.properties.r),
y: this.labelY * this.map.scale + this.map.transY * this.map.scale + this.offsets[1]
});
}
};
jvm.Marker.prototype.setStyle = function(property, value){
var isImage;
jvm.Marker.parentClass.prototype.setStyle.apply(this, arguments);
if (property === 'r') {
this.updateLabelPosition();
}
isImage = !!this.shape.get('image');
if (isImage != this.isImage) {
this.isImage = isImage;
this.config.style = jvm.$.extend(true, {}, this.shape.style);
this.createShape();
}
};

View File

@@ -0,0 +1,139 @@
/**
* Creates map with drill-down functionality.
* @constructor
* @param {Object} params Parameters to initialize map with.
* @param {Number} params.maxLevel Maximum number of levels user can go through
* @param {Object} params.main Config of the main map. See <a href="./jvm-map/">jvm.Map</a> for more information.
* @param {Function} params.mapNameByCode Function go generate map name by region code. Default value is:
<pre>
function(code, multiMap) {
return code.toLowerCase()+'_'+
multiMap.defaultProjection+'_en';
}
</pre>
* @param {Function} params.mapUrlByCode Function to generate map url by region code. Default value is:
<pre>
function(code, multiMap){
return 'jquery-jvectormap-data-'+
code.toLowerCase()+'-'+
multiMap.defaultProjection+'-en.js';
}
</pre>
*/
jvm.MultiMap = function(params) {
var that = this;
this.maps = {};
this.params = jvm.$.extend(true, {}, jvm.MultiMap.defaultParams, params);
this.params.maxLevel = this.params.maxLevel || Number.MAX_VALUE;
this.params.main = this.params.main || {};
this.params.main.multiMapLevel = 0;
this.history = [ this.addMap(this.params.main.map, this.params.main) ];
this.defaultProjection = this.history[0].mapData.projection.type;
this.mapsLoaded = {};
this.params.container.css({position: 'relative'});
this.backButton = jvm.$('<div/>').addClass('jvectormap-goback').text('Back').appendTo(this.params.container);
this.backButton.hide();
this.backButton.click(function(){
that.goBack();
});
this.spinner = jvm.$('<div/>').addClass('jvectormap-spinner').appendTo(this.params.container);
this.spinner.hide();
};
jvm.MultiMap.prototype = {
addMap: function(name, config){
var cnt = jvm.$('<div/>').css({
width: '100%',
height: '100%'
});
this.params.container.append(cnt);
this.maps[name] = new jvm.Map(jvm.$.extend(config, {container: cnt}));
if (this.params.maxLevel > config.multiMapLevel) {
this.maps[name].container.on('regionClick.jvectormap', {scope: this}, function(e, code){
var multimap = e.data.scope,
mapName = multimap.params.mapNameByCode(code, multimap);
if (!multimap.drillDownPromise || multimap.drillDownPromise.state() !== 'pending') {
multimap.drillDown(mapName, code);
}
});
}
return this.maps[name];
},
downloadMap: function(code){
var that = this,
deferred = jvm.$.Deferred();
if (!this.mapsLoaded[code]) {
jvm.$.get(this.params.mapUrlByCode(code, this)).then(function(){
that.mapsLoaded[code] = true;
deferred.resolve();
}, function(){
deferred.reject();
});
} else {
deferred.resolve();
}
return deferred;
},
drillDown: function(name, code){
var currentMap = this.history[this.history.length - 1],
that = this,
focusPromise = currentMap.setFocus({region: code, animate: true}),
downloadPromise = this.downloadMap(code);
focusPromise.then(function(){
if (downloadPromise.state() === 'pending') {
that.spinner.show();
}
});
downloadPromise.always(function(){
that.spinner.hide();
});
this.drillDownPromise = jvm.$.when(downloadPromise, focusPromise);
this.drillDownPromise.then(function(){
currentMap.params.container.hide();
if (!that.maps[name]) {
that.addMap(name, {map: name, multiMapLevel: currentMap.params.multiMapLevel + 1});
} else {
that.maps[name].params.container.show();
}
that.history.push( that.maps[name] );
that.backButton.show();
});
},
goBack: function(){
var currentMap = this.history.pop(),
prevMap = this.history[this.history.length - 1],
that = this;
currentMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true}).then(function(){
currentMap.params.container.hide();
prevMap.params.container.show();
prevMap.updateSize();
if (that.history.length === 1) {
that.backButton.hide();
}
prevMap.setFocus({scale: 1, x: 0.5, y: 0.5, animate: true});
});
}
};
jvm.MultiMap.defaultParams = {
mapNameByCode: function(code, multiMap){
return code.toLowerCase()+'_'+multiMap.defaultProjection+'_en';
},
mapUrlByCode: function(code, multiMap){
return 'jquery-jvectormap-data-'+code.toLowerCase()+'-'+multiMap.defaultProjection+'-en.js';
}
}

View File

@@ -0,0 +1,185 @@
jvm.NumericScale = function(scale, normalizeFunction, minValue, maxValue) {
this.scale = [];
normalizeFunction = normalizeFunction || 'linear';
if (scale) this.setScale(scale);
if (normalizeFunction) this.setNormalizeFunction(normalizeFunction);
if (typeof minValue !== 'undefined' ) this.setMin(minValue);
if (typeof maxValue !== 'undefined' ) this.setMax(maxValue);
};
jvm.NumericScale.prototype = {
setMin: function(min) {
this.clearMinValue = min;
if (typeof this.normalize === 'function') {
this.minValue = this.normalize(min);
} else {
this.minValue = min;
}
},
setMax: function(max) {
this.clearMaxValue = max;
if (typeof this.normalize === 'function') {
this.maxValue = this.normalize(max);
} else {
this.maxValue = max;
}
},
setScale: function(scale) {
var i;
this.scale = [];
for (i = 0; i < scale.length; i++) {
this.scale[i] = [scale[i]];
}
},
setNormalizeFunction: function(f) {
if (f === 'polynomial') {
this.normalize = function(value) {
return Math.pow(value, 0.2);
}
} else if (f === 'linear') {
delete this.normalize;
} else {
this.normalize = f;
}
this.setMin(this.clearMinValue);
this.setMax(this.clearMaxValue);
},
getValue: function(value) {
var lengthes = [],
fullLength = 0,
l,
i = 0,
c;
if (typeof this.normalize === 'function') {
value = this.normalize(value);
}
for (i = 0; i < this.scale.length-1; i++) {
l = this.vectorLength(this.vectorSubtract(this.scale[i+1], this.scale[i]));
lengthes.push(l);
fullLength += l;
}
c = (this.maxValue - this.minValue) / fullLength;
for (i=0; i<lengthes.length; i++) {
lengthes[i] *= c;
}
i = 0;
value -= this.minValue;
while (value - lengthes[i] >= 0) {
value -= lengthes[i];
i++;
}
if (i == this.scale.length - 1) {
value = this.vectorToNum(this.scale[i])
} else {
value = (
this.vectorToNum(
this.vectorAdd(this.scale[i],
this.vectorMult(
this.vectorSubtract(this.scale[i+1], this.scale[i]),
(value) / (lengthes[i])
)
)
)
);
}
return value;
},
vectorToNum: function(vector) {
var num = 0,
i;
for (i = 0; i < vector.length; i++) {
num += Math.round(vector[i])*Math.pow(256, vector.length-i-1);
}
return num;
},
vectorSubtract: function(vector1, vector2) {
var vector = [],
i;
for (i = 0; i < vector1.length; i++) {
vector[i] = vector1[i] - vector2[i];
}
return vector;
},
vectorAdd: function(vector1, vector2) {
var vector = [],
i;
for (i = 0; i < vector1.length; i++) {
vector[i] = vector1[i] + vector2[i];
}
return vector;
},
vectorMult: function(vector, num) {
var result = [],
i;
for (i = 0; i < vector.length; i++) {
result[i] = vector[i] * num;
}
return result;
},
vectorLength: function(vector) {
var result = 0,
i;
for (i = 0; i < vector.length; i++) {
result += vector[i] * vector[i];
}
return Math.sqrt(result);
},
/* Derived from d3 implementation https://github.com/mbostock/d3/blob/master/src/scale/linear.js#L94 */
getTicks: function(){
var m = 5,
extent = [this.clearMinValue, this.clearMaxValue],
span = extent[1] - extent[0],
step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)),
err = m / span * step,
ticks = [],
tick,
v;
if (err <= .15) step *= 10;
else if (err <= .35) step *= 5;
else if (err <= .75) step *= 2;
extent[0] = Math.floor(extent[0] / step) * step;
extent[1] = Math.ceil(extent[1] / step) * step;
tick = extent[0];
while (tick <= extent[1]) {
if (tick == extent[0]) {
v = this.clearMinValue;
} else if (tick == extent[1]) {
v = this.clearMaxValue;
} else {
v = tick;
}
ticks.push({
label: tick,
value: this.getValue(v)
});
tick += step;
}
return ticks;
}
};

View File

@@ -0,0 +1,21 @@
jvm.OrdinalScale = function(scale){
this.scale = scale;
};
jvm.OrdinalScale.prototype.getValue = function(value){
return this.scale[value];
};
jvm.OrdinalScale.prototype.getTicks = function(){
var ticks = [],
key;
for (key in this.scale) {
ticks.push({
label: key,
value: this.scale[key]
});
}
return ticks;
};

View File

@@ -0,0 +1,181 @@
/**
* Contains methods for transforming point on sphere to
* Cartesian coordinates using various projections.
* @class
*/
jvm.Proj = {
degRad: 180 / Math.PI,
radDeg: Math.PI / 180,
radius: 6381372,
sgn: function(n){
if (n > 0) {
return 1;
} else if (n < 0) {
return -1;
} else {
return n;
}
},
/**
* Converts point on sphere to the Cartesian coordinates using Miller projection
* @param {Number} lat Latitude in degrees
* @param {Number} lng Longitude in degrees
* @param {Number} c Central meridian in degrees
*/
mill: function(lat, lng, c){
return {
x: this.radius * (lng - c) * this.radDeg,
y: - this.radius * Math.log(Math.tan((45 + 0.4 * lat) * this.radDeg)) / 0.8
};
},
/**
* Inverse function of mill()
* Converts Cartesian coordinates to point on sphere using Miller projection
* @param {Number} x X of point in Cartesian system as integer
* @param {Number} y Y of point in Cartesian system as integer
* @param {Number} c Central meridian in degrees
*/
mill_inv: function(x, y, c){
return {
lat: (2.5 * Math.atan(Math.exp(0.8 * y / this.radius)) - 5 * Math.PI / 8) * this.degRad,
lng: (c * this.radDeg + x / this.radius) * this.degRad
};
},
/**
* Converts point on sphere to the Cartesian coordinates using Mercator projection
* @param {Number} lat Latitude in degrees
* @param {Number} lng Longitude in degrees
* @param {Number} c Central meridian in degrees
*/
merc: function(lat, lng, c){
return {
x: this.radius * (lng - c) * this.radDeg,
y: - this.radius * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360))
};
},
/**
* Inverse function of merc()
* Converts Cartesian coordinates to point on sphere using Mercator projection
* @param {Number} x X of point in Cartesian system as integer
* @param {Number} y Y of point in Cartesian system as integer
* @param {Number} c Central meridian in degrees
*/
merc_inv: function(x, y, c){
return {
lat: (2 * Math.atan(Math.exp(y / this.radius)) - Math.PI / 2) * this.degRad,
lng: (c * this.radDeg + x / this.radius) * this.degRad
};
},
/**
* Converts point on sphere to the Cartesian coordinates using Albers Equal-Area Conic
* projection
* @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
* @param {Number} lat Latitude in degrees
* @param {Number} lng Longitude in degrees
* @param {Number} c Central meridian in degrees
*/
aea: function(lat, lng, c){
var fi0 = 0,
lambda0 = c * this.radDeg,
fi1 = 29.5 * this.radDeg,
fi2 = 45.5 * this.radDeg,
fi = lat * this.radDeg,
lambda = lng * this.radDeg,
n = (Math.sin(fi1)+Math.sin(fi2)) / 2,
C = Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
theta = n*(lambda-lambda0),
ro = Math.sqrt(C-2*n*Math.sin(fi))/n,
ro0 = Math.sqrt(C-2*n*Math.sin(fi0))/n;
return {
x: ro * Math.sin(theta) * this.radius,
y: - (ro0 - ro * Math.cos(theta)) * this.radius
};
},
/**
* Converts Cartesian coordinates to the point on sphere using Albers Equal-Area Conic
* projection
* @see <a href="http://mathworld.wolfram.com/AlbersEqual-AreaConicProjection.html">Albers Equal-Area Conic projection</a>
* @param {Number} x X of point in Cartesian system as integer
* @param {Number} y Y of point in Cartesian system as integer
* @param {Number} c Central meridian in degrees
*/
aea_inv: function(xCoord, yCoord, c){
var x = xCoord / this.radius,
y = yCoord / this.radius,
fi0 = 0,
lambda0 = c * this.radDeg,
fi1 = 29.5 * this.radDeg,
fi2 = 45.5 * this.radDeg,
n = (Math.sin(fi1)+Math.sin(fi2)) / 2,
C = Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),
ro0 = Math.sqrt(C-2*n*Math.sin(fi0))/n,
ro = Math.sqrt(x*x+(ro0-y)*(ro0-y)),
theta = Math.atan( x / (ro0 - y) );
return {
lat: (Math.asin((C - ro * ro * n * n) / (2 * n))) * this.degRad,
lng: (lambda0 + theta / n) * this.degRad
};
},
/**
* Converts point on sphere to the Cartesian coordinates using Lambert conformal
* conic projection
* @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
* @param {Number} lat Latitude in degrees
* @param {Number} lng Longitude in degrees
* @param {Number} c Central meridian in degrees
*/
lcc: function(lat, lng, c){
var fi0 = 0,
lambda0 = c * this.radDeg,
lambda = lng * this.radDeg,
fi1 = 33 * this.radDeg,
fi2 = 45 * this.radDeg,
fi = lat * this.radDeg,
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) ) ),
F = ( Math.cos(fi1) * Math.pow( Math.tan( Math.PI / 4 + fi1 / 2 ), n ) ) / n,
ro = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi / 2 ), n ),
ro0 = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi0 / 2 ), n );
return {
x: ro * Math.sin( n * (lambda - lambda0) ) * this.radius,
y: - (ro0 - ro * Math.cos( n * (lambda - lambda0) ) ) * this.radius
};
},
/**
* Converts Cartesian coordinates to the point on sphere using Lambert conformal conic
* projection
* @see <a href="http://mathworld.wolfram.com/LambertConformalConicProjection.html">Lambert Conformal Conic Projection</a>
* @param {Number} x X of point in Cartesian system as integer
* @param {Number} y Y of point in Cartesian system as integer
* @param {Number} c Central meridian in degrees
*/
lcc_inv: function(xCoord, yCoord, c){
var x = xCoord / this.radius,
y = yCoord / this.radius,
fi0 = 0,
lambda0 = c * this.radDeg,
fi1 = 33 * this.radDeg,
fi2 = 45 * this.radDeg,
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) ) ),
F = ( Math.cos(fi1) * Math.pow( Math.tan( Math.PI / 4 + fi1 / 2 ), n ) ) / n,
ro0 = F * Math.pow( 1 / Math.tan( Math.PI / 4 + fi0 / 2 ), n ),
ro = this.sgn(n) * Math.sqrt(x*x+(ro0-y)*(ro0-y)),
theta = Math.atan( x / (ro0 - y) );
return {
lat: (2 * Math.atan(Math.pow(F/ro, 1/n)) - Math.PI / 2) * this.degRad,
lng: (lambda0 + theta / n) * this.degRad
};
}
};

View File

@@ -0,0 +1,45 @@
jvm.Region = function(config){
var bbox,
text,
offsets,
labelDx,
labelDy;
this.config = config;
this.map = this.config.map;
this.shape = config.canvas.addPath({
d: config.path,
'data-code': config.code
}, config.style, config.canvas.rootElement);
this.shape.addClass('jvectormap-region jvectormap-element');
bbox = this.shape.getBBox();
text = this.getLabelText(config.code);
if (this.config.label && text) {
offsets = this.getLabelOffsets(config.code);
this.labelX = bbox.x + bbox.width / 2 + offsets[0];
this.labelY = bbox.y + bbox.height / 2 + offsets[1];
this.label = config.canvas.addText({
text: text,
'text-anchor': 'middle',
'alignment-baseline': 'central',
x: this.labelX,
y: this.labelY,
'data-code': config.code
}, config.labelStyle, config.labelsGroup);
this.label.addClass('jvectormap-region jvectormap-element');
}
};
jvm.inherits(jvm.Region, jvm.MapObject);
jvm.Region.prototype.updateLabelPosition = function(){
if (this.label) {
this.label.set({
x: this.labelX * this.map.scale + this.map.transX * this.map.scale,
y: this.labelY * this.map.scale + this.map.transY * this.map.scale
});
}
};

View File

@@ -0,0 +1,7 @@
jvm.SimpleScale = function(scale){
this.scale = scale;
};
jvm.SimpleScale.prototype.getValue = function(value){
return value;
};

View File

@@ -0,0 +1,26 @@
jvm.SVGCanvasElement = function(container, width, height){
this.classPrefix = 'SVG';
jvm.SVGCanvasElement.parentClass.call(this, 'svg');
this.defsElement = new jvm.SVGElement('defs');
this.node.appendChild( this.defsElement.node );
jvm.AbstractCanvasElement.apply(this, arguments);
}
jvm.inherits(jvm.SVGCanvasElement, jvm.SVGElement);
jvm.mixin(jvm.SVGCanvasElement, jvm.AbstractCanvasElement);
jvm.SVGCanvasElement.prototype.setSize = function(width, height){
this.width = width;
this.height = height;
this.node.setAttribute('width', width);
this.node.setAttribute('height', height);
};
jvm.SVGCanvasElement.prototype.applyTransformParams = function(scale, transX, transY) {
this.scale = scale;
this.transX = transX;
this.transY = transY;
this.rootElement.node.setAttribute('transform', 'scale('+scale+') translate('+transX+', '+transY+')');
};

View File

@@ -0,0 +1,5 @@
jvm.SVGCircleElement = function(config, style){
jvm.SVGCircleElement.parentClass.call(this, 'circle', config, style);
};
jvm.inherits(jvm.SVGCircleElement, jvm.SVGShapeElement);

View File

@@ -0,0 +1,48 @@
/**
* Wrapper for SVG element.
* @constructor
* @extends jvm.AbstractElement
* @param {String} name Tag name of the element
* @param {Object} config Set of parameters to initialize element with
*/
jvm.SVGElement = function(name, config){
jvm.SVGElement.parentClass.apply(this, arguments);
}
jvm.inherits(jvm.SVGElement, jvm.AbstractElement);
jvm.SVGElement.svgns = "http://www.w3.org/2000/svg";
/**
* Creates DOM element.
* @param {String} tagName Name of element
* @private
* @returns DOMElement
*/
jvm.SVGElement.prototype.createElement = function( tagName ){
return document.createElementNS( jvm.SVGElement.svgns, tagName );
};
/**
* Adds CSS class for underlying DOM element.
* @param {String} className Name of CSS class name
*/
jvm.SVGElement.prototype.addClass = function( className ){
this.node.setAttribute('class', className);
};
/**
* Returns constructor for element by name prefixed with 'VML'.
* @param {String} ctr Name of basic constructor to return
* proper implementation for.
* @returns Function
* @private
*/
jvm.SVGElement.prototype.getElementCtr = function( ctr ){
return jvm['SVG'+ctr];
};
jvm.SVGElement.prototype.getBBox = function(){
return this.node.getBBox();
};

View File

@@ -0,0 +1,9 @@
jvm.SVGGroupElement = function(){
jvm.SVGGroupElement.parentClass.call(this, 'g');
}
jvm.inherits(jvm.SVGGroupElement, jvm.SVGElement);
jvm.SVGGroupElement.prototype.add = function(element){
this.node.appendChild( element.node );
};

View File

@@ -0,0 +1,36 @@
jvm.SVGImageElement = function(config, style){
jvm.SVGImageElement.parentClass.call(this, 'image', config, style);
};
jvm.inherits(jvm.SVGImageElement, jvm.SVGShapeElement);
jvm.SVGImageElement.prototype.applyAttr = function(attr, value){
var that = this;
if (attr == 'image') {
jvm.whenImageLoaded(value).then(function(img){
that.node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
that.width = img[0].width;
that.height = img[0].height;
that.applyAttr('width', that.width);
that.applyAttr('height', that.height);
that.applyAttr('x', that.cx - that.width / 2);
that.applyAttr('y', that.cy - that.height / 2);
jvm.$(that.node).trigger('imageloaded', [img]);
});
} else if(attr == 'cx') {
this.cx = value;
if (this.width) {
this.applyAttr('x', value - this.width / 2);
}
} else if(attr == 'cy') {
this.cy = value;
if (this.height) {
this.applyAttr('y', value - this.height / 2);
}
} else {
jvm.SVGImageElement.parentClass.prototype.applyAttr.apply(this, arguments);
}
};

View File

@@ -0,0 +1,6 @@
jvm.SVGPathElement = function(config, style){
jvm.SVGPathElement.parentClass.call(this, 'path', config, style);
this.node.setAttribute('fill-rule', 'evenodd');
}
jvm.inherits(jvm.SVGPathElement, jvm.SVGShapeElement);

View File

@@ -0,0 +1,49 @@
jvm.SVGShapeElement = function(name, config, style){
jvm.SVGShapeElement.parentClass.call(this, name, config);
jvm.AbstractShapeElement.apply(this, arguments);
};
jvm.inherits(jvm.SVGShapeElement, jvm.SVGElement);
jvm.mixin(jvm.SVGShapeElement, jvm.AbstractShapeElement);
jvm.SVGShapeElement.prototype.applyAttr = function(attr, value){
var patternEl,
imageEl,
that = this;
if (attr === 'fill' && jvm.isImageUrl(value)) {
if (!jvm.SVGShapeElement.images[value]) {
jvm.whenImageLoaded(value).then(function(img){
imageEl = new jvm.SVGElement('image');
imageEl.node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', value);
imageEl.applyAttr('x', '0');
imageEl.applyAttr('y', '0');
imageEl.applyAttr('width', img[0].width);
imageEl.applyAttr('height', img[0].height);
patternEl = new jvm.SVGElement('pattern');
patternEl.applyAttr('id', 'image'+jvm.SVGShapeElement.imageCounter);
patternEl.applyAttr('x', 0);
patternEl.applyAttr('y', 0);
patternEl.applyAttr('width', img[0].width / 2);
patternEl.applyAttr('height', img[0].height / 2);
patternEl.applyAttr('viewBox', '0 0 '+img[0].width+' '+img[0].height);
patternEl.applyAttr('patternUnits', 'userSpaceOnUse');
patternEl.node.appendChild( imageEl.node );
that.canvas.defsElement.node.appendChild( patternEl.node );
jvm.SVGShapeElement.images[value] = jvm.SVGShapeElement.imageCounter++;
that.applyAttr('fill', 'url(#image'+jvm.SVGShapeElement.images[value]+')');
});
} else {
this.applyAttr('fill', 'url(#image'+jvm.SVGShapeElement.images[value]+')');
}
} else {
jvm.SVGShapeElement.parentClass.prototype.applyAttr.apply(this, arguments);
}
};
jvm.SVGShapeElement.imageCounter = 1;
jvm.SVGShapeElement.images = {};

View File

@@ -0,0 +1,13 @@
jvm.SVGTextElement = function(config, style){
jvm.SVGTextElement.parentClass.call(this, 'text', config, style);
}
jvm.inherits(jvm.SVGTextElement, jvm.SVGShapeElement);
jvm.SVGTextElement.prototype.applyAttr = function(attr, value){
if (attr === 'text') {
this.node.textContent = value;
} else {
jvm.SVGTextElement.parentClass.prototype.applyAttr.apply(this, arguments);
}
};

View File

@@ -0,0 +1,18 @@
/**
* Class for vector images manipulations.
* @constructor
* @param {DOMElement} container to place canvas to
* @param {Number} width
* @param {Number} height
*/
jvm.VectorCanvas = function(container, width, height) {
this.mode = window.SVGAngle ? 'svg' : 'vml';
if (this.mode == 'svg') {
this.impl = new jvm.SVGCanvasElement(container, width, height);
} else {
this.impl = new jvm.VMLCanvasElement(container, width, height);
}
this.impl.mode = this.mode;
return this.impl;
};

View File

@@ -0,0 +1,45 @@
jvm.VMLCanvasElement = function(container, width, height){
this.classPrefix = 'VML';
jvm.VMLCanvasElement.parentClass.call(this, 'group');
jvm.AbstractCanvasElement.apply(this, arguments);
this.node.style.position = 'absolute';
};
jvm.inherits(jvm.VMLCanvasElement, jvm.VMLElement);
jvm.mixin(jvm.VMLCanvasElement, jvm.AbstractCanvasElement);
jvm.VMLCanvasElement.prototype.setSize = function(width, height){
var paths,
groups,
i,
l;
this.width = width;
this.height = height;
this.node.style.width = width + "px";
this.node.style.height = height + "px";
this.node.coordsize = width+' '+height;
this.node.coordorigin = "0 0";
if (this.rootElement) {
paths = this.rootElement.node.getElementsByTagName('shape');
for(i = 0, l = paths.length; i < l; i++) {
paths[i].coordsize = width+' '+height;
paths[i].style.width = width+'px';
paths[i].style.height = height+'px';
}
groups = this.node.getElementsByTagName('group');
for(i = 0, l = groups.length; i < l; i++) {
groups[i].coordsize = width+' '+height;
groups[i].style.width = width+'px';
groups[i].style.height = height+'px';
}
}
};
jvm.VMLCanvasElement.prototype.applyTransformParams = function(scale, transX, transY) {
this.scale = scale;
this.transX = transX;
this.transY = transY;
this.rootElement.node.coordorigin = (this.width-transX-this.width/100)+','+(this.height-transY-this.height/100);
this.rootElement.node.coordsize = this.width/scale+','+this.height/scale;
};

View File

@@ -0,0 +1,26 @@
jvm.VMLCircleElement = function(config, style){
jvm.VMLCircleElement.parentClass.call(this, 'oval', config, style);
};
jvm.inherits(jvm.VMLCircleElement, jvm.VMLShapeElement);
jvm.VMLCircleElement.prototype.applyAttr = function(attr, value){
switch (attr) {
case 'r':
this.node.style.width = value*2+'px';
this.node.style.height = value*2+'px';
this.applyAttr('cx', this.get('cx') || 0);
this.applyAttr('cy', this.get('cy') || 0);
break;
case 'cx':
if (!value) return;
this.node.style.left = value - (this.get('r') || 0) + 'px';
break;
case 'cy':
if (!value) return;
this.node.style.top = value - (this.get('r') || 0) + 'px';
break;
default:
jvm.VMLCircleElement.parentClass.prototype.applyAttr.call(this, attr, value);
}
};

View File

@@ -0,0 +1,107 @@
/**
* Wrapper for VML element.
* @constructor
* @extends jvm.AbstractElement
* @param {String} name Tag name of the element
* @param {Object} config Set of parameters to initialize element with
*/
jvm.VMLElement = function(name, config){
if (!jvm.VMLElement.VMLInitialized) {
jvm.VMLElement.initializeVML();
}
jvm.VMLElement.parentClass.apply(this, arguments);
};
jvm.inherits(jvm.VMLElement, jvm.AbstractElement);
/**
* Shows if VML was already initialized for the current document or not.
* @static
* @private
* @type {Boolean}
*/
jvm.VMLElement.VMLInitialized = false;
/**
* Initializes VML handling before creating the first element
* (adds CSS class and creates namespace). Adds one of two forms
* of createElement method depending of support by browser.
* @static
* @private
*/
// The following method of VML handling is borrowed from the
// Raphael library by Dmitry Baranovsky.
jvm.VMLElement.initializeVML = function(){
try {
if (!document.namespaces.rvml) {
document.namespaces.add("rvml","urn:schemas-microsoft-com:vml");
}
/**
* Creates DOM element.
* @param {String} tagName Name of element
* @private
* @returns DOMElement
*/
jvm.VMLElement.prototype.createElement = function (tagName) {
return document.createElement('<rvml:' + tagName + ' class="rvml">');
};
} catch (e) {
/**
* @private
*/
jvm.VMLElement.prototype.createElement = function (tagName) {
return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
};
}
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
jvm.VMLElement.VMLInitialized = true;
};
/**
* Returns constructor for element by name prefixed with 'VML'.
* @param {String} ctr Name of basic constructor to return
* proper implementation for.
* @returns Function
* @private
*/
jvm.VMLElement.prototype.getElementCtr = function( ctr ){
return jvm['VML'+ctr];
};
/**
* Adds CSS class for underlying DOM element.
* @param {String} className Name of CSS class name
*/
jvm.VMLElement.prototype.addClass = function( className ){
jvm.$(this.node).addClass(className);
};
/**
* Applies attribute value to the underlying DOM element.
* @param {String} name Name of attribute
* @param {Number|String} config Value of attribute to apply
* @private
*/
jvm.VMLElement.prototype.applyAttr = function( attr, value ){
this.node[attr] = value;
};
/**
* Returns boundary box for the element.
* @returns {Object} Boundary box with numeric fields: x, y, width, height
* @override
*/
jvm.VMLElement.prototype.getBBox = function(){
var node = jvm.$(this.node);
return {
x: node.position().left / this.canvas.scale,
y: node.position().top / this.canvas.scale,
width: node.width() / this.canvas.scale,
height: node.height() / this.canvas.scale
};
};

View File

@@ -0,0 +1,13 @@
jvm.VMLGroupElement = function(){
jvm.VMLGroupElement.parentClass.call(this, 'group');
this.node.style.left = '0px';
this.node.style.top = '0px';
this.node.coordorigin = "0 0";
};
jvm.inherits(jvm.VMLGroupElement, jvm.VMLElement);
jvm.VMLGroupElement.prototype.add = function(element){
this.node.appendChild( element.node );
};

View File

@@ -0,0 +1,47 @@
jvm.VMLImageElement = function(config, style){
jvm.VMLImageElement.parentClass.call(this, 'image', config, style);
};
jvm.inherits(jvm.VMLImageElement, jvm.VMLShapeElement);
jvm.VMLImageElement.prototype.applyAttr = function(attr, value){
var patternEl,
imageEl,
that = this;
if (attr == 'image') {
jvm.whenImageLoaded(value).then(function(img){
that.node.setAttribute('src', value);
that.width = img[0].width;
that.height = img[0].height;
that.applyAttr('width', that.width);
that.applyAttr('height', that.height);
jvm.VMLImageElement.images[value] = jvm.VMLImageElement.imageCounter++;
that.applyAttr('x', that.cx - that.width / 2);
that.applyAttr('y', that.cy - that.height / 2);
jvm.$(that.node).trigger('imageloaded', [img]);
});
} else if(attr == 'cx') {
this.cx = value;
if (this.width) {
this.applyAttr('x', value - this.width / 2);
}
} else if(attr == 'cy') {
this.cy = value;
if (this.height) {
this.applyAttr('y', value - this.height / 2);
}
} else if(attr == 'width' || attr == 'height') {
this.node.style[attr] = value + 'px';
} else if (attr == 'x' || attr == 'y') {
this.node.style[attr == 'x' ? 'left' : 'top'] = value + 'px';
} else {
jvm.VMLImageElement.parentClass.prototype.applyAttr.apply(this, arguments);
}
};
jvm.VMLImageElement.imageCounter = 1;
jvm.VMLImageElement.images = {}

View File

@@ -0,0 +1,98 @@
jvm.VMLPathElement = function(config, style){
var scale = new jvm.VMLElement('skew');
jvm.VMLPathElement.parentClass.call(this, 'shape', config, style);
this.node.coordorigin = "0 0";
scale.node.on = true;
scale.node.matrix = '0.01,0,0,0.01,0,0';
scale.node.offset = '0,0';
this.node.appendChild(scale.node);
};
jvm.inherits(jvm.VMLPathElement, jvm.VMLShapeElement);
jvm.VMLPathElement.prototype.applyAttr = function(attr, value){
if (attr === 'd') {
this.node.path = jvm.VMLPathElement.pathSvgToVml(value);
} else {
jvm.VMLShapeElement.prototype.applyAttr.call(this, attr, value);
}
};
jvm.VMLPathElement.pathSvgToVml = function(path) {
var cx = 0, cy = 0, ctrlx, ctrly;
path = path.replace(/(-?\d+)e(-?\d+)/g, '0');
return path.replace(/([MmLlHhVvCcSs])\s*((?:-?\d*(?:\.\d+)?\s*,?\s*)+)/g, function(segment, letter, coords, index){
coords = coords.replace(/(\d)-/g, '$1,-')
.replace(/^\s+/g, '')
.replace(/\s+$/g, '')
.replace(/\s+/g, ',').split(',');
if (!coords[0]) coords.shift();
for (var i=0, l=coords.length; i<l; i++) {
coords[i] = Math.round(100*coords[i]);
}
switch (letter) {
case 'm':
cx += coords[0];
cy += coords[1];
return 't'+coords.join(',');
case 'M':
cx = coords[0];
cy = coords[1];
return 'm'+coords.join(',');
case 'l':
cx += coords[0];
cy += coords[1];
return 'r'+coords.join(',');
case 'L':
cx = coords[0];
cy = coords[1];
return 'l'+coords.join(',');
case 'h':
cx += coords[0];
return 'r'+coords[0]+',0';
case 'H':
cx = coords[0];
return 'l'+cx+','+cy;
case 'v':
cy += coords[0];
return 'r0,'+coords[0];
case 'V':
cy = coords[0];
return 'l'+cx+','+cy;
case 'c':
ctrlx = cx + coords[coords.length-4];
ctrly = cy + coords[coords.length-3];
cx += coords[coords.length-2];
cy += coords[coords.length-1];
return 'v'+coords.join(',');
case 'C':
ctrlx = coords[coords.length-4];
ctrly = coords[coords.length-3];
cx = coords[coords.length-2];
cy = coords[coords.length-1];
return 'c'+coords.join(',');
case 's':
coords.unshift(cy-ctrly);
coords.unshift(cx-ctrlx);
ctrlx = cx + coords[coords.length-4];
ctrly = cy + coords[coords.length-3];
cx += coords[coords.length-2];
cy += coords[coords.length-1];
return 'v'+coords.join(',');
case 'S':
coords.unshift(cy+cy-ctrly);
coords.unshift(cx+cx-ctrlx);
ctrlx = coords[coords.length-4];
ctrly = coords[coords.length-3];
cx = coords[coords.length-2];
cy = coords[coords.length-1];
return 'c'+coords.join(',');
}
return '';
}).replace(/z/g, 'e');
};

View File

@@ -0,0 +1,49 @@
jvm.VMLShapeElement = function(name, config){
jvm.VMLShapeElement.parentClass.call(this, name, config);
this.fillElement = new jvm.VMLElement('fill');
this.strokeElement = new jvm.VMLElement('stroke');
this.node.appendChild(this.fillElement.node);
this.node.appendChild(this.strokeElement.node);
this.node.stroked = false;
jvm.AbstractShapeElement.apply(this, arguments);
};
jvm.inherits(jvm.VMLShapeElement, jvm.VMLElement);
jvm.mixin(jvm.VMLShapeElement, jvm.AbstractShapeElement);
jvm.VMLShapeElement.prototype.applyAttr = function(attr, value){
switch (attr) {
case 'fill':
this.node.fillcolor = value;
break;
case 'fill-opacity':
this.fillElement.node.opacity = Math.round(value*100)+'%';
break;
case 'stroke':
if (value === 'none') {
this.node.stroked = false;
} else {
this.node.stroked = true;
}
this.node.strokecolor = value;
break;
case 'stroke-opacity':
this.strokeElement.node.opacity = Math.round(value*100)+'%';
break;
case 'stroke-width':
if (parseInt(value, 10) === 0) {
this.node.stroked = false;
} else {
this.node.stroked = true;
}
this.node.strokeweight = value;
break;
case 'd':
this.node.path = jvm.VMLPathElement.pathSvgToVml(value);
break;
default:
jvm.VMLShapeElement.parentClass.prototype.applyAttr.apply(this, arguments);
}
};