3723 lines
163 KiB
JavaScript
3723 lines
163 KiB
JavaScript
/**
|
|
* Kendo UI v2016.1.226 (http://www.telerik.com/kendo-ui)
|
|
* Copyright 2016 Telerik AD. All rights reserved.
|
|
*
|
|
* Kendo UI commercial licenses may be obtained at
|
|
* http://www.telerik.com/purchase/license-agreement/kendo-ui-complete
|
|
* If you do not own a commercial license, this file shall be governed by the trial license terms.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
(function (f, define) {
|
|
define('util/main', ['kendo.core'], f);
|
|
}(function () {
|
|
(function () {
|
|
var math = Math, kendo = window.kendo, deepExtend = kendo.deepExtend;
|
|
var DEG_TO_RAD = math.PI / 180, MAX_NUM = Number.MAX_VALUE, MIN_NUM = -Number.MAX_VALUE, UNDEFINED = 'undefined';
|
|
function defined(value) {
|
|
return typeof value !== UNDEFINED;
|
|
}
|
|
function round(value, precision) {
|
|
var power = pow(precision);
|
|
return math.round(value * power) / power;
|
|
}
|
|
function pow(p) {
|
|
if (p) {
|
|
return math.pow(10, p);
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
function limitValue(value, min, max) {
|
|
return math.max(math.min(value, max), min);
|
|
}
|
|
function rad(degrees) {
|
|
return degrees * DEG_TO_RAD;
|
|
}
|
|
function deg(radians) {
|
|
return radians / DEG_TO_RAD;
|
|
}
|
|
function isNumber(val) {
|
|
return typeof val === 'number' && !isNaN(val);
|
|
}
|
|
function valueOrDefault(value, defaultValue) {
|
|
return defined(value) ? value : defaultValue;
|
|
}
|
|
function sqr(value) {
|
|
return value * value;
|
|
}
|
|
function objectKey(object) {
|
|
var parts = [];
|
|
for (var key in object) {
|
|
parts.push(key + object[key]);
|
|
}
|
|
return parts.sort().join('');
|
|
}
|
|
function hashKey(str) {
|
|
var hash = 2166136261;
|
|
for (var i = 0; i < str.length; ++i) {
|
|
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
hash ^= str.charCodeAt(i);
|
|
}
|
|
return hash >>> 0;
|
|
}
|
|
function hashObject(object) {
|
|
return hashKey(objectKey(object));
|
|
}
|
|
var now = Date.now;
|
|
if (!now) {
|
|
now = function () {
|
|
return new Date().getTime();
|
|
};
|
|
}
|
|
function arrayLimits(arr) {
|
|
var length = arr.length, i, min = MAX_NUM, max = MIN_NUM;
|
|
for (i = 0; i < length; i++) {
|
|
max = math.max(max, arr[i]);
|
|
min = math.min(min, arr[i]);
|
|
}
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
function arrayMin(arr) {
|
|
return arrayLimits(arr).min;
|
|
}
|
|
function arrayMax(arr) {
|
|
return arrayLimits(arr).max;
|
|
}
|
|
function sparseArrayMin(arr) {
|
|
return sparseArrayLimits(arr).min;
|
|
}
|
|
function sparseArrayMax(arr) {
|
|
return sparseArrayLimits(arr).max;
|
|
}
|
|
function sparseArrayLimits(arr) {
|
|
var min = MAX_NUM, max = MIN_NUM;
|
|
for (var i = 0, length = arr.length; i < length; i++) {
|
|
var n = arr[i];
|
|
if (n !== null && isFinite(n)) {
|
|
min = math.min(min, n);
|
|
max = math.max(max, n);
|
|
}
|
|
}
|
|
return {
|
|
min: min === MAX_NUM ? undefined : min,
|
|
max: max === MIN_NUM ? undefined : max
|
|
};
|
|
}
|
|
function last(array) {
|
|
if (array) {
|
|
return array[array.length - 1];
|
|
}
|
|
}
|
|
function append(first, second) {
|
|
first.push.apply(first, second);
|
|
return first;
|
|
}
|
|
function renderTemplate(text) {
|
|
return kendo.template(text, {
|
|
useWithBlock: false,
|
|
paramName: 'd'
|
|
});
|
|
}
|
|
function renderAttr(name, value) {
|
|
return defined(value) && value !== null ? ' ' + name + '=\'' + value + '\' ' : '';
|
|
}
|
|
function renderAllAttr(attrs) {
|
|
var output = '';
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
output += renderAttr(attrs[i][0], attrs[i][1]);
|
|
}
|
|
return output;
|
|
}
|
|
function renderStyle(attrs) {
|
|
var output = '';
|
|
for (var i = 0; i < attrs.length; i++) {
|
|
var value = attrs[i][1];
|
|
if (defined(value)) {
|
|
output += attrs[i][0] + ':' + value + ';';
|
|
}
|
|
}
|
|
if (output !== '') {
|
|
return output;
|
|
}
|
|
}
|
|
function renderSize(size) {
|
|
if (typeof size !== 'string') {
|
|
size += 'px';
|
|
}
|
|
return size;
|
|
}
|
|
function renderPos(pos) {
|
|
var result = [];
|
|
if (pos) {
|
|
var parts = kendo.toHyphens(pos).split('-');
|
|
for (var i = 0; i < parts.length; i++) {
|
|
result.push('k-pos-' + parts[i]);
|
|
}
|
|
}
|
|
return result.join(' ');
|
|
}
|
|
function isTransparent(color) {
|
|
return color === '' || color === null || color === 'none' || color === 'transparent' || !defined(color);
|
|
}
|
|
function arabicToRoman(n) {
|
|
var literals = {
|
|
1: 'i',
|
|
10: 'x',
|
|
100: 'c',
|
|
2: 'ii',
|
|
20: 'xx',
|
|
200: 'cc',
|
|
3: 'iii',
|
|
30: 'xxx',
|
|
300: 'ccc',
|
|
4: 'iv',
|
|
40: 'xl',
|
|
400: 'cd',
|
|
5: 'v',
|
|
50: 'l',
|
|
500: 'd',
|
|
6: 'vi',
|
|
60: 'lx',
|
|
600: 'dc',
|
|
7: 'vii',
|
|
70: 'lxx',
|
|
700: 'dcc',
|
|
8: 'viii',
|
|
80: 'lxxx',
|
|
800: 'dccc',
|
|
9: 'ix',
|
|
90: 'xc',
|
|
900: 'cm',
|
|
1000: 'm'
|
|
};
|
|
var values = [
|
|
1000,
|
|
900,
|
|
800,
|
|
700,
|
|
600,
|
|
500,
|
|
400,
|
|
300,
|
|
200,
|
|
100,
|
|
90,
|
|
80,
|
|
70,
|
|
60,
|
|
50,
|
|
40,
|
|
30,
|
|
20,
|
|
10,
|
|
9,
|
|
8,
|
|
7,
|
|
6,
|
|
5,
|
|
4,
|
|
3,
|
|
2,
|
|
1
|
|
];
|
|
var roman = '';
|
|
while (n > 0) {
|
|
if (n < values[0]) {
|
|
values.shift();
|
|
} else {
|
|
roman += literals[values[0]];
|
|
n -= values[0];
|
|
}
|
|
}
|
|
return roman;
|
|
}
|
|
function romanToArabic(r) {
|
|
r = r.toLowerCase();
|
|
var digits = {
|
|
i: 1,
|
|
v: 5,
|
|
x: 10,
|
|
l: 50,
|
|
c: 100,
|
|
d: 500,
|
|
m: 1000
|
|
};
|
|
var value = 0, prev = 0;
|
|
for (var i = 0; i < r.length; ++i) {
|
|
var v = digits[r.charAt(i)];
|
|
if (!v) {
|
|
return null;
|
|
}
|
|
value += v;
|
|
if (v > prev) {
|
|
value -= 2 * prev;
|
|
}
|
|
prev = v;
|
|
}
|
|
return value;
|
|
}
|
|
function memoize(f) {
|
|
var cache = Object.create(null);
|
|
return function () {
|
|
var id = '';
|
|
for (var i = arguments.length; --i >= 0;) {
|
|
id += ':' + arguments[i];
|
|
}
|
|
if (id in cache) {
|
|
return cache[id];
|
|
}
|
|
return f.apply(this, arguments);
|
|
};
|
|
}
|
|
function ucs2decode(string) {
|
|
var output = [], counter = 0, length = string.length, value, extra;
|
|
while (counter < length) {
|
|
value = string.charCodeAt(counter++);
|
|
if (value >= 55296 && value <= 56319 && counter < length) {
|
|
extra = string.charCodeAt(counter++);
|
|
if ((extra & 64512) == 56320) {
|
|
output.push(((value & 1023) << 10) + (extra & 1023) + 65536);
|
|
} else {
|
|
output.push(value);
|
|
counter--;
|
|
}
|
|
} else {
|
|
output.push(value);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
function ucs2encode(array) {
|
|
return array.map(function (value) {
|
|
var output = '';
|
|
if (value > 65535) {
|
|
value -= 65536;
|
|
output += String.fromCharCode(value >>> 10 & 1023 | 55296);
|
|
value = 56320 | value & 1023;
|
|
}
|
|
output += String.fromCharCode(value);
|
|
return output;
|
|
}).join('');
|
|
}
|
|
deepExtend(kendo, {
|
|
util: {
|
|
MAX_NUM: MAX_NUM,
|
|
MIN_NUM: MIN_NUM,
|
|
append: append,
|
|
arrayLimits: arrayLimits,
|
|
arrayMin: arrayMin,
|
|
arrayMax: arrayMax,
|
|
defined: defined,
|
|
deg: deg,
|
|
hashKey: hashKey,
|
|
hashObject: hashObject,
|
|
isNumber: isNumber,
|
|
isTransparent: isTransparent,
|
|
last: last,
|
|
limitValue: limitValue,
|
|
now: now,
|
|
objectKey: objectKey,
|
|
round: round,
|
|
rad: rad,
|
|
renderAttr: renderAttr,
|
|
renderAllAttr: renderAllAttr,
|
|
renderPos: renderPos,
|
|
renderSize: renderSize,
|
|
renderStyle: renderStyle,
|
|
renderTemplate: renderTemplate,
|
|
sparseArrayLimits: sparseArrayLimits,
|
|
sparseArrayMin: sparseArrayMin,
|
|
sparseArrayMax: sparseArrayMax,
|
|
sqr: sqr,
|
|
valueOrDefault: valueOrDefault,
|
|
romanToArabic: romanToArabic,
|
|
arabicToRoman: arabicToRoman,
|
|
memoize: memoize,
|
|
ucs2encode: ucs2encode,
|
|
ucs2decode: ucs2decode
|
|
}
|
|
});
|
|
kendo.drawing.util = kendo.util;
|
|
kendo.dataviz.util = kendo.util;
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/text-metrics', [
|
|
'kendo.core',
|
|
'util/main'
|
|
], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var doc = document, kendo = window.kendo, Class = kendo.Class, util = kendo.util, defined = util.defined;
|
|
var LRUCache = Class.extend({
|
|
init: function (size) {
|
|
this._size = size;
|
|
this._length = 0;
|
|
this._map = {};
|
|
},
|
|
put: function (key, value) {
|
|
var lru = this, map = lru._map, entry = {
|
|
key: key,
|
|
value: value
|
|
};
|
|
map[key] = entry;
|
|
if (!lru._head) {
|
|
lru._head = lru._tail = entry;
|
|
} else {
|
|
lru._tail.newer = entry;
|
|
entry.older = lru._tail;
|
|
lru._tail = entry;
|
|
}
|
|
if (lru._length >= lru._size) {
|
|
map[lru._head.key] = null;
|
|
lru._head = lru._head.newer;
|
|
lru._head.older = null;
|
|
} else {
|
|
lru._length++;
|
|
}
|
|
},
|
|
get: function (key) {
|
|
var lru = this, entry = lru._map[key];
|
|
if (entry) {
|
|
if (entry === lru._head && entry !== lru._tail) {
|
|
lru._head = entry.newer;
|
|
lru._head.older = null;
|
|
}
|
|
if (entry !== lru._tail) {
|
|
if (entry.older) {
|
|
entry.older.newer = entry.newer;
|
|
entry.newer.older = entry.older;
|
|
}
|
|
entry.older = lru._tail;
|
|
entry.newer = null;
|
|
lru._tail.newer = entry;
|
|
lru._tail = entry;
|
|
}
|
|
return entry.value;
|
|
}
|
|
}
|
|
});
|
|
var defaultMeasureBox = $('<div style=\'position: absolute !important; top: -4000px !important; width: auto !important; height: auto !important;' + 'padding: 0 !important; margin: 0 !important; border: 0 !important;' + 'line-height: normal !important; visibility: hidden !important; white-space: nowrap!important;\' />')[0];
|
|
function zeroSize() {
|
|
return {
|
|
width: 0,
|
|
height: 0,
|
|
baseline: 0
|
|
};
|
|
}
|
|
var TextMetrics = Class.extend({
|
|
init: function (options) {
|
|
this._cache = new LRUCache(1000);
|
|
this._initOptions(options);
|
|
},
|
|
options: { baselineMarkerSize: 1 },
|
|
measure: function (text, style, box) {
|
|
if (!text) {
|
|
return zeroSize();
|
|
}
|
|
var styleKey = util.objectKey(style), cacheKey = util.hashKey(text + styleKey), cachedResult = this._cache.get(cacheKey);
|
|
if (cachedResult) {
|
|
return cachedResult;
|
|
}
|
|
var size = zeroSize();
|
|
var measureBox = box ? box : defaultMeasureBox;
|
|
var baselineMarker = this._baselineMarker().cloneNode(false);
|
|
for (var key in style) {
|
|
var value = style[key];
|
|
if (defined(value)) {
|
|
measureBox.style[key] = value;
|
|
}
|
|
}
|
|
$(measureBox).text(text);
|
|
measureBox.appendChild(baselineMarker);
|
|
doc.body.appendChild(measureBox);
|
|
if ((text + '').length) {
|
|
size.width = measureBox.offsetWidth - this.options.baselineMarkerSize;
|
|
size.height = measureBox.offsetHeight;
|
|
size.baseline = baselineMarker.offsetTop + this.options.baselineMarkerSize;
|
|
}
|
|
if (size.width > 0 && size.height > 0) {
|
|
this._cache.put(cacheKey, size);
|
|
}
|
|
measureBox.parentNode.removeChild(measureBox);
|
|
return size;
|
|
},
|
|
_baselineMarker: function () {
|
|
return $('<div class=\'k-baseline-marker\' ' + 'style=\'display: inline-block; vertical-align: baseline;' + 'width: ' + this.options.baselineMarkerSize + 'px; height: ' + this.options.baselineMarkerSize + 'px;' + 'overflow: hidden;\' />')[0];
|
|
}
|
|
});
|
|
TextMetrics.current = new TextMetrics();
|
|
function measureText(text, style, measureBox) {
|
|
return TextMetrics.current.measure(text, style, measureBox);
|
|
}
|
|
function loadFonts(fonts, callback) {
|
|
var promises = [];
|
|
if (fonts.length > 0 && document.fonts) {
|
|
try {
|
|
promises = fonts.map(function (font) {
|
|
return document.fonts.load(font);
|
|
});
|
|
} catch (e) {
|
|
kendo.logToConsole(e);
|
|
}
|
|
Promise.all(promises).then(callback, callback);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
kendo.util.TextMetrics = TextMetrics;
|
|
kendo.util.LRUCache = LRUCache;
|
|
kendo.util.loadFonts = loadFonts;
|
|
kendo.util.measureText = measureText;
|
|
}(window.kendo.jQuery));
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('util/base64', ['util/main'], f);
|
|
}(function () {
|
|
(function () {
|
|
var kendo = window.kendo, deepExtend = kendo.deepExtend, fromCharCode = String.fromCharCode;
|
|
var KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
function encodeBase64(input) {
|
|
var output = '';
|
|
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
|
|
var i = 0;
|
|
input = encodeUTF8(input);
|
|
while (i < input.length) {
|
|
chr1 = input.charCodeAt(i++);
|
|
chr2 = input.charCodeAt(i++);
|
|
chr3 = input.charCodeAt(i++);
|
|
enc1 = chr1 >> 2;
|
|
enc2 = (chr1 & 3) << 4 | chr2 >> 4;
|
|
enc3 = (chr2 & 15) << 2 | chr3 >> 6;
|
|
enc4 = chr3 & 63;
|
|
if (isNaN(chr2)) {
|
|
enc3 = enc4 = 64;
|
|
} else if (isNaN(chr3)) {
|
|
enc4 = 64;
|
|
}
|
|
output = output + KEY_STR.charAt(enc1) + KEY_STR.charAt(enc2) + KEY_STR.charAt(enc3) + KEY_STR.charAt(enc4);
|
|
}
|
|
return output;
|
|
}
|
|
function encodeUTF8(input) {
|
|
var output = '';
|
|
for (var i = 0; i < input.length; i++) {
|
|
var c = input.charCodeAt(i);
|
|
if (c < 128) {
|
|
output += fromCharCode(c);
|
|
} else if (c < 2048) {
|
|
output += fromCharCode(192 | c >>> 6);
|
|
output += fromCharCode(128 | c & 63);
|
|
} else if (c < 65536) {
|
|
output += fromCharCode(224 | c >>> 12);
|
|
output += fromCharCode(128 | c >>> 6 & 63);
|
|
output += fromCharCode(128 | c & 63);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
deepExtend(kendo.util, {
|
|
encodeBase64: encodeBase64,
|
|
encodeUTF8: encodeUTF8
|
|
});
|
|
}());
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('mixins/observers', ['kendo.core'], f);
|
|
}(function () {
|
|
(function ($) {
|
|
var math = Math, kendo = window.kendo, deepExtend = kendo.deepExtend, inArray = $.inArray;
|
|
var ObserversMixin = {
|
|
observers: function () {
|
|
this._observers = this._observers || [];
|
|
return this._observers;
|
|
},
|
|
addObserver: function (element) {
|
|
if (!this._observers) {
|
|
this._observers = [element];
|
|
} else {
|
|
this._observers.push(element);
|
|
}
|
|
return this;
|
|
},
|
|
removeObserver: function (element) {
|
|
var observers = this.observers();
|
|
var index = inArray(element, observers);
|
|
if (index != -1) {
|
|
observers.splice(index, 1);
|
|
}
|
|
return this;
|
|
},
|
|
trigger: function (methodName, event) {
|
|
var observers = this._observers;
|
|
var observer;
|
|
var idx;
|
|
if (observers && !this._suspended) {
|
|
for (idx = 0; idx < observers.length; idx++) {
|
|
observer = observers[idx];
|
|
if (observer[methodName]) {
|
|
observer[methodName](event);
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
optionsChange: function (e) {
|
|
this.trigger('optionsChange', e);
|
|
},
|
|
geometryChange: function (e) {
|
|
this.trigger('geometryChange', e);
|
|
},
|
|
suspend: function () {
|
|
this._suspended = (this._suspended || 0) + 1;
|
|
return this;
|
|
},
|
|
resume: function () {
|
|
this._suspended = math.max((this._suspended || 0) - 1, 0);
|
|
return this;
|
|
},
|
|
_observerField: function (field, value) {
|
|
if (this[field]) {
|
|
this[field].removeObserver(this);
|
|
}
|
|
this[field] = value;
|
|
value.addObserver(this);
|
|
}
|
|
};
|
|
deepExtend(kendo, { mixins: { ObserversMixin: ObserversMixin } });
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
}));
|
|
(function (f, define) {
|
|
define('kendo.dataviz.core', [
|
|
'kendo.core',
|
|
'kendo.drawing'
|
|
], f);
|
|
}(function () {
|
|
var __meta__ = {
|
|
id: 'dataviz.core',
|
|
name: 'Core',
|
|
description: 'The DataViz core functions',
|
|
category: 'dataviz',
|
|
depends: [
|
|
'core',
|
|
'drawing'
|
|
],
|
|
hidden: true
|
|
};
|
|
(function ($, undefined) {
|
|
var kendo = window.kendo, util = kendo.util, append = util.append, defined = util.defined, last = util.last, valueOrDefault = util.valueOrDefault, dataviz = kendo.dataviz, geom = dataviz.geometry, draw = dataviz.drawing, measureText = draw.util.measureText, Class = kendo.Class, template = kendo.template, noop = $.noop, indexOf = $.inArray, isPlainObject = $.isPlainObject, trim = $.trim, math = Math, deepExtend = kendo.deepExtend;
|
|
var AXIS_LABEL_CLICK = 'axisLabelClick', BLACK = '#000', BOTTOM = 'bottom', CENTER = 'center', COORD_PRECISION = 3, CLIP = 'clip', CIRCLE = 'circle', CROSS = 'cross', DEFAULT_FONT = '12px sans-serif', DEFAULT_HEIGHT = 400, DEFAULT_ICON_SIZE = 7, DEFAULT_PRECISION = 10, DEFAULT_WIDTH = 600, DEG_TO_RAD = math.PI / 180, FORMAT_REGEX = /\{\d+:?/, HEIGHT = 'height', COORDINATE_LIMIT = 100000, INITIAL_ANIMATION_DURATION = 600, INSIDE = 'inside', LEFT = 'left', LINEAR = 'linear', MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, NONE = 'none', NOTE_CLICK = 'noteClick', NOTE_HOVER = 'noteHover', OUTSIDE = 'outside', RADIAL = 'radial', RIGHT = 'right', TOP = 'top', TRIANGLE = 'triangle', WIDTH = 'width', WHITE = '#fff', X = 'x', Y = 'y', ZERO_THRESHOLD = 0.2;
|
|
function getSpacing(value, defaultSpacing) {
|
|
var spacing = {
|
|
top: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
left: 0
|
|
};
|
|
defaultSpacing = defaultSpacing || 0;
|
|
if (typeof value === 'number') {
|
|
spacing[TOP] = spacing[RIGHT] = spacing[BOTTOM] = spacing[LEFT] = value;
|
|
} else {
|
|
spacing[TOP] = value[TOP] || defaultSpacing;
|
|
spacing[RIGHT] = value[RIGHT] || defaultSpacing;
|
|
spacing[BOTTOM] = value[BOTTOM] || defaultSpacing;
|
|
spacing[LEFT] = value[LEFT] || defaultSpacing;
|
|
}
|
|
return spacing;
|
|
}
|
|
var Point2D = function (x, y) {
|
|
var point = this;
|
|
if (!(point instanceof Point2D)) {
|
|
return new Point2D(x, y);
|
|
}
|
|
point.x = x || 0;
|
|
point.y = y || 0;
|
|
};
|
|
Point2D.fn = Point2D.prototype = {
|
|
clone: function () {
|
|
var point = this;
|
|
return new Point2D(point.x, point.y);
|
|
},
|
|
equals: function (point) {
|
|
return point && point.x === this.x && point.y === this.y;
|
|
},
|
|
rotate: function (center, degrees) {
|
|
var point = this, theta = degrees * DEG_TO_RAD, cosT = math.cos(theta), sinT = math.sin(theta), cx = center.x, cy = center.y, x = point.x, y = point.y;
|
|
point.x = round(cx + (x - cx) * cosT + (y - cy) * sinT, COORD_PRECISION);
|
|
point.y = round(cy + (y - cy) * cosT - (x - cx) * sinT, COORD_PRECISION);
|
|
return point;
|
|
},
|
|
multiply: function (a) {
|
|
var point = this;
|
|
point.x *= a;
|
|
point.y *= a;
|
|
return point;
|
|
},
|
|
distanceTo: function (point) {
|
|
var dx = this.x - point.x, dy = this.y - point.y;
|
|
return math.sqrt(dx * dx + dy * dy);
|
|
}
|
|
};
|
|
Point2D.onCircle = function (c, a, r) {
|
|
a *= DEG_TO_RAD;
|
|
return new Point2D(c.x - r * math.cos(a), c.y - r * math.sin(a));
|
|
};
|
|
var Box2D = function (x1, y1, x2, y2) {
|
|
var box = this;
|
|
if (!(box instanceof Box2D)) {
|
|
return new Box2D(x1, y1, x2, y2);
|
|
}
|
|
box.x1 = x1 || 0;
|
|
box.x2 = x2 || 0;
|
|
box.y1 = y1 || 0;
|
|
box.y2 = y2 || 0;
|
|
};
|
|
Box2D.fn = Box2D.prototype = {
|
|
width: function () {
|
|
return this.x2 - this.x1;
|
|
},
|
|
height: function () {
|
|
return this.y2 - this.y1;
|
|
},
|
|
translate: function (dx, dy) {
|
|
var box = this;
|
|
box.x1 += dx;
|
|
box.x2 += dx;
|
|
box.y1 += dy;
|
|
box.y2 += dy;
|
|
return box;
|
|
},
|
|
move: function (x, y) {
|
|
var box = this, height = box.height(), width = box.width();
|
|
if (defined(x)) {
|
|
box.x1 = x;
|
|
box.x2 = box.x1 + width;
|
|
}
|
|
if (defined(y)) {
|
|
box.y1 = y;
|
|
box.y2 = box.y1 + height;
|
|
}
|
|
return box;
|
|
},
|
|
wrap: function (targetBox) {
|
|
var box = this;
|
|
box.x1 = math.min(box.x1, targetBox.x1);
|
|
box.y1 = math.min(box.y1, targetBox.y1);
|
|
box.x2 = math.max(box.x2, targetBox.x2);
|
|
box.y2 = math.max(box.y2, targetBox.y2);
|
|
return box;
|
|
},
|
|
wrapPoint: function (point) {
|
|
this.wrap(new Box2D(point.x, point.y, point.x, point.y));
|
|
return this;
|
|
},
|
|
snapTo: function (targetBox, axis) {
|
|
var box = this;
|
|
if (axis == X || !axis) {
|
|
box.x1 = targetBox.x1;
|
|
box.x2 = targetBox.x2;
|
|
}
|
|
if (axis == Y || !axis) {
|
|
box.y1 = targetBox.y1;
|
|
box.y2 = targetBox.y2;
|
|
}
|
|
return box;
|
|
},
|
|
alignTo: function (targetBox, anchor) {
|
|
var box = this, height = box.height(), width = box.width(), axis = anchor == TOP || anchor == BOTTOM ? Y : X, offset = axis == Y ? height : width;
|
|
if (anchor === CENTER) {
|
|
var targetCenter = targetBox.center();
|
|
var center = box.center();
|
|
box.x1 += targetCenter.x - center.x;
|
|
box.y1 += targetCenter.y - center.y;
|
|
} else if (anchor === TOP || anchor === LEFT) {
|
|
box[axis + 1] = targetBox[axis + 1] - offset;
|
|
} else {
|
|
box[axis + 1] = targetBox[axis + 2];
|
|
}
|
|
box.x2 = box.x1 + width;
|
|
box.y2 = box.y1 + height;
|
|
return box;
|
|
},
|
|
shrink: function (dw, dh) {
|
|
var box = this;
|
|
box.x2 -= dw;
|
|
box.y2 -= dh;
|
|
return box;
|
|
},
|
|
expand: function (dw, dh) {
|
|
this.shrink(-dw, -dh);
|
|
return this;
|
|
},
|
|
pad: function (padding) {
|
|
var box = this, spacing = getSpacing(padding);
|
|
box.x1 -= spacing.left;
|
|
box.x2 += spacing.right;
|
|
box.y1 -= spacing.top;
|
|
box.y2 += spacing.bottom;
|
|
return box;
|
|
},
|
|
unpad: function (padding) {
|
|
var box = this, spacing = getSpacing(padding);
|
|
spacing.left = -spacing.left;
|
|
spacing.top = -spacing.top;
|
|
spacing.right = -spacing.right;
|
|
spacing.bottom = -spacing.bottom;
|
|
return box.pad(spacing);
|
|
},
|
|
clone: function () {
|
|
var box = this;
|
|
return new Box2D(box.x1, box.y1, box.x2, box.y2);
|
|
},
|
|
center: function () {
|
|
var box = this;
|
|
return new Point2D(box.x1 + box.width() / 2, box.y1 + box.height() / 2);
|
|
},
|
|
containsPoint: function (point) {
|
|
var box = this;
|
|
return point.x >= box.x1 && point.x <= box.x2 && point.y >= box.y1 && point.y <= box.y2;
|
|
},
|
|
points: function () {
|
|
var box = this;
|
|
return [
|
|
new Point2D(box.x1, box.y1),
|
|
new Point2D(box.x2, box.y1),
|
|
new Point2D(box.x2, box.y2),
|
|
new Point2D(box.x1, box.y2)
|
|
];
|
|
},
|
|
getHash: function () {
|
|
var box = this;
|
|
return [
|
|
box.x1,
|
|
box.y1,
|
|
box.x2,
|
|
box.y2
|
|
].join(',');
|
|
},
|
|
overlaps: function (box) {
|
|
return !(box.y2 < this.y1 || this.y2 < box.y1 || box.x2 < this.x1 || this.x2 < box.x1);
|
|
},
|
|
rotate: function (rotation) {
|
|
var box = this;
|
|
var width = box.width();
|
|
var height = box.height();
|
|
var center = box.center();
|
|
var cx = center.x;
|
|
var cy = center.y;
|
|
var r1 = rotatePoint(0, 0, cx, cy, rotation);
|
|
var r2 = rotatePoint(width, 0, cx, cy, rotation);
|
|
var r3 = rotatePoint(width, height, cx, cy, rotation);
|
|
var r4 = rotatePoint(0, height, cx, cy, rotation);
|
|
width = math.max(r1.x, r2.x, r3.x, r4.x) - math.min(r1.x, r2.x, r3.x, r4.x);
|
|
height = math.max(r1.y, r2.y, r3.y, r4.y) - math.min(r1.y, r2.y, r3.y, r4.y);
|
|
box.x2 = box.x1 + width;
|
|
box.y2 = box.y1 + height;
|
|
return box;
|
|
},
|
|
toRect: function () {
|
|
return new geom.Rect([
|
|
this.x1,
|
|
this.y1
|
|
], [
|
|
this.width(),
|
|
this.height()
|
|
]);
|
|
},
|
|
hasSize: function () {
|
|
return this.width() !== 0 && this.height() !== 0;
|
|
},
|
|
align: function (targetBox, axis, alignment) {
|
|
var box = this, c1 = axis + 1, c2 = axis + 2, sizeFunc = axis === X ? WIDTH : HEIGHT, size = box[sizeFunc]();
|
|
if (inArray(alignment, [
|
|
LEFT,
|
|
TOP
|
|
])) {
|
|
box[c1] = targetBox[c1];
|
|
box[c2] = box[c1] + size;
|
|
} else if (inArray(alignment, [
|
|
RIGHT,
|
|
BOTTOM
|
|
])) {
|
|
box[c2] = targetBox[c2];
|
|
box[c1] = box[c2] - size;
|
|
} else if (alignment == CENTER) {
|
|
box[c1] = targetBox[c1] + (targetBox[sizeFunc]() - size) / 2;
|
|
box[c2] = box[c1] + size;
|
|
}
|
|
}
|
|
};
|
|
var Ring = Class.extend({
|
|
init: function (center, innerRadius, radius, startAngle, angle) {
|
|
var ring = this;
|
|
ring.c = center;
|
|
ring.ir = innerRadius;
|
|
ring.r = radius;
|
|
ring.startAngle = startAngle;
|
|
ring.angle = angle;
|
|
},
|
|
clone: function () {
|
|
var r = this;
|
|
return new Ring(r.c, r.ir, r.r, r.startAngle, r.angle);
|
|
},
|
|
middle: function () {
|
|
return this.startAngle + this.angle / 2;
|
|
},
|
|
radius: function (newRadius, innerRadius) {
|
|
var that = this;
|
|
if (innerRadius) {
|
|
that.ir = newRadius;
|
|
} else {
|
|
that.r = newRadius;
|
|
}
|
|
return that;
|
|
},
|
|
point: function (angle, innerRadius) {
|
|
var ring = this, radianAngle = angle * DEG_TO_RAD, ax = math.cos(radianAngle), ay = math.sin(radianAngle), radius = innerRadius ? ring.ir : ring.r, x = round(ring.c.x - ax * radius, COORD_PRECISION), y = round(ring.c.y - ay * radius, COORD_PRECISION);
|
|
return new Point2D(x, y);
|
|
},
|
|
adjacentBox: function (distance, width, height) {
|
|
var sector = this.clone().expand(distance), midAndle = sector.middle(), midPoint = sector.point(midAndle), hw = width / 2, hh = height / 2, x = midPoint.x - hw, y = midPoint.y - hh, sa = math.sin(midAndle * DEG_TO_RAD), ca = math.cos(midAndle * DEG_TO_RAD);
|
|
if (math.abs(sa) < 0.9) {
|
|
x += hw * -ca / math.abs(ca);
|
|
}
|
|
if (math.abs(ca) < 0.9) {
|
|
y += hh * -sa / math.abs(sa);
|
|
}
|
|
return new Box2D(x, y, x + width, y + height);
|
|
},
|
|
containsPoint: function (p) {
|
|
var ring = this, c = ring.c, ir = ring.ir, r = ring.r, startAngle = ring.startAngle, endAngle = ring.startAngle + ring.angle, dx = p.x - c.x, dy = p.y - c.y, vector = new Point2D(dx, dy), startPoint = ring.point(startAngle), startVector = new Point2D(startPoint.x - c.x, startPoint.y - c.y), endPoint = ring.point(endAngle), endVector = new Point2D(endPoint.x - c.x, endPoint.y - c.y), dist = round(dx * dx + dy * dy, COORD_PRECISION);
|
|
return (startVector.equals(vector) || clockwise(startVector, vector)) && !clockwise(endVector, vector) && dist >= ir * ir && dist <= r * r;
|
|
},
|
|
getBBox: function () {
|
|
var ring = this, box = new Box2D(MAX_VALUE, MAX_VALUE, MIN_VALUE, MIN_VALUE), sa = round(ring.startAngle % 360), ea = round((sa + ring.angle) % 360), innerRadius = ring.ir, allAngles = [
|
|
0,
|
|
90,
|
|
180,
|
|
270,
|
|
sa,
|
|
ea
|
|
].sort(numericComparer), saIndex = indexOf(sa, allAngles), eaIndex = indexOf(ea, allAngles), angles, i, point;
|
|
if (sa == ea) {
|
|
angles = allAngles;
|
|
} else {
|
|
if (saIndex < eaIndex) {
|
|
angles = allAngles.slice(saIndex, eaIndex + 1);
|
|
} else {
|
|
angles = [].concat(allAngles.slice(0, eaIndex + 1), allAngles.slice(saIndex, allAngles.length));
|
|
}
|
|
}
|
|
for (i = 0; i < angles.length; i++) {
|
|
point = ring.point(angles[i]);
|
|
box.wrapPoint(point);
|
|
box.wrapPoint(point, innerRadius);
|
|
}
|
|
if (!innerRadius) {
|
|
box.wrapPoint(ring.c);
|
|
}
|
|
return box;
|
|
},
|
|
expand: function (value) {
|
|
this.r += value;
|
|
return this;
|
|
}
|
|
});
|
|
var Sector = Ring.extend({
|
|
init: function (center, radius, startAngle, angle) {
|
|
Ring.fn.init.call(this, center, 0, radius, startAngle, angle);
|
|
},
|
|
expand: function (value) {
|
|
return Ring.fn.expand.call(this, value);
|
|
},
|
|
clone: function () {
|
|
var sector = this;
|
|
return new Sector(sector.c, sector.r, sector.startAngle, sector.angle);
|
|
},
|
|
radius: function (newRadius) {
|
|
return Ring.fn.radius.call(this, newRadius);
|
|
},
|
|
point: function (angle) {
|
|
return Ring.fn.point.call(this, angle);
|
|
}
|
|
});
|
|
var ShapeBuilder = function () {
|
|
};
|
|
ShapeBuilder.fn = ShapeBuilder.prototype = {
|
|
createRing: function (sector, options) {
|
|
var startAngle = sector.startAngle + 180;
|
|
var endAngle = sector.angle + startAngle;
|
|
var center = new geom.Point(sector.c.x, sector.c.y);
|
|
var radius = math.max(sector.r, 0);
|
|
var innerRadius = math.max(sector.ir, 0);
|
|
var arc = new geom.Arc(center, {
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
radiusX: radius,
|
|
radiusY: radius
|
|
});
|
|
var path = draw.Path.fromArc(arc, options).close();
|
|
if (innerRadius) {
|
|
arc.radiusX = arc.radiusY = innerRadius;
|
|
var innerEnd = arc.pointAt(endAngle);
|
|
path.lineTo(innerEnd.x, innerEnd.y);
|
|
path.arc(endAngle, startAngle, innerRadius, innerRadius, true);
|
|
} else {
|
|
path.lineTo(center.x, center.y);
|
|
}
|
|
return path;
|
|
}
|
|
};
|
|
ShapeBuilder.current = new ShapeBuilder();
|
|
var ChartElement = Class.extend({
|
|
init: function (options) {
|
|
var element = this;
|
|
element.children = [];
|
|
element.options = deepExtend({}, element.options, options);
|
|
},
|
|
reflow: function (targetBox) {
|
|
var element = this, children = element.children, box, i, currentChild;
|
|
for (i = 0; i < children.length; i++) {
|
|
currentChild = children[i];
|
|
currentChild.reflow(targetBox);
|
|
box = box ? box.wrap(currentChild.box) : currentChild.box.clone();
|
|
}
|
|
element.box = box || targetBox;
|
|
},
|
|
destroy: function () {
|
|
var element = this, children = element.children, i;
|
|
if (this.animation) {
|
|
this.animation.destroy();
|
|
}
|
|
for (i = 0; i < children.length; i++) {
|
|
children[i].destroy();
|
|
}
|
|
},
|
|
getRoot: function () {
|
|
var parent = this.parent;
|
|
return parent ? parent.getRoot() : null;
|
|
},
|
|
getChart: function () {
|
|
var root = this.getRoot();
|
|
if (root) {
|
|
return root.chart;
|
|
}
|
|
},
|
|
translateChildren: function (dx, dy) {
|
|
var element = this, children = element.children, childrenCount = children.length, i;
|
|
for (i = 0; i < childrenCount; i++) {
|
|
children[i].box.translate(dx, dy);
|
|
}
|
|
},
|
|
append: function () {
|
|
append(this.children, arguments);
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
arguments[i].parent = this;
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
if (this.options.visible === false) {
|
|
return;
|
|
}
|
|
this.createVisual();
|
|
this.addVisual();
|
|
this.renderChildren();
|
|
this.createAnimation();
|
|
this.renderComplete();
|
|
},
|
|
addVisual: function () {
|
|
if (this.visual) {
|
|
this.visual.chartElement = this;
|
|
if (this.parent) {
|
|
this.parent.appendVisual(this.visual);
|
|
}
|
|
}
|
|
},
|
|
renderChildren: function () {
|
|
var children = this.children;
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].renderVisual();
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
this.visual = new dataviz.drawing.Group({
|
|
zIndex: this.options.zIndex,
|
|
visible: valueOrDefault(this.options.visible, true)
|
|
});
|
|
},
|
|
createAnimation: function () {
|
|
if (this.visual) {
|
|
this.animation = draw.Animation.create(this.visual, this.options.animation);
|
|
}
|
|
},
|
|
appendVisual: function (childVisual) {
|
|
if (!childVisual.chartElement) {
|
|
childVisual.chartElement = this;
|
|
}
|
|
if (childVisual.options.noclip) {
|
|
this.clipRoot().visual.append(childVisual);
|
|
} else if (defined(childVisual.options.zIndex)) {
|
|
this.stackRoot().stackVisual(childVisual);
|
|
} else if (this.visual) {
|
|
this.visual.append(childVisual);
|
|
} else {
|
|
this.parent.appendVisual(childVisual);
|
|
}
|
|
},
|
|
clipRoot: function () {
|
|
if (this.parent) {
|
|
return this.parent.clipRoot();
|
|
}
|
|
return this;
|
|
},
|
|
stackRoot: function () {
|
|
if (this.parent) {
|
|
return this.parent.stackRoot();
|
|
}
|
|
return this;
|
|
},
|
|
stackVisual: function (childVisual) {
|
|
var zIndex = childVisual.options.zIndex || 0;
|
|
var visuals = this.visual.children;
|
|
for (var pos = 0; pos < visuals.length; pos++) {
|
|
var sibling = visuals[pos];
|
|
var here = valueOrDefault(sibling.options.zIndex, 0);
|
|
if (here > zIndex) {
|
|
break;
|
|
}
|
|
}
|
|
this.visual.insertAt(childVisual, pos);
|
|
},
|
|
traverse: function (callback) {
|
|
var children = this.children;
|
|
for (var i = 0; i < children.length; i++) {
|
|
var child = children[i];
|
|
callback(child);
|
|
if (child.traverse) {
|
|
child.traverse(callback);
|
|
}
|
|
}
|
|
},
|
|
closest: function (match) {
|
|
var element = this;
|
|
var matched = false;
|
|
while (element && !matched) {
|
|
matched = match(element);
|
|
if (!matched) {
|
|
element = element.parent;
|
|
}
|
|
}
|
|
if (matched) {
|
|
return element;
|
|
}
|
|
},
|
|
renderComplete: $.noop,
|
|
hasHighlight: function () {
|
|
var options = (this.options || {}).highlight;
|
|
return !(!this.createHighlight || options && options.visible === false);
|
|
},
|
|
toggleHighlight: function (show) {
|
|
var that = this;
|
|
var highlight = that._highlight;
|
|
var options = (that.options || {}).highlight;
|
|
var customVisual = (options || {}).visual;
|
|
if (!highlight) {
|
|
var highlightOptions = {
|
|
fill: {
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
stroke: {
|
|
color: WHITE,
|
|
width: 1,
|
|
opacity: 0.2
|
|
}
|
|
};
|
|
if (customVisual) {
|
|
highlight = that._highlight = customVisual($.extend(that.highlightVisualArgs(), {
|
|
createVisual: function () {
|
|
return that.createHighlight(highlightOptions);
|
|
},
|
|
sender: that.getChart(),
|
|
series: that.series,
|
|
dataItem: that.dataItem,
|
|
category: that.category,
|
|
value: that.value,
|
|
percentage: that.percentage,
|
|
runningTotal: that.runningTotal,
|
|
total: that.total
|
|
}));
|
|
if (!highlight) {
|
|
return;
|
|
}
|
|
} else {
|
|
highlight = that._highlight = that.createHighlight(highlightOptions);
|
|
}
|
|
highlight.options.zIndex = that.options.zIndex;
|
|
that.appendVisual(highlight);
|
|
}
|
|
highlight.visible(show);
|
|
},
|
|
createGradientOverlay: function (element, options, gradientOptions) {
|
|
var overlay = new draw.Path(deepExtend({
|
|
stroke: { color: NONE },
|
|
fill: this.createGradient(gradientOptions),
|
|
closed: element.options.closed
|
|
}, options));
|
|
overlay.segments.elements(element.segments.elements());
|
|
return overlay;
|
|
},
|
|
createGradient: function (options) {
|
|
if (this.parent) {
|
|
return this.parent.createGradient(options);
|
|
}
|
|
}
|
|
});
|
|
var RootElement = ChartElement.extend({
|
|
init: function (options) {
|
|
var root = this;
|
|
root.gradients = {};
|
|
ChartElement.fn.init.call(root, options);
|
|
},
|
|
options: {
|
|
width: DEFAULT_WIDTH,
|
|
height: DEFAULT_HEIGHT,
|
|
background: WHITE,
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
margin: getSpacing(5),
|
|
zIndex: -2
|
|
},
|
|
reflow: function () {
|
|
var root = this, options = root.options, children = root.children, currentBox = new Box2D(0, 0, options.width, options.height);
|
|
root.box = currentBox.unpad(options.margin);
|
|
for (var i = 0; i < children.length; i++) {
|
|
children[i].reflow(currentBox);
|
|
currentBox = boxDiff(currentBox, children[i].box) || Box2D();
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
this.visual = new draw.Group();
|
|
this.createBackground();
|
|
},
|
|
createBackground: function () {
|
|
var options = this.options;
|
|
var border = options.border || {};
|
|
var box = this.box.clone().pad(options.margin).unpad(border.width);
|
|
var background = draw.Path.fromRect(box.toRect(), {
|
|
stroke: {
|
|
color: border.width ? border.color : '',
|
|
width: border.width,
|
|
dashType: border.dashType
|
|
},
|
|
fill: {
|
|
color: options.background,
|
|
opacity: options.opacity
|
|
},
|
|
zIndex: -10
|
|
});
|
|
this.visual.append(background);
|
|
},
|
|
getRoot: function () {
|
|
return this;
|
|
},
|
|
createGradient: function (options) {
|
|
var gradients = this.gradients;
|
|
var hashCode = util.objectKey(options);
|
|
var gradient = dataviz.Gradients[options.gradient];
|
|
var drawingGradient;
|
|
if (gradients[hashCode]) {
|
|
drawingGradient = gradients[hashCode];
|
|
} else {
|
|
var gradientOptions = deepExtend({}, gradient, options);
|
|
if (gradient.type == 'linear') {
|
|
drawingGradient = new draw.LinearGradient(gradientOptions);
|
|
} else {
|
|
if (options.innerRadius) {
|
|
gradientOptions.stops = innerRadialStops(gradientOptions);
|
|
}
|
|
drawingGradient = new draw.RadialGradient(gradientOptions);
|
|
drawingGradient.supportVML = gradient.supportVML !== false;
|
|
}
|
|
gradients[hashCode] = drawingGradient;
|
|
}
|
|
return drawingGradient;
|
|
}
|
|
});
|
|
var BoxElement = ChartElement.extend({
|
|
options: {
|
|
align: LEFT,
|
|
vAlign: TOP,
|
|
margin: {},
|
|
padding: {},
|
|
border: {
|
|
color: BLACK,
|
|
width: 0
|
|
},
|
|
background: '',
|
|
shrinkToFit: false,
|
|
width: 0,
|
|
height: 0,
|
|
visible: true
|
|
},
|
|
reflow: function (targetBox) {
|
|
var element = this, box, contentBox, options = element.options, width = options.width, height = options.height, hasSetSize = width && height, shrinkToFit = options.shrinkToFit, margin = getSpacing(options.margin), padding = getSpacing(options.padding), borderWidth = options.border.width, children = element.children, i, item;
|
|
function reflowPaddingBox() {
|
|
element.align(targetBox, X, options.align);
|
|
element.align(targetBox, Y, options.vAlign);
|
|
element.paddingBox = box.clone().unpad(margin).unpad(borderWidth);
|
|
}
|
|
contentBox = targetBox.clone();
|
|
if (hasSetSize) {
|
|
contentBox.x2 = contentBox.x1 + width;
|
|
contentBox.y2 = contentBox.y1 + height;
|
|
}
|
|
if (shrinkToFit) {
|
|
contentBox.unpad(margin).unpad(borderWidth).unpad(padding);
|
|
}
|
|
ChartElement.fn.reflow.call(element, contentBox);
|
|
if (hasSetSize) {
|
|
box = element.box = Box2D(0, 0, width, height);
|
|
} else {
|
|
box = element.box;
|
|
}
|
|
if (shrinkToFit && hasSetSize) {
|
|
reflowPaddingBox();
|
|
contentBox = element.contentBox = element.paddingBox.clone().unpad(padding);
|
|
} else {
|
|
contentBox = element.contentBox = box.clone();
|
|
box.pad(padding).pad(borderWidth).pad(margin);
|
|
reflowPaddingBox();
|
|
}
|
|
element.translateChildren(box.x1 - contentBox.x1 + margin.left + borderWidth + padding.left, box.y1 - contentBox.y1 + margin.top + borderWidth + padding.top);
|
|
for (i = 0; i < children.length; i++) {
|
|
item = children[i];
|
|
item.reflow(item.box);
|
|
}
|
|
},
|
|
align: function (targetBox, axis, alignment) {
|
|
this.box.align(targetBox, axis, alignment);
|
|
},
|
|
hasBox: function () {
|
|
var options = this.options;
|
|
return options.border.width || options.background;
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
var options = this.options;
|
|
if (options.visible && this.hasBox()) {
|
|
this.visual.append(draw.Path.fromRect(this.paddingBox.toRect(), this.visualStyle()));
|
|
}
|
|
},
|
|
visualStyle: function () {
|
|
var boxElement = this, options = boxElement.options, border = options.border || {};
|
|
return {
|
|
stroke: {
|
|
width: border.width,
|
|
color: border.color,
|
|
opacity: valueOrDefault(border.opacity, options.opacity),
|
|
dashType: border.dashType
|
|
},
|
|
fill: {
|
|
color: options.background,
|
|
opacity: options.opacity
|
|
},
|
|
cursor: options.cursor
|
|
};
|
|
}
|
|
});
|
|
var Text = ChartElement.extend({
|
|
init: function (content, options) {
|
|
var text = this;
|
|
ChartElement.fn.init.call(text, options);
|
|
text.content = content;
|
|
text.reflow(Box2D());
|
|
},
|
|
options: {
|
|
font: DEFAULT_FONT,
|
|
color: BLACK,
|
|
align: LEFT,
|
|
vAlign: ''
|
|
},
|
|
reflow: function (targetBox) {
|
|
var text = this, options = text.options, size;
|
|
size = options.size = measureText(text.content, { font: options.font });
|
|
text.baseline = size.baseline;
|
|
text.box = Box2D(targetBox.x1, targetBox.y1, targetBox.x1 + size.width, targetBox.y1 + size.height);
|
|
},
|
|
createVisual: function () {
|
|
var opt = this.options;
|
|
this.visual = new draw.Text(this.content, this.box.toRect().topLeft(), {
|
|
font: opt.font,
|
|
fill: {
|
|
color: opt.color,
|
|
opacity: opt.opacity
|
|
},
|
|
cursor: opt.cursor
|
|
});
|
|
}
|
|
});
|
|
var FloatElement = ChartElement.extend({
|
|
init: function (options) {
|
|
ChartElement.fn.init.call(this, options);
|
|
this._initDirection();
|
|
},
|
|
_initDirection: function () {
|
|
var options = this.options;
|
|
if (options.vertical) {
|
|
this.groupAxis = X;
|
|
this.elementAxis = Y;
|
|
this.groupSizeField = WIDTH;
|
|
this.elementSizeField = HEIGHT;
|
|
this.groupSpacing = options.spacing;
|
|
this.elementSpacing = options.vSpacing;
|
|
} else {
|
|
this.groupAxis = Y;
|
|
this.elementAxis = X;
|
|
this.groupSizeField = HEIGHT;
|
|
this.elementSizeField = WIDTH;
|
|
this.groupSpacing = options.vSpacing;
|
|
this.elementSpacing = options.spacing;
|
|
}
|
|
},
|
|
options: {
|
|
vertical: true,
|
|
wrap: true,
|
|
vSpacing: 0,
|
|
spacing: 0
|
|
},
|
|
reflow: function (targetBox) {
|
|
this.box = targetBox.clone();
|
|
this.reflowChildren();
|
|
},
|
|
reflowChildren: function () {
|
|
var floatElement = this;
|
|
var box = floatElement.box;
|
|
var elementAxis = floatElement.elementAxis;
|
|
var groupAxis = floatElement.groupAxis;
|
|
var elementSizeField = floatElement.elementSizeField;
|
|
var groupSizeField = floatElement.groupSizeField;
|
|
var groupOptions = floatElement.groupOptions();
|
|
var groups = groupOptions.groups;
|
|
var groupsCount = groups.length;
|
|
var groupsStart = box[groupAxis + 1] + floatElement.alignStart(groupOptions.groupsSize, box[groupSizeField]());
|
|
var groupStart = groupsStart;
|
|
var elementStart;
|
|
var groupElementStart;
|
|
var group;
|
|
var groupElements;
|
|
var groupElementsCount;
|
|
var idx;
|
|
var groupIdx;
|
|
var element;
|
|
var elementBox;
|
|
var elementSize;
|
|
if (groupsCount) {
|
|
for (groupIdx = 0; groupIdx < groupsCount; groupIdx++) {
|
|
group = groups[groupIdx];
|
|
groupElements = group.groupElements;
|
|
groupElementsCount = groupElements.length;
|
|
elementStart = box[elementAxis + 1];
|
|
for (idx = 0; idx < groupElementsCount; idx++) {
|
|
element = groupElements[idx];
|
|
elementSize = floatElement.elementSize(element);
|
|
groupElementStart = groupStart + floatElement.alignStart(elementSize[groupSizeField], group.groupSize);
|
|
elementBox = Box2D();
|
|
elementBox[groupAxis + 1] = groupElementStart;
|
|
elementBox[groupAxis + 2] = groupElementStart + elementSize[groupSizeField];
|
|
elementBox[elementAxis + 1] = elementStart;
|
|
elementBox[elementAxis + 2] = elementStart + elementSize[elementSizeField];
|
|
element.reflow(elementBox);
|
|
elementStart += elementSize[elementSizeField] + floatElement.elementSpacing;
|
|
}
|
|
groupStart += group.groupSize + floatElement.groupSpacing;
|
|
}
|
|
box[groupAxis + 1] = groupsStart;
|
|
box[groupAxis + 2] = groupsStart + groupOptions.groupsSize;
|
|
box[elementAxis + 2] = box[elementAxis + 1] + groupOptions.maxGroupElementsSize;
|
|
}
|
|
},
|
|
alignStart: function (size, maxSize) {
|
|
var start = 0;
|
|
var align = this.options.align;
|
|
if (align == RIGHT || align == BOTTOM) {
|
|
start = maxSize - size;
|
|
} else if (align == CENTER) {
|
|
start = (maxSize - size) / 2;
|
|
}
|
|
return start;
|
|
},
|
|
groupOptions: function () {
|
|
var floatElement = this;
|
|
var box = floatElement.box;
|
|
var children = floatElement.children;
|
|
var childrenCount = children.length;
|
|
var elementSizeField = this.elementSizeField;
|
|
var groupSizeField = this.groupSizeField;
|
|
var elementSpacing = this.elementSpacing;
|
|
var groupSpacing = this.groupSpacing;
|
|
var maxSize = round(box[elementSizeField]());
|
|
var idx = 0;
|
|
var groupSize = 0;
|
|
var elementSize;
|
|
var element;
|
|
var groupElementsSize = 0;
|
|
var groupsSize = 0;
|
|
var groups = [];
|
|
var groupElements = [];
|
|
var maxGroupElementsSize = 0;
|
|
for (idx = 0; idx < childrenCount; idx++) {
|
|
element = children[idx];
|
|
if (!element.box) {
|
|
element.reflow(box);
|
|
}
|
|
elementSize = this.elementSize(element);
|
|
if (floatElement.options.wrap && round(groupElementsSize + elementSpacing + elementSize[elementSizeField]) > maxSize) {
|
|
groups.push({
|
|
groupElements: groupElements,
|
|
groupSize: groupSize,
|
|
groupElementsSize: groupElementsSize
|
|
});
|
|
maxGroupElementsSize = math.max(maxGroupElementsSize, groupElementsSize);
|
|
groupsSize += groupSpacing + groupSize;
|
|
groupSize = 0;
|
|
groupElementsSize = 0;
|
|
groupElements = [];
|
|
}
|
|
groupSize = math.max(groupSize, elementSize[groupSizeField]);
|
|
if (groupElementsSize > 0) {
|
|
groupElementsSize += elementSpacing;
|
|
}
|
|
groupElementsSize += elementSize[elementSizeField];
|
|
groupElements.push(element);
|
|
}
|
|
groups.push({
|
|
groupElements: groupElements,
|
|
groupSize: groupSize,
|
|
groupElementsSize: groupElementsSize
|
|
});
|
|
maxGroupElementsSize = math.max(maxGroupElementsSize, groupElementsSize);
|
|
groupsSize += groupSize;
|
|
return {
|
|
groups: groups,
|
|
groupsSize: groupsSize,
|
|
maxGroupElementsSize: maxGroupElementsSize
|
|
};
|
|
},
|
|
elementSize: function (element) {
|
|
return {
|
|
width: element.box.width(),
|
|
height: element.box.height()
|
|
};
|
|
},
|
|
createVisual: noop
|
|
});
|
|
var TextBox = BoxElement.extend({
|
|
ROWS_SPLIT_REGEX: /\n|\\n/m,
|
|
init: function (content, options) {
|
|
var textbox = this;
|
|
textbox.content = content;
|
|
BoxElement.fn.init.call(textbox, options);
|
|
textbox._initContainer();
|
|
textbox.reflow(Box2D());
|
|
},
|
|
_initContainer: function () {
|
|
var textbox = this;
|
|
var options = textbox.options;
|
|
var rows = (textbox.content + '').split(textbox.ROWS_SPLIT_REGEX);
|
|
var floatElement = new FloatElement({
|
|
vertical: true,
|
|
align: options.align,
|
|
wrap: false
|
|
});
|
|
var textOptions = deepExtend({}, options, {
|
|
opacity: 1,
|
|
animation: null
|
|
});
|
|
var text;
|
|
var rowIdx;
|
|
textbox.container = floatElement;
|
|
textbox.append(floatElement);
|
|
for (rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
text = new Text(trim(rows[rowIdx]), textOptions);
|
|
floatElement.append(text);
|
|
}
|
|
},
|
|
reflow: function (targetBox) {
|
|
var options = this.options;
|
|
var visualFn = options.visual;
|
|
this.container.options.align = options.align;
|
|
if (visualFn && !this._boxReflow) {
|
|
if (!targetBox.hasSize()) {
|
|
this._boxReflow = true;
|
|
this.reflow(targetBox);
|
|
this._boxReflow = false;
|
|
targetBox = this.box;
|
|
}
|
|
this.visual = visualFn(this.visualContext(targetBox));
|
|
var visualBox = targetBox;
|
|
if (this.visual) {
|
|
visualBox = rectToBox(this.visual.clippedBBox() || new geom.Rect());
|
|
this.visual.options.zIndex = options.zIndex;
|
|
this.visual.options.noclip = options.noclip;
|
|
}
|
|
this.box = this.contentBox = this.paddingBox = visualBox;
|
|
} else {
|
|
BoxElement.fn.reflow.call(this, targetBox);
|
|
if (options.rotation) {
|
|
var margin = getSpacing(options.margin);
|
|
var box = this.box.unpad(margin);
|
|
this.targetBox = targetBox;
|
|
this.normalBox = box.clone();
|
|
box = this.rotate();
|
|
box.translate(margin.left - margin.right, margin.top - margin.bottom);
|
|
this.rotatedBox = box.clone();
|
|
box.pad(margin);
|
|
}
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
var options = this.options;
|
|
if (!options.visible) {
|
|
return;
|
|
}
|
|
this.visual = new dataviz.drawing.Group({
|
|
transform: this.rotationTransform(),
|
|
zIndex: options.zIndex,
|
|
noclip: options.noclip
|
|
});
|
|
if (this.hasBox()) {
|
|
var box = draw.Path.fromRect(this.paddingBox.toRect(), this.visualStyle());
|
|
this.visual.append(box);
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
if (this.options.visual) {
|
|
this.addVisual();
|
|
this.createAnimation();
|
|
} else {
|
|
BoxElement.fn.renderVisual.call(this);
|
|
}
|
|
},
|
|
visualOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
background: options.background,
|
|
border: options.border,
|
|
color: options.color,
|
|
font: options.font,
|
|
margin: options.margin,
|
|
padding: options.padding,
|
|
visible: options.visible
|
|
};
|
|
},
|
|
visualContext: function (targetBox) {
|
|
var textbox = this;
|
|
return {
|
|
text: textbox.content,
|
|
rect: targetBox.toRect(),
|
|
sender: this.getChart(),
|
|
options: textbox.visualOptions(),
|
|
createVisual: function () {
|
|
textbox._boxReflow = true;
|
|
textbox.reflow(targetBox);
|
|
textbox._boxReflow = false;
|
|
return textbox.getDefaultVisual();
|
|
}
|
|
};
|
|
},
|
|
getDefaultVisual: function () {
|
|
this.createVisual();
|
|
this.renderChildren();
|
|
var visual = this.visual;
|
|
delete this.visual;
|
|
return visual;
|
|
},
|
|
rotate: function () {
|
|
var options = this.options;
|
|
this.box.rotate(options.rotation);
|
|
this.align(this.targetBox, X, options.align);
|
|
this.align(this.targetBox, Y, options.vAlign);
|
|
return this.box;
|
|
},
|
|
rotationTransform: function () {
|
|
var rotation = this.options.rotation;
|
|
if (!rotation) {
|
|
return null;
|
|
}
|
|
var center = this.normalBox.center();
|
|
var cx = center.x;
|
|
var cy = center.y;
|
|
var boxCenter = this.rotatedBox.center();
|
|
return geom.transform().translate(boxCenter.x - cx, boxCenter.y - cy).rotate(rotation, [
|
|
cx,
|
|
cy
|
|
]);
|
|
}
|
|
});
|
|
var Title = ChartElement.extend({
|
|
init: function (options) {
|
|
var title = this;
|
|
ChartElement.fn.init.call(title, options);
|
|
options = title.options;
|
|
title.append(new TextBox(options.text, deepExtend({}, options, { vAlign: options.position })));
|
|
},
|
|
options: {
|
|
color: BLACK,
|
|
position: TOP,
|
|
align: CENTER,
|
|
margin: getSpacing(5),
|
|
padding: getSpacing(5)
|
|
},
|
|
reflow: function (targetBox) {
|
|
var title = this;
|
|
ChartElement.fn.reflow.call(title, targetBox);
|
|
title.box.snapTo(targetBox, X);
|
|
}
|
|
});
|
|
Title.buildTitle = function (options, parent, defaultOptions) {
|
|
var title;
|
|
if (typeof options === 'string') {
|
|
options = { text: options };
|
|
}
|
|
options = deepExtend({ visible: true }, defaultOptions, options);
|
|
if (options && options.visible && options.text) {
|
|
title = new Title(options);
|
|
parent.append(title);
|
|
}
|
|
return title;
|
|
};
|
|
var AxisLabel = TextBox.extend({
|
|
init: function (value, text, index, dataItem, options) {
|
|
var label = this;
|
|
label.text = text;
|
|
label.value = value;
|
|
label.index = index;
|
|
label.dataItem = dataItem;
|
|
TextBox.fn.init.call(label, text, options);
|
|
},
|
|
visualContext: function (targetBox) {
|
|
var context = TextBox.fn.visualContext.call(this, targetBox);
|
|
context.value = this.value;
|
|
context.dataItem = this.dataItem;
|
|
context.format = this.options.format;
|
|
context.culture = this.options.culture;
|
|
return context;
|
|
},
|
|
click: function (widget, e) {
|
|
var label = this;
|
|
widget.trigger(AXIS_LABEL_CLICK, {
|
|
element: $(e.target),
|
|
value: label.value,
|
|
text: label.text,
|
|
index: label.index,
|
|
dataItem: label.dataItem,
|
|
axis: label.parent.options
|
|
});
|
|
},
|
|
rotate: function () {
|
|
if (this.options.alignRotation != CENTER) {
|
|
var box = this.normalBox.toRect();
|
|
var transform = this.rotationTransform();
|
|
this.box = rectToBox(box.bbox(transform.matrix()));
|
|
} else {
|
|
TextBox.fn.rotate.call(this);
|
|
}
|
|
return this.box;
|
|
},
|
|
rotationTransform: function () {
|
|
var options = this.options;
|
|
var rotation = options.rotation;
|
|
if (!rotation) {
|
|
return null;
|
|
}
|
|
if (options.alignRotation == CENTER) {
|
|
return TextBox.fn.rotationTransform.call(this);
|
|
}
|
|
var rotationMatrix = geom.transform().rotate(rotation).matrix();
|
|
var box = this.normalBox.toRect();
|
|
var rect = this.targetBox.toRect();
|
|
var rotationOrigin = options.rotationOrigin || TOP;
|
|
var alignAxis = rotationOrigin == TOP || rotationOrigin == BOTTOM ? X : Y;
|
|
var distanceAxis = rotationOrigin == TOP || rotationOrigin == BOTTOM ? Y : X;
|
|
var axisAnchor = rotationOrigin == TOP || rotationOrigin == LEFT ? rect.origin : rect.bottomRight();
|
|
var topLeft = box.topLeft().transformCopy(rotationMatrix);
|
|
var topRight = box.topRight().transformCopy(rotationMatrix);
|
|
var bottomRight = box.bottomRight().transformCopy(rotationMatrix);
|
|
var bottomLeft = box.bottomLeft().transformCopy(rotationMatrix);
|
|
var rotatedBox = geom.Rect.fromPoints(topLeft, topRight, bottomRight, bottomLeft);
|
|
var translate = {};
|
|
translate[distanceAxis] = rect.origin[distanceAxis] - rotatedBox.origin[distanceAxis];
|
|
var distanceLeft = math.abs(topLeft[distanceAxis] + translate[distanceAxis] - axisAnchor[distanceAxis]);
|
|
var distanceRight = math.abs(topRight[distanceAxis] + translate[distanceAxis] - axisAnchor[distanceAxis]);
|
|
var alignStart;
|
|
var alignEnd;
|
|
if (round(distanceLeft, DEFAULT_PRECISION) === round(distanceRight, DEFAULT_PRECISION)) {
|
|
alignStart = topLeft;
|
|
alignEnd = topRight;
|
|
} else if (distanceRight < distanceLeft) {
|
|
alignStart = topRight;
|
|
alignEnd = bottomRight;
|
|
} else {
|
|
alignStart = topLeft;
|
|
alignEnd = bottomLeft;
|
|
}
|
|
var alignCenter = alignStart[alignAxis] + (alignEnd[alignAxis] - alignStart[alignAxis]) / 2;
|
|
translate[alignAxis] = rect.center()[alignAxis] - alignCenter;
|
|
return geom.transform().translate(translate.x, translate.y).rotate(rotation);
|
|
}
|
|
});
|
|
function createAxisTick(options, tickOptions) {
|
|
var tickX = options.tickX, tickY = options.tickY, position = options.position;
|
|
var tick = new draw.Path({
|
|
stroke: {
|
|
width: tickOptions.width,
|
|
color: tickOptions.color
|
|
}
|
|
});
|
|
if (options.vertical) {
|
|
tick.moveTo(tickX, position).lineTo(tickX + tickOptions.size, position);
|
|
} else {
|
|
tick.moveTo(position, tickY).lineTo(position, tickY + tickOptions.size);
|
|
}
|
|
alignPathToPixel(tick);
|
|
return tick;
|
|
}
|
|
function createAxisGridLine(options, gridLine) {
|
|
var lineStart = options.lineStart, lineEnd = options.lineEnd, position = options.position;
|
|
var line = new draw.Path({
|
|
stroke: {
|
|
width: gridLine.width,
|
|
color: gridLine.color,
|
|
dashType: gridLine.dashType
|
|
}
|
|
});
|
|
if (options.vertical) {
|
|
line.moveTo(lineStart, position).lineTo(lineEnd, position);
|
|
} else {
|
|
line.moveTo(position, lineStart).lineTo(position, lineEnd);
|
|
}
|
|
alignPathToPixel(line);
|
|
return line;
|
|
}
|
|
var Axis = ChartElement.extend({
|
|
init: function (options) {
|
|
var axis = this;
|
|
ChartElement.fn.init.call(axis, options);
|
|
if (!axis.options.visible) {
|
|
axis.options = deepExtend({}, axis.options, {
|
|
labels: { visible: false },
|
|
line: { visible: false },
|
|
margin: 0,
|
|
majorTickSize: 0,
|
|
minorTickSize: 0
|
|
});
|
|
}
|
|
axis.options.minorTicks = deepExtend({}, {
|
|
color: axis.options.line.color,
|
|
width: axis.options.line.width,
|
|
visible: axis.options.minorTickType != NONE
|
|
}, axis.options.minorTicks, {
|
|
size: axis.options.minorTickSize,
|
|
align: axis.options.minorTickType
|
|
});
|
|
axis.options.majorTicks = deepExtend({}, {
|
|
color: axis.options.line.color,
|
|
width: axis.options.line.width,
|
|
visible: axis.options.majorTickType != NONE
|
|
}, axis.options.majorTicks, {
|
|
size: axis.options.majorTickSize,
|
|
align: axis.options.majorTickType
|
|
});
|
|
if (!this.options._deferLabels) {
|
|
axis.createLabels();
|
|
}
|
|
axis.createTitle();
|
|
axis.createNotes();
|
|
},
|
|
options: {
|
|
labels: {
|
|
visible: true,
|
|
rotation: 0,
|
|
mirror: false,
|
|
step: 1,
|
|
skip: 0
|
|
},
|
|
line: {
|
|
width: 1,
|
|
color: BLACK,
|
|
visible: true
|
|
},
|
|
title: {
|
|
visible: true,
|
|
position: CENTER
|
|
},
|
|
majorTicks: {
|
|
align: OUTSIDE,
|
|
size: 4,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
minorTicks: {
|
|
align: OUTSIDE,
|
|
size: 3,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
axisCrossingValue: 0,
|
|
majorTickType: OUTSIDE,
|
|
minorTickType: NONE,
|
|
majorGridLines: {
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
minorGridLines: {
|
|
visible: false,
|
|
width: 1,
|
|
color: BLACK,
|
|
skip: 0,
|
|
step: 1
|
|
},
|
|
margin: 5,
|
|
visible: true,
|
|
reverse: false,
|
|
justified: true,
|
|
notes: { label: { text: '' } },
|
|
_alignLines: true,
|
|
_deferLabels: false
|
|
},
|
|
labelsRange: function () {
|
|
return {
|
|
min: this.options.labels.skip,
|
|
max: this.labelsCount()
|
|
};
|
|
},
|
|
createLabels: function () {
|
|
var axis = this, options = axis.options, align = options.vertical ? RIGHT : CENTER, labelOptions = deepExtend({}, options.labels, {
|
|
align: align,
|
|
zIndex: options.zIndex
|
|
}), step = math.max(1, labelOptions.step);
|
|
axis.children = $.grep(axis.children, function (child) {
|
|
return !(child instanceof AxisLabel);
|
|
});
|
|
axis.labels = [];
|
|
if (labelOptions.visible) {
|
|
var range = axis.labelsRange(), rotation = labelOptions.rotation, label, i;
|
|
if (isPlainObject(rotation)) {
|
|
labelOptions.alignRotation = rotation.align;
|
|
labelOptions.rotation = rotation.angle;
|
|
}
|
|
if (labelOptions.rotation == 'auto') {
|
|
labelOptions.rotation = 0;
|
|
options.autoRotateLabels = true;
|
|
}
|
|
for (i = range.min; i < range.max; i += step) {
|
|
label = axis.createAxisLabel(i, labelOptions);
|
|
if (label) {
|
|
axis.append(label);
|
|
axis.labels.push(label);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
lineBox: function () {
|
|
var axis = this, options = axis.options, box = axis.box, vertical = options.vertical, mirror = options.labels.mirror, axisX = mirror ? box.x1 : box.x2, axisY = mirror ? box.y2 : box.y1, lineWidth = options.line.width || 0;
|
|
return vertical ? Box2D(axisX, box.y1, axisX, box.y2 - lineWidth) : Box2D(box.x1, axisY, box.x2 - lineWidth, axisY);
|
|
},
|
|
createTitle: function () {
|
|
var axis = this, options = axis.options, titleOptions = deepExtend({
|
|
rotation: options.vertical ? -90 : 0,
|
|
text: '',
|
|
zIndex: 1,
|
|
visualSize: true
|
|
}, options.title), title;
|
|
if (titleOptions.visible && titleOptions.text) {
|
|
title = new TextBox(titleOptions.text, titleOptions);
|
|
axis.append(title);
|
|
axis.title = title;
|
|
}
|
|
},
|
|
createNotes: function () {
|
|
var axis = this, options = axis.options, notes = options.notes, items = notes.data || [], i, item, note;
|
|
axis.notes = [];
|
|
for (i = 0; i < items.length; i++) {
|
|
item = deepExtend({}, notes, items[i]);
|
|
item.value = axis.parseNoteValue(item.value);
|
|
note = new Note(item.value, item.label.text, null, null, null, item);
|
|
if (note.options.visible) {
|
|
if (defined(note.options.position)) {
|
|
if (options.vertical && !inArray(note.options.position, [
|
|
LEFT,
|
|
RIGHT
|
|
])) {
|
|
note.options.position = options.reverse ? LEFT : RIGHT;
|
|
} else if (!options.vertical && !inArray(note.options.position, [
|
|
TOP,
|
|
BOTTOM
|
|
])) {
|
|
note.options.position = options.reverse ? BOTTOM : TOP;
|
|
}
|
|
} else {
|
|
if (options.vertical) {
|
|
note.options.position = options.reverse ? LEFT : RIGHT;
|
|
} else {
|
|
note.options.position = options.reverse ? BOTTOM : TOP;
|
|
}
|
|
}
|
|
axis.append(note);
|
|
axis.notes.push(note);
|
|
}
|
|
}
|
|
},
|
|
parseNoteValue: function (value) {
|
|
return value;
|
|
},
|
|
renderVisual: function () {
|
|
ChartElement.fn.renderVisual.call(this);
|
|
this.createPlotBands();
|
|
},
|
|
createVisual: function () {
|
|
ChartElement.fn.createVisual.call(this);
|
|
this.createBackground();
|
|
this.createLine();
|
|
},
|
|
gridLinesVisual: function () {
|
|
var gridLines = this._gridLines;
|
|
if (!gridLines) {
|
|
gridLines = this._gridLines = new draw.Group({ zIndex: -2 });
|
|
this.appendVisual(this._gridLines);
|
|
}
|
|
return gridLines;
|
|
},
|
|
createTicks: function (lineGroup) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), mirror = options.labels.mirror, majorUnit = options.majorTicks.visible ? options.majorUnit : 0, tickLineOptions = { vertical: options.vertical };
|
|
function render(tickPositions, tickOptions, skipUnit) {
|
|
var i, count = tickPositions.length;
|
|
if (tickOptions.visible) {
|
|
for (i = tickOptions.skip; i < count; i += tickOptions.step) {
|
|
if (defined(skipUnit) && i % skipUnit === 0) {
|
|
continue;
|
|
}
|
|
tickLineOptions.tickX = mirror ? lineBox.x2 : lineBox.x2 - tickOptions.size;
|
|
tickLineOptions.tickY = mirror ? lineBox.y1 - tickOptions.size : lineBox.y1;
|
|
tickLineOptions.position = tickPositions[i];
|
|
lineGroup.append(createAxisTick(tickLineOptions, tickOptions));
|
|
}
|
|
}
|
|
}
|
|
render(axis.getMajorTickPositions(), options.majorTicks);
|
|
render(axis.getMinorTickPositions(), options.minorTicks, majorUnit / options.minorUnit);
|
|
},
|
|
createLine: function () {
|
|
var axis = this, options = axis.options, line = options.line, lineBox = axis.lineBox();
|
|
if (line.width > 0 && line.visible) {
|
|
var path = new draw.Path({
|
|
stroke: {
|
|
width: line.width,
|
|
color: line.color,
|
|
dashType: line.dashType
|
|
}
|
|
});
|
|
path.moveTo(lineBox.x1, lineBox.y1).lineTo(lineBox.x2, lineBox.y2);
|
|
if (options._alignLines) {
|
|
alignPathToPixel(path);
|
|
}
|
|
var group = this._lineGroup = new draw.Group();
|
|
group.append(path);
|
|
this.visual.append(group);
|
|
this.createTicks(group);
|
|
}
|
|
},
|
|
getActualTickSize: function () {
|
|
var axis = this, options = axis.options, tickSize = 0;
|
|
if (options.majorTicks.visible && options.minorTicks.visible) {
|
|
tickSize = math.max(options.majorTicks.size, options.minorTicks.size);
|
|
} else if (options.majorTicks.visible) {
|
|
tickSize = options.majorTicks.size;
|
|
} else if (options.minorTicks.visible) {
|
|
tickSize = options.minorTicks.size;
|
|
}
|
|
return tickSize;
|
|
},
|
|
createBackground: function () {
|
|
var axis = this, options = axis.options, background = options.background, box = axis.box;
|
|
if (background) {
|
|
axis._backgroundPath = draw.Path.fromRect(box.toRect(), {
|
|
fill: { color: background },
|
|
stroke: null
|
|
});
|
|
this.visual.append(axis._backgroundPath);
|
|
}
|
|
},
|
|
createPlotBands: function () {
|
|
var axis = this, options = axis.options, plotBands = options.plotBands || [], vertical = options.vertical, plotArea = axis.plotArea, slotX, slotY, from, to;
|
|
if (plotBands.length === 0) {
|
|
return;
|
|
}
|
|
var group = this._plotbandGroup = new draw.Group({ zIndex: -1 });
|
|
var altAxis = $.grep(axis.pane.axes, function (a) {
|
|
return a.options.vertical !== axis.options.vertical;
|
|
})[0];
|
|
$.each(plotBands, function (i, item) {
|
|
from = valueOrDefault(item.from, MIN_VALUE);
|
|
to = valueOrDefault(item.to, MAX_VALUE);
|
|
if (vertical) {
|
|
slotX = (altAxis || plotArea.axisX).lineBox();
|
|
slotY = axis.getSlot(item.from, item.to, true);
|
|
} else {
|
|
slotX = axis.getSlot(item.from, item.to, true);
|
|
slotY = (altAxis || plotArea.axisY).lineBox();
|
|
}
|
|
if (slotX.width() !== 0 && slotY.height() !== 0) {
|
|
var bandRect = new geom.Rect([
|
|
slotX.x1,
|
|
slotY.y1
|
|
], [
|
|
slotX.width(),
|
|
slotY.height()
|
|
]);
|
|
var path = draw.Path.fromRect(bandRect, {
|
|
fill: {
|
|
color: item.color,
|
|
opacity: item.opacity
|
|
},
|
|
stroke: null
|
|
});
|
|
group.append(path);
|
|
}
|
|
});
|
|
axis.appendVisual(group);
|
|
},
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, axisLineVisible = altAxis.options.line.visible, majorGridLines = options.majorGridLines, majorUnit = majorGridLines.visible ? options.majorUnit : 0, vertical = options.vertical, lineBox = altAxis.lineBox(), linePos = lineBox[vertical ? 'y1' : 'x1'], lineOptions = {
|
|
lineStart: lineBox[vertical ? 'x1' : 'y1'],
|
|
lineEnd: lineBox[vertical ? 'x2' : 'y2'],
|
|
vertical: vertical
|
|
}, pos, majorTicks = [];
|
|
var container = this.gridLinesVisual();
|
|
function render(tickPositions, gridLine, skipUnit) {
|
|
var count = tickPositions.length, i;
|
|
if (gridLine.visible) {
|
|
for (i = gridLine.skip; i < count; i += gridLine.step) {
|
|
pos = round(tickPositions[i]);
|
|
if (!inArray(pos, majorTicks)) {
|
|
if (i % skipUnit !== 0 && (!axisLineVisible || linePos !== pos)) {
|
|
lineOptions.position = pos;
|
|
container.append(createAxisGridLine(lineOptions, gridLine));
|
|
majorTicks.push(pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
render(axis.getMajorTickPositions(), options.majorGridLines);
|
|
render(axis.getMinorTickPositions(), options.minorGridLines, majorUnit / options.minorUnit);
|
|
return container.children;
|
|
},
|
|
reflow: function (box) {
|
|
var axis = this, options = axis.options, vertical = options.vertical, labels = axis.labels, count = labels.length, title = axis.title, sizeFn = vertical ? WIDTH : HEIGHT, titleSize = title ? title.box[sizeFn]() : 0, space = axis.getActualTickSize() + options.margin + titleSize, maxLabelSize = 0, rootBox = (this.getRoot() || {}).box || box, boxSize = rootBox[sizeFn](), labelSize, i;
|
|
for (i = 0; i < count; i++) {
|
|
labelSize = labels[i].box[sizeFn]();
|
|
if (labelSize + space <= boxSize) {
|
|
maxLabelSize = math.max(maxLabelSize, labelSize);
|
|
}
|
|
}
|
|
if (vertical) {
|
|
axis.box = Box2D(box.x1, box.y1, box.x1 + maxLabelSize + space, box.y2);
|
|
} else {
|
|
axis.box = Box2D(box.x1, box.y1, box.x2, box.y1 + maxLabelSize + space);
|
|
}
|
|
axis.arrangeTitle();
|
|
axis.arrangeLabels();
|
|
axis.arrangeNotes();
|
|
},
|
|
getLabelsTickPositions: function () {
|
|
return this.getMajorTickPositions();
|
|
},
|
|
labelTickIndex: function (label) {
|
|
return label.index;
|
|
},
|
|
arrangeLabels: function () {
|
|
var axis = this, options = axis.options, labels = axis.labels, labelsBetweenTicks = !options.justified, vertical = options.vertical, lineBox = axis.lineBox(), mirror = options.labels.mirror, tickPositions = axis.getLabelsTickPositions(), labelOffset = axis.getActualTickSize() + options.margin, labelBox, labelY, i;
|
|
for (i = 0; i < labels.length; i++) {
|
|
var label = labels[i], tickIx = axis.labelTickIndex(label), labelSize = vertical ? label.box.height() : label.box.width(), labelPos = tickPositions[tickIx] - labelSize / 2, firstTickPosition, nextTickPosition, middle, labelX;
|
|
if (vertical) {
|
|
if (labelsBetweenTicks) {
|
|
firstTickPosition = tickPositions[tickIx];
|
|
nextTickPosition = tickPositions[tickIx + 1];
|
|
middle = firstTickPosition + (nextTickPosition - firstTickPosition) / 2;
|
|
labelPos = middle - labelSize / 2;
|
|
}
|
|
labelX = lineBox.x2;
|
|
if (mirror) {
|
|
labelX += labelOffset;
|
|
label.options.rotationOrigin = LEFT;
|
|
} else {
|
|
labelX -= labelOffset + label.box.width();
|
|
label.options.rotationOrigin = RIGHT;
|
|
}
|
|
labelBox = label.box.move(labelX, labelPos);
|
|
} else {
|
|
if (labelsBetweenTicks) {
|
|
firstTickPosition = tickPositions[tickIx];
|
|
nextTickPosition = tickPositions[tickIx + 1];
|
|
} else {
|
|
firstTickPosition = labelPos;
|
|
nextTickPosition = labelPos + labelSize;
|
|
}
|
|
labelY = lineBox.y1;
|
|
if (mirror) {
|
|
labelY -= labelOffset + label.box.height();
|
|
label.options.rotationOrigin = BOTTOM;
|
|
} else {
|
|
labelY += labelOffset;
|
|
label.options.rotationOrigin = TOP;
|
|
}
|
|
labelBox = Box2D(firstTickPosition, labelY, nextTickPosition, labelY + label.box.height());
|
|
}
|
|
label.reflow(labelBox);
|
|
}
|
|
},
|
|
autoRotateLabels: function () {
|
|
if (this.options.autoRotateLabels && !this.options.vertical) {
|
|
var tickPositions = this.getMajorTickPositions();
|
|
var labels = this.labels;
|
|
var labelBox, angle, width, idx;
|
|
for (idx = 0; idx < labels.length; idx++) {
|
|
width = tickPositions[idx + 1] - tickPositions[idx];
|
|
labelBox = labels[idx].box;
|
|
if (labelBox.width() > width) {
|
|
if (labelBox.height() > width) {
|
|
angle = -90;
|
|
break;
|
|
}
|
|
angle = -45;
|
|
}
|
|
}
|
|
if (angle) {
|
|
for (idx = 0; idx < labels.length; idx++) {
|
|
labels[idx].options.rotation = angle;
|
|
labels[idx].reflow(Box2D());
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
},
|
|
arrangeTitle: function () {
|
|
var axis = this, options = axis.options, mirror = options.labels.mirror, vertical = options.vertical, title = axis.title;
|
|
if (title) {
|
|
if (vertical) {
|
|
title.options.align = mirror ? RIGHT : LEFT;
|
|
title.options.vAlign = title.options.position;
|
|
} else {
|
|
title.options.align = title.options.position;
|
|
title.options.vAlign = mirror ? TOP : BOTTOM;
|
|
}
|
|
title.reflow(axis.box);
|
|
}
|
|
},
|
|
arrangeNotes: function () {
|
|
var axis = this, i, item, slot, value;
|
|
for (i = 0; i < axis.notes.length; i++) {
|
|
item = axis.notes[i];
|
|
value = item.options.value;
|
|
if (defined(value)) {
|
|
if (axis.shouldRenderNote(value)) {
|
|
item.show();
|
|
} else {
|
|
item.hide();
|
|
}
|
|
slot = axis.getSlot(value);
|
|
} else {
|
|
item.hide();
|
|
}
|
|
item.reflow(slot || axis.lineBox());
|
|
}
|
|
},
|
|
alignTo: function (secondAxis) {
|
|
var axis = this, lineBox = secondAxis.lineBox(), vertical = axis.options.vertical, pos = vertical ? Y : X;
|
|
axis.box.snapTo(lineBox, pos);
|
|
if (vertical) {
|
|
axis.box.shrink(0, axis.lineBox().height() - lineBox.height());
|
|
} else {
|
|
axis.box.shrink(axis.lineBox().width() - lineBox.width(), 0);
|
|
}
|
|
axis.box[pos + 1] -= axis.lineBox()[pos + 1] - lineBox[pos + 1];
|
|
axis.box[pos + 2] -= axis.lineBox()[pos + 2] - lineBox[pos + 2];
|
|
},
|
|
axisLabelText: function (value, dataItem, options) {
|
|
var text = value;
|
|
if (options.template) {
|
|
var tmpl = template(options.template);
|
|
text = tmpl({
|
|
value: value,
|
|
dataItem: dataItem,
|
|
format: options.format,
|
|
culture: options.culture
|
|
});
|
|
} else if (options.format) {
|
|
if (options.format.match(FORMAT_REGEX)) {
|
|
text = kendo.format(options.format, value);
|
|
} else {
|
|
text = kendo.toString(value, options.format, options.culture);
|
|
}
|
|
}
|
|
return text;
|
|
},
|
|
slot: function (from, to) {
|
|
var slot = this.getSlot(from, to);
|
|
if (slot) {
|
|
return slot.toRect();
|
|
}
|
|
},
|
|
contentBox: function () {
|
|
var box = this.box.clone();
|
|
var labels = this.labels;
|
|
if (labels.length) {
|
|
if (labels[0].options.visible) {
|
|
box.wrap(labels[0].box);
|
|
}
|
|
if (last(labels).options.visible) {
|
|
box.wrap(last(labels).box);
|
|
}
|
|
}
|
|
return box;
|
|
},
|
|
limitRange: function (from, to, min, max, offset) {
|
|
var options = this.options;
|
|
if (from < min && offset < 0 && (!defined(options.min) || options.min <= min) || max < to && offset > 0 && (!defined(options.max) || max <= options.max)) {
|
|
return;
|
|
}
|
|
if (to < min && offset > 0 || max < from && offset < 0) {
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
}
|
|
var rangeSize = to - from;
|
|
if (from < min) {
|
|
from = util.limitValue(from, min, max);
|
|
to = util.limitValue(from + rangeSize, min + rangeSize, max);
|
|
} else if (to > max) {
|
|
to = util.limitValue(to, min, max);
|
|
from = util.limitValue(to - rangeSize, min, max - rangeSize);
|
|
}
|
|
return {
|
|
min: from,
|
|
max: to
|
|
};
|
|
}
|
|
});
|
|
var Note = BoxElement.extend({
|
|
init: function (value, text, dataItem, category, series, options) {
|
|
var note = this;
|
|
BoxElement.fn.init.call(note, options);
|
|
note.value = value;
|
|
note.text = text;
|
|
note.dataItem = dataItem;
|
|
note.category = category;
|
|
note.series = series;
|
|
note.render();
|
|
},
|
|
options: {
|
|
icon: {
|
|
visible: true,
|
|
type: CIRCLE
|
|
},
|
|
label: {
|
|
position: INSIDE,
|
|
visible: true,
|
|
align: CENTER,
|
|
vAlign: CENTER
|
|
},
|
|
line: { visible: true },
|
|
visible: true,
|
|
position: TOP,
|
|
zIndex: 2
|
|
},
|
|
hide: function () {
|
|
this.options.visible = false;
|
|
},
|
|
show: function () {
|
|
this.options.visible = true;
|
|
},
|
|
render: function () {
|
|
var note = this, options = note.options, label = options.label, text = note.text, icon = options.icon, size = icon.size, box = Box2D(), marker, width, height, noteTemplate;
|
|
if (options.visible) {
|
|
if (defined(label) && label.visible) {
|
|
if (label.template) {
|
|
noteTemplate = template(label.template);
|
|
text = noteTemplate({
|
|
dataItem: note.dataItem,
|
|
category: note.category,
|
|
value: note.value,
|
|
text: text,
|
|
series: note.series
|
|
});
|
|
} else if (label.format) {
|
|
text = autoFormat(label.format, text);
|
|
}
|
|
note.label = new TextBox(text, deepExtend({}, label));
|
|
if (label.position === INSIDE && !defined(size)) {
|
|
if (icon.type === CIRCLE) {
|
|
size = math.max(note.label.box.width(), note.label.box.height());
|
|
} else {
|
|
width = note.label.box.width();
|
|
height = note.label.box.height();
|
|
}
|
|
box.wrap(note.label.box);
|
|
}
|
|
}
|
|
icon.width = width || size || DEFAULT_ICON_SIZE;
|
|
icon.height = height || size || DEFAULT_ICON_SIZE;
|
|
marker = new ShapeElement(deepExtend({}, icon));
|
|
note.marker = marker;
|
|
note.append(marker);
|
|
if (note.label) {
|
|
note.append(note.label);
|
|
}
|
|
marker.reflow(Box2D());
|
|
note.wrapperBox = box.wrap(marker.box);
|
|
}
|
|
},
|
|
reflow: function (targetBox) {
|
|
var note = this, options = note.options, center = targetBox.center(), wrapperBox = note.wrapperBox, length = options.line.length, position = options.position, label = note.label, marker = note.marker, lineStart, box, contentBox;
|
|
if (options.visible) {
|
|
if (inArray(position, [
|
|
LEFT,
|
|
RIGHT
|
|
])) {
|
|
if (position === LEFT) {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(-length, targetBox.center().y - wrapperBox.center().y);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
targetBox.x1,
|
|
center.y
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
contentBox.x2,
|
|
center.y
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
} else {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(length, targetBox.center().y - wrapperBox.center().y);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
targetBox.x2,
|
|
center.y
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
contentBox.x1,
|
|
center.y
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
}
|
|
} else {
|
|
if (position === BOTTOM) {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(targetBox.center().x - wrapperBox.center().x, length);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
center.x,
|
|
targetBox.y2
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
center.x,
|
|
contentBox.y1
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
} else {
|
|
contentBox = wrapperBox.alignTo(targetBox, position).translate(targetBox.center().x - wrapperBox.center().x, -length);
|
|
if (options.line.visible) {
|
|
lineStart = [
|
|
center.x,
|
|
targetBox.y1
|
|
];
|
|
note.linePoints = [
|
|
lineStart,
|
|
[
|
|
center.x,
|
|
contentBox.y2
|
|
]
|
|
];
|
|
box = contentBox.clone().wrapPoint(lineStart);
|
|
}
|
|
}
|
|
}
|
|
if (marker) {
|
|
marker.reflow(contentBox);
|
|
}
|
|
if (label) {
|
|
label.reflow(contentBox);
|
|
if (marker) {
|
|
if (options.label.position === OUTSIDE) {
|
|
label.box.alignTo(marker.box, position);
|
|
}
|
|
label.reflow(label.box);
|
|
}
|
|
}
|
|
note.contentBox = contentBox;
|
|
note.targetBox = targetBox;
|
|
note.box = box || contentBox;
|
|
}
|
|
},
|
|
createVisual: function () {
|
|
BoxElement.fn.createVisual.call(this);
|
|
if (this.options.visible) {
|
|
this.createLine();
|
|
}
|
|
},
|
|
renderVisual: function () {
|
|
var that = this;
|
|
var options = that.options;
|
|
var customVisual = options.visual;
|
|
if (options.visible && customVisual) {
|
|
that.visual = customVisual({
|
|
dataItem: that.dataItem,
|
|
category: that.category,
|
|
value: that.value,
|
|
text: that.text,
|
|
sender: that.getChart(),
|
|
series: that.series,
|
|
rect: that.targetBox.toRect(),
|
|
options: {
|
|
background: options.background,
|
|
border: options.background,
|
|
icon: options.icon,
|
|
label: options.label,
|
|
line: options.line,
|
|
position: options.position,
|
|
visible: options.visible
|
|
},
|
|
createVisual: function () {
|
|
that.createVisual();
|
|
that.renderChildren();
|
|
var defaultVisual = that.visual;
|
|
delete that.visual;
|
|
return defaultVisual;
|
|
}
|
|
});
|
|
that.addVisual();
|
|
} else {
|
|
BoxElement.fn.renderVisual.call(that);
|
|
}
|
|
},
|
|
createLine: function () {
|
|
var options = this.options.line;
|
|
if (this.linePoints) {
|
|
var path = draw.Path.fromPoints(this.linePoints, {
|
|
stroke: {
|
|
color: options.color,
|
|
width: options.width,
|
|
dashType: options.dashType
|
|
}
|
|
});
|
|
alignPathToPixel(path);
|
|
this.visual.append(path);
|
|
}
|
|
},
|
|
click: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(NOTE_CLICK, args)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
hover: function (widget, e) {
|
|
var args = this.eventArgs(e);
|
|
if (!widget.trigger(NOTE_HOVER, args)) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
leave: function (widget) {
|
|
widget._unsetActivePoint();
|
|
},
|
|
eventArgs: function (e) {
|
|
var note = this, options = note.options;
|
|
return {
|
|
element: $(e.target),
|
|
text: defined(options.label) ? options.label.text : '',
|
|
dataItem: note.dataItem,
|
|
series: note.series,
|
|
value: note.value,
|
|
category: note.category,
|
|
visual: note.visual
|
|
};
|
|
}
|
|
});
|
|
var ShapeElement = BoxElement.extend({
|
|
init: function (options, pointData) {
|
|
this.pointData = pointData;
|
|
BoxElement.fn.init.call(this, options);
|
|
},
|
|
options: {
|
|
type: CIRCLE,
|
|
align: CENTER,
|
|
vAlign: CENTER
|
|
},
|
|
getElement: function () {
|
|
var marker = this, options = marker.options, type = options.type, rotation = options.rotation, box = marker.paddingBox, element, center = box.center(), halfWidth = box.width() / 2;
|
|
if (!options.visible || !marker.hasBox()) {
|
|
return;
|
|
}
|
|
var style = marker.visualStyle();
|
|
if (type === CIRCLE) {
|
|
element = new draw.Circle(new geom.Circle([
|
|
round(box.x1 + halfWidth, COORD_PRECISION),
|
|
round(box.y1 + box.height() / 2, COORD_PRECISION)
|
|
], halfWidth), style);
|
|
} else if (type === TRIANGLE) {
|
|
element = draw.Path.fromPoints([
|
|
[
|
|
box.x1 + halfWidth,
|
|
box.y1
|
|
],
|
|
[
|
|
box.x1,
|
|
box.y2
|
|
],
|
|
[
|
|
box.x2,
|
|
box.y2
|
|
]
|
|
], style).close();
|
|
} else if (type === CROSS) {
|
|
element = new draw.MultiPath(style);
|
|
element.moveTo(box.x1, box.y1).lineTo(box.x2, box.y2);
|
|
element.moveTo(box.x1, box.y2).lineTo(box.x2, box.y1);
|
|
} else {
|
|
element = draw.Path.fromRect(box.toRect(), style);
|
|
}
|
|
if (rotation) {
|
|
element.transform(geom.transform().rotate(-rotation, [
|
|
center.x,
|
|
center.y
|
|
]));
|
|
}
|
|
element.options.zIndex = this.options.zIndex;
|
|
return element;
|
|
},
|
|
createElement: function () {
|
|
var that = this;
|
|
var customVisual = that.options.visual;
|
|
var pointData = that.pointData || {};
|
|
var visual;
|
|
if (customVisual) {
|
|
visual = customVisual({
|
|
value: pointData.value,
|
|
dataItem: pointData.dataItem,
|
|
sender: that.getChart(),
|
|
series: pointData.series,
|
|
category: pointData.category,
|
|
rect: that.paddingBox.toRect(),
|
|
options: that.visualOptions(),
|
|
createVisual: function () {
|
|
return that.getElement();
|
|
}
|
|
});
|
|
} else {
|
|
visual = that.getElement();
|
|
}
|
|
return visual;
|
|
},
|
|
visualOptions: function () {
|
|
var options = this.options;
|
|
return {
|
|
background: options.background,
|
|
border: options.border,
|
|
margin: options.margin,
|
|
padding: options.padding,
|
|
type: options.type,
|
|
size: options.width,
|
|
visible: options.visible
|
|
};
|
|
},
|
|
createVisual: function () {
|
|
this.visual = this.createElement();
|
|
}
|
|
});
|
|
var NumericAxis = Axis.extend({
|
|
init: function (seriesMin, seriesMax, options) {
|
|
var axis = this, defaultOptions = axis.initDefaults(seriesMin, seriesMax, options);
|
|
Axis.fn.init.call(axis, defaultOptions);
|
|
},
|
|
startValue: function () {
|
|
return 0;
|
|
},
|
|
options: {
|
|
type: 'numeric',
|
|
min: 0,
|
|
max: 1,
|
|
vertical: true,
|
|
majorGridLines: {
|
|
visible: true,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
labels: { format: '#.####################' },
|
|
zIndex: 1
|
|
},
|
|
initDefaults: function (seriesMin, seriesMax, options) {
|
|
var axis = this, narrowRange = options.narrowRange, autoMin = axis.autoAxisMin(seriesMin, seriesMax, narrowRange), autoMax = axis.autoAxisMax(seriesMin, seriesMax, narrowRange), majorUnit = autoMajorUnit(autoMin, autoMax), autoOptions = { majorUnit: majorUnit }, userSetLimits;
|
|
if (options.roundToMajorUnit !== false) {
|
|
if (autoMin < 0 && remainderClose(autoMin, majorUnit, 1 / 3)) {
|
|
autoMin -= majorUnit;
|
|
}
|
|
if (autoMax > 0 && remainderClose(autoMax, majorUnit, 1 / 3)) {
|
|
autoMax += majorUnit;
|
|
}
|
|
}
|
|
autoOptions.min = floor(autoMin, majorUnit);
|
|
autoOptions.max = ceil(autoMax, majorUnit);
|
|
this.totalMin = defined(options.min) ? math.min(autoOptions.min, options.min) : autoOptions.min;
|
|
this.totalMax = defined(options.max) ? math.max(autoOptions.max, options.max) : autoOptions.max;
|
|
this.totalMajorUnit = majorUnit;
|
|
if (options) {
|
|
userSetLimits = defined(options.min) || defined(options.max);
|
|
if (userSetLimits) {
|
|
if (options.min === options.max) {
|
|
if (options.min > 0) {
|
|
options.min = 0;
|
|
} else {
|
|
options.max = 1;
|
|
}
|
|
}
|
|
}
|
|
if (options.majorUnit) {
|
|
autoOptions.min = floor(autoOptions.min, options.majorUnit);
|
|
autoOptions.max = ceil(autoOptions.max, options.majorUnit);
|
|
} else if (userSetLimits) {
|
|
options = deepExtend(autoOptions, options);
|
|
autoOptions.majorUnit = autoMajorUnit(options.min, options.max);
|
|
}
|
|
}
|
|
autoOptions.minorUnit = (options.majorUnit || autoOptions.majorUnit) / 5;
|
|
return deepExtend(autoOptions, options);
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: options.min,
|
|
max: options.max
|
|
};
|
|
},
|
|
autoAxisMax: function (min, max, narrow) {
|
|
var axisMax, diff;
|
|
if (!min && !max) {
|
|
return 1;
|
|
}
|
|
if (min <= 0 && max <= 0) {
|
|
max = min == max ? 0 : max;
|
|
diff = math.abs((max - min) / max);
|
|
if (narrow === false || !narrow && diff > ZERO_THRESHOLD) {
|
|
return 0;
|
|
}
|
|
axisMax = math.min(0, max - (min - max) / 2);
|
|
} else {
|
|
min = min == max ? 0 : min;
|
|
axisMax = max;
|
|
}
|
|
return axisMax;
|
|
},
|
|
autoAxisMin: function (min, max, narrow) {
|
|
var axisMin, diff;
|
|
if (!min && !max) {
|
|
return 0;
|
|
}
|
|
if (min >= 0 && max >= 0) {
|
|
min = min == max ? 0 : min;
|
|
diff = (max - min) / max;
|
|
if (narrow === false || !narrow && diff > ZERO_THRESHOLD) {
|
|
return 0;
|
|
}
|
|
axisMin = math.max(0, min - (max - min) / 2);
|
|
} else {
|
|
max = min == max ? 0 : max;
|
|
axisMin = min;
|
|
}
|
|
return axisMin;
|
|
},
|
|
getDivisions: function (stepValue) {
|
|
if (stepValue === 0) {
|
|
return 1;
|
|
}
|
|
var options = this.options, range = options.max - options.min;
|
|
return math.floor(round(range / stepValue, COORD_PRECISION)) + 1;
|
|
},
|
|
getTickPositions: function (unit, skipUnit) {
|
|
var axis = this, options = axis.options, vertical = options.vertical, reverse = options.reverse, lineBox = axis.lineBox(), lineSize = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = lineSize / range, step = unit * scale, skipStep = 0, divisions = axis.getDivisions(unit), dir = (vertical ? -1 : 1) * (reverse ? -1 : 1), startEdge = dir === 1 ? 1 : 2, pos = lineBox[(vertical ? Y : X) + startEdge], positions = [], i;
|
|
if (skipUnit) {
|
|
skipStep = skipUnit / unit;
|
|
}
|
|
for (i = 0; i < divisions; i++) {
|
|
if (i % skipStep !== 0) {
|
|
positions.push(round(pos, COORD_PRECISION));
|
|
}
|
|
pos = pos + step * dir;
|
|
}
|
|
return positions;
|
|
},
|
|
getMajorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.majorUnit);
|
|
},
|
|
getMinorTickPositions: function () {
|
|
var axis = this;
|
|
return axis.getTickPositions(axis.options.minorUnit);
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, step = dir * (lineSize / (options.max - options.min)), p1, p2, slotBox = new Box2D(lineBox.x1, lineBox.y1, lineBox.x1, lineBox.y1);
|
|
if (!defined(a)) {
|
|
a = b || 0;
|
|
}
|
|
if (!defined(b)) {
|
|
b = a || 0;
|
|
}
|
|
if (limit) {
|
|
a = math.max(math.min(a, options.max), options.min);
|
|
b = math.max(math.min(b, options.max), options.min);
|
|
}
|
|
if (vertical) {
|
|
p1 = options.max - math.max(a, b);
|
|
p2 = options.max - math.min(a, b);
|
|
} else {
|
|
p1 = math.min(a, b) - options.min;
|
|
p2 = math.max(a, b) - options.min;
|
|
}
|
|
slotBox[valueAxis + 1] = math.max(math.min(lineStart + step * (reverse ? p2 : p1), COORDINATE_LIMIT), -COORDINATE_LIMIT);
|
|
slotBox[valueAxis + 2] = math.max(math.min(lineStart + step * (reverse ? p1 : p2), COORDINATE_LIMIT), -COORDINATE_LIMIT);
|
|
return slotBox;
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, max = options.max * 1, min = options.min * 1, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, offset = dir * (point[valueAxis] - lineStart), step = (max - min) / lineSize, valueOffset = offset * step, value;
|
|
if (offset < 0 || offset > lineSize) {
|
|
return null;
|
|
}
|
|
value = vertical ? max - valueOffset : min + valueOffset;
|
|
return round(value, DEFAULT_PRECISION);
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, lineBox = axis.lineBox(), vertical = options.vertical, reverse = options.reverse, size = vertical ? lineBox.height() : lineBox.width(), range = options.max - options.min, scale = size / range, offset = round(delta / scale, DEFAULT_PRECISION);
|
|
if ((vertical || reverse) && !(vertical && reverse)) {
|
|
offset = -offset;
|
|
}
|
|
return {
|
|
min: options.min + offset,
|
|
max: options.max + offset
|
|
};
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, options = axis.options, offset = -delta * options.majorUnit;
|
|
return {
|
|
min: options.min - offset,
|
|
max: options.max + offset
|
|
};
|
|
},
|
|
labelsCount: function () {
|
|
return this.getDivisions(this.options.majorUnit);
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var axis = this, options = axis.options, value = round(options.min + index * options.majorUnit, DEFAULT_PRECISION), text = axis.axisLabelText(value, null, labelOptions);
|
|
return new AxisLabel(value, text, index, null, labelOptions);
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var range = this.range();
|
|
return range.min <= value && value <= range.max;
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.translateRange(delta);
|
|
return this.limitRange(range.min, range.max, this.totalMin, this.totalMax);
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var startValue = this.getValue(start);
|
|
var endValue = this.getValue(end);
|
|
var min = math.min(startValue, endValue);
|
|
var max = math.max(startValue, endValue);
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var newRange = this.scaleRange(delta);
|
|
var totalMax = this.totalMax;
|
|
var totalMin = this.totalMin;
|
|
var min = util.limitValue(newRange.min, totalMin, totalMax);
|
|
var max = util.limitValue(newRange.max, totalMin, totalMax);
|
|
var optionsRange = this.options.max - this.options.min;
|
|
if (optionsRange < this.totalMajorUnit || max - min >= this.totalMajorUnit) {
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
}
|
|
});
|
|
var LogarithmicAxis = Axis.extend({
|
|
init: function (seriesMin, seriesMax, options) {
|
|
this.options = this._initOptions(seriesMin, seriesMax, options);
|
|
Axis.fn.init.call(this, options);
|
|
},
|
|
startValue: function () {
|
|
return this.options.min;
|
|
},
|
|
options: {
|
|
type: 'log',
|
|
majorUnit: 10,
|
|
minorUnit: 1,
|
|
axisCrossingValue: 1,
|
|
vertical: true,
|
|
majorGridLines: {
|
|
visible: true,
|
|
width: 1,
|
|
color: BLACK
|
|
},
|
|
zIndex: 1
|
|
},
|
|
getSlot: function (a, b, limit) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), lineStart = lineBox[valueAxis + (reverse ? 2 : 1)], lineSize = vertical ? lineBox.height() : lineBox.width(), dir = reverse ? -1 : 1, base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, step = dir * (lineSize / (logMax - logMin)), p1, p2, slotBox = new Box2D(lineBox.x1, lineBox.y1, lineBox.x1, lineBox.y1);
|
|
if (!defined(a)) {
|
|
a = b || 1;
|
|
}
|
|
if (!defined(b)) {
|
|
b = a || 1;
|
|
}
|
|
if (a <= 0 || b <= 0) {
|
|
return;
|
|
}
|
|
if (limit) {
|
|
a = math.max(math.min(a, options.max), options.min);
|
|
b = math.max(math.min(b, options.max), options.min);
|
|
}
|
|
a = log(a, base);
|
|
b = log(b, base);
|
|
if (vertical) {
|
|
p1 = logMax - math.max(a, b);
|
|
p2 = logMax - math.min(a, b);
|
|
} else {
|
|
p1 = math.min(a, b) - logMin;
|
|
p2 = math.max(a, b) - logMin;
|
|
}
|
|
slotBox[valueAxis + 1] = lineStart + step * (reverse ? p2 : p1);
|
|
slotBox[valueAxis + 2] = lineStart + step * (reverse ? p1 : p2);
|
|
return slotBox;
|
|
},
|
|
getValue: function (point) {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, lineBox = axis.lineBox(), base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, dir = vertical === reverse ? 1 : -1, startEdge = dir === 1 ? 1 : 2, lineSize = vertical ? lineBox.height() : lineBox.width(), step = (logMax - logMin) / lineSize, valueAxis = vertical ? Y : X, lineStart = lineBox[valueAxis + startEdge], offset = dir * (point[valueAxis] - lineStart), valueOffset = offset * step, value;
|
|
if (offset < 0 || offset > lineSize) {
|
|
return null;
|
|
}
|
|
value = logMin + valueOffset;
|
|
return round(math.pow(base, value), DEFAULT_PRECISION);
|
|
},
|
|
range: function () {
|
|
var options = this.options;
|
|
return {
|
|
min: options.min,
|
|
max: options.max
|
|
};
|
|
},
|
|
scaleRange: function (delta) {
|
|
var axis = this, options = axis.options, base = options.majorUnit, offset = -delta;
|
|
return {
|
|
min: math.pow(base, axis.logMin - offset),
|
|
max: math.pow(base, axis.logMax + offset)
|
|
};
|
|
},
|
|
translateRange: function (delta) {
|
|
var axis = this, options = axis.options, base = options.majorUnit, lineBox = axis.lineBox(), vertical = options.vertical, reverse = options.reverse, size = vertical ? lineBox.height() : lineBox.width(), scale = size / (axis.logMax - axis.logMin), offset = round(delta / scale, DEFAULT_PRECISION);
|
|
if ((vertical || reverse) && !(vertical && reverse)) {
|
|
offset = -offset;
|
|
}
|
|
return {
|
|
min: math.pow(base, axis.logMin + offset),
|
|
max: math.pow(base, axis.logMax + offset)
|
|
};
|
|
},
|
|
labelsCount: function () {
|
|
var axis = this, floorMax = math.floor(axis.logMax), count = math.floor(floorMax - axis.logMin) + 1;
|
|
return count;
|
|
},
|
|
getMajorTickPositions: function () {
|
|
var axis = this, ticks = [];
|
|
axis.traverseMajorTicksPositions(function (position) {
|
|
ticks.push(position);
|
|
}, {
|
|
step: 1,
|
|
skip: 0
|
|
});
|
|
return ticks;
|
|
},
|
|
createTicks: function (lineGroup) {
|
|
var axis = this, ticks = [], options = axis.options, lineBox = axis.lineBox(), mirror = options.labels.mirror, majorTicks = options.majorTicks, minorTicks = options.minorTicks, tickLineOptions = { vertical: options.vertical };
|
|
function render(tickPosition, tickOptions) {
|
|
tickLineOptions.tickX = mirror ? lineBox.x2 : lineBox.x2 - tickOptions.size;
|
|
tickLineOptions.tickY = mirror ? lineBox.y1 - tickOptions.size : lineBox.y1;
|
|
tickLineOptions.position = tickPosition;
|
|
lineGroup.append(createAxisTick(tickLineOptions, tickOptions));
|
|
}
|
|
if (majorTicks.visible) {
|
|
axis.traverseMajorTicksPositions(render, majorTicks);
|
|
}
|
|
if (minorTicks.visible) {
|
|
axis.traverseMinorTicksPositions(render, minorTicks);
|
|
}
|
|
return ticks;
|
|
},
|
|
createGridLines: function (altAxis) {
|
|
var axis = this, options = axis.options, majorGridLines = options.majorGridLines, minorGridLines = options.minorGridLines, vertical = options.vertical, lineBox = altAxis.lineBox(), lineOptions = {
|
|
lineStart: lineBox[vertical ? 'x1' : 'y1'],
|
|
lineEnd: lineBox[vertical ? 'x2' : 'y2'],
|
|
vertical: vertical
|
|
}, majorTicks = [];
|
|
var container = this.gridLinesVisual();
|
|
function render(tickPosition, gridLine) {
|
|
if (!inArray(tickPosition, majorTicks)) {
|
|
lineOptions.position = tickPosition;
|
|
container.append(createAxisGridLine(lineOptions, gridLine));
|
|
majorTicks.push(tickPosition);
|
|
}
|
|
}
|
|
if (majorGridLines.visible) {
|
|
axis.traverseMajorTicksPositions(render, majorGridLines);
|
|
}
|
|
if (minorGridLines.visible) {
|
|
axis.traverseMinorTicksPositions(render, minorGridLines);
|
|
}
|
|
return container.children;
|
|
},
|
|
traverseMajorTicksPositions: function (callback, tickOptions) {
|
|
var axis = this, lineOptions = axis._lineOptions(), lineStart = lineOptions.lineStart, step = lineOptions.step, logMin = axis.logMin, logMax = axis.logMax, power, position;
|
|
for (power = math.ceil(logMin) + tickOptions.skip; power <= logMax; power += tickOptions.step) {
|
|
position = round(lineStart + step * (power - logMin), DEFAULT_PRECISION);
|
|
callback(position, tickOptions);
|
|
}
|
|
},
|
|
traverseMinorTicksPositions: function (callback, tickOptions) {
|
|
var axis = this, options = axis.options, lineOptions = axis._lineOptions(), lineStart = lineOptions.lineStart, lineStep = lineOptions.step, base = options.majorUnit, logMin = axis.logMin, logMax = axis.logMax, start = math.floor(logMin), max = options.max, min = options.min, minorUnit = options.minorUnit, power, value, position, minorOptions;
|
|
for (power = start; power < logMax; power++) {
|
|
minorOptions = axis._minorIntervalOptions(power);
|
|
for (var idx = tickOptions.skip; idx < minorUnit; idx += tickOptions.step) {
|
|
value = minorOptions.value + idx * minorOptions.minorStep;
|
|
if (value > max) {
|
|
break;
|
|
}
|
|
if (value >= min) {
|
|
position = round(lineStart + lineStep * (log(value, base) - logMin), DEFAULT_PRECISION);
|
|
callback(position, tickOptions);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
createAxisLabel: function (index, labelOptions) {
|
|
var axis = this, options = axis.options, power = math.ceil(axis.logMin + index), value = Math.pow(options.majorUnit, power), text = axis.axisLabelText(value, null, labelOptions);
|
|
return new AxisLabel(value, text, index, null, labelOptions);
|
|
},
|
|
shouldRenderNote: function (value) {
|
|
var range = this.range();
|
|
return range.min <= value && value <= range.max;
|
|
},
|
|
_throwNegativeValuesError: function () {
|
|
throw new Error('Non positive values cannot be used for a logarithmic axis');
|
|
},
|
|
_initOptions: function (seriesMin, seriesMax, options) {
|
|
var axis = this, axisOptions = deepExtend({}, axis.options, {
|
|
min: seriesMin,
|
|
max: seriesMax
|
|
}, options), min = axisOptions.min, max = axisOptions.max, base = axisOptions.majorUnit, autoMax = this._autoMax(seriesMax, base), autoMin = this._autoMin(seriesMin, seriesMax, axisOptions);
|
|
if (axisOptions.axisCrossingValue <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
if (!defined(options.max)) {
|
|
max = autoMax;
|
|
} else if (options.max <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
if (!defined(options.min)) {
|
|
min = autoMin;
|
|
} else if (options.min <= 0) {
|
|
axis._throwNegativeValuesError();
|
|
}
|
|
this.totalMin = defined(options.min) ? math.min(autoMin, options.min) : autoMin;
|
|
this.totalMax = defined(options.max) ? math.max(autoMax, options.max) : autoMax;
|
|
axis.logMin = round(log(min, base), DEFAULT_PRECISION);
|
|
axis.logMax = round(log(max, base), DEFAULT_PRECISION);
|
|
axisOptions.max = max;
|
|
axisOptions.min = min;
|
|
axisOptions.minorUnit = options.minorUnit || round(base - 1, DEFAULT_PRECISION);
|
|
return axisOptions;
|
|
},
|
|
_autoMin: function (min, max, options) {
|
|
var autoMin = min;
|
|
var base = options.majorUnit;
|
|
if (min <= 0) {
|
|
autoMin = max <= 1 ? math.pow(base, -2) : 1;
|
|
} else if (!options.narrowRange) {
|
|
autoMin = math.pow(base, math.floor(log(min, base)));
|
|
}
|
|
return autoMin;
|
|
},
|
|
_autoMax: function (max, base) {
|
|
var logMaxRemainder = round(log(max, base), DEFAULT_PRECISION) % 1;
|
|
var autoMax;
|
|
if (max <= 0) {
|
|
autoMax = base;
|
|
} else if (logMaxRemainder !== 0 && (logMaxRemainder < 0.3 || logMaxRemainder > 0.9)) {
|
|
autoMax = math.pow(base, log(max, base) + 0.2);
|
|
} else {
|
|
autoMax = math.pow(base, math.ceil(log(max, base)));
|
|
}
|
|
return autoMax;
|
|
},
|
|
pan: function (delta) {
|
|
var range = this.translateRange(delta);
|
|
return this.limitRange(range.min, range.max, this.totalMin, this.totalMax, -delta);
|
|
},
|
|
pointsRange: function (start, end) {
|
|
var startValue = this.getValue(start);
|
|
var endValue = this.getValue(end);
|
|
var min = math.min(startValue, endValue);
|
|
var max = math.max(startValue, endValue);
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
},
|
|
zoomRange: function (delta) {
|
|
var options = this.options;
|
|
var newRange = this.scaleRange(delta);
|
|
var totalMax = this.totalMax;
|
|
var totalMin = this.totalMin;
|
|
var min = util.limitValue(newRange.min, totalMin, totalMax);
|
|
var max = util.limitValue(newRange.max, totalMin, totalMax);
|
|
var base = options.majorUnit;
|
|
var acceptOptionsRange = max > min && options.min && options.max && round(log(options.max, base) - log(options.min, base), DEFAULT_PRECISION) < 1;
|
|
var acceptNewRange = !(options.min === totalMin && options.max === totalMax) && round(log(max, base) - log(min, base), DEFAULT_PRECISION) >= 1;
|
|
if (acceptOptionsRange || acceptNewRange) {
|
|
return {
|
|
min: min,
|
|
max: max
|
|
};
|
|
}
|
|
},
|
|
_minorIntervalOptions: function (power) {
|
|
var base = this.options.majorUnit, value = math.pow(base, power), nextValue = math.pow(base, power + 1), difference = nextValue - value, minorStep = difference / this.options.minorUnit;
|
|
return {
|
|
value: value,
|
|
minorStep: minorStep
|
|
};
|
|
},
|
|
_lineOptions: function () {
|
|
var axis = this, options = axis.options, reverse = options.reverse, vertical = options.vertical, valueAxis = vertical ? Y : X, lineBox = axis.lineBox(), dir = vertical === reverse ? 1 : -1, startEdge = dir === 1 ? 1 : 2, lineSize = vertical ? lineBox.height() : lineBox.width(), step = dir * (lineSize / (axis.logMax - axis.logMin)), lineStart = lineBox[valueAxis + startEdge];
|
|
return {
|
|
step: step,
|
|
lineStart: lineStart,
|
|
lineBox: lineBox
|
|
};
|
|
}
|
|
});
|
|
dataviz.Gradients = {
|
|
glass: {
|
|
type: LINEAR,
|
|
rotation: 0,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.25,
|
|
color: WHITE,
|
|
opacity: 0.3
|
|
},
|
|
{
|
|
offset: 1,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
sharpBevel: {
|
|
type: RADIAL,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0.55
|
|
},
|
|
{
|
|
offset: 0.65,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.95,
|
|
color: WHITE,
|
|
opacity: 0.25
|
|
}
|
|
]
|
|
},
|
|
roundedBevel: {
|
|
type: RADIAL,
|
|
stops: [
|
|
{
|
|
offset: 0.33,
|
|
color: WHITE,
|
|
opacity: 0.06
|
|
},
|
|
{
|
|
offset: 0.83,
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
{
|
|
offset: 0.95,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
roundedGlass: {
|
|
type: RADIAL,
|
|
supportVML: false,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: 0.5,
|
|
color: WHITE,
|
|
opacity: 0.3
|
|
},
|
|
{
|
|
offset: 0.99,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
},
|
|
sharpGlass: {
|
|
type: RADIAL,
|
|
supportVML: false,
|
|
stops: [
|
|
{
|
|
offset: 0,
|
|
color: WHITE,
|
|
opacity: 0.2
|
|
},
|
|
{
|
|
offset: 0.15,
|
|
color: WHITE,
|
|
opacity: 0.15
|
|
},
|
|
{
|
|
offset: 0.17,
|
|
color: WHITE,
|
|
opacity: 0.35
|
|
},
|
|
{
|
|
offset: 0.85,
|
|
color: WHITE,
|
|
opacity: 0.05
|
|
},
|
|
{
|
|
offset: 0.87,
|
|
color: WHITE,
|
|
opacity: 0.15
|
|
},
|
|
{
|
|
offset: 0.99,
|
|
color: WHITE,
|
|
opacity: 0
|
|
}
|
|
]
|
|
}
|
|
};
|
|
var ExportMixin = {
|
|
extend: function (proto, skipLegacy) {
|
|
if (!proto.exportVisual) {
|
|
throw new Error('Mixin target has no exportVisual method defined.');
|
|
}
|
|
proto.exportSVG = this.exportSVG;
|
|
proto.exportImage = this.exportImage;
|
|
proto.exportPDF = this.exportPDF;
|
|
if (!skipLegacy) {
|
|
proto.svg = this.svg;
|
|
proto.imageDataURL = this.imageDataURL;
|
|
}
|
|
},
|
|
exportSVG: function (options) {
|
|
return draw.exportSVG(this.exportVisual(), options);
|
|
},
|
|
exportImage: function (options) {
|
|
return draw.exportImage(this.exportVisual(options), options);
|
|
},
|
|
exportPDF: function (options) {
|
|
return draw.exportPDF(this.exportVisual(), options);
|
|
},
|
|
svg: function () {
|
|
if (draw.svg.Surface) {
|
|
return draw.svg._exportGroup(this.exportVisual());
|
|
} else {
|
|
throw new Error('SVG Export failed. Unable to export instantiate kendo.drawing.svg.Surface');
|
|
}
|
|
},
|
|
imageDataURL: function () {
|
|
if (!kendo.support.canvas) {
|
|
return null;
|
|
}
|
|
if (draw.canvas.Surface) {
|
|
var container = $('<div />').css({
|
|
display: 'none',
|
|
width: this.element.width(),
|
|
height: this.element.height()
|
|
}).appendTo(document.body);
|
|
var surface = new draw.canvas.Surface(container);
|
|
surface.draw(this.exportVisual());
|
|
var image = surface._rootElement.toDataURL();
|
|
surface.destroy();
|
|
container.remove();
|
|
return image;
|
|
} else {
|
|
throw new Error('Image Export failed. Unable to export instantiate kendo.drawing.canvas.Surface');
|
|
}
|
|
}
|
|
};
|
|
function autoMajorUnit(min, max) {
|
|
var diff = round(max - min, DEFAULT_PRECISION - 1);
|
|
if (diff === 0) {
|
|
if (max === 0) {
|
|
return 0.1;
|
|
}
|
|
diff = math.abs(max);
|
|
}
|
|
var scale = math.pow(10, math.floor(math.log(diff) / math.log(10))), relativeValue = round(diff / scale, DEFAULT_PRECISION), scaleMultiplier = 1;
|
|
if (relativeValue < 1.904762) {
|
|
scaleMultiplier = 0.2;
|
|
} else if (relativeValue < 4.761904) {
|
|
scaleMultiplier = 0.5;
|
|
} else if (relativeValue < 9.523809) {
|
|
scaleMultiplier = 1;
|
|
} else {
|
|
scaleMultiplier = 2;
|
|
}
|
|
return round(scale * scaleMultiplier, DEFAULT_PRECISION);
|
|
}
|
|
function rotatePoint(x, y, cx, cy, angle) {
|
|
var theta = angle * DEG_TO_RAD;
|
|
return new Point2D(cx + (x - cx) * math.cos(theta) + (y - cy) * math.sin(theta), cy - (x - cx) * math.sin(theta) + (y - cy) * math.cos(theta));
|
|
}
|
|
function boxDiff(r, s) {
|
|
if (r.x1 == s.x1 && r.y1 == s.y1 && r.x2 == s.x2 && r.y2 == s.y2) {
|
|
return s;
|
|
}
|
|
var a = math.min(r.x1, s.x1), b = math.max(r.x1, s.x1), c = math.min(r.x2, s.x2), d = math.max(r.x2, s.x2), e = math.min(r.y1, s.y1), f = math.max(r.y1, s.y1), g = math.min(r.y2, s.y2), h = math.max(r.y2, s.y2), result = [];
|
|
result[0] = Box2D(b, e, c, f);
|
|
result[1] = Box2D(a, f, b, g);
|
|
result[2] = Box2D(c, f, d, g);
|
|
result[3] = Box2D(b, g, c, h);
|
|
if (r.x1 == a && r.y1 == e || s.x1 == a && s.y1 == e) {
|
|
result[4] = Box2D(a, e, b, f);
|
|
result[5] = Box2D(c, g, d, h);
|
|
} else {
|
|
result[4] = Box2D(c, e, d, f);
|
|
result[5] = Box2D(a, g, b, h);
|
|
}
|
|
return $.grep(result, function (box) {
|
|
return box.height() > 0 && box.width() > 0;
|
|
})[0];
|
|
}
|
|
function inArray(value, array) {
|
|
return indexOf(value, array) != -1;
|
|
}
|
|
function ceil(value, step) {
|
|
return round(math.ceil(value / step) * step, DEFAULT_PRECISION);
|
|
}
|
|
function floor(value, step) {
|
|
return round(math.floor(value / step) * step, DEFAULT_PRECISION);
|
|
}
|
|
function round(value, precision) {
|
|
var power = math.pow(10, precision || 0);
|
|
return math.round(value * power) / power;
|
|
}
|
|
function log(y, x) {
|
|
return math.log(y) / math.log(x);
|
|
}
|
|
function remainderClose(value, divisor, ratio) {
|
|
var remainder = round(math.abs(value % divisor), DEFAULT_PRECISION), threshold = divisor * (1 - ratio);
|
|
return remainder === 0 || remainder > threshold;
|
|
}
|
|
function interpolateValue(start, end, progress) {
|
|
return round(start + (end - start) * progress, COORD_PRECISION);
|
|
}
|
|
function numericComparer(a, b) {
|
|
return a - b;
|
|
}
|
|
function autoFormat(format, value) {
|
|
if (format.match(FORMAT_REGEX)) {
|
|
return kendo.format.apply(this, arguments);
|
|
}
|
|
return kendo.toString(value, format);
|
|
}
|
|
function clockwise(v1, v2) {
|
|
return -v1.x * v2.y + v1.y * v2.x < 0;
|
|
}
|
|
function dateComparer(a, b) {
|
|
if (a && b) {
|
|
return a.getTime() - b.getTime();
|
|
}
|
|
return -1;
|
|
}
|
|
var CurveProcessor = function (closed) {
|
|
this.closed = closed;
|
|
};
|
|
CurveProcessor.prototype = CurveProcessor.fn = {
|
|
WEIGHT: 0.333,
|
|
EXTREMUM_ALLOWED_DEVIATION: 0.01,
|
|
process: function (dataPoints) {
|
|
var that = this, closed = that.closed, points = dataPoints.slice(0), length = points.length, segments = [], p0, p1, p2, controlPoints, initialControlPoint, lastControlPoint, tangent;
|
|
if (length > 2) {
|
|
that.removeDuplicates(0, points);
|
|
length = points.length;
|
|
}
|
|
if (length < 2 || length == 2 && points[0].equals(points[1])) {
|
|
return segments;
|
|
}
|
|
p0 = points[0];
|
|
p1 = points[1];
|
|
p2 = points[2];
|
|
segments.push(new draw.Segment(p0));
|
|
while (p0.equals(points[length - 1])) {
|
|
closed = true;
|
|
points.pop();
|
|
length--;
|
|
}
|
|
if (length == 2) {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
last(segments).controlOut(that.firstControlPoint(tangent, p0, p1, X, Y));
|
|
segments.push(new draw.Segment(p1, that.secondControlPoint(tangent, p0, p1, X, Y)));
|
|
return segments;
|
|
}
|
|
if (closed) {
|
|
p0 = points[length - 1];
|
|
p1 = points[0];
|
|
p2 = points[1];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
initialControlPoint = controlPoints[1];
|
|
lastControlPoint = controlPoints[0];
|
|
} else {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
initialControlPoint = that.firstControlPoint(tangent, p0, p1, X, Y);
|
|
}
|
|
var cp0 = initialControlPoint;
|
|
for (var idx = 0; idx <= length - 3; idx++) {
|
|
that.removeDuplicates(idx, points);
|
|
length = points.length;
|
|
if (idx + 3 <= length) {
|
|
p0 = points[idx];
|
|
p1 = points[idx + 1];
|
|
p2 = points[idx + 2];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
last(segments).controlOut(cp0);
|
|
cp0 = controlPoints[1];
|
|
var cp1 = controlPoints[0];
|
|
segments.push(new draw.Segment(p1, cp1));
|
|
}
|
|
}
|
|
if (closed) {
|
|
p0 = points[length - 2];
|
|
p1 = points[length - 1];
|
|
p2 = points[0];
|
|
controlPoints = that.controlPoints(p0, p1, p2);
|
|
last(segments).controlOut(cp0);
|
|
segments.push(new draw.Segment(p1, controlPoints[0]));
|
|
last(segments).controlOut(controlPoints[1]);
|
|
segments.push(new draw.Segment(p2, lastControlPoint));
|
|
} else {
|
|
tangent = that.tangent(p1, p2, X, Y);
|
|
last(segments).controlOut(cp0);
|
|
segments.push(new draw.Segment(p2, that.secondControlPoint(tangent, p1, p2, X, Y)));
|
|
}
|
|
return segments;
|
|
},
|
|
removeDuplicates: function (idx, points) {
|
|
while (points[idx].equals(points[idx + 1]) || points[idx + 1].equals(points[idx + 2])) {
|
|
points.splice(idx + 1, 1);
|
|
}
|
|
},
|
|
invertAxis: function (p0, p1, p2) {
|
|
var that = this, fn, y2, invertAxis = false;
|
|
if (p0.x === p1.x) {
|
|
invertAxis = true;
|
|
} else if (p1.x === p2.x) {
|
|
if (p1.y < p2.y && p0.y <= p1.y || p2.y < p1.y && p1.y <= p0.y) {
|
|
invertAxis = true;
|
|
}
|
|
} else {
|
|
fn = that.lineFunction(p0, p1);
|
|
y2 = that.calculateFunction(fn, p2.x);
|
|
if (!(p0.y <= p1.y && p2.y <= y2) && !(p1.y <= p0.y && p2.y >= y2)) {
|
|
invertAxis = true;
|
|
}
|
|
}
|
|
return invertAxis;
|
|
},
|
|
isLine: function (p0, p1, p2) {
|
|
var that = this, fn = that.lineFunction(p0, p1), y2 = that.calculateFunction(fn, p2.x);
|
|
return p0.x == p1.x && p1.x == p2.x || round(y2, 1) === round(p2.y, 1);
|
|
},
|
|
lineFunction: function (p1, p2) {
|
|
var a = (p2.y - p1.y) / (p2.x - p1.x), b = p1.y - a * p1.x;
|
|
return [
|
|
b,
|
|
a
|
|
];
|
|
},
|
|
controlPoints: function (p0, p1, p2) {
|
|
var that = this, xField = X, yField = Y, restrict = false, switchOrientation = false, tangent, monotonic, firstControlPoint, secondControlPoint, allowedDeviation = that.EXTREMUM_ALLOWED_DEVIATION;
|
|
if (that.isLine(p0, p1, p2)) {
|
|
tangent = that.tangent(p0, p1, X, Y);
|
|
} else {
|
|
monotonic = {
|
|
x: that.isMonotonicByField(p0, p1, p2, X),
|
|
y: that.isMonotonicByField(p0, p1, p2, Y)
|
|
};
|
|
if (monotonic.x && monotonic.y) {
|
|
tangent = that.tangent(p0, p2, X, Y);
|
|
restrict = true;
|
|
} else {
|
|
if (that.invertAxis(p0, p1, p2)) {
|
|
xField = Y;
|
|
yField = X;
|
|
}
|
|
if (monotonic[xField]) {
|
|
tangent = 0;
|
|
} else {
|
|
var sign;
|
|
if (p2[yField] < p0[yField] && p0[yField] <= p1[yField] || p0[yField] < p2[yField] && p1[yField] <= p0[yField]) {
|
|
sign = that.sign((p2[yField] - p0[yField]) * (p1[xField] - p0[xField]));
|
|
} else {
|
|
sign = -that.sign((p2[xField] - p0[xField]) * (p1[yField] - p0[yField]));
|
|
}
|
|
tangent = allowedDeviation * sign;
|
|
switchOrientation = true;
|
|
}
|
|
}
|
|
}
|
|
secondControlPoint = that.secondControlPoint(tangent, p0, p1, xField, yField);
|
|
if (switchOrientation) {
|
|
var oldXField = xField;
|
|
xField = yField;
|
|
yField = oldXField;
|
|
}
|
|
firstControlPoint = that.firstControlPoint(tangent, p1, p2, xField, yField);
|
|
if (restrict) {
|
|
that.restrictControlPoint(p0, p1, secondControlPoint, tangent);
|
|
that.restrictControlPoint(p1, p2, firstControlPoint, tangent);
|
|
}
|
|
return [
|
|
secondControlPoint,
|
|
firstControlPoint
|
|
];
|
|
},
|
|
sign: function (x) {
|
|
return x <= 0 ? -1 : 1;
|
|
},
|
|
restrictControlPoint: function (p1, p2, cp, tangent) {
|
|
if (p1.y < p2.y) {
|
|
if (p2.y < cp.y) {
|
|
cp.x = p1.x + (p2.y - p1.y) / tangent;
|
|
cp.y = p2.y;
|
|
} else if (cp.y < p1.y) {
|
|
cp.x = p2.x - (p2.y - p1.y) / tangent;
|
|
cp.y = p1.y;
|
|
}
|
|
} else {
|
|
if (cp.y < p2.y) {
|
|
cp.x = p1.x - (p1.y - p2.y) / tangent;
|
|
cp.y = p2.y;
|
|
} else if (p1.y < cp.y) {
|
|
cp.x = p2.x + (p1.y - p2.y) / tangent;
|
|
cp.y = p1.y;
|
|
}
|
|
}
|
|
},
|
|
tangent: function (p0, p1, xField, yField) {
|
|
var tangent, x = p1[xField] - p0[xField], y = p1[yField] - p0[yField];
|
|
if (x === 0) {
|
|
tangent = 0;
|
|
} else {
|
|
tangent = y / x;
|
|
}
|
|
return tangent;
|
|
},
|
|
isMonotonicByField: function (p0, p1, p2, field) {
|
|
return p2[field] > p1[field] && p1[field] > p0[field] || p2[field] < p1[field] && p1[field] < p0[field];
|
|
},
|
|
firstControlPoint: function (tangent, p0, p3, xField, yField) {
|
|
var that = this, t1 = p0[xField], t2 = p3[xField], distance = (t2 - t1) * that.WEIGHT;
|
|
return that.point(t1 + distance, p0[yField] + distance * tangent, xField, yField);
|
|
},
|
|
secondControlPoint: function (tangent, p0, p3, xField, yField) {
|
|
var that = this, t1 = p0[xField], t2 = p3[xField], distance = (t2 - t1) * that.WEIGHT;
|
|
return that.point(t2 - distance, p3[yField] - distance * tangent, xField, yField);
|
|
},
|
|
point: function (xValue, yValue, xField, yField) {
|
|
var controlPoint = new geom.Point();
|
|
controlPoint[xField] = xValue;
|
|
controlPoint[yField] = yValue;
|
|
return controlPoint;
|
|
},
|
|
calculateFunction: function (fn, x) {
|
|
var result = 0, length = fn.length;
|
|
for (var i = 0; i < length; i++) {
|
|
result += Math.pow(x, i) * fn[i];
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
function mwDelta(e) {
|
|
var origEvent = e.originalEvent, delta = 0;
|
|
if (origEvent.wheelDelta) {
|
|
delta = -origEvent.wheelDelta / 120;
|
|
delta = delta > 0 ? math.ceil(delta) : math.floor(delta);
|
|
}
|
|
if (origEvent.detail) {
|
|
delta = round(origEvent.detail / 3);
|
|
}
|
|
return delta;
|
|
}
|
|
function decodeEntities(text) {
|
|
if (!text || !text.indexOf || text.indexOf('&') < 0) {
|
|
return text;
|
|
} else {
|
|
var element = decodeEntities._element;
|
|
element.innerHTML = text;
|
|
return element.textContent || element.innerText;
|
|
}
|
|
}
|
|
function alignPathToPixel(path) {
|
|
if (!kendo.support.vml) {
|
|
var offset = 0.5;
|
|
if (path.options.stroke && defined(path.options.stroke.width)) {
|
|
if (path.options.stroke.width % 2 === 0) {
|
|
offset = 0;
|
|
}
|
|
}
|
|
for (var i = 0; i < path.segments.length; i++) {
|
|
path.segments[i].anchor().round(0).translate(offset, offset);
|
|
}
|
|
}
|
|
return path;
|
|
}
|
|
function innerRadialStops(options) {
|
|
var stops = options.stops, usedSpace = options.innerRadius / options.radius * 100, i, length = stops.length, currentStop, currentStops = [];
|
|
for (i = 0; i < length; i++) {
|
|
currentStop = deepExtend({}, stops[i]);
|
|
currentStop.offset = (currentStop.offset * (100 - usedSpace) + usedSpace) / 100;
|
|
currentStops.push(currentStop);
|
|
}
|
|
return currentStops;
|
|
}
|
|
function rectToBox(rect) {
|
|
var origin = rect.origin;
|
|
var bottomRight = rect.bottomRight();
|
|
return new Box2D(origin.x, origin.y, bottomRight.x, bottomRight.y);
|
|
}
|
|
decodeEntities._element = document.createElement('span');
|
|
deepExtend(kendo.dataviz, {
|
|
AXIS_LABEL_CLICK: AXIS_LABEL_CLICK,
|
|
COORD_PRECISION: COORD_PRECISION,
|
|
DEFAULT_PRECISION: DEFAULT_PRECISION,
|
|
DEFAULT_WIDTH: DEFAULT_WIDTH,
|
|
DEFAULT_HEIGHT: DEFAULT_HEIGHT,
|
|
DEFAULT_FONT: DEFAULT_FONT,
|
|
INITIAL_ANIMATION_DURATION: INITIAL_ANIMATION_DURATION,
|
|
NOTE_CLICK: NOTE_CLICK,
|
|
NOTE_HOVER: NOTE_HOVER,
|
|
CLIP: CLIP,
|
|
Axis: Axis,
|
|
AxisLabel: AxisLabel,
|
|
Box2D: Box2D,
|
|
BoxElement: BoxElement,
|
|
ChartElement: ChartElement,
|
|
CurveProcessor: CurveProcessor,
|
|
ExportMixin: ExportMixin,
|
|
FloatElement: FloatElement,
|
|
LogarithmicAxis: LogarithmicAxis,
|
|
Note: Note,
|
|
NumericAxis: NumericAxis,
|
|
Point2D: Point2D,
|
|
Ring: Ring,
|
|
RootElement: RootElement,
|
|
Sector: Sector,
|
|
ShapeBuilder: ShapeBuilder,
|
|
ShapeElement: ShapeElement,
|
|
Text: Text,
|
|
TextBox: TextBox,
|
|
Title: Title,
|
|
alignPathToPixel: alignPathToPixel,
|
|
autoFormat: autoFormat,
|
|
autoMajorUnit: autoMajorUnit,
|
|
boxDiff: boxDiff,
|
|
dateComparer: dateComparer,
|
|
decodeEntities: decodeEntities,
|
|
getSpacing: getSpacing,
|
|
inArray: inArray,
|
|
interpolateValue: interpolateValue,
|
|
mwDelta: mwDelta,
|
|
rectToBox: rectToBox,
|
|
rotatePoint: rotatePoint,
|
|
round: round,
|
|
ceil: ceil,
|
|
floor: floor
|
|
});
|
|
}(window.kendo.jQuery));
|
|
return window.kendo;
|
|
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
|
|
(a3 || a2)();
|
|
})); |