/** * Kendo UI v2016.1.112 (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 = $('
')[0]; var TextMetrics = Class.extend({ init: function (options) { this._cache = new LRUCache(1000); this._initOptions(options); }, options: { baselineMarkerSize: 1 }, measure: function (text, style, box) { var styleKey = util.objectKey(style), cacheKey = util.hashKey(text + styleKey), cachedResult = this._cache.get(cacheKey); if (cachedResult) { return cachedResult; } var size = { width: 0, height: 0, baseline: 0 }; 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 $('')[0]; } }); TextMetrics.current = new TextMetrics(); function measureText(text, style, measureBox) { return TextMetrics.current.measure(text, style, measureBox); } kendo.util.TextMetrics = TextMetrics; kendo.util.LRUCache = LRUCache; 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 = $('').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)(); }));