').addClass('k-map-controls ' + renderPos(pos)).appendTo(this.element);
}
return $('
').appendTo(wrap);
},
_createAttribution: function (options) {
var element = this._createControlElement(options, 'bottomRight');
this.attribution = new ui.Attribution(element, options);
},
_createNavigator: function (options) {
var element = this._createControlElement(options, 'topLeft');
var navigator = this.navigator = new ui.Navigator(element, options);
this._navigatorPan = proxy(this._navigatorPan, this);
navigator.bind('pan', this._navigatorPan);
this._navigatorCenter = proxy(this._navigatorCenter, this);
navigator.bind('center', this._navigatorCenter);
},
_navigatorPan: function (e) {
var map = this;
var scroller = map.scroller;
var x = scroller.scrollLeft + e.x;
var y = scroller.scrollTop - e.y;
var bounds = this._virtualSize;
var height = this.element.height();
var width = this.element.width();
x = limit(x, bounds.x.min, bounds.x.max - width);
y = limit(y, bounds.y.min, bounds.y.max - height);
map.scroller.one('scroll', function (e) {
map._scrollEnd(e);
});
map.scroller.scrollTo(-x, -y);
},
_navigatorCenter: function () {
this.center(this.options.center);
},
_createZoomControl: function (options) {
var element = this._createControlElement(options, 'topLeft');
var zoomControl = this.zoomControl = new ui.ZoomControl(element, options);
this._zoomControlChange = proxy(this._zoomControlChange, this);
zoomControl.bind('change', this._zoomControlChange);
},
_zoomControlChange: function (e) {
if (!this.trigger('zoomStart', { originalEvent: e })) {
this.zoom(this.zoom() + e.delta);
this.trigger('zoomEnd', { originalEvent: e });
}
},
_initScroller: function () {
var friction = kendo.support.mobileOS ? FRICTION_MOBILE : FRICTION;
var zoomable = this.options.zoomable !== false;
var scroller = this.scroller = new kendo.mobile.ui.Scroller(this.element.children(0), {
friction: friction,
velocityMultiplier: VELOCITY_MULTIPLIER,
zoom: zoomable,
mousewheelScrolling: false
});
scroller.bind('scroll', proxy(this._scroll, this));
scroller.bind('scrollEnd', proxy(this._scrollEnd, this));
scroller.userEvents.bind('gesturestart', proxy(this._scaleStart, this));
scroller.userEvents.bind('gestureend', proxy(this._scale, this));
this.scrollElement = scroller.scrollElement;
},
_initLayers: function () {
var defs = this.options.layers, layers = this.layers = [];
for (var i = 0; i < defs.length; i++) {
var options = defs[i];
var type = options.type || 'shape';
var defaults = this.options.layerDefaults[type];
var impl = dataviz.map.layers[type];
layers.push(new impl(this, deepExtend({}, defaults, options)));
}
},
_initMarkers: function () {
this.markers = new map.layers.MarkerLayer(this, this.options.markerDefaults);
this.markers.add(this.options.markers);
},
_scroll: function (e) {
var origin = this.locationToLayer(this._viewOrigin).round();
var movable = e.sender.movable;
var offset = new g.Point(movable.x, movable.y).scale(-1).scale(1 / movable.scale);
origin.x += offset.x;
origin.y += offset.y;
this._scrollOffset = offset;
this._setOrigin(this.layerToLocation(origin));
this.trigger('pan', {
originalEvent: e,
origin: this._getOrigin(),
center: this.center()
});
},
_scrollEnd: function (e) {
if (!this._scrollOffset || !this._panComplete()) {
return;
}
this._scrollOffset = null;
this._panEndTS = new Date();
this.trigger('panEnd', {
originalEvent: e,
origin: this._getOrigin(),
center: this.center()
});
},
_panComplete: function () {
return new Date() - (this._panEndTS || 0) > 50;
},
_scaleStart: function (e) {
if (this.trigger('zoomStart', { originalEvent: e })) {
var touch = e.touches[1];
if (touch) {
touch.cancel();
}
}
},
_scale: function (e) {
var scale = this.scroller.movable.scale;
var zoom = this._scaleToZoom(scale);
var gestureCenter = new g.Point(e.center.x, e.center.y);
var centerLocation = this.viewToLocation(gestureCenter, zoom);
var centerPoint = this.locationToLayer(centerLocation, zoom);
var originPoint = centerPoint.translate(-gestureCenter.x, -gestureCenter.y);
this._zoomAround(originPoint, zoom);
this.trigger('zoomEnd', { originalEvent: e });
},
_scaleToZoom: function (scaleDelta) {
var scale = this._layerSize() * scaleDelta;
var tiles = scale / this.options.minSize;
var zoom = math.log(tiles) / math.log(2);
return math.round(zoom);
},
_reset: function () {
if (this.attribution) {
this.attribution.filter(this.center(), this.zoom());
}
this._viewOrigin = this._getOrigin(true);
this._resetScroller();
this.trigger('beforeReset');
this.trigger('reset');
},
_resetScroller: function () {
var scroller = this.scroller;
var x = scroller.dimensions.x;
var y = scroller.dimensions.y;
var scale = this._layerSize();
var nw = this.extent().nw;
var topLeft = this.locationToLayer(nw).round();
scroller.movable.round = true;
scroller.reset();
scroller.userEvents.cancel();
var zoom = this.zoom();
scroller.dimensions.forcedMinScale = pow(2, this.options.minZoom - zoom);
scroller.dimensions.maxScale = pow(2, this.options.maxZoom - zoom);
var xBounds = {
min: -topLeft.x,
max: scale - topLeft.x
};
var yBounds = {
min: -topLeft.y,
max: scale - topLeft.y
};
if (this.options.wraparound) {
xBounds.max = 20 * scale;
xBounds.min = -xBounds.max;
}
if (this.options.pannable === false) {
var viewSize = this.viewSize();
xBounds.min = yBounds.min = 0;
xBounds.max = viewSize.width;
yBounds.max = viewSize.height;
}
x.makeVirtual();
y.makeVirtual();
x.virtualSize(xBounds.min, xBounds.max);
y.virtualSize(yBounds.min, yBounds.max);
this._virtualSize = {
x: xBounds,
y: yBounds
};
},
_renderLayers: function () {
var defs = this.options.layers, layers = this.layers = [], scrollWrap = this.scrollWrap;
scrollWrap.empty();
for (var i = 0; i < defs.length; i++) {
var options = defs[i];
var type = options.type || 'shape';
var defaults = this.options.layerDefaults[type];
var impl = dataviz.map.layers[type];
layers.push(new impl(this, deepExtend({}, defaults, options)));
}
},
_layerSize: function (zoom) {
zoom = valueOrDefault(zoom, this.options.zoom);
return this.options.minSize * pow(2, zoom);
},
_click: function (e) {
if (!this._panComplete()) {
return;
}
var cursor = this.eventOffset(e);
this.trigger('click', {
originalEvent: e,
location: this.viewToLocation(cursor)
});
},
_mousewheel: function (e) {
e.preventDefault();
var delta = dataviz.mwDelta(e) > 0 ? -1 : 1;
var options = this.options;
var fromZoom = this.zoom();
var toZoom = limit(fromZoom + delta, options.minZoom, options.maxZoom);
if (options.zoomable !== false && toZoom !== fromZoom) {
if (!this.trigger('zoomStart', { originalEvent: e })) {
var cursor = this.eventOffset(e);
var location = this.viewToLocation(cursor);
var postZoom = this.locationToLayer(location, toZoom);
var origin = postZoom.translate(-cursor.x, -cursor.y);
this._zoomAround(origin, toZoom);
this.trigger('zoomEnd', { originalEvent: e });
}
}
}
});
dataviz.ui.plugin(Map);
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.dataviz.map', [
'kendo.data',
'kendo.userevents',
'kendo.tooltip',
'kendo.mobile.scroller',
'kendo.draganddrop',
'kendo.drawing',
'dataviz/map/location',
'dataviz/map/attribution',
'dataviz/map/navigator',
'dataviz/map/zoom',
'dataviz/map/crs',
'dataviz/map/layers/base',
'dataviz/map/layers/shape',
'dataviz/map/layers/bubble',
'dataviz/map/layers/tile',
'dataviz/map/layers/bing',
'dataviz/map/layers/marker',
'dataviz/map/main'
], f);
}(function () {
var __meta__ = {
id: 'dataviz.map',
name: 'Map',
category: 'dataviz',
description: 'The Kendo DataViz Map displays spatial data',
depends: [
'data',
'userevents',
'tooltip',
'dataviz.core',
'drawing',
'mobile.scroller'
]
};
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/utils', ['kendo.core'], f);
}(function () {
(function ($, undefined) {
var kendo = window.kendo, diagram = kendo.dataviz.diagram = {}, deepExtend = kendo.deepExtend, isArray = $.isArray, EPSILON = 0.000001;
var Utils = {};
deepExtend(Utils, {
isNearZero: function (num) {
return Math.abs(num) < EPSILON;
},
isDefined: function (obj) {
return typeof obj !== 'undefined';
},
isUndefined: function (obj) {
return typeof obj === 'undefined' || obj === null;
},
isObject: function (obj) {
return obj === Object(obj);
},
has: function (obj, key) {
return Object.hasOwnProperty.call(obj, key);
},
isString: function (obj) {
return Object.prototype.toString.call(obj) == '[object String]';
},
isBoolean: function (obj) {
return Object.prototype.toString.call(obj) == '[object Boolean]';
},
isType: function (obj, type) {
return Object.prototype.toString.call(obj) == '[object ' + type + ']';
},
isNumber: function (obj) {
return !isNaN(parseFloat(obj)) && isFinite(obj);
},
isEmpty: function (obj) {
if (obj === null) {
return true;
}
if (isArray(obj) || Utils.isString(obj)) {
return obj.length === 0;
}
for (var key in obj) {
if (Utils.has(obj, key)) {
return false;
}
}
return true;
},
simpleExtend: function (destination, source) {
if (!Utils.isObject(source)) {
return;
}
for (var name in source) {
destination[name] = source[name];
}
},
initArray: function createIdArray(size, value) {
var array = [];
for (var i = 0; i < size; ++i) {
array[i] = value;
}
return array;
},
serializePoints: function (points) {
var res = [];
for (var i = 0; i < points.length; i++) {
var p = points[i];
res.push(p.x + ';' + p.y);
}
return res.join(';');
},
deserializePoints: function (s) {
var v = s.split(';'), points = [];
if (v.length % 2 !== 0) {
throw 'Not an array of points.';
}
for (var i = 0; i < v.length; i += 2) {
points.push(new diagram.Point(parseInt(v[i], 10), parseInt(v[i + 1], 10)));
}
return points;
},
randomInteger: function (lower, upper) {
return parseInt(Math.floor(Math.random() * upper) + lower, 10);
},
DFT: function (el, func) {
func(el);
if (el.childNodes) {
for (var i = 0; i < el.childNodes.length; i++) {
var item = el.childNodes[i];
this.DFT(item, func);
}
}
},
getMatrixAngle: function (m) {
if (m === null || m.d === 0) {
return 0;
}
return Math.atan2(m.b, m.d) * 180 / Math.PI;
},
getMatrixScaling: function (m) {
var sX = Math.sqrt(m.a * m.a + m.c * m.c);
var sY = Math.sqrt(m.b * m.b + m.d * m.d);
return [
sX,
sY
];
}
});
function Range(start, stop, step) {
if (typeof start == 'undefined' || typeof stop == 'undefined') {
return [];
}
if (step && Utils.sign(stop - start) != Utils.sign(step)) {
throw 'The sign of the increment should allow to reach the stop-value.';
}
step = step || 1;
start = start || 0;
stop = stop || start;
if ((stop - start) / step === Infinity) {
throw 'Infinite range defined.';
}
var range = [], i = -1, j;
function rangeIntegerScale(x) {
var k = 1;
while (x * k % 1) {
k *= 10;
}
return k;
}
var k = rangeIntegerScale(Math.abs(step));
start *= k;
stop *= k;
step *= k;
if (start > stop && step > 0) {
step = -step;
}
if (step < 0) {
while ((j = start + step * ++i) >= stop) {
range.push(j / k);
}
} else {
while ((j = start + step * ++i) <= stop) {
range.push(j / k);
}
}
return range;
}
function findRadian(start, end) {
if (start == end) {
return 0;
}
var sngXComp = end.x - start.x, sngYComp = start.y - end.y, atan = Math.atan(sngXComp / sngYComp);
if (sngYComp >= 0) {
return sngXComp < 0 ? atan + 2 * Math.PI : atan;
}
return atan + Math.PI;
}
Utils.sign = function (number) {
return number ? number < 0 ? -1 : 1 : 0;
};
Utils.findAngle = function (center, end) {
return findRadian(center, end) * 180 / Math.PI;
};
Utils.forEach = function (arr, iterator, thisRef) {
for (var i = 0; i < arr.length; i++) {
iterator.call(thisRef, arr[i], i, arr);
}
};
Utils.any = function (arr, predicate) {
for (var i = 0; i < arr.length; ++i) {
if (predicate(arr[i])) {
return arr[i];
}
}
return null;
};
Utils.remove = function (arr, what) {
var ax;
while ((ax = Utils.indexOf(arr, what)) !== -1) {
arr.splice(ax, 1);
}
return arr;
};
Utils.contains = function (arr, obj) {
return Utils.indexOf(arr, obj) !== -1;
};
Utils.indexOf = function (arr, what) {
return $.inArray(what, arr);
};
Utils.fold = function (list, iterator, acc, context) {
var initial = arguments.length > 2;
for (var i = 0; i < list.length; i++) {
var value = list[i];
if (!initial) {
acc = value;
initial = true;
} else {
acc = iterator.call(context, acc, value, i, list);
}
}
if (!initial) {
throw 'Reduce of empty array with no initial value';
}
return acc;
};
Utils.find = function (arr, iterator, context) {
var result;
Utils.any(arr, function (value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
return false;
});
return result;
};
Utils.first = function (arr, constraint, context) {
if (arr.length === 0) {
return null;
}
if (Utils.isUndefined(constraint)) {
return arr[0];
}
return Utils.find(arr, constraint, context);
};
Utils.insert = function (arr, element, position) {
arr.splice(position, 0, element);
return arr;
};
Utils.all = function (arr, iterator, context) {
var result = true;
var value;
for (var i = 0; i < arr.length; i++) {
value = arr[i];
result = result && iterator.call(context, value, i, arr);
if (!result) {
break;
}
}
return result;
};
Utils.clear = function (arr) {
arr.splice(0, arr.length);
};
Utils.bisort = function (a, b, sortfunc) {
if (Utils.isUndefined(a)) {
throw 'First array is not specified.';
}
if (Utils.isUndefined(b)) {
throw 'Second array is not specified.';
}
if (a.length != b.length) {
throw 'The two arrays should have equal length';
}
var all = [], i;
for (i = 0; i < a.length; i++) {
all.push({
'x': a[i],
'y': b[i]
});
}
if (Utils.isUndefined(sortfunc)) {
all.sort(function (m, n) {
return m.x - n.x;
});
} else {
all.sort(function (m, n) {
return sortfunc(m.x, n.x);
});
}
Utils.clear(a);
Utils.clear(b);
for (i = 0; i < all.length; i++) {
a.push(all[i].x);
b.push(all[i].y);
}
};
Utils.addRange = function (arr, range) {
arr.push.apply(arr, range);
};
var Easing = {
easeInOut: function (pos) {
return -Math.cos(pos * Math.PI) / 2 + 0.5;
}
};
var Ticker = kendo.Class.extend({
init: function () {
this.adapters = [];
this.target = 0;
this.tick = 0;
this.interval = 20;
this.duration = 800;
this.lastTime = null;
this.handlers = [];
var _this = this;
this.transition = Easing.easeInOut;
this.timerDelegate = function () {
_this.onTimerEvent();
};
},
addAdapter: function (a) {
this.adapters.push(a);
},
onComplete: function (handler) {
this.handlers.push(handler);
},
removeHandler: function (handler) {
this.handlers = $.grep(this.handlers, function (h) {
return h !== handler;
});
},
trigger: function () {
var _this = this;
if (this.handlers) {
Utils.forEach(this.handlers, function (h) {
return h.call(_this.caller !== null ? _this.caller : _this);
});
}
},
onStep: function () {
},
seekTo: function (to) {
this.seekFromTo(this.tick, to);
},
seekFromTo: function (from, to) {
this.target = Math.max(0, Math.min(1, to));
this.tick = Math.max(0, Math.min(1, from));
this.lastTime = new Date().getTime();
if (!this.intervalId) {
this.intervalId = window.setInterval(this.timerDelegate, this.interval);
}
},
stop: function () {
if (this.intervalId) {
window.clearInterval(this.intervalId);
this.intervalId = null;
this.trigger();
}
},
play: function (origin) {
if (this.adapters.length === 0) {
return;
}
if (origin !== null) {
this.caller = origin;
}
this.initState();
this.seekFromTo(0, 1);
},
reverse: function () {
this.seekFromTo(1, 0);
},
initState: function () {
if (this.adapters.length === 0) {
return;
}
for (var i = 0; i < this.adapters.length; i++) {
this.adapters[i].initState();
}
},
propagate: function () {
var value = this.transition(this.tick);
for (var i = 0; i < this.adapters.length; i++) {
this.adapters[i].update(value);
}
},
onTimerEvent: function () {
var now = new Date().getTime();
var timePassed = now - this.lastTime;
this.lastTime = now;
var movement = timePassed / this.duration * (this.tick < this.target ? 1 : -1);
if (Math.abs(movement) >= Math.abs(this.tick - this.target)) {
this.tick = this.target;
} else {
this.tick += movement;
}
try {
this.propagate();
} finally {
this.onStep.call(this);
if (this.target == this.tick) {
this.stop();
}
}
}
});
kendo.deepExtend(diagram, {
init: function (element) {
kendo.init(element, diagram.ui);
},
Utils: Utils,
Range: Range,
Ticker: Ticker
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/math', [
'dataviz/diagram/utils',
'kendo.dataviz.core'
], f);
}(function () {
(function ($, undefined) {
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Class = kendo.Class, deepExtend = kendo.deepExtend, dataviz = kendo.dataviz, Utils = diagram.Utils, Point = dataviz.Point2D, isFunction = kendo.isFunction, contains = Utils.contains, map = $.map;
var HITTESTAREA = 3, EPSILON = 0.000001;
deepExtend(Point.fn, {
plus: function (p) {
return new Point(this.x + p.x, this.y + p.y);
},
minus: function (p) {
return new Point(this.x - p.x, this.y - p.y);
},
offset: function (value) {
return new Point(this.x - value, this.y - value);
},
times: function (s) {
return new Point(this.x * s, this.y * s);
},
normalize: function () {
if (this.length() === 0) {
return new Point();
}
return this.times(1 / this.length());
},
length: function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
toString: function () {
return '(' + this.x + ',' + this.y + ')';
},
lengthSquared: function () {
return this.x * this.x + this.y * this.y;
},
middleOf: function MiddleOf(p, q) {
return new Point(q.x - p.x, q.y - p.y).times(0.5).plus(p);
},
toPolar: function (useDegrees) {
var factor = 1;
if (useDegrees) {
factor = 180 / Math.PI;
}
var a = Math.atan2(Math.abs(this.y), Math.abs(this.x));
var halfpi = Math.PI / 2;
var len = this.length();
if (this.x === 0) {
if (this.y === 0) {
return new Polar(0, 0);
}
if (this.y > 0) {
return new Polar(len, factor * halfpi);
}
if (this.y < 0) {
return new Polar(len, factor * 3 * halfpi);
}
} else if (this.x > 0) {
if (this.y === 0) {
return new Polar(len, 0);
}
if (this.y > 0) {
return new Polar(len, factor * a);
}
if (this.y < 0) {
return new Polar(len, factor * (4 * halfpi - a));
}
} else {
if (this.y === 0) {
return new Polar(len, 2 * halfpi);
}
if (this.y > 0) {
return new Polar(len, factor * (2 * halfpi - a));
}
if (this.y < 0) {
return new Polar(len, factor * (2 * halfpi + a));
}
}
},
isOnLine: function (from, to) {
if (from.x > to.x) {
var temp = to;
to = from;
from = temp;
}
var r1 = new Rect(from.x, from.y).inflate(HITTESTAREA, HITTESTAREA), r2 = new Rect(to.x, to.y).inflate(HITTESTAREA, HITTESTAREA), o1, u1;
if (r1.union(r2).contains(this)) {
if (from.x === to.x || from.y === to.y) {
return true;
} else if (from.y < to.y) {
o1 = r1.x + (r2.x - r1.x) * (this.y - (r1.y + r1.height)) / (r2.y + r2.height - (r1.y + r1.height));
u1 = r1.x + r1.width + (r2.x + r2.width - (r1.x + r1.width)) * (this.y - r1.y) / (r2.y - r1.y);
} else {
o1 = r1.x + (r2.x - r1.x) * (this.y - r1.y) / (r2.y - r1.y);
u1 = r1.x + r1.width + (r2.x + r2.width - (r1.x + r1.width)) * (this.y - (r1.y + r1.height)) / (r2.y + r2.height - (r1.y + r1.height));
}
return this.x > o1 && this.x < u1;
}
return false;
}
});
deepExtend(Point, {
parse: function (str) {
var tempStr = str.slice(1, str.length - 1), xy = tempStr.split(','), x = parseInt(xy[0], 10), y = parseInt(xy[1], 10);
if (!isNaN(x) && !isNaN(y)) {
return new Point(x, y);
}
}
});
var PathDefiner = Class.extend({
init: function (p, left, right) {
this.point = p;
this.left = left;
this.right = right;
}
});
var Rect = Class.extend({
init: function (x, y, width, height) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 0;
this.height = height || 0;
},
contains: function (point) {
return point.x >= this.x && point.x <= this.x + this.width && point.y >= this.y && point.y <= this.y + this.height;
},
inflate: function (dx, dy) {
if (dy === undefined) {
dy = dx;
}
this.x -= dx;
this.y -= dy;
this.width += 2 * dx + 1;
this.height += 2 * dy + 1;
return this;
},
offset: function (dx, dy) {
var x = dx, y = dy;
if (dx instanceof Point) {
x = dx.x;
y = dx.y;
}
this.x += x;
this.y += y;
return this;
},
union: function (r) {
var x1 = Math.min(this.x, r.x);
var y1 = Math.min(this.y, r.y);
var x2 = Math.max(this.x + this.width, r.x + r.width);
var y2 = Math.max(this.y + this.height, r.y + r.height);
return new Rect(x1, y1, x2 - x1, y2 - y1);
},
center: function () {
return new Point(this.x + this.width / 2, this.y + this.height / 2);
},
top: function () {
return new Point(this.x + this.width / 2, this.y);
},
right: function () {
return new Point(this.x + this.width, this.y + this.height / 2);
},
bottom: function () {
return new Point(this.x + this.width / 2, this.y + this.height);
},
left: function () {
return new Point(this.x, this.y + this.height / 2);
},
topLeft: function () {
return new Point(this.x, this.y);
},
topRight: function () {
return new Point(this.x + this.width, this.y);
},
bottomLeft: function () {
return new Point(this.x, this.y + this.height);
},
bottomRight: function () {
return new Point(this.x + this.width, this.y + this.height);
},
clone: function () {
return new Rect(this.x, this.y, this.width, this.height);
},
isEmpty: function () {
return !this.width && !this.height;
},
equals: function (rect) {
return this.x === rect.x && this.y === rect.y && this.width === rect.width && this.height === rect.height;
},
rotatedBounds: function (angle) {
var rect = this.clone(), points = this.rotatedPoints(angle), tl = points[0], tr = points[1], br = points[2], bl = points[3];
rect.x = Math.min(br.x, tl.x, tr.x, bl.x);
rect.y = Math.min(br.y, tl.y, tr.y, bl.y);
rect.width = Math.max(br.x, tl.x, tr.x, bl.x) - rect.x;
rect.height = Math.max(br.y, tl.y, tr.y, bl.y) - rect.y;
return rect;
},
rotatedPoints: function (angle) {
var rect = this, c = rect.center(), br = rect.bottomRight().rotate(c, 360 - angle), tl = rect.topLeft().rotate(c, 360 - angle), tr = rect.topRight().rotate(c, 360 - angle), bl = rect.bottomLeft().rotate(c, 360 - angle);
return [
tl,
tr,
br,
bl
];
},
toString: function (delimiter) {
delimiter = delimiter || ' ';
return this.x + delimiter + this.y + delimiter + this.width + delimiter + this.height;
},
scale: function (scaleX, scaleY, staicPoint, adornerCenter, angle) {
var tl = this.topLeft();
var thisCenter = this.center();
tl.rotate(thisCenter, 360 - angle).rotate(adornerCenter, angle);
var delta = staicPoint.minus(tl);
var scaled = new Point(delta.x * scaleX, delta.y * scaleY);
var position = delta.minus(scaled);
tl = tl.plus(position);
tl.rotate(adornerCenter, 360 - angle).rotate(thisCenter, angle);
this.x = tl.x;
this.y = tl.y;
this.width *= scaleX;
this.height *= scaleY;
},
zoom: function (zoom) {
this.x *= zoom;
this.y *= zoom;
this.width *= zoom;
this.height *= zoom;
return this;
},
overlaps: function (rect) {
var bottomRight = this.bottomRight();
var rectBottomRight = rect.bottomRight();
var overlaps = !(bottomRight.x < rect.x || bottomRight.y < rect.y || rectBottomRight.x < this.x || rectBottomRight.y < this.y);
return overlaps;
}
});
var Size = Class.extend({
init: function (width, height) {
this.width = width;
this.height = height;
}
});
Size.prototype.Empty = new Size(0, 0);
Rect.toRect = function (rect) {
if (!(rect instanceof Rect)) {
rect = new Rect(rect.x, rect.y, rect.width, rect.height);
}
return rect;
};
Rect.empty = function () {
return new Rect(0, 0, 0, 0);
};
Rect.fromPoints = function (p, q) {
if (isNaN(p.x) || isNaN(p.y) || isNaN(q.x) || isNaN(q.y)) {
throw 'Some values are NaN.';
}
return new Rect(Math.min(p.x, q.x), Math.min(p.y, q.y), Math.abs(p.x - q.x), Math.abs(p.y - q.y));
};
function isNearZero(num) {
return Math.abs(num) < EPSILON;
}
function intersectLine(start1, end1, start2, end2, isSegment) {
var tangensdiff = (end1.x - start1.x) * (end2.y - start2.y) - (end1.y - start1.y) * (end2.x - start2.x);
if (isNearZero(tangensdiff)) {
return;
}
var num1 = (start1.y - start2.y) * (end2.x - start2.x) - (start1.x - start2.x) * (end2.y - start2.y);
var num2 = (start1.y - start2.y) * (end1.x - start1.x) - (start1.x - start2.x) * (end1.y - start1.y);
var r = num1 / tangensdiff;
var s = num2 / tangensdiff;
if (isSegment && (r < 0 || r > 1 || s < 0 || s > 1)) {
return;
}
return new Point(start1.x + r * (end1.x - start1.x), start1.y + r * (end1.y - start1.y));
}
var Intersect = {
lines: function (start1, end1, start2, end2) {
return intersectLine(start1, end1, start2, end2);
},
segments: function (start1, end1, start2, end2) {
return intersectLine(start1, end1, start2, end2, true);
},
rectWithLine: function (rect, start, end) {
return Intersect.segments(start, end, rect.topLeft(), rect.topRight()) || Intersect.segments(start, end, rect.topRight(), rect.bottomRight()) || Intersect.segments(start, end, rect.bottomLeft(), rect.bottomRight()) || Intersect.segments(start, end, rect.topLeft(), rect.bottomLeft());
},
rects: function (rect1, rect2, angle) {
var tl = rect2.topLeft(), tr = rect2.topRight(), bl = rect2.bottomLeft(), br = rect2.bottomRight();
var center = rect2.center();
if (angle) {
tl = tl.rotate(center, angle);
tr = tr.rotate(center, angle);
bl = bl.rotate(center, angle);
br = br.rotate(center, angle);
}
var intersect = rect1.contains(tl) || rect1.contains(tr) || rect1.contains(bl) || rect1.contains(br) || Intersect.rectWithLine(rect1, tl, tr) || Intersect.rectWithLine(rect1, tl, bl) || Intersect.rectWithLine(rect1, tr, br) || Intersect.rectWithLine(rect1, bl, br);
if (!intersect) {
tl = rect1.topLeft();
tr = rect1.topRight();
bl = rect1.bottomLeft();
br = rect1.bottomRight();
if (angle) {
var reverseAngle = 360 - angle;
tl = tl.rotate(center, reverseAngle);
tr = tr.rotate(center, reverseAngle);
bl = bl.rotate(center, reverseAngle);
br = br.rotate(center, reverseAngle);
}
intersect = rect2.contains(tl) || rect2.contains(tr) || rect2.contains(bl) || rect2.contains(br);
}
return intersect;
}
};
var RectAlign = Class.extend({
init: function (container) {
this.container = Rect.toRect(container);
},
align: function (content, alignment) {
var alignValues = alignment.toLowerCase().split(' ');
for (var i = 0; i < alignValues.length; i++) {
content = this._singleAlign(content, alignValues[i]);
}
return content;
},
_singleAlign: function (content, alignment) {
if (isFunction(this[alignment])) {
return this[alignment](content);
} else {
return content;
}
},
left: function (content) {
return this._align(content, this._left);
},
center: function (content) {
return this._align(content, this._center);
},
right: function (content) {
return this._align(content, this._right);
},
stretch: function (content) {
return this._align(content, this._stretch);
},
top: function (content) {
return this._align(content, this._top);
},
middle: function (content) {
return this._align(content, this._middle);
},
bottom: function (content) {
return this._align(content, this._bottom);
},
_left: function (container, content) {
content.x = container.x;
},
_center: function (container, content) {
content.x = (container.width - content.width) / 2 || 0;
},
_right: function (container, content) {
content.x = container.width - content.width;
},
_top: function (container, content) {
content.y = container.y;
},
_middle: function (container, content) {
content.y = (container.height - content.height) / 2 || 0;
},
_bottom: function (container, content) {
content.y = container.height - content.height;
},
_stretch: function (container, content) {
content.x = 0;
content.y = 0;
content.height = container.height;
content.width = container.width;
},
_align: function (content, alignCalc) {
content = Rect.toRect(content);
alignCalc(this.container, content);
return content;
}
});
var Polar = Class.extend({
init: function (r, a) {
this.r = r;
this.angle = a;
}
});
var Matrix = Class.extend({
init: function (a, b, c, d, e, f) {
this.a = a || 0;
this.b = b || 0;
this.c = c || 0;
this.d = d || 0;
this.e = e || 0;
this.f = f || 0;
},
plus: function (m) {
this.a += m.a;
this.b += m.b;
this.c += m.c;
this.d += m.d;
this.e += m.e;
this.f += m.f;
},
minus: function (m) {
this.a -= m.a;
this.b -= m.b;
this.c -= m.c;
this.d -= m.d;
this.e -= m.e;
this.f -= m.f;
},
times: function (m) {
return new Matrix(this.a * m.a + this.c * m.b, this.b * m.a + this.d * m.b, this.a * m.c + this.c * m.d, this.b * m.c + this.d * m.d, this.a * m.e + this.c * m.f + this.e, this.b * m.e + this.d * m.f + this.f);
},
apply: function (p) {
return new Point(this.a * p.x + this.c * p.y + this.e, this.b * p.x + this.d * p.y + this.f);
},
applyRect: function (r) {
return Rect.fromPoints(this.apply(r.topLeft()), this.apply(r.bottomRight()));
},
toString: function () {
return 'matrix(' + this.a + ' ' + this.b + ' ' + this.c + ' ' + this.d + ' ' + this.e + ' ' + this.f + ')';
}
});
deepExtend(Matrix, {
fromSVGMatrix: function (vm) {
var m = new Matrix();
m.a = vm.a;
m.b = vm.b;
m.c = vm.c;
m.d = vm.d;
m.e = vm.e;
m.f = vm.f;
return m;
},
fromMatrixVector: function (v) {
var m = new Matrix();
m.a = v.a;
m.b = v.b;
m.c = v.c;
m.d = v.d;
m.e = v.e;
m.f = v.f;
return m;
},
fromList: function (v) {
if (v.length !== 6) {
throw 'The given list should consist of six elements.';
}
var m = new Matrix();
m.a = v[0];
m.b = v[1];
m.c = v[2];
m.d = v[3];
m.e = v[4];
m.f = v[5];
return m;
},
translation: function (x, y) {
var m = new Matrix();
m.a = 1;
m.b = 0;
m.c = 0;
m.d = 1;
m.e = x;
m.f = y;
return m;
},
unit: function () {
return new Matrix(1, 0, 0, 1, 0, 0);
},
rotation: function (angle, x, y) {
var m = new Matrix();
m.a = Math.cos(angle * Math.PI / 180);
m.b = Math.sin(angle * Math.PI / 180);
m.c = -m.b;
m.d = m.a;
m.e = x - x * m.a + y * m.b || 0;
m.f = y - y * m.a - x * m.b || 0;
return m;
},
scaling: function (scaleX, scaleY) {
var m = new Matrix();
m.a = scaleX;
m.b = 0;
m.c = 0;
m.d = scaleY;
m.e = 0;
m.f = 0;
return m;
},
parse: function (v) {
var parts, nums;
if (v) {
v = v.trim();
if (v.slice(0, 6).toLowerCase() === 'matrix') {
nums = v.slice(7, v.length - 1).trim();
parts = nums.split(',');
if (parts.length === 6) {
return Matrix.fromList(map(parts, function (p) {
return parseFloat(p);
}));
}
parts = nums.split(' ');
if (parts.length === 6) {
return Matrix.fromList(map(parts, function (p) {
return parseFloat(p);
}));
}
}
if (v.slice(0, 1) === '(' && v.slice(v.length - 1) === ')') {
v = v.substr(1, v.length - 1);
}
if (v.indexOf(',') > 0) {
parts = v.split(',');
if (parts.length === 6) {
return Matrix.fromList(map(parts, function (p) {
return parseFloat(p);
}));
}
}
if (v.indexOf(' ') > 0) {
parts = v.split(' ');
if (parts.length === 6) {
return Matrix.fromList(map(parts, function (p) {
return parseFloat(p);
}));
}
}
}
return parts;
}
});
var MatrixVector = Class.extend({
init: function (a, b, c, d, e, f) {
this.a = a || 0;
this.b = b || 0;
this.c = c || 0;
this.d = d || 0;
this.e = e || 0;
this.f = f || 0;
},
fromMatrix: function FromMatrix(m) {
var v = new MatrixVector();
v.a = m.a;
v.b = m.b;
v.c = m.c;
v.d = m.d;
v.e = m.e;
v.f = m.f;
return v;
}
});
function normalVariable(mean, deviation) {
var x, y, r;
do {
x = Math.random() * 2 - 1;
y = Math.random() * 2 - 1;
r = x * x + y * y;
} while (!r || r > 1);
return mean + deviation * x * Math.sqrt(-2 * Math.log(r) / r);
}
function randomId(length) {
if (Utils.isUndefined(length)) {
length = 10;
}
var result = '';
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var i = length; i > 0; --i) {
result += chars.charAt(Math.round(Math.random() * (chars.length - 1)));
}
return result;
}
var Geometry = {
_distanceToLineSquared: function (p, a, b) {
function d2(pt1, pt2) {
return (pt1.x - pt2.x) * (pt1.x - pt2.x) + (pt1.y - pt2.y) * (pt1.y - pt2.y);
}
if (a === b) {
return d2(p, a);
}
var vx = b.x - a.x, vy = b.y - a.y, dot = (p.x - a.x) * vx + (p.y - a.y) * vy;
if (dot < 0) {
return d2(a, p);
}
dot = (b.x - p.x) * vx + (b.y - p.y) * vy;
if (dot < 0) {
return d2(b, p);
}
dot = (b.x - p.x) * vy - (b.y - p.y) * vx;
return dot * dot / (vx * vx + vy * vy);
},
distanceToLine: function (p, a, b) {
return Math.sqrt(this._distanceToLineSquared(p, a, b));
},
distanceToPolyline: function (p, points) {
var minimum = Number.MAX_VALUE;
if (Utils.isUndefined(points) || points.length === 0) {
return Number.MAX_VALUE;
}
for (var s = 0; s < points.length - 1; s++) {
var p1 = points[s];
var p2 = points[s + 1];
var d = this._distanceToLineSquared(p, p1, p2);
if (d < minimum) {
minimum = d;
}
}
return Math.sqrt(minimum);
}
};
var HashTable = kendo.Class.extend({
init: function () {
this._buckets = [];
this.length = 0;
},
add: function (key, value) {
var obj = this._createGetBucket(key);
if (Utils.isDefined(value)) {
obj.value = value;
}
return obj;
},
get: function (key) {
if (this._bucketExists(key)) {
return this._createGetBucket(key);
}
return null;
},
set: function (key, value) {
this.add(key, value);
},
containsKey: function (key) {
return this._bucketExists(key);
},
remove: function (key) {
if (this._bucketExists(key)) {
var hashId = this._hash(key);
delete this._buckets[hashId];
this.length--;
return key;
}
},
forEach: function (func) {
var hashes = this._hashes();
for (var i = 0, len = hashes.length; i < len; i++) {
var hash = hashes[i];
var bucket = this._buckets[hash];
if (Utils.isUndefined(bucket)) {
continue;
}
func(bucket);
}
},
clone: function () {
var ht = new HashTable();
var hashes = this._hashes();
for (var i = 0, len = hashes.length; i < len; i++) {
var hash = hashes[i];
var bucket = this._buckets[hash];
if (Utils.isUndefined(bucket)) {
continue;
}
ht.add(bucket.key, bucket.value);
}
return ht;
},
_hashes: function () {
var hashes = [];
for (var hash in this._buckets) {
if (this._buckets.hasOwnProperty(hash)) {
hashes.push(hash);
}
}
return hashes;
},
_bucketExists: function (key) {
var hashId = this._hash(key);
return Utils.isDefined(this._buckets[hashId]);
},
_createGetBucket: function (key) {
var hashId = this._hash(key);
var bucket = this._buckets[hashId];
if (Utils.isUndefined(bucket)) {
bucket = { key: key };
this._buckets[hashId] = bucket;
this.length++;
}
return bucket;
},
_hash: function (key) {
if (Utils.isNumber(key)) {
return key;
}
if (Utils.isString(key)) {
return this._hashString(key);
}
if (Utils.isObject(key)) {
return this._objectHashId(key);
}
throw 'Unsupported key type.';
},
_hashString: function (s) {
var result = 0;
if (s.length === 0) {
return result;
}
for (var i = 0; i < s.length; i++) {
var ch = s.charCodeAt(i);
result = result * 32 - result + ch;
}
return result;
},
_objectHashId: function (key) {
var id = key._hashId;
if (Utils.isUndefined(id)) {
id = randomId();
key._hashId = id;
}
return id;
}
});
var Dictionary = kendo.Observable.extend({
init: function (dictionary) {
var that = this;
kendo.Observable.fn.init.call(that);
this._hashTable = new HashTable();
this.length = 0;
if (Utils.isDefined(dictionary)) {
if ($.isArray(dictionary)) {
for (var i = 0; i < dictionary.length; i++) {
this.add(dictionary[i]);
}
} else {
dictionary.forEach(function (k, v) {
this.add(k, v);
}, this);
}
}
},
add: function (key, value) {
var entry = this._hashTable.get(key);
if (!entry) {
entry = this._hashTable.add(key);
this.length++;
this.trigger('changed');
}
entry.value = value;
},
set: function (key, value) {
this.add(key, value);
},
get: function (key) {
var entry = this._hashTable.get(key);
if (entry) {
return entry.value;
}
throw new Error('Cannot find key ' + key);
},
containsKey: function (key) {
return this._hashTable.containsKey(key);
},
remove: function (key) {
if (this.containsKey(key)) {
this.trigger('changed');
this.length--;
return this._hashTable.remove(key);
}
},
forEach: function (func, thisRef) {
this._hashTable.forEach(function (entry) {
func.call(thisRef, entry.key, entry.value);
});
},
forEachValue: function (func, thisRef) {
this._hashTable.forEach(function (entry) {
func.call(thisRef, entry.value);
});
},
forEachKey: function (func, thisRef) {
this._hashTable.forEach(function (entry) {
func.call(thisRef, entry.key);
});
},
keys: function () {
var keys = [];
this.forEachKey(function (key) {
keys.push(key);
});
return keys;
}
});
var Queue = kendo.Class.extend({
init: function () {
this._tail = null;
this._head = null;
this.length = 0;
},
enqueue: function (value) {
var entry = {
value: value,
next: null
};
if (!this._head) {
this._head = entry;
this._tail = this._head;
} else {
this._tail.next = entry;
this._tail = this._tail.next;
}
this.length++;
},
dequeue: function () {
if (this.length < 1) {
throw new Error('The queue is empty.');
}
var value = this._head.value;
this._head = this._head.next;
this.length--;
return value;
},
contains: function (item) {
var current = this._head;
while (current) {
if (current.value === item) {
return true;
}
current = current.next;
}
return false;
}
});
var Set = kendo.Observable.extend({
init: function (resource) {
var that = this;
kendo.Observable.fn.init.call(that);
this._hashTable = new HashTable();
this.length = 0;
if (Utils.isDefined(resource)) {
if (resource instanceof HashTable) {
resource.forEach(function (d) {
this.add(d);
});
} else if (resource instanceof Dictionary) {
resource.forEach(function (k, v) {
this.add({
key: k,
value: v
});
}, this);
}
}
},
contains: function (item) {
return this._hashTable.containsKey(item);
},
add: function (item) {
var entry = this._hashTable.get(item);
if (!entry) {
this._hashTable.add(item, item);
this.length++;
this.trigger('changed');
}
},
get: function (item) {
if (this.contains(item)) {
return this._hashTable.get(item).value;
} else {
return null;
}
},
hash: function (item) {
return this._hashTable._hash(item);
},
remove: function (item) {
if (this.contains(item)) {
this._hashTable.remove(item);
this.length--;
this.trigger('changed');
}
},
forEach: function (func, context) {
this._hashTable.forEach(function (kv) {
func(kv.value);
}, context);
},
toArray: function () {
var r = [];
this.forEach(function (d) {
r.push(d);
});
return r;
}
});
var Node = kendo.Class.extend({
init: function (id, shape) {
this.links = [];
this.outgoing = [];
this.incoming = [];
this.weight = 1;
if (Utils.isDefined(id)) {
this.id = id;
} else {
this.id = randomId();
}
if (Utils.isDefined(shape)) {
this.associatedShape = shape;
var b = shape.bounds();
this.width = b.width;
this.height = b.height;
this.x = b.x;
this.y = b.y;
} else {
this.associatedShape = null;
}
this.data = null;
this.type = 'Node';
this.shortForm = 'Node \'' + this.id + '\'';
this.isVirtual = false;
},
isIsolated: function () {
return Utils.isEmpty(this.links);
},
bounds: function (r) {
if (!Utils.isDefined(r)) {
return new diagram.Rect(this.x, this.y, this.width, this.height);
}
this.x = r.x;
this.y = r.y;
this.width = r.width;
this.height = r.height;
},
isLinkedTo: function (node) {
var that = this;
return Utils.any(that.links, function (link) {
return link.getComplement(that) === node;
});
},
getChildren: function () {
if (this.outgoing.length === 0) {
return [];
}
var children = [];
for (var i = 0, len = this.outgoing.length; i < len; i++) {
var link = this.outgoing[i];
children.push(link.getComplement(this));
}
return children;
},
getParents: function () {
if (this.incoming.length === 0) {
return [];
}
var parents = [];
for (var i = 0, len = this.incoming.length; i < len; i++) {
var link = this.incoming[i];
parents.push(link.getComplement(this));
}
return parents;
},
clone: function () {
var copy = new Node();
if (Utils.isDefined(this.weight)) {
copy.weight = this.weight;
}
if (Utils.isDefined(this.balance)) {
copy.balance = this.balance;
}
if (Utils.isDefined(this.owner)) {
copy.owner = this.owner;
}
copy.associatedShape = this.associatedShape;
copy.x = this.x;
copy.y = this.y;
copy.width = this.width;
copy.height = this.height;
return copy;
},
adjacentTo: function (node) {
return this.isLinkedTo(node) !== null;
},
removeLink: function (link) {
if (link.source === this) {
Utils.remove(this.links, link);
Utils.remove(this.outgoing, link);
link.source = null;
}
if (link.target === this) {
Utils.remove(this.links, link);
Utils.remove(this.incoming, link);
link.target = null;
}
},
hasLinkTo: function (node) {
return Utils.any(this.outgoing, function (link) {
return link.target === node;
});
},
degree: function () {
return this.links.length;
},
incidentWith: function (link) {
return contains(this.links, link);
},
getLinksWith: function (node) {
return Utils.all(this.links, function (link) {
return link.getComplement(this) === node;
}, this);
},
getNeighbors: function () {
var neighbors = [];
Utils.forEach(this.incoming, function (e) {
neighbors.push(e.getComplement(this));
}, this);
Utils.forEach(this.outgoing, function (e) {
neighbors.push(e.getComplement(this));
}, this);
return neighbors;
}
});
var Link = kendo.Class.extend({
init: function (source, target, id, connection) {
if (Utils.isUndefined(source)) {
throw 'The source of the new link is not set.';
}
if (Utils.isUndefined(target)) {
throw 'The target of the new link is not set.';
}
var sourceFound, targetFound;
if (Utils.isString(source)) {
sourceFound = new Node(source);
} else {
sourceFound = source;
}
if (Utils.isString(target)) {
targetFound = new Node(target);
} else {
targetFound = target;
}
this.source = sourceFound;
this.target = targetFound;
this.source.links.push(this);
this.target.links.push(this);
this.source.outgoing.push(this);
this.target.incoming.push(this);
if (Utils.isDefined(id)) {
this.id = id;
} else {
this.id = randomId();
}
if (Utils.isDefined(connection)) {
this.associatedConnection = connection;
} else {
this.associatedConnection = null;
}
this.type = 'Link';
this.shortForm = 'Link \'' + this.source.id + '->' + this.target.id + '\'';
},
getComplement: function (node) {
if (this.source !== node && this.target !== node) {
throw 'The given node is not incident with this link.';
}
return this.source === node ? this.target : this.source;
},
getCommonNode: function (link) {
if (this.source === link.source || this.source === link.target) {
return this.source;
}
if (this.target === link.source || this.target === link.target) {
return this.target;
}
return null;
},
isBridging: function (v1, v2) {
return this.source === v1 && this.target === v2 || this.source === v2 && this.target === v1;
},
getNodes: function () {
return [
this.source,
this.target
];
},
incidentWith: function (node) {
return this.source === node || this.target === node;
},
adjacentTo: function (link) {
return contains(this.source.links, link) || contains(this.target.links, link);
},
changeSource: function (node) {
Utils.remove(this.source.links, this);
Utils.remove(this.source.outgoing, this);
node.links.push(this);
node.outgoing.push(this);
this.source = node;
},
changeTarget: function (node) {
Utils.remove(this.target.links, this);
Utils.remove(this.target.incoming, this);
node.links.push(this);
node.incoming.push(this);
this.target = node;
},
changesNodes: function (v, w) {
if (this.source === v) {
this.changeSource(w);
} else if (this.target === v) {
this.changeTarget(w);
}
},
reverse: function () {
var oldSource = this.source;
var oldTarget = this.target;
this.source = oldTarget;
Utils.remove(oldSource.outgoing, this);
this.source.outgoing.push(this);
this.target = oldSource;
Utils.remove(oldTarget.incoming, this);
this.target.incoming.push(this);
return this;
},
directTo: function (target) {
if (this.source !== target && this.target !== target) {
throw 'The given node is not incident with this link.';
}
if (this.target !== target) {
this.reverse();
}
},
createReverseEdge: function () {
var r = this.clone();
r.reverse();
r.reversed = true;
return r;
},
clone: function () {
var clone = new Link(this.source, this.target);
return clone;
}
});
var Graph = kendo.Class.extend({
init: function (idOrDiagram) {
this.links = [];
this.nodes = [];
this._nodeMap = new Dictionary();
this.diagram = null;
this._root = null;
if (Utils.isDefined(idOrDiagram)) {
if (Utils.isString(idOrDiagram)) {
this.id = idOrDiagram;
} else {
this.diagram = idOrDiagram;
this.id = idOrDiagram.id;
}
} else {
this.id = randomId();
}
this.bounds = new Rect();
this._hasCachedRelationships = false;
this.type = 'Graph';
},
cacheRelationships: function (forceRebuild) {
if (Utils.isUndefined(forceRebuild)) {
forceRebuild = false;
}
if (this._hasCachedRelationships && !forceRebuild) {
return;
}
for (var i = 0, len = this.nodes.length; i < len; i++) {
var node = this.nodes[i];
node.children = this.getChildren(node);
node.parents = this.getParents(node);
}
this._hasCachedRelationships = true;
},
assignLevels: function (startNode, offset, visited) {
if (!startNode) {
throw 'Start node not specified.';
}
if (Utils.isUndefined(offset)) {
offset = 0;
}
this.cacheRelationships();
if (Utils.isUndefined(visited)) {
visited = new Dictionary();
Utils.forEach(this.nodes, function (n) {
visited.add(n, false);
});
}
visited.set(startNode, true);
startNode.level = offset;
var children = startNode.children;
for (var i = 0, len = children.length; i < len; i++) {
var child = children[i];
if (!child || visited.get(child)) {
continue;
}
this.assignLevels(child, offset + 1, visited);
}
},
root: function (value) {
if (Utils.isUndefined(value)) {
if (!this._root) {
var found = Utils.first(this.nodes, function (n) {
return n.incoming.length === 0;
});
if (found) {
return found;
}
return Utils.first(this.nodes);
} else {
return this._root;
}
} else {
this._root = value;
}
},
getConnectedComponents: function () {
this.componentIndex = 0;
this.setItemIndices();
var componentId = Utils.initArray(this.nodes.length, -1);
for (var v = 0; v < this.nodes.length; v++) {
if (componentId[v] === -1) {
this._collectConnectedNodes(componentId, v);
this.componentIndex++;
}
}
var components = [], i;
for (i = 0; i < this.componentIndex; ++i) {
components[i] = new Graph();
}
for (i = 0; i < componentId.length; ++i) {
var graph = components[componentId[i]];
graph.addNodeAndOutgoings(this.nodes[i]);
}
components.sort(function (a, b) {
return b.nodes.length - a.nodes.length;
});
return components;
},
_collectConnectedNodes: function (setIds, nodeIndex) {
setIds[nodeIndex] = this.componentIndex;
var node = this.nodes[nodeIndex];
Utils.forEach(node.links, function (link) {
var next = link.getComplement(node);
var nextId = next.index;
if (setIds[nextId] === -1) {
this._collectConnectedNodes(setIds, nextId);
}
}, this);
},
calcBounds: function () {
if (this.isEmpty()) {
this.bounds = new Rect();
return this.bounds;
}
var b = null;
for (var i = 0, len = this.nodes.length; i < len; i++) {
var node = this.nodes[i];
if (!b) {
b = node.bounds();
} else {
b = b.union(node.bounds());
}
}
this.bounds = b;
return this.bounds;
},
getSpanningTree: function (root) {
var tree = new Graph();
var map = new Dictionary(), source, target;
tree.root = root.clone();
tree.root.level = 0;
tree.root.id = root.id;
map.add(root, tree.root);
root.level = 0;
var visited = [];
var remaining = [];
tree._addNode(tree.root);
visited.push(root);
remaining.push(root);
var levelCount = 1;
while (remaining.length > 0) {
var next = remaining.pop();
for (var ni = 0; ni < next.links.length; ni++) {
var link = next.links[ni];
var cn = link.getComplement(next);
if (contains(visited, cn)) {
continue;
}
cn.level = next.level + 1;
if (levelCount < cn.level + 1) {
levelCount = cn.level + 1;
}
if (!contains(remaining, cn)) {
remaining.push(cn);
}
if (!contains(visited, cn)) {
visited.push(cn);
}
if (map.containsKey(next)) {
source = map.get(next);
} else {
source = next.clone();
source.level = next.level;
source.id = next.id;
map.add(next, source);
}
if (map.containsKey(cn)) {
target = map.get(cn);
} else {
target = cn.clone();
target.level = cn.level;
target.id = cn.id;
map.add(cn, target);
}
var newLink = new Link(source, target);
tree.addLink(newLink);
}
}
var treeLevels = [];
for (var i = 0; i < levelCount; i++) {
treeLevels.push([]);
}
Utils.forEach(tree.nodes, function (node) {
treeLevels[node.level].push(node);
});
tree.treeLevels = treeLevels;
tree.cacheRelationships();
return tree;
},
takeRandomNode: function (excludedNodes, incidenceLessThan) {
if (Utils.isUndefined(excludedNodes)) {
excludedNodes = [];
}
if (Utils.isUndefined(incidenceLessThan)) {
incidenceLessThan = 4;
}
if (this.nodes.length === 0) {
return null;
}
if (this.nodes.length === 1) {
return contains(excludedNodes, this.nodes[0]) ? null : this.nodes[0];
}
var pool = $.grep(this.nodes, function (node) {
return !contains(excludedNodes, node) && node.degree() <= incidenceLessThan;
});
if (Utils.isEmpty(pool)) {
return null;
}
return pool[Utils.randomInteger(0, pool.length)];
},
isEmpty: function () {
return Utils.isEmpty(this.nodes);
},
isHealthy: function () {
return Utils.all(this.links, function (link) {
return contains(this.nodes, link.source) && contains(this.nodes, link.target);
}, this);
},
getParents: function (n) {
if (!this.hasNode(n)) {
throw 'The given node is not part of this graph.';
}
return n.getParents();
},
getChildren: function (n) {
if (!this.hasNode(n)) {
throw 'The given node is not part of this graph.';
}
return n.getChildren();
},
addLink: function (sourceOrLink, target, owner) {
if (Utils.isUndefined(sourceOrLink)) {
throw 'The source of the link is not defined.';
}
if (Utils.isUndefined(target)) {
if (Utils.isDefined(sourceOrLink.type) && sourceOrLink.type === 'Link') {
this.addExistingLink(sourceOrLink);
return;
} else {
throw 'The target of the link is not defined.';
}
}
var foundSource = this.getNode(sourceOrLink);
if (Utils.isUndefined(foundSource)) {
foundSource = this.addNode(sourceOrLink);
}
var foundTarget = this.getNode(target);
if (Utils.isUndefined(foundTarget)) {
foundTarget = this.addNode(target);
}
var newLink = new Link(foundSource, foundTarget);
if (Utils.isDefined(owner)) {
newLink.owner = owner;
}
this.links.push(newLink);
return newLink;
},
removeAllLinks: function () {
while (this.links.length > 0) {
var link = this.links[0];
this.removeLink(link);
}
},
addExistingLink: function (link) {
if (this.hasLink(link)) {
return;
}
this.links.push(link);
if (this.hasNode(link.source.id)) {
var s = this.getNode(link.source.id);
link.changeSource(s);
} else {
this.addNode(link.source);
}
if (this.hasNode(link.target.id)) {
var t = this.getNode(link.target.id);
link.changeTarget(t);
} else {
this.addNode(link.target);
}
},
hasLink: function (linkOrId) {
if (Utils.isString(linkOrId)) {
return Utils.any(this.links, function (link) {
return link.id === linkOrId;
});
}
if (linkOrId.type === 'Link') {
return contains(this.links, linkOrId);
}
throw 'The given object is neither an identifier nor a Link.';
},
getNode: function (nodeOrId) {
var id = nodeOrId.id || nodeOrId;
if (this._nodeMap.containsKey(id)) {
return this._nodeMap.get(id);
}
},
hasNode: function (nodeOrId) {
var id = nodeOrId.id || nodeOrId;
return this._nodeMap.containsKey(id);
},
_addNode: function (node) {
this.nodes.push(node);
this._nodeMap.add(node.id, node);
},
_removeNode: function (node) {
Utils.remove(this.nodes, node);
this._nodeMap.remove(node.id);
},
removeNode: function (nodeOrId) {
var n = nodeOrId;
if (Utils.isString(nodeOrId)) {
n = this.getNode(nodeOrId);
}
if (Utils.isDefined(n)) {
var links = n.links;
n.links = [];
for (var i = 0, len = links.length; i < len; i++) {
var link = links[i];
this.removeLink(link);
}
this._removeNode(n);
} else {
throw 'The identifier should be a Node or the Id (string) of a node.';
}
},
areConnected: function (n1, n2) {
return Utils.any(this.links, function (link) {
return link.source == n1 && link.target == n2 || link.source == n2 && link.target == n1;
});
},
removeLink: function (link) {
Utils.remove(this.links, link);
Utils.remove(link.source.outgoing, link);
Utils.remove(link.source.links, link);
Utils.remove(link.target.incoming, link);
Utils.remove(link.target.links, link);
},
addNode: function (nodeOrId, layoutRect, owner) {
var newNode = null;
if (!Utils.isDefined(nodeOrId)) {
throw 'No Node or identifier for a new Node is given.';
}
if (Utils.isString(nodeOrId)) {
if (this.hasNode(nodeOrId)) {
return this.getNode(nodeOrId);
}
newNode = new Node(nodeOrId);
} else {
if (this.hasNode(nodeOrId)) {
return this.getNode(nodeOrId);
}
newNode = nodeOrId;
}
if (Utils.isDefined(layoutRect)) {
newNode.bounds(layoutRect);
}
if (Utils.isDefined(owner)) {
newNode.owner = owner;
}
this._addNode(newNode);
return newNode;
},
addNodeAndOutgoings: function (node) {
if (!this.hasNode(node)) {
this._addNode(node);
}
var newLinks = node.outgoing;
node.outgoing = [];
Utils.forEach(newLinks, function (link) {
this.addExistingLink(link);
}, this);
},
setItemIndices: function () {
var i;
for (i = 0; i < this.nodes.length; ++i) {
this.nodes[i].index = i;
}
for (i = 0; i < this.links.length; ++i) {
this.links[i].index = i;
}
},
clone: function (saveMapping) {
var copy = new Graph();
var save = Utils.isDefined(saveMapping) && saveMapping === true;
if (save) {
copy.nodeMap = new Dictionary();
copy.linkMap = new Dictionary();
}
var map = new Dictionary();
Utils.forEach(this.nodes, function (nOriginal) {
var nCopy = nOriginal.clone();
map.set(nOriginal, nCopy);
copy._addNode(nCopy);
if (save) {
copy.nodeMap.set(nCopy, nOriginal);
}
});
Utils.forEach(this.links, function (linkOriginal) {
if (map.containsKey(linkOriginal.source) && map.containsKey(linkOriginal.target)) {
var linkCopy = copy.addLink(map.get(linkOriginal.source), map.get(linkOriginal.target));
if (save) {
copy.linkMap.set(linkCopy, linkOriginal);
}
}
});
return copy;
},
linearize: function (addIds) {
return Graph.Utils.linearize(this, addIds);
},
depthFirstTraversal: function (startNode, action) {
if (Utils.isUndefined(startNode)) {
throw 'You need to supply a starting node.';
}
if (Utils.isUndefined(action)) {
throw 'You need to supply an action.';
}
if (!this.hasNode(startNode)) {
throw 'The given start-node is not part of this graph';
}
var foundNode = this.getNode(startNode);
var visited = [];
this._dftIterator(foundNode, action, visited);
},
_dftIterator: function (node, action, visited) {
action(node);
visited.push(node);
var children = node.getChildren();
for (var i = 0, len = children.length; i < len; i++) {
var child = children[i];
if (contains(visited, child)) {
continue;
}
this._dftIterator(child, action, visited);
}
},
breadthFirstTraversal: function (startNode, action) {
if (Utils.isUndefined(startNode)) {
throw 'You need to supply a starting node.';
}
if (Utils.isUndefined(action)) {
throw 'You need to supply an action.';
}
if (!this.hasNode(startNode)) {
throw 'The given start-node is not part of this graph';
}
var foundNode = this.getNode(startNode);
var queue = new Queue();
var visited = [];
queue.enqueue(foundNode);
while (queue.length > 0) {
var node = queue.dequeue();
action(node);
visited.push(node);
var children = node.getChildren();
for (var i = 0, len = children.length; i < len; i++) {
var child = children[i];
if (contains(visited, child) || contains(queue, child)) {
continue;
}
queue.enqueue(child);
}
}
},
_stronglyConnectedComponents: function (excludeSingleItems, node, indices, lowLinks, connected, stack, index) {
indices.add(node, index);
lowLinks.add(node, index);
index++;
stack.push(node);
var children = node.getChildren(), next;
for (var i = 0, len = children.length; i < len; i++) {
next = children[i];
if (!indices.containsKey(next)) {
this._stronglyConnectedComponents(excludeSingleItems, next, indices, lowLinks, connected, stack, index);
lowLinks.add(node, Math.min(lowLinks.get(node), lowLinks.get(next)));
} else if (contains(stack, next)) {
lowLinks.add(node, Math.min(lowLinks.get(node), indices.get(next)));
}
}
if (lowLinks.get(node) === indices.get(node)) {
var component = [];
do {
next = stack.pop();
component.push(next);
} while (next !== node);
if (!excludeSingleItems || component.length > 1) {
connected.push(component);
}
}
},
findCycles: function (excludeSingleItems) {
if (Utils.isUndefined(excludeSingleItems)) {
excludeSingleItems = true;
}
var indices = new Dictionary();
var lowLinks = new Dictionary();
var connected = [];
var stack = [];
for (var i = 0, len = this.nodes.length; i < len; i++) {
var node = this.nodes[i];
if (indices.containsKey(node)) {
continue;
}
this._stronglyConnectedComponents(excludeSingleItems, node, indices, lowLinks, connected, stack, 0);
}
return connected;
},
isAcyclic: function () {
return Utils.isEmpty(this.findCycles());
},
isSubGraph: function (other) {
var otherArray = other.linearize();
var thisArray = this.linearize();
return Utils.all(otherArray, function (s) {
return contains(thisArray, s);
});
},
makeAcyclic: function () {
if (this.isEmpty() || this.nodes.length <= 1 || this.links.length <= 1) {
return [];
}
if (this.nodes.length == 2) {
var result = [];
if (this.links.length > 1) {
var oneLink = this.links[0];
var oneNode = oneLink.source;
for (var i = 0, len = this.links.length; i < len; i++) {
var link = this.links[i];
if (link.source == oneNode) {
continue;
}
var rev = link.reverse();
result.push(rev);
}
}
return result;
}
var copy = this.clone(true);
var N = this.nodes.length;
var intensityCatalog = new Dictionary();
var flowIntensity = function (node) {
if (node.outgoing.length === 0) {
return 2 - N;
} else if (node.incoming.length === 0) {
return N - 2;
} else {
return node.outgoing.length - node.incoming.length;
}
};
var catalogEqualIntensity = function (node, intensityCatalog) {
var intensity = flowIntensity(node, N);
if (!intensityCatalog.containsKey(intensity)) {
intensityCatalog.set(intensity, []);
}
intensityCatalog.get(intensity).push(node);
};
Utils.forEach(copy.nodes, function (v) {
catalogEqualIntensity(v, intensityCatalog);
});
var sourceStack = [];
var targetStack = [];
while (copy.nodes.length > 0) {
var source, target, intensity;
if (intensityCatalog.containsKey(2 - N)) {
var targets = intensityCatalog.get(2 - N);
while (targets.length > 0) {
target = targets.pop();
for (var li = 0; li < target.links.length; li++) {
var targetLink = target.links[li];
source = targetLink.getComplement(target);
intensity = flowIntensity(source, N);
Utils.remove(intensityCatalog.get(intensity), source);
source.removeLink(targetLink);
catalogEqualIntensity(source, intensityCatalog);
}
copy._removeNode(target);
targetStack.unshift(target);
}
}
if (intensityCatalog.containsKey(N - 2)) {
var sources = intensityCatalog.get(N - 2);
while (sources.length > 0) {
source = sources.pop();
for (var si = 0; si < source.links.length; si++) {
var sourceLink = source.links[si];
target = sourceLink.getComplement(source);
intensity = flowIntensity(target, N);
Utils.remove(intensityCatalog.get(intensity), target);
target.removeLink(sourceLink);
catalogEqualIntensity(target, intensityCatalog);
}
sourceStack.push(source);
copy._removeNode(source);
}
}
if (copy.nodes.length > 0) {
for (var k = N - 3; k > 2 - N; k--) {
if (intensityCatalog.containsKey(k) && intensityCatalog.get(k).length > 0) {
var maxdiff = intensityCatalog.get(k);
var v = maxdiff.pop();
for (var ri = 0; ri < v.links.length; ri++) {
var ril = v.links[ri];
var u = ril.getComplement(v);
intensity = flowIntensity(u, N);
Utils.remove(intensityCatalog.get(intensity), u);
u.removeLink(ril);
catalogEqualIntensity(u, intensityCatalog);
}
sourceStack.push(v);
copy._removeNode(v);
break;
}
}
}
}
sourceStack = sourceStack.concat(targetStack);
var vertexOrder = new Dictionary();
for (var kk = 0; kk < this.nodes.length; kk++) {
vertexOrder.set(copy.nodeMap.get(sourceStack[kk]), kk);
}
var reversedEdges = [];
Utils.forEach(this.links, function (link) {
if (vertexOrder.get(link.source) > vertexOrder.get(link.target)) {
link.reverse();
reversedEdges.push(link);
}
});
return reversedEdges;
}
});
Graph.Predefined = {
EightGraph: function () {
return Graph.Utils.parse([
'1->2',
'2->3',
'3->4',
'4->1',
'3->5',
'5->6',
'6->7',
'7->3'
]);
},
Mindmap: function () {
return Graph.Utils.parse([
'0->1',
'0->2',
'0->3',
'0->4',
'0->5',
'1->6',
'1->7',
'7->8',
'2->9',
'9->10',
'9->11',
'3->12',
'12->13',
'13->14',
'4->15',
'4->16',
'15->17',
'15->18',
'18->19',
'18->20',
'14->21',
'14->22',
'5->23',
'23->24',
'23->25',
'6->26'
]);
},
ThreeGraph: function () {
return Graph.Utils.parse([
'1->2',
'2->3',
'3->1'
]);
},
BinaryTree: function (levels) {
if (Utils.isUndefined(levels)) {
levels = 5;
}
return Graph.Utils.createBalancedTree(levels, 2);
},
Linear: function (length) {
if (Utils.isUndefined(length)) {
length = 10;
}
return Graph.Utils.createBalancedTree(length, 1);
},
Tree: function (levels, siblingsCount) {
return Graph.Utils.createBalancedTree(levels, siblingsCount);
},
Forest: function (levels, siblingsCount, trees) {
return Graph.Utils.createBalancedForest(levels, siblingsCount, trees);
},
Workflow: function () {
return Graph.Utils.parse([
'0->1',
'1->2',
'2->3',
'1->4',
'4->3',
'3->5',
'5->6',
'6->3',
'6->7',
'5->4'
]);
},
Grid: function (n, m) {
var g = new diagram.Graph();
if (n <= 0 && m <= 0) {
return g;
}
for (var i = 0; i < n + 1; i++) {
var previous = null;
for (var j = 0; j < m + 1; j++) {
var node = new Node(i.toString() + '.' + j.toString());
g.addNode(node);
if (previous) {
g.addLink(previous, node);
}
if (i > 0) {
var left = g.getNode((i - 1).toString() + '.' + j.toString());
g.addLink(left, node);
}
previous = node;
}
}
return g;
}
};
Graph.Utils = {
parse: function (graphString) {
var previousLink, graph = new diagram.Graph(), parts = graphString.slice();
for (var i = 0, len = parts.length; i < len; i++) {
var part = parts[i];
if (Utils.isString(part)) {
if (part.indexOf('->') < 0) {
throw 'The link should be specified as \'a->b\'.';
}
var p = part.split('->');
if (p.length != 2) {
throw 'The link should be specified as \'a->b\'.';
}
previousLink = new Link(p[0], p[1]);
graph.addLink(previousLink);
}
if (Utils.isObject(part)) {
if (!previousLink) {
throw 'Specification found before Link definition.';
}
kendo.deepExtend(previousLink, part);
}
}
return graph;
},
linearize: function (graph, addIds) {
if (Utils.isUndefined(graph)) {
throw 'Expected an instance of a Graph object in slot one.';
}
if (Utils.isUndefined(addIds)) {
addIds = false;
}
var lin = [];
for (var i = 0, len = graph.links.length; i < len; i++) {
var link = graph.links[i];
lin.push(link.source.id + '->' + link.target.id);
if (addIds) {
lin.push({ id: link.id });
}
}
return lin;
},
_addShape: function (kendoDiagram, p, id, shapeDefaults) {
if (Utils.isUndefined(p)) {
p = new diagram.Point(0, 0);
}
if (Utils.isUndefined(id)) {
id = randomId();
}
shapeDefaults = kendo.deepExtend({
width: 20,
height: 20,
id: id,
radius: 10,
fill: '#778899',
data: 'circle',
undoable: false,
x: p.x,
y: p.y
}, shapeDefaults);
return kendoDiagram.addShape(shapeDefaults);
},
_addConnection: function (diagram, from, to, options) {
return diagram.connect(from, to, options);
},
createDiagramFromGraph: function (diagram, graph, doLayout, randomSize) {
if (Utils.isUndefined(diagram)) {
throw 'The diagram surface is undefined.';
}
if (Utils.isUndefined(graph)) {
throw 'No graph specification defined.';
}
if (Utils.isUndefined(doLayout)) {
doLayout = true;
}
if (Utils.isUndefined(randomSize)) {
randomSize = false;
}
var width = diagram.element.clientWidth || 200;
var height = diagram.element.clientHeight || 200;
var map = [], node, shape;
for (var i = 0, len = graph.nodes.length; i < len; i++) {
node = graph.nodes[i];
var p = node.position;
if (Utils.isUndefined(p)) {
if (Utils.isDefined(node.x) && Utils.isDefined(node.y)) {
p = new Point(node.x, node.y);
} else {
p = new Point(Utils.randomInteger(10, width - 20), Utils.randomInteger(10, height - 20));
}
}
var opt = {};
if (node.id === '0') {
} else if (randomSize) {
kendo.deepExtend(opt, {
width: Math.random() * 150 + 20,
height: Math.random() * 80 + 50,
data: 'rectangle',
fill: { color: '#778899' }
});
}
shape = this._addShape(diagram, p, node.id, opt);
var bounds = shape.bounds();
if (Utils.isDefined(bounds)) {
node.x = bounds.x;
node.y = bounds.y;
node.width = bounds.width;
node.height = bounds.height;
}
map[node.id] = shape;
}
for (var gli = 0; gli < graph.links.length; gli++) {
var link = graph.links[gli];
var sourceShape = map[link.source.id];
if (Utils.isUndefined(sourceShape)) {
continue;
}
var targetShape = map[link.target.id];
if (Utils.isUndefined(targetShape)) {
continue;
}
this._addConnection(diagram, sourceShape, targetShape, { id: link.id });
}
if (doLayout) {
var l = new diagram.SpringLayout(diagram);
l.layoutGraph(graph, { limitToView: false });
for (var shi = 0; shi < graph.nodes.length; shi++) {
node = graph.nodes[shi];
shape = map[node.id];
shape.bounds(new Rect(node.x, node.y, node.width, node.height));
}
}
},
createBalancedTree: function (levels, siblingsCount) {
if (Utils.isUndefined(levels)) {
levels = 3;
}
if (Utils.isUndefined(siblingsCount)) {
siblingsCount = 3;
}
var g = new diagram.Graph(), counter = -1, lastAdded = [], news;
if (levels <= 0 || siblingsCount <= 0) {
return g;
}
var root = new Node((++counter).toString());
g.addNode(root);
g.root = root;
lastAdded.push(root);
for (var i = 0; i < levels; i++) {
news = [];
for (var j = 0; j < lastAdded.length; j++) {
var parent = lastAdded[j];
for (var k = 0; k < siblingsCount; k++) {
var item = new Node((++counter).toString());
g.addLink(parent, item);
news.push(item);
}
}
lastAdded = news;
}
return g;
},
createBalancedForest: function (levels, siblingsCount, treeCount) {
if (Utils.isUndefined(levels)) {
levels = 3;
}
if (Utils.isUndefined(siblingsCount)) {
siblingsCount = 3;
}
if (Utils.isUndefined(treeCount)) {
treeCount = 5;
}
var g = new diagram.Graph(), counter = -1, lastAdded = [], news;
if (levels <= 0 || siblingsCount <= 0 || treeCount <= 0) {
return g;
}
for (var t = 0; t < treeCount; t++) {
var root = new Node((++counter).toString());
g.addNode(root);
lastAdded = [root];
for (var i = 0; i < levels; i++) {
news = [];
for (var j = 0; j < lastAdded.length; j++) {
var parent = lastAdded[j];
for (var k = 0; k < siblingsCount; k++) {
var item = new Node((++counter).toString());
g.addLink(parent, item);
news.push(item);
}
}
lastAdded = news;
}
}
return g;
},
createRandomConnectedGraph: function (nodeCount, maxIncidence, isTree) {
if (Utils.isUndefined(nodeCount)) {
nodeCount = 40;
}
if (Utils.isUndefined(maxIncidence)) {
maxIncidence = 4;
}
if (Utils.isUndefined(isTree)) {
isTree = false;
}
var g = new diagram.Graph(), counter = -1;
if (nodeCount <= 0) {
return g;
}
var root = new Node((++counter).toString());
g.addNode(root);
if (nodeCount === 1) {
return g;
}
if (nodeCount > 1) {
for (var i = 1; i < nodeCount; i++) {
var poolNode = g.takeRandomNode([], maxIncidence);
if (!poolNode) {
break;
}
var newNode = g.addNode(i.toString());
g.addLink(poolNode, newNode);
}
if (!isTree && nodeCount > 1) {
var randomAdditions = Utils.randomInteger(1, nodeCount);
for (var ri = 0; ri < randomAdditions; ri++) {
var n1 = g.takeRandomNode([], maxIncidence);
var n2 = g.takeRandomNode([], maxIncidence);
if (n1 && n2 && !g.areConnected(n1, n2)) {
g.addLink(n1, n2);
}
}
}
return g;
}
},
randomDiagram: function (diagram, shapeCount, maxIncidence, isTree, randomSize) {
var g = kendo.dataviz.diagram.Graph.Utils.createRandomConnectedGraph(shapeCount, maxIncidence, isTree);
Graph.Utils.createDiagramFromGraph(diagram, g, false, randomSize);
}
};
kendo.deepExtend(diagram, {
init: function (element) {
kendo.init(element, diagram.ui);
},
Point: Point,
Intersect: Intersect,
Geometry: Geometry,
Rect: Rect,
Size: Size,
RectAlign: RectAlign,
Matrix: Matrix,
MatrixVector: MatrixVector,
normalVariable: normalVariable,
randomId: randomId,
Dictionary: Dictionary,
HashTable: HashTable,
Queue: Queue,
Set: Set,
Node: Node,
Link: Link,
Graph: Graph,
PathDefiner: PathDefiner
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/svg', [
'kendo.drawing',
'dataviz/diagram/math'
], f);
}(function () {
(function ($, undefined) {
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Class = kendo.Class, deepExtend = kendo.deepExtend, Point = diagram.Point, Rect = diagram.Rect, Matrix = diagram.Matrix, Utils = diagram.Utils, isNumber = Utils.isNumber, isString = Utils.isString, MatrixVector = diagram.MatrixVector, g = kendo.geometry, d = kendo.drawing, defined = kendo.util.defined, inArray = $.inArray;
var TRANSPARENT = 'transparent', Markers = {
none: 'none',
arrowStart: 'ArrowStart',
filledCircle: 'FilledCircle',
arrowEnd: 'ArrowEnd'
}, FULL_CIRCLE_ANGLE = 360, START = 'start', END = 'end', WIDTH = 'width', HEIGHT = 'height', X = 'x', Y = 'y';
diagram.Markers = Markers;
function diffNumericOptions(options, fields) {
var elementOptions = this.options;
var hasChanges = false;
var value, field;
for (var i = 0; i < fields.length; i++) {
field = fields[i];
value = options[field];
if (isNumber(value) && elementOptions[field] !== value) {
elementOptions[field] = value;
hasChanges = true;
}
}
return hasChanges;
}
var Scale = Class.extend({
init: function (x, y) {
this.x = x;
this.y = y;
},
toMatrix: function () {
return Matrix.scaling(this.x, this.y);
},
toString: function () {
return kendo.format('scale({0},{1})', this.x, this.y);
},
invert: function () {
return new Scale(1 / this.x, 1 / this.y);
}
});
var Translation = Class.extend({
init: function (x, y) {
this.x = x;
this.y = y;
},
toMatrixVector: function () {
return new MatrixVector(0, 0, 0, 0, this.x, this.y);
},
toMatrix: function () {
return Matrix.translation(this.x, this.y);
},
toString: function () {
return kendo.format('translate({0},{1})', this.x, this.y);
},
plus: function (delta) {
this.x += delta.x;
this.y += delta.y;
},
times: function (factor) {
this.x *= factor;
this.y *= factor;
},
length: function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
normalize: function () {
if (this.Length === 0) {
return;
}
this.times(1 / this.length());
},
invert: function () {
return new Translation(-this.x, -this.y);
}
});
var Rotation = Class.extend({
init: function (angle, x, y) {
this.x = x || 0;
this.y = y || 0;
this.angle = angle;
},
toString: function () {
if (this.x && this.y) {
return kendo.format('rotate({0},{1},{2})', this.angle, this.x, this.y);
} else {
return kendo.format('rotate({0})', this.angle);
}
},
toMatrix: function () {
return Matrix.rotation(this.angle, this.x, this.y);
},
center: function () {
return new Point(this.x, this.y);
},
invert: function () {
return new Rotation(FULL_CIRCLE_ANGLE - this.angle, this.x, this.y);
}
});
Rotation.ZERO = new Rotation(0);
Rotation.create = function (rotation) {
return new Rotation(rotation.angle, rotation.x, rotation.y);
};
Rotation.parse = function (str) {
var values = str.slice(1, str.length - 1).split(','), angle = values[0], x = values[1], y = values[2];
var rotation = new Rotation(angle, x, y);
return rotation;
};
var CompositeTransform = Class.extend({
init: function (x, y, scaleX, scaleY, angle, center) {
this.translate = new Translation(x, y);
if (scaleX !== undefined && scaleY !== undefined) {
this.scale = new Scale(scaleX, scaleY);
}
if (angle !== undefined) {
this.rotate = center ? new Rotation(angle, center.x, center.y) : new Rotation(angle);
}
},
toString: function () {
var toString = function (transform) {
return transform ? transform.toString() : '';
};
return toString(this.translate) + toString(this.rotate) + toString(this.scale);
},
render: function (visual) {
visual._transform = this;
visual._renderTransform();
},
toMatrix: function () {
var m = Matrix.unit();
if (this.translate) {
m = m.times(this.translate.toMatrix());
}
if (this.rotate) {
m = m.times(this.rotate.toMatrix());
}
if (this.scale) {
m = m.times(this.scale.toMatrix());
}
return m;
},
invert: function () {
var rotate = this.rotate ? this.rotate.invert() : undefined, rotateMatrix = rotate ? rotate.toMatrix() : Matrix.unit(), scale = this.scale ? this.scale.invert() : undefined, scaleMatrix = scale ? scale.toMatrix() : Matrix.unit();
var translatePoint = new Point(-this.translate.x, -this.translate.y);
translatePoint = rotateMatrix.times(scaleMatrix).apply(translatePoint);
var translate = new Translation(translatePoint.x, translatePoint.y);
var transform = new CompositeTransform();
transform.translate = translate;
transform.rotate = rotate;
transform.scale = scale;
return transform;
}
});
var AutoSizeableMixin = {
_setScale: function () {
var options = this.options;
var originWidth = this._originWidth;
var originHeight = this._originHeight;
var scaleX = options.width / originWidth;
var scaleY = options.height / originHeight;
if (!isNumber(scaleX)) {
scaleX = 1;
}
if (!isNumber(scaleY)) {
scaleY = 1;
}
this._transform.scale = new Scale(scaleX, scaleY);
},
_setTranslate: function () {
var options = this.options;
var x = options.x || 0;
var y = options.y || 0;
this._transform.translate = new Translation(x, y);
},
_initSize: function () {
var options = this.options;
var transform = false;
if (options.autoSize !== false && (defined(options.width) || defined(options.height))) {
this._measure(true);
this._setScale();
transform = true;
}
if (defined(options.x) || defined(options.y)) {
this._setTranslate();
transform = true;
}
if (transform) {
this._renderTransform();
}
},
_updateSize: function (options) {
var update = false;
if (this.options.autoSize !== false && this._diffNumericOptions(options, [
WIDTH,
HEIGHT
])) {
update = true;
this._measure(true);
this._setScale();
}
if (this._diffNumericOptions(options, [
X,
Y
])) {
update = true;
this._setTranslate();
}
if (update) {
this._renderTransform();
}
return update;
}
};
var Element = Class.extend({
init: function (options) {
var element = this;
element.options = deepExtend({}, element.options, options);
element.id = element.options.id;
element._originSize = Rect.empty();
element._transform = new CompositeTransform();
},
visible: function (value) {
return this.drawingContainer().visible(value);
},
redraw: function (options) {
if (options && options.id) {
this.id = options.id;
}
},
position: function (x, y) {
var options = this.options;
if (!defined(x)) {
return new Point(options.x, options.y);
}
if (defined(y)) {
options.x = x;
options.y = y;
} else if (x instanceof Point) {
options.x = x.x;
options.y = x.y;
}
this._transform.translate = new Translation(options.x, options.y);
this._renderTransform();
},
rotate: function (angle, center) {
if (defined(angle)) {
this._transform.rotate = new Rotation(angle, center.x, center.y);
this._renderTransform();
}
return this._transform.rotate || Rotation.ZERO;
},
drawingContainer: function () {
return this.drawingElement;
},
_renderTransform: function () {
var matrix = this._transform.toMatrix();
this.drawingContainer().transform(new g.Matrix(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f));
},
_hover: function () {
},
_diffNumericOptions: diffNumericOptions,
_measure: function (force) {
var rect;
if (!this._measured || force) {
var box = this._boundingBox() || new g.Rect();
var startPoint = box.topLeft();
rect = new Rect(startPoint.x, startPoint.y, box.width(), box.height());
this._originSize = rect;
this._originWidth = rect.width;
this._originHeight = rect.height;
this._measured = true;
} else {
rect = this._originSize;
}
return rect;
},
_boundingBox: function () {
return this.drawingElement.rawBBox();
}
});
var VisualBase = Element.extend({
init: function (options) {
Element.fn.init.call(this, options);
options = this.options;
options.fill = normalizeDrawingOptions(options.fill);
options.stroke = normalizeDrawingOptions(options.stroke);
},
options: {
stroke: {
color: 'gray',
width: 1
},
fill: { color: TRANSPARENT }
},
fill: function (color, opacity) {
this._fill({
color: getColor(color),
opacity: opacity
});
},
stroke: function (color, width, opacity) {
this._stroke({
color: getColor(color),
width: width,
opacity: opacity
});
},
redraw: function (options) {
if (options) {
var stroke = options.stroke;
var fill = options.fill;
if (stroke) {
this._stroke(normalizeDrawingOptions(stroke));
}
if (fill) {
this._fill(normalizeDrawingOptions(fill));
}
Element.fn.redraw.call(this, options);
}
},
_hover: function (show) {
var drawingElement = this.drawingElement;
var options = this.options;
var hover = options.hover;
if (hover && hover.fill) {
var fill = show ? normalizeDrawingOptions(hover.fill) : options.fill;
drawingElement.fill(fill.color, fill.opacity);
}
},
_stroke: function (strokeOptions) {
var options = this.options;
deepExtend(options, { stroke: strokeOptions });
strokeOptions = options.stroke;
var stroke = null;
if (strokeOptions.width > 0) {
stroke = {
color: strokeOptions.color,
width: strokeOptions.width,
opacity: strokeOptions.opacity,
dashType: strokeOptions.dashType
};
}
this.drawingElement.options.set('stroke', stroke);
},
_fill: function (fillOptions) {
var options = this.options;
deepExtend(options, { fill: fillOptions || {} });
var fill = options.fill;
if (fill.gradient) {
var gradient = fill.gradient;
var GradientClass = gradient.type === 'radial' ? d.RadialGradient : d.LinearGradient;
this.drawingElement.fill(new GradientClass(gradient));
} else {
this.drawingElement.fill(fill.color, fill.opacity);
}
}
});
var TextBlock = VisualBase.extend({
init: function (options) {
this._textColor(options);
VisualBase.fn.init.call(this, options);
this._font();
this._initText();
this._initSize();
},
options: {
fontSize: 15,
fontFamily: 'sans-serif',
stroke: { width: 0 },
fill: { color: 'black' },
autoSize: true
},
_initText: function () {
var options = this.options;
this.drawingElement = new d.Text(defined(options.text) ? options.text : '', new g.Point(), { font: options.font });
this._fill();
this._stroke();
},
_textColor: function (options) {
if (options && options.color) {
deepExtend(options, { fill: { color: options.color } });
}
},
_font: function () {
var options = this.options;
if (options.fontFamily && defined(options.fontSize)) {
options.font = options.fontSize + 'px ' + options.fontFamily;
} else {
delete options.font;
}
},
content: function (text) {
return this.drawingElement.content(text);
},
redraw: function (options) {
if (options) {
var sizeChanged = false;
var textOptions = this.options;
this._textColor(options);
VisualBase.fn.redraw.call(this, options);
if (options.fontFamily || defined(options.fontSize)) {
deepExtend(textOptions, {
fontFamily: options.fontFamily,
fontSize: options.fontSize
});
this._font();
this.drawingElement.options.set('font', textOptions.font);
sizeChanged = true;
}
if (options.text) {
this.content(options.text);
sizeChanged = true;
}
if (!this._updateSize(options) && sizeChanged) {
this._initSize();
}
}
}
});
deepExtend(TextBlock.fn, AutoSizeableMixin);
var Rectangle = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
this._initPath();
this._setPosition();
},
_setPosition: function () {
var options = this.options;
var x = options.x;
var y = options.y;
if (defined(x) || defined(y)) {
this.position(x || 0, y || 0);
}
},
redraw: function (options) {
if (options) {
VisualBase.fn.redraw.call(this, options);
if (this._diffNumericOptions(options, [
WIDTH,
HEIGHT
])) {
this._drawPath();
}
if (this._diffNumericOptions(options, [
X,
Y
])) {
this._setPosition();
}
}
},
_initPath: function () {
var options = this.options;
this.drawingElement = new d.Path({
stroke: options.stroke,
closed: true
});
this._fill();
this._drawPath();
},
_drawPath: function () {
var drawingElement = this.drawingElement;
var sizeOptions = sizeOptionsOrDefault(this.options);
var width = sizeOptions.width;
var height = sizeOptions.height;
drawingElement.segments.elements([
createSegment(0, 0),
createSegment(width, 0),
createSegment(width, height),
createSegment(0, height)
]);
}
});
var MarkerBase = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
var anchor = this.options.anchor;
this.anchor = new g.Point(anchor.x, anchor.y);
this.createElement();
},
options: {
stroke: {
color: TRANSPARENT,
width: 0
},
fill: { color: 'black' }
},
_transformToPath: function (point, path) {
var transform = path.transform();
if (point && transform) {
point = point.transformCopy(transform);
}
return point;
},
redraw: function (options) {
if (options) {
if (options.position) {
this.options.position = options.position;
}
VisualBase.fn.redraw.call(this, options);
}
}
});
var CircleMarker = MarkerBase.extend({
options: {
radius: 4,
anchor: {
x: 0,
y: 0
}
},
createElement: function () {
var options = this.options;
this.drawingElement = new d.Circle(new g.Circle(this.anchor, options.radius), {
fill: options.fill,
stroke: options.stroke
});
},
positionMarker: function (path) {
var options = this.options;
var position = options.position;
var segments = path.segments;
var targetSegment;
var point;
if (position == START) {
targetSegment = segments[0];
} else {
targetSegment = segments[segments.length - 1];
}
if (targetSegment) {
point = this._transformToPath(targetSegment.anchor(), path);
this.drawingElement.transform(g.transform().translate(point.x, point.y));
}
}
});
var ArrowMarker = MarkerBase.extend({
options: {
path: 'M 0 0 L 10 5 L 0 10 L 3 5 z',
anchor: {
x: 10,
y: 5
}
},
createElement: function () {
var options = this.options;
this.drawingElement = d.Path.parse(options.path, {
fill: options.fill,
stroke: options.stroke
});
},
positionMarker: function (path) {
var points = this._linePoints(path);
var start = points.start;
var end = points.end;
var transform = g.transform();
if (start) {
transform.rotate(lineAngle(start, end), end);
}
if (end) {
var anchor = this.anchor;
var translate = end.clone().translate(-anchor.x, -anchor.y);
transform.translate(translate.x, translate.y);
}
this.drawingElement.transform(transform);
},
_linePoints: function (path) {
var options = this.options;
var segments = path.segments;
var startPoint, endPoint, targetSegment;
if (options.position == START) {
targetSegment = segments[0];
if (targetSegment) {
endPoint = targetSegment.anchor();
startPoint = targetSegment.controlOut();
var nextSegment = segments[1];
if (!startPoint && nextSegment) {
startPoint = nextSegment.anchor();
}
}
} else {
targetSegment = segments[segments.length - 1];
if (targetSegment) {
endPoint = targetSegment.anchor();
startPoint = targetSegment.controlIn();
var prevSegment = segments[segments.length - 2];
if (!startPoint && prevSegment) {
startPoint = prevSegment.anchor();
}
}
}
if (endPoint) {
return {
start: this._transformToPath(startPoint, path),
end: this._transformToPath(endPoint, path)
};
}
}
});
var MarkerPathMixin = {
_getPath: function (position) {
var path = this.drawingElement;
if (path instanceof d.MultiPath) {
if (position == START) {
path = path.paths[0];
} else {
path = path.paths[path.paths.length - 1];
}
}
if (path && path.segments.length) {
return path;
}
},
_normalizeMarkerOptions: function (options) {
var startCap = options.startCap;
var endCap = options.endCap;
if (isString(startCap)) {
options.startCap = { type: startCap };
}
if (isString(endCap)) {
options.endCap = { type: endCap };
}
},
_removeMarker: function (position) {
var marker = this._markers[position];
if (marker) {
this.drawingContainer().remove(marker.drawingElement);
delete this._markers[position];
}
},
_createMarkers: function () {
var options = this.options;
this._normalizeMarkerOptions(options);
this._markers = {};
this._markers[START] = this._createMarker(options.startCap, START);
this._markers[END] = this._createMarker(options.endCap, END);
},
_createMarker: function (options, position) {
var type = (options || {}).type;
var path = this._getPath(position);
var markerType, marker;
if (!path) {
this._removeMarker(position);
return;
}
if (type == Markers.filledCircle) {
markerType = CircleMarker;
} else if (type == Markers.arrowStart || type == Markers.arrowEnd) {
markerType = ArrowMarker;
} else {
this._removeMarker(position);
}
if (markerType) {
marker = new markerType(deepExtend({}, options, { position: position }));
marker.positionMarker(path);
this.drawingContainer().append(marker.drawingElement);
return marker;
}
},
_positionMarker: function (position) {
var marker = this._markers[position];
if (marker) {
var path = this._getPath(position);
if (path) {
marker.positionMarker(path);
} else {
this._removeMarker(position);
}
}
},
_capMap: {
start: 'startCap',
end: 'endCap'
},
_redrawMarker: function (pathChange, position, options) {
this._normalizeMarkerOptions(options);
var pathOptions = this.options;
var cap = this._capMap[position];
var pathCapType = (pathOptions[cap] || {}).type;
var optionsCap = options[cap];
var created = false;
if (optionsCap) {
pathOptions[cap] = deepExtend({}, pathOptions[cap], optionsCap);
if (optionsCap.type && pathCapType != optionsCap.type) {
this._removeMarker(position);
this._markers[position] = this._createMarker(pathOptions[cap], position);
created = true;
} else if (this._markers[position]) {
this._markers[position].redraw(optionsCap);
}
} else if (pathChange && !this._markers[position] && pathOptions[cap]) {
this._markers[position] = this._createMarker(pathOptions[cap], position);
created = true;
}
return created;
},
_redrawMarkers: function (pathChange, options) {
if (!this._redrawMarker(pathChange, START, options) && pathChange) {
this._positionMarker(START);
}
if (!this._redrawMarker(pathChange, END, options) && pathChange) {
this._positionMarker(END);
}
}
};
var Path = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
this.container = new d.Group();
this._createElements();
this._initSize();
},
options: { autoSize: true },
drawingContainer: function () {
return this.container;
},
data: function (value) {
var options = this.options;
if (value) {
if (options.data != value) {
options.data = value;
this._setData(value);
this._initSize();
this._redrawMarkers(true, {});
}
} else {
return options.data;
}
},
redraw: function (options) {
if (options) {
VisualBase.fn.redraw.call(this, options);
var pathOptions = this.options;
var data = options.data;
if (defined(data) && pathOptions.data != data) {
pathOptions.data = data;
this._setData(data);
if (!this._updateSize(options)) {
this._initSize();
}
this._redrawMarkers(true, options);
} else {
this._updateSize(options);
this._redrawMarkers(false, options);
}
}
},
_createElements: function () {
var options = this.options;
this.drawingElement = d.Path.parse(options.data || '', { stroke: options.stroke });
this._fill();
this.container.append(this.drawingElement);
this._createMarkers();
},
_setData: function (data) {
var drawingElement = this.drawingElement;
var multipath = d.Path.parse(data || '');
var paths = multipath.paths.slice(0);
multipath.paths.elements([]);
drawingElement.paths.elements(paths);
}
});
deepExtend(Path.fn, AutoSizeableMixin);
deepExtend(Path.fn, MarkerPathMixin);
var Line = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
this.container = new d.Group();
this._initPath();
this._createMarkers();
},
drawingContainer: function () {
return this.container;
},
redraw: function (options) {
if (options) {
options = options || {};
var from = options.from;
var to = options.to;
if (from) {
this.options.from = from;
}
if (to) {
this.options.to = to;
}
if (from || to) {
this._drawPath();
this._redrawMarkers(true, options);
} else {
this._redrawMarkers(false, options);
}
VisualBase.fn.redraw.call(this, options);
}
},
_initPath: function () {
var options = this.options;
var drawingElement = this.drawingElement = new d.Path({ stroke: options.stroke });
this._fill();
this._drawPath();
this.container.append(drawingElement);
},
_drawPath: function () {
var options = this.options;
var drawingElement = this.drawingElement;
var from = options.from || new Point();
var to = options.to || new Point();
drawingElement.segments.elements([
createSegment(from.x, from.y),
createSegment(to.x, to.y)
]);
}
});
deepExtend(Line.fn, MarkerPathMixin);
var Polyline = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
this.container = new d.Group();
this._initPath();
this._createMarkers();
},
drawingContainer: function () {
return this.container;
},
points: function (points) {
var options = this.options;
if (points) {
options.points = points;
this._updatePath();
} else {
return options.points;
}
},
redraw: function (options) {
if (options) {
var points = options.points;
VisualBase.fn.redraw.call(this, options);
if (points && this._pointsDiffer(points)) {
this.points(points);
this._redrawMarkers(true, options);
} else {
this._redrawMarkers(false, options);
}
}
},
_initPath: function () {
var options = this.options;
this.drawingElement = new d.Path({ stroke: options.stroke });
this._fill();
this.container.append(this.drawingElement);
if (options.points) {
this._updatePath();
}
},
_pointsDiffer: function (points) {
var currentPoints = this.options.points;
var differ = currentPoints.length !== points.length;
if (!differ) {
for (var i = 0; i < points.length; i++) {
if (currentPoints[i].x !== points[i].x || currentPoints[i].y !== points[i].y) {
differ = true;
break;
}
}
}
return differ;
},
_updatePath: function () {
var drawingElement = this.drawingElement;
var options = this.options;
var points = options.points;
var segments = [];
var point;
for (var i = 0; i < points.length; i++) {
point = points[i];
segments.push(createSegment(point.x, point.y));
}
drawingElement.segments.elements(segments);
},
options: { points: [] }
});
deepExtend(Polyline.fn, MarkerPathMixin);
var Image = Element.extend({
init: function (options) {
Element.fn.init.call(this, options);
this._initImage();
},
redraw: function (options) {
if (options) {
if (options.source) {
this.drawingElement.src(options.source);
}
if (this._diffNumericOptions(options, [
WIDTH,
HEIGHT,
X,
Y
])) {
this.drawingElement.rect(this._rect());
}
Element.fn.redraw.call(this, options);
}
},
_initImage: function () {
var options = this.options;
var rect = this._rect();
this.drawingElement = new d.Image(options.source, rect, {});
},
_rect: function () {
var sizeOptions = sizeOptionsOrDefault(this.options);
var origin = new g.Point(sizeOptions.x, sizeOptions.y);
var size = new g.Size(sizeOptions.width, sizeOptions.height);
return new g.Rect(origin, size);
}
});
var Group = Element.extend({
init: function (options) {
this.children = [];
Element.fn.init.call(this, options);
this.drawingElement = new d.Group();
this._initSize();
},
options: { autoSize: false },
append: function (visual) {
this.drawingElement.append(visual.drawingContainer());
this.children.push(visual);
this._childrenChange = true;
},
remove: function (visual) {
if (this._remove(visual)) {
this._childrenChange = true;
}
},
_remove: function (visual) {
var index = inArray(visual, this.children);
if (index >= 0) {
this.drawingElement.removeAt(index);
this.children.splice(index, 1);
return true;
}
},
clear: function () {
this.drawingElement.clear();
this.children = [];
this._childrenChange = true;
},
toFront: function (visuals) {
var visual;
for (var i = 0; i < visuals.length; i++) {
visual = visuals[i];
if (this._remove(visual)) {
this.append(visual);
}
}
},
toBack: function (visuals) {
this._reorderChildren(visuals, 0);
},
toIndex: function (visuals, indices) {
this._reorderChildren(visuals, indices);
},
_reorderChildren: function (visuals, indices) {
var group = this.drawingElement;
var drawingChildren = group.children.slice(0);
var children = this.children;
var fixedPosition = isNumber(indices);
var i, index, toIndex, drawingElement, visual;
for (i = 0; i < visuals.length; i++) {
visual = visuals[i];
drawingElement = visual.drawingContainer();
index = inArray(visual, children);
if (index >= 0) {
drawingChildren.splice(index, 1);
children.splice(index, 1);
toIndex = fixedPosition ? indices : indices[i];
drawingChildren.splice(toIndex, 0, drawingElement);
children.splice(toIndex, 0, visual);
}
}
group.clear();
group.append.apply(group, drawingChildren);
},
redraw: function (options) {
if (options) {
if (this._childrenChange) {
this._childrenChange = false;
if (!this._updateSize(options)) {
this._initSize();
}
} else {
this._updateSize(options);
}
Element.fn.redraw.call(this, options);
}
},
_boundingBox: function () {
var children = this.children;
var boundingBox;
var visual, childBoundingBox;
for (var i = 0; i < children.length; i++) {
visual = children[i];
if (visual.visible() && visual._includeInBBox !== false) {
childBoundingBox = visual.drawingContainer().clippedBBox(null);
if (childBoundingBox) {
if (boundingBox) {
boundingBox = g.Rect.union(boundingBox, childBoundingBox);
} else {
boundingBox = childBoundingBox;
}
}
}
}
return boundingBox;
}
});
deepExtend(Group.fn, AutoSizeableMixin);
var Layout = Group.extend({
init: function (rect, options) {
this.children = [];
Element.fn.init.call(this, options);
this.drawingElement = new d.Layout(toDrawingRect(rect), options);
this._initSize();
},
rect: function (rect) {
if (rect) {
this.drawingElement.rect(toDrawingRect(rect));
} else {
var drawingRect = this.drawingElement.rect();
if (drawingRect) {
return new Rect(drawingRect.origin.x, drawingRect.origin.y, drawingRect.size.width, drawingRect.size.height);
}
}
},
reflow: function () {
this.drawingElement.reflow();
},
redraw: function (options) {
kendo.deepExtend(this.drawingElement.options, options);
Group.fn.redraw.call(this, options);
}
});
var Circle = VisualBase.extend({
init: function (options) {
VisualBase.fn.init.call(this, options);
this._initCircle();
this._initSize();
},
redraw: function (options) {
if (options) {
var circleOptions = this.options;
if (options.center) {
deepExtend(circleOptions, { center: options.center });
this._center.move(circleOptions.center.x, circleOptions.center.y);
}
if (this._diffNumericOptions(options, ['radius'])) {
this._circle.setRadius(circleOptions.radius);
}
this._updateSize(options);
VisualBase.fn.redraw.call(this, options);
}
},
_initCircle: function () {
var options = this.options;
var width = options.width;
var height = options.height;
var radius = options.radius;
if (!defined(radius)) {
if (!defined(width)) {
width = height;
}
if (!defined(height)) {
height = width;
}
options.radius = radius = Math.min(width, height) / 2;
}
var center = options.center || {
x: radius,
y: radius
};
this._center = new g.Point(center.x, center.y);
this._circle = new g.Circle(this._center, radius);
this.drawingElement = new d.Circle(this._circle, { stroke: options.stroke });
this._fill();
}
});
deepExtend(Circle.fn, AutoSizeableMixin);
var Canvas = Class.extend({
init: function (element, options) {
options = options || {};
this.element = element;
this.surface = d.Surface.create(element, options);
if (kendo.isFunction(this.surface.translate)) {
this.translate = this._translate;
}
this.drawingElement = new d.Group();
this._viewBox = new Rect(0, 0, options.width, options.height);
this.size(this._viewBox);
},
bounds: function () {
var box = this.drawingElement.clippedBBox();
return new Rect(0, 0, box.width(), box.height());
},
size: function (size) {
var viewBox = this._viewBox;
if (defined(size)) {
viewBox.width = size.width;
viewBox.height = size.height;
this.surface.setSize(size);
}
return {
width: viewBox.width,
height: viewBox.height
};
},
_translate: function (x, y) {
var viewBox = this._viewBox;
if (defined(x) && defined(y)) {
viewBox.x = x;
viewBox.y = y;
this.surface.translate({
x: x,
y: y
});
}
return {
x: viewBox.x,
y: viewBox.y
};
},
draw: function () {
this.surface.draw(this.drawingElement);
},
append: function (visual) {
this.drawingElement.append(visual.drawingContainer());
return this;
},
remove: function (visual) {
this.drawingElement.remove(visual.drawingContainer());
},
insertBefore: function () {
},
clear: function () {
this.drawingElement.clear();
},
destroy: function (clearHtml) {
this.surface.destroy();
if (clearHtml) {
$(this.element).remove();
}
}
});
function sizeOptionsOrDefault(options) {
return {
x: options.x || 0,
y: options.y || 0,
width: options.width || 0,
height: options.height || 0
};
}
function normalizeDrawingOptions(options) {
if (options) {
var drawingOptions = options;
if (isString(drawingOptions)) {
drawingOptions = { color: drawingOptions };
}
if (drawingOptions.color) {
drawingOptions.color = getColor(drawingOptions.color);
}
return drawingOptions;
}
}
function getColor(value) {
var color;
if (value != TRANSPARENT) {
color = new d.Color(value).toHex();
} else {
color = value;
}
return color;
}
function lineAngle(p1, p2) {
var xDiff = p2.x - p1.x;
var yDiff = p2.y - p1.y;
var angle = kendo.util.deg(Math.atan2(yDiff, xDiff));
return angle;
}
function createSegment(x, y) {
return new d.Segment(new g.Point(x, y));
}
function toDrawingRect(rect) {
if (rect) {
return new g.Rect([
rect.x,
rect.y
], [
rect.width,
rect.height
]);
}
}
kendo.deepExtend(diagram, {
init: function (element) {
kendo.init(element, diagram.ui);
},
diffNumericOptions: diffNumericOptions,
Element: Element,
Scale: Scale,
Translation: Translation,
Rotation: Rotation,
Circle: Circle,
Group: Group,
Rectangle: Rectangle,
Canvas: Canvas,
Path: Path,
Layout: Layout,
Line: Line,
MarkerBase: MarkerBase,
ArrowMarker: ArrowMarker,
CircleMarker: CircleMarker,
Polyline: Polyline,
CompositeTransform: CompositeTransform,
TextBlock: TextBlock,
Image: Image,
VisualBase: VisualBase
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/services', [
'kendo.drawing',
'dataviz/diagram/svg'
], f);
}(function () {
(function ($, undefined) {
var kendo = window.kendo, dataviz = kendo.dataviz, diagram = dataviz.diagram, Class = kendo.Class, Group = diagram.Group, Rect = diagram.Rect, Rectangle = diagram.Rectangle, Utils = diagram.Utils, isUndefined = Utils.isUndefined, Point = diagram.Point, Circle = diagram.Circle, Ticker = diagram.Ticker, deepExtend = kendo.deepExtend, Movable = kendo.ui.Movable, browser = kendo.support.browser, defined = kendo.util.defined, inArray = $.inArray, proxy = $.proxy;
var Cursors = {
arrow: 'default',
grip: 'pointer',
cross: 'pointer',
add: 'pointer',
move: 'move',
select: 'pointer',
south: 's-resize',
east: 'e-resize',
west: 'w-resize',
north: 'n-resize',
rowresize: 'row-resize',
colresize: 'col-resize'
}, HIT_TEST_DISTANCE = 10, AUTO = 'Auto', TOP = 'Top', RIGHT = 'Right', LEFT = 'Left', BOTTOM = 'Bottom', DEFAULT_SNAP_SIZE = 10, DEFAULT_SNAP_ANGLE = 10, DRAG_START = 'dragStart', DRAG = 'drag', DRAG_END = 'dragEnd', ITEMROTATE = 'itemRotate', ITEMBOUNDSCHANGE = 'itemBoundsChange', MIN_SNAP_SIZE = 5, MIN_SNAP_ANGLE = 5, MOUSE_ENTER = 'mouseEnter', MOUSE_LEAVE = 'mouseLeave', ZOOM_START = 'zoomStart', ZOOM_END = 'zoomEnd', SCROLL_MIN = -20000, SCROLL_MAX = 20000, FRICTION = 0.9, FRICTION_MOBILE = 0.93, VELOCITY_MULTIPLIER = 5, TRANSPARENT = 'transparent', PAN = 'pan', ROTATED = 'rotated';
diagram.Cursors = Cursors;
function selectSingle(item, meta) {
if (item.isSelected) {
if (meta.ctrlKey) {
item.select(false);
}
} else {
item.diagram.select(item, { addToSelection: meta.ctrlKey });
}
}
var PositionAdapter = kendo.Class.extend({
init: function (layoutState) {
this.layoutState = layoutState;
this.diagram = layoutState.diagram;
},
initState: function () {
this.froms = [];
this.tos = [];
this.subjects = [];
function pusher(id, bounds) {
var shape = this.diagram.getShapeById(id);
if (shape) {
this.subjects.push(shape);
this.froms.push(shape.bounds().topLeft());
this.tos.push(bounds.topLeft());
}
}
this.layoutState.nodeMap.forEach(pusher, this);
},
update: function (tick) {
if (this.subjects.length <= 0) {
return;
}
for (var i = 0; i < this.subjects.length; i++) {
this.subjects[i].position(new Point(this.froms[i].x + (this.tos[i].x - this.froms[i].x) * tick, this.froms[i].y + (this.tos[i].y - this.froms[i].y) * tick));
}
}
});
var LayoutUndoUnit = Class.extend({
init: function (initialState, finalState, animate) {
if (isUndefined(animate)) {
this.animate = false;
} else {
this.animate = animate;
}
this._initialState = initialState;
this._finalState = finalState;
this.title = 'Diagram layout';
},
undo: function () {
this.setState(this._initialState);
},
redo: function () {
this.setState(this._finalState);
},
setState: function (state) {
var diagram = state.diagram;
if (this.animate) {
state.linkMap.forEach(function (id, points) {
var conn = diagram.getShapeById(id);
conn.visible(false);
if (conn) {
conn.points(points);
}
});
var ticker = new Ticker();
ticker.addAdapter(new PositionAdapter(state));
ticker.onComplete(function () {
state.linkMap.forEach(function (id) {
var conn = diagram.getShapeById(id);
conn.visible(true);
});
});
ticker.play();
} else {
state.nodeMap.forEach(function (id, bounds) {
var shape = diagram.getShapeById(id);
if (shape) {
shape.position(bounds.topLeft());
}
});
state.linkMap.forEach(function (id, points) {
var conn = diagram.getShapeById(id);
if (conn) {
conn.points(points);
}
});
}
}
});
var CompositeUnit = Class.extend({
init: function (unit) {
this.units = [];
this.title = 'Composite unit';
if (unit !== undefined) {
this.units.push(unit);
}
},
add: function (undoUnit) {
this.units.push(undoUnit);
},
undo: function () {
for (var i = 0; i < this.units.length; i++) {
this.units[i].undo();
}
},
redo: function () {
for (var i = 0; i < this.units.length; i++) {
this.units[i].redo();
}
}
});
var ConnectionEditUnit = Class.extend({
init: function (item, redoSource, redoTarget) {
this.item = item;
this._redoSource = redoSource;
this._redoTarget = redoTarget;
if (defined(redoSource)) {
this._undoSource = item.source();
}
if (defined(redoTarget)) {
this._undoTarget = item.target();
}
this.title = 'Connection Editing';
},
undo: function () {
if (this._undoSource !== undefined) {
this.item._updateConnector(this._undoSource, 'source');
}
if (this._undoTarget !== undefined) {
this.item._updateConnector(this._undoTarget, 'target');
}
this.item.updateModel();
},
redo: function () {
if (this._redoSource !== undefined) {
this.item._updateConnector(this._redoSource, 'source');
}
if (this._redoTarget !== undefined) {
this.item._updateConnector(this._redoTarget, 'target');
}
this.item.updateModel();
}
});
var ConnectionEditUndoUnit = Class.extend({
init: function (item, undoSource, undoTarget) {
this.item = item;
this._undoSource = undoSource;
this._undoTarget = undoTarget;
this._redoSource = item.source();
this._redoTarget = item.target();
this.title = 'Connection Editing';
},
undo: function () {
this.item._updateConnector(this._undoSource, 'source');
this.item._updateConnector(this._undoTarget, 'target');
this.item.updateModel();
},
redo: function () {
this.item._updateConnector(this._redoSource, 'source');
this.item._updateConnector(this._redoTarget, 'target');
this.item.updateModel();
}
});
var DeleteConnectionUnit = Class.extend({
init: function (connection) {
this.connection = connection;
this.diagram = connection.diagram;
this.targetConnector = connection.targetConnector;
this.title = 'Delete connection';
},
undo: function () {
this.diagram._addConnection(this.connection, false);
},
redo: function () {
this.diagram.remove(this.connection, false);
}
});
var DeleteShapeUnit = Class.extend({
init: function (shape) {
this.shape = shape;
this.diagram = shape.diagram;
this.title = 'Deletion';
},
undo: function () {
this.diagram._addShape(this.shape, false);
this.shape.select(false);
},
redo: function () {
this.shape.select(false);
this.diagram.remove(this.shape, false);
}
});
var TransformUnit = Class.extend({
init: function (shapes, undoStates, adorner) {
this.shapes = shapes;
this.undoStates = undoStates;
this.title = 'Transformation';
this.redoStates = [];
this.adorner = adorner;
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
this.redoStates.push(shape.bounds());
}
},
undo: function () {
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
shape.bounds(this.undoStates[i]);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape, this.redoStates[i], this.undoStates[i]);
}
shape.updateModel();
}
if (this.adorner) {
this.adorner.refreshBounds();
this.adorner.refresh();
}
},
redo: function () {
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
shape.bounds(this.redoStates[i]);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape, this.undoStates[i], this.redoStates[i]);
}
shape.updateModel();
}
if (this.adorner) {
this.adorner.refreshBounds();
this.adorner.refresh();
}
}
});
var AddConnectionUnit = Class.extend({
init: function (connection, diagram) {
this.connection = connection;
this.diagram = diagram;
this.title = 'New connection';
},
undo: function () {
this.diagram.remove(this.connection, false);
},
redo: function () {
this.diagram._addConnection(this.connection, false);
}
});
var AddShapeUnit = Class.extend({
init: function (shape, diagram) {
this.shape = shape;
this.diagram = diagram;
this.title = 'New shape';
},
undo: function () {
this.diagram.deselect();
this.diagram.remove(this.shape, false);
},
redo: function () {
this.diagram._addShape(this.shape, false);
}
});
var PanUndoUnit = Class.extend({
init: function (initialPosition, finalPosition, diagram) {
this.initial = initialPosition;
this.finalPos = finalPosition;
this.diagram = diagram;
this.title = 'Pan Unit';
},
undo: function () {
this.diagram.pan(this.initial);
},
redo: function () {
this.diagram.pan(this.finalPos);
}
});
var RotateUnit = Class.extend({
init: function (adorner, shapes, undoRotates) {
this.shapes = shapes;
this.undoRotates = undoRotates;
this.title = 'Rotation';
this.redoRotates = [];
this.redoAngle = adorner._angle;
this.adorner = adorner;
this.center = adorner._innerBounds.center();
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
this.redoRotates.push(shape.rotate().angle);
}
},
undo: function () {
var i, shape;
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
shape.rotate(this.undoRotates[i], this.center, false);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape);
}
shape.updateModel();
}
if (this.adorner) {
this.adorner._initialize();
this.adorner.refresh();
}
},
redo: function () {
var i, shape;
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
shape.rotate(this.redoRotates[i], this.center, false);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape);
}
shape.updateModel();
}
if (this.adorner) {
this.adorner._initialize();
this.adorner.refresh();
}
}
});
var ToFrontUnit = Class.extend({
init: function (diagram, items, initialIndices) {
this.diagram = diagram;
this.indices = initialIndices;
this.items = items;
this.title = 'Rotate Unit';
},
undo: function () {
this.diagram._toIndex(this.items, this.indices);
},
redo: function () {
this.diagram.toFront(this.items, false);
}
});
var ToBackUnit = Class.extend({
init: function (diagram, items, initialIndices) {
this.diagram = diagram;
this.indices = initialIndices;
this.items = items;
this.title = 'Rotate Unit';
},
undo: function () {
this.diagram._toIndex(this.items, this.indices);
},
redo: function () {
this.diagram.toBack(this.items, false);
}
});
var UndoRedoService = kendo.Observable.extend({
init: function (options) {
kendo.Observable.fn.init.call(this, options);
this.bind(this.events, options);
this.stack = [];
this.index = 0;
this.capacity = 100;
},
events: [
'undone',
'redone'
],
begin: function () {
this.composite = new CompositeUnit();
},
cancel: function () {
this.composite = undefined;
},
commit: function (execute) {
if (this.composite.units.length > 0) {
this._restart(this.composite, execute);
}
this.composite = undefined;
},
addCompositeItem: function (undoUnit) {
if (this.composite) {
this.composite.add(undoUnit);
} else {
this.add(undoUnit);
}
},
add: function (undoUnit, execute) {
this._restart(undoUnit, execute);
},
pop: function () {
if (this.index > 0) {
this.stack.pop();
this.index--;
}
},
count: function () {
return this.stack.length;
},
undo: function () {
if (this.index > 0) {
this.index--;
this.stack[this.index].undo();
this.trigger('undone');
}
},
redo: function () {
if (this.stack.length > 0 && this.index < this.stack.length) {
this.stack[this.index].redo();
this.index++;
this.trigger('redone');
}
},
_restart: function (composite, execute) {
this.stack.splice(this.index, this.stack.length - this.index);
this.stack.push(composite);
if (execute !== false) {
this.redo();
} else {
this.index++;
}
if (this.stack.length > this.capacity) {
this.stack.splice(0, this.stack.length - this.capacity);
this.index = this.capacity;
}
},
clear: function () {
this.stack = [];
this.index = 0;
}
});
var EmptyTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
},
start: function () {
},
move: function () {
},
end: function () {
},
tryActivate: function () {
return false;
},
getCursor: function () {
return Cursors.arrow;
}
});
function noMeta(meta) {
return meta.ctrlKey === false && meta.altKey === false && meta.shiftKey === false;
}
function tryActivateSelection(options, meta) {
var enabled = options !== false;
if (options.key && options.key != 'none') {
enabled = meta[options.key + 'Key'];
}
return enabled;
}
var ScrollerTool = EmptyTool.extend({
init: function (toolService) {
var tool = this;
var friction = kendo.support.mobileOS ? FRICTION_MOBILE : FRICTION;
EmptyTool.fn.init.call(tool, toolService);
var diagram = tool.toolService.diagram, canvas = diagram.canvas;
var scroller = diagram.scroller = tool.scroller = $(diagram.scrollable).kendoMobileScroller({
friction: friction,
velocityMultiplier: VELOCITY_MULTIPLIER,
mousewheelScrolling: false,
zoom: false,
scroll: proxy(tool._move, tool)
}).data('kendoMobileScroller');
if (canvas.translate) {
tool.movableCanvas = new Movable(canvas.element);
}
var virtualScroll = function (dimension, min, max) {
dimension.makeVirtual();
dimension.virtualSize(min || SCROLL_MIN, max || SCROLL_MAX);
};
virtualScroll(scroller.dimensions.x);
virtualScroll(scroller.dimensions.y);
scroller.disable();
},
tryActivate: function (p, meta) {
var toolService = this.toolService;
var options = toolService.diagram.options.pannable;
var enabled = meta.ctrlKey;
if (defined(options.key)) {
if (!options.key || options.key == 'none') {
enabled = noMeta(meta);
} else {
enabled = meta[options.key + 'Key'] && !(meta.ctrlKey && defined(toolService.hoveredItem));
}
}
return options !== false && enabled && !defined(toolService.hoveredAdorner) && !defined(toolService._hoveredConnector);
},
start: function () {
this.scroller.enable();
},
move: function () {
},
_move: function (args) {
var tool = this, diagram = tool.toolService.diagram, canvas = diagram.canvas, scrollPos = new Point(args.scrollLeft, args.scrollTop);
if (canvas.translate) {
diagram._storePan(scrollPos.times(-1));
tool.movableCanvas.moveTo(scrollPos);
canvas.translate(scrollPos.x, scrollPos.y);
} else {
scrollPos = scrollPos.plus(diagram._pan.times(-1));
}
diagram.trigger(PAN, { pan: scrollPos });
},
end: function () {
this.scroller.disable();
},
getCursor: function () {
return Cursors.move;
}
});
var PointerTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
},
tryActivate: function () {
return true;
},
start: function (p, meta) {
var toolService = this.toolService, diagram = toolService.diagram, hoveredItem = toolService.hoveredItem, selectable = diagram.options.selectable;
if (hoveredItem) {
if (tryActivateSelection(selectable, meta)) {
selectSingle(hoveredItem, meta);
}
if (hoveredItem.adorner) {
this.adorner = hoveredItem.adorner;
this.handle = this.adorner._hitTest(p);
}
}
if (!this.handle) {
this.handle = diagram._resizingAdorner._hitTest(p);
if (this.handle) {
this.adorner = diagram._resizingAdorner;
}
}
if (this.adorner) {
if (!this.adorner.isDragHandle(this.handle) || !diagram.trigger(DRAG_START, {
shapes: this.adorner.shapes,
connections: []
})) {
this.adorner.start(p);
} else {
toolService.startPoint = p;
toolService.end(p);
}
}
},
move: function (p) {
if (this.adorner) {
this.adorner.move(this.handle, p);
if (this.adorner.isDragHandle(this.handle)) {
this.toolService.diagram.trigger(DRAG, {
shapes: this.adorner.shapes,
connections: []
});
}
}
},
end: function (p, meta) {
var diagram = this.toolService.diagram, service = this.toolService, adorner = this.adorner, unit;
if (adorner) {
if (!adorner.isDragHandle(this.handle) || !diagram.trigger(DRAG_END, {
shapes: adorner.shapes,
connections: []
})) {
unit = adorner.stop();
if (unit) {
diagram.undoRedoService.add(unit, false);
}
} else {
adorner.cancel();
}
}
if (service.hoveredItem) {
this.toolService.triggerClick({
item: service.hoveredItem,
point: p,
meta: meta
});
}
this.adorner = undefined;
this.handle = undefined;
},
getCursor: function (p) {
return this.toolService.hoveredItem ? this.toolService.hoveredItem._getCursor(p) : Cursors.arrow;
}
});
var SelectionTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
},
tryActivate: function (p, meta) {
var toolService = this.toolService;
var enabled = tryActivateSelection(toolService.diagram.options.selectable, meta);
return enabled && !defined(toolService.hoveredItem) && !defined(toolService.hoveredAdorner);
},
start: function (p) {
var diagram = this.toolService.diagram;
diagram.deselect();
diagram.selector.start(p);
},
move: function (p) {
var diagram = this.toolService.diagram;
diagram.selector.move(p);
},
end: function (p, meta) {
var diagram = this.toolService.diagram, hoveredItem = this.toolService.hoveredItem;
var rect = diagram.selector.bounds();
if ((!hoveredItem || !hoveredItem.isSelected) && !meta.ctrlKey) {
diagram.deselect();
}
if (!rect.isEmpty()) {
diagram.selectArea(rect);
}
diagram.selector.end();
},
getCursor: function () {
return Cursors.arrow;
}
});
var ConnectionTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
this.type = 'ConnectionTool';
},
tryActivate: function () {
return this.toolService._hoveredConnector;
},
start: function (p, meta) {
var diagram = this.toolService.diagram, connector = this.toolService._hoveredConnector, connection = diagram._createConnection({}, connector._c, p);
if (canDrag(connection) && !diagram.trigger(DRAG_START, {
shapes: [],
connections: [connection]
}) && diagram._addConnection(connection)) {
this.toolService._connectionManipulation(connection, connector._c.shape, true);
this.toolService._removeHover();
selectSingle(this.toolService.activeConnection, meta);
} else {
connection.source(null);
this.toolService.end(p);
}
},
move: function (p) {
var toolService = this.toolService;
var connection = toolService.activeConnection;
connection.target(p);
toolService.diagram.trigger(DRAG, {
shapes: [],
connections: [connection]
});
return true;
},
end: function (p) {
var toolService = this.toolService, d = toolService.diagram, connection = toolService.activeConnection, hoveredItem = toolService.hoveredItem, connector = toolService._hoveredConnector, target;
if (!connection) {
return;
}
if (connector && connector._c != connection.sourceConnector) {
target = connector._c;
} else if (hoveredItem && hoveredItem instanceof diagram.Shape) {
target = hoveredItem.getConnector(AUTO) || hoveredItem.getConnector(p);
} else {
target = p;
}
connection.target(target);
if (!d.trigger(DRAG_END, {
shapes: [],
connections: [connection]
})) {
connection.updateModel();
d._syncConnectionChanges();
} else {
d.remove(connection, false);
d.undoRedoService.pop();
}
toolService._connectionManipulation();
},
getCursor: function () {
return Cursors.arrow;
}
});
var ConnectionEditTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
this.type = 'ConnectionTool';
},
tryActivate: function (p, meta) {
var toolService = this.toolService, diagram = toolService.diagram, selectable = diagram.options.selectable, item = toolService.hoveredItem, isActive = tryActivateSelection(selectable, meta) && item && item.path && !(item.isSelected && meta.ctrlKey);
if (isActive) {
this._c = item;
}
return isActive;
},
start: function (p, meta) {
var connection = this._c;
selectSingle(connection, meta);
var adorner = connection.adorner;
if (canDrag(connection) && adorner && !this.toolService.diagram.trigger(DRAG_START, {
shapes: [],
connections: [connection]
})) {
this.handle = adorner._hitTest(p);
adorner.start(p);
} else {
this.toolService.startPoint = p;
this.toolService.end(p);
}
},
move: function (p) {
var adorner = this._c.adorner;
if (canDrag(this._c) && adorner) {
adorner.move(this.handle, p);
this.toolService.diagram.trigger(DRAG, {
shapes: [],
connections: [this._c]
});
return true;
}
},
end: function (p, meta) {
var connection = this._c;
var adorner = connection.adorner;
var toolService = this.toolService;
var diagram = toolService.diagram;
if (adorner) {
toolService.triggerClick({
item: connection,
point: p,
meta: meta
});
if (canDrag(connection)) {
var unit = adorner.stop(p);
if (!diagram.trigger(DRAG_END, {
shapes: [],
connections: [connection]
})) {
diagram.undoRedoService.add(unit, false);
connection.updateModel();
diagram._syncConnectionChanges();
} else {
unit.undo();
}
}
}
},
getCursor: function () {
return Cursors.move;
}
});
function testKey(key, str) {
return str.charCodeAt(0) == key || str.toUpperCase().charCodeAt(0) == key;
}
var ToolService = Class.extend({
init: function (diagram) {
this.diagram = diagram;
this.tools = [
new ScrollerTool(this),
new ConnectionEditTool(this),
new ConnectionTool(this),
new SelectionTool(this),
new PointerTool(this)
];
this.activeTool = undefined;
},
start: function (p, meta) {
meta = deepExtend({}, meta);
if (this.activeTool) {
this.activeTool.end(p, meta);
}
this._updateHoveredItem(p);
this._activateTool(p, meta);
this.activeTool.start(p, meta);
this._updateCursor(p);
this.diagram.focus();
this.startPoint = p;
return true;
},
move: function (p, meta) {
meta = deepExtend({}, meta);
var updateHovered = true;
if (this.activeTool) {
updateHovered = this.activeTool.move(p, meta);
}
if (updateHovered) {
this._updateHoveredItem(p);
}
this._updateCursor(p);
return true;
},
end: function (p, meta) {
meta = deepExtend({}, meta);
if (this.activeTool) {
this.activeTool.end(p, meta);
}
this.activeTool = undefined;
this._updateCursor(p);
return true;
},
keyDown: function (key, meta) {
var diagram = this.diagram;
meta = deepExtend({
ctrlKey: false,
metaKey: false,
altKey: false
}, meta);
if ((meta.ctrlKey || meta.metaKey) && !meta.altKey) {
if (testKey(key, 'a')) {
diagram.selectAll();
diagram._destroyToolBar();
return true;
} else if (testKey(key, 'z')) {
diagram.undo();
diagram._destroyToolBar();
return true;
} else if (testKey(key, 'y')) {
diagram.redo();
diagram._destroyToolBar();
return true;
} else if (testKey(key, 'c')) {
diagram.copy();
diagram._destroyToolBar();
} else if (testKey(key, 'x')) {
diagram.cut();
diagram._destroyToolBar();
} else if (testKey(key, 'v')) {
diagram.paste();
diagram._destroyToolBar();
} else if (testKey(key, 'l')) {
diagram.layout();
diagram._destroyToolBar();
} else if (testKey(key, 'd')) {
diagram._destroyToolBar();
diagram.copy();
diagram.paste();
}
} else if (key === 46 || key === 8) {
var toRemove = this.diagram._triggerRemove(diagram.select());
if (toRemove.length) {
this.diagram.remove(toRemove, true);
this.diagram._syncChanges();
this.diagram._destroyToolBar();
}
return true;
} else if (key === 27) {
this._discardNewConnection();
diagram.deselect();
diagram._destroyToolBar();
return true;
}
},
wheel: function (p, meta) {
var diagram = this.diagram, delta = meta.delta, z = diagram.zoom(), options = diagram.options, zoomRate = options.zoomRate, zoomOptions = {
point: p,
meta: meta,
zoom: z
};
if (diagram.trigger(ZOOM_START, zoomOptions)) {
return;
}
if (delta < 0) {
z += zoomRate;
} else {
z -= zoomRate;
}
z = kendo.dataviz.round(Math.max(options.zoomMin, Math.min(options.zoomMax, z)), 2);
zoomOptions.zoom = z;
diagram.zoom(z, zoomOptions);
diagram.trigger(ZOOM_END, zoomOptions);
return true;
},
setTool: function (tool, index) {
tool.toolService = this;
this.tools[index] = tool;
},
triggerClick: function (data) {
if (this.startPoint.equals(data.point)) {
this.diagram.trigger('click', data);
}
},
_discardNewConnection: function () {
if (this.newConnection) {
this.diagram.remove(this.newConnection);
this.newConnection = undefined;
}
},
_activateTool: function (p, meta) {
for (var i = 0; i < this.tools.length; i++) {
var tool = this.tools[i];
if (tool.tryActivate(p, meta)) {
this.activeTool = tool;
break;
}
}
},
_updateCursor: function (p) {
var element = this.diagram.element;
var cursor = this.activeTool ? this.activeTool.getCursor(p) : this.hoveredAdorner ? this.hoveredAdorner._getCursor(p) : this.hoveredItem ? this.hoveredItem._getCursor(p) : Cursors.arrow;
element.css({ cursor: cursor });
if (browser.msie && browser.version == 7) {
element[0].style.cssText = element[0].style.cssText;
}
},
_connectionManipulation: function (connection, disabledShape, isNew) {
this.activeConnection = connection;
this.disabledShape = disabledShape;
if (isNew) {
this.newConnection = this.activeConnection;
} else {
this.newConnection = undefined;
}
},
_updateHoveredItem: function (p) {
var hit = this._hitTest(p);
var diagram = this.diagram;
if (hit != this.hoveredItem && (!this.disabledShape || hit != this.disabledShape)) {
if (this.hoveredItem) {
diagram.trigger(MOUSE_LEAVE, { item: this.hoveredItem });
this.hoveredItem._hover(false);
}
if (hit && hit.options.enable) {
diagram.trigger(MOUSE_ENTER, { item: hit });
this.hoveredItem = hit;
this.hoveredItem._hover(true);
} else {
this.hoveredItem = undefined;
}
}
},
_removeHover: function () {
if (this.hoveredItem) {
this.hoveredItem._hover(false);
this.hoveredItem = undefined;
}
},
_hitTest: function (point) {
var hit, d = this.diagram, item, i;
if (this._hoveredConnector) {
this._hoveredConnector._hover(false);
this._hoveredConnector = undefined;
}
if (d._connectorsAdorner._visible) {
hit = d._connectorsAdorner._hitTest(point);
if (hit) {
return hit;
}
}
hit = this.diagram._resizingAdorner._hitTest(point);
if (hit) {
this.hoveredAdorner = d._resizingAdorner;
if (hit.x !== 0 || hit.y !== 0) {
return;
}
hit = undefined;
} else {
this.hoveredAdorner = undefined;
}
if (!this.activeTool || this.activeTool.type !== 'ConnectionTool') {
var selectedConnections = [];
for (i = 0; i < d._selectedItems.length; i++) {
item = d._selectedItems[i];
if (item instanceof diagram.Connection) {
selectedConnections.push(item);
}
}
hit = this._hitTestItems(selectedConnections, point);
}
return hit || this._hitTestElements(point);
},
_hitTestElements: function (point) {
var diagram = this.diagram;
var shapeHit = this._hitTestItems(diagram.shapes, point);
var connectionHit = this._hitTestItems(diagram.connections, point);
var hit;
if ((!this.activeTool || this.activeTool.type != 'ConnectionTool') && shapeHit && connectionHit && !hitTestShapeConnectors(shapeHit, point)) {
var mainLayer = diagram.mainLayer;
var shapeIdx = inArray(shapeHit.visual, mainLayer.children);
var connectionIdx = inArray(connectionHit.visual, mainLayer.children);
hit = shapeIdx > connectionIdx ? shapeHit : connectionHit;
}
return hit || shapeHit || connectionHit;
},
_hitTestItems: function (array, point) {
var i, item, hit;
for (i = array.length - 1; i >= 0; i--) {
item = array[i];
hit = item._hitTest(point);
if (hit) {
return hit;
}
}
}
});
var ConnectionRouterBase = kendo.Class.extend({
init: function () {
}
});
var LinearConnectionRouter = ConnectionRouterBase.extend({
init: function (connection) {
var that = this;
ConnectionRouterBase.fn.init.call(that);
this.connection = connection;
},
hitTest: function (p) {
var rec = this.getBounds().inflate(HIT_TEST_DISTANCE);
if (!rec.contains(p)) {
return false;
}
return diagram.Geometry.distanceToPolyline(p, this.connection.allPoints()) < HIT_TEST_DISTANCE;
},
getBounds: function () {
var points = this.connection.allPoints(), s = points[0], e = points[points.length - 1], right = Math.max(s.x, e.x), left = Math.min(s.x, e.x), top = Math.min(s.y, e.y), bottom = Math.max(s.y, e.y);
for (var i = 1; i < points.length - 1; ++i) {
right = Math.max(right, points[i].x);
left = Math.min(left, points[i].x);
top = Math.min(top, points[i].y);
bottom = Math.max(bottom, points[i].y);
}
return new Rect(left, top, right - left, bottom - top);
}
});
var PolylineRouter = LinearConnectionRouter.extend({
init: function (connection) {
var that = this;
LinearConnectionRouter.fn.init.call(that);
this.connection = connection;
},
route: function () {
}
});
var CascadingRouter = LinearConnectionRouter.extend({
SAME_SIDE_DISTANCE_RATIO: 5,
init: function (connection) {
var that = this;
LinearConnectionRouter.fn.init.call(that);
this.connection = connection;
},
routePoints: function (start, end, sourceConnector, targetConnector) {
var result;
if (sourceConnector && targetConnector) {
result = this._connectorPoints(start, end, sourceConnector, targetConnector);
} else {
result = this._floatingPoints(start, end, sourceConnector);
}
return result;
},
route: function () {
var sourceConnector = this.connection._resolvedSourceConnector;
var targetConnector = this.connection._resolvedTargetConnector;
var start = this.connection.sourcePoint();
var end = this.connection.targetPoint();
var points = this.routePoints(start, end, sourceConnector, targetConnector);
this.connection.points(points);
},
_connectorSides: [
{
name: 'Top',
axis: 'y',
boundsPoint: 'topLeft',
secondarySign: 1
},
{
name: 'Left',
axis: 'x',
boundsPoint: 'topLeft',
secondarySign: 1
},
{
name: 'Bottom',
axis: 'y',
boundsPoint: 'bottomRight',
secondarySign: -1
},
{
name: 'Right',
axis: 'x',
boundsPoint: 'bottomRight',
secondarySign: -1
}
],
_connectorSide: function (connector, targetPoint) {
var position = connector.position();
var shapeBounds = connector.shape.bounds(ROTATED);
var bounds = {
topLeft: shapeBounds.topLeft(),
bottomRight: shapeBounds.bottomRight()
};
var sides = this._connectorSides;
var min = kendo.util.MAX_NUM;
var sideDistance;
var minSide;
var axis;
var side;
for (var idx = 0; idx < sides.length; idx++) {
side = sides[idx];
axis = side.axis;
sideDistance = Math.round(Math.abs(position[axis] - bounds[side.boundsPoint][axis]));
if (sideDistance < min) {
min = sideDistance;
minSide = side;
} else if (sideDistance === min && (position[axis] - targetPoint[axis]) * side.secondarySign > (position[minSide.axis] - targetPoint[minSide.axis]) * minSide.secondarySign) {
minSide = side;
}
}
return minSide.name;
},
_sameSideDistance: function (connector) {
var bounds = connector.shape.bounds(ROTATED);
return Math.min(bounds.width, bounds.height) / this.SAME_SIDE_DISTANCE_RATIO;
},
_connectorPoints: function (start, end, sourceConnector, targetConnector) {
var sourceConnectorSide = this._connectorSide(sourceConnector, end);
var targetConnectorSide = this._connectorSide(targetConnector, start);
var deltaX = end.x - start.x;
var deltaY = end.y - start.y;
var sameSideDistance = this._sameSideDistance(sourceConnector);
var result = [];
var pointX, pointY;
if (sourceConnectorSide === TOP || sourceConnectorSide == BOTTOM) {
if (targetConnectorSide == TOP || targetConnectorSide == BOTTOM) {
if (sourceConnectorSide == targetConnectorSide) {
if (sourceConnectorSide == TOP) {
pointY = Math.min(start.y, end.y) - sameSideDistance;
} else {
pointY = Math.max(start.y, end.y) + sameSideDistance;
}
result = [
new Point(start.x, pointY),
new Point(end.x, pointY)
];
} else {
result = [
new Point(start.x, start.y + deltaY / 2),
new Point(end.x, start.y + deltaY / 2)
];
}
} else {
result = [new Point(start.x, end.y)];
}
} else {
if (targetConnectorSide == LEFT || targetConnectorSide == RIGHT) {
if (sourceConnectorSide == targetConnectorSide) {
if (sourceConnectorSide == LEFT) {
pointX = Math.min(start.x, end.x) - sameSideDistance;
} else {
pointX = Math.max(start.x, end.x) + sameSideDistance;
}
result = [
new Point(pointX, start.y),
new Point(pointX, end.y)
];
} else {
result = [
new Point(start.x + deltaX / 2, start.y),
new Point(start.x + deltaX / 2, start.y + deltaY)
];
}
} else {
result = [new Point(end.x, start.y)];
}
}
return result;
},
_floatingPoints: function (start, end, sourceConnector) {
var sourceConnectorSide = sourceConnector ? this._connectorSide(sourceConnector, end) : null;
var cascadeStartHorizontal = this._startHorizontal(start, end, sourceConnectorSide);
var points = [
start,
start,
end,
end
];
var deltaX = end.x - start.x;
var deltaY = end.y - start.y;
var length = points.length;
var shiftX;
var shiftY;
for (var idx = 1; idx < length - 1; ++idx) {
if (cascadeStartHorizontal) {
if (idx % 2 !== 0) {
shiftX = deltaX / (length / 2);
shiftY = 0;
} else {
shiftX = 0;
shiftY = deltaY / ((length - 1) / 2);
}
} else {
if (idx % 2 !== 0) {
shiftX = 0;
shiftY = deltaY / (length / 2);
} else {
shiftX = deltaX / ((length - 1) / 2);
shiftY = 0;
}
}
points[idx] = new Point(points[idx - 1].x + shiftX, points[idx - 1].y + shiftY);
}
idx--;
if (cascadeStartHorizontal && idx % 2 !== 0 || !cascadeStartHorizontal && idx % 2 === 0) {
points[length - 2] = new Point(points[length - 1].x, points[length - 2].y);
} else {
points[length - 2] = new Point(points[length - 2].x, points[length - 1].y);
}
return [
points[1],
points[2]
];
},
_startHorizontal: function (start, end, sourceSide) {
var horizontal;
if (sourceSide !== null && (sourceSide === RIGHT || sourceSide === LEFT)) {
horizontal = true;
} else {
horizontal = Math.abs(start.x - end.x) > Math.abs(start.y - end.y);
}
return horizontal;
}
});
var AdornerBase = Class.extend({
init: function (diagram, options) {
var that = this;
that.diagram = diagram;
that.options = deepExtend({}, that.options, options);
that.visual = new Group();
that.diagram._adorners.push(that);
},
refresh: function () {
}
});
var ConnectionEditAdorner = AdornerBase.extend({
init: function (connection, options) {
var that = this, diagram;
that.connection = connection;
diagram = that.connection.diagram;
that._ts = diagram.toolService;
AdornerBase.fn.init.call(that, diagram, options);
var sp = that.connection.sourcePoint();
var tp = that.connection.targetPoint();
that.spVisual = new Circle(deepExtend(that.options.handles, { center: sp }));
that.epVisual = new Circle(deepExtend(that.options.handles, { center: tp }));
that.visual.append(that.spVisual);
that.visual.append(that.epVisual);
},
options: { handles: {} },
_getCursor: function () {
return Cursors.move;
},
start: function (p) {
this.handle = this._hitTest(p);
this.startPoint = p;
this._initialSource = this.connection.source();
this._initialTarget = this.connection.target();
switch (this.handle) {
case -1:
if (this.connection.targetConnector) {
this._ts._connectionManipulation(this.connection, this.connection.targetConnector.shape);
}
break;
case 1:
if (this.connection.sourceConnector) {
this._ts._connectionManipulation(this.connection, this.connection.sourceConnector.shape);
}
break;
}
},
move: function (handle, p) {
switch (handle) {
case -1:
this.connection.source(p);
break;
case 1:
this.connection.target(p);
break;
default:
var delta = p.minus(this.startPoint);
this.startPoint = p;
if (!this.connection.sourceConnector) {
this.connection.source(this.connection.sourcePoint().plus(delta));
}
if (!this.connection.targetConnector) {
this.connection.target(this.connection.targetPoint().plus(delta));
}
break;
}
this.refresh();
return true;
},
stop: function (p) {
var ts = this.diagram.toolService, item = ts.hoveredItem, target;
if (ts._hoveredConnector) {
target = ts._hoveredConnector._c;
} else if (item && item instanceof diagram.Shape) {
target = item.getConnector(AUTO) || item.getConnector(p);
} else {
target = p;
}
if (this.handle === -1) {
this.connection.source(target);
} else if (this.handle === 1) {
this.connection.target(target);
}
this.handle = undefined;
this._ts._connectionManipulation();
return new ConnectionEditUndoUnit(this.connection, this._initialSource, this._initialTarget);
},
_hitTest: function (p) {
var sp = this.connection.sourcePoint(), tp = this.connection.targetPoint(), rx = this.options.handles.width / 2, ry = this.options.handles.height / 2, sb = new Rect(sp.x, sp.y).inflate(rx, ry), tb = new Rect(tp.x, tp.y).inflate(rx, ry);
return sb.contains(p) ? -1 : tb.contains(p) ? 1 : 0;
},
refresh: function () {
this.spVisual.redraw({ center: this.diagram.modelToLayer(this.connection.sourcePoint()) });
this.epVisual.redraw({ center: this.diagram.modelToLayer(this.connection.targetPoint()) });
}
});
var ConnectorsAdorner = AdornerBase.extend({
init: function (diagram, options) {
var that = this;
AdornerBase.fn.init.call(that, diagram, options);
that._refreshHandler = function (e) {
if (e.item == that.shape) {
that.refresh();
}
};
},
show: function (shape) {
var that = this, len, i, ctr;
that._visible = true;
that.shape = shape;
that.diagram.bind(ITEMBOUNDSCHANGE, that._refreshHandler);
len = shape.connectors.length;
that.connectors = [];
that.visual.clear();
for (i = 0; i < len; i++) {
ctr = new ConnectorVisual(shape.connectors[i]);
that.connectors.push(ctr);
that.visual.append(ctr.visual);
}
that.visual.visible(true);
that.refresh();
},
destroy: function () {
var that = this;
that.diagram.unbind(ITEMBOUNDSCHANGE, that._refreshHandler);
that.shape = undefined;
that._visible = undefined;
that.visual.visible(false);
},
_hitTest: function (p) {
var ctr, i;
for (i = 0; i < this.connectors.length; i++) {
ctr = this.connectors[i];
if (ctr._hitTest(p)) {
ctr._hover(true);
this.diagram.toolService._hoveredConnector = ctr;
break;
}
}
},
refresh: function () {
if (this.shape) {
var bounds = this.shape.bounds();
bounds = this.diagram.modelToLayer(bounds);
this.visual.position(bounds.topLeft());
$.each(this.connectors, function () {
this.refresh();
});
}
}
});
function hitToOppositeSide(hit, bounds) {
var result;
if (hit.x == -1 && hit.y == -1) {
result = bounds.bottomRight();
} else if (hit.x == 1 && hit.y == 1) {
result = bounds.topLeft();
} else if (hit.x == -1 && hit.y == 1) {
result = bounds.topRight();
} else if (hit.x == 1 && hit.y == -1) {
result = bounds.bottomLeft();
} else if (hit.x === 0 && hit.y == -1) {
result = bounds.bottom();
} else if (hit.x === 0 && hit.y == 1) {
result = bounds.top();
} else if (hit.x == 1 && hit.y === 0) {
result = bounds.left();
} else if (hit.x == -1 && hit.y === 0) {
result = bounds.right();
}
return result;
}
var ResizingAdorner = AdornerBase.extend({
init: function (diagram, options) {
var that = this;
AdornerBase.fn.init.call(that, diagram, options);
that._manipulating = false;
that.map = [];
that.shapes = [];
that._initSelection();
that._createHandles();
that.redraw();
that.diagram.bind('select', function (e) {
that._initialize(e.selected);
});
that._refreshHandler = function () {
if (!that._internalChange) {
that.refreshBounds();
that.refresh();
}
};
that._rotatedHandler = function () {
if (that.shapes.length == 1) {
that._angle = that.shapes[0].rotate().angle;
}
that._refreshHandler();
};
that.diagram.bind(ITEMBOUNDSCHANGE, that._refreshHandler).bind(ITEMROTATE, that._rotatedHandler);
that.refreshBounds();
that.refresh();
},
options: {
handles: {
fill: { color: '#fff' },
stroke: { color: '#282828' },
height: 7,
width: 7,
hover: {
fill: { color: '#282828' },
stroke: { color: '#282828' }
}
},
selectable: {
stroke: {
color: '#778899',
width: 1,
dashType: 'dash'
},
fill: { color: TRANSPARENT }
},
offset: 10
},
_initSelection: function () {
var that = this;
var diagram = that.diagram;
var selectable = diagram.options.selectable;
var options = deepExtend({}, that.options.selectable, selectable);
that.rect = new Rectangle(options);
that.visual.append(that.rect);
},
_resizable: function () {
return this.options.editable && this.options.editable.resize !== false;
},
_handleOptions: function () {
return (this.options.editable.resize || {}).handles || this.options.handles;
},
_createHandles: function () {
var handles, item, y, x;
if (this._resizable()) {
handles = this._handleOptions();
for (x = -1; x <= 1; x++) {
for (y = -1; y <= 1; y++) {
if (x !== 0 || y !== 0) {
item = new Rectangle(handles);
item.drawingElement._hover = proxy(this._hover, this);
this.map.push({
x: x,
y: y,
visual: item
});
this.visual.append(item);
}
}
}
}
},
bounds: function (value) {
if (value) {
this._innerBounds = value.clone();
this._bounds = this.diagram.modelToLayer(value).inflate(this.options.offset, this.options.offset);
} else {
return this._bounds;
}
},
_hitTest: function (p) {
var tp = this.diagram.modelToLayer(p), i, hit, handleBounds, handlesCount = this.map.length, handle;
if (this._angle) {
tp = tp.clone().rotate(this._bounds.center(), this._angle);
}
if (this._resizable()) {
for (i = 0; i < handlesCount; i++) {
handle = this.map[i];
hit = new Point(handle.x, handle.y);
handleBounds = this._getHandleBounds(hit);
handleBounds.offset(this._bounds.x, this._bounds.y);
if (handleBounds.contains(tp)) {
return hit;
}
}
}
if (this._bounds.contains(tp)) {
return new Point(0, 0);
}
},
_getHandleBounds: function (p) {
if (this._resizable()) {
var handles = this._handleOptions(), w = handles.width, h = handles.height, r = new Rect(0, 0, w, h);
if (p.x < 0) {
r.x = -w / 2;
} else if (p.x === 0) {
r.x = Math.floor(this._bounds.width / 2) - w / 2;
} else if (p.x > 0) {
r.x = this._bounds.width + 1 - w / 2;
}
if (p.y < 0) {
r.y = -h / 2;
} else if (p.y === 0) {
r.y = Math.floor(this._bounds.height / 2) - h / 2;
} else if (p.y > 0) {
r.y = this._bounds.height + 1 - h / 2;
}
return r;
}
},
_getCursor: function (point) {
var hit = this._hitTest(point);
if (hit && hit.x >= -1 && hit.x <= 1 && hit.y >= -1 && hit.y <= 1 && this._resizable()) {
var angle = this._angle;
if (angle) {
angle = 360 - angle;
hit.rotate(new Point(0, 0), angle);
hit = new Point(Math.round(hit.x), Math.round(hit.y));
}
if (hit.x == -1 && hit.y == -1) {
return 'nw-resize';
}
if (hit.x == 1 && hit.y == 1) {
return 'se-resize';
}
if (hit.x == -1 && hit.y == 1) {
return 'sw-resize';
}
if (hit.x == 1 && hit.y == -1) {
return 'ne-resize';
}
if (hit.x === 0 && hit.y == -1) {
return 'n-resize';
}
if (hit.x === 0 && hit.y == 1) {
return 's-resize';
}
if (hit.x == 1 && hit.y === 0) {
return 'e-resize';
}
if (hit.x == -1 && hit.y === 0) {
return 'w-resize';
}
}
return this._manipulating ? Cursors.move : Cursors.select;
},
_initialize: function () {
var that = this, i, item, items = that.diagram.select();
that.shapes = [];
for (i = 0; i < items.length; i++) {
item = items[i];
if (item instanceof diagram.Shape) {
that.shapes.push(item);
item._rotationOffset = new Point();
}
}
that._angle = that.shapes.length == 1 ? that.shapes[0].rotate().angle : 0;
that._startAngle = that._angle;
that._rotates();
that._positions();
that.refreshBounds();
that.refresh();
that.redraw();
},
_rotates: function () {
var that = this, i, shape;
that.initialRotates = [];
for (i = 0; i < that.shapes.length; i++) {
shape = that.shapes[i];
that.initialRotates.push(shape.rotate().angle);
}
},
_positions: function () {
var that = this, i, shape;
that.initialStates = [];
for (i = 0; i < that.shapes.length; i++) {
shape = that.shapes[i];
that.initialStates.push(shape.bounds());
}
},
_hover: function (value, element) {
if (this._resizable()) {
var handleOptions = this._handleOptions(), hover = handleOptions.hover, stroke = handleOptions.stroke, fill = handleOptions.fill;
if (value && Utils.isDefined(hover.stroke)) {
stroke = deepExtend({}, stroke, hover.stroke);
}
if (value && Utils.isDefined(hover.fill)) {
fill = hover.fill;
}
element.stroke(stroke.color, stroke.width, stroke.opacity);
element.fill(fill.color, fill.opacity);
}
},
start: function (p) {
this._sp = p;
this._cp = p;
this._lp = p;
this._manipulating = true;
this._internalChange = true;
this.shapeStates = [];
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
this.shapeStates.push(shape.bounds());
}
},
redraw: function () {
var i, handle, visibleHandles = this._resizable();
for (i = 0; i < this.map.length; i++) {
handle = this.map[i];
handle.visual.visible(visibleHandles);
}
},
angle: function (value) {
if (defined(value)) {
this._angle = value;
}
return this._angle;
},
rotate: function () {
var center = this._innerBounds.center();
var currentAngle = this.angle();
this._internalChange = true;
for (var i = 0; i < this.shapes.length; i++) {
var shape = this.shapes[i];
currentAngle = (currentAngle + this.initialRotates[i] - this._startAngle) % 360;
shape.rotate(currentAngle, center);
}
this.refresh();
},
move: function (handle, p) {
var delta, dragging, dtl = new Point(), dbr = new Point(), bounds, center, shape, i, angle, newBounds, changed = 0, staticPoint, scaleX, scaleY;
if (handle.y === -2 && handle.x === -1) {
center = this._innerBounds.center();
this._angle = this._truncateAngle(Utils.findAngle(center, p));
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
angle = (this._angle + this.initialRotates[i] - this._startAngle) % 360;
shape.rotate(angle, center);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape);
}
this._rotating = true;
}
this.refresh();
} else {
if (this.shouldSnap()) {
var thr = this._truncateDistance(p.minus(this._lp));
if (thr.x === 0 && thr.y === 0) {
this._cp = p;
return;
}
delta = thr;
this._lp = new Point(this._lp.x + thr.x, this._lp.y + thr.y);
} else {
delta = p.minus(this._cp);
}
if (this.isDragHandle(handle)) {
dbr = dtl = delta;
dragging = true;
} else {
if (this._angle) {
delta.rotate(new Point(0, 0), this._angle);
}
if (handle.x == -1) {
dtl.x = delta.x;
} else if (handle.x == 1) {
dbr.x = delta.x;
}
if (handle.y == -1) {
dtl.y = delta.y;
} else if (handle.y == 1) {
dbr.y = delta.y;
}
}
if (!dragging) {
staticPoint = hitToOppositeSide(handle, this._innerBounds);
scaleX = (this._innerBounds.width + delta.x * handle.x) / this._innerBounds.width;
scaleY = (this._innerBounds.height + delta.y * handle.y) / this._innerBounds.height;
}
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
bounds = shape.bounds();
if (dragging) {
if (!canDrag(shape)) {
continue;
}
newBounds = this._displaceBounds(bounds, dtl, dbr, dragging);
} else {
newBounds = bounds.clone();
newBounds.scale(scaleX, scaleY, staticPoint, this._innerBounds.center(), shape.rotate().angle);
var newCenter = newBounds.center();
newCenter.rotate(bounds.center(), -this._angle);
newBounds = new Rect(newCenter.x - newBounds.width / 2, newCenter.y - newBounds.height / 2, newBounds.width, newBounds.height);
}
if (newBounds.width >= shape.options.minWidth && newBounds.height >= shape.options.minHeight) {
var oldBounds = bounds;
shape.bounds(newBounds);
if (shape.hasOwnProperty('layout')) {
shape.layout(shape, oldBounds, newBounds);
}
if (oldBounds.width !== newBounds.width || oldBounds.height !== newBounds.height) {
shape.rotate(shape.rotate().angle);
}
changed += 1;
}
}
if (changed) {
if (changed == i) {
newBounds = this._displaceBounds(this._innerBounds, dtl, dbr, dragging);
this.bounds(newBounds);
} else {
this.refreshBounds();
}
this.refresh();
}
this._positions();
}
this._cp = p;
},
isDragHandle: function (handle) {
return handle.x === 0 && handle.y === 0;
},
cancel: function () {
var shapes = this.shapes;
var states = this.shapeStates;
for (var idx = 0; idx < shapes.length; idx++) {
shapes[idx].bounds(states[idx]);
}
this.refreshBounds();
this.refresh();
this._manipulating = undefined;
this._internalChange = undefined;
this._rotating = undefined;
},
_truncatePositionToGuides: function (bounds) {
if (this.diagram.ruler) {
return this.diagram.ruler.truncatePositionToGuides(bounds);
}
return bounds;
},
_truncateSizeToGuides: function (bounds) {
if (this.diagram.ruler) {
return this.diagram.ruler.truncateSizeToGuides(bounds);
}
return bounds;
},
_truncateAngle: function (a) {
var snap = this.snapOptions();
var snapAngle = Math.max(snap.angle || DEFAULT_SNAP_ANGLE, MIN_SNAP_ANGLE);
return snap ? Math.floor(a % 360 / snapAngle) * snapAngle : a % 360;
},
_truncateDistance: function (d) {
if (d instanceof diagram.Point) {
return new diagram.Point(this._truncateDistance(d.x), this._truncateDistance(d.y));
} else {
var snap = this.snapOptions() || {};
var snapSize = Math.max(snap.size || DEFAULT_SNAP_SIZE, MIN_SNAP_SIZE);
return snap ? Math.floor(d / snapSize) * snapSize : d;
}
},
snapOptions: function () {
var editable = this.diagram.options.editable;
var snap = ((editable || {}).drag || {}).snap || {};
return snap;
},
shouldSnap: function () {
var editable = this.diagram.options.editable;
var drag = (editable || {}).drag;
var snap = (drag || {}).snap;
return editable !== false && drag !== false && snap !== false;
},
_displaceBounds: function (bounds, dtl, dbr, dragging) {
var tl = bounds.topLeft().plus(dtl), br = bounds.bottomRight().plus(dbr), newBounds = Rect.fromPoints(tl, br), newCenter;
if (!dragging) {
newCenter = newBounds.center();
newCenter.rotate(bounds.center(), -this._angle);
newBounds = new Rect(newCenter.x - newBounds.width / 2, newCenter.y - newBounds.height / 2, newBounds.width, newBounds.height);
}
return newBounds;
},
stop: function () {
var unit, i, shape;
if (this._cp != this._sp) {
if (this._rotating) {
unit = new RotateUnit(this, this.shapes, this.initialRotates);
this._rotating = false;
} else if (this._diffStates()) {
if (this.diagram.ruler) {
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
var bounds = shape.bounds();
bounds = this._truncateSizeToGuides(this._truncatePositionToGuides(bounds));
shape.bounds(bounds);
this.refreshBounds();
this.refresh();
}
}
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
shape.updateModel();
}
unit = new TransformUnit(this.shapes, this.shapeStates, this);
this.diagram._syncShapeChanges();
}
}
this._manipulating = undefined;
this._internalChange = undefined;
this._rotating = undefined;
return unit;
},
_diffStates: function () {
var shapes = this.shapes;
var states = this.shapeStates;
for (var idx = 0; idx < shapes.length; idx++) {
if (!shapes[idx].bounds().equals(states[idx])) {
return true;
}
}
return false;
},
refreshBounds: function () {
var bounds = this.shapes.length == 1 ? this.shapes[0].bounds().clone() : this.diagram.boundingBox(this.shapes, true);
this.bounds(bounds);
},
refresh: function () {
var that = this, b, bounds;
if (this.shapes.length > 0) {
bounds = this.bounds();
this.visual.visible(true);
this.visual.position(bounds.topLeft());
$.each(this.map, function () {
b = that._getHandleBounds(new Point(this.x, this.y));
this.visual.position(b.topLeft());
});
this.visual.position(bounds.topLeft());
var center = new Point(bounds.width / 2, bounds.height / 2);
this.visual.rotate(this._angle, center);
this.rect.redraw({
width: bounds.width,
height: bounds.height
});
if (this.rotationThumb) {
var thumb = this.options.editable.rotate.thumb;
this._rotationThumbBounds = new Rect(bounds.center().x, bounds.y + thumb.y, 0, 0).inflate(thumb.width);
this.rotationThumb.redraw({ x: bounds.width / 2 - thumb.width / 2 });
}
} else {
this.visual.visible(false);
}
}
});
var Selector = Class.extend({
init: function (diagram) {
var selectable = diagram.options.selectable;
this.options = deepExtend({}, this.options, selectable);
this.visual = new Rectangle(this.options);
this.diagram = diagram;
},
options: {
stroke: {
color: '#778899',
width: 1,
dashType: 'dash'
},
fill: { color: TRANSPARENT }
},
start: function (p) {
this._sp = this._ep = p;
this.refresh();
this.diagram._adorn(this, true);
},
end: function () {
this._sp = this._ep = undefined;
this.diagram._adorn(this, false);
},
bounds: function (value) {
if (value) {
this._bounds = value;
}
return this._bounds;
},
move: function (p) {
this._ep = p;
this.refresh();
},
refresh: function () {
if (this._sp) {
var visualBounds = Rect.fromPoints(this.diagram.modelToLayer(this._sp), this.diagram.modelToLayer(this._ep));
this.bounds(Rect.fromPoints(this._sp, this._ep));
this.visual.position(visualBounds.topLeft());
this.visual.redraw({
height: visualBounds.height + 1,
width: visualBounds.width + 1
});
}
}
});
var ConnectorVisual = Class.extend({
init: function (connector) {
this.options = deepExtend({}, connector.options);
this._c = connector;
this.visual = new Circle(this.options);
this.refresh();
},
_hover: function (value) {
var options = this.options, hover = options.hover, stroke = options.stroke, fill = options.fill;
if (value && Utils.isDefined(hover.stroke)) {
stroke = deepExtend({}, stroke, hover.stroke);
}
if (value && Utils.isDefined(hover.fill)) {
fill = hover.fill;
}
this.visual.redraw({
stroke: stroke,
fill: fill
});
},
refresh: function () {
var p = this._c.shape.diagram.modelToView(this._c.position()), relative = p.minus(this._c.shape.bounds('transformed').topLeft()), value = new Rect(p.x, p.y, 0, 0);
value.inflate(this.options.width / 2, this.options.height / 2);
this._visualBounds = value;
this.visual.redraw({ center: new Point(relative.x, relative.y) });
},
_hitTest: function (p) {
var tp = this._c.shape.diagram.modelToView(p);
return this._visualBounds.contains(tp);
}
});
function canDrag(element) {
var editable = element.options.editable;
return editable && editable.drag !== false;
}
function hitTestShapeConnectors(shape, point) {
var connector, position, rect;
for (var idx = 0; idx < shape.connectors.length; idx++) {
connector = shape.connectors[idx];
position = connector.position();
rect = new Rect(position.x, position.y);
rect.inflate(HIT_TEST_DISTANCE, HIT_TEST_DISTANCE);
if (rect.contains(point)) {
return connector;
}
}
}
deepExtend(diagram, {
CompositeUnit: CompositeUnit,
TransformUnit: TransformUnit,
PanUndoUnit: PanUndoUnit,
AddShapeUnit: AddShapeUnit,
AddConnectionUnit: AddConnectionUnit,
DeleteShapeUnit: DeleteShapeUnit,
DeleteConnectionUnit: DeleteConnectionUnit,
ConnectionEditAdorner: ConnectionEditAdorner,
ConnectionTool: ConnectionTool,
ConnectorVisual: ConnectorVisual,
UndoRedoService: UndoRedoService,
ResizingAdorner: ResizingAdorner,
Selector: Selector,
ToolService: ToolService,
ConnectorsAdorner: ConnectorsAdorner,
LayoutUndoUnit: LayoutUndoUnit,
ConnectionEditUnit: ConnectionEditUnit,
ToFrontUnit: ToFrontUnit,
ToBackUnit: ToBackUnit,
ConnectionRouterBase: ConnectionRouterBase,
PolylineRouter: PolylineRouter,
CascadingRouter: CascadingRouter,
SelectionTool: SelectionTool,
ScrollerTool: ScrollerTool,
PointerTool: PointerTool,
ConnectionEditTool: ConnectionEditTool,
RotateUnit: RotateUnit
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/layout', ['dataviz/diagram/math'], f);
}(function () {
(function ($, undefined) {
var kendo = window.kendo, diagram = kendo.dataviz.diagram, Graph = diagram.Graph, Node = diagram.Node, Link = diagram.Link, deepExtend = kendo.deepExtend, Size = diagram.Size, Rect = diagram.Rect, Dictionary = diagram.Dictionary, Set = diagram.Set, HyperTree = diagram.Graph, Utils = diagram.Utils, Point = diagram.Point, EPSILON = 0.000001, DEG_TO_RAD = Math.PI / 180, contains = Utils.contains, grep = $.grep;
var LayoutBase = kendo.Class.extend({
defaultOptions: {
type: 'Tree',
subtype: 'Down',
roots: null,
animate: false,
limitToView: false,
friction: 0.9,
nodeDistance: 50,
iterations: 300,
horizontalSeparation: 90,
verticalSeparation: 50,
underneathVerticalTopOffset: 15,
underneathHorizontalOffset: 15,
underneathVerticalSeparation: 15,
grid: {
width: 1500,
offsetX: 50,
offsetY: 50,
componentSpacingX: 20,
componentSpacingY: 20
},
layerSeparation: 50,
layeredIterations: 2,
startRadialAngle: 0,
endRadialAngle: 360,
radialSeparation: 150,
radialFirstLevelSeparation: 200,
keepComponentsInOneRadialLayout: false,
ignoreContainers: true,
layoutContainerChildren: false,
ignoreInvisible: true,
animateTransitions: false
},
init: function () {
},
gridLayoutComponents: function (components) {
if (!components) {
throw 'No components supplied.';
}
Utils.forEach(components, function (c) {
c.calcBounds();
});
components.sort(function (a, b) {
return b.bounds.width - a.bounds.width;
});
var maxWidth = this.options.grid.width, offsetX = this.options.grid.componentSpacingX, offsetY = this.options.grid.componentSpacingY, height = 0, startX = this.options.grid.offsetX, startY = this.options.grid.offsetY, x = startX, y = startY, i, resultLinkSet = [], resultNodeSet = [];
while (components.length > 0) {
if (x >= maxWidth) {
x = startX;
y += height + offsetY;
height = 0;
}
var component = components.pop();
this.moveToOffset(component, new Point(x, y));
for (i = 0; i < component.nodes.length; i++) {
resultNodeSet.push(component.nodes[i]);
}
for (i = 0; i < component.links.length; i++) {
resultLinkSet.push(component.links[i]);
}
var boundingRect = component.bounds;
var currentHeight = boundingRect.height;
if (currentHeight <= 0 || isNaN(currentHeight)) {
currentHeight = 0;
}
var currentWidth = boundingRect.width;
if (currentWidth <= 0 || isNaN(currentWidth)) {
currentWidth = 0;
}
if (currentHeight >= height) {
height = currentHeight;
}
x += currentWidth + offsetX;
}
return {
nodes: resultNodeSet,
links: resultLinkSet
};
},
moveToOffset: function (component, p) {
var i, j, bounds = component.bounds, deltax = p.x - bounds.x, deltay = p.y - bounds.y;
for (i = 0; i < component.nodes.length; i++) {
var node = component.nodes[i];
var nodeBounds = node.bounds();
if (nodeBounds.width === 0 && nodeBounds.height === 0 && nodeBounds.x === 0 && nodeBounds.y === 0) {
nodeBounds = new Rect(0, 0, 0, 0);
}
nodeBounds.x += deltax;
nodeBounds.y += deltay;
node.bounds(nodeBounds);
}
for (i = 0; i < component.links.length; i++) {
var link = component.links[i];
if (link.points) {
var newpoints = [];
var points = link.points;
for (j = 0; j < points.length; j++) {
var pt = points[j];
pt.x += deltax;
pt.y += deltay;
newpoints.push(pt);
}
link.points = newpoints;
}
}
this.currentHorizontalOffset += bounds.width + this.options.grid.offsetX;
return new Point(deltax, deltay);
},
transferOptions: function (options) {
this.options = kendo.deepExtend({}, this.defaultOptions);
if (Utils.isUndefined(options)) {
return;
}
this.options = kendo.deepExtend(this.options, options || {});
}
});
var DiagramToHyperTreeAdapter = kendo.Class.extend({
init: function (diagram) {
this.nodeMap = new Dictionary();
this.shapeMap = new Dictionary();
this.nodes = [];
this.edges = [];
this.edgeMap = new Dictionary();
this.finalNodes = [];
this.finalLinks = [];
this.ignoredConnections = [];
this.ignoredShapes = [];
this.hyperMap = new Dictionary();
this.hyperTree = new Graph();
this.finalGraph = null;
this.diagram = diagram;
},
convert: function (options) {
if (Utils.isUndefined(this.diagram)) {
throw 'No diagram to convert.';
}
this.options = kendo.deepExtend({
ignoreInvisible: true,
ignoreContainers: true,
layoutContainerChildren: false
}, options || {});
this.clear();
this._renormalizeShapes();
this._renormalizeConnections();
this.finalNodes = new Dictionary(this.nodes);
this.finalLinks = new Dictionary(this.edges);
this.finalGraph = new Graph();
this.finalNodes.forEach(function (n) {
this.finalGraph.addNode(n);
}, this);
this.finalLinks.forEach(function (l) {
this.finalGraph.addExistingLink(l);
}, this);
return this.finalGraph;
},
mapConnection: function (connection) {
return this.edgeMap.get(connection.id);
},
mapShape: function (shape) {
return this.nodeMap.get(shape.id);
},
getEdge: function (a, b) {
return Utils.first(a.links, function (link) {
return link.getComplement(a) === b;
});
},
clear: function () {
this.finalGraph = null;
this.hyperTree = !this.options.ignoreContainers && this.options.layoutContainerChildren ? new HyperTree() : null;
this.hyperMap = !this.options.ignoreContainers && this.options.layoutContainerChildren ? new Dictionary() : null;
this.nodeMap = new Dictionary();
this.shapeMap = new Dictionary();
this.nodes = [];
this.edges = [];
this.edgeMap = new Dictionary();
this.ignoredConnections = [];
this.ignoredShapes = [];
this.finalNodes = [];
this.finalLinks = [];
},
listToRoot: function (containerGraph) {
var list = [];
var s = containerGraph.container;
if (!s) {
return list;
}
list.push(s);
while (s.parentContainer) {
s = s.parentContainer;
list.push(s);
}
list.reverse();
return list;
},
firstNonIgnorableContainer: function (shape) {
if (shape.isContainer && !this._isIgnorableItem(shape)) {
return shape;
}
return !shape.parentContainer ? null : this.firstNonIgnorableContainer(shape.parentContainer);
},
isContainerConnection: function (a, b) {
if (a.isContainer && this.isDescendantOf(a, b)) {
return true;
}
return b.isContainer && this.isDescendantOf(b, a);
},
isDescendantOf: function (scope, a) {
if (!scope.isContainer) {
throw 'Expecting a container.';
}
if (scope === a) {
return false;
}
if (contains(scope.children, a)) {
return true;
}
var containers = [];
for (var i = 0, len = scope.children.length; i < len; i++) {
var c = scope.children[i];
if (c.isContainer && this.isDescendantOf(c, a)) {
containers.push(c);
}
}
return containers.length > 0;
},
isIgnorableItem: function (shape) {
if (this.options.ignoreInvisible) {
if (shape.isCollapsed && this._isVisible(shape)) {
return false;
}
if (!shape.isCollapsed && this._isVisible(shape)) {
return false;
}
return true;
} else {
return shape.isCollapsed && !this._isTop(shape);
}
},
isShapeMapped: function (shape) {
return shape.isCollapsed && !this._isVisible(shape) && !this._isTop(shape);
},
leastCommonAncestor: function (a, b) {
if (!a) {
throw 'Parameter should not be null.';
}
if (!b) {
throw 'Parameter should not be null.';
}
if (!this.hyperTree) {
throw 'No hypertree available.';
}
var al = this.listToRoot(a);
var bl = this.listToRoot(b);
var found = null;
if (Utils.isEmpty(al) || Utils.isEmpty(bl)) {
return this.hyperTree.root.data;
}
var xa = al[0];
var xb = bl[0];
var i = 0;
while (xa === xb) {
found = al[i];
i++;
if (i >= al.length || i >= bl.length) {
break;
}
xa = al[i];
xb = bl[i];
}
if (!found) {
return this.hyperTree.root.data;
} else {
return grep(this.hyperTree.nodes, function (n) {
return n.data.container === found;
});
}
},
_isTop: function (item) {
return !item.parentContainer;
},
_isVisible: function (shape) {
if (!shape.visible()) {
return false;
}
return !shape.parentContainer ? shape.visible() : this._isVisible(shape.parentContainer);
},
_isCollapsed: function (shape) {
if (shape.isContainer && shape.isCollapsed) {
return true;
}
return shape.parentContainer && this._isCollapsed(shape.parentContainer);
},
_renormalizeShapes: function () {
if (this.options.ignoreContainers) {
for (var i = 0, len = this.diagram.shapes.length; i < len; i++) {
var shape = this.diagram.shapes[i];
if (this.options.ignoreInvisible && !this._isVisible(shape) || shape.isContainer) {
this.ignoredShapes.push(shape);
continue;
}
var node = new Node(shape.id, shape);
node.isVirtual = false;
this.nodeMap.add(shape.id, node);
this.nodes.push(node);
}
} else {
throw 'Containers are not supported yet, but stay tuned.';
}
},
_renormalizeConnections: function () {
if (this.diagram.connections.length === 0) {
return;
}
for (var i = 0, len = this.diagram.connections.length; i < len; i++) {
var conn = this.diagram.connections[i];
if (this.isIgnorableItem(conn)) {
this.ignoredConnections.push(conn);
continue;
}
var source = !conn.sourceConnector ? null : conn.sourceConnector.shape;
var sink = !conn.targetConnector ? null : conn.targetConnector.shape;
if (!source || !sink) {
this.ignoredConnections.push(conn);
continue;
}
if (contains(this.ignoredShapes, source) && !this.shapeMap.containsKey(source)) {
this.ignoredConnections.push(conn);
continue;
}
if (contains(this.ignoredShapes, sink) && !this.shapeMap.containsKey(sink)) {
this.ignoredConnections.push(conn);
continue;
}
if (this.shapeMap.containsKey(source)) {
source = this.shapeMap[source];
}
if (this.shapeMap.containsKey(sink)) {
sink = this.shapeMap[sink];
}
var sourceNode = this.mapShape(source);
var sinkNode = this.mapShape(sink);
if (sourceNode === sinkNode || this.areConnectedAlready(sourceNode, sinkNode)) {
this.ignoredConnections.push(conn);
continue;
}
if (sourceNode === null || sinkNode === null) {
throw 'A shape was not mapped to a node.';
}
if (this.options.ignoreContainers) {
if (sourceNode.isVirtual || sinkNode.isVirtual) {
this.ignoredConnections.push(conn);
continue;
}
var newEdge = new Link(sourceNode, sinkNode, conn.id, conn);
this.edgeMap.add(conn.id, newEdge);
this.edges.push(newEdge);
} else {
throw 'Containers are not supported yet, but stay tuned.';
}
}
},
areConnectedAlready: function (n, m) {
return Utils.any(this.edges, function (l) {
return l.source === n && l.target === m || l.source === m && l.target === n;
});
}
});
var SpringLayout = LayoutBase.extend({
init: function (diagram) {
var that = this;
LayoutBase.fn.init.call(that);
if (Utils.isUndefined(diagram)) {
throw 'Diagram is not specified.';
}
this.diagram = diagram;
},
layout: function (options) {
this.transferOptions(options);
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
var graph = adapter.convert(options);
if (graph.isEmpty()) {
return;
}
var components = graph.getConnectedComponents();
if (Utils.isEmpty(components)) {
return;
}
for (var i = 0; i < components.length; i++) {
var component = components[i];
this.layoutGraph(component, options);
}
var finalNodeSet = this.gridLayoutComponents(components);
return new diagram.LayoutState(this.diagram, finalNodeSet);
},
layoutGraph: function (graph, options) {
if (Utils.isDefined(options)) {
this.transferOptions(options);
}
this.graph = graph;
var initialTemperature = this.options.nodeDistance * 9;
this.temperature = initialTemperature;
var guessBounds = this._expectedBounds();
this.width = guessBounds.width;
this.height = guessBounds.height;
for (var step = 0; step < this.options.iterations; step++) {
this.refineStage = step >= this.options.iterations * 5 / 6;
this.tick();
this.temperature = this.refineStage ? initialTemperature / 30 : initialTemperature * (1 - step / (2 * this.options.iterations));
}
},
tick: function () {
var i;
for (i = 0; i < this.graph.nodes.length; i++) {
this._repulsion(this.graph.nodes[i]);
}
for (i = 0; i < this.graph.links.length; i++) {
this._attraction(this.graph.links[i]);
}
for (i = 0; i < this.graph.nodes.length; i++) {
var node = this.graph.nodes[i];
var offset = Math.sqrt(node.dx * node.dx + node.dy * node.dy);
if (offset === 0) {
return;
}
node.x += Math.min(offset, this.temperature) * node.dx / offset;
node.y += Math.min(offset, this.temperature) * node.dy / offset;
if (this.options.limitToView) {
node.x = Math.min(this.width, Math.max(node.width / 2, node.x));
node.y = Math.min(this.height, Math.max(node.height / 2, node.y));
}
}
},
_shake: function (node) {
var rho = Math.random() * this.options.nodeDistance / 4;
var alpha = Math.random() * 2 * Math.PI;
node.x += rho * Math.cos(alpha);
node.y -= rho * Math.sin(alpha);
},
_InverseSquareForce: function (d, n, m) {
var force;
if (!this.refineStage) {
force = Math.pow(d, 2) / Math.pow(this.options.nodeDistance, 2);
} else {
var deltax = n.x - m.x;
var deltay = n.y - m.y;
var wn = n.width / 2;
var hn = n.height / 2;
var wm = m.width / 2;
var hm = m.height / 2;
force = Math.pow(deltax, 2) / Math.pow(wn + wm + this.options.nodeDistance, 2) + Math.pow(deltay, 2) / Math.pow(hn + hm + this.options.nodeDistance, 2);
}
return force * 4 / 3;
},
_SquareForce: function (d, n, m) {
return 1 / this._InverseSquareForce(d, n, m);
},
_repulsion: function (n) {
n.dx = 0;
n.dy = 0;
Utils.forEach(this.graph.nodes, function (m) {
if (m === n) {
return;
}
while (n.x === m.x && n.y === m.y) {
this._shake(m);
}
var vx = n.x - m.x;
var vy = n.y - m.y;
var distance = Math.sqrt(vx * vx + vy * vy);
var r = this._SquareForce(distance, n, m) * 2;
n.dx += vx / distance * r;
n.dy += vy / distance * r;
}, this);
},
_attraction: function (link) {
var t = link.target;
var s = link.source;
if (s === t) {
return;
}
while (s.x === t.x && s.y === t.y) {
this._shake(t);
}
var vx = s.x - t.x;
var vy = s.y - t.y;
var distance = Math.sqrt(vx * vx + vy * vy);
var a = this._InverseSquareForce(distance, s, t) * 5;
var dx = vx / distance * a;
var dy = vy / distance * a;
t.dx += dx;
t.dy += dy;
s.dx -= dx;
s.dy -= dy;
},
_expectedBounds: function () {
var size, N = this.graph.nodes.length, ratio = 1.5, multiplier = 4;
if (N === 0) {
return size;
}
size = Utils.fold(this.graph.nodes, function (s, node) {
var area = node.width * node.height;
if (area > 0) {
s += Math.sqrt(area);
return s;
}
return 0;
}, 0, this);
var av = size / N;
var squareSize = av * Math.ceil(Math.sqrt(N));
var width = squareSize * Math.sqrt(ratio);
var height = squareSize / Math.sqrt(ratio);
return {
width: width * multiplier,
height: height * multiplier
};
}
});
var TreeLayoutProcessor = kendo.Class.extend({
init: function (options) {
this.center = null;
this.options = options;
},
layout: function (treeGraph, root) {
this.graph = treeGraph;
if (!this.graph.nodes || this.graph.nodes.length === 0) {
return;
}
if (!contains(this.graph.nodes, root)) {
throw 'The given root is not in the graph.';
}
this.center = root;
this.graph.cacheRelationships();
this.layoutSwitch();
},
layoutLeft: function (left) {
this.setChildrenDirection(this.center, 'Left', false);
this.setChildrenLayout(this.center, 'Default', false);
var h = 0, w = 0, y, i, node;
for (i = 0; i < left.length; i++) {
node = left[i];
node.TreeDirection = 'Left';
var s = this.measure(node, Size.Empty);
w = Math.max(w, s.Width);
h += s.height + this.options.verticalSeparation;
}
h -= this.options.verticalSeparation;
var x = this.center.x - this.options.horizontalSeparation;
y = this.center.y + (this.center.height - h) / 2;
for (i = 0; i < left.length; i++) {
node = left[i];
var p = new Point(x - node.Size.width, y);
this.arrange(node, p);
y += node.Size.height + this.options.verticalSeparation;
}
},
layoutRight: function (right) {
this.setChildrenDirection(this.center, 'Right', false);
this.setChildrenLayout(this.center, 'Default', false);
var h = 0, w = 0, y, i, node;
for (i = 0; i < right.length; i++) {
node = right[i];
node.TreeDirection = 'Right';
var s = this.measure(node, Size.Empty);
w = Math.max(w, s.Width);
h += s.height + this.options.verticalSeparation;
}
h -= this.options.verticalSeparation;
var x = this.center.x + this.options.horizontalSeparation + this.center.width;
y = this.center.y + (this.center.height - h) / 2;
for (i = 0; i < right.length; i++) {
node = right[i];
var p = new Point(x, y);
this.arrange(node, p);
y += node.Size.height + this.options.verticalSeparation;
}
},
layoutUp: function (up) {
this.setChildrenDirection(this.center, 'Up', false);
this.setChildrenLayout(this.center, 'Default', false);
var w = 0, y, node, i;
for (i = 0; i < up.length; i++) {
node = up[i];
node.TreeDirection = 'Up';
var s = this.measure(node, Size.Empty);
w += s.width + this.options.horizontalSeparation;
}
w -= this.options.horizontalSeparation;
var x = this.center.x + this.center.width / 2 - w / 2;
for (i = 0; i < up.length; i++) {
node = up[i];
y = this.center.y - this.options.verticalSeparation - node.Size.height;
var p = new Point(x, y);
this.arrange(node, p);
x += node.Size.width + this.options.horizontalSeparation;
}
},
layoutDown: function (down) {
var node, i;
this.setChildrenDirection(this.center, 'Down', false);
this.setChildrenLayout(this.center, 'Default', false);
var w = 0, y;
for (i = 0; i < down.length; i++) {
node = down[i];
node.treeDirection = 'Down';
var s = this.measure(node, Size.Empty);
w += s.width + this.options.horizontalSeparation;
}
w -= this.options.horizontalSeparation;
var x = this.center.x + this.center.width / 2 - w / 2;
y = this.center.y + this.options.verticalSeparation + this.center.height;
for (i = 0; i < down.length; i++) {
node = down[i];
var p = new Point(x, y);
this.arrange(node, p);
x += node.Size.width + this.options.horizontalSeparation;
}
},
layoutRadialTree: function () {
this.setChildrenDirection(this.center, 'Radial', false);
this.setChildrenLayout(this.center, 'Default', false);
this.previousRoot = null;
var startAngle = this.options.startRadialAngle * DEG_TO_RAD;
var endAngle = this.options.endRadialAngle * DEG_TO_RAD;
if (endAngle <= startAngle) {
throw 'Final angle should not be less than the start angle.';
}
this.maxDepth = 0;
this.origin = new Point(this.center.x, this.center.y);
this.calculateAngularWidth(this.center, 0);
if (this.maxDepth > 0) {
this.radialLayout(this.center, this.options.radialFirstLevelSeparation, startAngle, endAngle);
}
this.center.Angle = endAngle - startAngle;
},
tipOverTree: function (down, startFromLevel) {
if (Utils.isUndefined(startFromLevel)) {
startFromLevel = 0;
}
this.setChildrenDirection(this.center, 'Down', false);
this.setChildrenLayout(this.center, 'Default', false);
this.setChildrenLayout(this.center, 'Underneath', false, startFromLevel);
var w = 0, y, node, i;
for (i = 0; i < down.length; i++) {
node = down[i];
node.TreeDirection = 'Down';
var s = this.measure(node, Size.Empty);
w += s.width + this.options.horizontalSeparation;
}
w -= this.options.horizontalSeparation;
w -= down[down.length - 1].width;
w += down[down.length - 1].associatedShape.bounds().width;
var x = this.center.x + this.center.width / 2 - w / 2;
y = this.center.y + this.options.verticalSeparation + this.center.height;
for (i = 0; i < down.length; i++) {
node = down[i];
var p = new Point(x, y);
this.arrange(node, p);
x += node.Size.width + this.options.horizontalSeparation;
}
},
calculateAngularWidth: function (n, d) {
if (d > this.maxDepth) {
this.maxDepth = d;
}
var aw = 0, w = 1000, h = 1000, diameter = d === 0 ? 0 : Math.sqrt(w * w + h * h) / d;
if (n.children.length > 0) {
for (var i = 0, len = n.children.length; i < len; i++) {
var child = n.children[i];
aw += this.calculateAngularWidth(child, d + 1);
}
aw = Math.max(diameter, aw);
} else {
aw = diameter;
}
n.sectorAngle = aw;
return aw;
},
sortChildren: function (n) {
var basevalue = 0, i;
if (n.parents.length > 1) {
throw 'Node is not part of a tree.';
}
var p = n.parents[0];
if (p) {
var pl = new Point(p.x, p.y);
var nl = new Point(n.x, n.y);
basevalue = this.normalizeAngle(Math.atan2(pl.y - nl.y, pl.x - nl.x));
}
var count = n.children.length;
if (count === 0) {
return null;
}
var angle = [];
var idx = [];
for (i = 0; i < count; ++i) {
var c = n.children[i];
var l = new Point(c.x, c.y);
idx[i] = i;
angle[i] = this.normalizeAngle(-basevalue + Math.atan2(l.y - l.y, l.x - l.x));
}
Utils.bisort(angle, idx);
var col = [];
var children = n.children;
for (i = 0; i < count; ++i) {
col.push(children[idx[i]]);
}
return col;
},
normalizeAngle: function (angle) {
while (angle > Math.PI * 2) {
angle -= 2 * Math.PI;
}
while (angle < 0) {
angle += Math.PI * 2;
}
return angle;
},
radialLayout: function (node, radius, startAngle, endAngle) {
var deltaTheta = endAngle - startAngle;
var deltaThetaHalf = deltaTheta / 2;
var parentSector = node.sectorAngle;
var fraction = 0;
var sorted = this.sortChildren(node);
for (var i = 0, len = sorted.length; i < len; i++) {
var childNode = sorted[i];
var cp = childNode;
var childAngleFraction = cp.sectorAngle / parentSector;
if (childNode.children.length > 0) {
this.radialLayout(childNode, radius + this.options.radialSeparation, startAngle + fraction * deltaTheta, startAngle + (fraction + childAngleFraction) * deltaTheta);
}
this.setPolarLocation(childNode, radius, startAngle + fraction * deltaTheta + childAngleFraction * deltaThetaHalf);
cp.angle = childAngleFraction * deltaTheta;
fraction += childAngleFraction;
}
},
setPolarLocation: function (node, radius, angle) {
node.x = this.origin.x + radius * Math.cos(angle);
node.y = this.origin.y + radius * Math.sin(angle);
node.BoundingRectangle = new Rect(node.x, node.y, node.width, node.height);
},
setChildrenDirection: function (node, direction, includeStart) {
var rootDirection = node.treeDirection;
this.graph.depthFirstTraversal(node, function (n) {
n.treeDirection = direction;
});
if (!includeStart) {
node.treeDirection = rootDirection;
}
},
setChildrenLayout: function (node, layout, includeStart, startFromLevel) {
if (Utils.isUndefined(startFromLevel)) {
startFromLevel = 0;
}
var rootLayout = node.childrenLayout;
if (startFromLevel > 0) {
this.graph.assignLevels(node);
this.graph.depthFirstTraversal(node, function (s) {
if (s.level >= startFromLevel + 1) {
s.childrenLayout = layout;
}
});
} else {
this.graph.depthFirstTraversal(node, function (s) {
s.childrenLayout = layout;
});
if (!includeStart) {
node.childrenLayout = rootLayout;
}
}
},
measure: function (node, givenSize) {
var w = 0, h = 0, s;
var result = new Size(0, 0);
if (!node) {
throw '';
}
var b = node.associatedShape.bounds();
var shapeWidth = b.width;
var shapeHeight = b.height;
if (node.parents.length !== 1) {
throw 'Node not in a spanning tree.';
}
var parent = node.parents[0];
if (node.treeDirection === 'Undefined') {
node.treeDirection = parent.treeDirection;
}
if (Utils.isEmpty(node.children)) {
result = new Size(Math.abs(shapeWidth) < EPSILON ? 50 : shapeWidth, Math.abs(shapeHeight) < EPSILON ? 25 : shapeHeight);
} else if (node.children.length === 1) {
switch (node.treeDirection) {
case 'Radial':
s = this.measure(node.children[0], givenSize);
w = shapeWidth + this.options.radialSeparation * Math.cos(node.AngleToParent) + s.width;
h = shapeHeight + Math.abs(this.options.radialSeparation * Math.sin(node.AngleToParent)) + s.height;
break;
case 'Left':
case 'Right':
switch (node.childrenLayout) {
case 'TopAlignedWithParent':
break;
case 'BottomAlignedWithParent':
break;
case 'Underneath':
s = this.measure(node.children[0], givenSize);
w = shapeWidth + s.width + this.options.underneathHorizontalOffset;
h = shapeHeight + this.options.underneathVerticalTopOffset + s.height;
break;
case 'Default':
s = this.measure(node.children[0], givenSize);
w = shapeWidth + this.options.horizontalSeparation + s.width;
h = Math.max(shapeHeight, s.height);
break;
default:
throw 'Unhandled TreeDirection in the Radial layout measuring.';
}
break;
case 'Up':
case 'Down':
switch (node.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
s = this.measure(node.children[0], givenSize);
w = Math.max(shapeWidth, s.width + this.options.underneathHorizontalOffset);
h = shapeHeight + this.options.underneathVerticalTopOffset + s.height;
break;
case 'Default':
s = this.measure(node.children[0], givenSize);
h = shapeHeight + this.options.verticalSeparation + s.height;
w = Math.max(shapeWidth, s.width);
break;
default:
throw 'Unhandled TreeDirection in the Down layout measuring.';
}
break;
default:
throw 'Unhandled TreeDirection in the layout measuring.';
}
result = new Size(w, h);
} else {
var i, childNode;
switch (node.treeDirection) {
case 'Left':
case 'Right':
switch (node.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
w = shapeWidth;
h = shapeHeight + this.options.underneathVerticalTopOffset;
for (i = 0; i < node.children.length; i++) {
childNode = node.children[i];
s = this.measure(childNode, givenSize);
w = Math.max(w, s.width + this.options.underneathHorizontalOffset);
h += s.height + this.options.underneathVerticalSeparation;
}
h -= this.options.underneathVerticalSeparation;
break;
case 'Default':
w = shapeWidth;
h = 0;
for (i = 0; i < node.children.length; i++) {
childNode = node.children[i];
s = this.measure(childNode, givenSize);
w = Math.max(w, shapeWidth + this.options.horizontalSeparation + s.width);
h += s.height + this.options.verticalSeparation;
}
h -= this.options.verticalSeparation;
break;
default:
throw 'Unhandled TreeDirection in the Right layout measuring.';
}
break;
case 'Up':
case 'Down':
switch (node.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
w = shapeWidth;
h = shapeHeight + this.options.underneathVerticalTopOffset;
for (i = 0; i < node.children.length; i++) {
childNode = node.children[i];
s = this.measure(childNode, givenSize);
w = Math.max(w, s.width + this.options.underneathHorizontalOffset);
h += s.height + this.options.underneathVerticalSeparation;
}
h -= this.options.underneathVerticalSeparation;
break;
case 'Default':
w = 0;
h = 0;
for (i = 0; i < node.children.length; i++) {
childNode = node.children[i];
s = this.measure(childNode, givenSize);
w += s.width + this.options.horizontalSeparation;
h = Math.max(h, s.height + this.options.verticalSeparation + shapeHeight);
}
w -= this.options.horizontalSeparation;
break;
default:
throw 'Unhandled TreeDirection in the Down layout measuring.';
}
break;
default:
throw 'Unhandled TreeDirection in the layout measuring.';
}
result = new Size(w, h);
}
node.SectorAngle = Math.sqrt(w * w / 4 + h * h / 4);
node.Size = result;
return result;
},
arrange: function (n, p) {
var i, pp, child, node, childrenwidth, b = n.associatedShape.bounds();
var shapeWidth = b.width;
var shapeHeight = b.height;
if (Utils.isEmpty(n.children)) {
n.x = p.x;
n.y = p.y;
n.BoundingRectangle = new Rect(p.x, p.y, shapeWidth, shapeHeight);
} else {
var x, y;
var selfLocation;
switch (n.treeDirection) {
case 'Left':
switch (n.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
selfLocation = p;
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
for (i = 0; i < node.children.length; i++) {
node = node.children[i];
x = selfLocation.x - node.associatedShape.width - this.options.underneathHorizontalOffset;
pp = new Point(x, y);
this.arrange(node, pp);
y += node.Size.height + this.options.underneathVerticalSeparation;
}
break;
case 'Default':
selfLocation = new Point(p.x + n.Size.width - shapeWidth, p.y + (n.Size.height - shapeHeight) / 2);
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
x = selfLocation.x - this.options.horizontalSeparation;
y = p.y;
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
pp = new Point(x - node.Size.width, y);
this.arrange(node, pp);
y += node.Size.height + this.options.verticalSeparation;
}
break;
default:
throw 'Unsupported TreeDirection';
}
break;
case 'Right':
switch (n.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
selfLocation = p;
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
x = p.x + shapeWidth + this.options.underneathHorizontalOffset;
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
pp = new Point(x, y);
this.arrange(node, pp);
y += node.Size.height + this.options.underneathVerticalSeparation;
}
break;
case 'Default':
selfLocation = new Point(p.x, p.y + (n.Size.height - shapeHeight) / 2);
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
x = p.x + shapeWidth + this.options.horizontalSeparation;
y = p.y;
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
pp = new Point(x, y);
this.arrange(node, pp);
y += node.Size.height + this.options.verticalSeparation;
}
break;
default:
throw 'Unsupported TreeDirection';
}
break;
case 'Up':
selfLocation = new Point(p.x + (n.Size.width - shapeWidth) / 2, p.y + n.Size.height - shapeHeight);
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
if (Math.abs(selfLocation.x - p.x) < EPSILON) {
childrenwidth = 0;
for (i = 0; i < n.children.length; i++) {
child = n.children[i];
childrenwidth += child.Size.width + this.options.horizontalSeparation;
}
childrenwidth -= this.options.horizontalSeparation;
x = p.x + (shapeWidth - childrenwidth) / 2;
} else {
x = p.x;
}
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
y = selfLocation.y - this.options.verticalSeparation - node.Size.height;
pp = new Point(x, y);
this.arrange(node, pp);
x += node.Size.width + this.options.horizontalSeparation;
}
break;
case 'Down':
switch (n.childrenLayout) {
case 'TopAlignedWithParent':
case 'BottomAlignedWithParent':
break;
case 'Underneath':
selfLocation = p;
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
x = p.x + this.options.underneathHorizontalOffset;
y = p.y + shapeHeight + this.options.underneathVerticalTopOffset;
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
pp = new Point(x, y);
this.arrange(node, pp);
y += node.Size.height + this.options.underneathVerticalSeparation;
}
break;
case 'Default':
selfLocation = new Point(p.x + (n.Size.width - shapeWidth) / 2, p.y);
n.x = selfLocation.x;
n.y = selfLocation.y;
n.BoundingRectangle = new Rect(n.x, n.y, n.width, n.height);
if (Math.abs(selfLocation.x - p.x) < EPSILON) {
childrenwidth = 0;
for (i = 0; i < n.children.length; i++) {
child = n.children[i];
childrenwidth += child.Size.width + this.options.horizontalSeparation;
}
childrenwidth -= this.options.horizontalSeparation;
x = p.x + (shapeWidth - childrenwidth) / 2;
} else {
x = p.x;
}
for (i = 0; i < n.children.length; i++) {
node = n.children[i];
y = selfLocation.y + this.options.verticalSeparation + shapeHeight;
pp = new Point(x, y);
this.arrange(node, pp);
x += node.Size.width + this.options.horizontalSeparation;
}
break;
default:
throw 'Unsupported TreeDirection';
}
break;
case 'None':
break;
default:
throw 'Unsupported TreeDirection';
}
}
},
layoutSwitch: function () {
if (!this.center) {
return;
}
if (Utils.isEmpty(this.center.children)) {
return;
}
var type = this.options.subtype;
if (Utils.isUndefined(type)) {
type = 'Down';
}
var single, male, female, leftcount;
var children = this.center.children;
switch (type.toLowerCase()) {
case 'radial':
case 'radialtree':
this.layoutRadialTree();
break;
case 'mindmaphorizontal':
case 'mindmap':
single = this.center.children;
if (this.center.children.length === 1) {
this.layoutRight(single);
} else {
leftcount = children.length / 2;
male = grep(this.center.children, function (n) {
return Utils.indexOf(children, n) < leftcount;
});
female = grep(this.center.children, function (n) {
return Utils.indexOf(children, n) >= leftcount;
});
this.layoutLeft(male);
this.layoutRight(female);
}
break;
case 'mindmapvertical':
single = this.center.children;
if (this.center.children.length === 1) {
this.layoutDown(single);
} else {
leftcount = children.length / 2;
male = grep(this.center.children, function (n) {
return Utils.indexOf(children, n) < leftcount;
});
female = grep(this.center.children, function (n) {
return Utils.indexOf(children, n) >= leftcount;
});
this.layoutUp(male);
this.layoutDown(female);
}
break;
case 'right':
this.layoutRight(this.center.children);
break;
case 'left':
this.layoutLeft(this.center.children);
break;
case 'up':
case 'bottom':
this.layoutUp(this.center.children);
break;
case 'down':
case 'top':
this.layoutDown(this.center.children);
break;
case 'tipover':
case 'tipovertree':
if (this.options.tipOverTreeStartLevel < 0) {
throw 'The tip-over level should be a positive integer.';
}
this.tipOverTree(this.center.children, this.options.tipOverTreeStartLevel);
break;
case 'undefined':
case 'none':
break;
}
}
});
var TreeLayout = LayoutBase.extend({
init: function (diagram) {
var that = this;
LayoutBase.fn.init.call(that);
if (Utils.isUndefined(diagram)) {
throw 'No diagram specified.';
}
this.diagram = diagram;
},
layout: function (options) {
this.transferOptions(options);
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
this.graph = adapter.convert();
var finalNodeSet = this.layoutComponents();
return new diagram.LayoutState(this.diagram, finalNodeSet);
},
layoutComponents: function () {
if (this.graph.isEmpty()) {
return;
}
var components = this.graph.getConnectedComponents();
if (Utils.isEmpty(components)) {
return;
}
var layout = new TreeLayoutProcessor(this.options);
var trees = [];
for (var i = 0; i < components.length; i++) {
var component = components[i];
var treeGraph = this.getTree(component);
if (!treeGraph) {
throw 'Failed to find a spanning tree for the component.';
}
var root = treeGraph.root;
var tree = treeGraph.tree;
layout.layout(tree, root);
trees.push(tree);
}
return this.gridLayoutComponents(trees);
},
getTree: function (graph) {
var root = null;
if (this.options.roots && this.options.roots.length > 0) {
for (var i = 0, len = graph.nodes.length; i < len; i++) {
var node = graph.nodes[i];
for (var j = 0; j < this.options.roots.length; j++) {
var givenRootShape = this.options.roots[j];
if (givenRootShape === node.associatedShape) {
root = node;
break;
}
}
}
}
if (!root) {
root = graph.root();
if (!root) {
throw 'Unable to find a root for the tree.';
}
}
return this.getTreeForRoot(graph, root);
},
getTreeForRoot: function (graph, root) {
var tree = graph.getSpanningTree(root);
if (Utils.isUndefined(tree) || tree.isEmpty()) {
return null;
}
return {
tree: tree,
root: tree.root
};
}
});
var LayeredLayout = LayoutBase.extend({
init: function (diagram) {
var that = this;
LayoutBase.fn.init.call(that);
if (Utils.isUndefined(diagram)) {
throw 'Diagram is not specified.';
}
this.diagram = diagram;
},
layout: function (options) {
this.transferOptions(options);
var adapter = new DiagramToHyperTreeAdapter(this.diagram);
var graph = adapter.convert(options);
if (graph.isEmpty()) {
return;
}
var components = graph.getConnectedComponents();
if (Utils.isEmpty(components)) {
return;
}
for (var i = 0; i < components.length; i++) {
var component = components[i];
this.layoutGraph(component, options);
}
var finalNodeSet = this.gridLayoutComponents(components);
return new diagram.LayoutState(this.diagram, finalNodeSet);
},
_initRuntimeProperties: function () {
for (var k = 0; k < this.graph.nodes.length; k++) {
var node = this.graph.nodes[k];
node.layer = -1;
node.downstreamLinkCount = 0;
node.upstreamLinkCount = 0;
node.isVirtual = false;
node.uBaryCenter = 0;
node.dBaryCenter = 0;
node.upstreamPriority = 0;
node.downstreamPriority = 0;
node.gridPosition = 0;
}
},
_prepare: function (graph) {
var current = [], i, l, link;
var layerMap = new Dictionary();
var layerCount = 0;
var targetLayer, next, target;
Utils.forEach(graph.nodes, function (node) {
if (node.incoming.length === 0) {
layerMap.set(node, 0);
current.push(node);
}
});
while (current.length > 0) {
next = current.shift();
for (i = 0; i < next.outgoing.length; i++) {
link = next.outgoing[i];
target = link.target;
if (layerMap.containsKey(target)) {
targetLayer = Math.max(layerMap.get(next) + 1, layerMap.get(target));
} else {
targetLayer = layerMap.get(next) + 1;
}
layerMap.set(target, targetLayer);
if (targetLayer > layerCount) {
layerCount = targetLayer;
}
if (!contains(current, target)) {
current.push(target);
}
}
}
var sortedNodes = layerMap.keys();
sortedNodes.sort(function (o1, o2) {
var o1layer = layerMap.get(o1);
var o2layer = layerMap.get(o2);
return Utils.sign(o2layer - o1layer);
});
for (var n = 0; n < sortedNodes.length; ++n) {
var node = sortedNodes[n];
var minLayer = Number.MAX_VALUE;
if (node.outgoing.length === 0) {
continue;
}
for (l = 0; l < node.outgoing.length; ++l) {
link = node.outgoing[l];
minLayer = Math.min(minLayer, layerMap.get(link.target));
}
if (minLayer > 1) {
layerMap.set(node, minLayer - 1);
}
}
this.layers = [];
var layer;
for (i = 0; i < layerCount + 1; i++) {
layer = [];
layer.linksTo = {};
this.layers.push(layer);
}
layerMap.forEach(function (node, layer) {
node.layer = layer;
this.layers[layer].push(node);
}, this);
for (l = 0; l < this.layers.length; l++) {
layer = this.layers[l];
for (i = 0; i < layer.length; i++) {
layer[i].gridPosition = i;
}
}
},
layoutGraph: function (graph, options) {
if (Utils.isUndefined(graph)) {
throw 'No graph given or graph analysis of the diagram failed.';
}
if (Utils.isDefined(options)) {
this.transferOptions(options);
}
this.graph = graph;
graph.setItemIndices();
var reversedEdges = graph.makeAcyclic();
this._initRuntimeProperties();
this._prepare(graph, options);
this._dummify();
this._optimizeCrossings();
this._swapPairs();
this.arrangeNodes();
this._moveThingsAround();
this._dedummify();
Utils.forEach(reversedEdges, function (e) {
if (e.points) {
e.points.reverse();
}
});
},
setMinDist: function (m, n, minDist) {
var l = m.layer;
var i = m.layerIndex;
this.minDistances[l][i] = minDist;
},
getMinDist: function (m, n) {
var dist = 0, i1 = m.layerIndex, i2 = n.layerIndex, l = m.layer, min = Math.min(i1, i2), max = Math.max(i1, i2);
for (var k = min; k < max; ++k) {
dist += this.minDistances[l][k];
}
return dist;
},
placeLeftToRight: function (leftClasses) {
var leftPos = new Dictionary(), n, node;
for (var c = 0; c < this.layers.length; ++c) {
var classNodes = leftClasses[c];
if (!classNodes) {
continue;
}
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
if (!leftPos.containsKey(node)) {
this.placeLeft(node, leftPos, c);
}
}
var d = Number.POSITIVE_INFINITY;
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
var rightSibling = this.rightSibling(node);
if (rightSibling && this.nodeLeftClass.get(rightSibling) !== c) {
d = Math.min(d, leftPos.get(rightSibling) - leftPos.get(node) - this.getMinDist(node, rightSibling));
}
}
if (d === Number.POSITIVE_INFINITY) {
var D = [];
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
var neighbors = [];
Utils.addRange(neighbors, this.upNodes.get(node));
Utils.addRange(neighbors, this.downNodes.get(node));
for (var e = 0; e < neighbors.length; e++) {
var neighbor = neighbors[e];
if (this.nodeLeftClass.get(neighbor) < c) {
D.push(leftPos.get(neighbor) - leftPos.get(node));
}
}
}
D.sort();
if (D.length === 0) {
d = 0;
} else if (D.length % 2 === 1) {
d = D[this.intDiv(D.length, 2)];
} else {
d = (D[this.intDiv(D.length, 2) - 1] + D[this.intDiv(D.length, 2)]) / 2;
}
}
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
leftPos.set(node, leftPos.get(node) + d);
}
}
return leftPos;
},
placeRightToLeft: function (rightClasses) {
var rightPos = new Dictionary(), n, node;
for (var c = 0; c < this.layers.length; ++c) {
var classNodes = rightClasses[c];
if (!classNodes) {
continue;
}
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
if (!rightPos.containsKey(node)) {
this.placeRight(node, rightPos, c);
}
}
var d = Number.NEGATIVE_INFINITY;
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
var leftSibling = this.leftSibling(node);
if (leftSibling && this.nodeRightClass.get(leftSibling) !== c) {
d = Math.max(d, rightPos.get(leftSibling) - rightPos.get(node) + this.getMinDist(leftSibling, node));
}
}
if (d === Number.NEGATIVE_INFINITY) {
var D = [];
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
var neighbors = [];
Utils.addRange(neighbors, this.upNodes.get(node));
Utils.addRange(neighbors, this.downNodes.get(node));
for (var e = 0; e < neighbors.length; e++) {
var neighbor = neighbors[e];
if (this.nodeRightClass.get(neighbor) < c) {
D.push(rightPos.get(node) - rightPos.get(neighbor));
}
}
}
D.sort();
if (D.length === 0) {
d = 0;
} else if (D.length % 2 === 1) {
d = D[this.intDiv(D.length, 2)];
} else {
d = (D[this.intDiv(D.length, 2) - 1] + D[this.intDiv(D.length, 2)]) / 2;
}
}
for (n = 0; n < classNodes.length; n++) {
node = classNodes[n];
rightPos.set(node, rightPos.get(node) + d);
}
}
return rightPos;
},
_getLeftWing: function () {
var leftWing = { value: null };
var result = this.computeClasses(leftWing, 1);
this.nodeLeftClass = leftWing.value;
return result;
},
_getRightWing: function () {
var rightWing = { value: null };
var result = this.computeClasses(rightWing, -1);
this.nodeRightClass = rightWing.value;
return result;
},
computeClasses: function (wingPair, d) {
var currentWing = 0, wing = wingPair.value = new Dictionary();
for (var l = 0; l < this.layers.length; ++l) {
currentWing = l;
var layer = this.layers[l];
for (var n = d === 1 ? 0 : layer.length - 1; 0 <= n && n < layer.length; n += d) {
var node = layer[n];
if (!wing.containsKey(node)) {
wing.set(node, currentWing);
if (node.isVirtual) {
var ndsinl = this._nodesInLink(node);
for (var kk = 0; kk < ndsinl.length; kk++) {
var vnode = ndsinl[kk];
wing.set(vnode, currentWing);
}
}
} else {
currentWing = wing.get(node);
}
}
}
var wings = [];
for (var i = 0; i < this.layers.length; i++) {
wings.push(null);
}
wing.forEach(function (node, classIndex) {
if (wings[classIndex] === null) {
wings[classIndex] = [];
}
wings[classIndex].push(node);
});
return wings;
},
_isVerticalLayout: function () {
return this.options.subtype.toLowerCase() === 'up' || this.options.subtype.toLowerCase() === 'down' || this.options.subtype.toLowerCase() === 'vertical';
},
_isHorizontalLayout: function () {
return this.options.subtype.toLowerCase() === 'right' || this.options.subtype.toLowerCase() === 'left' || this.options.subtype.toLowerCase() === 'horizontal';
},
_isIncreasingLayout: function () {
return this.options.subtype.toLowerCase() === 'right' || this.options.subtype.toLowerCase() === 'down';
},
_moveThingsAround: function () {
var i, l, node, layer, n, w;
for (l = 0; l < this.layers.length; ++l) {
layer = this.layers[l];
layer.sort(this._gridPositionComparer);
}
this.minDistances = [];
for (l = 0; l < this.layers.length; ++l) {
layer = this.layers[l];
this.minDistances[l] = [];
for (n = 0; n < layer.length; ++n) {
node = layer[n];
node.layerIndex = n;
this.minDistances[l][n] = this.options.nodeDistance;
if (n < layer.length - 1) {
if (this._isVerticalLayout()) {
this.minDistances[l][n] += (node.width + layer[n + 1].width) / 2;
} else {
this.minDistances[l][n] += (node.height + layer[n + 1].height) / 2;
}
}
}
}
this.downNodes = new Dictionary();
this.upNodes = new Dictionary();
Utils.forEach(this.graph.nodes, function (node) {
this.downNodes.set(node, []);
this.upNodes.set(node, []);
}, this);
Utils.forEach(this.graph.links, function (link) {
var origin = link.source;
var dest = link.target;
var down = null, up = null;
if (origin.layer > dest.layer) {
down = link.source;
up = link.target;
} else {
up = link.source;
down = link.target;
}
this.downNodes.get(up).push(down);
this.upNodes.get(down).push(up);
}, this);
this.downNodes.forEachValue(function (list) {
list.sort(this._gridPositionComparer);
}, this);
this.upNodes.forEachValue(function (list) {
list.sort(this._gridPositionComparer);
}, this);
for (l = 0; l < this.layers.length - 1; ++l) {
layer = this.layers[l];
for (w = 0; w < layer.length - 1; w++) {
var currentNode = layer[w];
if (!currentNode.isVirtual) {
continue;
}
var currDown = this.downNodes.get(currentNode)[0];
if (!currDown.isVirtual) {
continue;
}
for (n = w + 1; n < layer.length; ++n) {
node = layer[n];
if (!node.isVirtual) {
continue;
}
var downNode = this.downNodes.get(node)[0];
if (!downNode.isVirtual) {
continue;
}
if (currDown.gridPosition > downNode.gridPosition) {
var pos = currDown.gridPosition;
currDown.gridPosition = downNode.gridPosition;
downNode.gridPosition = pos;
var i1 = currDown.layerIndex;
var i2 = downNode.layerIndex;
this.layers[l + 1][i1] = downNode;
this.layers[l + 1][i2] = currDown;
currDown.layerIndex = i2;
downNode.layerIndex = i1;
}
}
}
}
var leftClasses = this._getLeftWing();
var rightClasses = this._getRightWing();
var leftPos = this.placeLeftToRight(leftClasses);
var rightPos = this.placeRightToLeft(rightClasses);
var x = new Dictionary();
Utils.forEach(this.graph.nodes, function (node) {
x.set(node, (leftPos.get(node) + rightPos.get(node)) / 2);
});
var order = new Dictionary();
var placed = new Dictionary();
for (l = 0; l < this.layers.length; ++l) {
layer = this.layers[l];
var sequenceStart = -1, sequenceEnd = -1;
for (n = 0; n < layer.length; ++n) {
node = layer[n];
order.set(node, 0);
placed.set(node, false);
if (node.isVirtual) {
if (sequenceStart === -1) {
sequenceStart = n;
} else if (sequenceStart === n - 1) {
sequenceStart = n;
} else {
sequenceEnd = n;
order.set(layer[sequenceStart], 0);
if (x.get(node) - x.get(layer[sequenceStart]) === this.getMinDist(layer[sequenceStart], node)) {
placed.set(layer[sequenceStart], true);
} else {
placed.set(layer[sequenceStart], false);
}
sequenceStart = n;
}
}
}
}
var directions = [
1,
-1
];
Utils.forEach(directions, function (d) {
var start = d === 1 ? 0 : this.layers.length - 1;
for (var l = start; 0 <= l && l < this.layers.length; l += d) {
var layer = this.layers[l];
var virtualStartIndex = this._firstVirtualNode(layer);
var virtualStart = null;
var sequence = null;
if (virtualStartIndex !== -1) {
virtualStart = layer[virtualStartIndex];
sequence = [];
for (i = 0; i < virtualStartIndex; i++) {
sequence.push(layer[i]);
}
} else {
virtualStart = null;
sequence = layer;
}
if (sequence.length > 0) {
this._sequencer(x, null, virtualStart, d, sequence);
for (i = 0; i < sequence.length - 1; ++i) {
this.setMinDist(sequence[i], sequence[i + 1], x.get(sequence[i + 1]) - x.get(sequence[i]));
}
if (virtualStart) {
this.setMinDist(sequence[sequence.length - 1], virtualStart, x.get(virtualStart) - x.get(sequence[sequence.length - 1]));
}
}
while (virtualStart) {
var virtualEnd = this.nextVirtualNode(layer, virtualStart);
if (!virtualEnd) {
virtualStartIndex = virtualStart.layerIndex;
sequence = [];
for (i = virtualStartIndex + 1; i < layer.length; i++) {
sequence.push(layer[i]);
}
if (sequence.length > 0) {
this._sequencer(x, virtualStart, null, d, sequence);
for (i = 0; i < sequence.length - 1; ++i) {
this.setMinDist(sequence[i], sequence[i + 1], x.get(sequence[i + 1]) - x.get(sequence[i]));
}
this.setMinDist(virtualStart, sequence[0], x.get(sequence[0]) - x.get(virtualStart));
}
} else if (order.get(virtualStart) === d) {
virtualStartIndex = virtualStart.layerIndex;
var virtualEndIndex = virtualEnd.layerIndex;
sequence = [];
for (i = virtualStartIndex + 1; i < virtualEndIndex; i++) {
sequence.push(layer[i]);
}
if (sequence.length > 0) {
this._sequencer(x, virtualStart, virtualEnd, d, sequence);
}
placed.set(virtualStart, true);
}
virtualStart = virtualEnd;
}
this.adjustDirections(l, d, order, placed);
}
}, this);
var fromLayerIndex = this._isIncreasingLayout() ? 0 : this.layers.length - 1;
var reachedFinalLayerIndex = function (k, ctx) {
if (ctx._isIncreasingLayout()) {
return k < ctx.layers.length;
} else {
return k >= 0;
}
};
var layerIncrement = this._isIncreasingLayout() ? +1 : -1, offset = 0;
function maximumHeight(layer, ctx) {
var height = Number.MIN_VALUE;
for (var n = 0; n < layer.length; ++n) {
var node = layer[n];
if (ctx._isVerticalLayout()) {
height = Math.max(height, node.height);
} else {
height = Math.max(height, node.width);
}
}
return height;
}
for (i = fromLayerIndex; reachedFinalLayerIndex(i, this); i += layerIncrement) {
layer = this.layers[i];
var height = maximumHeight(layer, this);
for (n = 0; n < layer.length; ++n) {
node = layer[n];
if (this._isVerticalLayout()) {
node.x = x.get(node);
node.y = offset + height / 2;
} else {
node.x = offset + height / 2;
node.y = x.get(node);
}
}
offset += this.options.layerSeparation + height;
}
},
adjustDirections: function (l, d, order, placed) {
if (l + d < 0 || l + d >= this.layers.length) {
return;
}
var prevBridge = null, prevBridgeTarget = null;
var layer = this.layers[l + d];
for (var n = 0; n < layer.length; ++n) {
var nextBridge = layer[n];
if (nextBridge.isVirtual) {
var nextBridgeTarget = this.getNeighborOnLayer(nextBridge, l);
if (nextBridgeTarget.isVirtual) {
if (prevBridge) {
var p = placed.get(prevBridgeTarget);
var clayer = this.layers[l];
var i1 = prevBridgeTarget.layerIndex;
var i2 = nextBridgeTarget.layerIndex;
for (var i = i1 + 1; i < i2; ++i) {
if (clayer[i].isVirtual) {
p = p && placed.get(clayer[i]);
}
}
if (p) {
order.set(prevBridge, d);
var j1 = prevBridge.layerIndex;
var j2 = nextBridge.layerIndex;
for (var j = j1 + 1; j < j2; ++j) {
if (layer[j].isVirtual) {
order.set(layer[j], d);
}
}
}
}
prevBridge = nextBridge;
prevBridgeTarget = nextBridgeTarget;
}
}
}
},
getNeighborOnLayer: function (node, l) {
var neighbor = this.upNodes.get(node)[0];
if (neighbor.layer === l) {
return neighbor;
}
neighbor = this.downNodes.get(node)[0];
if (neighbor.layer === l) {
return neighbor;
}
return null;
},
_sequencer: function (x, virtualStart, virtualEnd, dir, sequence) {
if (sequence.length === 1) {
this._sequenceSingle(x, virtualStart, virtualEnd, dir, sequence[0]);
}
if (sequence.length > 1) {
var r = sequence.length, t = this.intDiv(r, 2);
this._sequencer(x, virtualStart, virtualEnd, dir, sequence.slice(0, t));
this._sequencer(x, virtualStart, virtualEnd, dir, sequence.slice(t));
this.combineSequences(x, virtualStart, virtualEnd, dir, sequence);
}
},
_sequenceSingle: function (x, virtualStart, virtualEnd, dir, node) {
var neighbors = dir === -1 ? this.downNodes.get(node) : this.upNodes.get(node);
var n = neighbors.length;
if (n !== 0) {
if (n % 2 === 1) {
x.set(node, x.get(neighbors[this.intDiv(n, 2)]));
} else {
x.set(node, (x.get(neighbors[this.intDiv(n, 2) - 1]) + x.get(neighbors[this.intDiv(n, 2)])) / 2);
}
if (virtualStart) {
x.set(node, Math.max(x.get(node), x.get(virtualStart) + this.getMinDist(virtualStart, node)));
}
if (virtualEnd) {
x.set(node, Math.min(x.get(node), x.get(virtualEnd) - this.getMinDist(node, virtualEnd)));
}
}
},
combineSequences: function (x, virtualStart, virtualEnd, dir, sequence) {
var r = sequence.length, t = this.intDiv(r, 2);
var leftHeap = [], i, c, n, neighbors, neighbor, pair;
for (i = 0; i < t; ++i) {
c = 0;
neighbors = dir === -1 ? this.downNodes.get(sequence[i]) : this.upNodes.get(sequence[i]);
for (n = 0; n < neighbors.length; ++n) {
neighbor = neighbors[n];
if (x.get(neighbor) >= x.get(sequence[i])) {
c++;
} else {
c--;
leftHeap.push({
k: x.get(neighbor) + this.getMinDist(sequence[i], sequence[t - 1]),
v: 2
});
}
}
leftHeap.push({
k: x.get(sequence[i]) + this.getMinDist(sequence[i], sequence[t - 1]),
v: c
});
}
if (virtualStart) {
leftHeap.push({
k: x.get(virtualStart) + this.getMinDist(virtualStart, sequence[t - 1]),
v: Number.MAX_VALUE
});
}
leftHeap.sort(this._positionDescendingComparer);
var rightHeap = [];
for (i = t; i < r; ++i) {
c = 0;
neighbors = dir === -1 ? this.downNodes.get(sequence[i]) : this.upNodes.get(sequence[i]);
for (n = 0; n < neighbors.length; ++n) {
neighbor = neighbors[n];
if (x.get(neighbor) <= x.get(sequence[i])) {
c++;
} else {
c--;
rightHeap.push({
k: x.get(neighbor) - this.getMinDist(sequence[i], sequence[t]),
v: 2
});
}
}
rightHeap.push({
k: x.get(sequence[i]) - this.getMinDist(sequence[i], sequence[t]),
v: c
});
}
if (virtualEnd) {
rightHeap.push({
k: x.get(virtualEnd) - this.getMinDist(virtualEnd, sequence[t]),
v: Number.MAX_VALUE
});
}
rightHeap.sort(this._positionAscendingComparer);
var leftRes = 0, rightRes = 0;
var m = this.getMinDist(sequence[t - 1], sequence[t]);
while (x.get(sequence[t]) - x.get(sequence[t - 1]) < m) {
if (leftRes < rightRes) {
if (leftHeap.length === 0) {
x.set(sequence[t - 1], x.get(sequence[t]) - m);
break;
} else {
pair = leftHeap.shift();
leftRes = leftRes + pair.v;
x.set(sequence[t - 1], pair.k);
x.set(sequence[t - 1], Math.max(x.get(sequence[t - 1]), x.get(sequence[t]) - m));
}
} else {
if (rightHeap.length === 0) {
x.set(sequence[t], x.get(sequence[t - 1]) + m);
break;
} else {
pair = rightHeap.shift();
rightRes = rightRes + pair.v;
x.set(sequence[t], pair.k);
x.set(sequence[t], Math.min(x.get(sequence[t]), x.get(sequence[t - 1]) + m));
}
}
}
for (i = t - 2; i >= 0; i--) {
x.set(sequence[i], Math.min(x.get(sequence[i]), x.get(sequence[t - 1]) - this.getMinDist(sequence[i], sequence[t - 1])));
}
for (i = t + 1; i < r; i++) {
x.set(sequence[i], Math.max(x.get(sequence[i]), x.get(sequence[t]) + this.getMinDist(sequence[i], sequence[t])));
}
},
placeLeft: function (node, leftPos, leftClass) {
var pos = Number.NEGATIVE_INFINITY;
Utils.forEach(this._getComposite(node), function (v) {
var leftSibling = this.leftSibling(v);
if (leftSibling && this.nodeLeftClass.get(leftSibling) === this.nodeLeftClass.get(v)) {
if (!leftPos.containsKey(leftSibling)) {
this.placeLeft(leftSibling, leftPos, leftClass);
}
pos = Math.max(pos, leftPos.get(leftSibling) + this.getMinDist(leftSibling, v));
}
}, this);
if (pos === Number.NEGATIVE_INFINITY) {
pos = 0;
}
Utils.forEach(this._getComposite(node), function (v) {
leftPos.set(v, pos);
});
},
placeRight: function (node, rightPos, rightClass) {
var pos = Number.POSITIVE_INFINITY;
Utils.forEach(this._getComposite(node), function (v) {
var rightSibling = this.rightSibling(v);
if (rightSibling && this.nodeRightClass.get(rightSibling) === this.nodeRightClass.get(v)) {
if (!rightPos.containsKey(rightSibling)) {
this.placeRight(rightSibling, rightPos, rightClass);
}
pos = Math.min(pos, rightPos.get(rightSibling) - this.getMinDist(v, rightSibling));
}
}, this);
if (pos === Number.POSITIVE_INFINITY) {
pos = 0;
}
Utils.forEach(this._getComposite(node), function (v) {
rightPos.set(v, pos);
});
},
leftSibling: function (node) {
var layer = this.layers[node.layer], layerIndex = node.layerIndex;
return layerIndex === 0 ? null : layer[layerIndex - 1];
},
rightSibling: function (node) {
var layer = this.layers[node.layer];
var layerIndex = node.layerIndex;
return layerIndex === layer.length - 1 ? null : layer[layerIndex + 1];
},
_getComposite: function (node) {
return node.isVirtual ? this._nodesInLink(node) : [node];
},
arrangeNodes: function () {
var i, l, ni, layer, node;
for (l = 0; l < this.layers.length; l++) {
layer = this.layers[l];
for (ni = 0; ni < layer.length; ni++) {
node = layer[ni];
node.upstreamPriority = node.upstreamLinkCount;
node.downstreamPriority = node.downstreamLinkCount;
}
}
var maxLayoutIterations = 2;
for (var it = 0; it < maxLayoutIterations; it++) {
for (i = this.layers.length - 1; i >= 1; i--) {
this.layoutLayer(false, i);
}
for (i = 0; i < this.layers.length - 1; i++) {
this.layoutLayer(true, i);
}
}
var gridPos = Number.MAX_VALUE;
for (l = 0; l < this.layers.length; l++) {
layer = this.layers[l];
for (ni = 0; ni < layer.length; ni++) {
node = layer[ni];
gridPos = Math.min(gridPos, node.gridPosition);
}
}
if (gridPos < 0) {
for (l = 0; l < this.layers.length; l++) {
layer = this.layers[l];
for (ni = 0; ni < layer.length; ni++) {
node = layer[ni];
node.gridPosition = node.gridPosition - gridPos;
}
}
}
},
layoutLayer: function (down, layer) {
var iconsidered;
var considered;
if (down) {
considered = this.layers[iconsidered = layer + 1];
} else {
considered = this.layers[iconsidered = layer - 1];
}
var sorted = [];
for (var n = 0; n < considered.length; n++) {
sorted.push(considered[n]);
}
sorted.sort(function (n1, n2) {
var n1Priority = (n1.upstreamPriority + n1.downstreamPriority) / 2;
var n2Priority = (n2.upstreamPriority + n2.downstreamPriority) / 2;
if (Math.abs(n1Priority - n2Priority) < 0.0001) {
return 0;
}
if (n1Priority < n2Priority) {
return 1;
}
return -1;
});
Utils.forEach(sorted, function (node) {
var nodeGridPos = node.gridPosition;
var nodeBaryCenter = this.calcBaryCenter(node);
var nodePriority = (node.upstreamPriority + node.downstreamPriority) / 2;
if (Math.abs(nodeGridPos - nodeBaryCenter) < 0.0001) {
return;
}
if (Math.abs(nodeGridPos - nodeBaryCenter) < 0.25 + 0.0001) {
return;
}
if (nodeGridPos < nodeBaryCenter) {
while (nodeGridPos < nodeBaryCenter) {
if (!this.moveRight(node, considered, nodePriority)) {
break;
}
nodeGridPos = node.gridPosition;
}
} else {
while (nodeGridPos > nodeBaryCenter) {
if (!this.moveLeft(node, considered, nodePriority)) {
break;
}
nodeGridPos = node.gridPosition;
}
}
}, this);
if (iconsidered > 0) {
this.calcDownData(iconsidered - 1);
}
if (iconsidered < this.layers.length - 1) {
this.calcUpData(iconsidered + 1);
}
},
moveRight: function (node, layer, priority) {
var index = Utils.indexOf(layer, node);
if (index === layer.length - 1) {
node.gridPosition = node.gridPosition + 0.5;
return true;
}
var rightNode = layer[index + 1];
var rightNodePriority = (rightNode.upstreamPriority + rightNode.downstreamPriority) / 2;
if (rightNode.gridPosition > node.gridPosition + 1) {
node.gridPosition = node.gridPosition + 0.5;
return true;
}
if (rightNodePriority > priority || Math.abs(rightNodePriority - priority) < 0.0001) {
return false;
}
if (this.moveRight(rightNode, layer, priority)) {
node.gridPosition = node.gridPosition + 0.5;
return true;
}
return false;
},
moveLeft: function (node, layer, priority) {
var index = Utils.indexOf(layer, node);
if (index === 0) {
node.gridPosition = node.gridPosition - 0.5;
return true;
}
var leftNode = layer[index - 1];
var leftNodePriority = (leftNode.upstreamPriority + leftNode.downstreamPriority) / 2;
if (leftNode.gridPosition < node.gridPosition - 1) {
node.gridPosition = node.gridPosition - 0.5;
return true;
}
if (leftNodePriority > priority || Math.abs(leftNodePriority - priority) < 0.0001) {
return false;
}
if (this.moveLeft(leftNode, layer, priority)) {
node.gridPosition = node.gridPosition - 0.5;
return true;
}
return false;
},
mapVirtualNode: function (node, link) {
this.nodeToLinkMap.set(node, link);
if (!this.linkToNodeMap.containsKey(link)) {
this.linkToNodeMap.set(link, []);
}
this.linkToNodeMap.get(link).push(node);
},
_nodesInLink: function (node) {
return this.linkToNodeMap.get(this.nodeToLinkMap.get(node));
},
_dummify: function () {
this.linkToNodeMap = new Dictionary();
this.nodeToLinkMap = new Dictionary();
var layer, pos, newNode, node, r, newLink, i, l, links = this.graph.links.slice(0);
var layers = this.layers;
var addLinkBetweenLayers = function (upLayer, downLayer, link) {
layers[upLayer].linksTo[downLayer] = layers[upLayer].linksTo[downLayer] || [];
layers[upLayer].linksTo[downLayer].push(link);
};
for (l = 0; l < links.length; l++) {
var link = links[l];
var o = link.source;
var d = link.target;
var oLayer = o.layer;
var dLayer = d.layer;
var oPos = o.gridPosition;
var dPos = d.gridPosition;
var step = (dPos - oPos) / Math.abs(dLayer - oLayer);
var p = o;
if (oLayer - dLayer > 1) {
for (i = oLayer - 1; i > dLayer; i--) {
newNode = new Node();
newNode.x = o.x;
newNode.y = o.y;
newNode.width = o.width / 100;
newNode.height = o.height / 100;
layer = layers[i];
pos = (i - dLayer) * step + oPos;
if (pos > layer.length) {
pos = layer.length;
}
if (oPos >= layers[oLayer].length - 1 && dPos >= layers[dLayer].length - 1) {
pos = layer.length;
} else if (oPos === 0 && dPos === 0) {
pos = 0;
}
newNode.layer = i;
newNode.uBaryCenter = 0;
newNode.dBaryCenter = 0;
newNode.upstreamLinkCount = 0;
newNode.downstreamLinkCount = 0;
newNode.gridPosition = pos;
newNode.isVirtual = true;
Utils.insert(layer, newNode, pos);
for (r = pos + 1; r < layer.length; r++) {
node = layer[r];
node.gridPosition = node.gridPosition + 1;
}
newLink = new Link(p, newNode);
newLink.depthOfDumminess = 0;
addLinkBetweenLayers(i - 1, i, newLink);
p = newNode;
this.graph._addNode(newNode);
this.graph.addLink(newLink);
newNode.index = this.graph.nodes.length - 1;
this.mapVirtualNode(newNode, link);
}
addLinkBetweenLayers(dLayer - 1, dLayer, newLink);
link.changeSource(p);
link.depthOfDumminess = oLayer - dLayer - 1;
} else if (oLayer - dLayer < -1) {
for (i = oLayer + 1; i < dLayer; i++) {
newNode = new Node();
newNode.x = o.x;
newNode.y = o.y;
newNode.width = o.width / 100;
newNode.height = o.height / 100;
layer = layers[i];
pos = (i - oLayer) * step + oPos;
if (pos > layer.length) {
pos = layer.length;
}
if (oPos >= layers[oLayer].length - 1 && dPos >= layers[dLayer].length - 1) {
pos = layer.length;
} else if (oPos === 0 && dPos === 0) {
pos = 0;
}
newNode.layer = i;
newNode.uBaryCenter = 0;
newNode.dBaryCenter = 0;
newNode.upstreamLinkCount = 0;
newNode.downstreamLinkCount = 0;
newNode.gridPosition = pos;
newNode.isVirtual = true;
pos &= pos;
Utils.insert(layer, newNode, pos);
for (r = pos + 1; r < layer.length; r++) {
node = layer[r];
node.gridPosition = node.gridPosition + 1;
}
newLink = new Link(p, newNode);
newLink.depthOfDumminess = 0;
addLinkBetweenLayers(i - 1, i, newLink);
p = newNode;
this.graph._addNode(newNode);
this.graph.addLink(newLink);
newNode.index = this.graph.nodes.length - 1;
this.mapVirtualNode(newNode, link);
}
addLinkBetweenLayers(dLayer - 1, dLayer, link);
link.changeSource(p);
link.depthOfDumminess = dLayer - oLayer - 1;
} else {
addLinkBetweenLayers(oLayer, dLayer, link);
}
}
},
_dedummify: function () {
var dedum = true;
while (dedum) {
dedum = false;
for (var l = 0; l < this.graph.links.length; l++) {
var link = this.graph.links[l];
if (!link.depthOfDumminess) {
continue;
}
var points = [];
points.unshift({
x: link.target.x,
y: link.target.y
});
points.unshift({
x: link.source.x,
y: link.source.y
});
var temp = link;
var depthOfDumminess = link.depthOfDumminess;
for (var d = 0; d < depthOfDumminess; d++) {
var node = temp.source;
var prevLink = node.incoming[0];
points.unshift({
x: prevLink.source.x,
y: prevLink.source.y
});
temp = prevLink;
}
link.changeSource(temp.source);
link.depthOfDumminess = 0;
if (points.length > 2) {
points.splice(0, 1);
points.splice(points.length - 1);
link.points = points;
} else {
link.points = [];
}
dedum = true;
break;
}
}
},
_optimizeCrossings: function () {
var moves = -1, i;
var maxIterations = 3;
var iter = 0;
while (moves !== 0) {
if (iter++ > maxIterations) {
break;
}
moves = 0;
for (i = this.layers.length - 1; i >= 1; i--) {
moves += this.optimizeLayerCrossings(false, i);
}
for (i = 0; i < this.layers.length - 1; i++) {
moves += this.optimizeLayerCrossings(true, i);
}
}
},
calcUpData: function (layer) {
if (layer === 0) {
return;
}
var considered = this.layers[layer], i, l, link;
var upLayer = new Set();
var temp = this.layers[layer - 1];
for (i = 0; i < temp.length; i++) {
upLayer.add(temp[i]);
}
for (i = 0; i < considered.length; i++) {
var node = considered[i];
var sum = 0;
var total = 0;
for (l = 0; l < node.incoming.length; l++) {
link = node.incoming[l];
if (upLayer.contains(link.source)) {
total++;
sum += link.source.gridPosition;
}
}
for (l = 0; l < node.outgoing.length; l++) {
link = node.outgoing[l];
if (upLayer.contains(link.target)) {
total++;
sum += link.target.gridPosition;
}
}
if (total > 0) {
node.uBaryCenter = sum / total;
node.upstreamLinkCount = total;
} else {
node.uBaryCenter = i;
node.upstreamLinkCount = 0;
}
}
},
calcDownData: function (layer) {
if (layer === this.layers.length - 1) {
return;
}
var considered = this.layers[layer], i, l, link;
var downLayer = new Set();
var temp = this.layers[layer + 1];
for (i = 0; i < temp.length; i++) {
downLayer.add(temp[i]);
}
for (i = 0; i < considered.length; i++) {
var node = considered[i];
var sum = 0;
var total = 0;
for (l = 0; l < node.incoming.length; l++) {
link = node.incoming[l];
if (downLayer.contains(link.source)) {
total++;
sum += link.source.gridPosition;
}
}
for (l = 0; l < node.outgoing.length; l++) {
link = node.outgoing[l];
if (downLayer.contains(link.target)) {
total++;
sum += link.target.gridPosition;
}
}
if (total > 0) {
node.dBaryCenter = sum / total;
node.downstreamLinkCount = total;
} else {
node.dBaryCenter = i;
node.downstreamLinkCount = 0;
}
}
},
optimizeLayerCrossings: function (down, layer) {
var iconsidered;
var considered;
if (down) {
considered = this.layers[iconsidered = layer + 1];
} else {
considered = this.layers[iconsidered = layer - 1];
}
var presorted = considered.slice(0);
if (down) {
this.calcUpData(iconsidered);
} else {
this.calcDownData(iconsidered);
}
var that = this;
considered.sort(function (n1, n2) {
var n1BaryCenter = that.calcBaryCenter(n1), n2BaryCenter = that.calcBaryCenter(n2);
if (Math.abs(n1BaryCenter - n2BaryCenter) < 0.0001) {
if (n1.degree() === n2.degree()) {
return that.compareByIndex(n1, n2);
} else if (n1.degree() < n2.degree()) {
return 1;
}
return -1;
}
var compareValue = (n2BaryCenter - n1BaryCenter) * 1000;
if (compareValue > 0) {
return -1;
} else if (compareValue < 0) {
return 1;
}
return that.compareByIndex(n1, n2);
});
var i, moves = 0;
for (i = 0; i < considered.length; i++) {
if (considered[i] !== presorted[i]) {
moves++;
}
}
if (moves > 0) {
var inode = 0;
for (i = 0; i < considered.length; i++) {
var node = considered[i];
node.gridPosition = inode++;
}
}
return moves;
},
_swapPairs: function () {
var maxIterations = this.options.layeredIterations;
var iter = 0;
while (true) {
if (iter++ > maxIterations) {
break;
}
var downwards = iter % 4 <= 1;
var secondPass = iter % 4 === 1;
for (var l = downwards ? 0 : this.layers.length - 1; downwards ? l <= this.layers.length - 1 : l >= 0; l += downwards ? 1 : -1) {
var layer = this.layers[l];
var hasSwapped = false;
var calcCrossings = true;
var memCrossings = 0;
for (var n = 0; n < layer.length - 1; n++) {
var up = 0;
var down = 0;
var crossBefore = 0;
if (calcCrossings) {
if (l !== 0) {
up = this.countLinksCrossingBetweenTwoLayers(l - 1, l);
}
if (l !== this.layers.length - 1) {
down = this.countLinksCrossingBetweenTwoLayers(l, l + 1);
}
if (downwards) {
up *= 2;
} else {
down *= 2;
}
crossBefore = up + down;
} else {
crossBefore = memCrossings;
}
if (crossBefore === 0) {
continue;
}
var node1 = layer[n];
var node2 = layer[n + 1];
var node1GridPos = node1.gridPosition;
var node2GridPos = node2.gridPosition;
layer[n] = node2;
layer[n + 1] = node1;
node1.gridPosition = node2GridPos;
node2.gridPosition = node1GridPos;
up = 0;
if (l !== 0) {
up = this.countLinksCrossingBetweenTwoLayers(l - 1, l);
}
down = 0;
if (l !== this.layers.length - 1) {
down = this.countLinksCrossingBetweenTwoLayers(l, l + 1);
}
if (downwards) {
up *= 2;
} else {
down *= 2;
}
var crossAfter = up + down;
var revert = false;
if (secondPass) {
revert = crossAfter >= crossBefore;
} else {
revert = crossAfter > crossBefore;
}
if (revert) {
node1 = layer[n];
node2 = layer[n + 1];
node1GridPos = node1.gridPosition;
node2GridPos = node2.gridPosition;
layer[n] = node2;
layer[n + 1] = node1;
node1.gridPosition = node2GridPos;
node2.gridPosition = node1GridPos;
memCrossings = crossBefore;
calcCrossings = false;
} else {
hasSwapped = true;
calcCrossings = true;
}
}
if (hasSwapped) {
if (l !== this.layers.length - 1) {
this.calcUpData(l + 1);
}
if (l !== 0) {
this.calcDownData(l - 1);
}
}
}
}
},
countLinksCrossingBetweenTwoLayers: function (ulayer, dlayer) {
var links = this.layers[ulayer].linksTo[dlayer];
var link1, link2, n11, n12, n21, n22, l1, l2;
var crossings = 0;
var length = links.length;
for (l1 = 0; l1 < length; l1++) {
link1 = links[l1];
for (l2 = l1 + 1; l2 < length; l2++) {
link2 = links[l2];
if (link1.target.layer === dlayer) {
n11 = link1.source;
n12 = link1.target;
} else {
n11 = link1.target;
n12 = link1.source;
}
if (link2.target.layer === dlayer) {
n21 = link2.source;
n22 = link2.target;
} else {
n21 = link2.target;
n22 = link2.source;
}
var n11gp = n11.gridPosition;
var n12gp = n12.gridPosition;
var n21gp = n21.gridPosition;
var n22gp = n22.gridPosition;
if ((n11gp - n21gp) * (n12gp - n22gp) < 0) {
crossings++;
}
}
}
return crossings;
},
calcBaryCenter: function (node) {
var upstreamLinkCount = node.upstreamLinkCount;
var downstreamLinkCount = node.downstreamLinkCount;
var uBaryCenter = node.uBaryCenter;
var dBaryCenter = node.dBaryCenter;
if (upstreamLinkCount > 0 && downstreamLinkCount > 0) {
return (uBaryCenter + dBaryCenter) / 2;
}
if (upstreamLinkCount > 0) {
return uBaryCenter;
}
if (downstreamLinkCount > 0) {
return dBaryCenter;
}
return 0;
},
_gridPositionComparer: function (x, y) {
if (x.gridPosition < y.gridPosition) {
return -1;
}
if (x.gridPosition > y.gridPosition) {
return 1;
}
return 0;
},
_positionAscendingComparer: function (x, y) {
return x.k < y.k ? -1 : x.k > y.k ? 1 : 0;
},
_positionDescendingComparer: function (x, y) {
return x.k < y.k ? 1 : x.k > y.k ? -1 : 0;
},
_firstVirtualNode: function (layer) {
for (var c = 0; c < layer.length; c++) {
if (layer[c].isVirtual) {
return c;
}
}
return -1;
},
compareByIndex: function (o1, o2) {
var i1 = o1.index;
var i2 = o2.index;
if (i1 < i2) {
return 1;
}
if (i1 > i2) {
return -1;
}
return 0;
},
intDiv: function (numerator, denominator) {
return (numerator - numerator % denominator) / denominator;
},
nextVirtualNode: function (layer, node) {
var nodeIndex = node.layerIndex;
for (var i = nodeIndex + 1; i < layer.length; ++i) {
if (layer[i].isVirtual) {
return layer[i];
}
}
return null;
}
});
var LayoutState = kendo.Class.extend({
init: function (diagram, graphOrNodes) {
if (Utils.isUndefined(diagram)) {
throw 'No diagram given';
}
this.diagram = diagram;
this.nodeMap = new Dictionary();
this.linkMap = new Dictionary();
this.capture(graphOrNodes ? graphOrNodes : diagram);
},
capture: function (diagramOrGraphOrNodes) {
var node, nodes, shape, i, conn, link, links;
if (diagramOrGraphOrNodes instanceof diagram.Graph) {
for (i = 0; i < diagramOrGraphOrNodes.nodes.length; i++) {
node = diagramOrGraphOrNodes.nodes[i];
shape = node.associatedShape;
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
}
for (i = 0; i < diagramOrGraphOrNodes.links.length; i++) {
link = diagramOrGraphOrNodes.links[i];
conn = link.associatedConnection;
this.linkMap.set(conn.visual.id, link.points());
}
} else if (diagramOrGraphOrNodes instanceof Array) {
nodes = diagramOrGraphOrNodes;
for (i = 0; i < nodes.length; i++) {
node = nodes[i];
shape = node.associatedShape;
if (shape) {
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
}
}
} else if (diagramOrGraphOrNodes.hasOwnProperty('links') && diagramOrGraphOrNodes.hasOwnProperty('nodes')) {
nodes = diagramOrGraphOrNodes.nodes;
links = diagramOrGraphOrNodes.links;
for (i = 0; i < nodes.length; i++) {
node = nodes[i];
shape = node.associatedShape;
if (shape) {
this.nodeMap.set(shape.visual.id, new Rect(node.x, node.y, node.width, node.height));
}
}
for (i = 0; i < links.length; i++) {
link = links[i];
conn = link.associatedConnection;
if (conn) {
this.linkMap.set(conn.visual.id, link.points);
}
}
} else {
var shapes = this.diagram.shapes;
var connections = this.diagram.connections;
for (i = 0; i < shapes.length; i++) {
shape = shapes[i];
this.nodeMap.set(shape.visual.id, shape.bounds());
}
for (i = 0; i < connections.length; i++) {
conn = connections[i];
this.linkMap.set(conn.visual.id, conn.points());
}
}
}
});
deepExtend(diagram, {
init: function (element) {
kendo.init(element, diagram.ui);
},
SpringLayout: SpringLayout,
TreeLayout: TreeLayout,
GraphAdapter: DiagramToHyperTreeAdapter,
LayeredLayout: LayeredLayout,
LayoutBase: LayoutBase,
LayoutState: LayoutState
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.toolbar', [
'kendo.core',
'kendo.userevents',
'kendo.popup'
], f);
}(function () {
var __meta__ = {
id: 'toolbar',
name: 'ToolBar',
category: 'web',
description: 'The ToolBar widget displays one or more command buttons divided into groups.',
depends: ['core']
};
(function ($, undefined) {
var kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, proxy = $.proxy, isFunction = kendo.isFunction, keys = kendo.keys, TOOLBAR = 'k-toolbar', BUTTON = 'k-button', OVERFLOW_BUTTON = 'k-overflow-button', TOGGLE_BUTTON = 'k-toggle-button', BUTTON_GROUP = 'k-button-group', SPLIT_BUTTON = 'k-split-button', SEPARATOR = 'k-separator', POPUP = 'k-popup', RESIZABLE_TOOLBAR = 'k-toolbar-resizable', STATE_ACTIVE = 'k-state-active', STATE_DISABLED = 'k-state-disabled', STATE_HIDDEN = 'k-state-hidden', GROUP_START = 'k-group-start', GROUP_END = 'k-group-end', PRIMARY = 'k-primary', ICON = 'k-icon', ICON_PREFIX = 'k-i-', BUTTON_ICON = 'k-button-icon', BUTTON_ICON_TEXT = 'k-button-icontext', LIST_CONTAINER = 'k-list-container k-split-container', SPLIT_BUTTON_ARROW = 'k-split-button-arrow', OVERFLOW_ANCHOR = 'k-overflow-anchor', OVERFLOW_CONTAINER = 'k-overflow-container', FIRST_TOOLBAR_VISIBLE = 'k-toolbar-first-visible', LAST_TOOLBAR_VISIBLE = 'k-toolbar-last-visible', CLICK = 'click', TOGGLE = 'toggle', OPEN = 'open', CLOSE = 'close', OVERFLOW_OPEN = 'overflowOpen', OVERFLOW_CLOSE = 'overflowClose', OVERFLOW_NEVER = 'never', OVERFLOW_AUTO = 'auto', OVERFLOW_ALWAYS = 'always', OVERFLOW_HIDDEN = 'k-overflow-hidden', KENDO_UID_ATTR = kendo.attr('uid');
kendo.toolbar = {};
var components = {
overflowAnchor: '
',
overflowContainer: '
'
};
kendo.toolbar.registerComponent = function (name, toolbar, overflow) {
components[name] = {
toolbar: toolbar,
overflow: overflow
};
};
var Item = kendo.Class.extend({
addOverflowAttr: function () {
this.element.attr(kendo.attr('overflow'), this.options.overflow || OVERFLOW_AUTO);
},
addUidAttr: function () {
this.element.attr(KENDO_UID_ATTR, this.options.uid);
},
addIdAttr: function () {
if (this.options.id) {
this.element.attr('id', this.options.id);
}
},
addOverflowIdAttr: function () {
if (this.options.id) {
this.element.attr('id', this.options.id + '_overflow');
}
},
attributes: function () {
if (this.options.attributes) {
this.element.attr(this.options.attributes);
}
},
show: function () {
this.element.removeClass(STATE_HIDDEN).show();
this.options.hidden = false;
},
hide: function () {
this.element.addClass(STATE_HIDDEN).hide();
this.options.hidden = true;
},
remove: function () {
this.element.remove();
},
enable: function (isEnabled) {
if (isEnabled === undefined) {
isEnabled = true;
}
this.element.toggleClass(STATE_DISABLED, !isEnabled);
this.options.enable = isEnabled;
},
twin: function () {
var uid = this.element.attr(KENDO_UID_ATTR);
if (this.overflow) {
return this.toolbar.element.find('[' + KENDO_UID_ATTR + '=\'' + uid + '\']').data(this.options.type);
} else if (this.toolbar.options.resizable) {
return this.toolbar.popup.element.find('[' + KENDO_UID_ATTR + '=\'' + uid + '\']').data(this.options.type);
}
}
});
kendo.toolbar.Item = Item;
var Button = Item.extend({
init: function (options, toolbar) {
var element = options.useButtonTag ? $('
') : $('
');
this.element = element;
this.options = options;
this.toolbar = toolbar;
this.attributes();
if (options.primary) {
element.addClass(PRIMARY);
}
if (options.togglable) {
element.addClass(TOGGLE_BUTTON);
this.toggle(options.selected);
}
if (options.url !== undefined && !options.useButtonTag) {
element.attr('href', options.url);
if (options.mobile) {
element.attr(kendo.attr('role'), 'button');
}
}
if (options.group) {
element.attr(kendo.attr('group'), options.group);
this.group = this.toolbar.addToGroup(this, options.group);
}
if (!options.togglable && options.click && isFunction(options.click)) {
this.clickHandler = options.click;
}
if (options.togglable && options.toggle && isFunction(options.toggle)) {
this.toggleHandler = options.toggle;
}
},
toggle: function (state, propagate) {
state = !!state;
if (this.group && state) {
this.group.select(this);
} else if (!this.group) {
this.select(state);
}
if (propagate && this.twin()) {
this.twin().toggle(state);
}
},
getParentGroup: function () {
if (this.options.isChild) {
return this.element.closest('.' + BUTTON_GROUP).data('buttonGroup');
}
},
_addGraphics: function () {
var element = this.element, icon = this.options.icon, spriteCssClass = this.options.spriteCssClass, imageUrl = this.options.imageUrl, isEmpty, span, img;
if (spriteCssClass || imageUrl || icon) {
isEmpty = true;
element.contents().not('span.k-sprite,span.' + ICON + ',img.k-image').each(function (idx, el) {
if (el.nodeType == 1 || el.nodeType == 3 && $.trim(el.nodeValue).length > 0) {
isEmpty = false;
}
});
if (isEmpty) {
element.addClass(BUTTON_ICON);
} else {
element.addClass(BUTTON_ICON_TEXT);
}
}
if (icon) {
span = element.children('span.' + ICON).first();
if (!span[0]) {
span = $('
').prependTo(element);
}
span.addClass(ICON_PREFIX + icon);
} else if (spriteCssClass) {
span = element.children('span.k-sprite').first();
if (!span[0]) {
span = $('
').prependTo(element);
}
span.addClass(spriteCssClass);
} else if (imageUrl) {
img = element.children('img.k-image').first();
if (!img[0]) {
img = $('
![icon]()
').prependTo(element);
}
img.attr('src', imageUrl);
}
}
});
kendo.toolbar.Button = Button;
var ToolBarButton = Button.extend({
init: function (options, toolbar) {
Button.fn.init.call(this, options, toolbar);
var element = this.element;
element.addClass(BUTTON);
this.addIdAttr();
if (options.align) {
element.addClass('k-align-' + options.align);
}
if (options.showText != 'overflow' && options.text) {
if (options.mobile) {
element.html('
' + options.text + '');
} else {
element.html(options.text);
}
}
options.hasIcon = options.showIcon != 'overflow' && (options.icon || options.spriteCssClass || options.imageUrl);
if (options.hasIcon) {
this._addGraphics();
}
this.addUidAttr();
this.addOverflowAttr();
this.enable(options.enable);
if (options.hidden) {
this.hide();
}
this.element.data({
type: 'button',
button: this
});
},
select: function (selected) {
if (selected === undefined) {
selected = false;
}
this.element.toggleClass(STATE_ACTIVE, selected);
this.options.selected = selected;
}
});
kendo.toolbar.ToolBarButton = ToolBarButton;
var OverflowButton = Button.extend({
init: function (options, toolbar) {
this.overflow = true;
Button.fn.init.call(this, options, toolbar);
var element = this.element;
if (options.showText != 'toolbar' && options.text) {
if (options.mobile) {
element.html('
' + options.text + '');
} else {
element.html('
' + options.text + '');
}
}
options.hasIcon = options.showIcon != 'toolbar' && (options.icon || options.spriteCssClass || options.imageUrl);
if (options.hasIcon) {
this._addGraphics();
}
if (!options.isChild) {
this._wrap();
}
this.addOverflowIdAttr();
this.attributes();
this.addUidAttr();
this.addOverflowAttr();
this.enable(options.enable);
element.addClass(OVERFLOW_BUTTON + ' ' + BUTTON);
if (options.hidden) {
this.hide();
}
this.element.data({
type: 'button',
button: this
});
},
_wrap: function () {
this.element = this.element.wrap('
').parent();
},
overflowHidden: function () {
this.element.addClass(OVERFLOW_HIDDEN);
},
select: function (selected) {
if (selected === undefined) {
selected = false;
}
if (this.options.isChild) {
this.element.toggleClass(STATE_ACTIVE, selected);
} else {
this.element.find('.k-button').toggleClass(STATE_ACTIVE, selected);
}
this.options.selected = selected;
}
});
kendo.toolbar.OverflowButton = OverflowButton;
kendo.toolbar.registerComponent('button', ToolBarButton, OverflowButton);
var ButtonGroup = Item.extend({
createButtons: function (buttonConstructor) {
var options = this.options;
var items = options.buttons || [];
var item;
for (var i = 0; i < items.length; i++) {
if (!items[i].uid) {
items[i].uid = kendo.guid();
}
item = new buttonConstructor($.extend({
mobile: options.mobile,
isChild: true,
type: 'button'
}, items[i]), this.toolbar);
item.element.appendTo(this.element);
}
},
refresh: function () {
this.element.children().filter(':not(\'.' + STATE_HIDDEN + '\'):first').addClass(GROUP_START);
this.element.children().filter(':not(\'.' + STATE_HIDDEN + '\'):last').addClass(GROUP_END);
}
});
kendo.toolbar.ButtonGroup = ButtonGroup;
var ToolBarButtonGroup = ButtonGroup.extend({
init: function (options, toolbar) {
var element = this.element = $('
');
this.options = options;
this.toolbar = toolbar;
this.addIdAttr();
if (options.align) {
element.addClass('k-align-' + options.align);
}
this.createButtons(ToolBarButton);
this.attributes();
this.addUidAttr();
this.addOverflowAttr();
this.refresh();
element.addClass(BUTTON_GROUP);
this.element.data({
type: 'buttonGroup',
buttonGroup: this
});
}
});
kendo.toolbar.ToolBarButtonGroup = ToolBarButtonGroup;
var OverflowButtonGroup = ButtonGroup.extend({
init: function (options, toolbar) {
var element = this.element = $('
');
this.options = options;
this.toolbar = toolbar;
this.overflow = true;
this.addOverflowIdAttr();
this.createButtons(OverflowButton);
this.attributes();
this.addUidAttr();
this.addOverflowAttr();
this.refresh();
element.addClass((options.mobile ? '' : BUTTON_GROUP) + ' k-overflow-group');
this.element.data({
type: 'buttonGroup',
buttonGroup: this
});
},
overflowHidden: function () {
this.element.addClass(OVERFLOW_HIDDEN);
}
});
kendo.toolbar.OverflowButtonGroup = OverflowButtonGroup;
kendo.toolbar.registerComponent('buttonGroup', ToolBarButtonGroup, OverflowButtonGroup);
var ToolBarSplitButton = Item.extend({
init: function (options, toolbar) {
var element = this.element = $('
');
this.options = options;
this.toolbar = toolbar;
this.mainButton = new ToolBarButton(options, toolbar);
this.arrowButton = $('
');
this.popupElement = $('
');
this.mainButton.element.removeAttr('href tabindex').appendTo(element);
this.arrowButton.appendTo(element);
this.popupElement.appendTo(element);
if (options.align) {
element.addClass('k-align-' + options.align);
}
if (!options.id) {
options.id = options.uid;
}
element.attr('id', options.id + '_wrapper');
this.addOverflowAttr();
this.addUidAttr();
this.createMenuButtons();
this.createPopup();
this._navigatable();
this.mainButton.main = true;
element.data({
type: 'splitButton',
splitButton: this,
kendoPopup: this.popup
});
},
_navigatable: function () {
var that = this;
that.popupElement.on('keydown', '.' + BUTTON, function (e) {
var li = $(e.target).parent();
e.preventDefault();
if (e.keyCode === keys.ESC || e.keyCode === keys.TAB || e.altKey && e.keyCode === keys.UP) {
that.toggle();
that.focus();
} else if (e.keyCode === keys.DOWN) {
findFocusableSibling(li, 'next').focus();
} else if (e.keyCode === keys.UP) {
findFocusableSibling(li, 'prev').focus();
} else if (e.keyCode === keys.SPACEBAR || e.keyCode === keys.ENTER) {
that.toolbar.userEvents.trigger('tap', { target: $(e.target) });
}
});
},
createMenuButtons: function () {
var options = this.options;
var items = options.menuButtons;
var item;
for (var i = 0; i < items.length; i++) {
item = new ToolBarButton($.extend({
mobile: options.mobile,
type: 'button',
click: options.click
}, items[i]), this.toolbar);
item.element.wrap('
').parent().appendTo(this.popupElement);
}
},
createPopup: function () {
var options = this.options;
var element = this.element;
this.popupElement.attr('id', options.id + '_optionlist').attr(KENDO_UID_ATTR, options.rootUid);
if (options.mobile) {
this.popupElement = actionSheetWrap(this.popupElement);
}
this.popup = this.popupElement.kendoPopup({
appendTo: options.mobile ? $(options.mobile).children('.km-pane') : null,
anchor: element,
isRtl: this.toolbar._isRtl,
copyAnchorStyles: false,
animation: options.animation,
open: adjustPopupWidth,
activate: function () {
this.element.find(':kendoFocusable').first().focus();
},
close: function () {
element.focus();
}
}).data('kendoPopup');
this.popup.element.on(CLICK, 'a.k-button', preventClick);
},
remove: function () {
this.popup.element.off(CLICK, 'a.k-button');
this.popup.destroy();
this.element.remove();
},
toggle: function () {
this.popup.toggle();
},
enable: function (isEnabled) {
if (isEnabled === undefined) {
isEnabled = true;
}
this.mainButton.enable(isEnabled);
this.options.enable = isEnabled;
},
focus: function () {
this.element.focus();
}
});
kendo.toolbar.ToolBarSplitButton = ToolBarSplitButton;
var OverflowSplitButton = Item.extend({
init: function (options, toolbar) {
var element = this.element = $('
'), items = options.menuButtons, item;
this.options = options;
this.toolbar = toolbar;
this.overflow = true;
this.mainButton = new OverflowButton($.extend({ isChild: true }, options));
this.mainButton.element.appendTo(element);
for (var i = 0; i < items.length; i++) {
item = new OverflowButton($.extend({
mobile: options.mobile,
isChild: true
}, items[i]), this.toolbar);
item.element.appendTo(element);
}
this.addUidAttr();
this.addOverflowAttr();
this.mainButton.main = true;
element.data({
type: 'splitButton',
splitButton: this
});
},
overflowHidden: function () {
this.element.addClass(OVERFLOW_HIDDEN);
}
});
kendo.toolbar.OverflowSplitButton = OverflowSplitButton;
kendo.toolbar.registerComponent('splitButton', ToolBarSplitButton, OverflowSplitButton);
var ToolBarSeparator = Item.extend({
init: function (options, toolbar) {
var element = this.element = $('
');
this.element = element;
this.options = options;
this.toolbar = toolbar;
this.attributes();
this.addIdAttr();
this.addUidAttr();
this.addOverflowAttr();
element.addClass(SEPARATOR);
element.data({
type: 'separator',
separator: this
});
}
});
var OverflowSeparator = Item.extend({
init: function (options, toolbar) {
var element = this.element = $('
');
this.element = element;
this.options = options;
this.toolbar = toolbar;
this.overflow = true;
this.attributes();
this.addUidAttr();
this.addOverflowIdAttr();
element.addClass(SEPARATOR);
element.data({
type: 'separator',
separator: this
});
},
overflowHidden: function () {
this.element.addClass(OVERFLOW_HIDDEN);
}
});
kendo.toolbar.registerComponent('separator', ToolBarSeparator, OverflowSeparator);
var TemplateItem = Item.extend({
init: function (template, options, toolbar) {
var element = isFunction(template) ? template(options) : template;
if (!(element instanceof jQuery)) {
element = $('
').html(element);
} else {
element = element.wrap('
').parent();
}
this.element = element;
this.options = options;
this.options.type = 'template';
this.toolbar = toolbar;
this.attributes();
this.addUidAttr();
this.addIdAttr();
this.addOverflowAttr();
element.data({
type: 'template',
template: this
});
}
});
kendo.toolbar.TemplateItem = TemplateItem;
var OverflowTemplateItem = Item.extend({
init: function (template, options, toolbar) {
var element = isFunction(template) ? $(template(options)) : $(template);
if (!(element instanceof jQuery)) {
element = $('
').html(element);
} else {
element = element.wrap('
').parent();
}
this.element = element;
this.options = options;
this.options.type = 'template';
this.toolbar = toolbar;
this.overflow = true;
this.attributes();
this.addUidAttr();
this.addOverflowIdAttr();
this.addOverflowAttr();
element.data({
type: 'template',
template: this
});
},
overflowHidden: function () {
this.element.addClass(OVERFLOW_HIDDEN);
}
});
kendo.toolbar.OverflowTemplateItem = OverflowTemplateItem;
function adjustPopupWidth() {
var anchor = this.options.anchor, computedWidth = anchor.outerWidth(), width;
kendo.wrap(this.element).addClass('k-split-wrapper');
if (this.element.css('box-sizing') !== 'border-box') {
width = computedWidth - (this.element.outerWidth() - this.element.width());
} else {
width = computedWidth;
}
this.element.css({
fontFamily: anchor.css('font-family'),
'min-width': width
});
}
function toggleActive(e) {
if (!e.target.is('.k-toggle-button')) {
e.target.toggleClass(STATE_ACTIVE, e.type == 'press');
}
}
function actionSheetWrap(element) {
element = $(element);
return element.hasClass('km-actionsheet') ? element.closest('.km-popup-wrapper') : element.addClass('km-widget km-actionsheet').wrap('').parent().wrap('').parent();
}
function preventClick(e) {
e.preventDefault();
}
function findFocusableSibling(element, dir) {
var getSibling = dir === 'next' ? $.fn.next : $.fn.prev;
var getter = dir === 'next' ? $.fn.first : $.fn.last;
var candidate = getSibling.call(element);
if (candidate.is(':kendoFocusable') || !candidate.length) {
return candidate;
}
if (candidate.find(':kendoFocusable').length) {
return getter.call(candidate.find(':kendoFocusable'));
}
return findFocusableSibling(candidate, dir);
}
var Group = Class.extend({
init: function (name) {
this.name = name;
this.buttons = [];
},
add: function (button) {
this.buttons[this.buttons.length] = button;
},
remove: function (button) {
var index = $.inArray(button, this.buttons);
this.buttons.splice(index, 1);
},
select: function (button) {
var tmp;
for (var i = 0; i < this.buttons.length; i++) {
tmp = this.buttons[i];
tmp.select(false);
}
button.select(true);
if (button.twin()) {
button.twin().select(true);
}
}
});
var ToolBar = Widget.extend({
init: function (element, options) {
var that = this;
Widget.fn.init.call(that, element, options);
options = that.options;
element = that.wrapper = that.element;
element.addClass(TOOLBAR + ' k-widget');
this.uid = kendo.guid();
this._isRtl = kendo.support.isRtl(element);
this._groups = {};
element.attr(KENDO_UID_ATTR, this.uid);
that.isMobile = typeof options.mobile === 'boolean' ? options.mobile : that.element.closest('.km-root')[0];
that.animation = that.isMobile ? { open: { effects: 'fade' } } : {};
if (that.isMobile) {
element.addClass('km-widget');
ICON = 'km-icon';
ICON_PREFIX = 'km-';
BUTTON = 'km-button';
BUTTON_GROUP = 'km-buttongroup km-widget';
STATE_ACTIVE = 'km-state-active';
STATE_DISABLED = 'km-state-disabled';
}
if (options.resizable) {
that._renderOverflow();
element.addClass(RESIZABLE_TOOLBAR);
that.overflowUserEvents = new kendo.UserEvents(that.element, {
threshold: 5,
allowSelection: true,
filter: '.' + OVERFLOW_ANCHOR,
tap: proxy(that._toggleOverflow, that)
});
that._resizeHandler = kendo.onResize(function () {
that.resize();
});
} else {
that.popup = { element: $([]) };
}
if (options.items && options.items.length) {
for (var i = 0; i < options.items.length; i++) {
that.add(options.items[i]);
}
}
that.userEvents = new kendo.UserEvents(document, {
threshold: 5,
allowSelection: true,
filter: '[' + KENDO_UID_ATTR + '=' + this.uid + '] a.' + BUTTON + ', ' + '[' + KENDO_UID_ATTR + '=' + this.uid + '] .' + OVERFLOW_BUTTON,
tap: proxy(that._buttonClick, that),
press: toggleActive,
release: toggleActive
});
that.element.on(CLICK, 'a.k-button', preventClick);
that._navigatable();
if (options.resizable) {
that.popup.element.on(CLICK, +'a.k-button', preventClick);
}
if (options.resizable) {
this._toggleOverflowAnchor();
}
kendo.notify(that);
},
events: [
CLICK,
TOGGLE,
OPEN,
CLOSE,
OVERFLOW_OPEN,
OVERFLOW_CLOSE
],
options: {
name: 'ToolBar',
items: [],
resizable: true,
mobile: null
},
addToGroup: function (button, groupName) {
var group;
if (!this._groups[groupName]) {
group = this._groups[groupName] = new Group();
} else {
group = this._groups[groupName];
}
group.add(button);
return group;
},
destroy: function () {
var that = this;
that.element.find('.' + SPLIT_BUTTON).each(function (idx, element) {
$(element).data('kendoPopup').destroy();
});
that.element.off(CLICK, 'a.k-button');
that.userEvents.destroy();
if (that.options.resizable) {
kendo.unbindResize(that._resizeHandler);
that.overflowUserEvents.destroy();
that.popup.element.off(CLICK, 'a.k-button');
that.popup.destroy();
}
Widget.fn.destroy.call(that);
},
add: function (options) {
var component = components[options.type], template = options.template, tool, that = this, itemClasses = that.isMobile ? '' : 'k-item k-state-default', overflowTemplate = options.overflowTemplate, overflowTool;
$.extend(options, {
uid: kendo.guid(),
animation: that.animation,
mobile: that.isMobile,
rootUid: that.uid
});
if (options.menuButtons) {
for (var i = 0; i < options.menuButtons.length; i++) {
$.extend(options.menuButtons[i], { uid: kendo.guid() });
}
}
if (template && !overflowTemplate) {
options.overflow = OVERFLOW_NEVER;
} else if (!options.overflow) {
options.overflow = OVERFLOW_AUTO;
}
if (options.overflow !== OVERFLOW_NEVER && that.options.resizable) {
if (overflowTemplate) {
overflowTool = new OverflowTemplateItem(overflowTemplate, options, that);
} else if (component) {
overflowTool = new component.overflow(options, that);
overflowTool.element.addClass(itemClasses);
}
if (overflowTool) {
if (options.overflow === OVERFLOW_AUTO) {
overflowTool.overflowHidden();
}
overflowTool.element.appendTo(that.popup.container);
that.angular('compile', function () {
return { elements: overflowTool.element.get() };
});
}
}
if (options.overflow !== OVERFLOW_ALWAYS) {
if (template) {
tool = new TemplateItem(template, options, that);
} else if (component) {
tool = new component.toolbar(options, that);
}
if (tool) {
if (that.options.resizable) {
tool.element.appendTo(that.element).css('visibility', 'hidden');
that._shrink(that.element.innerWidth());
tool.element.css('visibility', 'visible');
} else {
tool.element.appendTo(that.element);
}
that.angular('compile', function () {
return { elements: tool.element.get() };
});
}
}
},
_getItem: function (candidate) {
var element, toolbarItem, overflowItem, isResizable = this.options.resizable, type;
element = this.element.find(candidate);
if (!element.length) {
element = $('.k-split-container[data-uid=' + this.uid + ']').find(candidate);
}
type = element.length ? element.data('type') : '';
toolbarItem = element.data(type);
if (toolbarItem) {
if (toolbarItem.main) {
element = element.parent('.' + SPLIT_BUTTON);
type = 'splitButton';
toolbarItem = element.data(type);
}
if (isResizable) {
overflowItem = toolbarItem.twin();
}
} else if (isResizable) {
element = this.popup.element.find(candidate);
type = element.length ? element.data('type') : '';
overflowItem = element.data(type);
if (overflowItem && overflowItem.main) {
element = element.parent('.' + SPLIT_BUTTON);
type = 'splitButton';
overflowItem = element.data(type);
}
}
return {
type: type,
toolbar: toolbarItem,
overflow: overflowItem
};
},
remove: function (candidate) {
var item = this._getItem(candidate);
if (item.toolbar) {
item.toolbar.remove();
}
if (item.overflow) {
item.overflow.remove();
}
this.resize(true);
},
hide: function (candidate) {
var item = this._getItem(candidate);
if (item.toolbar) {
item.toolbar.hide();
if (item.toolbar.options.type === 'button' && item.toolbar.options.isChild) {
item.toolbar.getParentGroup().refresh();
}
}
if (item.overflow) {
item.overflow.hide();
if (item.overflow.options.type === 'button' && item.overflow.options.isChild) {
item.overflow.getParentGroup().refresh();
}
}
this.resize(true);
},
show: function (candidate) {
var item = this._getItem(candidate);
if (item.toolbar) {
item.toolbar.show();
if (item.toolbar.options.type === 'button' && item.toolbar.options.isChild) {
item.toolbar.getParentGroup().refresh();
}
}
if (item.overflow) {
item.overflow.show();
if (item.overflow.options.type === 'button' && item.overflow.options.isChild) {
item.overflow.getParentGroup().refresh();
}
}
this.resize(true);
},
enable: function (element, enable) {
var item = this._getItem(element);
if (typeof enable == 'undefined') {
enable = true;
}
if (item.toolbar) {
item.toolbar.enable(enable);
}
if (item.overflow) {
item.overflow.enable(enable);
}
},
getSelectedFromGroup: function (groupName) {
return this.element.find('.' + TOGGLE_BUTTON + '[data-group=\'' + groupName + '\']').filter('.' + STATE_ACTIVE);
},
toggle: function (button, checked) {
var element = $(button), item = element.data('button');
if (item.options.togglable) {
if (checked === undefined) {
checked = true;
}
item.toggle(checked, true);
}
},
_renderOverflow: function () {
var that = this, overflowContainer = components.overflowContainer, isRtl = that._isRtl, horizontalDirection = isRtl ? 'left' : 'right';
that.overflowAnchor = $(components.overflowAnchor).addClass(BUTTON);
that.element.append(that.overflowAnchor);
if (that.isMobile) {
that.overflowAnchor.append('
');
overflowContainer = actionSheetWrap(overflowContainer);
} else {
that.overflowAnchor.append('
');
}
that.popup = new kendo.ui.Popup(overflowContainer, {
origin: 'bottom ' + horizontalDirection,
position: 'top ' + horizontalDirection,
anchor: that.overflowAnchor,
isRtl: isRtl,
animation: that.animation,
appendTo: that.isMobile ? $(that.isMobile).children('.km-pane') : null,
copyAnchorStyles: false,
open: function (e) {
var wrapper = kendo.wrap(that.popup.element).addClass('k-overflow-wrapper');
if (!that.isMobile) {
wrapper.css('margin-left', (isRtl ? -1 : 1) * ((wrapper.outerWidth() - wrapper.width()) / 2 + 1));
} else {
that.popup.container.css('max-height', parseFloat($('.km-content:visible').innerHeight()) - 15 + 'px');
}
if (that.trigger(OVERFLOW_OPEN)) {
e.preventDefault();
}
},
activate: function () {
this.element.find(':kendoFocusable').first().focus();
},
close: function (e) {
if (that.trigger(OVERFLOW_CLOSE)) {
e.preventDefault();
}
this.element.focus();
}
});
that.popup.element.on('keydown', '.' + BUTTON, function (e) {
var target = $(e.target), li = target.parent(), isComplexTool = li.is('.' + BUTTON_GROUP) || li.is('.' + SPLIT_BUTTON), element;
e.preventDefault();
if (e.keyCode === keys.ESC || e.keyCode === keys.TAB || e.altKey && e.keyCode === keys.UP) {
that._toggleOverflow();
that.overflowAnchor.focus();
} else if (e.keyCode === keys.DOWN) {
element = !isComplexTool || isComplexTool && target.is(':last-child') ? li : target;
findFocusableSibling(element, 'next').focus();
} else if (e.keyCode === keys.UP) {
element = !isComplexTool || isComplexTool && target.is(':first-child') ? li : target;
findFocusableSibling(element, 'prev').focus();
} else if (e.keyCode === keys.SPACEBAR || e.keyCode === keys.ENTER) {
that.userEvents.trigger('tap', { target: $(e.target) });
}
});
if (that.isMobile) {
that.popup.container = that.popup.element.find('.' + OVERFLOW_CONTAINER);
} else {
that.popup.container = that.popup.element;
}
that.popup.container.attr(KENDO_UID_ATTR, this.uid);
},
_toggleOverflowAnchor: function () {
var hasVisibleChildren = false;
if (this.options.mobile) {
hasVisibleChildren = this.popup.element.find('.' + OVERFLOW_CONTAINER).children(':not(.' + OVERFLOW_HIDDEN + ', .' + POPUP + ')').length > 0;
} else {
hasVisibleChildren = this.popup.element.children(':not(.' + OVERFLOW_HIDDEN + ', .' + POPUP + ')').length > 0;
}
if (hasVisibleChildren) {
this.overflowAnchor.css({
visibility: 'visible',
width: ''
});
} else {
this.overflowAnchor.css({
visibility: 'hidden',
width: '1px'
});
}
},
_buttonClick: function (e) {
var that = this, popup, target, item, splitContainer, isSplitButtonArrow = e.target.closest('.' + SPLIT_BUTTON_ARROW).length, handler, eventData, urlTarget;
e.preventDefault();
if (isSplitButtonArrow) {
that._toggle(e);
return;
}
target = $(e.target).closest('.' + BUTTON, that.element);
if (target.hasClass(OVERFLOW_ANCHOR)) {
return;
}
item = target.data('button');
if (!item && that.popup) {
target = $(e.target).closest('.' + OVERFLOW_BUTTON, that.popup.container);
item = target.parent('li').data('button');
}
if (!item || !item.options.enable) {
return;
}
if (item.options.togglable) {
handler = isFunction(item.toggleHandler) ? item.toggleHandler : null;
item.toggle(!item.options.selected, true);
eventData = {
target: target,
group: item.options.group,
checked: item.options.selected,
id: item.options.id
};
if (handler) {
handler.call(that, eventData);
}
that.trigger(TOGGLE, eventData);
} else {
handler = isFunction(item.clickHandler) ? item.clickHandler : null;
eventData = {
sender: that,
target: target,
id: item.options.id
};
if (handler) {
handler.call(that, eventData);
}
that.trigger(CLICK, eventData);
}
if (item.options.url) {
if (item.options.attributes && item.options.attributes.target) {
urlTarget = item.options.attributes.target;
}
window.open(item.options.url, urlTarget || '_self');
}
if (target.hasClass(OVERFLOW_BUTTON)) {
that.popup.close();
}
splitContainer = target.closest('.k-split-container');
if (splitContainer[0]) {
popup = splitContainer.data('kendoPopup');
(popup ? popup : splitContainer.parents('.km-popup-wrapper').data('kendoPopup')).close();
}
},
_navigatable: function () {
var that = this;
that.element.attr('tabindex', 0).focus(function () {
var element = $(this).find(':kendoFocusable:first');
if (element.is('.' + OVERFLOW_ANCHOR)) {
element = findFocusableSibling(element, 'next');
}
element[0].focus();
}).on('keydown', proxy(that._keydown, that));
},
_keydown: function (e) {
var target = $(e.target), keyCode = e.keyCode, items = this.element.children(':not(.k-separator):visible');
if (keyCode === keys.TAB) {
var element = target.parentsUntil(this.element).last(), lastHasFocus = false, firstHasFocus = false;
if (!element.length) {
element = target;
}
if (element.is('.' + OVERFLOW_ANCHOR)) {
if (e.shiftKey) {
e.preventDefault();
}
if (items.last().is(':kendoFocusable')) {
items.last().focus();
} else {
items.last().find(':kendoFocusable').last().focus();
}
}
if (!e.shiftKey && items.index(element) === items.length - 1) {
if (element.is('.' + BUTTON_GROUP)) {
lastHasFocus = target.is(':last-child');
} else {
lastHasFocus = true;
}
}
if (e.shiftKey && items.index(element) === 1) {
if (element.is('.' + BUTTON_GROUP)) {
firstHasFocus = target.is(':first-child');
} else {
firstHasFocus = true;
}
}
if (lastHasFocus && this.overflowAnchor.css('visibility') !== 'hidden') {
e.preventDefault();
this.overflowAnchor.focus();
}
if (firstHasFocus) {
e.preventDefault();
this.wrapper.prev(':kendoFocusable').focus();
}
}
if (e.altKey && keyCode === keys.DOWN) {
var splitButton = $(document.activeElement).data('splitButton');
var isOverflowAnchor = $(document.activeElement).is('.' + OVERFLOW_ANCHOR);
if (splitButton) {
splitButton.toggle();
} else if (isOverflowAnchor) {
this._toggleOverflow();
}
return;
}
if ((keyCode === keys.SPACEBAR || keyCode === keys.ENTER) && !target.is('input, checkbox')) {
e.preventDefault();
if (target.is('.' + SPLIT_BUTTON)) {
target = target.children().first();
}
this.userEvents.trigger('tap', { target: target });
return;
}
},
_toggle: function (e) {
var splitButton = $(e.target).closest('.' + SPLIT_BUTTON).data('splitButton'), isDefaultPrevented;
e.preventDefault();
if (!splitButton.options.enable) {
return;
}
if (splitButton.popup.element.is(':visible')) {
isDefaultPrevented = this.trigger(CLOSE, { target: splitButton.element });
} else {
isDefaultPrevented = this.trigger(OPEN, { target: splitButton.element });
}
if (!isDefaultPrevented) {
splitButton.toggle();
}
},
_toggleOverflow: function () {
this.popup.toggle();
},
_resize: function (e) {
var containerWidth = e.width;
if (!this.options.resizable) {
return;
}
this.popup.close();
this._shrink(containerWidth);
this._stretch(containerWidth);
this._markVisibles();
this._toggleOverflowAnchor();
},
_childrenWidth: function () {
var childrenWidth = 0;
this.element.children(':visible:not(\'.' + STATE_HIDDEN + '\')').each(function () {
childrenWidth += $(this).outerWidth(true);
});
return Math.ceil(childrenWidth);
},
_shrink: function (containerWidth) {
var commandElement, visibleCommands;
if (containerWidth < this._childrenWidth()) {
visibleCommands = this.element.children(':visible:not([data-overflow=\'never\'], .' + OVERFLOW_ANCHOR + ')');
for (var i = visibleCommands.length - 1; i >= 0; i--) {
commandElement = visibleCommands.eq(i);
if (containerWidth > this._childrenWidth()) {
break;
} else {
this._hideItem(commandElement);
}
}
}
},
_stretch: function (containerWidth) {
var commandElement, hiddenCommands;
if (containerWidth > this._childrenWidth()) {
hiddenCommands = this.element.children(':hidden:not(\'.' + STATE_HIDDEN + '\')');
for (var i = 0; i < hiddenCommands.length; i++) {
commandElement = hiddenCommands.eq(i);
if (containerWidth < this._childrenWidth() || !this._showItem(commandElement, containerWidth)) {
break;
}
}
}
},
_hideItem: function (item) {
item.hide();
if (this.popup) {
this.popup.container.find('>li[data-uid=\'' + item.data('uid') + '\']').removeClass(OVERFLOW_HIDDEN);
}
},
_showItem: function (item, containerWidth) {
if (item.length && containerWidth > this._childrenWidth() + item.outerWidth(true)) {
item.show();
if (this.popup) {
this.popup.container.find('>li[data-uid=\'' + item.data('uid') + '\']').addClass(OVERFLOW_HIDDEN);
}
return true;
}
return false;
},
_markVisibles: function () {
var overflowItems = this.popup.container.children(), toolbarItems = this.element.children(':not(.k-overflow-anchor)'), visibleOverflowItems = overflowItems.filter(':not(.k-overflow-hidden)'), visibleToolbarItems = toolbarItems.filter(':visible');
overflowItems.add(toolbarItems).removeClass(FIRST_TOOLBAR_VISIBLE + ' ' + LAST_TOOLBAR_VISIBLE);
visibleOverflowItems.first().add(visibleToolbarItems.first()).addClass(FIRST_TOOLBAR_VISIBLE);
visibleOverflowItems.last().add(visibleToolbarItems.last()).addClass(LAST_TOOLBAR_VISIBLE);
}
});
kendo.ui.plugin(ToolBar);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.calendar', ['kendo.core'], f);
}(function () {
var __meta__ = {
id: 'calendar',
name: 'Calendar',
category: 'web',
description: 'The Calendar widget renders a graphical calendar that supports navigation and selection.',
depends: ['core']
};
(function ($, undefined) {
var kendo = window.kendo, support = kendo.support, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, parse = kendo.parseDate, adjustDST = kendo.date.adjustDST, extractFormat = kendo._extractFormat, template = kendo.template, getCulture = kendo.getCulture, transitions = kendo.support.transitions, transitionOrigin = transitions ? transitions.css + 'transform-origin' : '', cellTemplate = template('
#=data.value# | ', { useWithBlock: false }), emptyCellTemplate = template('
| ', { useWithBlock: false }), browser = kendo.support.browser, isIE8 = browser.msie && browser.version < 9, ns = '.kendoCalendar', CLICK = 'click' + ns, KEYDOWN_NS = 'keydown' + ns, ID = 'id', MIN = 'min', LEFT = 'left', SLIDE = 'slideIn', MONTH = 'month', CENTURY = 'century', CHANGE = 'change', NAVIGATE = 'navigate', VALUE = 'value', HOVER = 'k-state-hover', DISABLED = 'k-state-disabled', FOCUSED = 'k-state-focused', OTHERMONTH = 'k-other-month', OTHERMONTHCLASS = ' class="' + OTHERMONTH + '"', TODAY = 'k-nav-today', CELLSELECTOR = 'td:has(.k-link)', BLUR = 'blur' + ns, FOCUS = 'focus', FOCUS_WITH_NS = FOCUS + ns, MOUSEENTER = support.touch ? 'touchstart' : 'mouseenter', MOUSEENTER_WITH_NS = support.touch ? 'touchstart' + ns : 'mouseenter' + ns, MOUSELEAVE = support.touch ? 'touchend' + ns + ' touchmove' + ns : 'mouseleave' + ns, MS_PER_MINUTE = 60000, MS_PER_DAY = 86400000, PREVARROW = '_prevArrow', NEXTARROW = '_nextArrow', ARIA_DISABLED = 'aria-disabled', ARIA_SELECTED = 'aria-selected', proxy = $.proxy, extend = $.extend, DATE = Date, views = {
month: 0,
year: 1,
decade: 2,
century: 3
};
var Calendar = Widget.extend({
init: function (element, options) {
var that = this, value, id;
Widget.fn.init.call(that, element, options);
element = that.wrapper = that.element;
options = that.options;
options.url = window.unescape(options.url);
that.options.disableDates = getDisabledExpr(that.options.disableDates);
that._templates();
that._header();
that._footer(that.footer);
id = element.addClass('k-widget k-calendar').on(MOUSEENTER_WITH_NS + ' ' + MOUSELEAVE, CELLSELECTOR, mousetoggle).on(KEYDOWN_NS, 'table.k-content', proxy(that._move, that)).on(CLICK, CELLSELECTOR, function (e) {
var link = e.currentTarget.firstChild, value = that._toDateObject(link);
if (link.href.indexOf('#') != -1) {
e.preventDefault();
}
if (that.options.disableDates(value) && that._view.name == 'month') {
return;
}
that._click($(link));
}).on('mouseup' + ns, 'table.k-content, .k-footer', function () {
that._focusView(that.options.focusOnNav !== false);
}).attr(ID);
if (id) {
that._cellID = id + '_cell_selected';
}
normalize(options);
value = parse(options.value, options.format, options.culture);
that._index = views[options.start];
that._current = new DATE(+restrictValue(value, options.min, options.max));
that._addClassProxy = function () {
that._active = true;
if (that._cell.hasClass(DISABLED)) {
var todayString = that._view.toDateString(getToday());
that._cell = that._cellByDate(todayString);
}
that._cell.addClass(FOCUSED);
};
that._removeClassProxy = function () {
that._active = false;
that._cell.removeClass(FOCUSED);
};
that.value(value);
kendo.notify(that);
},
options: {
name: 'Calendar',
value: null,
min: new DATE(1900, 0, 1),
max: new DATE(2099, 11, 31),
dates: [],
url: '',
culture: '',
footer: '',
format: '',
month: {},
start: MONTH,
depth: MONTH,
animation: {
horizontal: {
effects: SLIDE,
reverse: true,
duration: 500,
divisor: 2
},
vertical: {
effects: 'zoomIn',
duration: 400
}
}
},
events: [
CHANGE,
NAVIGATE
],
setOptions: function (options) {
var that = this;
normalize(options);
if (!options.dates[0]) {
options.dates = that.options.dates;
}
options.disableDates = getDisabledExpr(options.disableDates);
Widget.fn.setOptions.call(that, options);
that._templates();
that._footer(that.footer);
that._index = views[that.options.start];
that.navigate();
},
destroy: function () {
var that = this, today = that._today;
that.element.off(ns);
that._title.off(ns);
that[PREVARROW].off(ns);
that[NEXTARROW].off(ns);
kendo.destroy(that._table);
if (today) {
kendo.destroy(today.off(ns));
}
Widget.fn.destroy.call(that);
},
current: function () {
return this._current;
},
view: function () {
return this._view;
},
focus: function (table) {
table = table || this._table;
this._bindTable(table);
table.focus();
},
min: function (value) {
return this._option(MIN, value);
},
max: function (value) {
return this._option('max', value);
},
navigateToPast: function () {
this._navigate(PREVARROW, -1);
},
navigateToFuture: function () {
this._navigate(NEXTARROW, 1);
},
navigateUp: function () {
var that = this, index = that._index;
if (that._title.hasClass(DISABLED)) {
return;
}
that.navigate(that._current, ++index);
},
navigateDown: function (value) {
var that = this, index = that._index, depth = that.options.depth;
if (!value) {
return;
}
if (index === views[depth]) {
if (!isEqualDate(that._value, that._current) || !isEqualDate(that._value, value)) {
that.value(value);
that.trigger(CHANGE);
}
return;
}
that.navigate(value, --index);
},
navigate: function (value, view) {
view = isNaN(view) ? views[view] : view;
var that = this, options = that.options, culture = options.culture, min = options.min, max = options.max, title = that._title, from = that._table, old = that._oldTable, selectedValue = that._value, currentValue = that._current, future = value && +value > +currentValue, vertical = view !== undefined && view !== that._index, to, currentView, compare, disabled;
if (!value) {
value = currentValue;
}
that._current = value = new DATE(+restrictValue(value, min, max));
if (view === undefined) {
view = that._index;
} else {
that._index = view;
}
that._view = currentView = calendar.views[view];
compare = currentView.compare;
disabled = view === views[CENTURY];
title.toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
disabled = compare(value, min) < 1;
that[PREVARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
disabled = compare(value, max) > -1;
that[NEXTARROW].toggleClass(DISABLED, disabled).attr(ARIA_DISABLED, disabled);
if (from && old && old.data('animating')) {
old.kendoStop(true, true);
from.kendoStop(true, true);
}
that._oldTable = from;
if (!from || that._changeView) {
title.html(currentView.title(value, min, max, culture));
that._table = to = $(currentView.content(extend({
min: min,
max: max,
date: value,
url: options.url,
dates: options.dates,
format: options.format,
culture: culture,
disableDates: options.disableDates
}, that[currentView.name])));
makeUnselectable(to);
var replace = from && from.data('start') === to.data('start');
that._animate({
from: from,
to: to,
vertical: vertical,
future: future,
replace: replace
});
that.trigger(NAVIGATE);
that._focus(value);
}
if (view === views[options.depth] && selectedValue && !that.options.disableDates(selectedValue)) {
that._class('k-state-selected', selectedValue);
}
that._class(FOCUSED, value);
if (!from && that._cell) {
that._cell.removeClass(FOCUSED);
}
that._changeView = true;
},
value: function (value) {
var that = this, view = that._view, options = that.options, old = that._view, min = options.min, max = options.max;
if (value === undefined) {
return that._value;
}
if (value === null) {
that._current = new Date(that._current.getFullYear(), that._current.getMonth(), that._current.getDate());
}
value = parse(value, options.format, options.culture);
if (value !== null) {
value = new DATE(+value);
if (!isInRange(value, min, max)) {
value = null;
}
}
if (!that.options.disableDates(value)) {
that._value = value;
} else if (that._value === undefined) {
that._value = null;
}
if (old && value === null && that._cell) {
that._cell.removeClass('k-state-selected');
} else {
that._changeView = !value || view && view.compare(value, that._current) !== 0;
that.navigate(value);
}
},
_move: function (e) {
var that = this, options = that.options, key = e.keyCode, view = that._view, index = that._index, min = that.options.min, max = that.options.max, currentValue = new DATE(+that._current), isRtl = kendo.support.isRtl(that.wrapper), isDisabled = that.options.disableDates, value, prevent, method, temp;
if (e.target === that._table[0]) {
that._active = true;
}
if (e.ctrlKey) {
if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) {
that.navigateToFuture();
prevent = true;
} else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) {
that.navigateToPast();
prevent = true;
} else if (key == keys.UP) {
that.navigateUp();
prevent = true;
} else if (key == keys.DOWN) {
that._click($(that._cell[0].firstChild));
prevent = true;
}
} else {
if (key == keys.RIGHT && !isRtl || key == keys.LEFT && isRtl) {
value = 1;
prevent = true;
} else if (key == keys.LEFT && !isRtl || key == keys.RIGHT && isRtl) {
value = -1;
prevent = true;
} else if (key == keys.UP) {
value = index === 0 ? -7 : -4;
prevent = true;
} else if (key == keys.DOWN) {
value = index === 0 ? 7 : 4;
prevent = true;
} else if (key == keys.ENTER) {
that._click($(that._cell[0].firstChild));
prevent = true;
} else if (key == keys.HOME || key == keys.END) {
method = key == keys.HOME ? 'first' : 'last';
temp = view[method](currentValue);
currentValue = new DATE(temp.getFullYear(), temp.getMonth(), temp.getDate(), currentValue.getHours(), currentValue.getMinutes(), currentValue.getSeconds(), currentValue.getMilliseconds());
prevent = true;
} else if (key == keys.PAGEUP) {
prevent = true;
that.navigateToPast();
} else if (key == keys.PAGEDOWN) {
prevent = true;
that.navigateToFuture();
}
if (value || method) {
if (!method) {
view.setDate(currentValue, value);
}
if (isDisabled(currentValue)) {
currentValue = that._nextNavigatable(currentValue, value);
}
if (isInRange(currentValue, min, max)) {
that._focus(restrictValue(currentValue, options.min, options.max));
}
}
}
if (prevent) {
e.preventDefault();
}
return that._current;
},
_nextNavigatable: function (currentValue, value) {
var that = this, disabled = true, view = that._view, min = that.options.min, max = that.options.max, isDisabled = that.options.disableDates, navigatableDate = new Date(currentValue.getTime());
view.setDate(navigatableDate, -value);
while (disabled) {
view.setDate(currentValue, value);
if (!isInRange(currentValue, min, max)) {
currentValue = navigatableDate;
break;
}
disabled = isDisabled(currentValue);
}
return currentValue;
},
_animate: function (options) {
var that = this, from = options.from, to = options.to, active = that._active;
if (!from) {
to.insertAfter(that.element[0].firstChild);
that._bindTable(to);
} else if (from.parent().data('animating')) {
from.off(ns);
from.parent().kendoStop(true, true).remove();
from.remove();
to.insertAfter(that.element[0].firstChild);
that._focusView(active);
} else if (!from.is(':visible') || that.options.animation === false || options.replace) {
to.insertAfter(from);
from.off(ns).remove();
that._focusView(active);
} else {
that[options.vertical ? '_vertical' : '_horizontal'](from, to, options.future);
}
},
_horizontal: function (from, to, future) {
var that = this, active = that._active, horizontal = that.options.animation.horizontal, effects = horizontal.effects, viewWidth = from.outerWidth();
if (effects && effects.indexOf(SLIDE) != -1) {
from.add(to).css({ width: viewWidth });
from.wrap('
');
that._focusView(active, from);
from.parent().css({
position: 'relative',
width: viewWidth * 2,
'float': LEFT,
'margin-left': future ? 0 : -viewWidth
});
to[future ? 'insertAfter' : 'insertBefore'](from);
extend(horizontal, {
effects: SLIDE + ':' + (future ? 'right' : LEFT),
complete: function () {
from.off(ns).remove();
that._oldTable = null;
to.unwrap();
that._focusView(active);
}
});
from.parent().kendoStop(true, true).kendoAnimate(horizontal);
}
},
_vertical: function (from, to) {
var that = this, vertical = that.options.animation.vertical, effects = vertical.effects, active = that._active, cell, position;
if (effects && effects.indexOf('zoom') != -1) {
to.css({
position: 'absolute',
top: from.prev().outerHeight(),
left: 0
}).insertBefore(from);
if (transitionOrigin) {
cell = that._cellByDate(that._view.toDateString(that._current));
position = cell.position();
position = position.left + parseInt(cell.width() / 2, 10) + 'px' + ' ' + (position.top + parseInt(cell.height() / 2, 10) + 'px');
to.css(transitionOrigin, position);
}
from.kendoStop(true, true).kendoAnimate({
effects: 'fadeOut',
duration: 600,
complete: function () {
from.off(ns).remove();
that._oldTable = null;
to.css({
position: 'static',
top: 0,
left: 0
});
that._focusView(active);
}
});
to.kendoStop(true, true).kendoAnimate(vertical);
}
},
_cellByDate: function (value) {
return this._table.find('td:not(.' + OTHERMONTH + ')').filter(function () {
return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
});
},
_class: function (className, date) {
var that = this, id = that._cellID, cell = that._cell, value = that._view.toDateString(date), disabledDate;
if (cell) {
cell.removeAttr(ARIA_SELECTED).removeAttr('aria-label').removeAttr(ID);
}
if (date) {
disabledDate = that.options.disableDates(date);
}
cell = that._table.find('td:not(.' + OTHERMONTH + ')').removeClass(className).filter(function () {
return $(this.firstChild).attr(kendo.attr(VALUE)) === value;
}).attr(ARIA_SELECTED, true);
if (className === FOCUSED && !that._active && that.options.focusOnNav !== false || disabledDate) {
className = '';
}
cell.addClass(className);
if (cell[0]) {
that._cell = cell;
}
if (id) {
cell.attr(ID, id);
that._table.removeAttr('aria-activedescendant').attr('aria-activedescendant', id);
}
},
_bindTable: function (table) {
table.on(FOCUS_WITH_NS, this._addClassProxy).on(BLUR, this._removeClassProxy);
},
_click: function (link) {
var that = this, options = that.options, currentValue = new Date(+that._current), value = that._toDateObject(link);
adjustDST(value, 0);
if (that.options.disableDates(value) && that._view.name == 'month') {
value = that._value;
}
that._view.setDate(currentValue, value);
that.navigateDown(restrictValue(currentValue, options.min, options.max));
},
_focus: function (value) {
var that = this, view = that._view;
if (view.compare(value, that._current) !== 0) {
that.navigate(value);
} else {
that._current = value;
that._class(FOCUSED, value);
}
},
_focusView: function (active, table) {
if (active) {
this.focus(table);
}
},
_footer: function (template) {
var that = this, today = getToday(), element = that.element, footer = element.find('.k-footer');
if (!template) {
that._toggle(false);
footer.hide();
return;
}
if (!footer[0]) {
footer = $('').appendTo(element);
}
that._today = footer.show().find('.k-link').html(template(today)).attr('title', kendo.toString(today, 'D', that.options.culture));
that._toggle();
},
_header: function () {
var that = this, element = that.element, links;
if (!element.find('.k-header')[0]) {
element.html('');
}
links = element.find('.k-link').on(MOUSEENTER_WITH_NS + ' ' + MOUSELEAVE + ' ' + FOCUS_WITH_NS + ' ' + BLUR, mousetoggle).click(false);
that._title = links.eq(1).on(CLICK, function () {
that._active = that.options.focusOnNav !== false;
that.navigateUp();
});
that[PREVARROW] = links.eq(0).on(CLICK, function () {
that._active = that.options.focusOnNav !== false;
that.navigateToPast();
});
that[NEXTARROW] = links.eq(2).on(CLICK, function () {
that._active = that.options.focusOnNav !== false;
that.navigateToFuture();
});
},
_navigate: function (arrow, modifier) {
var that = this, index = that._index + 1, currentValue = new DATE(+that._current);
arrow = that[arrow];
if (!arrow.hasClass(DISABLED)) {
if (index > 3) {
currentValue.setFullYear(currentValue.getFullYear() + 100 * modifier);
} else {
calendar.views[index].setDate(currentValue, modifier);
}
that.navigate(currentValue);
}
},
_option: function (option, value) {
var that = this, options = that.options, currentValue = that._value || that._current, isBigger;
if (value === undefined) {
return options[option];
}
value = parse(value, options.format, options.culture);
if (!value) {
return;
}
options[option] = new DATE(+value);
if (option === MIN) {
isBigger = value > currentValue;
} else {
isBigger = currentValue > value;
}
if (isBigger || isEqualMonth(currentValue, value)) {
if (isBigger) {
that._value = null;
}
that._changeView = true;
}
if (!that._changeView) {
that._changeView = !!(options.month.content || options.month.empty);
}
that.navigate(that._value);
that._toggle();
},
_toggle: function (toggle) {
var that = this, options = that.options, isTodayDisabled = that.options.disableDates(getToday()), link = that._today;
if (toggle === undefined) {
toggle = isInRange(getToday(), options.min, options.max);
}
if (link) {
link.off(CLICK);
if (toggle && !isTodayDisabled) {
link.addClass(TODAY).removeClass(DISABLED).on(CLICK, proxy(that._todayClick, that));
} else {
link.removeClass(TODAY).addClass(DISABLED).on(CLICK, prevent);
}
}
},
_todayClick: function (e) {
var that = this, depth = views[that.options.depth], disabled = that.options.disableDates, today = getToday();
e.preventDefault();
if (disabled(today)) {
return;
}
if (that._view.compare(that._current, today) === 0 && that._index == depth) {
that._changeView = false;
}
that._value = today;
that.navigate(today, depth);
that.trigger(CHANGE);
},
_toDateObject: function (link) {
var value = $(link).attr(kendo.attr(VALUE)).split('/');
value = new DATE(value[0], value[1], value[2]);
return value;
},
_templates: function () {
var that = this, options = that.options, footer = options.footer, month = options.month, content = month.content, empty = month.empty;
that.month = {
content: template('
' + (content || '#=data.value#') + ' | ', { useWithBlock: !!content }),
empty: template('
' + (empty || ' ') + ' | ', { useWithBlock: !!empty })
};
that.footer = footer !== false ? template(footer || '#= kendo.toString(data,"D","' + options.culture + '") #', { useWithBlock: false }) : null;
}
});
ui.plugin(Calendar);
var calendar = {
firstDayOfMonth: function (date) {
return new DATE(date.getFullYear(), date.getMonth(), 1);
},
firstVisibleDay: function (date, calendarInfo) {
calendarInfo = calendarInfo || kendo.culture().calendar;
var firstDay = calendarInfo.firstDay, firstVisibleDay = new DATE(date.getFullYear(), date.getMonth(), 0, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
while (firstVisibleDay.getDay() != firstDay) {
calendar.setTime(firstVisibleDay, -1 * MS_PER_DAY);
}
return firstVisibleDay;
},
setTime: function (date, time) {
var tzOffsetBefore = date.getTimezoneOffset(), resultDATE = new DATE(date.getTime() + time), tzOffsetDiff = resultDATE.getTimezoneOffset() - tzOffsetBefore;
date.setTime(resultDATE.getTime() + tzOffsetDiff * MS_PER_MINUTE);
},
views: [
{
name: MONTH,
title: function (date, min, max, culture) {
return getCalendarInfo(culture).months.names[date.getMonth()] + ' ' + date.getFullYear();
},
content: function (options) {
var that = this, idx = 0, min = options.min, max = options.max, date = options.date, dates = options.dates, format = options.format, culture = options.culture, navigateUrl = options.url, hasUrl = navigateUrl && dates[0], currentCalendar = getCalendarInfo(culture), firstDayIdx = currentCalendar.firstDay, days = currentCalendar.days, names = shiftArray(days.names, firstDayIdx), shortNames = shiftArray(days.namesShort, firstDayIdx), start = calendar.firstVisibleDay(date, currentCalendar), firstDayOfMonth = that.first(date), lastDayOfMonth = that.last(date), toDateString = that.toDateString, today = new DATE(), html = '
';
for (; idx < 7; idx++) {
html += '| ' + shortNames[idx] + ' | ';
}
today = new DATE(today.getFullYear(), today.getMonth(), today.getDate());
adjustDST(today, 0);
today = +today;
return view({
cells: 42,
perRow: 7,
html: html += '
',
start: start,
min: new DATE(min.getFullYear(), min.getMonth(), min.getDate()),
max: new DATE(max.getFullYear(), max.getMonth(), max.getDate()),
content: options.content,
empty: options.empty,
setter: that.setDate,
disableDates: options.disableDates,
build: function (date, idx, disableDates) {
var cssClass = [], day = date.getDay(), linkClass = '', url = '#';
if (date < firstDayOfMonth || date > lastDayOfMonth) {
cssClass.push(OTHERMONTH);
}
if (disableDates(date)) {
cssClass.push(DISABLED);
}
if (+date === today) {
cssClass.push('k-today');
}
if (day === 0 || day === 6) {
cssClass.push('k-weekend');
}
if (hasUrl && inArray(+date, dates)) {
url = navigateUrl.replace('{0}', kendo.toString(date, format, culture));
linkClass = ' k-action-link';
}
return {
date: date,
dates: dates,
ns: kendo.ns,
title: kendo.toString(date, 'D', culture),
value: date.getDate(),
dateString: toDateString(date),
cssClass: cssClass[0] ? ' class="' + cssClass.join(' ') + '"' : '',
linkClass: linkClass,
url: url
};
}
});
},
first: function (date) {
return calendar.firstDayOfMonth(date);
},
last: function (date) {
var last = new DATE(date.getFullYear(), date.getMonth() + 1, 0), first = calendar.firstDayOfMonth(date), timeOffset = Math.abs(last.getTimezoneOffset() - first.getTimezoneOffset());
if (timeOffset) {
last.setHours(first.getHours() + timeOffset / 60);
}
return last;
},
compare: function (date1, date2) {
var result, month1 = date1.getMonth(), year1 = date1.getFullYear(), month2 = date2.getMonth(), year2 = date2.getFullYear();
if (year1 > year2) {
result = 1;
} else if (year1 < year2) {
result = -1;
} else {
result = month1 == month2 ? 0 : month1 > month2 ? 1 : -1;
}
return result;
},
setDate: function (date, value) {
var hours = date.getHours();
if (value instanceof DATE) {
date.setFullYear(value.getFullYear(), value.getMonth(), value.getDate());
} else {
calendar.setTime(date, value * MS_PER_DAY);
}
adjustDST(date, hours);
},
toDateString: function (date) {
return date.getFullYear() + '/' + date.getMonth() + '/' + date.getDate();
}
},
{
name: 'year',
title: function (date) {
return date.getFullYear();
},
content: function (options) {
var namesAbbr = getCalendarInfo(options.culture).months.namesAbbr, toDateString = this.toDateString, min = options.min, max = options.max;
return view({
min: new DATE(min.getFullYear(), min.getMonth(), 1),
max: new DATE(max.getFullYear(), max.getMonth(), 1),
start: new DATE(options.date.getFullYear(), 0, 1),
setter: this.setDate,
build: function (date) {
return {
value: namesAbbr[date.getMonth()],
ns: kendo.ns,
dateString: toDateString(date),
cssClass: ''
};
}
});
},
first: function (date) {
return new DATE(date.getFullYear(), 0, date.getDate());
},
last: function (date) {
return new DATE(date.getFullYear(), 11, date.getDate());
},
compare: function (date1, date2) {
return compare(date1, date2);
},
setDate: function (date, value) {
var month, hours = date.getHours();
if (value instanceof DATE) {
month = value.getMonth();
date.setFullYear(value.getFullYear(), month, date.getDate());
if (month !== date.getMonth()) {
date.setDate(0);
}
} else {
month = date.getMonth() + value;
date.setMonth(month);
if (month > 11) {
month -= 12;
}
if (month > 0 && date.getMonth() != month) {
date.setDate(0);
}
}
adjustDST(date, hours);
},
toDateString: function (date) {
return date.getFullYear() + '/' + date.getMonth() + '/1';
}
},
{
name: 'decade',
title: function (date, min, max) {
return title(date, min, max, 10);
},
content: function (options) {
var year = options.date.getFullYear(), toDateString = this.toDateString;
return view({
start: new DATE(year - year % 10 - 1, 0, 1),
min: new DATE(options.min.getFullYear(), 0, 1),
max: new DATE(options.max.getFullYear(), 0, 1),
setter: this.setDate,
build: function (date, idx) {
return {
value: date.getFullYear(),
ns: kendo.ns,
dateString: toDateString(date),
cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : ''
};
}
});
},
first: function (date) {
var year = date.getFullYear();
return new DATE(year - year % 10, date.getMonth(), date.getDate());
},
last: function (date) {
var year = date.getFullYear();
return new DATE(year - year % 10 + 9, date.getMonth(), date.getDate());
},
compare: function (date1, date2) {
return compare(date1, date2, 10);
},
setDate: function (date, value) {
setDate(date, value, 1);
},
toDateString: function (date) {
return date.getFullYear() + '/0/1';
}
},
{
name: CENTURY,
title: function (date, min, max) {
return title(date, min, max, 100);
},
content: function (options) {
var year = options.date.getFullYear(), min = options.min.getFullYear(), max = options.max.getFullYear(), toDateString = this.toDateString, minYear = min, maxYear = max;
minYear = minYear - minYear % 10;
maxYear = maxYear - maxYear % 10;
if (maxYear - minYear < 10) {
maxYear = minYear + 9;
}
return view({
start: new DATE(year - year % 100 - 10, 0, 1),
min: new DATE(minYear, 0, 1),
max: new DATE(maxYear, 0, 1),
setter: this.setDate,
build: function (date, idx) {
var start = date.getFullYear(), end = start + 9;
if (start < min) {
start = min;
}
if (end > max) {
end = max;
}
return {
ns: kendo.ns,
value: start + ' - ' + end,
dateString: toDateString(date),
cssClass: idx === 0 || idx == 11 ? OTHERMONTHCLASS : ''
};
}
});
},
first: function (date) {
var year = date.getFullYear();
return new DATE(year - year % 100, date.getMonth(), date.getDate());
},
last: function (date) {
var year = date.getFullYear();
return new DATE(year - year % 100 + 99, date.getMonth(), date.getDate());
},
compare: function (date1, date2) {
return compare(date1, date2, 100);
},
setDate: function (date, value) {
setDate(date, value, 10);
},
toDateString: function (date) {
var year = date.getFullYear();
return year - year % 10 + '/0/1';
}
}
]
};
function title(date, min, max, modular) {
var start = date.getFullYear(), minYear = min.getFullYear(), maxYear = max.getFullYear(), end;
start = start - start % modular;
end = start + (modular - 1);
if (start < minYear) {
start = minYear;
}
if (end > maxYear) {
end = maxYear;
}
return start + '-' + end;
}
function view(options) {
var idx = 0, data, min = options.min, max = options.max, start = options.start, setter = options.setter, build = options.build, length = options.cells || 12, cellsPerRow = options.perRow || 4, content = options.content || cellTemplate, empty = options.empty || emptyCellTemplate, html = options.html || '';
for (; idx < length; idx++) {
if (idx > 0 && idx % cellsPerRow === 0) {
html += '
';
}
start = new DATE(start.getFullYear(), start.getMonth(), start.getDate(), 0, 0, 0);
adjustDST(start, 0);
data = build(start, idx, options.disableDates);
html += isInRange(start, min, max) ? content(data) : empty(data);
setter(start, 1);
}
return html + '
';
}
function compare(date1, date2, modifier) {
var year1 = date1.getFullYear(), start = date2.getFullYear(), end = start, result = 0;
if (modifier) {
start = start - start % modifier;
end = start - start % modifier + modifier - 1;
}
if (year1 > end) {
result = 1;
} else if (year1 < start) {
result = -1;
}
return result;
}
function getToday() {
var today = new DATE();
return new DATE(today.getFullYear(), today.getMonth(), today.getDate());
}
function restrictValue(value, min, max) {
var today = getToday();
if (value) {
today = new DATE(+value);
}
if (min > today) {
today = new DATE(+min);
} else if (max < today) {
today = new DATE(+max);
}
return today;
}
function isInRange(date, min, max) {
return +date >= +min && +date <= +max;
}
function shiftArray(array, idx) {
return array.slice(idx).concat(array.slice(0, idx));
}
function setDate(date, value, multiplier) {
value = value instanceof DATE ? value.getFullYear() : date.getFullYear() + multiplier * value;
date.setFullYear(value);
}
function mousetoggle(e) {
var disabled = $(this).hasClass('k-state-disabled');
if (!disabled) {
$(this).toggleClass(HOVER, MOUSEENTER.indexOf(e.type) > -1 || e.type == FOCUS);
}
}
function prevent(e) {
e.preventDefault();
}
function getCalendarInfo(culture) {
return getCulture(culture).calendars.standard;
}
function normalize(options) {
var start = views[options.start], depth = views[options.depth], culture = getCulture(options.culture);
options.format = extractFormat(options.format || culture.calendars.standard.patterns.d);
if (isNaN(start)) {
start = 0;
options.start = MONTH;
}
if (depth === undefined || depth > start) {
options.depth = MONTH;
}
if (!options.dates) {
options.dates = [];
}
}
function makeUnselectable(element) {
if (isIE8) {
element.find('*').attr('unselectable', 'on');
}
}
function inArray(date, dates) {
for (var i = 0, length = dates.length; i < length; i++) {
if (date === +dates[i]) {
return true;
}
}
return false;
}
function isEqualDatePart(value1, value2) {
if (value1) {
return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth() && value1.getDate() === value2.getDate();
}
return false;
}
function isEqualMonth(value1, value2) {
if (value1) {
return value1.getFullYear() === value2.getFullYear() && value1.getMonth() === value2.getMonth();
}
return false;
}
function getDisabledExpr(option) {
if (kendo.isFunction(option)) {
return option;
}
if ($.isArray(option)) {
return createDisabledExpr(option);
}
return $.noop;
}
function convertDatesArray(dates) {
var result = [];
for (var i = 0; i < dates.length; i++) {
result.push(dates[i].setHours(0, 0, 0, 0));
}
return result;
}
function createDisabledExpr(dates) {
var body, callback, disabledDates = [], days = [
'su',
'mo',
'tu',
'we',
'th',
'fr',
'sa'
], searchExpression = 'if (found) {' + ' return true ' + '} else {' + 'return false' + '}';
if (dates[0] instanceof DATE) {
disabledDates = convertDatesArray(dates);
body = 'var found = date && $.inArray(date.setHours(0, 0, 0, 0),[' + disabledDates + ']) > -1;' + searchExpression;
} else {
for (var i = 0; i < dates.length; i++) {
var day = dates[i].slice(0, 2).toLowerCase();
var index = $.inArray(day, days);
if (index > -1) {
disabledDates.push(index);
}
}
body = 'var found = date && $.inArray(date.getDay(),[' + disabledDates + ']) > -1;' + searchExpression;
}
callback = new Function('date', body);
return callback;
}
function isEqualDate(oldValue, newValue) {
if (oldValue instanceof Date && newValue instanceof Date) {
oldValue = oldValue.getTime();
newValue = newValue.getTime();
}
return oldValue === newValue;
}
calendar.isEqualDatePart = isEqualDatePart;
calendar.makeUnselectable = makeUnselectable;
calendar.restrictValue = restrictValue;
calendar.isInRange = isInRange;
calendar.normalize = normalize;
calendar.viewsEnum = views;
calendar.disabled = getDisabledExpr;
kendo.calendar = calendar;
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.datepicker', [
'kendo.calendar',
'kendo.popup'
], f);
}(function () {
var __meta__ = {
id: 'datepicker',
name: 'DatePicker',
category: 'web',
description: 'The DatePicker widget allows the user to select a date from a calendar or by direct input.',
depends: [
'calendar',
'popup'
]
};
(function ($, undefined) {
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, parse = kendo.parseDate, keys = kendo.keys, template = kendo.template, activeElement = kendo._activeElement, DIV = '', SPAN = '', ns = '.kendoDatePicker', CLICK = 'click' + ns, OPEN = 'open', CLOSE = 'close', CHANGE = 'change', DISABLED = 'disabled', READONLY = 'readonly', DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', HOVER = 'k-state-hover', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, MOUSEDOWN = 'mousedown' + ns, ID = 'id', MIN = 'min', MAX = 'max', MONTH = 'month', ARIA_DISABLED = 'aria-disabled', ARIA_EXPANDED = 'aria-expanded', ARIA_HIDDEN = 'aria-hidden', ARIA_READONLY = 'aria-readonly', calendar = kendo.calendar, isInRange = calendar.isInRange, restrictValue = calendar.restrictValue, isEqualDatePart = calendar.isEqualDatePart, extend = $.extend, proxy = $.proxy, DATE = Date;
function normalize(options) {
var parseFormats = options.parseFormats, format = options.format;
calendar.normalize(options);
parseFormats = $.isArray(parseFormats) ? parseFormats : [parseFormats];
if (!parseFormats.length) {
parseFormats.push('yyyy-MM-dd');
}
if ($.inArray(format, parseFormats) === -1) {
parseFormats.splice(0, 0, options.format);
}
options.parseFormats = parseFormats;
}
function preventDefault(e) {
e.preventDefault();
}
var DateView = function (options) {
var that = this, id, body = document.body, div = $(DIV).attr(ARIA_HIDDEN, 'true').addClass('k-calendar-container').appendTo(body);
that.options = options = options || {};
id = options.id;
if (id) {
id += '_dateview';
div.attr(ID, id);
that._dateViewID = id;
}
that.popup = new ui.Popup(div, extend(options.popup, options, {
name: 'Popup',
isRtl: kendo.support.isRtl(options.anchor)
}));
that.div = div;
that.value(options.value);
};
DateView.prototype = {
_calendar: function () {
var that = this;
var calendar = that.calendar;
var options = that.options;
var div;
if (!calendar) {
div = $(DIV).attr(ID, kendo.guid()).appendTo(that.popup.element).on(MOUSEDOWN, preventDefault).on(CLICK, 'td:has(.k-link)', proxy(that._click, that));
that.calendar = calendar = new ui.Calendar(div);
that._setOptions(options);
kendo.calendar.makeUnselectable(calendar.element);
calendar.navigate(that._value || that._current, options.start);
that.value(that._value);
}
},
_setOptions: function (options) {
this.calendar.setOptions({
focusOnNav: false,
change: options.change,
culture: options.culture,
dates: options.dates,
depth: options.depth,
footer: options.footer,
format: options.format,
max: options.max,
min: options.min,
month: options.month,
start: options.start,
disableDates: options.disableDates
});
},
setOptions: function (options) {
var old = this.options;
this.options = extend(old, options, {
change: old.change,
close: old.close,
open: old.open
});
if (this.calendar) {
this._setOptions(this.options);
}
},
destroy: function () {
this.popup.destroy();
},
open: function () {
var that = this;
that._calendar();
that.popup.open();
},
close: function () {
this.popup.close();
},
min: function (value) {
this._option(MIN, value);
},
max: function (value) {
this._option(MAX, value);
},
toggle: function () {
var that = this;
that[that.popup.visible() ? CLOSE : OPEN]();
},
move: function (e) {
var that = this, key = e.keyCode, calendar = that.calendar, selectIsClicked = e.ctrlKey && key == keys.DOWN || key == keys.ENTER, handled = false;
if (e.altKey) {
if (key == keys.DOWN) {
that.open();
e.preventDefault();
handled = true;
} else if (key == keys.UP) {
that.close();
e.preventDefault();
handled = true;
}
} else if (that.popup.visible()) {
if (key == keys.ESC || selectIsClicked && calendar._cell.hasClass(SELECTED)) {
that.close();
e.preventDefault();
return true;
}
that._current = calendar._move(e);
handled = true;
}
return handled;
},
current: function (date) {
this._current = date;
this.calendar._focus(date);
},
value: function (value) {
var that = this, calendar = that.calendar, options = that.options, disabledDate = options.disableDates;
if (disabledDate && disabledDate(value)) {
value = null;
}
that._value = value;
that._current = new DATE(+restrictValue(value, options.min, options.max));
if (calendar) {
calendar.value(value);
}
},
_click: function (e) {
if (e.currentTarget.className.indexOf(SELECTED) !== -1) {
this.close();
}
},
_option: function (option, value) {
var that = this;
var calendar = that.calendar;
that.options[option] = value;
if (calendar) {
calendar[option](value);
}
}
};
DateView.normalize = normalize;
kendo.DateView = DateView;
var DatePicker = Widget.extend({
init: function (element, options) {
var that = this, disabled, div;
Widget.fn.init.call(that, element, options);
element = that.element;
options = that.options;
options.disableDates = kendo.calendar.disabled(options.disableDates);
options.min = parse(element.attr('min')) || parse(options.min);
options.max = parse(element.attr('max')) || parse(options.max);
normalize(options);
that._initialOptions = extend({}, options);
that._wrapper();
that.dateView = new DateView(extend({}, options, {
id: element.attr(ID),
anchor: that.wrapper,
change: function () {
that._change(this.value());
that.close();
},
close: function (e) {
if (that.trigger(CLOSE)) {
e.preventDefault();
} else {
element.attr(ARIA_EXPANDED, false);
div.attr(ARIA_HIDDEN, true);
}
},
open: function (e) {
var options = that.options, date;
if (that.trigger(OPEN)) {
e.preventDefault();
} else {
if (that.element.val() !== that._oldText) {
date = parse(element.val(), options.parseFormats, options.culture);
that.dateView[date ? 'current' : 'value'](date);
}
element.attr(ARIA_EXPANDED, true);
div.attr(ARIA_HIDDEN, false);
that._updateARIA(date);
}
}
}));
div = that.dateView.div;
that._icon();
try {
element[0].setAttribute('type', 'text');
} catch (e) {
element[0].type = 'text';
}
element.addClass('k-input').attr({
role: 'combobox',
'aria-expanded': false,
'aria-owns': that.dateView._dateViewID
});
that._reset();
that._template();
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
if (disabled) {
that.enable(false);
} else {
that.readonly(element.is('[readonly]'));
}
that._old = that._update(options.value || that.element.val());
that._oldText = element.val();
kendo.notify(that);
},
events: [
OPEN,
CLOSE,
CHANGE
],
options: {
name: 'DatePicker',
value: null,
footer: '',
format: '',
culture: '',
parseFormats: [],
min: new Date(1900, 0, 1),
max: new Date(2099, 11, 31),
start: MONTH,
depth: MONTH,
animation: {},
month: {},
dates: [],
ARIATemplate: 'Current focused date is #=kendo.toString(data.current, "D")#'
},
setOptions: function (options) {
var that = this;
var value = that._value;
Widget.fn.setOptions.call(that, options);
options = that.options;
options.min = parse(options.min);
options.max = parse(options.max);
normalize(options);
that.dateView.setOptions(options);
if (value) {
that.element.val(kendo.toString(value, options.format, options.culture));
that._updateARIA(value);
}
},
_editable: function (options) {
var that = this, icon = that._dateIcon.off(ns), element = that.element.off(ns), wrapper = that._inputWrapper.off(ns), readonly = options.readonly, disable = options.disable;
if (!readonly && !disable) {
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
element.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, proxy(that._keydown, that)).on('focusout' + ns, proxy(that._blur, that)).on('focus' + ns, function () {
that._inputWrapper.addClass(FOCUSED);
});
icon.on(CLICK, proxy(that._click, that)).on(MOUSEDOWN, preventDefault);
} else {
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
element.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
}
},
readonly: function (readonly) {
this._editable({
readonly: readonly === undefined ? true : readonly,
disable: false
});
},
enable: function (enable) {
this._editable({
readonly: false,
disable: !(enable = enable === undefined ? true : enable)
});
},
destroy: function () {
var that = this;
Widget.fn.destroy.call(that);
that.dateView.destroy();
that.element.off(ns);
that._dateIcon.off(ns);
that._inputWrapper.off(ns);
if (that._form) {
that._form.off('reset', that._resetHandler);
}
},
open: function () {
this.dateView.open();
},
close: function () {
this.dateView.close();
},
min: function (value) {
return this._option(MIN, value);
},
max: function (value) {
return this._option(MAX, value);
},
value: function (value) {
var that = this;
if (value === undefined) {
return that._value;
}
that._old = that._update(value);
if (that._old === null) {
that.element.val('');
}
that._oldText = that.element.val();
},
_toggleHover: function (e) {
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
},
_blur: function () {
var that = this, value = that.element.val();
that.close();
if (value !== that._oldText) {
that._change(value);
}
that._inputWrapper.removeClass(FOCUSED);
},
_click: function () {
var that = this, element = that.element;
that.dateView.toggle();
if (!kendo.support.touch && element[0] !== activeElement()) {
element.focus();
}
},
_change: function (value) {
var that = this, oldValue = that.element.val(), dateChanged;
value = that._update(value);
dateChanged = +that._old != +value;
var valueUpdated = dateChanged && !that._typing;
var textFormatted = oldValue !== that.element.val();
if (valueUpdated || textFormatted) {
that.element.trigger(CHANGE);
}
if (dateChanged) {
that._old = value;
that._oldText = that.element.val();
that.trigger(CHANGE);
}
that._typing = false;
},
_keydown: function (e) {
var that = this, dateView = that.dateView, value = that.element.val(), handled = false;
if (!dateView.popup.visible() && e.keyCode == keys.ENTER && value !== that._oldText) {
that._change(value);
} else {
handled = dateView.move(e);
that._updateARIA(dateView._current);
if (!handled) {
that._typing = true;
}
}
},
_icon: function () {
var that = this, element = that.element, icon;
icon = element.next('span.k-select');
if (!icon[0]) {
icon = $('select').insertAfter(element);
}
that._dateIcon = icon.attr({
'role': 'button',
'aria-controls': that.dateView._dateViewID
});
},
_option: function (option, value) {
var that = this, options = that.options;
if (value === undefined) {
return options[option];
}
value = parse(value, options.parseFormats, options.culture);
if (!value) {
return;
}
options[option] = new DATE(+value);
that.dateView[option](value);
},
_update: function (value) {
var that = this, options = that.options, min = options.min, max = options.max, current = that._value, date = parse(value, options.parseFormats, options.culture), isSameType = date === null && current === null || date instanceof Date && current instanceof Date, formattedValue;
if (options.disableDates(date)) {
date = null;
if (!that._old) {
value = null;
}
}
if (+date === +current && isSameType) {
formattedValue = kendo.toString(date, options.format, options.culture);
if (formattedValue !== value) {
that.element.val(date === null ? value : formattedValue);
}
return date;
}
if (date !== null && isEqualDatePart(date, min)) {
date = restrictValue(date, min, max);
} else if (!isInRange(date, min, max)) {
date = null;
}
that._value = date;
that.dateView.value(date);
that.element.val(date ? kendo.toString(date, options.format, options.culture) : value);
that._updateARIA(date);
return date;
},
_wrapper: function () {
var that = this, element = that.element, wrapper;
wrapper = element.parents('.k-datepicker');
if (!wrapper[0]) {
wrapper = element.wrap(SPAN).parent().addClass('k-picker-wrap k-state-default');
wrapper = wrapper.wrap(SPAN).parent();
}
wrapper[0].style.cssText = element[0].style.cssText;
element.css({
width: '100%',
height: element[0].style.height
});
that.wrapper = wrapper.addClass('k-widget k-datepicker k-header').addClass(element[0].className);
that._inputWrapper = $(wrapper[0].firstChild);
},
_reset: function () {
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
if (form[0]) {
that._resetHandler = function () {
that.value(element[0].defaultValue);
that.max(that._initialOptions.max);
that.min(that._initialOptions.min);
};
that._form = form.on('reset', that._resetHandler);
}
},
_template: function () {
this._ariaTemplate = template(this.options.ARIATemplate);
},
_updateARIA: function (date) {
var cell;
var that = this;
var calendar = that.dateView.calendar;
that.element.removeAttr('aria-activedescendant');
if (calendar) {
cell = calendar._cell;
cell.attr('aria-label', that._ariaTemplate({ current: date || calendar.current() }));
that.element.attr('aria-activedescendant', cell.attr('id'));
}
}
});
ui.plugin(DatePicker);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.numerictextbox', [
'kendo.core',
'kendo.userevents'
], f);
}(function () {
var __meta__ = {
id: 'numerictextbox',
name: 'NumericTextBox',
category: 'web',
description: 'The NumericTextBox widget can format and display numeric, percentage or currency textbox.',
depends: [
'core',
'userevents'
]
};
(function ($, undefined) {
var kendo = window.kendo, caret = kendo.caret, keys = kendo.keys, ui = kendo.ui, Widget = ui.Widget, activeElement = kendo._activeElement, extractFormat = kendo._extractFormat, parse = kendo.parseFloat, placeholderSupported = kendo.support.placeholder, getCulture = kendo.getCulture, round = kendo._round, CHANGE = 'change', DISABLED = 'disabled', READONLY = 'readonly', INPUT = 'k-input', SPIN = 'spin', ns = '.kendoNumericTextBox', TOUCHEND = 'touchend', MOUSELEAVE = 'mouseleave' + ns, HOVEREVENTS = 'mouseenter' + ns + ' ' + MOUSELEAVE, DEFAULT = 'k-state-default', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', FOCUS = 'focus', POINT = '.', SELECTED = 'k-state-selected', STATEDISABLED = 'k-state-disabled', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', INTEGER_REGEXP = /^(-)?(\d*)$/, NULL = null, proxy = $.proxy, extend = $.extend;
var NumericTextBox = Widget.extend({
init: function (element, options) {
var that = this, isStep = options && options.step !== undefined, min, max, step, value, disabled;
Widget.fn.init.call(that, element, options);
options = that.options;
element = that.element.on('focusout' + ns, proxy(that._focusout, that)).attr('role', 'spinbutton');
options.placeholder = options.placeholder || element.attr('placeholder');
that._initialOptions = extend({}, options);
that._reset();
that._wrapper();
that._arrows();
that._input();
if (!kendo.support.mobileOS) {
that._text.on(FOCUS + ns, proxy(that._click, that));
} else {
that._text.on(TOUCHEND + ns + ' ' + FOCUS + ns, function () {
that._toggleText(false);
element.focus();
});
}
min = that.min(element.attr('min'));
max = that.max(element.attr('max'));
step = that._parse(element.attr('step'));
if (options.min === NULL && min !== NULL) {
options.min = min;
}
if (options.max === NULL && max !== NULL) {
options.max = max;
}
if (!isStep && step !== NULL) {
options.step = step;
}
element.attr('aria-valuemin', options.min).attr('aria-valuemax', options.max);
options.format = extractFormat(options.format);
value = options.value;
that.value(value !== NULL ? value : element.val());
disabled = element.is('[disabled]') || $(that.element).parents('fieldset').is(':disabled');
if (disabled) {
that.enable(false);
} else {
that.readonly(element.is('[readonly]'));
}
kendo.notify(that);
},
options: {
name: 'NumericTextBox',
decimals: NULL,
min: NULL,
max: NULL,
value: NULL,
step: 1,
culture: '',
format: 'n',
spinners: true,
placeholder: '',
upArrowText: 'Increase value',
downArrowText: 'Decrease value'
},
events: [
CHANGE,
SPIN
],
_editable: function (options) {
var that = this, element = that.element, disable = options.disable, readonly = options.readonly, text = that._text.add(element), wrapper = that._inputWrapper.off(HOVEREVENTS);
that._toggleText(true);
that._upArrowEventHandler.unbind('press');
that._downArrowEventHandler.unbind('press');
element.off('keydown' + ns).off('keypress' + ns).off('paste' + ns);
if (!readonly && !disable) {
wrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
text.removeAttr(DISABLED).removeAttr(READONLY).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false);
that._upArrowEventHandler.bind('press', function (e) {
e.preventDefault();
that._spin(1);
that._upArrow.addClass(SELECTED);
});
that._downArrowEventHandler.bind('press', function (e) {
e.preventDefault();
that._spin(-1);
that._downArrow.addClass(SELECTED);
});
that.element.on('keydown' + ns, proxy(that._keydown, that)).on('keypress' + ns, proxy(that._keypress, that)).on('paste' + ns, proxy(that._paste, that));
} else {
wrapper.addClass(disable ? STATEDISABLED : DEFAULT).removeClass(disable ? DEFAULT : STATEDISABLED);
text.attr(DISABLED, disable).attr(READONLY, readonly).attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
}
},
readonly: function (readonly) {
this._editable({
readonly: readonly === undefined ? true : readonly,
disable: false
});
},
enable: function (enable) {
this._editable({
readonly: false,
disable: !(enable = enable === undefined ? true : enable)
});
},
destroy: function () {
var that = this;
that.element.add(that._text).add(that._upArrow).add(that._downArrow).add(that._inputWrapper).off(ns);
that._upArrowEventHandler.destroy();
that._downArrowEventHandler.destroy();
if (that._form) {
that._form.off('reset', that._resetHandler);
}
Widget.fn.destroy.call(that);
},
min: function (value) {
return this._option('min', value);
},
max: function (value) {
return this._option('max', value);
},
step: function (value) {
return this._option('step', value);
},
value: function (value) {
var that = this, adjusted;
if (value === undefined) {
return that._value;
}
value = that._parse(value);
adjusted = that._adjust(value);
if (value !== adjusted) {
return;
}
that._update(value);
that._old = that._value;
},
focus: function () {
this._focusin();
},
_adjust: function (value) {
var that = this, options = that.options, min = options.min, max = options.max;
if (value === NULL) {
return value;
}
if (min !== NULL && value < min) {
value = min;
} else if (max !== NULL && value > max) {
value = max;
}
return value;
},
_arrows: function () {
var that = this, arrows, _release = function () {
clearTimeout(that._spinning);
arrows.removeClass(SELECTED);
}, options = that.options, spinners = options.spinners, element = that.element;
arrows = element.siblings('.k-icon');
if (!arrows[0]) {
arrows = $(buttonHtml('n', options.upArrowText) + buttonHtml('s', options.downArrowText)).insertAfter(element);
arrows.wrapAll('');
}
if (!spinners) {
arrows.parent().toggle(spinners);
that._inputWrapper.addClass('k-expand-padding');
}
that._upArrow = arrows.eq(0);
that._upArrowEventHandler = new kendo.UserEvents(that._upArrow, { release: _release });
that._downArrow = arrows.eq(1);
that._downArrowEventHandler = new kendo.UserEvents(that._downArrow, { release: _release });
},
_blur: function () {
var that = this;
that._toggleText(true);
that._change(that.element.val());
},
_click: function (e) {
var that = this;
clearTimeout(that._focusing);
that._focusing = setTimeout(function () {
var input = e.target, idx = caret(input)[0], value = input.value.substring(0, idx), format = that._format(that.options.format), group = format[','], result, groupRegExp, extractRegExp, caretPosition = 0;
if (group) {
groupRegExp = new RegExp('\\' + group, 'g');
extractRegExp = new RegExp('([\\d\\' + group + ']+)(\\' + format[POINT] + ')?(\\d+)?');
}
if (extractRegExp) {
result = extractRegExp.exec(value);
}
if (result) {
caretPosition = result[0].replace(groupRegExp, '').length;
if (value.indexOf('(') != -1 && that._value < 0) {
caretPosition++;
}
}
that._focusin();
caret(that.element[0], caretPosition);
});
},
_change: function (value) {
var that = this;
that._update(value);
value = that._value;
if (that._old != value) {
that._old = value;
if (!that._typing) {
that.element.trigger(CHANGE);
}
that.trigger(CHANGE);
}
that._typing = false;
},
_culture: function (culture) {
return culture || getCulture(this.options.culture);
},
_focusin: function () {
var that = this;
that._inputWrapper.addClass(FOCUSED);
that._toggleText(false);
that.element[0].focus();
},
_focusout: function () {
var that = this;
clearTimeout(that._focusing);
that._inputWrapper.removeClass(FOCUSED).removeClass(HOVER);
that._blur();
},
_format: function (format, culture) {
var numberFormat = this._culture(culture).numberFormat;
format = format.toLowerCase();
if (format.indexOf('c') > -1) {
numberFormat = numberFormat.currency;
} else if (format.indexOf('p') > -1) {
numberFormat = numberFormat.percent;
}
return numberFormat;
},
_input: function () {
var that = this, CLASSNAME = 'k-formatted-value', element = that.element.addClass(INPUT).show()[0], accessKey = element.accessKey, wrapper = that.wrapper, text;
text = wrapper.find(POINT + CLASSNAME);
if (!text[0]) {
text = $('').insertBefore(element).addClass(CLASSNAME);
}
try {
element.setAttribute('type', 'text');
} catch (e) {
element.type = 'text';
}
text[0].tabIndex = element.tabIndex;
text[0].style.cssText = element.style.cssText;
text[0].title = element.title;
text.prop('placeholder', that.options.placeholder);
if (accessKey) {
text.attr('accesskey', accessKey);
element.accessKey = '';
}
that._text = text.addClass(element.className);
},
_keydown: function (e) {
var that = this, key = e.keyCode;
that._key = key;
if (key == keys.DOWN) {
that._step(-1);
} else if (key == keys.UP) {
that._step(1);
} else if (key == keys.ENTER) {
that._change(that.element.val());
} else {
that._typing = true;
}
},
_keypress: function (e) {
if (e.which === 0 || e.metaKey || e.ctrlKey || e.keyCode === keys.BACKSPACE || e.keyCode === keys.ENTER) {
return;
}
var that = this;
var min = that.options.min;
var element = that.element;
var selection = caret(element);
var selectionStart = selection[0];
var selectionEnd = selection[1];
var character = String.fromCharCode(e.which);
var numberFormat = that._format(that.options.format);
var isNumPadDecimal = that._key === keys.NUMPAD_DOT;
var value = element.val();
var isValid;
if (isNumPadDecimal) {
character = numberFormat[POINT];
}
value = value.substring(0, selectionStart) + character + value.substring(selectionEnd);
isValid = that._numericRegex(numberFormat).test(value);
if (isValid && isNumPadDecimal) {
element.val(value);
caret(element, selectionStart + character.length);
e.preventDefault();
} else if (min !== null && min >= 0 && value.charAt(0) === '-' || !isValid) {
e.preventDefault();
}
that._key = 0;
},
_numericRegex: function (numberFormat) {
var that = this;
var separator = numberFormat[POINT];
var precision = that.options.decimals;
if (separator === POINT) {
separator = '\\' + separator;
}
if (precision === NULL) {
precision = numberFormat.decimals;
}
if (precision === 0) {
return INTEGER_REGEXP;
}
if (that._separator !== separator) {
that._separator = separator;
that._floatRegExp = new RegExp('^(-)?(((\\d+(' + separator + '\\d*)?)|(' + separator + '\\d*)))?$');
}
return that._floatRegExp;
},
_paste: function (e) {
var that = this, element = e.target, value = element.value;
setTimeout(function () {
if (that._parse(element.value) === NULL) {
that._update(value);
}
});
},
_option: function (option, value) {
var that = this, options = that.options;
if (value === undefined) {
return options[option];
}
value = that._parse(value);
if (!value && option === 'step') {
return;
}
options[option] = value;
that.element.attr('aria-value' + option, value).attr(option, value);
},
_spin: function (step, timeout) {
var that = this;
timeout = timeout || 500;
clearTimeout(that._spinning);
that._spinning = setTimeout(function () {
that._spin(step, 50);
}, timeout);
that._step(step);
},
_step: function (step) {
var that = this, element = that.element, value = that._parse(element.val()) || 0;
if (activeElement() != element[0]) {
that._focusin();
}
value += that.options.step * step;
that._update(that._adjust(value));
that._typing = false;
that.trigger(SPIN);
},
_toggleHover: function (e) {
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
},
_toggleText: function (toggle) {
var that = this;
that._text.toggle(toggle);
that.element.toggle(!toggle);
},
_parse: function (value, culture) {
return parse(value, this._culture(culture), this.options.format);
},
_update: function (value) {
var that = this, options = that.options, format = options.format, decimals = options.decimals, culture = that._culture(), numberFormat = that._format(format, culture), isNotNull;
if (decimals === NULL) {
decimals = numberFormat.decimals;
}
value = that._parse(value, culture);
isNotNull = value !== NULL;
if (isNotNull) {
value = parseFloat(round(value, decimals));
}
that._value = value = that._adjust(value);
that._placeholder(kendo.toString(value, format, culture));
if (isNotNull) {
value = value.toString();
if (value.indexOf('e') !== -1) {
value = round(+value, decimals);
}
value = value.replace(POINT, numberFormat[POINT]);
} else {
value = '';
}
that.element.val(value).attr('aria-valuenow', value);
},
_placeholder: function (value) {
this._text.val(value);
if (!placeholderSupported && !value) {
this._text.val(this.options.placeholder);
}
},
_wrapper: function () {
var that = this, element = that.element, DOMElement = element[0], wrapper;
wrapper = element.parents('.k-numerictextbox');
if (!wrapper.is('span.k-numerictextbox')) {
wrapper = element.hide().wrap('').parent();
wrapper = wrapper.wrap('').parent();
}
wrapper[0].style.cssText = DOMElement.style.cssText;
DOMElement.style.width = '';
that.wrapper = wrapper.addClass('k-widget k-numerictextbox').addClass(DOMElement.className).css('display', '');
that._inputWrapper = $(wrapper[0].firstChild);
},
_reset: function () {
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
if (form[0]) {
that._resetHandler = function () {
setTimeout(function () {
that.value(element[0].value);
that.max(that._initialOptions.max);
that.min(that._initialOptions.min);
});
};
that._form = form.on('reset', that._resetHandler);
}
}
});
function buttonHtml(className, text) {
return '' + text + '';
}
ui.plugin(NumericTextBox);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.validator', ['kendo.core'], f);
}(function () {
var __meta__ = {
id: 'validator',
name: 'Validator',
category: 'web',
description: 'The Validator offers an easy way to do a client-side form validation.',
depends: ['core']
};
(function ($, undefined) {
var kendo = window.kendo, Widget = kendo.ui.Widget, NS = '.kendoValidator', INVALIDMSG = 'k-invalid-msg', invalidMsgRegExp = new RegExp(INVALIDMSG, 'i'), INVALIDINPUT = 'k-invalid', VALIDINPUT = 'k-valid', emailRegExp = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i, urlRegExp = /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i, INPUTSELECTOR = ':input:not(:button,[type=submit],[type=reset],[disabled],[readonly])', CHECKBOXSELECTOR = ':checkbox:not([disabled],[readonly])', NUMBERINPUTSELECTOR = '[type=number],[type=range]', BLUR = 'blur', NAME = 'name', FORM = 'form', NOVALIDATE = 'novalidate', proxy = $.proxy, patternMatcher = function (value, pattern) {
if (typeof pattern === 'string') {
pattern = new RegExp('^(?:' + pattern + ')$');
}
return pattern.test(value);
}, matcher = function (input, selector, pattern) {
var value = input.val();
if (input.filter(selector).length && value !== '') {
return patternMatcher(value, pattern);
}
return true;
}, hasAttribute = function (input, name) {
if (input.length) {
return input[0].attributes[name] != null;
}
return false;
};
if (!kendo.ui.validator) {
kendo.ui.validator = {
rules: {},
messages: {}
};
}
function resolveRules(element) {
var resolvers = kendo.ui.validator.ruleResolvers || {}, rules = {}, name;
for (name in resolvers) {
$.extend(true, rules, resolvers[name].resolve(element));
}
return rules;
}
function decode(value) {
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, '\'').replace(/</g, '<').replace(/>/g, '>');
}
function numberOfDecimalDigits(value) {
value = (value + '').split('.');
if (value.length > 1) {
return value[1].length;
}
return 0;
}
function parseHtml(text) {
if ($.parseHTML) {
return $($.parseHTML(text));
}
return $(text);
}
function searchForMessageContainer(elements, fieldName) {
var containers = $(), element, attr;
for (var idx = 0, length = elements.length; idx < length; idx++) {
element = elements[idx];
if (invalidMsgRegExp.test(element.className)) {
attr = element.getAttribute(kendo.attr('for'));
if (attr === fieldName) {
containers = containers.add(element);
}
}
}
return containers;
}
var Validator = Widget.extend({
init: function (element, options) {
var that = this, resolved = resolveRules(element), validateAttributeSelector = '[' + kendo.attr('validate') + '!=false]';
options = options || {};
options.rules = $.extend({}, kendo.ui.validator.rules, resolved.rules, options.rules);
options.messages = $.extend({}, kendo.ui.validator.messages, resolved.messages, options.messages);
Widget.fn.init.call(that, element, options);
that._errorTemplate = kendo.template(that.options.errorTemplate);
if (that.element.is(FORM)) {
that.element.attr(NOVALIDATE, NOVALIDATE);
}
that._inputSelector = INPUTSELECTOR + validateAttributeSelector;
that._checkboxSelector = CHECKBOXSELECTOR + validateAttributeSelector;
that._errors = {};
that._attachEvents();
that._isValidated = false;
},
events: [
'validate',
'change'
],
options: {
name: 'Validator',
errorTemplate: '' + ' #=message#',
messages: {
required: '{0} is required',
pattern: '{0} is not valid',
min: '{0} should be greater than or equal to {1}',
max: '{0} should be smaller than or equal to {1}',
step: '{0} is not valid',
email: '{0} is not valid email',
url: '{0} is not valid URL',
date: '{0} is not valid date',
dateCompare: 'End date should be greater than or equal to the start date'
},
rules: {
required: function (input) {
var checkbox = input.filter('[type=checkbox]').length && !input.is(':checked'), value = input.val();
return !(hasAttribute(input, 'required') && (value === '' || !value || checkbox));
},
pattern: function (input) {
if (input.filter('[type=text],[type=email],[type=url],[type=tel],[type=search],[type=password]').filter('[pattern]').length && input.val() !== '') {
return patternMatcher(input.val(), input.attr('pattern'));
}
return true;
},
min: function (input) {
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[min]').length && input.val() !== '') {
var min = parseFloat(input.attr('min')) || 0, val = kendo.parseFloat(input.val());
return min <= val;
}
return true;
},
max: function (input) {
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[max]').length && input.val() !== '') {
var max = parseFloat(input.attr('max')) || 0, val = kendo.parseFloat(input.val());
return max >= val;
}
return true;
},
step: function (input) {
if (input.filter(NUMBERINPUTSELECTOR + ',[' + kendo.attr('type') + '=number]').filter('[step]').length && input.val() !== '') {
var min = parseFloat(input.attr('min')) || 0, step = parseFloat(input.attr('step')) || 1, val = parseFloat(input.val()), decimals = numberOfDecimalDigits(step), raise;
if (decimals) {
raise = Math.pow(10, decimals);
return Math.floor((val - min) * raise) % (step * raise) / Math.pow(100, decimals) === 0;
}
return (val - min) % step === 0;
}
return true;
},
email: function (input) {
return matcher(input, '[type=email],[' + kendo.attr('type') + '=email]', emailRegExp);
},
url: function (input) {
return matcher(input, '[type=url],[' + kendo.attr('type') + '=url]', urlRegExp);
},
date: function (input) {
if (input.filter('[type^=date],[' + kendo.attr('type') + '=date]').length && input.val() !== '') {
return kendo.parseDate(input.val(), input.attr(kendo.attr('format'))) !== null;
}
return true;
}
},
validateOnBlur: true
},
destroy: function () {
Widget.fn.destroy.call(this);
this.element.off(NS);
},
value: function () {
if (!this._isValidated) {
return false;
}
return this.errors().length === 0;
},
_submit: function (e) {
if (!this.validate()) {
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
return false;
}
return true;
},
_checkElement: function (element) {
var state = this.value();
this.validateInput(element);
if (this.value() !== state) {
this.trigger('change');
}
},
_attachEvents: function () {
var that = this;
if (that.element.is(FORM)) {
that.element.on('submit' + NS, proxy(that._submit, that));
}
if (that.options.validateOnBlur) {
if (!that.element.is(INPUTSELECTOR)) {
that.element.on(BLUR + NS, that._inputSelector, function () {
that._checkElement($(this));
});
that.element.on('click' + NS, that._checkboxSelector, function () {
that._checkElement($(this));
});
} else {
that.element.on(BLUR + NS, function () {
that._checkElement(that.element);
});
if (that.element.is(CHECKBOXSELECTOR)) {
that.element.on('click' + NS, function () {
that._checkElement(that.element);
});
}
}
}
},
validate: function () {
var inputs;
var idx;
var result = false;
var length;
var isValid = this.value();
this._errors = {};
if (!this.element.is(INPUTSELECTOR)) {
var invalid = false;
inputs = this.element.find(this._inputSelector);
for (idx = 0, length = inputs.length; idx < length; idx++) {
if (!this.validateInput(inputs.eq(idx))) {
invalid = true;
}
}
result = !invalid;
} else {
result = this.validateInput(this.element);
}
this.trigger('validate', { valid: result });
if (isValid !== result) {
this.trigger('change');
}
return result;
},
validateInput: function (input) {
input = $(input);
this._isValidated = true;
var that = this, template = that._errorTemplate, result = that._checkValidity(input), valid = result.valid, className = '.' + INVALIDMSG, fieldName = input.attr(NAME) || '', lbl = that._findMessageContainer(fieldName).add(input.next(className).filter(function () {
var element = $(this);
if (element.filter('[' + kendo.attr('for') + ']').length) {
return element.attr(kendo.attr('for')) === fieldName;
}
return true;
})).hide(), messageText;
input.removeAttr('aria-invalid');
if (!valid) {
messageText = that._extractMessage(input, result.key);
that._errors[fieldName] = messageText;
var messageLabel = parseHtml(template({ message: decode(messageText) }));
var lblId = lbl.attr('id');
that._decorateMessageContainer(messageLabel, fieldName);
if (lblId) {
messageLabel.attr('id', lblId);
}
if (!lbl.replaceWith(messageLabel).length) {
messageLabel.insertAfter(input);
}
messageLabel.show();
input.attr('aria-invalid', true);
} else {
delete that._errors[fieldName];
}
input.toggleClass(INVALIDINPUT, !valid);
input.toggleClass(VALIDINPUT, valid);
return valid;
},
hideMessages: function () {
var that = this, className = '.' + INVALIDMSG, element = that.element;
if (!element.is(INPUTSELECTOR)) {
element.find(className).hide();
} else {
element.next(className).hide();
}
},
_findMessageContainer: function (fieldName) {
var locators = kendo.ui.validator.messageLocators, name, containers = $();
for (var idx = 0, length = this.element.length; idx < length; idx++) {
containers = containers.add(searchForMessageContainer(this.element[idx].getElementsByTagName('*'), fieldName));
}
for (name in locators) {
containers = containers.add(locators[name].locate(this.element, fieldName));
}
return containers;
},
_decorateMessageContainer: function (container, fieldName) {
var locators = kendo.ui.validator.messageLocators, name;
container.addClass(INVALIDMSG).attr(kendo.attr('for'), fieldName || '');
for (name in locators) {
locators[name].decorate(container, fieldName);
}
container.attr('role', 'alert');
},
_extractMessage: function (input, ruleKey) {
var that = this, customMessage = that.options.messages[ruleKey], fieldName = input.attr(NAME);
customMessage = kendo.isFunction(customMessage) ? customMessage(input) : customMessage;
return kendo.format(input.attr(kendo.attr(ruleKey + '-msg')) || input.attr('validationMessage') || input.attr('title') || customMessage || '', fieldName, input.attr(ruleKey) || input.attr(kendo.attr(ruleKey)));
},
_checkValidity: function (input) {
var rules = this.options.rules, rule;
for (rule in rules) {
if (!rules[rule].call(this, input)) {
return {
valid: false,
key: rule
};
}
}
return { valid: true };
},
errors: function () {
var results = [], errors = this._errors, error;
for (error in errors) {
results.push(errors[error]);
}
return results;
}
});
kendo.ui.plugin(Validator);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.editable', [
'kendo.datepicker',
'kendo.numerictextbox',
'kendo.validator',
'kendo.binder'
], f);
}(function () {
var __meta__ = {
id: 'editable',
name: 'Editable',
category: 'framework',
depends: [
'datepicker',
'numerictextbox',
'validator',
'binder'
],
hidden: true
};
(function ($, undefined) {
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, extend = $.extend, oldIE = kendo.support.browser.msie && kendo.support.browser.version < 9, isFunction = kendo.isFunction, isPlainObject = $.isPlainObject, inArray = $.inArray, nameSpecialCharRegExp = /("|\%|'|\[|\]|\$|\.|\,|\:|\;|\+|\*|\&|\!|\#|\(|\)|<|>|\=|\?|\@|\^|\{|\}|\~|\/|\||`)/g, ERRORTEMPLATE = '', CHANGE = 'change';
var specialRules = [
'url',
'email',
'number',
'date',
'boolean'
];
function fieldType(field) {
field = field != null ? field : '';
return field.type || $.type(field) || 'string';
}
function convertToValueBinding(container) {
container.find(':input:not(:button, [' + kendo.attr('role') + '=upload], [' + kendo.attr('skip') + '], [type=file]), select').each(function () {
var bindAttr = kendo.attr('bind'), binding = this.getAttribute(bindAttr) || '', bindingName = this.type === 'checkbox' || this.type === 'radio' ? 'checked:' : 'value:', fieldName = this.name;
if (binding.indexOf(bindingName) === -1 && fieldName) {
binding += (binding.length ? ',' : '') + bindingName + fieldName;
$(this).attr(bindAttr, binding);
}
});
}
function createAttributes(options) {
var field = (options.model.fields || options.model)[options.field], type = fieldType(field), validation = field ? field.validation : {}, ruleName, DATATYPE = kendo.attr('type'), BINDING = kendo.attr('bind'), rule, attr = { name: options.field };
for (ruleName in validation) {
rule = validation[ruleName];
if (inArray(ruleName, specialRules) >= 0) {
attr[DATATYPE] = ruleName;
} else if (!isFunction(rule)) {
attr[ruleName] = isPlainObject(rule) ? rule.value || ruleName : rule;
}
attr[kendo.attr(ruleName + '-msg')] = rule.message;
}
if (inArray(type, specialRules) >= 0) {
attr[DATATYPE] = type;
}
attr[BINDING] = (type === 'boolean' ? 'checked:' : 'value:') + options.field;
return attr;
}
function convertItems(items) {
var idx, length, item, value, text, result;
if (items && items.length) {
result = [];
for (idx = 0, length = items.length; idx < length; idx++) {
item = items[idx];
text = item.text || item.value || item;
value = item.value == null ? item.text || item : item.value;
result[idx] = {
text: text,
value: value
};
}
}
return result;
}
var editors = {
'number': function (container, options) {
var attr = createAttributes(options);
$('').attr(attr).appendTo(container).kendoNumericTextBox({ format: options.format });
$('').hide().appendTo(container);
},
'date': function (container, options) {
var attr = createAttributes(options), format = options.format;
if (format) {
format = kendo._extractFormat(format);
}
attr[kendo.attr('format')] = format;
$('').attr(attr).appendTo(container).kendoDatePicker({ format: options.format });
$('').hide().appendTo(container);
},
'string': function (container, options) {
var attr = createAttributes(options);
$('').attr(attr).appendTo(container);
},
'boolean': function (container, options) {
var attr = createAttributes(options);
$('').attr(attr).appendTo(container);
},
'values': function (container, options) {
var attr = createAttributes(options);
var items = kendo.stringify(convertItems(options.values));
$('').attr(attr).appendTo(container);
$('').hide().appendTo(container);
}
};
function addValidationRules(modelField, rules) {
var validation = modelField ? modelField.validation || {} : {}, rule, descriptor;
for (rule in validation) {
descriptor = validation[rule];
if (isPlainObject(descriptor) && descriptor.value) {
descriptor = descriptor.value;
}
if (isFunction(descriptor)) {
rules[rule] = descriptor;
}
}
}
var Editable = Widget.extend({
init: function (element, options) {
var that = this;
if (options.target) {
options.$angular = options.target.options.$angular;
}
Widget.fn.init.call(that, element, options);
that._validateProxy = $.proxy(that._validate, that);
that.refresh();
},
events: [CHANGE],
options: {
name: 'Editable',
editors: editors,
clearContainer: true,
errorTemplate: ERRORTEMPLATE
},
editor: function (field, modelField) {
var that = this, editors = that.options.editors, isObject = isPlainObject(field), fieldName = isObject ? field.field : field, model = that.options.model || {}, isValuesEditor = isObject && field.values, type = isValuesEditor ? 'values' : fieldType(modelField), isCustomEditor = isObject && field.editor, editor = isCustomEditor ? field.editor : editors[type], container = that.element.find('[' + kendo.attr('container-for') + '=' + fieldName.replace(nameSpecialCharRegExp, '\\$1') + ']');
editor = editor ? editor : editors.string;
if (isCustomEditor && typeof field.editor === 'string') {
editor = function (container) {
container.append(field.editor);
};
}
container = container.length ? container : that.element;
editor(container, extend(true, {}, isObject ? field : { field: fieldName }, { model: model }));
},
_validate: function (e) {
var that = this, input, value = e.value, preventChangeTrigger = that._validationEventInProgress, values = {}, bindAttribute = kendo.attr('bind'), fieldName = e.field.replace(nameSpecialCharRegExp, '\\$1'), bindingRegex = new RegExp('(value|checked)\\s*:\\s*' + fieldName + '\\s*(,|$)');
values[e.field] = e.value;
input = $(':input[' + bindAttribute + '*="' + fieldName + '"]', that.element).filter('[' + kendo.attr('validate') + '!=\'false\']').filter(function () {
return bindingRegex.test($(this).attr(bindAttribute));
});
if (input.length > 1) {
input = input.filter(function () {
var element = $(this);
return !element.is(':radio') || element.val() == value;
});
}
try {
that._validationEventInProgress = true;
if (!that.validatable.validateInput(input) || !preventChangeTrigger && that.trigger(CHANGE, { values: values })) {
e.preventDefault();
}
} finally {
that._validationEventInProgress = false;
}
},
end: function () {
return this.validatable.validate();
},
destroy: function () {
var that = this;
that.angular('cleanup', function () {
return { elements: that.element };
});
Widget.fn.destroy.call(that);
that.options.model.unbind('set', that._validateProxy);
kendo.unbind(that.element);
if (that.validatable) {
that.validatable.destroy();
}
kendo.destroy(that.element);
that.element.removeData('kendoValidator');
if (that.element.is('[' + kendo.attr('role') + '=editable]')) {
that.element.removeAttr(kendo.attr('role'));
}
},
refresh: function () {
var that = this, idx, length, fields = that.options.fields || [], container = that.options.clearContainer ? that.element.empty() : that.element, model = that.options.model || {}, rules = {}, field, isObject, fieldName, modelField, modelFields;
if (!$.isArray(fields)) {
fields = [fields];
}
for (idx = 0, length = fields.length; idx < length; idx++) {
field = fields[idx];
isObject = isPlainObject(field);
fieldName = isObject ? field.field : field;
modelField = (model.fields || model)[fieldName];
addValidationRules(modelField, rules);
that.editor(field, modelField);
}
if (that.options.target) {
that.angular('compile', function () {
return {
elements: container,
data: container.map(function () {
return { dataItem: model };
})
};
});
}
if (!length) {
modelFields = model.fields || model;
for (fieldName in modelFields) {
addValidationRules(modelFields[fieldName], rules);
}
}
convertToValueBinding(container);
if (that.validatable) {
that.validatable.destroy();
}
kendo.bind(container, that.options.model);
that.options.model.unbind('set', that._validateProxy);
that.options.model.bind('set', that._validateProxy);
that.validatable = new kendo.ui.Validator(container, {
validateOnBlur: false,
errorTemplate: that.options.errorTemplate || undefined,
rules: rules
});
var focusable = container.find(':kendoFocusable').eq(0).focus();
if (oldIE) {
focusable.focus();
}
}
});
ui.plugin(Editable);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.window', ['kendo.draganddrop'], f);
}(function () {
var __meta__ = {
id: 'window',
name: 'Window',
category: 'web',
description: 'The Window widget displays content in a modal or non-modal HTML window.',
depends: ['draganddrop']
};
(function ($, undefined) {
var kendo = window.kendo, Widget = kendo.ui.Widget, Draggable = kendo.ui.Draggable, isPlainObject = $.isPlainObject, activeElement = kendo._activeElement, proxy = $.proxy, extend = $.extend, each = $.each, template = kendo.template, BODY = 'body', templates, NS = '.kendoWindow', KWINDOW = '.k-window', KWINDOWTITLE = '.k-window-title', KWINDOWTITLEBAR = KWINDOWTITLE + 'bar', KWINDOWCONTENT = '.k-window-content', KWINDOWRESIZEHANDLES = '.k-resize-handle', KOVERLAY = '.k-overlay', KCONTENTFRAME = 'k-content-frame', LOADING = 'k-loading', KHOVERSTATE = 'k-state-hover', KFOCUSEDSTATE = 'k-state-focused', MAXIMIZEDSTATE = 'k-window-maximized', VISIBLE = ':visible', HIDDEN = 'hidden', CURSOR = 'cursor', OPEN = 'open', ACTIVATE = 'activate', DEACTIVATE = 'deactivate', CLOSE = 'close', REFRESH = 'refresh', MINIMIZE = 'minimize', MAXIMIZE = 'maximize', RESIZE = 'resize', RESIZEEND = 'resizeEnd', DRAGSTART = 'dragstart', DRAGEND = 'dragend', ERROR = 'error', OVERFLOW = 'overflow', ZINDEX = 'zIndex', MINIMIZE_MAXIMIZE = '.k-window-actions .k-i-minimize,.k-window-actions .k-i-maximize', KPIN = '.k-i-pin', KUNPIN = '.k-i-unpin', PIN_UNPIN = KPIN + ',' + KUNPIN, TITLEBAR_BUTTONS = '.k-window-titlebar .k-window-action', REFRESHICON = '.k-window-titlebar .k-i-refresh', isLocalUrl = kendo.isLocalUrl;
function defined(x) {
return typeof x != 'undefined';
}
function constrain(value, low, high) {
return Math.max(Math.min(parseInt(value, 10), high === Infinity ? high : parseInt(high, 10)), parseInt(low, 10));
}
function executableScript() {
return !this.type || this.type.toLowerCase().indexOf('script') >= 0;
}
var Window = Widget.extend({
init: function (element, options) {
var that = this, wrapper, offset = {}, visibility, display, position, isVisible = false, content, windowContent, suppressActions = options && options.actions && !options.actions.length, id;
Widget.fn.init.call(that, element, options);
options = that.options;
position = options.position;
element = that.element;
content = options.content;
if (suppressActions) {
options.actions = [];
}
that.appendTo = $(options.appendTo);
if (content && !isPlainObject(content)) {
content = options.content = { url: content };
}
element.find('script').filter(executableScript).remove();
if (!element.parent().is(that.appendTo) && (position.top === undefined || position.left === undefined)) {
if (element.is(VISIBLE)) {
offset = element.offset();
isVisible = true;
} else {
visibility = element.css('visibility');
display = element.css('display');
element.css({
visibility: HIDDEN,
display: ''
});
offset = element.offset();
element.css({
visibility: visibility,
display: display
});
}
if (position.top === undefined) {
position.top = offset.top;
}
if (position.left === undefined) {
position.left = offset.left;
}
}
if (!defined(options.visible) || options.visible === null) {
options.visible = element.is(VISIBLE);
}
wrapper = that.wrapper = element.closest(KWINDOW);
if (!element.is('.k-content') || !wrapper[0]) {
element.addClass('k-window-content k-content');
that._createWindow(element, options);
wrapper = that.wrapper = element.closest(KWINDOW);
that._dimensions();
}
that._position();
if (options.pinned) {
that.pin(true);
}
if (content) {
that.refresh(content);
}
if (options.visible) {
that.toFront();
}
windowContent = wrapper.children(KWINDOWCONTENT);
that._tabindex(windowContent);
if (options.visible && options.modal) {
that._overlay(wrapper.is(VISIBLE)).css({ opacity: 0.5 });
}
wrapper.on('mouseenter' + NS, TITLEBAR_BUTTONS, proxy(that._buttonEnter, that)).on('mouseleave' + NS, TITLEBAR_BUTTONS, proxy(that._buttonLeave, that)).on('click' + NS, '> ' + TITLEBAR_BUTTONS, proxy(that._windowActionHandler, that));
windowContent.on('keydown' + NS, proxy(that._keydown, that)).on('focus' + NS, proxy(that._focus, that)).on('blur' + NS, proxy(that._blur, that));
this._resizable();
this._draggable();
id = element.attr('id');
if (id) {
id = id + '_wnd_title';
wrapper.children(KWINDOWTITLEBAR).children(KWINDOWTITLE).attr('id', id);
windowContent.attr({
'role': 'dialog',
'aria-labelledby': id
});
}
wrapper.add(wrapper.children('.k-resize-handle,' + KWINDOWTITLEBAR)).on('mousedown' + NS, proxy(that.toFront, that));
that.touchScroller = kendo.touchScroller(element);
that._resizeHandler = proxy(that._onDocumentResize, that);
that._marker = kendo.guid().substring(0, 8);
$(window).on('resize' + NS + that._marker, that._resizeHandler);
if (options.visible) {
that.trigger(OPEN);
that.trigger(ACTIVATE);
}
kendo.notify(that);
},
_buttonEnter: function (e) {
$(e.currentTarget).addClass(KHOVERSTATE);
},
_buttonLeave: function (e) {
$(e.currentTarget).removeClass(KHOVERSTATE);
},
_focus: function () {
this.wrapper.addClass(KFOCUSEDSTATE);
},
_blur: function () {
this.wrapper.removeClass(KFOCUSEDSTATE);
},
_dimensions: function () {
var wrapper = this.wrapper;
var options = this.options;
var width = options.width;
var height = options.height;
var maxHeight = options.maxHeight;
var dimensions = [
'minWidth',
'minHeight',
'maxWidth',
'maxHeight'
];
this.title(options.title);
for (var i = 0; i < dimensions.length; i++) {
var value = options[dimensions[i]];
if (value && value != Infinity) {
wrapper.css(dimensions[i], value);
}
}
if (maxHeight && maxHeight != Infinity) {
this.element.css('maxHeight', maxHeight);
}
if (width) {
if (width.toString().indexOf('%') > 0) {
wrapper.width(width);
} else {
wrapper.width(constrain(width, options.minWidth, options.maxWidth));
}
}
if (height) {
if (height.toString().indexOf('%') > 0) {
wrapper.height(height);
} else {
wrapper.height(constrain(height, options.minHeight, options.maxHeight));
}
}
if (!options.visible) {
wrapper.hide();
}
},
_position: function () {
var wrapper = this.wrapper, position = this.options.position;
if (position.top === 0) {
position.top = position.top.toString();
}
if (position.left === 0) {
position.left = position.left.toString();
}
wrapper.css({
top: position.top || '',
left: position.left || ''
});
},
_animationOptions: function (id) {
var animation = this.options.animation;
var basicAnimation = {
open: { effects: {} },
close: {
hide: true,
effects: {}
}
};
return animation && animation[id] || basicAnimation[id];
},
_resize: function () {
kendo.resize(this.element.children());
},
_resizable: function () {
var resizable = this.options.resizable;
var wrapper = this.wrapper;
if (this.resizing) {
wrapper.off('dblclick' + NS).children(KWINDOWRESIZEHANDLES).remove();
this.resizing.destroy();
this.resizing = null;
}
if (resizable) {
wrapper.on('dblclick' + NS, KWINDOWTITLEBAR, proxy(function (e) {
if (!$(e.target).closest('.k-window-action').length) {
this.toggleMaximization();
}
}, this));
each('n e s w se sw ne nw'.split(' '), function (index, handler) {
wrapper.append(templates.resizeHandle(handler));
});
this.resizing = new WindowResizing(this);
}
wrapper = null;
},
_draggable: function () {
var draggable = this.options.draggable;
if (this.dragging) {
this.dragging.destroy();
this.dragging = null;
}
if (draggable) {
this.dragging = new WindowDragging(this, draggable.dragHandle || KWINDOWTITLEBAR);
}
},
_actions: function () {
var actions = this.options.actions;
var titlebar = this.wrapper.children(KWINDOWTITLEBAR);
var container = titlebar.find('.k-window-actions');
actions = $.map(actions, function (action) {
return { name: action };
});
container.html(kendo.render(templates.action, actions));
},
setOptions: function (options) {
Widget.fn.setOptions.call(this, options);
var scrollable = this.options.scrollable !== false;
this.restore();
this._dimensions();
this._position();
this._resizable();
this._draggable();
this._actions();
if (typeof options.modal !== 'undefined') {
var visible = this.options.visible !== false;
this._overlay(options.modal && visible);
}
this.element.css(OVERFLOW, scrollable ? '' : 'hidden');
},
events: [
OPEN,
ACTIVATE,
DEACTIVATE,
CLOSE,
MINIMIZE,
MAXIMIZE,
REFRESH,
RESIZE,
RESIZEEND,
DRAGSTART,
DRAGEND,
ERROR
],
options: {
name: 'Window',
animation: {
open: {
effects: {
zoom: { direction: 'in' },
fade: { direction: 'in' }
},
duration: 350
},
close: {
effects: {
zoom: {
direction: 'out',
properties: { scale: 0.7 }
},
fade: { direction: 'out' }
},
duration: 350,
hide: true
}
},
title: '',
actions: ['Close'],
autoFocus: true,
modal: false,
resizable: true,
draggable: true,
minWidth: 90,
minHeight: 50,
maxWidth: Infinity,
maxHeight: Infinity,
pinned: false,
scrollable: true,
position: {},
content: null,
visible: null,
height: null,
width: null,
appendTo: 'body'
},
_closable: function () {
return $.inArray('close', $.map(this.options.actions, function (x) {
return x.toLowerCase();
})) > -1;
},
_keydown: function (e) {
var that = this, options = that.options, keys = kendo.keys, keyCode = e.keyCode, wrapper = that.wrapper, offset, handled, distance = 10, isMaximized = that.options.isMaximized, newWidth, newHeight, w, h;
if (e.target != e.currentTarget || that._closing) {
return;
}
if (keyCode == keys.ESC && that._closable()) {
that._close(false);
}
if (options.draggable && !e.ctrlKey && !isMaximized) {
offset = kendo.getOffset(wrapper);
if (keyCode == keys.UP) {
handled = wrapper.css('top', offset.top - distance);
} else if (keyCode == keys.DOWN) {
handled = wrapper.css('top', offset.top + distance);
} else if (keyCode == keys.LEFT) {
handled = wrapper.css('left', offset.left - distance);
} else if (keyCode == keys.RIGHT) {
handled = wrapper.css('left', offset.left + distance);
}
}
if (options.resizable && e.ctrlKey && !isMaximized) {
if (keyCode == keys.UP) {
handled = true;
newHeight = wrapper.height() - distance;
} else if (keyCode == keys.DOWN) {
handled = true;
newHeight = wrapper.height() + distance;
}
if (keyCode == keys.LEFT) {
handled = true;
newWidth = wrapper.width() - distance;
} else if (keyCode == keys.RIGHT) {
handled = true;
newWidth = wrapper.width() + distance;
}
if (handled) {
w = constrain(newWidth, options.minWidth, options.maxWidth);
h = constrain(newHeight, options.minHeight, options.maxHeight);
if (!isNaN(w)) {
wrapper.width(w);
that.options.width = w + 'px';
}
if (!isNaN(h)) {
wrapper.height(h);
that.options.height = h + 'px';
}
that.resize();
}
}
if (handled) {
e.preventDefault();
}
},
_overlay: function (visible) {
var overlay = this.appendTo.children(KOVERLAY), wrapper = this.wrapper;
if (!overlay.length) {
overlay = $('');
}
overlay.insertBefore(wrapper[0]).toggle(visible).css(ZINDEX, parseInt(wrapper.css(ZINDEX), 10) - 1);
return overlay;
},
_actionForIcon: function (icon) {
var iconClass = /\bk-i-\w+\b/.exec(icon[0].className)[0];
return {
'k-i-close': '_close',
'k-i-maximize': 'maximize',
'k-i-minimize': 'minimize',
'k-i-restore': 'restore',
'k-i-refresh': 'refresh',
'k-i-pin': 'pin',
'k-i-unpin': 'unpin'
}[iconClass];
},
_windowActionHandler: function (e) {
if (this._closing) {
return;
}
var icon = $(e.target).closest('.k-window-action').find('.k-icon');
var action = this._actionForIcon(icon);
if (action) {
e.preventDefault();
this[action]();
return false;
}
},
_modals: function () {
var that = this;
var zStack = $(KWINDOW).filter(function () {
var dom = $(this);
var object = that._object(dom);
var options = object && object.options;
return options && options.modal && options.visible && options.appendTo === that.options.appendTo && dom.is(VISIBLE);
}).sort(function (a, b) {
return +$(a).css('zIndex') - +$(b).css('zIndex');
});
that = null;
return zStack;
},
_object: function (element) {
var content = element.children(KWINDOWCONTENT);
var widget = kendo.widgetInstance(content);
if (widget instanceof Window) {
return widget;
}
return undefined;
},
center: function () {
var that = this, position = that.options.position, wrapper = that.wrapper, documentWindow = $(window), scrollTop = 0, scrollLeft = 0, newTop, newLeft;
if (that.options.isMaximized) {
return that;
}
if (!that.options.pinned) {
scrollTop = documentWindow.scrollTop();
scrollLeft = documentWindow.scrollLeft();
}
newLeft = scrollLeft + Math.max(0, (documentWindow.width() - wrapper.width()) / 2);
newTop = scrollTop + Math.max(0, (documentWindow.height() - wrapper.height() - parseInt(wrapper.css('paddingTop'), 10)) / 2);
wrapper.css({
left: newLeft,
top: newTop
});
position.top = newTop;
position.left = newLeft;
return that;
},
title: function (text) {
var that = this, wrapper = that.wrapper, options = that.options, titleBar = wrapper.children(KWINDOWTITLEBAR), title = titleBar.children(KWINDOWTITLE), titleBarHeight;
if (!arguments.length) {
return title.html();
}
if (text === false) {
wrapper.addClass('k-window-titleless');
titleBar.remove();
} else {
if (!titleBar.length) {
wrapper.prepend(templates.titlebar(options));
that._actions();
titleBar = wrapper.children(KWINDOWTITLEBAR);
} else {
title.html(text);
}
titleBarHeight = titleBar.outerHeight();
wrapper.css('padding-top', titleBarHeight);
titleBar.css('margin-top', -titleBarHeight);
}
that.options.title = text;
return that;
},
content: function (html, data) {
var content = this.wrapper.children(KWINDOWCONTENT), scrollContainer = content.children('.km-scroll-container');
content = scrollContainer[0] ? scrollContainer : content;
if (!defined(html)) {
return content.html();
}
this.angular('cleanup', function () {
return { elements: content.children() };
});
kendo.destroy(this.element.children());
content.empty().html(html);
this.angular('compile', function () {
var a = [];
for (var i = content.length; --i >= 0;) {
a.push({ dataItem: data });
}
return {
elements: content.children(),
data: a
};
});
return this;
},
open: function () {
var that = this, wrapper = that.wrapper, options = that.options, showOptions = this._animationOptions('open'), contentElement = wrapper.children(KWINDOWCONTENT), overlay, doc = $(document);
if (!that.trigger(OPEN)) {
if (that._closing) {
wrapper.kendoStop(true, true);
}
that._closing = false;
that.toFront();
if (options.autoFocus) {
that.element.focus();
}
options.visible = true;
if (options.modal) {
overlay = that._overlay(false);
overlay.kendoStop(true, true);
if (showOptions.duration && kendo.effects.Fade) {
var overlayFx = kendo.fx(overlay).fadeIn();
overlayFx.duration(showOptions.duration || 0);
overlayFx.endValue(0.5);
overlayFx.play();
} else {
overlay.css('opacity', 0.5);
}
overlay.show();
}
if (!wrapper.is(VISIBLE)) {
contentElement.css(OVERFLOW, HIDDEN);
wrapper.show().kendoStop().kendoAnimate({
effects: showOptions.effects,
duration: showOptions.duration,
complete: proxy(this._activate, this)
});
}
}
if (options.isMaximized) {
that._documentScrollTop = doc.scrollTop();
that._documentScrollLeft = doc.scrollLeft();
$('html, body').css(OVERFLOW, HIDDEN);
}
return that;
},
_activate: function () {
var scrollable = this.options.scrollable !== false;
if (this.options.autoFocus) {
this.element.focus();
}
this.element.css(OVERFLOW, scrollable ? '' : 'hidden');
this.trigger(ACTIVATE);
},
_removeOverlay: function (suppressAnimation) {
var modals = this._modals();
var options = this.options;
var hideOverlay = options.modal && !modals.length;
var overlay = options.modal ? this._overlay(true) : $(undefined);
var hideOptions = this._animationOptions('close');
if (hideOverlay) {
if (!suppressAnimation && hideOptions.duration && kendo.effects.Fade) {
var overlayFx = kendo.fx(overlay).fadeOut();
overlayFx.duration(hideOptions.duration || 0);
overlayFx.startValue(0.5);
overlayFx.play();
} else {
this._overlay(false).remove();
}
} else if (modals.length) {
this._object(modals.last())._overlay(true);
}
},
_close: function (systemTriggered) {
var that = this, wrapper = that.wrapper, options = that.options, showOptions = this._animationOptions('open'), hideOptions = this._animationOptions('close'), doc = $(document);
if (wrapper.is(VISIBLE) && !that.trigger(CLOSE, { userTriggered: !systemTriggered })) {
if (that._closing) {
return;
}
that._closing = true;
options.visible = false;
$(KWINDOW).each(function (i, element) {
var contentElement = $(element).children(KWINDOWCONTENT);
if (element != wrapper && contentElement.find('> .' + KCONTENTFRAME).length > 0) {
contentElement.children(KOVERLAY).remove();
}
});
this._removeOverlay();
wrapper.kendoStop().kendoAnimate({
effects: hideOptions.effects || showOptions.effects,
reverse: hideOptions.reverse === true,
duration: hideOptions.duration,
complete: proxy(this._deactivate, this)
});
}
if (that.options.isMaximized) {
$('html, body').css(OVERFLOW, '');
if (that._documentScrollTop && that._documentScrollTop > 0) {
doc.scrollTop(that._documentScrollTop);
}
if (that._documentScrollLeft && that._documentScrollLeft > 0) {
doc.scrollLeft(that._documentScrollLeft);
}
}
},
_deactivate: function () {
var that = this;
that.wrapper.hide().css('opacity', '');
that.trigger(DEACTIVATE);
if (that.options.modal) {
var lastModal = that._object(that._modals().last());
if (lastModal) {
lastModal.toFront();
}
}
},
close: function () {
this._close(true);
return this;
},
_actionable: function (element) {
return $(element).is(TITLEBAR_BUTTONS + ',' + TITLEBAR_BUTTONS + ' .k-icon,:input,a');
},
_shouldFocus: function (target) {
var active = activeElement(), element = this.element;
return this.options.autoFocus && !$(active).is(element) && !this._actionable(target) && (!element.find(active).length || !element.find(target).length);
},
toFront: function (e) {
var that = this, wrapper = that.wrapper, currentWindow = wrapper[0], zIndex = +wrapper.css(ZINDEX), originalZIndex = zIndex, target = e && e.target || null;
$(KWINDOW).each(function (i, element) {
var windowObject = $(element), zIndexNew = windowObject.css(ZINDEX), contentElement = windowObject.children(KWINDOWCONTENT);
if (!isNaN(zIndexNew)) {
zIndex = Math.max(+zIndexNew, zIndex);
}
if (element != currentWindow && contentElement.find('> .' + KCONTENTFRAME).length > 0) {
contentElement.append(templates.overlay);
}
});
if (!wrapper[0].style.zIndex || originalZIndex < zIndex) {
wrapper.css(ZINDEX, zIndex + 2);
}
that.element.find('> .k-overlay').remove();
if (that._shouldFocus(target)) {
that.element.focus();
var scrollTop = $(window).scrollTop(), windowTop = parseInt(wrapper.position().top, 10);
if (windowTop > 0 && windowTop < scrollTop) {
if (scrollTop > 0) {
$(window).scrollTop(windowTop);
} else {
wrapper.css('top', scrollTop);
}
}
}
wrapper = null;
return that;
},
toggleMaximization: function () {
if (this._closing) {
return this;
}
return this[this.options.isMaximized ? 'restore' : 'maximize']();
},
restore: function () {
var that = this;
var options = that.options;
var minHeight = options.minHeight;
var restoreOptions = that.restoreOptions;
var doc = $(document);
if (!options.isMaximized && !options.isMinimized) {
return that;
}
if (minHeight && minHeight != Infinity) {
that.wrapper.css('min-height', minHeight);
}
that.wrapper.css({
position: options.pinned ? 'fixed' : 'absolute',
left: restoreOptions.left,
top: restoreOptions.top,
width: restoreOptions.width,
height: restoreOptions.height
}).removeClass(MAXIMIZEDSTATE).find('.k-window-content,.k-resize-handle').show().end().find('.k-window-titlebar .k-i-restore').parent().remove().end().end().find(MINIMIZE_MAXIMIZE).parent().show().end().end().find(PIN_UNPIN).parent().show();
that.options.width = restoreOptions.width;
that.options.height = restoreOptions.height;
$('html, body').css(OVERFLOW, '');
if (this._documentScrollTop && this._documentScrollTop > 0) {
doc.scrollTop(this._documentScrollTop);
}
if (this._documentScrollLeft && this._documentScrollLeft > 0) {
doc.scrollLeft(this._documentScrollLeft);
}
options.isMaximized = options.isMinimized = false;
that.resize();
return that;
},
_sizingAction: function (actionId, callback) {
var that = this, wrapper = that.wrapper, style = wrapper[0].style, options = that.options;
if (options.isMaximized || options.isMinimized) {
return that;
}
that.restoreOptions = {
width: style.width,
height: style.height
};
wrapper.children(KWINDOWRESIZEHANDLES).hide().end().children(KWINDOWTITLEBAR).find(MINIMIZE_MAXIMIZE).parent().hide().eq(0).before(templates.action({ name: 'Restore' }));
callback.call(that);
that.wrapper.children(KWINDOWTITLEBAR).find(PIN_UNPIN).parent().toggle(actionId !== 'maximize');
that.trigger(actionId);
return that;
},
maximize: function () {
this._sizingAction('maximize', function () {
var that = this, wrapper = that.wrapper, position = wrapper.position(), doc = $(document);
extend(that.restoreOptions, {
left: position.left,
top: position.top
});
wrapper.css({
left: 0,
top: 0,
position: 'fixed'
}).addClass(MAXIMIZEDSTATE);
this._documentScrollTop = doc.scrollTop();
this._documentScrollLeft = doc.scrollLeft();
$('html, body').css(OVERFLOW, HIDDEN);
that.options.isMaximized = true;
that._onDocumentResize();
});
},
minimize: function () {
this._sizingAction('minimize', function () {
var that = this;
that.wrapper.css({
height: '',
minHeight: ''
});
that.element.hide();
that.options.isMinimized = true;
});
},
pin: function (force) {
var that = this, win = $(window), wrapper = that.wrapper, top = parseInt(wrapper.css('top'), 10), left = parseInt(wrapper.css('left'), 10);
if (force || !that.options.pinned && !that.options.isMaximized) {
wrapper.css({
position: 'fixed',
top: top - win.scrollTop(),
left: left - win.scrollLeft()
});
wrapper.children(KWINDOWTITLEBAR).find(KPIN).addClass('k-i-unpin').removeClass('k-i-pin');
that.options.pinned = true;
}
},
unpin: function () {
var that = this, win = $(window), wrapper = that.wrapper, top = parseInt(wrapper.css('top'), 10), left = parseInt(wrapper.css('left'), 10);
if (that.options.pinned && !that.options.isMaximized) {
wrapper.css({
position: '',
top: top + win.scrollTop(),
left: left + win.scrollLeft()
});
wrapper.children(KWINDOWTITLEBAR).find(KUNPIN).addClass('k-i-pin').removeClass('k-i-unpin');
that.options.pinned = false;
}
},
_onDocumentResize: function () {
var that = this, wrapper = that.wrapper, wnd = $(window), zoomLevel = kendo.support.zoomLevel(), w, h;
if (!that.options.isMaximized) {
return;
}
w = wnd.width() / zoomLevel;
h = wnd.height() / zoomLevel - parseInt(wrapper.css('padding-top'), 10);
wrapper.css({
width: w,
height: h
});
that.options.width = w;
that.options.height = h;
that.resize();
},
refresh: function (options) {
var that = this, initOptions = that.options, element = $(that.element), iframe, showIframe, url;
if (!isPlainObject(options)) {
options = { url: options };
}
options = extend({}, initOptions.content, options);
showIframe = defined(initOptions.iframe) ? initOptions.iframe : options.iframe;
url = options.url;
if (url) {
if (!defined(showIframe)) {
showIframe = !isLocalUrl(url);
}
if (!showIframe) {
that._ajaxRequest(options);
} else {
iframe = element.find('.' + KCONTENTFRAME)[0];
if (iframe) {
iframe.src = url || iframe.src;
} else {
element.html(templates.contentFrame(extend({}, initOptions, { content: options })));
}
element.find('.' + KCONTENTFRAME).unbind('load' + NS).on('load' + NS, proxy(this._triggerRefresh, this));
}
} else {
if (options.template) {
that.content(template(options.template)({}));
}
that.trigger(REFRESH);
}
element.toggleClass('k-window-iframecontent', !!showIframe);
return that;
},
_triggerRefresh: function () {
this.trigger(REFRESH);
},
_ajaxComplete: function () {
clearTimeout(this._loadingIconTimeout);
this.wrapper.find(REFRESHICON).removeClass(LOADING);
},
_ajaxError: function (xhr, status) {
this.trigger(ERROR, {
status: status,
xhr: xhr
});
},
_ajaxSuccess: function (contentTemplate) {
return function (data) {
var html = data;
if (contentTemplate) {
html = template(contentTemplate)(data || {});
}
this.content(html, data);
this.element.prop('scrollTop', 0);
this.trigger(REFRESH);
};
},
_showLoading: function () {
this.wrapper.find(REFRESHICON).addClass(LOADING);
},
_ajaxRequest: function (options) {
this._loadingIconTimeout = setTimeout(proxy(this._showLoading, this), 100);
$.ajax(extend({
type: 'GET',
dataType: 'html',
cache: false,
error: proxy(this._ajaxError, this),
complete: proxy(this._ajaxComplete, this),
success: proxy(this._ajaxSuccess(options.template), this)
}, options));
},
_destroy: function () {
if (this.resizing) {
this.resizing.destroy();
}
if (this.dragging) {
this.dragging.destroy();
}
this.wrapper.off(NS).children(KWINDOWCONTENT).off(NS).end().find('.k-resize-handle,.k-window-titlebar').off(NS);
$(window).off('resize' + NS + this._marker);
clearTimeout(this._loadingIconTimeout);
Widget.fn.destroy.call(this);
this.unbind(undefined);
kendo.destroy(this.wrapper);
this._removeOverlay(true);
},
destroy: function () {
this._destroy();
this.wrapper.empty().remove();
this.wrapper = this.appendTo = this.element = $();
},
_createWindow: function () {
var contentHtml = this.element, options = this.options, iframeSrcAttributes, wrapper, isRtl = kendo.support.isRtl(contentHtml);
if (options.scrollable === false) {
contentHtml.attr('style', 'overflow:hidden;');
}
wrapper = $(templates.wrapper(options));
iframeSrcAttributes = contentHtml.find('iframe:not(.k-content)').map(function () {
var src = this.getAttribute('src');
this.src = '';
return src;
});
wrapper.toggleClass('k-rtl', isRtl).appendTo(this.appendTo).append(contentHtml).find('iframe:not(.k-content)').each(function (index) {
this.src = iframeSrcAttributes[index];
});
wrapper.find('.k-window-title').css(isRtl ? 'left' : 'right', wrapper.find('.k-window-actions').outerWidth() + 10);
contentHtml.css('visibility', '').show();
contentHtml.find('[data-role=editor]').each(function () {
var editor = $(this).data('kendoEditor');
if (editor) {
editor.refresh();
}
});
wrapper = contentHtml = null;
}
});
templates = {
wrapper: template(''),
action: template('' + '#= name #' + ''),
titlebar: template(' ' + '
#= title #' + '
' + '
'),
overlay: '',
contentFrame: template(''),
resizeHandle: template('')
};
function WindowResizing(wnd) {
var that = this;
that.owner = wnd;
that._draggable = new Draggable(wnd.wrapper, {
filter: '>' + KWINDOWRESIZEHANDLES,
group: wnd.wrapper.id + '-resizing',
dragstart: proxy(that.dragstart, that),
drag: proxy(that.drag, that),
dragend: proxy(that.dragend, that)
});
that._draggable.userEvents.bind('press', proxy(that.addOverlay, that));
that._draggable.userEvents.bind('release', proxy(that.removeOverlay, that));
}
WindowResizing.prototype = {
addOverlay: function () {
this.owner.wrapper.append(templates.overlay);
},
removeOverlay: function () {
this.owner.wrapper.find(KOVERLAY).remove();
},
dragstart: function (e) {
var that = this;
var wnd = that.owner;
var wrapper = wnd.wrapper;
that.elementPadding = parseInt(wrapper.css('padding-top'), 10);
that.initialPosition = kendo.getOffset(wrapper, 'position');
that.resizeDirection = e.currentTarget.prop('className').replace('k-resize-handle k-resize-', '');
that.initialSize = {
width: wrapper.width(),
height: wrapper.height()
};
that.containerOffset = kendo.getOffset(wnd.appendTo, 'position');
wrapper.children(KWINDOWRESIZEHANDLES).not(e.currentTarget).hide();
$(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
},
drag: function (e) {
var that = this, wnd = that.owner, wrapper = wnd.wrapper, options = wnd.options, direction = that.resizeDirection, containerOffset = that.containerOffset, initialPosition = that.initialPosition, initialSize = that.initialSize, newWidth, newHeight, windowBottom, windowRight, x = Math.max(e.x.location, containerOffset.left), y = Math.max(e.y.location, containerOffset.top);
if (direction.indexOf('e') >= 0) {
newWidth = x - initialPosition.left;
wrapper.width(constrain(newWidth, options.minWidth, options.maxWidth));
} else if (direction.indexOf('w') >= 0) {
windowRight = initialPosition.left + initialSize.width;
newWidth = constrain(windowRight - x, options.minWidth, options.maxWidth);
wrapper.css({
left: windowRight - newWidth - containerOffset.left,
width: newWidth
});
}
if (direction.indexOf('s') >= 0) {
newHeight = y - initialPosition.top - that.elementPadding;
wrapper.height(constrain(newHeight, options.minHeight, options.maxHeight));
} else if (direction.indexOf('n') >= 0) {
windowBottom = initialPosition.top + initialSize.height;
newHeight = constrain(windowBottom - y, options.minHeight, options.maxHeight);
wrapper.css({
top: windowBottom - newHeight - containerOffset.top,
height: newHeight
});
}
if (newWidth) {
wnd.options.width = newWidth + 'px';
}
if (newHeight) {
wnd.options.height = newHeight + 'px';
}
wnd.resize();
},
dragend: function (e) {
var that = this, wnd = that.owner, wrapper = wnd.wrapper;
wrapper.children(KWINDOWRESIZEHANDLES).not(e.currentTarget).show();
$(BODY).css(CURSOR, '');
if (wnd.touchScroller) {
wnd.touchScroller.reset();
}
if (e.keyCode == 27) {
wrapper.css(that.initialPosition).css(that.initialSize);
}
wnd.trigger(RESIZEEND);
return false;
},
destroy: function () {
if (this._draggable) {
this._draggable.destroy();
}
this._draggable = this.owner = null;
}
};
function WindowDragging(wnd, dragHandle) {
var that = this;
that.owner = wnd;
that._draggable = new Draggable(wnd.wrapper, {
filter: dragHandle,
group: wnd.wrapper.id + '-moving',
dragstart: proxy(that.dragstart, that),
drag: proxy(that.drag, that),
dragend: proxy(that.dragend, that),
dragcancel: proxy(that.dragcancel, that)
});
that._draggable.userEvents.stopPropagation = false;
}
WindowDragging.prototype = {
dragstart: function (e) {
var wnd = this.owner, element = wnd.element, actions = element.find('.k-window-actions'), containerOffset = kendo.getOffset(wnd.appendTo);
wnd.trigger(DRAGSTART);
wnd.initialWindowPosition = kendo.getOffset(wnd.wrapper, 'position');
wnd.startPosition = {
left: e.x.client - wnd.initialWindowPosition.left,
top: e.y.client - wnd.initialWindowPosition.top
};
if (actions.length > 0) {
wnd.minLeftPosition = actions.outerWidth() + parseInt(actions.css('right'), 10) - element.outerWidth();
} else {
wnd.minLeftPosition = 20 - element.outerWidth();
}
wnd.minLeftPosition -= containerOffset.left;
wnd.minTopPosition = -containerOffset.top;
wnd.wrapper.append(templates.overlay).children(KWINDOWRESIZEHANDLES).hide();
$(BODY).css(CURSOR, e.currentTarget.css(CURSOR));
},
drag: function (e) {
var wnd = this.owner, position = wnd.options.position, newTop = Math.max(e.y.client - wnd.startPosition.top, wnd.minTopPosition), newLeft = Math.max(e.x.client - wnd.startPosition.left, wnd.minLeftPosition), coordinates = {
left: newLeft,
top: newTop
};
$(wnd.wrapper).css(coordinates);
position.top = newTop;
position.left = newLeft;
},
_finishDrag: function () {
var wnd = this.owner;
wnd.wrapper.children(KWINDOWRESIZEHANDLES).toggle(!wnd.options.isMinimized).end().find(KOVERLAY).remove();
$(BODY).css(CURSOR, '');
},
dragcancel: function (e) {
this._finishDrag();
e.currentTarget.closest(KWINDOW).css(this.owner.initialWindowPosition);
},
dragend: function () {
this._finishDrag();
this.owner.trigger(DRAGEND);
return false;
},
destroy: function () {
if (this._draggable) {
this._draggable.destroy();
}
this._draggable = this.owner = null;
}
};
kendo.ui.plugin(Window);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.list', [
'kendo.data',
'kendo.popup'
], f);
}(function () {
var __meta__ = {
id: 'list',
name: 'List',
category: 'framework',
depends: [
'data',
'popup'
],
hidden: true
};
(function ($, undefined) {
var kendo = window.kendo, ui = kendo.ui, Widget = ui.Widget, keys = kendo.keys, support = kendo.support, htmlEncode = kendo.htmlEncode, activeElement = kendo._activeElement, ObservableArray = kendo.data.ObservableArray, ID = 'id', CHANGE = 'change', FOCUSED = 'k-state-focused', HOVER = 'k-state-hover', LOADING = 'k-loading', OPEN = 'open', CLOSE = 'close', CASCADE = 'cascade', SELECT = 'select', SELECTED = 'selected', REQUESTSTART = 'requestStart', REQUESTEND = 'requestEnd', WIDTH = 'width', extend = $.extend, proxy = $.proxy, isArray = $.isArray, browser = support.browser, isIE8 = browser.msie && browser.version < 9, quotRegExp = /"/g, alternativeNames = {
'ComboBox': 'DropDownList',
'DropDownList': 'ComboBox'
};
var List = kendo.ui.DataBoundWidget.extend({
init: function (element, options) {
var that = this, ns = that.ns, id;
Widget.fn.init.call(that, element, options);
element = that.element;
options = that.options;
that._isSelect = element.is(SELECT);
if (that._isSelect && that.element[0].length) {
if (!options.dataSource) {
options.dataTextField = options.dataTextField || 'text';
options.dataValueField = options.dataValueField || 'value';
}
}
that.ul = $('').attr({
tabIndex: -1,
'aria-hidden': true
});
that.list = $('').append(that.ul).on('mousedown' + ns, proxy(that._listMousedown, that));
id = element.attr(ID);
if (id) {
that.list.attr(ID, id + '-list');
that.ul.attr(ID, id + '_listbox');
}
that._header();
that._accessors();
that._initValue();
},
options: {
valuePrimitive: false,
headerTemplate: ''
},
setOptions: function (options) {
Widget.fn.setOptions.call(this, options);
if (options && options.enable !== undefined) {
options.enabled = options.enable;
}
},
focus: function () {
this._focused.focus();
},
readonly: function (readonly) {
this._editable({
readonly: readonly === undefined ? true : readonly,
disable: false
});
},
enable: function (enable) {
this._editable({
readonly: false,
disable: !(enable = enable === undefined ? true : enable)
});
},
_listOptions: function (options) {
var that = this;
var currentOptions = that.options;
var virtual = currentOptions.virtual;
var listBoundHandler = proxy(that._listBound, that);
virtual = typeof virtual === 'object' ? virtual : {};
options = $.extend({
autoBind: false,
selectable: true,
dataSource: that.dataSource,
click: proxy(that._click, that),
change: proxy(that._listChange, that),
activate: proxy(that._activateItem, that),
deactivate: proxy(that._deactivateItem, that),
dataBinding: function () {
that.trigger('dataBinding');
that._angularItems('cleanup');
},
dataBound: listBoundHandler,
listBound: listBoundHandler,
height: currentOptions.height,
dataValueField: currentOptions.dataValueField,
dataTextField: currentOptions.dataTextField,
groupTemplate: currentOptions.groupTemplate,
fixedGroupTemplate: currentOptions.fixedGroupTemplate,
template: currentOptions.template
}, options, virtual);
if (!options.template) {
options.template = '#:' + kendo.expr(options.dataTextField, 'data') + '#';
}
return options;
},
_initList: function () {
var that = this;
var listOptions = that._listOptions({ selectedItemChange: proxy(that._listChange, that) });
if (!that.options.virtual) {
that.listView = new kendo.ui.StaticList(that.ul, listOptions);
} else {
that.listView = new kendo.ui.VirtualList(that.ul, listOptions);
}
that._setListValue();
},
_setListValue: function (value) {
value = value || this.options.value;
if (value !== undefined) {
this.listView.value(value).done(proxy(this._updateSelectionState, this));
}
},
_updateSelectionState: $.noop,
_listMousedown: function (e) {
if (!this.filterInput || this.filterInput[0] !== e.target) {
e.preventDefault();
}
},
_isFilterEnabled: function () {
var filter = this.options.filter;
return filter && filter !== 'none';
},
_filterSource: function (filter, force) {
var that = this;
var options = that.options;
var dataSource = that.dataSource;
var expression = extend({}, dataSource.filter() || {});
var removed = removeFiltersForField(expression, options.dataTextField);
if ((filter || removed) && that.trigger('filtering', { filter: filter })) {
return;
}
expression = {
filters: expression.filters || [],
logic: 'and'
};
if (filter) {
expression.filters.push(filter);
}
if (that._cascading) {
this.listView.setDSFilter(expression);
}
if (!force) {
dataSource.filter(expression);
} else {
dataSource.read({ filter: expression });
}
},
_header: function () {
var that = this;
var template = that.options.headerTemplate;
var header;
if ($.isFunction(template)) {
template = template({});
}
if (template) {
that.list.prepend(template);
header = that.ul.prev();
that.header = header[0] ? header : null;
if (that.header) {
that.angular('compile', function () {
return { elements: that.header };
});
}
}
},
_initValue: function () {
var that = this, value = that.options.value;
if (value !== null) {
that.element.val(value);
} else {
value = that._accessor();
that.options.value = value;
}
that._old = value;
},
_ignoreCase: function () {
var that = this, model = that.dataSource.reader.model, field;
if (model && model.fields) {
field = model.fields[that.options.dataTextField];
if (field && field.type && field.type !== 'string') {
that.options.ignoreCase = false;
}
}
},
_focus: function (candidate) {
return this.listView.focus(candidate);
},
current: function (candidate) {
return this._focus(candidate);
},
items: function () {
return this.ul[0].children;
},
destroy: function () {
var that = this;
var ns = that.ns;
Widget.fn.destroy.call(that);
that._unbindDataSource();
that.listView.destroy();
that.list.off(ns);
that.popup.destroy();
if (that._form) {
that._form.off('reset', that._resetHandler);
}
},
dataItem: function (index) {
var that = this;
if (index === undefined) {
return that.listView.selectedDataItems()[0];
}
if (typeof index !== 'number') {
if (that.options.virtual) {
return that.dataSource.getByUid($(index).data('uid'));
}
index = $(that.items()).index(index);
}
return that.dataSource.flatView()[index];
},
_activateItem: function () {
var current = this.listView.focus();
if (current) {
this._focused.add(this.filterInput).attr('aria-activedescendant', current.attr('id'));
}
},
_deactivateItem: function () {
this._focused.add(this.filterInput).removeAttr('aria-activedescendant');
},
_accessors: function () {
var that = this;
var element = that.element;
var options = that.options;
var getter = kendo.getter;
var textField = element.attr(kendo.attr('text-field'));
var valueField = element.attr(kendo.attr('value-field'));
if (!options.dataTextField && textField) {
options.dataTextField = textField;
}
if (!options.dataValueField && valueField) {
options.dataValueField = valueField;
}
that._text = getter(options.dataTextField);
that._value = getter(options.dataValueField);
},
_aria: function (id) {
var that = this, options = that.options, element = that._focused.add(that.filterInput);
if (options.suggest !== undefined) {
element.attr('aria-autocomplete', options.suggest ? 'both' : 'list');
}
id = id ? id + ' ' + that.ul[0].id : that.ul[0].id;
element.attr('aria-owns', id);
that.ul.attr('aria-live', !that._isFilterEnabled() ? 'off' : 'polite');
},
_blur: function () {
var that = this;
that._change();
that.close();
},
_change: function () {
var that = this;
var index = that.selectedIndex;
var optionValue = that.options.value;
var value = that.value();
var trigger;
if (that._isSelect && !that.listView.bound() && optionValue) {
value = optionValue;
}
if (value !== unifyType(that._old, typeof value)) {
trigger = true;
} else if (index !== undefined && index !== that._oldIndex) {
trigger = true;
}
if (trigger) {
that._old = value;
that._oldIndex = index;
if (!that._typing) {
that.element.trigger(CHANGE);
}
that.trigger(CHANGE);
}
that.typing = false;
},
_data: function () {
return this.dataSource.view();
},
_enable: function () {
var that = this, options = that.options, disabled = that.element.is('[disabled]');
if (options.enable !== undefined) {
options.enabled = options.enable;
}
if (!options.enabled || disabled) {
that.enable(false);
} else {
that.readonly(that.element.is('[readonly]'));
}
},
_dataValue: function (dataItem) {
var value = this._value(dataItem);
if (value === undefined) {
value = this._text(dataItem);
}
return value;
},
_offsetHeight: function () {
var offsetHeight = 0;
var siblings = this.listView.content.prevAll(':visible');
siblings.each(function () {
var element = $(this);
if (element.hasClass('k-list-filter')) {
offsetHeight += element.children().outerHeight();
} else {
offsetHeight += element.outerHeight();
}
});
return offsetHeight;
},
_height: function (length) {
var that = this;
var list = that.list;
var height = that.options.height;
var visible = that.popup.visible();
var offsetTop;
var popups;
if (length) {
popups = list.add(list.parent('.k-animation-container')).show();
if (!list.is(':visible')) {
popups.hide();
return;
}
height = that.listView.content[0].scrollHeight > height ? height : 'auto';
popups.height(height);
if (height !== 'auto') {
offsetTop = that._offsetHeight();
if (offsetTop) {
height -= offsetTop;
}
}
that.listView.content.height(height);
if (!visible) {
popups.hide();
}
}
return height;
},
_adjustListWidth: function () {
var list = this.list, width = list[0].style.width, wrapper = this.wrapper, computedStyle, computedWidth;
if (!list.data(WIDTH) && width) {
return;
}
computedStyle = window.getComputedStyle ? window.getComputedStyle(wrapper[0], null) : 0;
computedWidth = parseFloat(computedStyle && computedStyle.width) || wrapper.outerWidth();
if (computedStyle && browser.msie) {
computedWidth += parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight) + parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth);
}
if (list.css('box-sizing') !== 'border-box') {
width = computedWidth - (list.outerWidth() - list.width());
} else {
width = computedWidth;
}
list.css({
fontFamily: wrapper.css('font-family'),
width: width
}).data(WIDTH, width);
return true;
},
_openHandler: function (e) {
this._adjustListWidth();
if (this.trigger(OPEN)) {
e.preventDefault();
} else {
this._focused.attr('aria-expanded', true);
this.ul.attr('aria-hidden', false);
}
},
_closeHandler: function (e) {
if (this.trigger(CLOSE)) {
e.preventDefault();
} else {
this._focused.attr('aria-expanded', false);
this.ul.attr('aria-hidden', true);
}
},
_focusItem: function () {
var listView = this.listView;
var focusedItem = listView.focus();
var index = listView.select();
index = index[index.length - 1];
if (index === undefined && this.options.highlightFirst && !focusedItem) {
index = 0;
}
if (index !== undefined) {
listView.focus(index);
} else {
listView.scrollToIndex(0);
}
},
_calculateGroupPadding: function (height) {
var li = this.ul.children('.k-first:first');
var groupHeader = this.listView.content.prev('.k-group-header');
var padding = 0;
if (groupHeader[0] && groupHeader[0].style.display !== 'none') {
if (height !== 'auto') {
padding = kendo.support.scrollbar();
}
padding += parseFloat(li.css('border-right-width'), 10) + parseFloat(li.children('.k-group').css('padding-right'), 10);
groupHeader.css('padding-right', padding);
}
},
_calculatePopupHeight: function (force) {
var height = this._height(this.dataSource.flatView().length || force);
this._calculateGroupPadding(height);
},
_resizePopup: function (force) {
if (this.options.virtual) {
return;
}
if (!this.popup.element.is(':visible')) {
this.popup.one('open', function (force) {
return proxy(function () {
this._calculatePopupHeight(force);
}, this);
}.call(this, force));
} else {
this._calculatePopupHeight(force);
}
},
_popup: function () {
var that = this;
that.popup = new ui.Popup(that.list, extend({}, that.options.popup, {
anchor: that.wrapper,
open: proxy(that._openHandler, that),
close: proxy(that._closeHandler, that),
animation: that.options.animation,
isRtl: support.isRtl(that.wrapper)
}));
},
_makeUnselectable: function () {
if (isIE8) {
this.list.find('*').not('.k-textbox').attr('unselectable', 'on');
}
},
_toggleHover: function (e) {
$(e.currentTarget).toggleClass(HOVER, e.type === 'mouseenter');
},
_toggle: function (open, preventFocus) {
var that = this;
var touchEnabled = support.mobileOS && (support.touch || support.MSPointers || support.pointers);
open = open !== undefined ? open : !that.popup.visible();
if (!preventFocus && !touchEnabled && that._focused[0] !== activeElement()) {
that._prevent = true;
that._focused.focus();
that._prevent = false;
}
that[open ? OPEN : CLOSE]();
},
_triggerCascade: function () {
var that = this;
if (!that._cascadeTriggered || that._old !== that.value() || that._oldIndex !== that.selectedIndex) {
that._cascadeTriggered = true;
that.trigger(CASCADE, { userTriggered: that._userTriggered });
}
},
_triggerChange: function () {
if (this._valueBeforeCascade !== this.value()) {
this.trigger(CHANGE);
}
},
_unbindDataSource: function () {
var that = this;
that.dataSource.unbind(REQUESTSTART, that._requestStartHandler).unbind(REQUESTEND, that._requestEndHandler).unbind('error', that._errorHandler);
}
});
function unifyType(value, type) {
if (value !== undefined && value !== '' && value !== null) {
if (type === 'boolean') {
value = Boolean(value);
} else if (type === 'number') {
value = Number(value);
} else if (type === 'string') {
value = value.toString();
}
}
return value;
}
extend(List, {
inArray: function (node, parentNode) {
var idx, length, siblings = parentNode.children;
if (!node || node.parentNode !== parentNode) {
return -1;
}
for (idx = 0, length = siblings.length; idx < length; idx++) {
if (node === siblings[idx]) {
return idx;
}
}
return -1;
},
unifyType: unifyType
});
kendo.ui.List = List;
ui.Select = List.extend({
init: function (element, options) {
List.fn.init.call(this, element, options);
this._initial = this.element.val();
},
setDataSource: function (dataSource) {
var that = this;
var parent;
that.options.dataSource = dataSource;
that._dataSource();
if (that.listView.bound()) {
that._initialIndex = null;
}
that.listView.setDataSource(that.dataSource);
if (that.options.autoBind) {
that.dataSource.fetch();
}
parent = that._parentWidget();
if (parent) {
that._cascadeSelect(parent);
}
},
close: function () {
this.popup.close();
},
select: function (candidate) {
var that = this;
if (candidate === undefined) {
return that.selectedIndex;
} else {
that._select(candidate);
that._old = that._accessor();
that._oldIndex = that.selectedIndex;
}
},
search: function (word) {
word = typeof word === 'string' ? word : this.text();
var that = this;
var length = word.length;
var options = that.options;
var ignoreCase = options.ignoreCase;
var field = options.dataTextField;
clearTimeout(that._typingTimeout);
if (!length || length >= options.minLength) {
that._state = 'filter';
if (!that._isFilterEnabled()) {
that._filter(word);
} else {
that._open = true;
that._filterSource({
value: ignoreCase ? word.toLowerCase() : word,
field: field,
operator: options.filter,
ignoreCase: ignoreCase
});
}
}
},
_accessor: function (value, idx) {
return this[this._isSelect ? '_accessorSelect' : '_accessorInput'](value, idx);
},
_accessorInput: function (value) {
var element = this.element[0];
if (value === undefined) {
return element.value;
} else {
if (value === null) {
value = '';
}
element.value = value;
}
},
_accessorSelect: function (value, idx) {
var element = this.element[0];
var selectedIndex = element.selectedIndex;
var option;
if (value === undefined) {
if (selectedIndex > -1) {
option = element.options[selectedIndex];
}
if (option) {
value = option.value;
}
return value || '';
} else {
if (selectedIndex > -1) {
element.options[selectedIndex].removeAttribute(SELECTED);
element.options[selectedIndex].selected = false;
}
if (idx === undefined) {
idx = -1;
}
if (value !== null && value !== '' && idx == -1) {
this._custom(value);
} else {
if (value) {
element.value = value;
} else {
element.selectedIndex = idx;
}
if (element.selectedIndex > -1) {
option = element.options[element.selectedIndex];
}
if (option) {
option.setAttribute(SELECTED, SELECTED);
}
}
}
},
_custom: function (value) {
var that = this;
var element = that.element;
var custom = that._customOption;
if (!custom) {
custom = $('
');
that._customOption = custom;
element.append(custom);
}
custom.text(value);
custom[0].setAttribute(SELECTED, SELECTED);
custom[0].selected = true;
},
_hideBusy: function () {
var that = this;
clearTimeout(that._busy);
that._arrow.removeClass(LOADING);
that._focused.attr('aria-busy', false);
that._busy = null;
},
_showBusy: function () {
var that = this;
that._request = true;
if (that._busy) {
return;
}
that._busy = setTimeout(function () {
if (that._arrow) {
that._focused.attr('aria-busy', true);
that._arrow.addClass(LOADING);
}
}, 100);
},
_requestEnd: function () {
this._request = false;
this._hideBusy();
},
_dataSource: function () {
var that = this, element = that.element, options = that.options, dataSource = options.dataSource || {}, idx;
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
if (that._isSelect) {
idx = element[0].selectedIndex;
if (idx > -1) {
options.index = idx;
}
dataSource.select = element;
dataSource.fields = [
{ field: options.dataTextField },
{ field: options.dataValueField }
];
}
if (that.dataSource) {
that._unbindDataSource();
} else {
that._requestStartHandler = proxy(that._showBusy, that);
that._requestEndHandler = proxy(that._requestEnd, that);
that._errorHandler = proxy(that._hideBusy, that);
}
that.dataSource = kendo.data.DataSource.create(dataSource).bind(REQUESTSTART, that._requestStartHandler).bind(REQUESTEND, that._requestEndHandler).bind('error', that._errorHandler);
},
_firstItem: function () {
this.listView.focusFirst();
},
_lastItem: function () {
this.listView.focusLast();
},
_nextItem: function () {
this.listView.focusNext();
},
_prevItem: function () {
this.listView.focusPrev();
},
_move: function (e) {
var that = this;
var key = e.keyCode;
var down = key === keys.DOWN;
var dataItem;
var pressed;
var current;
if (key === keys.UP || down) {
if (e.altKey) {
that.toggle(down);
} else {
if (!that.listView.bound()) {
if (!that._fetch) {
that.dataSource.one(CHANGE, function () {
that._fetch = false;
that._move(e);
});
that._fetch = true;
that._filterSource();
}
e.preventDefault();
return true;
}
current = that._focus();
if (!that._fetch && (!current || current.hasClass('k-state-selected'))) {
if (down) {
that._nextItem();
if (!that._focus()) {
that._lastItem();
}
} else {
that._prevItem();
if (!that._focus()) {
that._firstItem();
}
}
}
if (that.trigger(SELECT, { item: that._focus() })) {
that._focus(current);
return;
}
that._select(that._focus(), true);
if (!that.popup.visible()) {
that._blur();
}
}
e.preventDefault();
pressed = true;
} else if (key === keys.ENTER || key === keys.TAB) {
if (that.popup.visible()) {
e.preventDefault();
}
current = that._focus();
dataItem = that.dataItem();
if (!that.popup.visible() && (!dataItem || that.text() !== that._text(dataItem))) {
current = null;
}
var activeFilter = that.filterInput && that.filterInput[0] === activeElement();
if (current) {
if (that.trigger(SELECT, { item: current })) {
return;
}
that._select(current);
} else if (that.input) {
that._accessor(that.input.val());
that.listView.value(that.input.val());
}
if (that._focusElement) {
that._focusElement(that.wrapper);
}
if (activeFilter && key === keys.TAB) {
that.wrapper.focusout();
} else {
that._blur();
}
that.close();
pressed = true;
} else if (key === keys.ESC) {
if (that.popup.visible()) {
e.preventDefault();
}
that.close();
pressed = true;
}
return pressed;
},
_fetchData: function () {
var that = this;
var hasItems = !!that.dataSource.view().length;
if (that._request || that.options.cascadeFrom) {
return;
}
if (!that.listView.bound() && !that._fetch && !hasItems) {
that._fetch = true;
that.dataSource.fetch().done(function () {
that._fetch = false;
});
}
},
_options: function (data, optionLabel, value) {
var that = this, element = that.element, length = data.length, options = '', option, dataItem, dataText, dataValue, idx = 0;
if (optionLabel) {
options = optionLabel;
}
for (; idx < length; idx++) {
option = '
';
options += option;
}
element.html(options);
if (value !== undefined) {
element[0].value = value;
if (element[0].value && !value) {
element[0].selectedIndex = -1;
}
}
},
_reset: function () {
var that = this, element = that.element, formId = element.attr('form'), form = formId ? $('#' + formId) : element.closest('form');
if (form[0]) {
that._resetHandler = function () {
setTimeout(function () {
that.value(that._initial);
});
};
that._form = form.on('reset', that._resetHandler);
}
},
_parentWidget: function () {
var name = this.options.name;
var parentElement = $('#' + this.options.cascadeFrom);
var parent = parentElement.data('kendo' + name);
if (!parent) {
parent = parentElement.data('kendo' + alternativeNames[name]);
}
return parent;
},
_cascade: function () {
var that = this;
var options = that.options;
var cascade = options.cascadeFrom;
var cascadeHandler;
var parent;
if (cascade) {
parent = that._parentWidget();
if (!parent) {
return;
}
parent.bind('set', function () {
that.one('set', function (e) {
that._selectedValue = e.value;
});
});
options.autoBind = false;
cascadeHandler = proxy(function (e) {
var valueBeforeCascade = this.value();
this._userTriggered = e.userTriggered;
if (this.listView.bound()) {
this._clearSelection(parent, true);
}
this._cascadeSelect(parent, valueBeforeCascade);
}, that);
parent.first(CASCADE, cascadeHandler);
parent._focused.bind('focus', function () {
parent.unbind(CASCADE, cascadeHandler);
parent.first(CHANGE, cascadeHandler);
});
parent._focused.bind('focusout', function () {
parent.unbind(CHANGE, cascadeHandler);
parent.first(CASCADE, cascadeHandler);
});
if (parent.listView.bound()) {
that._cascadeSelect(parent);
} else if (!parent.value()) {
that.enable(false);
}
}
},
_cascadeChange: function (parent) {
var that = this;
var value = that._accessor() || that._selectedValue;
that._selectedValue = null;
if (that._userTriggered) {
that._clearSelection(parent, true);
} else if (value) {
if (value !== that.listView.value()[0]) {
that.value(value);
}
if (!that.dataSource.view()[0] || that.selectedIndex === -1) {
that._clearSelection(parent, true);
}
} else if (that.dataSource.flatView().length) {
that.select(that.options.index);
}
that.enable();
that._triggerCascade();
that._triggerChange();
that._userTriggered = false;
},
_cascadeSelect: function (parent, valueBeforeCascade) {
var that = this;
var dataItem = parent.dataItem();
var filterValue = dataItem ? parent._value(dataItem) : null;
var valueField = that.options.cascadeFromField || parent.options.dataValueField;
var expressions, filters;
that._valueBeforeCascade = valueBeforeCascade !== undefined ? valueBeforeCascade : that.value();
if (filterValue || filterValue === 0) {
expressions = that.dataSource.filter() || {};
removeFiltersForField(expressions, valueField);
filters = (expressions.filters || []).slice(0);
filters.push({
field: valueField,
operator: 'eq',
value: filterValue
});
var handler = function () {
that.unbind('dataBound', handler);
that._cascadeChange(parent);
};
that.first('dataBound', handler);
that._cascading = true;
that._filterSource({
field: valueField,
operator: 'eq',
value: filterValue
});
that._cascading = false;
} else {
that.enable(false);
that._clearSelection(parent);
that._triggerCascade();
that._triggerChange();
that._userTriggered = false;
}
}
});
var STATIC_LIST_NS = '.StaticList';
var StaticList = kendo.ui.DataBoundWidget.extend({
init: function (element, options) {
Widget.fn.init.call(this, element, options);
this.element.attr('role', 'listbox').on('click' + STATIC_LIST_NS, 'li', proxy(this._click, this)).on('mouseenter' + STATIC_LIST_NS, 'li', function () {
$(this).addClass(HOVER);
}).on('mouseleave' + STATIC_LIST_NS, 'li', function () {
$(this).removeClass(HOVER);
});
this.content = this.element.wrap('
').parent();
this.header = this.content.before('').prev();
this.bound(false);
this._optionID = kendo.guid();
this._selectedIndices = [];
this._view = [];
this._dataItems = [];
this._values = [];
var value = this.options.value;
if (value) {
this._values = $.isArray(value) ? value.slice(0) : [value];
}
this._getter();
this._templates();
this.setDataSource(this.options.dataSource);
this._onScroll = proxy(function () {
var that = this;
clearTimeout(that._scrollId);
that._scrollId = setTimeout(function () {
that._renderHeader();
}, 50);
}, this);
},
options: {
name: 'StaticList',
dataValueField: null,
valuePrimitive: false,
selectable: true,
template: null,
groupTemplate: null,
fixedGroupTemplate: null
},
events: [
'click',
CHANGE,
'activate',
'deactivate',
'dataBinding',
'dataBound',
'selectedItemChange'
],
setDataSource: function (source) {
var that = this;
var dataSource = source || {};
var value;
dataSource = $.isArray(dataSource) ? { data: dataSource } : dataSource;
dataSource = kendo.data.DataSource.create(dataSource);
if (that.dataSource) {
that.dataSource.unbind(CHANGE, that._refreshHandler);
value = that.value();
that.value([]);
that.bound(false);
that.value(value);
} else {
that._refreshHandler = proxy(that.refresh, that);
}
that.setDSFilter(dataSource.filter());
that.dataSource = dataSource.bind(CHANGE, that._refreshHandler);
that._fixedHeader();
},
skip: function () {
return this.dataSource.skip();
},
setOptions: function (options) {
Widget.fn.setOptions.call(this, options);
this._getter();
this._templates();
this._render();
},
destroy: function () {
this.element.off(STATIC_LIST_NS);
if (this._refreshHandler) {
this.dataSource.unbind(CHANGE, this._refreshHandler);
}
clearTimeout(this._scrollId);
Widget.fn.destroy.call(this);
},
scrollToIndex: function (index) {
var item = this.element[0].children[index];
if (item) {
this.scroll(item);
}
},
scroll: function (item) {
if (!item) {
return;
}
if (item[0]) {
item = item[0];
}
var content = this.content[0], itemOffsetTop = item.offsetTop, itemOffsetHeight = item.offsetHeight, contentScrollTop = content.scrollTop, contentOffsetHeight = content.clientHeight, bottomDistance = itemOffsetTop + itemOffsetHeight;
if (contentScrollTop > itemOffsetTop) {
contentScrollTop = itemOffsetTop;
} else if (bottomDistance > contentScrollTop + contentOffsetHeight) {
contentScrollTop = bottomDistance - contentOffsetHeight;
}
content.scrollTop = contentScrollTop;
},
selectedDataItems: function (dataItems) {
if (dataItems === undefined) {
return this._dataItems.slice();
}
this._dataItems = dataItems;
this._values = this._getValues(dataItems);
},
_getValues: function (dataItems) {
var getter = this._valueGetter;
return $.map(dataItems, function (dataItem) {
return getter(dataItem);
});
},
focusNext: function () {
var current = this.focus();
if (!current) {
current = 0;
} else {
current = current.next();
}
this.focus(current);
},
focusPrev: function () {
var current = this.focus();
if (!current) {
current = this.element[0].children.length - 1;
} else {
current = current.prev();
}
this.focus(current);
},
focusFirst: function () {
this.focus(this.element[0].children[0]);
},
focusLast: function () {
this.focus(this.element[0].children[this.element[0].children.length - 1]);
},
focus: function (candidate) {
var that = this;
var id = that._optionID;
var hasCandidate;
if (candidate === undefined) {
return that._current;
}
candidate = that._get(candidate);
candidate = candidate[candidate.length - 1];
candidate = $(this.element[0].children[candidate]);
if (that._current) {
that._current.removeClass(FOCUSED).removeAttr('aria-selected').removeAttr(ID);
that.trigger('deactivate');
}
hasCandidate = !!candidate[0];
if (hasCandidate) {
candidate.addClass(FOCUSED);
that.scroll(candidate);
candidate.attr('id', id);
}
that._current = hasCandidate ? candidate : null;
that.trigger('activate');
},
focusIndex: function () {
return this.focus() ? this.focus().index() : undefined;
},
skipUpdate: function (skipUpdate) {
this._skipUpdate = skipUpdate;
},
select: function (indices) {
var that = this;
var selectable = that.options.selectable;
var singleSelection = selectable !== 'multiple' && selectable !== false;
var selectedIndices = that._selectedIndices;
var added = [];
var removed = [];
var result;
if (indices === undefined) {
return selectedIndices.slice();
}
indices = that._get(indices);
if (indices.length === 1 && indices[0] === -1) {
indices = [];
}
var filtered = that.isFiltered();
if (filtered && !singleSelection && that._deselectFiltered(indices)) {
return;
}
if (singleSelection && !filtered && $.inArray(indices[indices.length - 1], selectedIndices) !== -1) {
if (that._dataItems.length && that._view.length) {
that._dataItems = [that._view[selectedIndices[0]].item];
}
return;
}
result = that._deselect(indices);
removed = result.removed;
indices = result.indices;
if (indices.length) {
if (singleSelection) {
indices = [indices[indices.length - 1]];
}
added = that._select(indices);
}
if (added.length || removed.length) {
that._valueComparer = null;
that.trigger(CHANGE, {
added: added,
removed: removed
});
}
},
removeAt: function (position) {
this._selectedIndices.splice(position, 1);
this._values.splice(position, 1);
this._valueComparer = null;
return {
position: position,
dataItem: this._dataItems.splice(position, 1)[0]
};
},
setValue: function (value) {
value = $.isArray(value) || value instanceof ObservableArray ? value.slice(0) : [value];
this._values = value;
this._valueComparer = null;
},
value: function (value) {
var that = this;
var deferred = that._valueDeferred;
var indices;
if (value === undefined) {
return that._values.slice();
}
that.setValue(value);
if (!deferred || deferred.state() === 'resolved') {
that._valueDeferred = deferred = $.Deferred();
}
if (that.bound()) {
indices = that._valueIndices(that._values);
if (that.options.selectable === 'multiple') {
that.select(-1);
}
that.select(indices);
deferred.resolve();
}
that._skipUpdate = false;
return deferred;
},
items: function () {
return this.element.children('.k-item');
},
_click: function (e) {
if (!e.isDefaultPrevented()) {
if (!this.trigger('click', { item: $(e.currentTarget) })) {
this.select(e.currentTarget);
}
}
},
_valueExpr: function (type, values) {
var that = this;
var idx = 0;
var body;
var comparer;
var normalized = [];
if (!that._valueComparer || that._valueType !== type) {
that._valueType = type;
for (; idx < values.length; idx++) {
normalized.push(unifyType(values[idx], type));
}
body = 'for (var idx = 0; idx < ' + normalized.length + '; idx++) {' + ' if (current === values[idx]) {' + ' return idx;' + ' }' + '} ' + 'return -1;';
comparer = new Function('current', 'values', body);
that._valueComparer = function (current) {
return comparer(current, normalized);
};
}
return that._valueComparer;
},
_dataItemPosition: function (dataItem, values) {
var value = this._valueGetter(dataItem);
var valueExpr = this._valueExpr(typeof value, values);
return valueExpr(value);
},
_getter: function () {
this._valueGetter = kendo.getter(this.options.dataValueField);
},
_deselect: function (indices) {
var that = this;
var children = that.element[0].children;
var selectable = that.options.selectable;
var selectedIndices = that._selectedIndices;
var dataItems = that._dataItems;
var values = that._values;
var removed = [];
var i = 0;
var j;
var index, selectedIndex;
var removedIndices = 0;
indices = indices.slice();
if (selectable === true || !indices.length) {
for (; i < selectedIndices.length; i++) {
$(children[selectedIndices[i]]).removeClass('k-state-selected');
removed.push({
position: i,
dataItem: dataItems[i]
});
}
that._values = [];
that._dataItems = [];
that._selectedIndices = [];
} else if (selectable === 'multiple') {
for (; i < indices.length; i++) {
index = indices[i];
if (!$(children[index]).hasClass('k-state-selected')) {
continue;
}
for (j = 0; j < selectedIndices.length; j++) {
selectedIndex = selectedIndices[j];
if (selectedIndex === index) {
$(children[selectedIndex]).removeClass('k-state-selected');
removed.push({
position: j + removedIndices,
dataItem: dataItems.splice(j, 1)[0]
});
selectedIndices.splice(j, 1);
indices.splice(i, 1);
values.splice(j, 1);
removedIndices += 1;
i -= 1;
j -= 1;
break;
}
}
}
}
return {
indices: indices,
removed: removed
};
},
_deselectFiltered: function (indices) {
var children = this.element[0].children;
var dataItem, index, position;
var removed = [];
var idx = 0;
for (; idx < indices.length; idx++) {
index = indices[idx];
dataItem = this._view[index].item;
position = this._dataItemPosition(dataItem, this._values);
if (position > -1) {
removed.push(this.removeAt(position));
$(children[index]).removeClass('k-state-selected');
}
}
if (removed.length) {
this.trigger(CHANGE, {
added: [],
removed: removed
});
return true;
}
return false;
},
_select: function (indices) {
var that = this;
var children = that.element[0].children;
var data = that._view;
var dataItem, index;
var added = [];
var idx = 0;
if (indices[indices.length - 1] !== -1) {
that.focus(indices);
}
for (; idx < indices.length; idx++) {
index = indices[idx];
dataItem = data[index];
if (index === -1 || !dataItem) {
continue;
}
dataItem = dataItem.item;
that._selectedIndices.push(index);
that._dataItems.push(dataItem);
that._values.push(that._valueGetter(dataItem));
$(children[index]).addClass('k-state-selected').attr('aria-selected', true);
added.push({ dataItem: dataItem });
}
return added;
},
_get: function (candidate) {
if (typeof candidate === 'number') {
candidate = [candidate];
} else if (!isArray(candidate)) {
candidate = $(candidate).data('offset-index');
if (candidate === undefined) {
candidate = -1;
}
candidate = [candidate];
}
return candidate;
},
_template: function () {
var that = this;
var options = that.options;
var template = options.template;
if (!template) {
template = kendo.template('
${' + kendo.expr(options.dataTextField, 'data') + '}', { useWithBlock: false });
} else {
template = kendo.template(template);
template = function (data) {
return '
' + template(data) + '';
};
}
return template;
},
_templates: function () {
var template;
var templates = {
template: this.options.template,
groupTemplate: this.options.groupTemplate,
fixedGroupTemplate: this.options.fixedGroupTemplate
};
for (var key in templates) {
template = templates[key];
if (template && typeof template !== 'function') {
templates[key] = kendo.template(template);
}
}
this.templates = templates;
},
_normalizeIndices: function (indices) {
var newIndices = [];
var idx = 0;
for (; idx < indices.length; idx++) {
if (indices[idx] !== undefined) {
newIndices.push(indices[idx]);
}
}
return newIndices;
},
_valueIndices: function (values, indices) {
var data = this._view;
var idx = 0;
var index;
indices = indices ? indices.slice() : [];
if (!values.length) {
return [];
}
for (; idx < data.length; idx++) {
index = this._dataItemPosition(data[idx].item, values);
if (index !== -1) {
indices[index] = idx;
}
}
return this._normalizeIndices(indices);
},
_firstVisibleItem: function () {
var element = this.element[0];
var content = this.content[0];
var scrollTop = content.scrollTop;
var itemHeight = $(element.children[0]).height();
var itemIndex = Math.floor(scrollTop / itemHeight) || 0;
var item = element.children[itemIndex] || element.lastChild;
var forward = item.offsetTop < scrollTop;
while (item) {
if (forward) {
if (item.offsetTop + itemHeight > scrollTop || !item.nextSibling) {
break;
}
item = item.nextSibling;
} else {
if (item.offsetTop <= scrollTop || !item.previousSibling) {
break;
}
item = item.previousSibling;
}
}
return this._view[$(item).data('offset-index')];
},
_fixedHeader: function () {
if (this.isGrouped() && this.templates.fixedGroupTemplate) {
this.header.show();
this.content.scroll(this._onScroll);
} else {
this.header.hide();
this.content.off('scroll', this._onScroll);
}
},
_renderHeader: function () {
var template = this.templates.fixedGroupTemplate;
if (!template) {
return;
}
var visibleItem = this._firstVisibleItem();
if (visibleItem) {
this.header.html(template(visibleItem.group));
}
},
_renderItem: function (context) {
var item = '
';
item += this.templates.template(dataItem);
if (notFirstItem && context.newGroup) {
item += '' + this.templates.groupTemplate(context.group) + '
';
}
return item + '';
},
_render: function () {
var html = '';
var i = 0;
var idx = 0;
var context;
var dataContext = [];
var view = this.dataSource.view();
var values = this.value();
var group, newGroup, j;
var isGrouped = this.isGrouped();
if (isGrouped) {
for (i = 0; i < view.length; i++) {
group = view[i];
newGroup = true;
for (j = 0; j < group.items.length; j++) {
context = {
selected: this._selected(group.items[j], values),
item: group.items[j],
group: group.value,
newGroup: newGroup,
index: idx
};
dataContext[idx] = context;
idx += 1;
html += this._renderItem(context);
newGroup = false;
}
}
} else {
for (i = 0; i < view.length; i++) {
context = {
selected: this._selected(view[i], values),
item: view[i],
index: i
};
dataContext[i] = context;
html += this._renderItem(context);
}
}
this._view = dataContext;
this.element[0].innerHTML = html;
if (isGrouped && dataContext.length) {
this._renderHeader();
}
},
_selected: function (dataItem, values) {
var select = !this.isFiltered() || this.options.selectable === 'multiple';
return select && this._dataItemPosition(dataItem, values) !== -1;
},
setDSFilter: function (filter) {
this._lastDSFilter = extend({}, filter);
},
isFiltered: function () {
if (!this._lastDSFilter) {
this.setDSFilter(this.dataSource.filter());
}
return !kendo.data.Query.compareFilters(this.dataSource.filter(), this._lastDSFilter);
},
refresh: function (e) {
var that = this;
var action = e && e.action;
var skipUpdateOnBind = that.options.skipUpdateOnBind;
var isItemChange = action === 'itemchange';
var result;
that.trigger('dataBinding');
that._fixedHeader();
that._render();
that.bound(true);
if (isItemChange || action === 'remove') {
result = mapChangedItems(that._dataItems, e.items);
if (result.changed.length) {
if (isItemChange) {
that.trigger('selectedItemChange', { items: result.changed });
} else {
that.value(that._getValues(result.unchanged));
}
}
} else if (that.isFiltered() || that._skipUpdate) {
that.focus(0);
if (that._skipUpdate) {
that._skipUpdate = false;
that._selectedIndices = that._valueIndices(that._values, that._selectedIndices);
}
} else if (!skipUpdateOnBind && (!action || action === 'add')) {
that.value(that._values);
}
if (that._valueDeferred) {
that._valueDeferred.resolve();
}
that.trigger('dataBound');
},
bound: function (bound) {
if (bound === undefined) {
return this._bound;
}
this._bound = bound;
},
isGrouped: function () {
return (this.dataSource.group() || []).length;
}
});
ui.plugin(StaticList);
function mapChangedItems(selected, itemsToMatch) {
var itemsLength = itemsToMatch.length;
var selectedLength = selected.length;
var dataItem;
var found;
var i, j;
var changed = [];
var unchanged = [];
if (selectedLength) {
for (i = 0; i < selectedLength; i++) {
dataItem = selected[i];
found = false;
for (j = 0; j < itemsLength; j++) {
if (dataItem === itemsToMatch[j]) {
found = true;
changed.push({
index: i,
item: dataItem
});
break;
}
}
if (!found) {
unchanged.push(dataItem);
}
}
}
return {
changed: changed,
unchanged: unchanged
};
}
function removeFiltersForField(expression, field) {
var filters;
var found = false;
if (expression.filters) {
filters = $.grep(expression.filters, function (filter) {
found = removeFiltersForField(filter, field);
if (filter.filters) {
return filter.filters.length;
} else {
return filter.field != field;
}
});
if (!found && expression.filters.length !== filters.length) {
found = true;
}
expression.filters = filters;
}
return found;
}
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.dropdownlist', [
'kendo.list',
'kendo.mobile.scroller'
], f);
}(function () {
var __meta__ = {
id: 'dropdownlist',
name: 'DropDownList',
category: 'web',
description: 'The DropDownList widget displays a list of values and allows the selection of a single value from the list.',
depends: ['list'],
features: [
{
id: 'mobile-scroller',
name: 'Mobile scroller',
description: 'Support for kinetic scrolling in mobile device',
depends: ['mobile.scroller']
},
{
id: 'virtualization',
name: 'VirtualList',
description: 'Support for virtualization',
depends: ['virtuallist']
}
]
};
(function ($, undefined) {
var kendo = window.kendo, ui = kendo.ui, List = ui.List, Select = ui.Select, support = kendo.support, activeElement = kendo._activeElement, ObservableObject = kendo.data.ObservableObject, keys = kendo.keys, ns = '.kendoDropDownList', DISABLED = 'disabled', READONLY = 'readonly', CHANGE = 'change', FOCUSED = 'k-state-focused', DEFAULT = 'k-state-default', STATEDISABLED = 'k-state-disabled', ARIA_DISABLED = 'aria-disabled', ARIA_READONLY = 'aria-readonly', HOVEREVENTS = 'mouseenter' + ns + ' mouseleave' + ns, TABINDEX = 'tabindex', STATE_FILTER = 'filter', STATE_ACCEPT = 'accept', MSG_INVALID_OPTION_LABEL = 'The `optionLabel` option is not valid due to missing fields. Define a custom optionLabel as shown here http://docs.telerik.com/kendo-ui/api/javascript/ui/dropdownlist#configuration-optionLabel', proxy = $.proxy;
var DropDownList = Select.extend({
init: function (element, options) {
var that = this;
var index = options && options.index;
var optionLabel, text, disabled;
that.ns = ns;
options = $.isArray(options) ? { dataSource: options } : options;
Select.fn.init.call(that, element, options);
options = that.options;
element = that.element.on('focus' + ns, proxy(that._focusHandler, that));
that._focusInputHandler = $.proxy(that._focusInput, that);
that.optionLabel = $();
that._optionLabel();
that._inputTemplate();
that._reset();
that._prev = '';
that._word = '';
that._wrapper();
that._tabindex();
that.wrapper.data(TABINDEX, that.wrapper.attr(TABINDEX));
that._span();
that._popup();
that._mobile();
that._dataSource();
that._ignoreCase();
that._filterHeader();
that._aria();
that._enable();
that._oldIndex = that.selectedIndex = -1;
if (index !== undefined) {
options.index = index;
}
that._initialIndex = options.index;
that._initList();
that._cascade();
if (options.autoBind) {
that.dataSource.fetch();
} else if (that.selectedIndex === -1) {
text = options.text || '';
if (!text) {
optionLabel = options.optionLabel;
if (optionLabel && options.index === 0) {
text = optionLabel;
} else if (that._isSelect) {
text = element.children(':selected').text();
}
}
that._textAccessor(text);
}
disabled = $(that.element).parents('fieldset').is(':disabled');
if (disabled) {
that.enable(false);
}
that.listView.bind('click', function (e) {
e.preventDefault();
});
kendo.notify(that);
},
options: {
name: 'DropDownList',
enabled: true,
autoBind: true,
index: 0,
text: null,
value: null,
delay: 500,
height: 200,
dataTextField: '',
dataValueField: '',
optionLabel: '',
cascadeFrom: '',
cascadeFromField: '',
ignoreCase: true,
animation: {},
filter: 'none',
minLength: 1,
virtual: false,
template: null,
valueTemplate: null,
optionLabelTemplate: null,
groupTemplate: '#:data#',
fixedGroupTemplate: '#:data#'
},
events: [
'open',
'close',
CHANGE,
'select',
'filtering',
'dataBinding',
'dataBound',
'cascade',
'set'
],
setOptions: function (options) {
Select.fn.setOptions.call(this, options);
this.listView.setOptions(this._listOptions(options));
this._optionLabel();
this._inputTemplate();
this._accessors();
this._filterHeader();
this._enable();
this._aria();
if (!this.value() && this.hasOptionLabel()) {
this.select(0);
}
},
destroy: function () {
var that = this;
Select.fn.destroy.call(that);
that.wrapper.off(ns);
that.element.off(ns);
that._inputWrapper.off(ns);
that._arrow.off();
that._arrow = null;
that.optionLabel.off();
},
open: function () {
var that = this;
if (that.popup.visible()) {
return;
}
if (!that.listView.bound() || that._state === STATE_ACCEPT) {
that._open = true;
that._state = 'rebind';
if (that.filterInput) {
that.filterInput.val('');
that._prev = '';
}
that._filterSource();
} else if (that._allowOpening()) {
that.popup.one('activate', that._focusInputHandler);
that.popup.open();
that._focusItem();
}
},
_focusInput: function () {
this._focusElement(this.filterInput);
},
_allowOpening: function () {
return this.hasOptionLabel() || this.filterInput || this.dataSource.view().length;
},
toggle: function (toggle) {
this._toggle(toggle, true);
},
current: function (candidate) {
var current;
if (candidate === undefined) {
current = this.listView.focus();
if (!current && this.selectedIndex === 0 && this.hasOptionLabel()) {
return this.optionLabel;
}
return current;
}
this._focus(candidate);
},
dataItem: function (index) {
var that = this;
var dataItem = null;
if (index === null) {
return index;
}
if (index === undefined) {
dataItem = that.listView.selectedDataItems()[0];
} else {
if (typeof index !== 'number') {
if (that.options.virtual) {
return that.dataSource.getByUid($(index).data('uid'));
}
if (index.hasClass('k-list-optionlabel')) {
index = -1;
} else {
index = $(that.items()).index(index);
}
} else if (that.hasOptionLabel()) {
index -= 1;
}
dataItem = that.dataSource.flatView()[index];
}
if (!dataItem) {
dataItem = that._optionLabelDataItem();
}
return dataItem;
},
refresh: function () {
this.listView.refresh();
},
text: function (text) {
var that = this;
var dataItem, loweredText;
var ignoreCase = that.options.ignoreCase;
text = text === null ? '' : text;
if (text !== undefined) {
if (typeof text === 'string') {
loweredText = ignoreCase ? text.toLowerCase() : text;
that._select(function (data) {
data = that._text(data);
if (ignoreCase) {
data = (data + '').toLowerCase();
}
return data === loweredText;
});
dataItem = that.dataItem();
if (dataItem) {
text = dataItem;
}
}
that._textAccessor(text);
} else {
return that._textAccessor();
}
},
value: function (value) {
var that = this;
var listView = that.listView;
var dataSource = that.dataSource;
if (value === undefined) {
value = that._accessor() || that.listView.value()[0];
return value === undefined || value === null ? '' : value;
}
if (value || !that.hasOptionLabel()) {
that._initialIndex = null;
}
this.trigger('set', { value: value });
if (that._request && that.options.cascadeFrom && that.listView.bound()) {
if (that._valueSetter) {
dataSource.unbind(CHANGE, that._valueSetter);
}
that._valueSetter = proxy(function () {
that.value(value);
}, that);
dataSource.one(CHANGE, that._valueSetter);
return;
}
if (that._isFilterEnabled() && listView.bound() && listView.isFiltered()) {
listView.bound(false);
that._filterSource();
} else {
that._fetchData();
}
listView.value(value).done(function () {
if (that.selectedIndex === -1 && that.text()) {
that.text('');
that._accessor('', -1);
}
that._old = that._accessor();
that._oldIndex = that.selectedIndex;
});
},
hasOptionLabel: function () {
return this.optionLabel && !!this.optionLabel[0];
},
_optionLabel: function () {
var that = this;
var options = that.options;
var optionLabel = options.optionLabel;
var template = options.optionLabelTemplate;
if (!optionLabel) {
that.optionLabel.off().remove();
that.optionLabel = $();
return;
}
if (!template) {
template = '#:';
if (typeof optionLabel === 'string') {
template += 'data';
} else {
template += kendo.expr(options.dataTextField, 'data');
}
template += '#';
}
if (typeof template !== 'function') {
template = kendo.template(template);
}
that.optionLabelTemplate = template;
if (!that.hasOptionLabel()) {
that.optionLabel = $('
').prependTo(that.list);
}
that.optionLabel.html(template(optionLabel)).off().click(proxy(that._click, that)).on(HOVEREVENTS, that._toggleHover);
that.angular('compile', function () {
return {
elements: that.optionLabel,
data: [{ dataItem: that._optionLabelDataItem() }]
};
});
},
_optionLabelText: function () {
var optionLabel = this.options.optionLabel;
return typeof optionLabel === 'string' ? optionLabel : this._text(optionLabel);
},
_optionLabelDataItem: function () {
var that = this;
var optionLabel = that.options.optionLabel;
if (that.hasOptionLabel()) {
return $.isPlainObject(optionLabel) ? new ObservableObject(optionLabel) : that._assignInstance(that._optionLabelText(), '');
}
return null;
},
_buildOptions: function (data) {
var that = this;
if (!that._isSelect) {
return;
}
var value = that.listView.value()[0];
var optionLabel = that._optionLabelDataItem();
if (value === undefined || value === null) {
value = '';
}
if (optionLabel) {
optionLabel = '
';
}
that._options(data, optionLabel, value);
if (value !== List.unifyType(that._accessor(), typeof value)) {
that._customOption = null;
that._custom(value);
}
},
_listBound: function () {
var that = this;
var initialIndex = that._initialIndex;
var filtered = that._state === STATE_FILTER;
var data = that.dataSource.flatView();
var dataItem;
that._angularItems('compile');
that._presetValue = false;
that._resizePopup(true);
that.popup.position();
that._buildOptions(data);
that._makeUnselectable();
if (!filtered) {
if (that._open) {
that.toggle(that._allowOpening());
}
that._open = false;
if (!that._fetch) {
if (data.length) {
if (!that.listView.value().length && initialIndex > -1 && initialIndex !== null) {
that.select(initialIndex);
}
that._initialIndex = null;
dataItem = that.listView.selectedDataItems()[0];
if (dataItem && that.text() !== that._text(dataItem)) {
that._selectValue(dataItem);
}
} else if (that._textAccessor() !== that._optionLabelText()) {
that.listView.value('');
that._selectValue(null);
that._oldIndex = that.selectedIndex;
}
}
}
that._hideBusy();
that.trigger('dataBound');
},
_listChange: function () {
this._selectValue(this.listView.selectedDataItems()[0]);
if (this._presetValue || this._old && this._oldIndex === -1) {
this._oldIndex = this.selectedIndex;
}
},
_focusHandler: function () {
this.wrapper.focus();
},
_focusinHandler: function () {
this._inputWrapper.addClass(FOCUSED);
this._prevent = false;
},
_focusoutHandler: function () {
var that = this;
var filtered = that._state === STATE_FILTER;
var isIFrame = window.self !== window.top;
var focusedItem = that._focus();
if (!that._prevent) {
clearTimeout(that._typingTimeout);
if (filtered && focusedItem && !that.trigger('select', { item: focusedItem })) {
that._select(focusedItem, !that.dataSource.view().length);
}
if (support.mobileOS.ios && isIFrame) {
that._change();
} else {
that._blur();
}
that._inputWrapper.removeClass(FOCUSED);
that._prevent = true;
that._open = false;
that.element.blur();
}
},
_wrapperMousedown: function () {
this._prevent = !!this.filterInput;
},
_wrapperClick: function (e) {
e.preventDefault();
this.popup.unbind('activate', this._focusInputHandler);
this._focused = this.wrapper;
this._toggle();
},
_editable: function (options) {
var that = this;
var element = that.element;
var disable = options.disable;
var readonly = options.readonly;
var wrapper = that.wrapper.add(that.filterInput).off(ns);
var dropDownWrapper = that._inputWrapper.off(HOVEREVENTS);
if (!readonly && !disable) {
element.removeAttr(DISABLED).removeAttr(READONLY);
dropDownWrapper.addClass(DEFAULT).removeClass(STATEDISABLED).on(HOVEREVENTS, that._toggleHover);
wrapper.attr(TABINDEX, wrapper.data(TABINDEX)).attr(ARIA_DISABLED, false).attr(ARIA_READONLY, false).on('keydown' + ns, proxy(that._keydown, that)).on('focusin' + ns, proxy(that._focusinHandler, that)).on('focusout' + ns, proxy(that._focusoutHandler, that)).on('mousedown' + ns, proxy(that._wrapperMousedown, that));
that.wrapper.on('click' + ns, proxy(that._wrapperClick, that));
if (!that.filterInput) {
wrapper.on('keypress' + ns, proxy(that._keypress, that));
}
} else if (disable) {
wrapper.removeAttr(TABINDEX);
dropDownWrapper.addClass(STATEDISABLED).removeClass(DEFAULT);
} else {
dropDownWrapper.addClass(DEFAULT).removeClass(STATEDISABLED);
wrapper.on('focusin' + ns, proxy(that._focusinHandler, that)).on('focusout' + ns, proxy(that._focusoutHandler, that));
}
element.attr(DISABLED, disable).attr(READONLY, readonly);
wrapper.attr(ARIA_DISABLED, disable).attr(ARIA_READONLY, readonly);
},
_keydown: function (e) {
var that = this;
var key = e.keyCode;
var altKey = e.altKey;
var isInputActive;
var handled;
var isPopupVisible = that.popup.visible();
if (that.filterInput) {
isInputActive = that.filterInput[0] === activeElement();
}
if (key === keys.LEFT) {
key = keys.UP;
handled = true;
} else if (key === keys.RIGHT) {
key = keys.DOWN;
handled = true;
}
if (handled && isInputActive) {
return;
}
e.keyCode = key;
if (altKey && key === keys.UP || key === keys.ESC) {
that._focusElement(that.wrapper);
}
if (key === keys.ENTER && that._typingTimeout && that.filterInput && isPopupVisible) {
e.preventDefault();
return;
}
handled = that._move(e);
if (handled) {
return;
}
if (!isPopupVisible || !that.filterInput) {
if (key === keys.HOME) {
handled = true;
that._firstItem();
} else if (key === keys.END) {
handled = true;
that._lastItem();
}
if (handled) {
that._select(that._focus());
e.preventDefault();
}
}
if (!altKey && !handled && that.filterInput) {
that._search();
}
},
_matchText: function (text, word) {
var ignoreCase = this.options.ignoreCase;
if (text === undefined || text === null) {
return false;
}
text = text + '';
if (ignoreCase) {
text = text.toLowerCase();
}
return text.indexOf(word) === 0;
},
_shuffleData: function (data, splitIndex) {
var optionDataItem = this._optionLabelDataItem();
if (optionDataItem) {
data = [optionDataItem].concat(data);
}
return data.slice(splitIndex).concat(data.slice(0, splitIndex));
},
_selectNext: function () {
var that = this;
var data = that.dataSource.flatView();
var dataLength = data.length + (that.hasOptionLabel() ? 1 : 0);
var isInLoop = sameCharsOnly(that._word, that._last);
var startIndex = that.selectedIndex;
var oldFocusedItem;
var text;
if (startIndex === -1) {
startIndex = 0;
} else {
startIndex += isInLoop ? 1 : 0;
startIndex = normalizeIndex(startIndex, dataLength);
}
data = data.toJSON ? data.toJSON() : data.slice();
data = that._shuffleData(data, startIndex);
for (var idx = 0; idx < dataLength; idx++) {
text = that._text(data[idx]);
if (isInLoop && that._matchText(text, that._last)) {
break;
} else if (that._matchText(text, that._word)) {
break;
}
}
if (idx !== dataLength) {
oldFocusedItem = that._focus();
that._select(normalizeIndex(startIndex + idx, dataLength));
if (that.trigger('select', { item: that._focus() })) {
that._select(oldFocusedItem);
}
if (!that.popup.visible()) {
that._change();
}
}
},
_keypress: function (e) {
var that = this;
if (e.which === 0 || e.keyCode === kendo.keys.ENTER) {
return;
}
var character = String.fromCharCode(e.charCode || e.keyCode);
if (that.options.ignoreCase) {
character = character.toLowerCase();
}
if (character === ' ') {
e.preventDefault();
}
that._word += character;
that._last = character;
that._search();
},
_popupOpen: function () {
var popup = this.popup;
popup.wrapper = kendo.wrap(popup.element);
if (popup.element.closest('.km-root')[0]) {
popup.wrapper.addClass('km-popup km-widget');
this.wrapper.addClass('km-widget');
}
},
_popup: function () {
Select.fn._popup.call(this);
this.popup.one('open', proxy(this._popupOpen, this));
},
_click: function (e) {
var item = e.item || $(e.currentTarget);
e.preventDefault();
if (this.trigger('select', { item: item })) {
this.close();
return;
}
this._userTriggered = true;
this._select(item);
this._focusElement(this.wrapper);
this._blur();
},
_focusElement: function (element) {
var active = activeElement();
var wrapper = this.wrapper;
var filterInput = this.filterInput;
var compareElement = element === filterInput ? wrapper : filterInput;
var touchEnabled = support.mobileOS && (support.touch || support.MSPointers || support.pointers);
if (filterInput && filterInput[0] === element[0] && touchEnabled) {
return;
}
if (filterInput && compareElement[0] === active) {
this._prevent = true;
this._focused = element.focus();
}
},
_filter: function (word) {
if (word) {
var that = this;
var ignoreCase = that.options.ignoreCase;
if (ignoreCase) {
word = word.toLowerCase();
}
that._select(function (dataItem) {
return that._matchText(that._text(dataItem), word);
});
}
},
_search: function () {
var that = this;
var dataSource = that.dataSource;
clearTimeout(that._typingTimeout);
if (that._isFilterEnabled()) {
that._typingTimeout = setTimeout(function () {
var value = that.filterInput.val();
if (that._prev !== value) {
that._prev = value;
that.search(value);
}
that._typingTimeout = null;
}, that.options.delay);
} else {
that._typingTimeout = setTimeout(function () {
that._word = '';
}, that.options.delay);
if (!that.listView.bound()) {
dataSource.fetch().done(function () {
that._selectNext();
});
return;
}
that._selectNext();
}
},
_get: function (candidate) {
var data, found, idx;
var isFunction = typeof candidate === 'function';
var jQueryCandidate = !isFunction ? $(candidate) : $();
if (this.hasOptionLabel()) {
if (typeof candidate === 'number') {
if (candidate > -1) {
candidate -= 1;
}
} else if (jQueryCandidate.hasClass('k-list-optionlabel')) {
candidate = -1;
}
}
if (isFunction) {
data = this.dataSource.flatView();
for (idx = 0; idx < data.length; idx++) {
if (candidate(data[idx])) {
candidate = idx;
found = true;
break;
}
}
if (!found) {
candidate = -1;
}
}
return candidate;
},
_firstItem: function () {
if (this.hasOptionLabel()) {
this._focus(this.optionLabel);
} else {
this.listView.focusFirst();
}
},
_lastItem: function () {
this._resetOptionLabel();
this.listView.focusLast();
},
_nextItem: function () {
if (this.optionLabel.hasClass('k-state-focused')) {
this._resetOptionLabel();
this.listView.focusFirst();
} else {
this.listView.focusNext();
}
},
_prevItem: function () {
if (this.optionLabel.hasClass('k-state-focused')) {
return;
}
this.listView.focusPrev();
if (!this.listView.focus()) {
this._focus(this.optionLabel);
}
},
_focusItem: function () {
var listView = this.listView;
var focusedItem = listView.focus();
var index = listView.select();
index = index[index.length - 1];
if (index === undefined && this.options.highlightFirst && !focusedItem) {
index = 0;
}
if (index !== undefined) {
listView.focus(index);
} else {
if (this.options.optionLabel) {
this._focus(this.optionLabel);
this._select(this.optionLabel);
} else {
listView.scrollToIndex(0);
}
}
},
_resetOptionLabel: function (additionalClass) {
this.optionLabel.removeClass('k-state-focused' + (additionalClass || '')).removeAttr('id');
},
_focus: function (candidate) {
var listView = this.listView;
var optionLabel = this.optionLabel;
if (candidate === undefined) {
candidate = listView.focus();
if (!candidate && optionLabel.hasClass('k-state-focused')) {
candidate = optionLabel;
}
return candidate;
}
this._resetOptionLabel();
candidate = this._get(candidate);
listView.focus(candidate);
if (candidate === -1) {
optionLabel.addClass('k-state-focused').attr('id', listView._optionID);
this._focused.add(this.filterInput).removeAttr('aria-activedescendant').attr('aria-activedescendant', listView._optionID);
}
},
_select: function (candidate, keepState) {
var that = this;
candidate = that._get(candidate);
that.listView.select(candidate);
if (!keepState && that._state === STATE_FILTER) {
that._state = STATE_ACCEPT;
}
if (candidate === -1) {
that._selectValue(null);
}
},
_selectValue: function (dataItem) {
var that = this;
var optionLabel = that.options.optionLabel;
var idx = that.listView.select();
var value = '';
var text = '';
idx = idx[idx.length - 1];
if (idx === undefined) {
idx = -1;
}
this._resetOptionLabel(' k-state-selected');
if (dataItem) {
text = dataItem;
value = that._dataValue(dataItem);
if (optionLabel) {
idx += 1;
}
} else if (optionLabel) {
that._focus(that.optionLabel.addClass('k-state-selected'));
text = that._optionLabelText();
if (typeof optionLabel === 'string') {
value = '';
} else {
value = that._value(optionLabel);
}
idx = 0;
}
that.selectedIndex = idx;
if (value === null) {
value = '';
}
that._textAccessor(text);
that._accessor(value, idx);
that._triggerCascade();
},
_mobile: function () {
var that = this, popup = that.popup, mobileOS = support.mobileOS, root = popup.element.parents('.km-root').eq(0);
if (root.length && mobileOS) {
popup.options.animation.open.effects = mobileOS.android || mobileOS.meego ? 'fadeIn' : mobileOS.ios || mobileOS.wp ? 'slideIn:up' : popup.options.animation.open.effects;
}
},
_filterHeader: function () {
var icon;
if (this.filterInput) {
this.filterInput.off(ns).parent().remove();
this.filterInput = null;
}
if (this._isFilterEnabled()) {
icon = '
select';
this.filterInput = $('
').attr({
placeholder: this.element.attr('placeholder'),
role: 'listbox',
'aria-haspopup': true,
'aria-expanded': false
});
this.list.prepend($('
').append(this.filterInput.add(icon)));
}
},
_span: function () {
var that = this, wrapper = that.wrapper, SELECTOR = 'span.k-input', span;
span = wrapper.find(SELECTOR);
if (!span[0]) {
wrapper.append('
select').append(that.element);
span = wrapper.find(SELECTOR);
}
that.span = span;
that._inputWrapper = $(wrapper[0].firstChild);
that._arrow = wrapper.find('.k-icon');
},
_wrapper: function () {
var that = this, element = that.element, DOMelement = element[0], wrapper;
wrapper = element.parent();
if (!wrapper.is('span.k-widget')) {
wrapper = element.wrap('
').parent();
wrapper[0].style.cssText = DOMelement.style.cssText;
wrapper[0].title = DOMelement.title;
}
element.hide();
that._focused = that.wrapper = wrapper.addClass('k-widget k-dropdown k-header').addClass(DOMelement.className).css('display', '').attr({
accesskey: element.attr('accesskey'),
unselectable: 'on',
role: 'listbox',
'aria-haspopup': true,
'aria-expanded': false
});
},
_clearSelection: function (parent) {
this.select(parent.value() ? 0 : -1);
},
_inputTemplate: function () {
var that = this, template = that.options.valueTemplate;
if (!template) {
template = $.proxy(kendo.template('#:this._text(data)#', { useWithBlock: false }), that);
} else {
template = kendo.template(template);
}
that.valueTemplate = template;
if (that.hasOptionLabel() && !that.options.optionLabelTemplate) {
try {
that.valueTemplate(that._optionLabelDataItem());
} catch (e) {
throw new Error(MSG_INVALID_OPTION_LABEL);
}
}
},
_textAccessor: function (text) {
var dataItem = null;
var template = this.valueTemplate;
var options = this.options;
var optionLabel = options.optionLabel;
var span = this.span;
if (text !== undefined) {
if ($.isPlainObject(text) || text instanceof ObservableObject) {
dataItem = text;
} else if (optionLabel && this._optionLabelText() === text) {
dataItem = optionLabel;
template = this.optionLabelTemplate;
}
if (!dataItem) {
dataItem = this._assignInstance(text, this._accessor());
}
var getElements = function () {
return {
elements: span.get(),
data: [{ dataItem: dataItem }]
};
};
this.angular('cleanup', getElements);
try {
span.html(template(dataItem));
} catch (e) {
span.html('');
}
this.angular('compile', getElements);
} else {
return span.text();
}
},
_preselect: function (value, text) {
if (!value && !text) {
text = this._optionLabelText();
}
this._accessor(value);
this._textAccessor(text);
this._old = this._accessor();
this._oldIndex = this.selectedIndex;
this.listView.setValue(value);
this._initialIndex = null;
this._presetValue = true;
},
_assignInstance: function (text, value) {
var dataTextField = this.options.dataTextField;
var dataItem = {};
if (dataTextField) {
assign(dataItem, dataTextField.split('.'), text);
assign(dataItem, this.options.dataValueField.split('.'), value);
dataItem = new ObservableObject(dataItem);
} else {
dataItem = text;
}
return dataItem;
}
});
function assign(instance, fields, value) {
var idx = 0, lastIndex = fields.length - 1, field;
for (; idx < lastIndex; ++idx) {
field = fields[idx];
if (!(field in instance)) {
instance[field] = {};
}
instance = instance[field];
}
instance[fields[lastIndex]] = value;
}
function normalizeIndex(index, length) {
if (index >= length) {
index -= length;
}
return index;
}
function sameCharsOnly(word, character) {
for (var idx = 0; idx < word.length; idx++) {
if (word.charAt(idx) !== character) {
return false;
}
}
return true;
}
ui.plugin(DropDownList);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('dataviz/diagram/dom', [
'kendo.data',
'kendo.draganddrop',
'kendo.toolbar',
'kendo.editable',
'kendo.window',
'kendo.dropdownlist',
'kendo.dataviz.core',
'kendo.dataviz.themes',
'dataviz/diagram/svg',
'dataviz/diagram/services',
'dataviz/diagram/layout'
], f);
}(function () {
(function ($, undefined) {
var dataviz = kendo.dataviz, draw = kendo.drawing, geom = kendo.geometry, diagram = dataviz.diagram, Widget = kendo.ui.Widget, Class = kendo.Class, proxy = $.proxy, deepExtend = kendo.deepExtend, extend = $.extend, HierarchicalDataSource = kendo.data.HierarchicalDataSource, Canvas = diagram.Canvas, Group = diagram.Group, Rectangle = diagram.Rectangle, Circle = diagram.Circle, CompositeTransform = diagram.CompositeTransform, Rect = diagram.Rect, Path = diagram.Path, DeleteShapeUnit = diagram.DeleteShapeUnit, DeleteConnectionUnit = diagram.DeleteConnectionUnit, TextBlock = diagram.TextBlock, Image = diagram.Image, Point = diagram.Point, Intersect = diagram.Intersect, ConnectionEditAdorner = diagram.ConnectionEditAdorner, UndoRedoService = diagram.UndoRedoService, ToolService = diagram.ToolService, Selector = diagram.Selector, ResizingAdorner = diagram.ResizingAdorner, ConnectorsAdorner = diagram.ConnectorsAdorner, Cursors = diagram.Cursors, Utils = diagram.Utils, Observable = kendo.Observable, ToBackUnit = diagram.ToBackUnit, ToFrontUnit = diagram.ToFrontUnit, PolylineRouter = diagram.PolylineRouter, CascadingRouter = diagram.CascadingRouter, isUndefined = Utils.isUndefined, isDefined = Utils.isDefined, defined = kendo.util.defined, isArray = $.isArray, isFunction = kendo.isFunction, isString = Utils.isString, isPlainObject = $.isPlainObject, math = Math;
var NS = '.kendoDiagram', CASCADING = 'cascading', ITEMBOUNDSCHANGE = 'itemBoundsChange', CHANGE = 'change', CLICK = 'click', DRAG = 'drag', DRAG_END = 'dragEnd', DRAG_START = 'dragStart', MOUSE_ENTER = 'mouseEnter', MOUSE_LEAVE = 'mouseLeave', ERROR = 'error', AUTO = 'Auto', TOP = 'Top', RIGHT = 'Right', LEFT = 'Left', BOTTOM = 'Bottom', MAXINT = 9007199254740992, SELECT = 'select', ITEMROTATE = 'itemRotate', PAN = 'pan', ZOOM_START = 'zoomStart', ZOOM_END = 'zoomEnd', NONE = 'none', DEFAULT_CANVAS_WIDTH = 600, DEFAULT_CANVAS_HEIGHT = 600, DEFAULT_SHAPE_TYPE = 'rectangle', DEFAULT_SHAPE_WIDTH = 100, DEFAULT_SHAPE_HEIGHT = 100, DEFAULT_SHAPE_MINWIDTH = 20, DEFAULT_SHAPE_MINHEIGHT = 20, DEFAULT_SHAPE_POSITION = 0, DEFAULT_CONNECTION_BACKGROUND = 'Yellow', MAX_VALUE = Number.MAX_VALUE, MIN_VALUE = -Number.MAX_VALUE, ABSOLUTE = 'absolute', TRANSFORMED = 'transformed', ROTATED = 'rotated', TRANSPARENT = 'transparent', WIDTH = 'width', HEIGHT = 'height', X = 'x', Y = 'y', MOUSEWHEEL_NS = 'DOMMouseScroll' + NS + ' mousewheel' + NS, MOBILE_ZOOM_RATE = 0.05, MOBILE_PAN_DISTANCE = 5, BUTTON_TEMPLATE = '
#=text#', CONNECTION_CONTENT_OFFSET = 5;
diagram.DefaultConnectors = [
{ name: TOP },
{ name: BOTTOM },
{ name: LEFT },
{ name: RIGHT },
{
name: AUTO,
position: function (shape) {
return shape.getPosition('center');
}
}
];
var defaultButtons = {
cancel: {
text: 'Cancel',
imageClass: 'k-cancel',
className: 'k-diagram-cancel',
iconClass: 'k-icon'
},
update: {
text: 'Update',
imageClass: 'k-update',
className: 'k-diagram-update',
iconClass: 'k-icon'
}
};
diagram.shapeDefaults = function (extra) {
var defaults = {
type: DEFAULT_SHAPE_TYPE,
path: '',
autoSize: true,
visual: null,
x: DEFAULT_SHAPE_POSITION,
y: DEFAULT_SHAPE_POSITION,
minWidth: DEFAULT_SHAPE_MINWIDTH,
minHeight: DEFAULT_SHAPE_MINHEIGHT,
width: DEFAULT_SHAPE_WIDTH,
height: DEFAULT_SHAPE_HEIGHT,
hover: {},
editable: {
connect: true,
tools: []
},
connectors: diagram.DefaultConnectors,
rotation: { angle: 0 }
};
Utils.simpleExtend(defaults, extra);
return defaults;
};
function mwDelta(e) {
var origEvent = e.originalEvent, delta = 0;
if (origEvent.wheelDelta) {
delta = -origEvent.wheelDelta / 40;
delta = delta > 0 ? math.ceil(delta) : math.floor(delta);
} else if (origEvent.detail) {
delta = origEvent.detail;
}
return delta;
}
function isAutoConnector(connector) {
return connector.options.name.toLowerCase() === AUTO.toLowerCase();
}
function closestConnector(point, shape) {
var minimumDistance = MAXINT, resCtr, ctrs = shape.connectors;
for (var i = 0; i < ctrs.length; i++) {
var ctr = ctrs[i];
if (!isAutoConnector(ctr)) {
var dist = point.distanceTo(ctr.position());
if (dist < minimumDistance) {
minimumDistance = dist;
resCtr = ctr;
}
}
}
return resCtr;
}
function indicesOfItems(group, visuals) {
var i, indices = [], visual;
var children = group.drawingContainer().children;
var length = children.length;
for (i = 0; i < visuals.length; i++) {
visual = visuals[i];
for (var j = 0; j < length; j++) {
if (children[j] == visual.drawingContainer()) {
indices.push(j);
break;
}
}
}
return indices;
}
var DiagramElement = Observable.extend({
init: function (options) {
var that = this;
that.dataItem = (options || {}).dataItem;
Observable.fn.init.call(that);
that.options = deepExtend({ id: diagram.randomId() }, that.options, options);
that.isSelected = false;
that.visual = new Group({
id: that.options.id,
autoSize: that.options.autoSize
});
that.id = that.options.id;
that._template();
},
options: {
hover: {},
cursor: Cursors.grip,
content: { align: 'center middle' },
selectable: true,
serializable: true,
enable: true
},
_getCursor: function (point) {
if (this.adorner) {
return this.adorner._getCursor(point);
}
return this.options.cursor;
},
visible: function (value) {
if (isUndefined(value)) {
return this.visual.visible();
} else {
this.visual.visible(value);
}
},
bounds: function () {
},
refresh: function () {
this.visual.redraw();
},
position: function (point) {
this.options.x = point.x;
this.options.y = point.y;
this.visual.position(point);
},
toString: function () {
return this.options.id;
},
serialize: function () {
var json = deepExtend({}, { options: this.options });
if (this.dataItem) {
json.dataItem = this.dataItem.toString();
}
return json;
},
_content: function (content) {
if (content !== undefined) {
var options = this.options;
if (diagram.Utils.isString(content)) {
options.content.text = content;
} else {
deepExtend(options.content, content);
}
var contentOptions = options.content;
var contentVisual = this._contentVisual;
if (!contentVisual) {
this._createContentVisual(contentOptions);
} else {
this._updateContentVisual(contentOptions);
}
}
return this.options.content.text;
},
_createContentVisual: function (options) {
if (options.text) {
this._contentVisual = new TextBlock(options);
this._contentVisual._includeInBBox = false;
this.visual.append(this._contentVisual);
}
},
_updateContentVisual: function (options) {
this._contentVisual.redraw(options);
},
_hitTest: function (point) {
var bounds = this.bounds();
return this.visible() && bounds.contains(point) && this.options.enable;
},
_template: function () {
var that = this;
if (that.options.content.template) {
var data = that.dataItem || {}, elementTemplate = kendo.template(that.options.content.template, { paramName: 'dataItem' });
that.options.content.text = elementTemplate(data);
}
},
_canSelect: function () {
return this.options.selectable !== false;
},
toJSON: function () {
return { id: this.options.id };
}
});
var Connector = Class.extend({
init: function (shape, options) {
this.options = deepExtend({}, this.options, options);
this.connections = [];
this.shape = shape;
},
options: {
width: 7,
height: 7,
fill: { color: DEFAULT_CONNECTION_BACKGROUND },
hover: {}
},
position: function () {
if (this.options.position) {
return this.options.position(this.shape);
} else {
return this.shape.getPosition(this.options.name);
}
},
toJSON: function () {
return {
shapeId: this.shape.toString(),
connector: this.options.name
};
}
});
Connector.parse = function (diagram, str) {
var tempStr = str.split(':'), id = tempStr[0], name = tempStr[1] || AUTO;
for (var i = 0; i < diagram.shapes.length; i++) {
var shape = diagram.shapes[i];
if (shape.options.id == id) {
return shape.getConnector(name.trim());
}
}
};
var Shape = DiagramElement.extend({
init: function (options, diagram) {
var that = this;
DiagramElement.fn.init.call(that, options);
this.diagram = diagram;
this.updateOptionsFromModel();
options = that.options;
that.connectors = [];
that.type = options.type;
that.createShapeVisual();
that.updateBounds();
that.content(that.content());
that._createConnectors();
},
options: diagram.shapeDefaults(),
_setOptionsFromModel: function (model) {
var modelOptions = filterShapeDataItem(model || this.dataItem);
this.options = deepExtend({}, this.options, modelOptions);
this.redrawVisual();
if (this.options.content) {
this._template();
this.content(this.options.content);
}
},
updateOptionsFromModel: function (model, field) {
if (this.diagram && this.diagram._isEditable) {
var modelOptions = filterShapeDataItem(model || this.dataItem);
if (model && field) {
if (!dataviz.inArray(field, [
'x',
'y',
'width',
'height'
])) {
if (this.options.visual) {
this.redrawVisual();
} else if (modelOptions.type) {
this.options = deepExtend({}, this.options, modelOptions);
this.redrawVisual();
}
if (this.options.content) {
this._template();
this.content(this.options.content);
}
} else {
var bounds = this.bounds();
bounds[field] = model[field];
this.bounds(bounds);
}
} else {
this.options = deepExtend({}, this.options, modelOptions);
}
}
},
redrawVisual: function () {
this.visual.clear();
this._contentVisual = null;
this.options.dataItem = this.dataItem;
this.createShapeVisual();
this.updateBounds();
},
updateModel: function (syncChanges) {
var diagram = this.diagram;
if (diagram && diagram._isEditable) {
var bounds = this._bounds;
var model = this.dataItem;
if (model) {
diagram._suspendModelRefresh();
if (defined(model.x) && bounds.x !== model.x) {
model.set('x', bounds.x);
}
if (defined(model.y) && bounds.y !== model.y) {
model.set('y', bounds.y);
}
if (defined(model.width) && bounds.width !== model.width) {
model.set('width', bounds.width);
}
if (defined(model.height) && bounds.height !== model.height) {
model.set('height', bounds.height);
}
this.dataItem = model;
diagram._resumeModelRefresh();
if (syncChanges) {
diagram._syncShapeChanges();
}
}
}
},
updateBounds: function () {
var bounds = this.visual._measure(true);
var options = this.options;
this.bounds(new Rect(options.x, options.y, bounds.width, bounds.height));
this._rotate();
this._alignContent();
},
content: function (content) {
var result = this._content(content);
this._alignContent();
return result;
},
_alignContent: function () {
var contentOptions = this.options.content || {};
var contentVisual = this._contentVisual;
if (contentVisual && contentOptions.align) {
var containerRect = this.visual._measure();
var aligner = new diagram.RectAlign(containerRect);
var contentBounds = contentVisual.drawingElement.bbox(null);
var contentRect = new Rect(0, 0, contentBounds.width(), contentBounds.height());
var alignedBounds = aligner.align(contentRect, contentOptions.align);
contentVisual.position(alignedBounds.topLeft());
}
},
_createConnectors: function () {
var options = this.options, length = options.connectors.length, connectorDefaults = options.connectorDefaults, connector, i;
for (i = 0; i < length; i++) {
connector = new Connector(this, deepExtend({}, connectorDefaults, options.connectors[i]));
this.connectors.push(connector);
}
},
bounds: function (value) {
var bounds;
if (value) {
if (isString(value)) {
switch (value) {
case TRANSFORMED:
bounds = this._transformedBounds();
break;
case ABSOLUTE:
bounds = this._transformedBounds();
var pan = this.diagram._pan;
bounds.x += pan.x;
bounds.y += pan.y;
break;
case ROTATED:
bounds = this._rotatedBounds();
break;
default:
bounds = this._bounds;
}
} else {
this._setBounds(value);
this._triggerBoundsChange();
if (!(this.diagram && this.diagram._layouting)) {
this.refreshConnections();
}
}
} else {
bounds = this._bounds;
}
return bounds;
},
_setBounds: function (rect) {
var options = this.options;
var topLeft = rect.topLeft();
var x = options.x = topLeft.x;
var y = options.y = topLeft.y;
var width = options.width = math.max(rect.width, options.minWidth);
var height = options.height = math.max(rect.height, options.minHeight);
this._bounds = new Rect(x, y, width, height);
this.visual.redraw({
x: x,
y: y,
width: width,
height: height
});
},
position: function (point) {
if (point) {
this.bounds(new Rect(point.x, point.y, this._bounds.width, this._bounds.height));
} else {
return this._bounds.topLeft();
}
},
clone: function () {
var json = this.serialize();
json.options.id = diagram.randomId();
if (this.diagram && this.diagram._isEditable && defined(this.dataItem)) {
json.options.dataItem = cloneDataItem(this.dataItem);
}
return new Shape(json.options);
},
select: function (value) {
var diagram = this.diagram, selected, deselected;
if (isUndefined(value)) {
value = true;
}
if (this._canSelect()) {
if (this.isSelected != value) {
selected = [];
deselected = [];
this.isSelected = value;
if (this.isSelected) {
diagram._selectedItems.push(this);
selected.push(this);
} else {
Utils.remove(diagram._selectedItems, this);
deselected.push(this);
}
if (!diagram._internalSelection) {
diagram._selectionChanged(selected, deselected);
}
return true;
}
}
},
rotate: function (angle, center, undoable) {
var rotate = this.visual.rotate();
if (angle !== undefined) {
if (undoable !== false && this.diagram && this.diagram.undoRedoService && angle !== rotate.angle) {
this.diagram.undoRedoService.add(new diagram.RotateUnit(this.diagram._resizingAdorner, [this], [rotate.angle]), false);
}
var b = this.bounds(), sc = new Point(b.width / 2, b.height / 2), deltaAngle, newPosition;
if (center) {
deltaAngle = angle - rotate.angle;
newPosition = b.center().rotate(center, 360 - deltaAngle).minus(sc);
this._rotationOffset = this._rotationOffset.plus(newPosition.minus(b.topLeft()));
this.position(newPosition);
}
this.visual.rotate(angle, sc);
this.options.rotation.angle = angle;
if (this.diagram && this.diagram._connectorsAdorner) {
this.diagram._connectorsAdorner.refresh();
}
this.refreshConnections();
if (this.diagram) {
this.diagram.trigger(ITEMROTATE, { item: this });
}
}
return rotate;
},
connections: function (type) {
var result = [], i, j, con, cons, ctr;
for (i = 0; i < this.connectors.length; i++) {
ctr = this.connectors[i];
cons = ctr.connections;
for (j = 0, cons; j < cons.length; j++) {
con = cons[j];
if (type == 'out') {
var source = con.source();
if (source.shape && source.shape == this) {
result.push(con);
}
} else if (type == 'in') {
var target = con.target();
if (target.shape && target.shape == this) {
result.push(con);
}
} else {
result.push(con);
}
}
}
return result;
},
refreshConnections: function () {
$.each(this.connections(), function () {
this.refresh();
});
},
getConnector: function (nameOrPoint) {
var i, ctr;
if (isString(nameOrPoint)) {
nameOrPoint = nameOrPoint.toLocaleLowerCase();
for (i = 0; i < this.connectors.length; i++) {
ctr = this.connectors[i];
if (ctr.options.name.toLocaleLowerCase() == nameOrPoint) {
return ctr;
}
}
} else if (nameOrPoint instanceof Point) {
return closestConnector(nameOrPoint, this);
} else {
return this.connectors.length ? this.connectors[0] : null;
}
},
getPosition: function (side) {
var b = this.bounds(), fnName = side.charAt(0).toLowerCase() + side.slice(1);
if (isFunction(b[fnName])) {
return this._transformPoint(b[fnName]());
}
return b.center();
},
redraw: function (options) {
if (options) {
var shapeOptions = this.options;
var boundsChange;
this.shapeVisual.redraw(this._visualOptions(options));
if (this._diffNumericOptions(options, [
WIDTH,
HEIGHT,
X,
Y
])) {
this.bounds(new Rect(shapeOptions.x, shapeOptions.y, shapeOptions.width, shapeOptions.height));
boundsChange = true;
}
if (options.connectors) {
shapeOptions.connectors = options.connectors;
this._updateConnectors();
}
shapeOptions = deepExtend(shapeOptions, options);
if (options.rotation || boundsChange) {
this._rotate();
}
if (shapeOptions.content) {
this.content(shapeOptions.content);
}
}
},
_updateConnectors: function () {
var connections = this.connections();
this.connectors = [];
this._createConnectors();
var connection;
var source;
var target;
for (var idx = 0; idx < connections.length; idx++) {
connection = connections[idx];
source = connection.source();
target = connection.target();
if (source.shape && source.shape === this) {
connection.source(this.getConnector(source.options.name) || null);
} else if (target.shape && target.shape === this) {
connection.target(this.getConnector(target.options.name) || null);
}
connection.updateModel();
}
},
_diffNumericOptions: diagram.diffNumericOptions,
_visualOptions: function (options) {
return {
data: options.path,
source: options.source,
hover: options.hover,
fill: options.fill,
stroke: options.stroke
};
},
_triggerBoundsChange: function () {
if (this.diagram) {
this.diagram.trigger(ITEMBOUNDSCHANGE, {
item: this,
bounds: this._bounds.clone()
});
}
},
_transformPoint: function (point) {
var rotate = this.rotate(), bounds = this.bounds(), tl = bounds.topLeft();
if (rotate.angle) {
point.rotate(rotate.center().plus(tl), 360 - rotate.angle);
}
return point;
},
_transformedBounds: function () {
var bounds = this.bounds(), tl = bounds.topLeft(), br = bounds.bottomRight();
return Rect.fromPoints(this.diagram.modelToView(tl), this.diagram.modelToView(br));
},
_rotatedBounds: function () {
var bounds = this.bounds().rotatedBounds(this.rotate().angle), tl = bounds.topLeft(), br = bounds.bottomRight();
return Rect.fromPoints(tl, br);
},
_rotate: function () {
var rotation = this.options.rotation;
if (rotation && rotation.angle) {
this.rotate(rotation.angle);
}
this._rotationOffset = new Point();
},
_hover: function (value) {
var options = this.options, hover = options.hover, stroke = options.stroke, fill = options.fill;
if (value && isDefined(hover.stroke)) {
stroke = deepExtend({}, stroke, hover.stroke);
}
if (value && isDefined(hover.fill)) {
fill = hover.fill;
}
this.shapeVisual.redraw({
stroke: stroke,
fill: fill
});
if (options.editable && options.editable.connect) {
this.diagram._showConnectors(this, value);
}
},
_hitTest: function (value) {
if (this.visible()) {
var bounds = this.bounds(), rotatedPoint, angle = this.rotate().angle;
if (value.isEmpty && !value.isEmpty()) {
return Intersect.rects(value, bounds, angle ? angle : 0);
} else {
rotatedPoint = value.clone().rotate(bounds.center(), angle);
if (bounds.contains(rotatedPoint)) {
return this;
}
}
}
},
toJSON: function () {
return { shapeId: this.options.id };
},
createShapeVisual: function () {
var options = this.options;
var visualOptions = this._visualOptions(options);
var visualTemplate = options.visual;
var type = (options.type + '').toLocaleLowerCase();
var shapeVisual;
visualOptions.width = options.width;
visualOptions.height = options.height;
if (isFunction(visualTemplate)) {
shapeVisual = visualTemplate.call(this, options);
} else if (visualOptions.data) {
shapeVisual = new Path(visualOptions);
translateToOrigin(shapeVisual);
} else if (type == 'rectangle') {
shapeVisual = new Rectangle(visualOptions);
} else if (type == 'circle') {
shapeVisual = new Circle(visualOptions);
} else if (type == 'text') {
shapeVisual = new TextBlock(visualOptions);
} else if (type == 'image') {
shapeVisual = new Image(visualOptions);
} else {
shapeVisual = new Path(visualOptions);
}
this.shapeVisual = shapeVisual;
this.visual.append(this.shapeVisual);
}
});
var Connection = DiagramElement.extend({
init: function (from, to, options) {
var that = this;
DiagramElement.fn.init.call(that, options);
this.updateOptionsFromModel();
this._initRouter();
that.path = new diagram.Polyline(that.options);
that.path.fill(TRANSPARENT);
that.visual.append(that.path);
that._sourcePoint = that._targetPoint = new Point();
that._setSource(from);
that._setTarget(to);
that.content(that.options.content);
that.definers = [];
if (defined(options) && options.points) {
that.points(options.points);
}
},
options: {
hover: { stroke: {} },
startCap: NONE,
endCap: NONE,
points: [],
selectable: true,
fromConnector: AUTO,
toConenctor: AUTO
},
_setOptionsFromModel: function (model) {
this.updateOptionsFromModel(model || this.dataItem);
},
updateOptionsFromModel: function (model) {
if (this.diagram && this.diagram._isEditable) {
var dataMap = this.diagram._dataMap;
var options = filterConnectionDataItem(model || this.dataItem);
if (model) {
if (defined(options.from)) {
var from = dataMap[options.from];
if (from && defined(options.fromConnector)) {
from = from.getConnector(options.fromConnector);
}
this.source(from);
} else if (defined(options.fromX) && defined(options.fromY)) {
this.source(new Point(options.fromX, options.fromY));
}
if (defined(options.to)) {
var to = dataMap[options.to];
if (to && defined(options.toConnector)) {
to = to.getConnector(options.toConnector);
}
this.target(to);
} else if (defined(options.toX) && defined(options.toY)) {
this.target(new Point(options.toX, options.toY));
}
if (defined(options.type) && this.type() !== options.type) {
this.points([]);
this.type(options.type);
}
this.dataItem = model;
this._template();
this.redraw(this.options);
} else {
this.options = deepExtend({}, options, this.options);
}
}
},
updateModel: function (syncChanges) {
if (this.diagram && this.diagram._isEditable) {
if (this.diagram.connectionsDataSource) {
var model = this.diagram.connectionsDataSource.getByUid(this.dataItem.uid);
if (model) {
this.diagram._suspendModelRefresh();
if (defined(this.options.fromX) && this.options.fromX !== null) {
clearField('from', model);
clearField('fromConnector', model);
model.set('fromX', this.options.fromX);
model.set('fromY', this.options.fromY);
} else {
model.set('from', this.options.from);
if (defined(model.fromConnector)) {
model.set('fromConnector', this.sourceConnector ? this.sourceConnector.options.name : null);
}
clearField('fromX', model);
clearField('fromY', model);
}
if (defined(this.options.toX) && this.options.toX !== null) {
clearField('to', model);
clearField('toConnector', model);
model.set('toX', this.options.toX);
model.set('toY', this.options.toY);
} else {
model.set('to', this.options.to);
if (defined(model.toConnector)) {
model.set('toConnector', this.targetConnector ? this.targetConnector.options.name : null);
}
clearField('toX', model);
clearField('toY', model);
}
if (defined(this.options.type) && defined(model.type)) {
model.set('type', this.options.type);
}
this.dataItem = model;
this.diagram._resumeModelRefresh();
if (syncChanges) {
this.diagram._syncConnectionChanges();
}
}
}
}
},
sourcePoint: function () {
return this._resolvedSourceConnector ? this._resolvedSourceConnector.position() : this._sourcePoint;
},
_setSource: function (source) {
var shapeSource = source instanceof Shape;
var defaultConnector = this.options.fromConnector || AUTO;
var dataItem;
if (shapeSource && !source.getConnector(defaultConnector)) {
return;
}
if (source !== undefined) {
this.from = source;
}
this._removeFromSourceConnector();
if (source === null) {
if (this.sourceConnector) {
this._sourcePoint = (this._resolvedSourceConnector || this.sourceConnector).position();
this._clearSourceConnector();
this._setFromOptions(null, this._sourcePoint);
}
} else if (source instanceof Connector) {
dataItem = source.shape.dataItem;
if (dataItem) {
this._setFromOptions(dataItem.id);
}
this.sourceConnector = source;
this.sourceConnector.connections.push(this);
} else if (source instanceof Point) {
this._setFromOptions(null, source);
this._sourcePoint = source;
if (this.sourceConnector) {
this._clearSourceConnector();
}
} else if (shapeSource) {
dataItem = source.dataItem;
if (dataItem) {
this._setFromOptions(dataItem.id);
}
this.sourceConnector = source.getConnector(defaultConnector);
this.sourceConnector.connections.push(this);
}
},
source: function (source, undoable) {
if (isDefined(source)) {
if (undoable && this.diagram) {
this.diagram.undoRedoService.addCompositeItem(new diagram.ConnectionEditUnit(this, source));
}
this._setSource(source);
this.refresh();
}
return this.sourceConnector ? this.sourceConnector : this._sourcePoint;
},
_setFromOptions: function (from, fromPoint) {
this.options.from = from;
if (fromPoint) {
this.options.fromX = fromPoint.x;
this.options.fromY = fromPoint.y;
} else {
this.options.fromX = null;
this.options.fromY = null;
}
},
sourceDefiner: function (value) {
if (value) {
if (value instanceof diagram.PathDefiner) {
value.left = null;
this._sourceDefiner = value;
this.source(value.point);
} else {
throw 'The sourceDefiner needs to be a PathDefiner.';
}
} else {
if (!this._sourceDefiner) {
this._sourceDefiner = new diagram.PathDefiner(this.sourcePoint(), null, null);
}
return this._sourceDefiner;
}
},
targetPoint: function () {
return this._resolvedTargetConnector ? this._resolvedTargetConnector.position() : this._targetPoint;
},
_setTarget: function (target) {
var shapeTarget = target instanceof Shape;
var defaultConnector = this.options.toConnector || AUTO;
var dataItem;
if (shapeTarget && !target.getConnector(defaultConnector)) {
return;
}
if (target !== undefined) {
this.to = target;
}
this._removeFromTargetConnector();
if (target === null) {
if (this.targetConnector) {
this._targetPoint = (this._resolvedTargetConnector || this.targetConnector).position();
this._clearTargetConnector();
this._setToOptions(null, this._targetPoint);
}
} else if (target instanceof Connector) {
dataItem = target.shape.dataItem;
if (dataItem) {
this._setToOptions(dataItem.id);
}
this.targetConnector = target;
this.targetConnector.connections.push(this);
} else if (target instanceof Point) {
this._setToOptions(null, target);
this._targetPoint = target;
if (this.targetConnector) {
this._clearTargetConnector();
}
} else if (shapeTarget) {
dataItem = target.dataItem;
if (dataItem) {
this._setToOptions(dataItem.id);
}
this.targetConnector = target.getConnector(defaultConnector);
this.targetConnector.connections.push(this);
}
},
target: function (target, undoable) {
if (isDefined(target)) {
if (undoable && this.diagram) {
this.diagram.undoRedoService.addCompositeItem(new diagram.ConnectionEditUnit(this, undefined, target));
}
this._setTarget(target);
this.refresh();
}
return this.targetConnector ? this.targetConnector : this._targetPoint;
},
_setToOptions: function (to, toPoint) {
this.options.to = to;
if (toPoint) {
this.options.toX = toPoint.x;
this.options.toY = toPoint.y;
} else {
this.options.toX = null;
this.options.toY = null;
}
},
targetDefiner: function (value) {
if (value) {
if (value instanceof diagram.PathDefiner) {
value.right = null;
this._targetDefiner = value;
this.target(value.point);
} else {
throw 'The sourceDefiner needs to be a PathDefiner.';
}
} else {
if (!this._targetDefiner) {
this._targetDefiner = new diagram.PathDefiner(this.targetPoint(), null, null);
}
return this._targetDefiner;
}
},
_updateConnectors: function () {
this._updateConnector(this.source(), 'source');
this._updateConnector(this.target(), 'target');
},
_updateConnector: function (instance, name) {
var that = this;
var diagram = that.diagram;
if (instance instanceof Connector && !diagram.getShapeById(instance.shape.id)) {
var dataItem = instance.shape.dataItem;
var connectorName = instance.options.name;
var setNewTarget = function () {
var shape = diagram._dataMap[dataItem.id];
instance = shape.getConnector(connectorName);
that[name](instance, false);
that.updateModel();
};
if (diagram._dataMap[dataItem.id]) {
setNewTarget();
} else {
var inactiveItem = diagram._inactiveShapeItems.getByUid(dataItem.uid);
if (inactiveItem) {
diagram._deferredConnectionUpdates.push(inactiveItem.onActivate(setNewTarget));
}
}
} else {
that[name](instance, false);
}
},
content: function (content) {
var result = this._content(content);
if (defined(content)) {
this._alignContent();
}
return result;
},
_createContentVisual: function (options) {
var visual;
if (isFunction(options.visual)) {
visual = options.visual.call(this, options);
} else if (options.text) {
visual = new TextBlock(options);
}
if (visual) {
this._contentVisual = visual;
visual._includeInBBox = false;
this.visual.append(visual);
}
return visual;
},
_updateContentVisual: function (options) {
if (isFunction(options.visual)) {
this.visual.remove(this._contentVisual);
this._createContentVisual(options);
} else {
this._contentVisual.redraw(options);
}
},
_alignContent: function () {
if (this._contentVisual) {
var offset = CONNECTION_CONTENT_OFFSET;
var points = this.allPoints();
var endIdx = math.floor(points.length / 2);
var startIdx = endIdx - 1;
while (startIdx > 0 && points[startIdx].equals(points[endIdx])) {
startIdx--;
endIdx++;
}
var endPoint = points[endIdx];
var startPoint = points[startIdx];
var boundingBox = this._contentVisual._measure();
var width = boundingBox.width;
var height = boundingBox.height;
var alignToPath = points.length % 2 === 0;
var distance = startPoint.distanceTo(endPoint);
if (alignToPath && points.length > 2 && distance > 0 && (startPoint.y === endPoint.y && distance < width || startPoint.x === endPoint.x && distance < height)) {
alignToPath = false;
offset = 0;
}
var point;
if (alignToPath) {
var angle = kendo.util.deg(math.atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x));
point = new Point((endPoint.x - startPoint.x) / 2 + startPoint.x, (endPoint.y - startPoint.y) / 2 + startPoint.y);
if (math.abs(angle) === 90) {
point.x += offset;
point.y -= height / 2;
} else if (angle % 180 === 0) {
point.x -= width / 2;
point.y -= height + offset;
} else if (angle < -90 || 0 < angle && angle < 90) {
point.y -= height;
} else if (angle < 0 || angle > 90) {
point.x -= width;
point.y -= height;
}
} else {
var midIdx = math.floor(points.length / 2);
point = points[midIdx].clone();
startPoint = points[midIdx - 1];
endPoint = points[midIdx + 1];
var offsetX = startPoint.x <= point.x && endPoint.x <= point.x ? offset : -boundingBox.width - offset;
var offsetY = startPoint.y <= point.y && endPoint.y <= point.y ? offset : -boundingBox.height - offset;
point.x += offsetX;
point.y += offsetY;
}
this._contentVisual.position(point);
}
},
select: function (value) {
var diagram = this.diagram, selected, deselected;
if (this._canSelect()) {
if (this.isSelected !== value) {
this.isSelected = value;
selected = [];
deselected = [];
if (this.isSelected) {
this.adorner = new ConnectionEditAdorner(this, this.options.selection);
diagram._adorn(this.adorner, true);
diagram._selectedItems.push(this);
selected.push(this);
} else {
if (this.adorner) {
diagram._adorn(this.adorner, false);
Utils.remove(diagram._selectedItems, this);
this.adorner = undefined;
deselected.push(this);
}
}
if (this.adorner) {
this.adorner.refresh();
}
if (!diagram._internalSelection) {
diagram._selectionChanged(selected, deselected);
}
return true;
}
}
},
bounds: function (value) {
if (value && !isString(value)) {
this._bounds = value;
} else {
return this._bounds;
}
},
type: function (value) {
var options = this.options;
if (value) {
if (value !== options.type) {
options.type = value;
this._initRouter();
this.refresh();
}
} else {
return options.type;
}
},
_initRouter: function () {
var type = (this.options.type || '').toLowerCase();
if (type == CASCADING) {
this._router = new CascadingRouter(this);
} else {
this._router = new PolylineRouter(this);
}
},
points: function (value) {
if (value) {
this.definers = [];
for (var i = 0; i < value.length; i++) {
var definition = value[i];
if (definition instanceof diagram.Point) {
this.definers.push(new diagram.PathDefiner(definition));
} else if (definition.hasOwnProperty('x') && definition.hasOwnProperty('y')) {
this.definers.push(new diagram.PathDefiner(new Point(definition.x, definition.y)));
} else {
throw 'A Connection point needs to be a Point or an object with x and y properties.';
}
}
} else {
var pts = [];
if (isDefined(this.definers)) {
for (var k = 0; k < this.definers.length; k++) {
pts.push(this.definers[k].point);
}
}
return pts;
}
},
allPoints: function () {
var pts = [this.sourcePoint()];
if (this.definers) {
for (var k = 0; k < this.definers.length; k++) {
pts.push(this.definers[k].point);
}
}
pts.push(this.targetPoint());
return pts;
},
refresh: function () {
this._resolveConnectors();
this._refreshPath();
this._alignContent();
if (this.adorner) {
this.adorner.refresh();
}
},
_resolveConnectors: function () {
var connection = this, sourcePoint, targetPoint, source = connection.source(), target = connection.target(), autoSourceShape, autoTargetShape;
if (source instanceof Point) {
sourcePoint = source;
} else if (source instanceof Connector) {
if (isAutoConnector(source)) {
autoSourceShape = source.shape;
} else {
connection._resolvedSourceConnector = source;
sourcePoint = source.position();
}
}
if (target instanceof Point) {
targetPoint = target;
} else if (target instanceof Connector) {
if (isAutoConnector(target)) {
autoTargetShape = target.shape;
} else {
connection._resolvedTargetConnector = target;
targetPoint = target.position();
}
}
if (sourcePoint) {
if (autoTargetShape) {
connection._resolvedTargetConnector = closestConnector(sourcePoint, autoTargetShape);
}
} else if (autoSourceShape) {
if (targetPoint) {
connection._resolvedSourceConnector = closestConnector(targetPoint, autoSourceShape);
} else if (autoTargetShape) {
this._resolveAutoConnectors(autoSourceShape, autoTargetShape);
}
}
},
_resolveAutoConnectors: function (autoSourceShape, autoTargetShape) {
var minNonConflict = MAXINT;
var minDist = MAXINT;
var sourceConnectors = autoSourceShape.connectors;
var targetConnectors;
var minNonConflictSource, minNonConflictTarget;
var sourcePoint, targetPoint;
var minSource, minTarget;
var sourceConnector, targetConnector;
var sourceIdx, targetIdx;
var dist;
for (sourceIdx = 0; sourceIdx < sourceConnectors.length; sourceIdx++) {
sourceConnector = sourceConnectors[sourceIdx];
if (!isAutoConnector(sourceConnector)) {
sourcePoint = sourceConnector.position();
targetConnectors = autoTargetShape.connectors;
for (targetIdx = 0; targetIdx < targetConnectors.length; targetIdx++) {
targetConnector = targetConnectors[targetIdx];
if (!isAutoConnector(targetConnector)) {
targetPoint = targetConnector.position();
dist = math.round(sourcePoint.distanceTo(targetPoint));
if (dist < minNonConflict && this.diagram && this._testRoutePoints(sourcePoint, targetPoint, sourceConnector, targetConnector)) {
minNonConflict = dist;
minNonConflictSource = sourceConnector;
minNonConflictTarget = targetConnector;
}
if (dist < minDist) {
minSource = sourceConnector;
minTarget = targetConnector;
minDist = dist;
}
}
}
}
}
if (minNonConflictSource) {
minSource = minNonConflictSource;
minTarget = minNonConflictTarget;
}
this._resolvedSourceConnector = minSource;
this._resolvedTargetConnector = minTarget;
},
_testRoutePoints: function (sourcePoint, targetPoint, sourceConnector, targetConnector) {
var router = this._router;
var passRoute = true;
if (router instanceof CascadingRouter) {
var points = router.routePoints(sourcePoint, targetPoint, sourceConnector, targetConnector), start, end, rect;
points.unshift(sourcePoint);
points.push(targetPoint);
for (var idx = 1; idx < points.length; idx++) {
start = points[idx - 1];
end = points[idx];
rect = new Rect(math.min(start.x, end.x), math.min(start.y, end.y), math.abs(start.x - end.x), math.abs(start.y - end.y));
if (rect.width > 0) {
rect.x++;
rect.width -= 2;
}
if (rect.height > 0) {
rect.y++;
rect.height -= 2;
}
if (!rect.isEmpty() && this.diagram._shapesQuadTree.hitTestRect(rect)) {
passRoute = false;
break;
}
}
}
return passRoute;
},
redraw: function (options) {
if (options) {
this.options = deepExtend({}, this.options, options);
var points = this.options.points;
if (defined(points) && points.length > 0) {
this.points(points);
this._refreshPath();
}
if (options && options.content || options.text) {
this.content(options.content);
}
this.path.redraw({
fill: options.fill,
stroke: options.stroke,
startCap: options.startCap,
endCap: options.endCap
});
}
},
clone: function () {
var json = this.serialize();
if (this.diagram && this.diagram._isEditable && defined(this.dataItem)) {
json.options.dataItem = cloneDataItem(this.dataItem);
}
return new Connection(this.from, this.to, json.options);
},
serialize: function () {
var from = this.from.toJSON ? this.from.toJSON : this.from.toString(), to = this.to.toJSON ? this.to.toJSON : this.to.toString();
var json = deepExtend({}, {
options: this.options,
from: from,
to: to
});
if (defined(this.dataItem)) {
json.dataItem = this.dataItem.toString();
}
json.options.points = this.points();
return json;
},
_hitTest: function (value) {
if (this.visible()) {
var p = new Point(value.x, value.y), from = this.sourcePoint(), to = this.targetPoint();
if (value.isEmpty && !value.isEmpty() && value.contains(from) && value.contains(to)) {
return this;
}
if (this._router.hitTest(p)) {
return this;
}
}
},
_hover: function (value) {
var color = (this.options.stroke || {}).color;
if (value && isDefined(this.options.hover.stroke.color)) {
color = this.options.hover.stroke.color;
}
this.path.redraw({ stroke: { color: color } });
},
_refreshPath: function () {
if (!defined(this.path)) {
return;
}
this._drawPath();
this.bounds(this._router.getBounds());
},
_drawPath: function () {
if (this._router) {
this._router.route();
}
var source = this.sourcePoint();
var target = this.targetPoint();
var points = this.points();
this.path.redraw({ points: [source].concat(points, [target]) });
},
_clearSourceConnector: function () {
this.sourceConnector = undefined;
this._resolvedSourceConnector = undefined;
},
_clearTargetConnector: function () {
this.targetConnector = undefined;
this._resolvedTargetConnector = undefined;
},
_removeFromSourceConnector: function () {
if (this.sourceConnector) {
Utils.remove(this.sourceConnector.connections, this);
}
},
_removeFromTargetConnector: function () {
if (this.targetConnector) {
Utils.remove(this.targetConnector.connections, this);
}
},
toJSON: function () {
var connection = this;
var from, to, point;
if (connection.from && connection.from.toJSON) {
from = connection.from.toJSON();
} else {
point = connection._sourcePoint;
from = {
x: point.x,
y: point.y
};
}
if (connection.to && connection.to.toJSON) {
to = connection.to.toJSON();
} else {
point = connection._targetPoint;
to = {
x: point.x,
y: point.y
};
}
return {
from: from,
to: to
};
}
});
var Diagram = Widget.extend({
init: function (element, userOptions) {
var that = this;
kendo.destroy(element);
Widget.fn.init.call(that, element, userOptions);
that._initTheme();
that._initElements();
that._extendLayoutOptions(that.options);
that._initDefaults(userOptions);
that._initCanvas();
that.mainLayer = new Group({ id: 'main-layer' });
that.canvas.append(that.mainLayer);
that._shapesQuadTree = new ShapesQuadTree(that);
that._pan = new Point();
that._adorners = [];
that.adornerLayer = new Group({ id: 'adorner-layer' });
that.canvas.append(that.adornerLayer);
that._createHandlers();
that._initialize();
that._fetchFreshData();
that._createGlobalToolBar();
that._resizingAdorner = new ResizingAdorner(that, { editable: that.options.editable });
that._connectorsAdorner = new ConnectorsAdorner(that);
that._adorn(that._resizingAdorner, true);
that._adorn(that._connectorsAdorner, true);
that.selector = new Selector(that);
that._clipboard = [];
that.pauseMouseHandlers = false;
that._createOptionElements();
that.zoom(that.options.zoom);
that.canvas.draw();
},
options: {
name: 'Diagram',
theme: 'default',
layout: '',
zoomRate: 0.1,
zoom: 1,
zoomMin: 0,
zoomMax: 2,
dataSource: {},
draggable: true,
template: '',
autoBind: true,
editable: {
rotate: {},
resize: {},
text: true,
tools: [],
drag: {
snap: {
size: 10,
angle: 10
}
},
remove: true
},
pannable: { key: 'ctrl' },
selectable: { key: 'none' },
tooltip: {
enabled: true,
format: '{0}'
},
copy: {
enabled: true,
offsetX: 20,
offsetY: 20
},
shapeDefaults: diagram.shapeDefaults({ undoable: true }),
connectionDefaults: {
editable: { tools: [] },
type: CASCADING
},
shapes: [],
connections: []
},
events: [
ZOOM_END,
ZOOM_START,
PAN,
SELECT,
ITEMROTATE,
ITEMBOUNDSCHANGE,
CHANGE,
CLICK,
MOUSE_ENTER,
MOUSE_LEAVE,
'toolBarClick',
'save',
'cancel',
'edit',
'remove',
'add',
'dataBound',
DRAG_START,
DRAG,
DRAG_END
],
items: function () {
return $();
},
_createGlobalToolBar: function () {
var editable = this.options.editable;
if (editable) {
var tools = editable.tools;
if (this._isEditable && tools !== false && (!tools || tools.length === 0)) {
tools = [
'createShape',
'undo',
'redo',
'rotateClockwise',
'rotateAnticlockwise'
];
}
if (tools && tools.length) {
this.toolBar = new DiagramToolBar(this, {
tools: tools || {},
click: proxy(this._toolBarClick, this),
modal: false
});
this.toolBar.element.css({ textAlign: 'left' });
this.element.prepend(this.toolBar.element);
this._resize();
}
}
},
createShape: function () {
if (this.editor && this.editor.end() || !this.editor) {
var dataSource = this.dataSource;
var view = dataSource.view() || [];
var index = view.length;
var model = createModel(dataSource, {});
var shape = this._createShape(model, {});
if (!this.trigger('add', { shape: shape })) {
dataSource.insert(index, model);
var inactiveItem = this._inactiveShapeItems.getByUid(model.uid);
inactiveItem.element = shape;
this.edit(shape);
}
}
},
_createShape: function (dataItem, options) {
options = deepExtend({}, this.options.shapeDefaults, options);
options.dataItem = dataItem;
var shape = new Shape(options, this);
return shape;
},
createConnection: function () {
if (this.editor && this.editor.end() || !this.editor) {
var connectionsDataSource = this.connectionsDataSource;
var view = connectionsDataSource.view() || [];
var index = view.length;
var model = createModel(connectionsDataSource, {});
var connection = this._createConnection(model);
if (!this.trigger('add', { connection: connection })) {
this._connectionsDataMap[model.uid] = connection;
connectionsDataSource.insert(index, model);
this.addConnection(connection, false);
this.edit(connection);
}
}
},
_createConnection: function (dataItem, source, target) {
var options = deepExtend({}, this.options.connectionDefaults);
options.dataItem = dataItem;
var connection = new Connection(source || new Point(), target || new Point(), options);
return connection;
},
editModel: function (dataItem, editorType) {
this.cancelEdit();
var editors, template;
var editable = this.options.editable;
if (editorType == 'shape') {
editors = editable.shapeEditors;
template = editable.shapeTemplate;
} else if (editorType == 'connection') {
var connectionSelectorHandler = proxy(connectionSelector, this);
editors = deepExtend({}, {
from: connectionSelectorHandler,
to: connectionSelectorHandler
}, editable.connectionEditors);
template = editable.connectionTemplate;
} else {
return;
}
this.editor = new PopupEditor(this.element, {
update: proxy(this._update, this),
cancel: proxy(this._cancel, this),
model: dataItem,
type: editorType,
target: this,
editors: editors,
template: template
});
this.trigger('edit', this._editArgs());
},
edit: function (item) {
if (item.dataItem) {
var editorType = item instanceof Shape ? 'shape' : 'connection';
this.editModel(item.dataItem, editorType);
}
},
cancelEdit: function () {
if (this.editor) {
this._getEditDataSource().cancelChanges(this.editor.model);
this._destroyEditor();
}
},
saveEdit: function () {
if (this.editor && this.editor.end() && !this.trigger('save', this._editArgs())) {
this._getEditDataSource().sync();
}
},
_update: function () {
if (this.editor && this.editor.end() && !this.trigger('save', this._editArgs())) {
this._getEditDataSource().sync();
this._destroyEditor();
}
},
_cancel: function () {
if (this.editor && !this.trigger('cancel', this._editArgs())) {
var model = this.editor.model;
this._getEditDataSource().cancelChanges(model);
var element = this._connectionsDataMap[model.uid] || this._dataMap[model.id];
if (element) {
element._setOptionsFromModel(model);
}
this._destroyEditor();
}
},
_getEditDataSource: function () {
return this.editor.options.type === 'shape' ? this.dataSource : this.connectionsDataSource;
},
_editArgs: function () {
var result = { container: this.editor.wrapper };
result[this.editor.options.type] = this.editor.model;
return result;
},
_destroyEditor: function () {
if (this.editor) {
this.editor.close();
this.editor = null;
}
},
_initElements: function () {
this.wrapper = this.element.empty().css('position', 'relative').attr('tabindex', 0).addClass('k-widget k-diagram');
this.scrollable = $('
').appendTo(this.element);
},
_initDefaults: function (userOptions) {
var options = this.options;
var editable = options.editable;
var shapeDefaults = options.shapeDefaults;
var connectionDefaults = options.connectionDefaults;
var userShapeDefaults = (userOptions || {}).shapeDefaults;
if (editable === false) {
shapeDefaults.editable = false;
connectionDefaults.editable = false;
} else {
copyDefaultOptions(editable, shapeDefaults.editable, [
'drag',
'remove',
'connect'
]);
copyDefaultOptions(editable, connectionDefaults.editable, [
'drag',
'remove'
]);
}
if (userShapeDefaults && userShapeDefaults.connectors) {
options.shapeDefaults.connectors = userShapeDefaults.connectors;
}
},
_initCanvas: function () {
var canvasContainer = $('
').appendTo(this.scrollable)[0];
var viewPort = this.viewport();
this.canvas = new Canvas(canvasContainer, {
width: viewPort.width || DEFAULT_CANVAS_WIDTH,
height: viewPort.height || DEFAULT_CANVAS_HEIGHT
});
},
_createHandlers: function () {
var that = this;
var element = that.element;
element.on(MOUSEWHEEL_NS, proxy(that._wheel, that));
if (!kendo.support.touch && !kendo.support.mobileOS) {
that.toolService = new ToolService(that);
this.scroller.wrapper.on('mousemove' + NS, proxy(that._mouseMove, that)).on('mouseup' + NS, proxy(that._mouseUp, that)).on('mousedown' + NS, proxy(that._mouseDown, that)).on('mouseover' + NS, proxy(that._mouseover, that)).on('mouseout' + NS, proxy(that._mouseout, that));
element.on('keydown' + NS, proxy(that._keydown, that));
} else {
that._userEvents = new kendo.UserEvents(element, {
multiTouch: true,
tap: proxy(that._tap, that)
});
that._userEvents.bind([
'gesturestart',
'gesturechange',
'gestureend'
], {
gesturestart: proxy(that._gestureStart, that),
gesturechange: proxy(that._gestureChange, that),
gestureend: proxy(that._gestureEnd, that)
});
that.toolService = new ToolService(that);
if (that.options.pannable !== false) {
that.scroller.enable();
}
}
this._syncHandler = proxy(that._syncChanges, that);
that._resizeHandler = proxy(that.resize, that, false);
kendo.onResize(that._resizeHandler);
this.bind(ZOOM_START, proxy(that._destroyToolBar, that));
this.bind(PAN, proxy(that._destroyToolBar, that));
},
_tap: function (e) {
var toolService = this.toolService;
var p = this._caculateMobilePosition(e);
toolService._updateHoveredItem(p);
if (toolService.hoveredItem) {
var item = toolService.hoveredItem;
if (this.options.selectable !== false) {
this._destroyToolBar();
if (item.isSelected) {
item.select(false);
} else {
this.select(item, { addToSelection: true });
}
this._createToolBar();
}
this.trigger('click', {
item: item,
point: p
});
}
},
_caculateMobilePosition: function (e) {
return this.documentToModel(Point(e.x.location, e.y.location));
},
_gestureStart: function (e) {
this._destroyToolBar();
this.scroller.disable();
var initialCenter = this.documentToModel(new Point(e.center.x, e.center.y));
var eventArgs = {
point: initialCenter,
zoom: this.zoom()
};
if (this.trigger(ZOOM_START, eventArgs)) {
return;
}
this._gesture = e;
this._initialCenter = initialCenter;
},
_gestureChange: function (e) {
var previousGesture = this._gesture;
var initialCenter = this._initialCenter;
var center = this.documentToView(new Point(e.center.x, e.center.y));
var scaleDelta = e.distance / previousGesture.distance;
var zoom = this._zoom;
var updateZoom = false;
if (math.abs(scaleDelta - 1) >= MOBILE_ZOOM_RATE) {
this._zoom = zoom = this._getValidZoom(zoom * scaleDelta);
this.options.zoom = zoom;
this._gesture = e;
updateZoom = true;
}
var zoomedPoint = initialCenter.times(zoom);
var pan = center.minus(zoomedPoint);
if (updateZoom || this._pan.distanceTo(pan) >= MOBILE_PAN_DISTANCE) {
this._panTransform(pan);
this._updateAdorners();
}
e.preventDefault();
},
_gestureEnd: function () {
if (this.options.pannable !== false) {
this.scroller.enable();
}
this.trigger(ZOOM_END, {
point: this._initialCenter,
zoom: this.zoom()
});
},
_resize: function () {
var viewport = this.viewport();
if (this.canvas) {
this.canvas.size(viewport);
}
if (this.scrollable && this.toolBar) {
this.scrollable.height(viewport.height);
}
},
_mouseover: function (e) {
var node = e.target._kendoNode;
if (node && node.srcElement._hover) {
node.srcElement._hover(true, node.srcElement);
}
},
_mouseout: function (e) {
var node = e.target._kendoNode;
if (node && node.srcElement._hover) {
node.srcElement._hover(false, node.srcElement);
}
},
_initTheme: function () {
var that = this, themes = dataviz.ui.themes || {}, themeName = ((that.options || {}).theme || '').toLowerCase(), themeOptions = (themes[themeName] || {}).diagram;
that.options = deepExtend({}, themeOptions, that.options);
if (that.options.editable === true) {
deepExtend(that.options, { editable: (themeOptions || {}).editable });
}
},
_createOptionElements: function () {
var options = this.options;
var shapesLength = options.shapes.length;
if (shapesLength) {
this._createShapes();
}
if (options.connections.length) {
this._createConnections();
}
if (shapesLength && options.layout) {
this.layout(options.layout);
}
},
_createShapes: function () {
var that = this, options = that.options, shapes = options.shapes, shape, i;
for (i = 0; i < shapes.length; i++) {
shape = shapes[i];
that.addShape(shape);
}
},
_createConnections: function () {
var diagram = this, options = diagram.options, defaults = options.connectionDefaults, connections = options.connections, conn, source, target, i;
for (i = 0; i < connections.length; i++) {
conn = connections[i];
source = diagram._findConnectionTarget(conn.from);
target = diagram._findConnectionTarget(conn.to);
diagram.connect(source, target, deepExtend({}, defaults, conn));
}
},
_findConnectionTarget: function (options) {
var diagram = this;
var shapeId = isString(options) ? options : options.shapeId || options.id;
var target;
if (shapeId) {
target = diagram.getShapeById(shapeId);
if (options.connector) {
target = target.getConnector(options.connector);
}
} else {
target = new Point(options.x || 0, options.y || 0);
}
return target;
},
destroy: function () {
var that = this;
Widget.fn.destroy.call(that);
if (this._userEvents) {
this._userEvents.destroy();
}
kendo.unbindResize(that._resizeHandler);
that.clear();
that.element.off(NS);
that.scroller.wrapper.off(NS);
that.canvas.destroy(true);
that.canvas = undefined;
that._destroyEditor();
that.destroyScroller();
that._destroyGlobalToolBar();
that._destroyToolBar();
},
destroyScroller: function () {
var scroller = this.scroller;
if (!scroller) {
return;
}
scroller.destroy();
scroller.element.remove();
this.scroller = null;
},
save: function () {
var json = {
shapes: [],
connections: []
};
var i, connection, shape;
for (i = 0; i < this.shapes.length; i++) {
shape = this.shapes[i];
if (shape.options.serializable) {
json.shapes.push(shape.options);
}
}
for (i = 0; i < this.connections.length; i++) {
connection = this.connections[i];
json.connections.push(deepExtend({}, connection.options, connection.toJSON()));
}
return json;
},
focus: function () {
if (!this.element.is(kendo._activeElement())) {
var element = this.element, scrollContainer = element[0], containers = [], offsets = [], documentElement = document.documentElement, i;
do {
scrollContainer = scrollContainer.parentNode;
if (scrollContainer.scrollHeight > scrollContainer.clientHeight) {
containers.push(scrollContainer);
offsets.push(scrollContainer.scrollTop);
}
} while (scrollContainer != documentElement);
element.focus();
for (i = 0; i < containers.length; i++) {
containers[i].scrollTop = offsets[i];
}
}
},
load: function (options) {
this.clear();
this.setOptions(options);
this._createShapes();
this._createConnections();
},
setOptions: function (options) {
deepExtend(this.options, options);
},
clear: function () {
var that = this;
that.select(false);
that.mainLayer.clear();
that._shapesQuadTree.clear();
that._initialize();
},
connect: function (source, target, options) {
var connection;
if (this.connectionsDataSource && this._isEditable) {
var dataItem = this.connectionsDataSource.add({});
connection = this._connectionsDataMap[dataItem.uid];
connection.source(source);
connection.target(target);
connection.redraw(options);
connection.updateModel();
} else {
connection = new Connection(source, target, deepExtend({}, this.options.connectionDefaults, options));
this.addConnection(connection);
}
return connection;
},
connected: function (source, target) {
for (var i = 0; i < this.connections.length; i++) {
var c = this.connections[i];
if (c.from == source && c.to == target) {
return true;
}
}
return false;
},
addConnection: function (connection, undoable) {
if (undoable !== false) {
this.undoRedoService.add(new diagram.AddConnectionUnit(connection, this), false);
}
connection.diagram = this;
connection._setOptionsFromModel();
connection.refresh();
this.mainLayer.append(connection.visual);
this.connections.push(connection);
this.trigger(CHANGE, {
added: [connection],
removed: []
});
return connection;
},
_addConnection: function (connection, undoable) {
var connectionsDataSource = this.connectionsDataSource;
var dataItem;
if (connectionsDataSource && this._isEditable) {
dataItem = createModel(connectionsDataSource, cloneDataItem(connection.dataItem));
connection.dataItem = dataItem;
connection.updateModel();
if (!this.trigger('add', { connection: connection })) {
this._connectionsDataMap[dataItem.uid] = connection;
connectionsDataSource.add(dataItem);
this.addConnection(connection, undoable);
connection._updateConnectors();
return connection;
}
} else if (!this.trigger('add', { connection: connection })) {
this.addConnection(connection, undoable);
connection._updateConnectors();
return connection;
}
},
addShape: function (item, undoable) {
var shape, shapeDefaults = this.options.shapeDefaults;
if (item instanceof Shape) {
shape = item;
} else if (!(item instanceof kendo.Class)) {
shapeDefaults = deepExtend({}, shapeDefaults, item || {});
shape = new Shape(shapeDefaults, this);
} else {
return;
}
if (undoable !== false) {
this.undoRedoService.add(new diagram.AddShapeUnit(shape, this), false);
}
this.shapes.push(shape);
if (shape.diagram !== this) {
this._shapesQuadTree.insert(shape);
shape.diagram = this;
}
this.mainLayer.append(shape.visual);
this.trigger(CHANGE, {
added: [shape],
removed: []
});
return shape;
},
_addShape: function (shape, undoable) {
var that = this;
var dataSource = that.dataSource;
var dataItem;
if (dataSource && this._isEditable) {
dataItem = createModel(dataSource, cloneDataItem(shape.dataItem));
shape.dataItem = dataItem;
shape.updateModel();
if (!this.trigger('add', { shape: shape })) {
this.dataSource.add(dataItem);
var inactiveItem = this._inactiveShapeItems.getByUid(dataItem.uid);
inactiveItem.element = shape;
inactiveItem.undoable = undoable;
return shape;
}
} else if (!this.trigger('add', { shape: shape })) {
return this.addShape(shape, undoable);
}
},
remove: function (items, undoable) {
items = isArray(items) ? items.slice(0) : [items];
var elements = splitDiagramElements(items);
var shapes = elements.shapes;
var connections = elements.connections;
var i;
if (!defined(undoable)) {
undoable = true;
}
if (undoable) {
this.undoRedoService.begin();
}
this._suspendModelRefresh();
for (i = shapes.length - 1; i >= 0; i--) {
this._removeItem(shapes[i], undoable, connections);
}
for (i = connections.length - 1; i >= 0; i--) {
this._removeItem(connections[i], undoable);
}
this._resumeModelRefresh();
if (undoable) {
this.undoRedoService.commit(false);
}
this.trigger(CHANGE, {
added: [],
removed: items
});
},
_removeShapeDataItem: function (item) {
if (this._isEditable) {
this.dataSource.remove(item.dataItem);
delete this._dataMap[item.dataItem.id];
}
},
_removeConnectionDataItem: function (item) {
if (this._isEditable) {
this.connectionsDataSource.remove(item.dataItem);
delete this._connectionsDataMap[item.dataItem.uid];
}
},
_triggerRemove: function (items) {
var toRemove = [];
var item, args, editable;
for (var idx = 0; idx < items.length; idx++) {
item = items[idx];
editable = item.options.editable;
if (item instanceof Shape) {
args = { shape: item };
} else {
args = { connection: item };
}
if (editable && editable.remove !== false && !this.trigger('remove', args)) {
toRemove.push(item);
}
}
return toRemove;
},
undo: function () {
this.undoRedoService.undo();
},
redo: function () {
this.undoRedoService.redo();
},
select: function (item, options) {
if (isDefined(item)) {
options = deepExtend({ addToSelection: false }, options);
var addToSelection = options.addToSelection, items = [], selected = [], i, element;
if (!addToSelection) {
this.deselect();
}
this._internalSelection = true;
if (item instanceof Array) {
items = item;
} else if (item instanceof DiagramElement) {
items = [item];
}
for (i = 0; i < items.length; i++) {
element = items[i];
if (element.select(true)) {
selected.push(element);
}
}
this._selectionChanged(selected, []);
this._internalSelection = false;
} else {
return this._selectedItems;
}
},
selectAll: function () {
this.select(this.shapes.concat(this.connections));
},
selectArea: function (rect) {
var i, items, item;
this._internalSelection = true;
var selected = [];
if (rect instanceof Rect) {
items = this.shapes.concat(this.connections);
for (i = 0; i < items.length; i++) {
item = items[i];
if ((!rect || item._hitTest(rect)) && item.options.enable) {
if (item.select(true)) {
selected.push(item);
}
}
}
}
this._selectionChanged(selected, []);
this._internalSelection = false;
},
deselect: function (item) {
this._internalSelection = true;
var deselected = [], items = [], element, i;
if (item instanceof Array) {
items = item;
} else if (item instanceof DiagramElement) {
items.push(item);
} else if (!isDefined(item)) {
items = this._selectedItems.slice(0);
}
for (i = 0; i < items.length; i++) {
element = items[i];
if (element.select(false)) {
deselected.push(element);
}
}
this._selectionChanged([], deselected);
this._internalSelection = false;
},
toFront: function (items, undoable) {
if (!items) {
items = this._selectedItems.slice();
}
var result = this._getDiagramItems(items), indices;
if (!defined(undoable) || undoable) {
indices = indicesOfItems(this.mainLayer, result.visuals);
var unit = new ToFrontUnit(this, items, indices);
this.undoRedoService.add(unit);
} else {
this.mainLayer.toFront(result.visuals);
this._fixOrdering(result, true);
}
},
toBack: function (items, undoable) {
if (!items) {
items = this._selectedItems.slice();
}
var result = this._getDiagramItems(items), indices;
if (!defined(undoable) || undoable) {
indices = indicesOfItems(this.mainLayer, result.visuals);
var unit = new ToBackUnit(this, items, indices);
this.undoRedoService.add(unit);
} else {
this.mainLayer.toBack(result.visuals);
this._fixOrdering(result, false);
}
},
bringIntoView: function (item, options) {
var viewport = this.viewport();
var aligner = new diagram.RectAlign(viewport);
var current, rect, original, newPan;
if (viewport.width === 0 || viewport.height === 0) {
return;
}
options = deepExtend({
animate: false,
align: 'center middle'
}, options);
if (options.align == 'none') {
options.align = 'center middle';
}
if (item instanceof DiagramElement) {
rect = item.bounds(TRANSFORMED);
} else if (isArray(item)) {
rect = this.boundingBox(item);
} else if (item instanceof Rect) {
rect = item.clone();
}
original = rect.clone();
rect.zoom(this._zoom);
if (rect.width > viewport.width || rect.height > viewport.height) {
this._zoom = this._getValidZoom(math.min(viewport.width / original.width, viewport.height / original.height));
rect = original.clone().zoom(this._zoom);
}
this._zoomMainLayer();
current = rect.clone();
aligner.align(rect, options.align);
newPan = rect.topLeft().minus(current.topLeft());
this.pan(newPan.times(-1), options.animate);
},
alignShapes: function (direction) {
if (isUndefined(direction)) {
direction = 'Left';
}
var items = this.select(), val, item, i;
if (items.length === 0) {
return;
}
switch (direction.toLowerCase()) {
case 'left':
case 'top':
val = MAX_VALUE;
break;
case 'right':
case 'bottom':
val = MIN_VALUE;
break;
}
for (i = 0; i < items.length; i++) {
item = items[i];
if (item instanceof Shape) {
switch (direction.toLowerCase()) {
case 'left':
val = math.min(val, item.options.x);
break;
case 'top':
val = math.min(val, item.options.y);
break;
case 'right':
val = math.max(val, item.options.x);
break;
case 'bottom':
val = math.max(val, item.options.y);
break;
}
}
}
var undoStates = [];
var shapes = [];
for (i = 0; i < items.length; i++) {
item = items[i];
if (item instanceof Shape) {
shapes.push(item);
undoStates.push(item.bounds());
switch (direction.toLowerCase()) {
case 'left':
case 'right':
item.position(new Point(val, item.options.y));
break;
case 'top':
case 'bottom':
item.position(new Point(item.options.x, val));
break;
}
}
}
var unit = new diagram.TransformUnit(shapes, undoStates);
this.undoRedoService.add(unit, false);
},
zoom: function (zoom, options) {
if (zoom) {
var staticPoint = options ? options.point : new diagram.Point(0, 0);
zoom = this._zoom = this._getValidZoom(zoom);
if (!isUndefined(staticPoint)) {
staticPoint = new diagram.Point(math.round(staticPoint.x), math.round(staticPoint.y));
var zoomedPoint = staticPoint.times(zoom);
var viewportVector = this.modelToView(staticPoint);
var raw = viewportVector.minus(zoomedPoint);
this._storePan(new diagram.Point(math.round(raw.x), math.round(raw.y)));
}
if (options) {
options.zoom = zoom;
}
this._panTransform();
this._updateAdorners();
}
return this._zoom;
},
_getPan: function (pan) {
var canvas = this.canvas;
if (!canvas.translate) {
pan = pan.plus(this._pan);
}
return pan;
},
pan: function (pan, animate) {
if (pan instanceof Point) {
var that = this;
var scroller = that.scroller;
pan = that._getPan(pan);
pan = pan.times(-1);
if (animate) {
scroller.animatedScrollTo(pan.x, pan.y, function () {
that._updateAdorners();
});
} else {
scroller.scrollTo(pan.x, pan.y);
that._updateAdorners();
}
}
},
viewport: function () {
var element = this.element;
var width = element.width();
var height = element.height();
if (this.toolBar) {
height -= this.toolBar.element.outerHeight();
}
return new Rect(0, 0, width, height);
},
copy: function () {
if (this.options.copy.enabled) {
this._clipboard = [];
this._copyOffset = 1;
for (var i = 0; i < this._selectedItems.length; i++) {
var item = this._selectedItems[i];
this._clipboard.push(item);
}
}
},
cut: function () {
if (this.options.copy.enabled) {
this._clipboard = [];
this._copyOffset = 0;
for (var i = 0; i < this._selectedItems.length; i++) {
var item = this._selectedItems[i];
this._clipboard.push(item);
}
this.remove(this._clipboard, true);
}
},
paste: function () {
if (this._clipboard.length > 0) {
var item, copied, i;
var mapping = {};
var elements = splitDiagramElements(this._clipboard);
var connections = elements.connections;
var shapes = elements.shapes;
var offset = {
x: this._copyOffset * this.options.copy.offsetX,
y: this._copyOffset * this.options.copy.offsetY
};
this.deselect();
for (i = 0; i < shapes.length; i++) {
item = shapes[i];
copied = item.clone();
mapping[item.id] = copied;
copied.position(new Point(item.options.x + offset.x, item.options.y + offset.y));
copied.diagram = this;
copied = this._addShape(copied);
if (copied) {
copied.select();
}
}
for (i = 0; i < connections.length; i++) {
item = connections[i];
copied = this._addConnection(item.clone());
if (copied) {
this._updateCopiedConnection(copied, item, 'source', mapping, offset);
this._updateCopiedConnection(copied, item, 'target', mapping, offset);
copied.select(true);
copied.updateModel();
}
}
this._syncChanges();
this._copyOffset += 1;
}
},
_updateCopiedConnection: function (connection, sourceConnection, connectorName, mapping, offset) {
var onActivate, inactiveItem, targetShape;
var target = sourceConnection[connectorName]();
var diagram = this;
if (target instanceof Connector && mapping[target.shape.id]) {
targetShape = mapping[target.shape.id];
if (diagram.getShapeById(targetShape.id)) {
connection[connectorName](targetShape.getConnector(target.options.name));
} else {
inactiveItem = diagram._inactiveShapeItems.getByUid(targetShape.dataItem.uid);
if (inactiveItem) {
onActivate = function (item) {
targetShape = diagram._dataMap[item.id];
connection[connectorName](targetShape.getConnector(target.options.name));
connection.updateModel();
};
diagram._deferredConnectionUpdates.push(inactiveItem.onActivate(onActivate));
}
}
} else {
connection[connectorName](new Point(sourceConnection[connectorName + 'Point']().x + offset.x, sourceConnection[connectorName + 'Point']().y + offset.y));
}
},
boundingBox: function (items, origin) {
var rect = Rect.empty(), temp, di = isDefined(items) ? this._getDiagramItems(items) : { shapes: this.shapes };
if (di.shapes.length > 0) {
var item = di.shapes[0];
rect = item.bounds(ROTATED);
for (var i = 1; i < di.shapes.length; i++) {
item = di.shapes[i];
temp = item.bounds(ROTATED);
if (origin === true) {
temp.x -= item._rotationOffset.x;
temp.y -= item._rotationOffset.y;
}
rect = rect.union(temp);
}
}
return rect;
},
_containerOffset: function () {
var containerOffset = this.element.offset();
if (this.toolBar) {
containerOffset.top += this.toolBar.element.outerHeight();
}
return containerOffset;
},
documentToView: function (point) {
var containerOffset = this._containerOffset();
return new Point(point.x - containerOffset.left, point.y - containerOffset.top);
},
viewToDocument: function (point) {
var containerOffset = this._containerOffset();
return new Point(point.x + containerOffset.left, point.y + containerOffset.top);
},
viewToModel: function (point) {
return this._transformWithMatrix(point, this._matrixInvert);
},
modelToView: function (point) {
return this._transformWithMatrix(point, this._matrix);
},
modelToLayer: function (point) {
return this._transformWithMatrix(point, this._layerMatrix);
},
layerToModel: function (point) {
return this._transformWithMatrix(point, this._layerMatrixInvert);
},
documentToModel: function (point) {
var viewPoint = this.documentToView(point);
if (!this.canvas.translate) {
viewPoint.x = viewPoint.x + this.scroller.scrollLeft;
viewPoint.y = viewPoint.y + this.scroller.scrollTop;
}
return this.viewToModel(viewPoint);
},
modelToDocument: function (point) {
return this.viewToDocument(this.modelToView(point));
},
_transformWithMatrix: function (point, matrix) {
var result = point;
if (point instanceof Point) {
if (matrix) {
result = matrix.apply(point);
}
} else {
var tl = this._transformWithMatrix(point.topLeft(), matrix), br = this._transformWithMatrix(point.bottomRight(), matrix);
result = Rect.fromPoints(tl, br);
}
return result;
},
setDataSource: function (dataSource) {
this.options.dataSource = dataSource;
this._dataSource();
if (this.options.autoBind) {
this.dataSource.fetch();
}
},
setConnectionsDataSource: function (dataSource) {
this.options.connectionsDataSource = dataSource;
this._connectionDataSource();
if (this.options.autoBind) {
this.connectionsDataSource.fetch();
}
},
layout: function (options) {
this._layouting = true;
var type;
if (isUndefined(options)) {
options = this.options.layout;
}
if (isUndefined(options) || isUndefined(options.type)) {
type = 'Tree';
} else {
type = options.type;
}
var l;
switch (type.toLowerCase()) {
case 'tree':
l = new diagram.TreeLayout(this);
break;
case 'layered':
l = new diagram.LayeredLayout(this);
break;
case 'forcedirected':
case 'force':
case 'spring':
case 'springembedder':
l = new diagram.SpringLayout(this);
break;
default:
throw 'Layout algorithm \'' + type + '\' is not supported.';
}
var initialState = new diagram.LayoutState(this);
var finalState = l.layout(options);
if (finalState) {
var unit = new diagram.LayoutUndoUnit(initialState, finalState, options ? options.animate : null);
this.undoRedoService.add(unit);
}
this._layouting = false;
this._redrawConnections();
},
getShapeById: function (id) {
var found;
found = Utils.first(this.shapes, function (s) {
return s.visual.id === id;
});
if (found) {
return found;
}
found = Utils.first(this.connections, function (c) {
return c.visual.id === id;
});
return found;
},
_extendLayoutOptions: function (options) {
if (options.layout) {
options.layout = deepExtend(diagram.LayoutBase.fn.defaultOptions || {}, options.layout);
}
},
_selectionChanged: function (selected, deselected) {
if (selected.length || deselected.length) {
this.trigger(SELECT, {
selected: selected,
deselected: deselected
});
}
},
_getValidZoom: function (zoom) {
return math.min(math.max(zoom, this.options.zoomMin), this.options.zoomMax);
},
_panTransform: function (pos) {
var diagram = this, pan = pos || diagram._pan;
if (diagram.canvas.translate) {
diagram.scroller.scrollTo(pan.x, pan.y);
diagram._zoomMainLayer();
} else {
diagram._storePan(pan);
diagram._transformMainLayer();
}
},
_finishPan: function () {
this.trigger(PAN, {
total: this._pan,
delta: Number.NaN
});
},
_storePan: function (pan) {
this._pan = pan;
this._storeViewMatrix();
},
_zoomMainLayer: function () {
var zoom = this._zoom;
var transform = new CompositeTransform(0, 0, zoom, zoom);
transform.render(this.mainLayer);
this._storeLayerMatrix(transform);
this._storeViewMatrix();
},
_transformMainLayer: function () {
var pan = this._pan, zoom = this._zoom;
var transform = new CompositeTransform(pan.x, pan.y, zoom, zoom);
transform.render(this.mainLayer);
this._storeLayerMatrix(transform);
this._storeViewMatrix();
},
_storeLayerMatrix: function (canvasTransform) {
this._layerMatrix = canvasTransform.toMatrix();
this._layerMatrixInvert = canvasTransform.invert().toMatrix();
},
_storeViewMatrix: function () {
var pan = this._pan, zoom = this._zoom;
var transform = new CompositeTransform(pan.x, pan.y, zoom, zoom);
this._matrix = transform.toMatrix();
this._matrixInvert = transform.invert().toMatrix();
},
_toIndex: function (items, indices) {
var result = this._getDiagramItems(items);
this.mainLayer.toIndex(result.visuals, indices);
this._fixOrdering(result, false);
},
_fixOrdering: function (result, toFront) {
var shapePos = toFront ? this.shapes.length - 1 : 0, conPos = toFront ? this.connections.length - 1 : 0, i, item;
for (i = 0; i < result.shapes.length; i++) {
item = result.shapes[i];
Utils.remove(this.shapes, item);
Utils.insert(this.shapes, item, shapePos);
}
for (i = 0; i < result.cons.length; i++) {
item = result.cons[i];
Utils.remove(this.connections, item);
Utils.insert(this.connections, item, conPos);
}
},
_getDiagramItems: function (items) {
var i, result = {}, args = items;
result.visuals = [];
result.shapes = [];
result.cons = [];
if (!items) {
args = this._selectedItems.slice();
} else if (!isArray(items)) {
args = [items];
}
for (i = 0; i < args.length; i++) {
var item = args[i];
if (item instanceof Shape) {
result.shapes.push(item);
result.visuals.push(item.visual);
} else if (item instanceof Connection) {
result.cons.push(item);
result.visuals.push(item.visual);
}
}
return result;
},
_removeItem: function (item, undoable, removedConnections) {
item.select(false);
if (item instanceof Shape) {
this._removeShapeDataItem(item);
this._removeShape(item, undoable, removedConnections);
} else if (item instanceof Connection) {
this._removeConnectionDataItem(item);
this._removeConnection(item, undoable);
}
this.mainLayer.remove(item.visual);
},
_removeShape: function (shape, undoable, removedConnections) {
var i, connection, connector, sources = [], targets = [];
this.toolService._removeHover();
if (undoable) {
this.undoRedoService.addCompositeItem(new DeleteShapeUnit(shape));
}
Utils.remove(this.shapes, shape);
this._shapesQuadTree.remove(shape);
for (i = 0; i < shape.connectors.length; i++) {
connector = shape.connectors[i];
for (var j = 0; j < connector.connections.length; j++) {
connection = connector.connections[j];
if (!removedConnections || !dataviz.inArray(connection, removedConnections)) {
if (connection.sourceConnector == connector) {
sources.push(connection);
} else if (connection.targetConnector == connector) {
targets.push(connection);
}
}
}
}
for (i = 0; i < sources.length; i++) {
sources[i].source(null, undoable);
sources[i].updateModel();
}
for (i = 0; i < targets.length; i++) {
targets[i].target(null, undoable);
targets[i].updateModel();
}
},
_removeConnection: function (connection, undoable) {
if (connection.sourceConnector) {
Utils.remove(connection.sourceConnector.connections, connection);
}
if (connection.targetConnector) {
Utils.remove(connection.targetConnector.connections, connection);
}
if (undoable) {
this.undoRedoService.addCompositeItem(new DeleteConnectionUnit(connection));
}
Utils.remove(this.connections, connection);
},
_removeDataItems: function (items, recursive) {
var item, children, shape, idx;
items = isArray(items) ? items : [items];
while (items.length) {
item = items.shift();
shape = this._dataMap[item.uid];
if (shape) {
this._removeShapeConnections(shape);
this._removeItem(shape, false);
delete this._dataMap[item.uid];
if (recursive && item.hasChildren && item.loaded()) {
children = item.children.data();
for (idx = 0; idx < children.length; idx++) {
items.push(children[idx]);
}
}
}
}
},
_removeShapeConnections: function (shape) {
var connections = shape.connections();
var idx;
if (connections) {
for (idx = 0; idx < connections.length; idx++) {
this._removeItem(connections[idx], false);
}
}
},
_addDataItem: function (dataItem, undoable) {
if (!defined(dataItem)) {
return;
}
var shape = this._dataMap[dataItem.id];
if (shape) {
return shape;
}
var options = deepExtend({}, this.options.shapeDefaults);
options.dataItem = dataItem;
shape = new Shape(options, this);
this.addShape(shape, undoable !== false);
this._dataMap[dataItem.id] = shape;
return shape;
},
_addDataItemByUid: function (dataItem) {
if (!defined(dataItem)) {
return;
}
var shape = this._dataMap[dataItem.uid];
if (shape) {
return shape;
}
var options = deepExtend({}, this.options.shapeDefaults);
options.dataItem = dataItem;
shape = new Shape(options, this);
this.addShape(shape);
this._dataMap[dataItem.uid] = shape;
return shape;
},
_addDataItems: function (items, parent) {
var item, idx, shape, parentShape, connection;
for (idx = 0; idx < items.length; idx++) {
item = items[idx];
shape = this._addDataItemByUid(item);
parentShape = this._addDataItemByUid(parent);
if (parentShape && !this.connected(parentShape, shape)) {
connection = this.connect(parentShape, shape);
}
}
},
_refreshSource: function (e) {
var that = this, node = e.node, action = e.action, items = e.items, options = that.options, idx, dataBound;
if (e.field) {
return;
}
if (action == 'remove') {
this._removeDataItems(e.items, true);
} else {
if ((!action || action === 'itemloaded') && !this._bindingRoots) {
this._bindingRoots = true;
dataBound = true;
}
if (!action && !node) {
that.clear();
}
this._addDataItems(items, node);
for (idx = 0; idx < items.length; idx++) {
items[idx].load();
}
}
if (options.layout && (dataBound || action == 'remove' || action == 'add')) {
that.layout(options.layout);
}
if (dataBound) {
this.trigger('dataBound');
this._bindingRoots = false;
}
},
_mouseDown: function (e) {
var p = this._calculatePosition(e);
if (e.which == 1 && this.toolService.start(p, this._meta(e))) {
this._destroyToolBar();
e.preventDefault();
}
},
_addItem: function (item) {
if (item instanceof Shape) {
this.addShape(item);
} else if (item instanceof Connection) {
this.addConnection(item);
}
},
_mouseUp: function (e) {
var p = this._calculatePosition(e);
if (e.which == 1 && this.toolService.end(p, this._meta(e))) {
this._createToolBar();
e.preventDefault();
}
},
_createToolBar: function () {
var diagram = this.toolService.diagram;
if (!this.singleToolBar && diagram.select().length === 1) {
var element = diagram.select()[0];
if (element && element.options.editable !== false) {
var editable = element.options.editable;
var tools = editable.tools;
if (this._isEditable && tools.length === 0) {
if (element instanceof Shape) {
tools = [
'edit',
'rotateClockwise',
'rotateAnticlockwise'
];
} else if (element instanceof Connection) {
tools = ['edit'];
}
if (editable && editable.remove !== false) {
tools.push('delete');
}
}
if (tools && tools.length) {
var padding = 20;
var point;
this.singleToolBar = new DiagramToolBar(diagram, {
tools: tools,
click: proxy(this._toolBarClick, this),
modal: true
});
var popupWidth = this.singleToolBar._popup.element.outerWidth();
var popupHeight = this.singleToolBar._popup.element.outerHeight();
if (element instanceof Shape) {
var shapeBounds = this.modelToView(element.bounds(ROTATED));
point = Point(shapeBounds.x, shapeBounds.y).minus(Point((popupWidth - shapeBounds.width) / 2, popupHeight + padding));
} else if (element instanceof Connection) {
var connectionBounds = this.modelToView(element.bounds());
point = Point(connectionBounds.x, connectionBounds.y).minus(Point((popupWidth - connectionBounds.width - 20) / 2, popupHeight + padding));
}
if (point) {
if (!this.canvas.translate) {
point = point.minus(Point(this.scroller.scrollLeft, this.scroller.scrollTop));
}
point = this.viewToDocument(point);
point = Point(math.max(point.x, 0), math.max(point.y, 0));
this.singleToolBar.showAt(point);
} else {
this._destroyToolBar();
}
}
}
}
},
_toolBarClick: function (e) {
this.trigger('toolBarClick', e);
this._destroyToolBar();
},
_mouseMove: function (e) {
if (this.pauseMouseHandlers) {
return;
}
var p = this._calculatePosition(e);
if ((e.which === 0 || e.which == 1) && this.toolService.move(p, this._meta(e))) {
e.preventDefault();
}
},
_keydown: function (e) {
if (this.toolService.keyDown(e.keyCode, this._meta(e))) {
e.preventDefault();
}
},
_wheel: function (e) {
var delta = mwDelta(e), p = this._calculatePosition(e), meta = deepExtend(this._meta(e), { delta: delta });
if (this.toolService.wheel(p, meta)) {
e.preventDefault();
}
},
_meta: function (e) {
return {
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
altKey: e.altKey,
shiftKey: e.shiftKey
};
},
_calculatePosition: function (e) {
var pointEvent = e.pageX === undefined ? e.originalEvent : e, point = new Point(pointEvent.pageX, pointEvent.pageY), offset = this.documentToModel(point);
return offset;
},
_normalizePointZoom: function (point) {
return point.times(1 / this.zoom());
},
_initialize: function () {
this.shapes = [];
this._selectedItems = [];
this.connections = [];
this._dataMap = {};
this._connectionsDataMap = {};
this._inactiveShapeItems = new InactiveItemsCollection();
this._deferredConnectionUpdates = [];
this.undoRedoService = new UndoRedoService({
undone: this._syncHandler,
redone: this._syncHandler
});
this.id = diagram.randomId();
},
_fetchFreshData: function () {
var that = this;
that._dataSource();
if (that._isEditable) {
that._connectionDataSource();
}
if (that.options.autoBind) {
if (that._isEditable) {
this._loadingShapes = true;
this._loadingConnections = true;
that.dataSource.fetch();
that.connectionsDataSource.fetch();
} else {
that.dataSource.fetch();
}
}
},
_dataSource: function () {
if (defined(this.options.connectionsDataSource)) {
this._isEditable = true;
var dsOptions = this.options.dataSource || {};
var ds = isArray(dsOptions) ? { data: dsOptions } : dsOptions;
if (this.dataSource && this._shapesRefreshHandler) {
this.dataSource.unbind('change', this._shapesRefreshHandler).unbind('requestStart', this._shapesRequestStartHandler).unbind('error', this._shapesErrorHandler);
} else {
this._shapesRefreshHandler = proxy(this._refreshShapes, this);
this._shapesRequestStartHandler = proxy(this._shapesRequestStart, this);
this._shapesErrorHandler = proxy(this._error, this);
}
this.dataSource = kendo.data.DataSource.create(ds).bind('change', this._shapesRefreshHandler).bind('requestStart', this._shapesRequestStartHandler).bind('error', this._shapesErrorHandler);
} else {
this._treeDataSource();
this._isEditable = false;
}
},
_connectionDataSource: function () {
var dsOptions = this.options.connectionsDataSource;
if (dsOptions) {
var ds = isArray(dsOptions) ? { data: dsOptions } : dsOptions;
if (this.connectionsDataSource && this._connectionsRefreshHandler) {
this.connectionsDataSource.unbind('change', this._connectionsRefreshHandler).unbind('requestStart', this._connectionsRequestStartHandler).unbind('error', this._connectionsErrorHandler);
} else {
this._connectionsRefreshHandler = proxy(this._refreshConnections, this);
this._connectionsRequestStartHandler = proxy(this._connectionsRequestStart, this);
this._connectionsErrorHandler = proxy(this._connectionsError, this);
}
this.connectionsDataSource = kendo.data.DataSource.create(ds).bind('change', this._connectionsRefreshHandler).bind('requestStart', this._connectionsRequestStartHandler).bind('error', this._connectionsErrorHandler);
}
},
_shapesRequestStart: function (e) {
if (e.type == 'read') {
this._loadingShapes = true;
}
},
_connectionsRequestStart: function (e) {
if (e.type == 'read') {
this._loadingConnections = true;
}
},
_error: function () {
this._loadingShapes = false;
},
_connectionsError: function () {
this._loadingConnections = false;
},
_refreshShapes: function (e) {
if (e.action === 'remove') {
if (this._shouldRefresh()) {
this._removeShapes(e.items);
}
} else if (e.action === 'itemchange') {
if (this._shouldRefresh()) {
this._updateShapes(e.items, e.field);
}
} else if (e.action === 'add') {
this._inactiveShapeItems.add(e.items);
} else if (e.action === 'sync') {
this._syncShapes(e.items);
} else {
this.refresh();
}
},
_shouldRefresh: function () {
return !this._suspended;
},
_suspendModelRefresh: function () {
this._suspended = (this._suspended || 0) + 1;
},
_resumeModelRefresh: function () {
this._suspended = math.max((this._suspended || 0) - 1, 0);
},
refresh: function () {
this._loadingShapes = false;
if (!this._loadingConnections) {
this._rebindShapesAndConnections();
}
},
_rebindShapesAndConnections: function () {
this.clear();
this._addShapes(this.dataSource.view());
if (this.connectionsDataSource) {
this._addConnections(this.connectionsDataSource.view(), false);
}
if (this.options.layout) {
this.layout(this.options.layout);
} else {
this._redrawConnections();
}
this.trigger('dataBound');
},
refreshConnections: function () {
this._loadingConnections = false;
if (!this._loadingShapes) {
this._rebindShapesAndConnections();
}
},
_redrawConnections: function () {
var connections = this.connections;
for (var idx = 0; idx < connections.length; idx++) {
connections[idx].refresh();
}
},
_removeShapes: function (items) {
var dataMap = this._dataMap;
var item, i;
for (i = 0; i < items.length; i++) {
item = items[i];
if (dataMap[item.id]) {
this.remove(dataMap[item.id], false);
dataMap[item.id] = null;
}
}
},
_syncShapes: function () {
var diagram = this;
var inactiveItems = diagram._inactiveShapeItems;
inactiveItems.forEach(function (inactiveItem) {
var dataItem = inactiveItem.dataItem;
var shape = inactiveItem.element;
if (!dataItem.isNew()) {
if (shape) {
shape._setOptionsFromModel();
diagram.addShape(shape, inactiveItem.undoable);
diagram._dataMap[dataItem.id] = shape;
} else {
diagram._addDataItem(dataItem);
}
inactiveItem.activate();
inactiveItems.remove(dataItem);
}
});
},
_updateShapes: function (items, field) {
for (var i = 0; i < items.length; i++) {
var dataItem = items[i];
var shape = this._dataMap[dataItem.id];
if (shape) {
shape.updateOptionsFromModel(dataItem, field);
}
}
},
_addShapes: function (dataItems) {
for (var i = 0; i < dataItems.length; i++) {
this._addDataItem(dataItems[i], false);
}
},
_refreshConnections: function (e) {
if (e.action === 'remove') {
if (this._shouldRefresh()) {
this._removeConnections(e.items);
}
} else if (e.action === 'add') {
this._addConnections(e.items);
} else if (e.action === 'sync') {
} else if (e.action === 'itemchange') {
if (this._shouldRefresh()) {
this._updateConnections(e.items);
}
} else {
this.refreshConnections();
}
},
_removeConnections: function (items) {
for (var i = 0; i < items.length; i++) {
this.remove(this._connectionsDataMap[items[i].uid], false);
this._connectionsDataMap[items[i].uid] = null;
}
},
_updateConnections: function (items) {
for (var i = 0; i < items.length; i++) {
var dataItem = items[i];
var connection = this._connectionsDataMap[dataItem.uid];
connection.updateOptionsFromModel(dataItem);
}
},
_addConnections: function (connections, undoable) {
var length = connections.length;
for (var i = 0; i < length; i++) {
var dataItem = connections[i];
this._addConnectionDataItem(dataItem, undoable);
}
},
_addConnectionDataItem: function (dataItem, undoable) {
if (!this._connectionsDataMap[dataItem.uid]) {
var from = this._validateConnector(dataItem.from);
if (!defined(from) || from === null) {
from = new Point(dataItem.fromX, dataItem.fromY);
}
var to = this._validateConnector(dataItem.to);
if (!defined(to) || to === null) {
to = new Point(dataItem.toX, dataItem.toY);
}
if (defined(from) && defined(to)) {
var options = deepExtend({}, this.options.connectionDefaults);
options.dataItem = dataItem;
var connection = new Connection(from, to, options);
this._connectionsDataMap[dataItem.uid] = connection;
this.addConnection(connection, undoable);
}
}
},
_validateConnector: function (value) {
var connector;
if (defined(value) && value !== null) {
connector = this._dataMap[value];
}
return connector;
},
_treeDataSource: function () {
var that = this, options = that.options, dataSource = options.dataSource;
dataSource = isArray(dataSource) ? { data: dataSource } : dataSource;
if (!dataSource.fields) {
dataSource.fields = [
{ field: 'text' },
{ field: 'url' },
{ field: 'spriteCssClass' },
{ field: 'imageUrl' }
];
}
if (that.dataSource && that._refreshHandler) {
that._unbindDataSource();
}
that._refreshHandler = proxy(that._refreshSource, that);
that._errorHandler = proxy(that._error, that);
that.dataSource = HierarchicalDataSource.create(dataSource).bind(CHANGE, that._refreshHandler).bind(ERROR, that._errorHandler);
},
_unbindDataSource: function () {
var that = this;
that.dataSource.unbind(CHANGE, that._refreshHandler).unbind(ERROR, that._errorHandler);
},
_adorn: function (adorner, isActive) {
if (isActive !== undefined && adorner) {
if (isActive) {
this._adorners.push(adorner);
this.adornerLayer.append(adorner.visual);
} else {
Utils.remove(this._adorners, adorner);
this.adornerLayer.remove(adorner.visual);
}
}
},
_showConnectors: function (shape, value) {
if (value) {
this._connectorsAdorner.show(shape);
} else {
this._connectorsAdorner.destroy();
}
},
_updateAdorners: function () {
var adorners = this._adorners;
for (var i = 0; i < adorners.length; i++) {
var adorner = adorners[i];
if (adorner.refreshBounds) {
adorner.refreshBounds();
}
adorner.refresh();
}
},
_refresh: function () {
for (var i = 0; i < this.connections.length; i++) {
this.connections[i].refresh();
}
},
_destroyToolBar: function () {
if (this.singleToolBar) {
this.singleToolBar.hide();
this.singleToolBar.destroy();
this.singleToolBar = null;
}
},
_destroyGlobalToolBar: function () {
if (this.toolBar) {
this.toolBar.hide();
this.toolBar.destroy();
this.toolBar = null;
}
},
exportDOMVisual: function () {
var viewBox = this.canvas._viewBox;
var scrollOffset = geom.transform().translate(-viewBox.x, -viewBox.y);
var viewRect = new geom.Rect([
0,
0
], [
viewBox.width,
viewBox.height
]);
var clipPath = draw.Path.fromRect(viewRect);
var wrap = new draw.Group({ transform: scrollOffset });
var clipWrap = new draw.Group({ clip: clipPath });
var root = this.canvas.drawingElement.children[0];
clipWrap.append(wrap);
wrap.children.push(root);
return clipWrap;
},
exportVisual: function () {
var scale = geom.transform().scale(1 / this._zoom);
var wrap = new draw.Group({ transform: scale });
var root = this.mainLayer.drawingElement;
wrap.children.push(root);
return wrap;
},
_syncChanges: function () {
this._syncShapeChanges();
this._syncConnectionChanges();
},
_syncShapeChanges: function () {
if (this.dataSource && this._isEditable) {
this.dataSource.sync();
}
},
_syncConnectionChanges: function () {
var that = this;
if (that.connectionsDataSource && that._isEditable) {
$.when.apply($, that._deferredConnectionUpdates).then(function () {
that.connectionsDataSource.sync();
});
that.deferredConnectionUpdates = [];
}
}
});
dataviz.ExportMixin.extend(Diagram.fn, true);
if (kendo.PDFMixin) {
kendo.PDFMixin.extend(Diagram.fn);
}
function filterShapeDataItem(dataItem) {
var result = {};
dataItem = dataItem || {};
if (defined(dataItem.text) && dataItem.text !== null) {
result.text = dataItem.text;
}
if (defined(dataItem.x) && dataItem.x !== null) {
result.x = dataItem.x;
}
if (defined(dataItem.y) && dataItem.y !== null) {
result.y = dataItem.y;
}
if (defined(dataItem.width) && dataItem.width !== null) {
result.width = dataItem.width;
}
if (defined(dataItem.height) && dataItem.height !== null) {
result.height = dataItem.height;
}
if (defined(dataItem.type) && dataItem.type !== null) {
result.type = dataItem.type;
}
return result;
}
function filterConnectionDataItem(dataItem) {
var result = {};
dataItem = dataItem || {};
if (defined(dataItem.text) && dataItem.text !== null) {
result.content = dataItem.text;
}
if (defined(dataItem.type) && dataItem.type !== null) {
result.type = dataItem.type;
}
if (defined(dataItem.from) && dataItem.from !== null) {
result.from = dataItem.from;
}
if (defined(dataItem.fromConnector) && dataItem.fromConnector !== null) {
result.fromConnector = dataItem.fromConnector;
}
if (defined(dataItem.fromX) && dataItem.fromX !== null) {
result.fromX = dataItem.fromX;
}
if (defined(dataItem.fromY) && dataItem.fromY !== null) {
result.fromY = dataItem.fromY;
}
if (defined(dataItem.to) && dataItem.to !== null) {
result.to = dataItem.to;
}
if (defined(dataItem.toConnector) && dataItem.toConnector !== null) {
result.toConnector = dataItem.toConnector;
}
if (defined(dataItem.toX) && dataItem.toX !== null) {
result.toX = dataItem.toX;
}
if (defined(dataItem.toY) && dataItem.toY !== null) {
result.toY = dataItem.toY;
}
return result;
}
var DiagramToolBar = kendo.Observable.extend({
init: function (diagram, options) {
kendo.Observable.fn.init.call(this);
this.diagram = diagram;
this.options = deepExtend({}, this.options, options);
this._tools = [];
this.createToolBar();
this.createTools();
this.appendTools();
if (this.options.modal) {
this.createPopup();
}
this.bind(this.events, options);
},
events: ['click'],
createPopup: function () {
this.container = $('
').append(this.element);
this._popup = this.container.kendoPopup({}).getKendoPopup();
},
appendTools: function () {
for (var i = 0; i < this._tools.length; i++) {
var tool = this._tools[i];
if (tool.buttons && tool.buttons.length || !defined(tool.buttons)) {
this._toolBar.add(tool);
}
}
},
createToolBar: function () {
this.element = $('
');
this._toolBar = this.element.kendoToolBar({
click: proxy(this.click, this),
resizable: false
}).getKendoToolBar();
this.element.css('border', 'none');
},
createTools: function () {
for (var i = 0; i < this.options.tools.length; i++) {
this.createTool(this.options.tools[i]);
}
},
createTool: function (tool) {
var toolName = (isPlainObject(tool) ? tool.name : tool) + 'Tool';
if (this[toolName]) {
this[toolName](tool);
} else {
this._tools.push(tool);
}
},
showAt: function (point) {
if (this._popup) {
this._popup.open(point.x, point.y);
}
},
hide: function () {
if (this._popup) {
this._popup.close();
}
},
newGroup: function () {
return {
type: 'buttonGroup',
buttons: []
};
},
editTool: function () {
this._tools.push({
spriteCssClass: 'k-icon k-i-pencil',
showText: 'overflow',
type: 'button',
text: 'Edit',
attributes: this._setAttributes({ action: 'edit' })
});
},
deleteTool: function () {
this._tools.push({
spriteCssClass: 'k-icon k-i-close',
showText: 'overflow',
type: 'button',
text: 'Delete',
attributes: this._setAttributes({ action: 'delete' })
});
},
rotateAnticlockwiseTool: function (options) {
this._appendGroup('rotate');
this._rotateGroup.buttons.push({
spriteCssClass: 'k-icon k-i-rotateccw',
showText: 'overflow',
text: 'RotateAnticlockwise',
group: 'rotate',
attributes: this._setAttributes({
action: 'rotateAnticlockwise',
step: options.step
})
});
},
rotateClockwiseTool: function (options) {
this._appendGroup('rotate');
this._rotateGroup.buttons.push({
spriteCssClass: 'k-icon k-i-rotatecw',
attributes: this._setAttributes({
action: 'rotateClockwise',
step: options.step
}),
showText: 'overflow',
text: 'RotateClockwise',
group: 'rotate'
});
},
createShapeTool: function () {
this._appendGroup('create');
this._createGroup.buttons.push({
spriteCssClass: 'k-icon k-i-shape',
showText: 'overflow',
text: 'CreateShape',
group: 'create',
attributes: this._setAttributes({ action: 'createShape' })
});
},
createConnectionTool: function () {
this._appendGroup('create');
this._createGroup.buttons.push({
spriteCssClass: 'k-icon k-i-connector',
showText: 'overflow',
text: 'CreateConnection',
group: 'create',
attributes: this._setAttributes({ action: 'createConnection' })
});
},
undoTool: function () {
this._appendGroup('history');
this._historyGroup.buttons.push({
spriteCssClass: 'k-icon k-i-undo',
showText: 'overflow',
text: 'Undo',
group: 'history',
attributes: this._setAttributes({ action: 'undo' })
});
},
redoTool: function () {
this._appendGroup('history');
this._historyGroup.buttons.push({
spriteCssClass: 'k-icon k-i-redo',
showText: 'overflow',
text: 'Redo',
group: 'history',
attributes: this._setAttributes({ action: 'redo' })
});
},
_appendGroup: function (name) {
var prop = '_' + name + 'Group';
if (!this[prop]) {
this[prop] = this.newGroup();
this._tools.push(this[prop]);
}
},
_setAttributes: function (attributes) {
var attr = {};
if (attributes.action) {
attr[kendo.attr('action')] = attributes.action;
}
if (attributes.step) {
attr[kendo.attr('step')] = attributes.step;
}
return attr;
},
_getAttributes: function (element) {
var attr = {};
var action = element.attr(kendo.attr('action'));
if (action) {
attr.action = action;
}
var step = element.attr(kendo.attr('step'));
if (step) {
attr.step = step;
}
return attr;
},
click: function (e) {
var attributes = this._getAttributes($(e.target));
var action = attributes.action;
if (action) {
this[action](attributes);
}
this.trigger('click', this.eventData(action));
},
eventData: function (action) {
var element = this.selectedElements(), shapes = [], connections = [];
if (element instanceof Shape) {
shapes.push(element);
} else {
connections.push(element);
}
return {
shapes: shapes,
connections: connections,
action: action
};
},
'delete': function () {
var diagram = this.diagram;
var toRemove = diagram._triggerRemove(this.selectedElements());
if (toRemove.length) {
this.diagram.remove(toRemove, true);
this.diagram._syncChanges();
}
},
edit: function () {
this.diagram.edit(this.selectedElements()[0]);
},
rotateClockwise: function (options) {
var angle = parseFloat(options.step || 90);
this._rotate(angle);
},
rotateAnticlockwise: function (options) {
var angle = parseFloat(options.step || 90);
this._rotate(-angle);
},
_rotate: function (angle) {
var adorner = this.diagram._resizingAdorner;
adorner.angle(adorner.angle() + angle);
adorner.rotate();
},
selectedElements: function () {
return this.diagram.select();
},
createShape: function () {
this.diagram.createShape();
},
createConnection: function () {
this.diagram.createConnection();
},
undo: function () {
this.diagram.undo();
},
redo: function () {
this.diagram.redo();
},
destroy: function () {
this.diagram = null;
this.element = null;
this.options = null;
if (this._toolBar) {
this._toolBar.destroy();
}
if (this._popup) {
this._popup.destroy();
}
}
});
var Editor = kendo.Observable.extend({
init: function (element, options) {
kendo.Observable.fn.init.call(this);
this.options = extend(true, {}, this.options, options);
this.element = element;
this.model = this.options.model;
this.fields = this._getFields();
this._initContainer();
this.createEditable();
},
options: { editors: {} },
_initContainer: function () {
this.wrapper = this.element;
},
createEditable: function () {
var options = this.options;
this.editable = new kendo.ui.Editable(this.wrapper, {
fields: this.fields,
target: options.target,
clearContainer: false,
model: this.model
});
},
_isEditable: function (field) {
return this.model.editable && this.model.editable(field);
},
_getFields: function () {
var fields = [];
var modelFields = this.model.fields;
for (var field in modelFields) {
var result = {};
if (this._isEditable(field)) {
var editor = this.options.editors[field];
if (editor) {
result.editor = editor;
}
result.field = field;
fields.push(result);
}
}
return fields;
},
end: function () {
return this.editable.end();
},
destroy: function () {
this.editable.destroy();
this.editable.element.find('[' + kendo.attr('container-for') + ']').empty();
this.model = this.wrapper = this.element = this.columns = this.editable = null;
}
});
var PopupEditor = Editor.extend({
init: function (element, options) {
Editor.fn.init.call(this, element, options);
this.bind(this.events, this.options);
this.open();
},
events: [
'update',
'cancel'
],
options: {
window: {
modal: true,
resizable: false,
draggable: true,
title: 'Edit',
visible: false
}
},
_initContainer: function () {
var that = this;
this.wrapper = $('').attr(kendo.attr('uid'), this.model.uid);
var formContent = '';
if (this.options.template) {
formContent += this._renderTemplate();
this.fields = [];
} else {
formContent += this._renderFields();
}
formContent += this._renderButtons();
this.wrapper.append($('
').append(formContent));
this.window = new kendo.ui.Window(this.wrapper.appendTo(this.element), this.options.window);
this.window.bind('close', function (e) {
if (e.userTriggered) {
e.sender.element.focus();
that._cancelClick(e);
}
});
this._attachButtonEvents();
},
_renderTemplate: function () {
var template = this.options.template;
if (typeof template === 'string') {
template = window.unescape(template);
}
template = kendo.template(template)(this.model);
return template;
},
_renderFields: function () {
var form = '';
for (var i = 0; i < this.fields.length; i++) {
var field = this.fields[i];
form += '
';
if (this._isEditable(field.field)) {
form += '
';
}
}
return form;
},
_renderButtons: function () {
var form = '
';
form += this._createButton('update');
form += this._createButton('cancel');
form += '
';
return form;
},
_createButton: function (name) {
return kendo.template(BUTTON_TEMPLATE)(defaultButtons[name]);
},
_attachButtonEvents: function () {
this._cancelClickHandler = proxy(this._cancelClick, this);
this.window.element.on(CLICK + NS, 'a.k-diagram-cancel', this._cancelClickHandler);
this._updateClickHandler = proxy(this._updateClick, this);
this.window.element.on(CLICK + NS, 'a.k-diagram-update', this._updateClickHandler);
},
_updateClick: function (e) {
e.preventDefault();
this.trigger('update');
},
_cancelClick: function (e) {
e.preventDefault();
this.trigger('cancel');
},
open: function () {
this.window.center().open();
},
close: function () {
this.window.bind('deactivate', proxy(this.destroy, this)).close();
},
destroy: function () {
this.window.close().destroy();
this.window.element.off(CLICK + NS, 'a.k-diagram-cancel', this._cancelClickHandler);
this.window.element.off(CLICK + NS, 'a.k-diagram-update', this._updateClickHandler);
this._cancelClickHandler = null;
this._editUpdateClickHandler = null;
this.window = null;
Editor.fn.destroy.call(this);
}
});
function connectionSelector(container, options) {
var model = this.dataSource.reader.model;
if (model) {
var textField = model.fn.fields.text ? 'text' : model.idField;
$('
').appendTo(container).kendoDropDownList({
dataValueField: model.idField,
dataTextField: textField,
dataSource: this.dataSource.data().toJSON(),
optionLabel: ' ',
valuePrimitive: true
});
}
}
function InactiveItem(dataItem) {
this.dataItem = dataItem;
this.callbacks = [];
}
InactiveItem.fn = InactiveItem.prototype = {
onActivate: function (callback) {
var deffered = $.Deferred();
this.callbacks.push({
callback: callback,
deferred: deffered
});
return deffered;
},
activate: function () {
var callbacks = this.callbacks;
var item;
for (var idx = 0; idx < callbacks.length; idx++) {
item = this.callbacks[idx];
item.callback(this.dataItem);
item.deferred.resolve();
}
this.callbacks = [];
}
};
function InactiveItemsCollection() {
this.items = {};
}
InactiveItemsCollection.fn = InactiveItemsCollection.prototype = {
add: function (items) {
for (var idx = 0; idx < items.length; idx++) {
this.items[items[idx].uid] = new InactiveItem(items[idx]);
}
},
forEach: function (callback) {
for (var uid in this.items) {
callback(this.items[uid]);
}
},
getByUid: function (uid) {
return this.items[uid];
},
remove: function (item) {
delete this.items[item.uid];
}
};
var QuadRoot = Class.extend({
init: function () {
this.shapes = [];
},
_add: function (shape, bounds) {
this.shapes.push({
bounds: bounds,
shape: shape
});
shape._quadNode = this;
},
insert: function (shape, bounds) {
this._add(shape, bounds);
},
remove: function (shape) {
var shapes = this.shapes;
var length = shapes.length;
for (var idx = 0; idx < length; idx++) {
if (shapes[idx].shape === shape) {
shapes.splice(idx, 1);
break;
}
}
},
hitTestRect: function (rect) {
var shapes = this.shapes;
var length = shapes.length;
for (var i = 0; i < length; i++) {
if (this._testRect(shapes[i].shape, rect)) {
return true;
}
}
},
_testRect: function (shape, rect) {
var angle = shape.rotate().angle;
var bounds = shape.bounds();
var hit;
if (!angle) {
hit = bounds.overlaps(rect);
} else {
hit = Intersect.rects(rect, bounds, -angle);
}
return hit;
}
});
var QuadNode = QuadRoot.extend({
init: function (rect) {
QuadRoot.fn.init.call(this);
this.children = [];
this.rect = rect;
},
inBounds: function (rect) {
var nodeRect = this.rect;
var nodeBottomRight = nodeRect.bottomRight();
var bottomRight = rect.bottomRight();
var inBounds = nodeRect.x <= rect.x && nodeRect.y <= rect.y && bottomRight.x <= nodeBottomRight.x && bottomRight.y <= nodeBottomRight.y;
return inBounds;
},
overlapsBounds: function (rect) {
return this.rect.overlaps(rect);
},
insert: function (shape, bounds) {
var inserted = false;
var children = this.children;
var length = children.length;
if (this.inBounds(bounds)) {
if (!length && this.shapes.length < 4) {
this._add(shape, bounds);
} else {
if (!length) {
this._initChildren();
}
for (var idx = 0; idx < children.length; idx++) {
if (children[idx].insert(shape, bounds)) {
inserted = true;
break;
}
}
if (!inserted) {
this._add(shape, bounds);
}
}
inserted = true;
}
return inserted;
},
_initChildren: function () {
var rect = this.rect, children = this.children, shapes = this.shapes, center = rect.center(), halfWidth = rect.width / 2, halfHeight = rect.height / 2, childIdx, shapeIdx;
children.push(new QuadNode(new Rect(rect.x, rect.y, halfWidth, halfHeight)), new QuadNode(new Rect(center.x, rect.y, halfWidth, halfHeight)), new QuadNode(new Rect(rect.x, center.y, halfWidth, halfHeight)), new QuadNode(new Rect(center.x, center.y, halfWidth, halfHeight)));
for (shapeIdx = shapes.length - 1; shapeIdx >= 0; shapeIdx--) {
for (childIdx = 0; childIdx < children.length; childIdx++) {
if (children[childIdx].insert(shapes[shapeIdx].shape, shapes[shapeIdx].bounds)) {
shapes.splice(shapeIdx, 1);
break;
}
}
}
},
hitTestRect: function (rect) {
var idx;
var children = this.children;
var length = children.length;
var hit = false;
if (this.overlapsBounds(rect)) {
if (QuadRoot.fn.hitTestRect.call(this, rect)) {
hit = true;
} else {
for (idx = 0; idx < length; idx++) {
if (children[idx].hitTestRect(rect)) {
hit = true;
break;
}
}
}
}
return hit;
}
});
var ShapesQuadTree = Class.extend({
ROOT_SIZE: 1000,
init: function (diagram) {
var boundsChangeHandler = proxy(this._boundsChange, this);
diagram.bind(ITEMBOUNDSCHANGE, boundsChangeHandler);
diagram.bind(ITEMROTATE, boundsChangeHandler);
this.initRoots();
},
initRoots: function () {
this.rootMap = {};
this.root = new QuadRoot();
},
clear: function () {
this.initRoots();
},
_boundsChange: function (e) {
if (e.item._quadNode) {
e.item._quadNode.remove(e.item);
}
this.insert(e.item);
},
insert: function (shape) {
var bounds = shape.bounds(ROTATED);
var rootSize = this.ROOT_SIZE;
var sectors = this.getSectors(bounds);
var x = sectors[0][0];
var y = sectors[1][0];
if (this.inRoot(sectors)) {
this.root.insert(shape, bounds);
} else {
if (!this.rootMap[x]) {
this.rootMap[x] = {};
}
if (!this.rootMap[x][y]) {
this.rootMap[x][y] = new QuadNode(new Rect(x * rootSize, y * rootSize, rootSize, rootSize));
}
this.rootMap[x][y].insert(shape, bounds);
}
},
remove: function (shape) {
if (shape._quadNode) {
shape._quadNode.remove(shape);
}
},
inRoot: function (sectors) {
return sectors[0].length > 1 || sectors[1].length > 1;
},
getSectors: function (rect) {
var rootSize = this.ROOT_SIZE;
var bottomRight = rect.bottomRight();
var bottomX = math.floor(bottomRight.x / rootSize);
var bottomY = math.floor(bottomRight.y / rootSize);
var sectors = [
[],
[]
];
for (var x = math.floor(rect.x / rootSize); x <= bottomX; x++) {
sectors[0].push(x);
}
for (var y = math.floor(rect.y / rootSize); y <= bottomY; y++) {
sectors[1].push(y);
}
return sectors;
},
hitTestRect: function (rect) {
var sectors = this.getSectors(rect);
var xIdx, yIdx, x, y;
var root;
if (this.root.hitTestRect(rect)) {
return true;
}
for (xIdx = 0; xIdx < sectors[0].length; xIdx++) {
x = sectors[0][xIdx];
for (yIdx = 0; yIdx < sectors[1].length; yIdx++) {
y = sectors[1][yIdx];
root = (this.rootMap[x] || {})[y];
if (root && root.hitTestRect(rect)) {
return true;
}
}
}
return false;
}
});
function cloneDataItem(dataItem) {
var result = dataItem;
if (dataItem instanceof kendo.data.Model) {
result = dataItem.toJSON();
result[dataItem.idField] = dataItem._defaultId;
}
return result;
}
function splitDiagramElements(elements) {
var connections = [];
var shapes = [];
var element, idx;
for (idx = 0; idx < elements.length; idx++) {
element = elements[idx];
if (element instanceof Shape) {
shapes.push(element);
} else {
connections.push(element);
}
}
return {
shapes: shapes,
connections: connections
};
}
function createModel(dataSource, model) {
if (dataSource.reader.model) {
return new dataSource.reader.model(model);
}
return new kendo.data.ObservableObject(model);
}
function clearField(field, model) {
if (defined(model[field])) {
model.set(field, null);
}
}
function copyDefaultOptions(mainOptions, elementOptions, fields) {
var field;
for (var idx = 0; idx < fields.length; idx++) {
field = fields[idx];
if (elementOptions && !defined(elementOptions[field])) {
elementOptions[field] = mainOptions[field];
}
}
}
function translateToOrigin(visual) {
var bbox = visual.drawingContainer().clippedBBox(null);
if (bbox.origin.x !== 0 || bbox.origin.y !== 0) {
visual.position(-bbox.origin.x, -bbox.origin.y);
}
}
dataviz.ui.plugin(Diagram);
deepExtend(diagram, {
Shape: Shape,
Connection: Connection,
Connector: Connector,
DiagramToolBar: DiagramToolBar,
QuadNode: QuadNode,
QuadRoot: QuadRoot,
ShapesQuadTree: ShapesQuadTree,
PopupEditor: PopupEditor
});
}(window.kendo.jQuery));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.dataviz.diagram', [
'kendo.data',
'kendo.draganddrop',
'kendo.userevents',
'kendo.mobile.scroller',
'kendo.drawing',
'dataviz/diagram/utils',
'dataviz/diagram/math',
'dataviz/diagram/svg',
'dataviz/diagram/services',
'dataviz/diagram/layout',
'dataviz/diagram/dom'
], f);
}(function () {
var __meta__ = {
id: 'dataviz.diagram',
name: 'Diagram',
category: 'dataviz',
description: 'The Kendo DataViz Diagram ',
depends: [
'data',
'userevents',
'mobile.scroller',
'draganddrop',
'drawing',
'dataviz.core',
'dataviz.themes',
'toolbar'
],
features: [
{
id: 'dataviz.diagram-pdf-export',
name: 'PDF export',
description: 'Export Diagram as PDF',
depends: ['pdf']
},
{
id: 'dataviz.diagram-editing',
name: 'Editing',
description: 'Support for model editing',
depends: [
'editable',
'window',
'dropdownlist'
]
}
]
};
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.dataviz.treemap', [
'kendo.data',
'kendo.userevents',
'kendo.dataviz.themes'
], f);
}(function () {
var __meta__ = {
id: 'dataviz.treeMap',
name: 'TreeMap',
category: 'dataviz',
description: 'The Kendo DataViz TreeMap',
depends: [
'data',
'userevents',
'dataviz.themes'
]
};
(function ($, undefined) {
var math = Math, proxy = $.proxy, isArray = $.isArray, kendo = window.kendo, Class = kendo.Class, Widget = kendo.ui.Widget, template = kendo.template, deepExtend = kendo.deepExtend, HierarchicalDataSource = kendo.data.HierarchicalDataSource, getter = kendo.getter, dataviz = kendo.dataviz;
var NS = '.kendoTreeMap', CHANGE = 'change', DATA_BOUND = 'dataBound', ITEM_CREATED = 'itemCreated', MAX_VALUE = Number.MAX_VALUE, MOUSEOVER_NS = 'mouseover' + NS, MOUSELEAVE_NS = 'mouseleave' + NS, UNDEFINED = 'undefined';
var TreeMap = Widget.extend({
init: function (element, options) {
kendo.destroy(element);
$(element).empty();
Widget.fn.init.call(this, element, options);
this.wrapper = this.element;
this._initTheme(this.options);
this.element.addClass('k-widget k-treemap');
this._setLayout();
this._originalOptions = deepExtend({}, this.options);
this._initDataSource();
this._attachEvents();
kendo.notify(this, dataviz.ui);
},
options: {
name: 'TreeMap',
theme: 'default',
autoBind: true,
textField: 'text',
valueField: 'value',
colorField: 'color'
},
events: [
DATA_BOUND,
ITEM_CREATED
],
_initTheme: function (options) {
var that = this, themes = dataviz.ui.themes || {}, themeName = ((options || {}).theme || '').toLowerCase(), themeOptions = (themes[themeName] || {}).treeMap;
that.options = deepExtend({}, themeOptions, options);
},
_attachEvents: function () {
this.element.on(MOUSEOVER_NS, proxy(this._mouseover, this)).on(MOUSELEAVE_NS, proxy(this._mouseleave, this));
this._resizeHandler = proxy(this.resize, this, false);
kendo.onResize(this._resizeHandler);
},
_setLayout: function () {
if (this.options.type === 'horizontal') {
this._layout = new SliceAndDice(false);
this._view = new SliceAndDiceView(this, this.options);
} else if (this.options.type === 'vertical') {
this._layout = new SliceAndDice(true);
this._view = new SliceAndDiceView(this, this.options);
} else {
this._layout = new Squarified();
this._view = new SquarifiedView(this, this.options);
}
},
_initDataSource: function () {
var that = this, options = that.options, dataSource = options.dataSource;
that._dataChangeHandler = proxy(that._onDataChange, that);
that.dataSource = HierarchicalDataSource.create(dataSource).bind(CHANGE, that._dataChangeHandler);
if (dataSource) {
if (that.options.autoBind) {
that.dataSource.fetch();
}
}
},
setDataSource: function (dataSource) {
var that = this;
that.dataSource.unbind(CHANGE, that._dataChangeHandler);
that.dataSource = dataSource.bind(CHANGE, that._dataChangeHandler);
if (dataSource) {
if (that.options.autoBind) {
that.dataSource.fetch();
}
}
},
_onDataChange: function (e) {
var node = e.node;
var items = e.items;
var options = this.options;
var item, i;
if (!node) {
this._cleanItems();
this.element.empty();
item = this._wrapItem(items[0]);
this._layout.createRoot(item, this.element.outerWidth(), this.element.outerHeight(), this.options.type === 'vertical');
this._view.createRoot(item);
this._root = item;
this._colorIdx = 0;
} else {
if (items.length) {
var root = this._getByUid(node.uid);
root.children = [];
items = new kendo.data.Query(items)._sortForGrouping(options.valueField, 'desc');
for (i = 0; i < items.length; i++) {
item = items[i];
root.children.push(this._wrapItem(item));
}
var htmlSize = this._view.htmlSize(root);
this._layout.compute(root.children, root.coord, htmlSize);
this._setColors(root.children);
this._view.render(root);
}
}
for (i = 0; i < items.length; i++) {
items[i].load();
}
if (node) {
this.trigger(DATA_BOUND, { node: node });
}
},
_cleanItems: function () {
var that = this;
that.angular('cleanup', function () {
return { elements: that.element.find('.k-leaf div,.k-treemap-title,.k-treemap-title-vertical') };
});
},
_setColors: function (items) {
var colors = this.options.colors;
var colorIdx = this._colorIdx;
var color = colors[colorIdx % colors.length];
var colorRange, item;
if (isArray(color)) {
colorRange = colorsByLength(color[0], color[1], items.length);
}
var leafNodes = false;
for (var i = 0; i < items.length; i++) {
item = items[i];
if (!defined(item.color)) {
if (colorRange) {
item.color = colorRange[i];
} else {
item.color = color;
}
}
if (!item.dataItem.hasChildren) {
leafNodes = true;
}
}
if (leafNodes) {
this._colorIdx++;
}
},
_contentSize: function (root) {
this.view.renderHeight(root);
},
_wrapItem: function (item) {
var wrap = {};
if (defined(this.options.valueField)) {
wrap.value = getField(this.options.valueField, item);
}
if (defined(this.options.colorField)) {
wrap.color = getField(this.options.colorField, item);
}
if (defined(this.options.textField)) {
wrap.text = getField(this.options.textField, item);
}
wrap.level = item.level();
wrap.dataItem = item;
return wrap;
},
_getByUid: function (uid) {
var items = [this._root];
var item;
while (items.length) {
item = items.pop();
if (item.dataItem.uid === uid) {
return item;
}
if (item.children) {
items = items.concat(item.children);
}
}
},
dataItem: function (node) {
var uid = $(node).attr(kendo.attr('uid')), dataSource = this.dataSource;
return dataSource && dataSource.getByUid(uid);
},
findByUid: function (uid) {
return this.element.find('.k-treemap-tile[' + kendo.attr('uid') + '=\'' + uid + '\']');
},
_mouseover: function (e) {
var target = $(e.target);
if (target.hasClass('k-leaf')) {
this._removeActiveState();
target.removeClass('k-state-hover').addClass('k-state-hover');
}
},
_removeActiveState: function () {
this.element.find('.k-state-hover').removeClass('k-state-hover');
},
_mouseleave: function () {
this._removeActiveState();
},
destroy: function () {
Widget.fn.destroy.call(this);
this.element.off(NS);
if (this.dataSource) {
this.dataSource.unbind(CHANGE, this._dataChangeHandler);
}
this._root = null;
kendo.unbindResize(this._resizeHandler);
kendo.destroy(this.element);
},
items: function () {
return $();
},
getSize: function () {
return kendo.dimensions(this.element);
},
_resize: function () {
var root = this._root;
if (root) {
var element = this.element;
var rootElement = element.children();
root.coord.width = element.outerWidth();
root.coord.height = element.outerHeight();
rootElement.css({
width: root.coord.width,
height: root.coord.height
});
this._resizeItems(root, rootElement);
}
},
_resizeItems: function (root, element) {
if (root.children && root.children.length) {
var elements = element.children('.k-treemap-wrap').children();
var child, childElement;
this._layout.compute(root.children, root.coord, { text: this._view.titleSize(root, element) });
for (var idx = 0; idx < root.children.length; idx++) {
child = root.children[idx];
childElement = elements.filter('[' + kendo.attr('uid') + '=\'' + child.dataItem.uid + '\']');
this._view.setItemSize(child, childElement);
this._resizeItems(child, childElement);
}
}
},
setOptions: function (options) {
var dataSource = options.dataSource;
options.dataSource = undefined;
this._originalOptions = deepExtend(this._originalOptions, options);
this.options = deepExtend({}, this._originalOptions);
this._setLayout();
this._initTheme(this.options);
Widget.fn._setEvents.call(this, options);
if (dataSource) {
this.setDataSource(HierarchicalDataSource.create(dataSource));
}
if (this.options.autoBind) {
this.dataSource.fetch();
}
}
});
var Squarified = Class.extend({
createRoot: function (root, width, height) {
root.coord = {
width: width,
height: height,
top: 0,
left: 0
};
},
leaf: function (tree) {
return !tree.children;
},
layoutChildren: function (items, coord) {
var parentArea = coord.width * coord.height;
var totalArea = 0, itemsArea = [], i;
for (i = 0; i < items.length; i++) {
itemsArea[i] = parseFloat(items[i].value);
totalArea += itemsArea[i];
}
for (i = 0; i < itemsArea.length; i++) {
items[i].area = parentArea * itemsArea[i] / totalArea;
}
var minimumSideValue = this.layoutHorizontal() ? coord.height : coord.width;
var firstElement = [items[0]];
var tail = items.slice(1);
this.squarify(tail, firstElement, minimumSideValue, coord);
},
squarify: function (tail, initElement, width, coord) {
this.computeDim(tail, initElement, width, coord);
},
computeDim: function (tail, initElement, width, coord) {
if (tail.length + initElement.length == 1) {
var element = tail.length == 1 ? tail : initElement;
this.layoutLast(element, width, coord);
return;
}
if (tail.length >= 2 && initElement.length === 0) {
initElement = [tail[0]];
tail = tail.slice(1);
}
if (tail.length === 0) {
if (initElement.length > 0) {
this.layoutRow(initElement, width, coord);
}
return;
}
var firstElement = tail[0];
if (this.worstAspectRatio(initElement, width) >= this.worstAspectRatio([firstElement].concat(initElement), width)) {
this.computeDim(tail.slice(1), initElement.concat([firstElement]), width, coord);
} else {
var newCoords = this.layoutRow(initElement, width, coord);
this.computeDim(tail, [], newCoords.dim, newCoords);
}
},
layoutLast: function (items, w, coord) {
items[0].coord = coord;
},
layoutRow: function (items, width, coord) {
if (this.layoutHorizontal()) {
return this.layoutV(items, width, coord);
} else {
return this.layoutH(items, width, coord);
}
},
orientation: 'h',
layoutVertical: function () {
return this.orientation === 'v';
},
layoutHorizontal: function () {
return this.orientation === 'h';
},
layoutChange: function () {
this.orientation = this.layoutVertical() ? 'h' : 'v';
},
worstAspectRatio: function (items, width) {
if (!items || items.length === 0) {
return MAX_VALUE;
}
var areaSum = 0, maxArea = 0, minArea = MAX_VALUE;
for (var i = 0; i < items.length; i++) {
var area = items[i].area;
areaSum += area;
minArea = minArea < area ? minArea : area;
maxArea = maxArea > area ? maxArea : area;
}
return math.max(width * width * maxArea / (areaSum * areaSum), areaSum * areaSum / (width * width * minArea));
},
compute: function (children, rootCoord, htmlSize) {
if (!(rootCoord.width >= rootCoord.height && this.layoutHorizontal())) {
this.layoutChange();
}
if (children && children.length > 0) {
var newRootCoord = {
width: rootCoord.width,
height: rootCoord.height - htmlSize.text,
top: 0,
left: 0
};
this.layoutChildren(children, newRootCoord);
}
},
layoutV: function (items, width, coord) {
var totalArea = this._totalArea(items), top = 0;
width = round(totalArea / width);
for (var i = 0; i < items.length; i++) {
var height = round(items[i].area / width);
items[i].coord = {
height: height,
width: width,
top: coord.top + top,
left: coord.left
};
top += height;
}
var ans = {
height: coord.height,
width: coord.width - width,
top: coord.top,
left: coord.left + width
};
ans.dim = math.min(ans.width, ans.height);
if (ans.dim != ans.height) {
this.layoutChange();
}
return ans;
},
layoutH: function (items, width, coord) {
var totalArea = this._totalArea(items);
var height = round(totalArea / width), top = coord.top, left = 0;
for (var i = 0; i < items.length; i++) {
items[i].coord = {
height: height,
width: round(items[i].area / height),
top: top,
left: coord.left + left
};
left += items[i].coord.width;
}
var ans = {
height: coord.height - height,
width: coord.width,
top: coord.top + height,
left: coord.left
};
ans.dim = math.min(ans.width, ans.height);
if (ans.dim != ans.width) {
this.layoutChange();
}
return ans;
},
_totalArea: function (items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].area;
}
return total;
}
});
var SquarifiedView = Class.extend({
init: function (treeMap, options) {
this.options = deepExtend({}, this.options, options);
this.treeMap = treeMap;
this.element = $(treeMap.element);
this.offset = 0;
},
titleSize: function (item, element) {
var text = element.children('.k-treemap-title');
return text.height();
},
htmlSize: function (root) {
var rootElement = this._getByUid(root.dataItem.uid);
var htmlSize = { text: 0 };
if (root.children) {
this._clean(rootElement);
var text = this._getText(root);
if (text) {
var title = this._createTitle(root);
rootElement.append(title);
this._compile(title, root.dataItem);
htmlSize.text = title.height();
}
rootElement.append(this._createWrap());
this.offset = (rootElement.outerWidth() - rootElement.innerWidth()) / 2;
}
return htmlSize;
},
_compile: function (element, dataItem) {
this.treeMap.angular('compile', function () {
return {
elements: element,
data: [{ dataItem: dataItem }]
};
});
},
_getByUid: function (uid) {
return this.element.find('.k-treemap-tile[' + kendo.attr('uid') + '=\'' + uid + '\']');
},
render: function (root) {
var rootElement = this._getByUid(root.dataItem.uid);
var children = root.children;
if (children) {
var rootWrap = rootElement.find('.k-treemap-wrap');
for (var i = 0; i < children.length; i++) {
var leaf = children[i];
var htmlElement = this._createLeaf(leaf);
rootWrap.append(htmlElement);
this._compile(htmlElement.children(), leaf.dataItem);
this.treeMap.trigger(ITEM_CREATED, { element: htmlElement });
}
}
},
createRoot: function (root) {
var htmlElement = this._createLeaf(root);
this.element.append(htmlElement);
this._compile(htmlElement.children(), root.dataItem);
this.treeMap.trigger(ITEM_CREATED, { element: htmlElement });
},
_clean: function (root) {
this.treeMap.angular('cleanup', function () {
return { elements: root.children(':not(.k-treemap-wrap)') };
});
root.css('background-color', '');
root.removeClass('k-leaf');
root.removeClass('k-inverse');
root.empty();
},
_createLeaf: function (item) {
return this._createTile(item).css('background-color', item.color).addClass('k-leaf').toggleClass('k-inverse', this._tileColorBrightness(item) > 180).append($('
').html(this._getText(item)));
},
_createTile: function (item) {
var tile = $('
');
this.setItemSize(item, tile);
if (defined(item.dataItem) && defined(item.dataItem.uid)) {
tile.attr(kendo.attr('uid'), item.dataItem.uid);
}
return tile;
},
_itemCoordinates: function (item) {
var coordinates = {
width: item.coord.width,
height: item.coord.height,
left: item.coord.left,
top: item.coord.top
};
if (coordinates.left && this.offset) {
coordinates.width += this.offset * 2;
} else {
coordinates.width += this.offset;
}
if (coordinates.top) {
coordinates.height += this.offset * 2;
} else {
coordinates.height += this.offset;
}
return coordinates;
},
setItemSize: function (item, element) {
var coordinates = this._itemCoordinates(item);
element.css({
width: coordinates.width,
height: coordinates.height,
left: coordinates.left,
top: coordinates.top
});
},
_getText: function (item) {
var text = item.text;
if (this.options.template) {
text = this._renderTemplate(item);
}
return text;
},
_renderTemplate: function (item) {
var titleTemplate = template(this.options.template);
return titleTemplate({
dataItem: item.dataItem,
text: item.text
});
},
_createTitle: function (item) {
return $('
').append($('
').html(this._getText(item)));
},
_createWrap: function () {
return $('
');
},
_tileColorBrightness: function (item) {
return colorBrightness(item.color);
}
});
var SliceAndDice = Class.extend({
createRoot: function (root, width, height, vertical) {
root.coord = {
width: width,
height: height,
top: 0,
left: 0
};
root.vertical = vertical;
},
init: function (vertical) {
this.vertical = vertical;
this.quotient = vertical ? 1 : 0;
},
compute: function (children, rootCoord, htmlSize) {
if (children.length > 0) {
var width = rootCoord.width;
var height = rootCoord.height;
if (this.vertical) {
height -= htmlSize.text;
} else {
width -= htmlSize.text;
}
var newRootCoord = {
width: width,
height: height,
top: 0,
left: 0
};
this.layoutChildren(children, newRootCoord);
}
},
layoutChildren: function (items, coord) {
var parentArea = coord.width * coord.height;
var totalArea = 0;
var itemsArea = [];
var i;
for (i = 0; i < items.length; i++) {
var item = items[i];
itemsArea[i] = parseFloat(items[i].value);
totalArea += itemsArea[i];
item.vertical = this.vertical;
}
for (i = 0; i < itemsArea.length; i++) {
items[i].area = parentArea * itemsArea[i] / totalArea;
}
this.sliceAndDice(items, coord);
},
sliceAndDice: function (items, coord) {
var totalArea = this._totalArea(items);
if (items[0].level % 2 === this.quotient) {
this.layoutHorizontal(items, coord, totalArea);
} else {
this.layoutVertical(items, coord, totalArea);
}
},
layoutHorizontal: function (items, coord, totalArea) {
var left = 0;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var width = item.area / (totalArea / coord.width);
item.coord = {
height: coord.height,
width: width,
top: coord.top,
left: coord.left + left
};
left += width;
}
},
layoutVertical: function (items, coord, totalArea) {
var top = 0;
for (var i = 0; i < items.length; i++) {
var item = items[i];
var height = item.area / (totalArea / coord.height);
item.coord = {
height: height,
width: coord.width,
top: coord.top + top,
left: coord.left
};
top += height;
}
},
_totalArea: function (items) {
var total = 0;
for (var i = 0; i < items.length; i++) {
total += items[i].area;
}
return total;
}
});
var SliceAndDiceView = SquarifiedView.extend({
htmlSize: function (root) {
var rootElement = this._getByUid(root.dataItem.uid);
var htmlSize = {
text: 0,
offset: 0
};
if (root.children) {
this._clean(rootElement);
var text = this._getText(root);
if (text) {
var title = this._createTitle(root);
rootElement.append(title);
this._compile(title, root.dataItem);
if (root.vertical) {
htmlSize.text = title.height();
} else {
htmlSize.text = title.width();
}
}
rootElement.append(this._createWrap());
this.offset = (rootElement.outerWidth() - rootElement.innerWidth()) / 2;
}
return htmlSize;
},
titleSize: function (item, element) {
var size;
if (item.vertical) {
size = element.children('.k-treemap-title').height();
} else {
size = element.children('.k-treemap-title-vertical').width();
}
return size;
},
_createTitle: function (item) {
var title;
if (item.vertical) {
title = $('
');
} else {
title = $('
');
}
return title.append($('
').html(this._getText(item)));
}
});
function getField(field, row) {
if (row === null) {
return row;
}
var get = getter(field, true);
return get(row);
}
function defined(value) {
return typeof value !== UNDEFINED;
}
function colorsByLength(min, max, length) {
var minRGBtoDecimal = rgbToDecimal(min);
var maxRGBtoDecimal = rgbToDecimal(max);
var isDarker = colorBrightness(min) - colorBrightness(max) < 0;
var colors = [];
colors.push(min);
for (var i = 0; i < length; i++) {
var rgbColor = {
r: colorByIndex(minRGBtoDecimal.r, maxRGBtoDecimal.r, i, length, isDarker),
g: colorByIndex(minRGBtoDecimal.g, maxRGBtoDecimal.g, i, length, isDarker),
b: colorByIndex(minRGBtoDecimal.b, maxRGBtoDecimal.b, i, length, isDarker)
};
colors.push(buildColorFromRGB(rgbColor));
}
colors.push(max);
return colors;
}
function colorByIndex(min, max, index, length, isDarker) {
var minColor = math.min(math.abs(min), math.abs(max));
var maxColor = math.max(math.abs(min), math.abs(max));
var step = (maxColor - minColor) / (length + 1);
var currentStep = step * (index + 1);
var color;
if (isDarker) {
color = minColor + currentStep;
} else {
color = maxColor - currentStep;
}
return color;
}
function buildColorFromRGB(color) {
return '#' + decimalToRgb(color.r) + decimalToRgb(color.g) + decimalToRgb(color.b);
}
function rgbToDecimal(color) {
color = color.replace('#', '');
var rgbColor = colorToRGB(color);
return {
r: rgbToHex(rgbColor.r),
g: rgbToHex(rgbColor.g),
b: rgbToHex(rgbColor.b)
};
}
function decimalToRgb(number) {
var result = math.round(number).toString(16).toUpperCase();
if (result.length === 1) {
result = '0' + result;
}
return result;
}
function colorToRGB(color) {
var colorLength = color.length;
var rgbColor = {};
if (colorLength === 3) {
rgbColor.r = color[0];
rgbColor.g = color[1];
rgbColor.b = color[2];
} else {
rgbColor.r = color.substring(0, 2);
rgbColor.g = color.substring(2, 4);
rgbColor.b = color.substring(4, 6);
}
return rgbColor;
}
function rgbToHex(rgb) {
return parseInt(rgb.toString(16), 16);
}
function colorBrightness(color) {
var brightness = 0;
if (color) {
color = rgbToDecimal(color);
brightness = math.sqrt(0.241 * color.r * color.r + 0.691 * color.g * color.g + 0.068 * color.b * color.b);
}
return brightness;
}
function round(value) {
var power = math.pow(10, 4);
return math.round(value * power) / power;
}
dataviz.ui.plugin(TreeMap);
}(window.kendo.jQuery));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.angular', ['kendo.core'], f);
}(function () {
var __meta__ = {
id: 'angular',
name: 'AngularJS Directives',
category: 'framework',
description: 'Adds Kendo UI for AngularJS directives',
depends: ['core'],
defer: true
};
(function ($, angular, undefined) {
'use strict';
if (!angular || !angular.injector) {
return;
}
var module = angular.module('kendo.directives', []), $injector = angular.injector(['ng']), $parse = $injector.get('$parse'), $timeout = $injector.get('$timeout'), $defaultCompile, $log = $injector.get('$log');
function withoutTimeout(f) {
var save = $timeout;
try {
$timeout = function (f) {
return f();
};
return f();
} finally {
$timeout = save;
}
}
var OPTIONS_NOW;
var createDataSource = function () {
var types = {
TreeList: 'TreeListDataSource',
TreeView: 'HierarchicalDataSource',
Scheduler: 'SchedulerDataSource',
PanelBar: '$PLAIN',
Menu: '$PLAIN',
ContextMenu: '$PLAIN'
};
var toDataSource = function (dataSource, type) {
if (type == '$PLAIN') {
return dataSource;
}
return kendo.data[type].create(dataSource);
};
return function (scope, element, role, source) {
var type = types[role] || 'DataSource';
var current = scope.$eval(source);
var ds = toDataSource(current, type);
scope.$watch(source, function (mew) {
var widget = kendoWidgetInstance(element);
if (widget && typeof widget.setDataSource == 'function') {
if (mew !== current) {
var ds = toDataSource(mew, type);
widget.setDataSource(ds);
current = mew;
}
}
});
return ds;
};
}();
var ignoredAttributes = {
kDataSource: true,
kOptions: true,
kRebind: true,
kNgModel: true,
kNgDelay: true
};
var ignoredOwnProperties = {
name: true,
title: true,
style: true
};
function createWidget(scope, element, attrs, widget, origAttr, controllers) {
if (!(element instanceof jQuery)) {
throw new Error('The Kendo UI directives require jQuery to be available before AngularJS. Please include jquery before angular in the document.');
}
var kNgDelay = attrs.kNgDelay, delayValue = scope.$eval(kNgDelay);
controllers = controllers || [];
var ngModel = controllers[0], ngForm = controllers[1];
var ctor = $(element)[widget];
if (!ctor) {
window.console.error('Could not find: ' + widget);
return null;
}
var parsed = parseOptions(scope, element, attrs, widget, ctor);
var options = parsed.options;
if (parsed.unresolved.length) {
var promises = [];
for (var i = 0, len = parsed.unresolved.length; i < len; i++) {
var unresolved = parsed.unresolved[i];
var promise = $.Deferred(function (d) {
var unwatch = scope.$watch(unresolved.path, function (newValue) {
if (newValue !== undefined) {
unwatch();
d.resolve();
}
});
}).promise();
promises.push(promise);
}
$.when.apply(null, promises).then(createIt);
return;
}
if (kNgDelay && !delayValue) {
var root = scope.$root || scope;
var register = function () {
var unregister = scope.$watch(kNgDelay, function (newValue) {
if (newValue !== undefined) {
unregister();
element.removeAttr(attrs.$attr.kNgDelay);
kNgDelay = null;
$timeout(createIt);
}
});
};
if (/^\$(digest|apply)$/.test(root.$$phase)) {
register();
} else {
scope.$apply(register);
}
return;
} else {
return createIt();
}
function createIt() {
var originalElement;
if (attrs.kRebind) {
originalElement = $($(element)[0].cloneNode(true));
}
options = parseOptions(scope, element, attrs, widget, ctor).options;
if (element.is('select')) {
(function (options) {
if (options.length > 0) {
var first = $(options[0]);
if (!/\S/.test(first.text()) && /^\?/.test(first.val())) {
first.remove();
}
}
}(element[0].options));
}
var object = ctor.call(element, OPTIONS_NOW = options).data(widget);
exposeWidget(object, scope, attrs, widget, origAttr);
scope.$emit('kendoWidgetCreated', object);
var destroyRegister = destroyWidgetOnScopeDestroy(scope, object);
if (attrs.kRebind) {
setupRebind(object, scope, element, originalElement, attrs.kRebind, destroyRegister, attrs);
}
if (attrs.kNgDisabled) {
var kNgDisabled = attrs.kNgDisabled;
var isDisabled = scope.$eval(kNgDisabled);
if (isDisabled) {
object.enable(!isDisabled);
}
bindToKNgDisabled(object, scope, element, kNgDisabled);
}
if (attrs.kNgReadonly) {
var kNgReadonly = attrs.kNgReadonly;
var isReadonly = scope.$eval(kNgReadonly);
if (isReadonly) {
object.readonly(isReadonly);
}
bindToKNgReadonly(object, scope, element, kNgReadonly);
}
if (attrs.kNgModel) {
bindToKNgModel(object, scope, attrs.kNgModel);
}
if (ngModel) {
bindToNgModel(object, scope, element, ngModel, ngForm);
}
if (object) {
propagateClassToWidgetWrapper(object, element);
}
return object;
}
}
function parseOptions(scope, element, attrs, widget, ctor) {
var role = widget.replace(/^kendo/, '');
var unresolved = [];
var optionsPath = attrs.kOptions || attrs.options;
var optionsValue = scope.$eval(optionsPath);
if (optionsPath && optionsValue === undefined) {
unresolved.push({
option: 'options',
path: optionsPath
});
}
var options = angular.extend({}, attrs.defaultOptions, optionsValue);
function addOption(name, value) {
var scopeValue = angular.copy(scope.$eval(value));
if (scopeValue === undefined) {
unresolved.push({
option: name,
path: value
});
} else {
options[name] = scopeValue;
}
}
var widgetOptions = ctor.widget.prototype.options;
var widgetEvents = ctor.widget.prototype.events;
$.each(attrs, function (name, value) {
if (name === 'source' || name === 'kDataSource' || name === 'kScopeField' || name === 'scopeField') {
return;
}
var dataName = 'data' + name.charAt(0).toUpperCase() + name.slice(1);
if (name.indexOf('on') === 0) {
var eventKey = name.replace(/^on./, function (prefix) {
return prefix.charAt(2).toLowerCase();
});
if (widgetEvents.indexOf(eventKey) > -1) {
options[eventKey] = value;
}
}
if (widgetOptions.hasOwnProperty(dataName)) {
addOption(dataName, value);
} else if (widgetOptions.hasOwnProperty(name) && !ignoredOwnProperties[name]) {
addOption(name, value);
} else if (!ignoredAttributes[name]) {
var match = name.match(/^k(On)?([A-Z].*)/);
if (match) {
var optionName = match[2].charAt(0).toLowerCase() + match[2].slice(1);
if (match[1] && name != 'kOnLabel') {
options[optionName] = value;
} else {
if (name == 'kOnLabel') {
optionName = 'onLabel';
}
addOption(optionName, value);
}
}
}
});
var dataSource = attrs.kDataSource || attrs.source;
if (dataSource) {
options.dataSource = createDataSource(scope, element, role, dataSource);
}
options.$angular = [scope];
return {
options: options,
unresolved: unresolved
};
}
function bindToKNgDisabled(widget, scope, element, kNgDisabled) {
if (kendo.ui.PanelBar && widget instanceof kendo.ui.PanelBar || kendo.ui.Menu && widget instanceof kendo.ui.Menu) {
$log.warn('k-ng-disabled specified on a widget that does not have the enable() method: ' + widget.options.name);
return;
}
scope.$watch(kNgDisabled, function (newValue, oldValue) {
if (newValue != oldValue) {
widget.enable(!newValue);
}
});
}
function bindToKNgReadonly(widget, scope, element, kNgReadonly) {
if (typeof widget.readonly != 'function') {
$log.warn('k-ng-readonly specified on a widget that does not have the readonly() method: ' + widget.options.name);
return;
}
scope.$watch(kNgReadonly, function (newValue, oldValue) {
if (newValue != oldValue) {
widget.readonly(newValue);
}
});
}
function exposeWidget(widget, scope, attrs, kendoWidget, origAttr) {
if (attrs[origAttr]) {
var set = $parse(attrs[origAttr]).assign;
if (set) {
set(scope, widget);
} else {
throw new Error(origAttr + ' attribute used but expression in it is not assignable: ' + attrs[kendoWidget]);
}
}
}
function formValue(element) {
if (/checkbox|radio/i.test(element.attr('type'))) {
return element.prop('checked');
}
return element.val();
}
var formRegExp = /^(input|select|textarea)$/i;
function isForm(element) {
return formRegExp.test(element[0].tagName);
}
function bindToNgModel(widget, scope, element, ngModel, ngForm) {
if (!widget.value) {
return;
}
var value;
if (isForm(element)) {
value = function () {
return formValue(element);
};
} else {
value = function () {
return widget.value();
};
}
ngModel.$render = function () {
var val = ngModel.$viewValue;
if (val === undefined) {
val = ngModel.$modelValue;
}
if (val === undefined) {
val = null;
}
setTimeout(function () {
if (widget) {
var kNgModel = scope[widget.element.attr('k-ng-model')];
if (kNgModel) {
val = kNgModel;
}
if (widget.options.autoBind === false && !widget.listView.bound()) {
if (val) {
widget.value(val);
}
} else {
widget.value(val);
}
}
}, 0);
};
var haveChangeOnElement = false;
if (isForm(element)) {
element.on('change', function () {
haveChangeOnElement = true;
});
}
var onChange = function (pristine) {
return function () {
var formPristine;
if (haveChangeOnElement) {
return;
}
if (pristine && ngForm) {
formPristine = ngForm.$pristine;
}
ngModel.$setViewValue(value());
if (pristine) {
ngModel.$setPristine();
if (formPristine) {
ngForm.$setPristine();
}
}
digest(scope);
};
};
widget.first('change', onChange(false));
if (!(kendo.ui.AutoComplete && widget instanceof kendo.ui.AutoComplete)) {
widget.first('dataBound', onChange(true));
}
var currentVal = value();
if (!isNaN(ngModel.$viewValue) && currentVal != ngModel.$viewValue) {
if (!ngModel.$isEmpty(ngModel.$viewValue)) {
widget.value(ngModel.$viewValue);
} else if (currentVal != null && currentVal !== '' && currentVal != ngModel.$viewValue) {
ngModel.$setViewValue(currentVal);
}
}
ngModel.$setPristine();
}
function bindToKNgModel(widget, scope, kNgModel) {
if (typeof widget.value != 'function') {
$log.warn('k-ng-model specified on a widget that does not have the value() method: ' + widget.options.name);
return;
}
var form = $(widget.element).parents('form');
var ngForm = scope[form.attr('name')];
var getter = $parse(kNgModel);
var setter = getter.assign;
var updating = false;
var valueIsCollection = kendo.ui.MultiSelect && widget instanceof kendo.ui.MultiSelect;
var length = function (value) {
return valueIsCollection ? value.length : 0;
};
var currentValueLength = length(getter(scope));
widget.$angular_setLogicValue(getter(scope));
var watchHandler = function (newValue, oldValue) {
if (newValue === undefined) {
newValue = null;
}
if (updating || newValue == oldValue && length(newValue) == currentValueLength) {
return;
}
currentValueLength = length(newValue);
widget.$angular_setLogicValue(newValue);
};
if (valueIsCollection) {
scope.$watchCollection(kNgModel, watchHandler);
} else {
scope.$watch(kNgModel, watchHandler);
}
widget.first('change', function () {
updating = true;
if (ngForm && ngForm.$pristine) {
ngForm.$setDirty();
}
digest(scope, function () {
setter(scope, widget.$angular_getLogicValue());
currentValueLength = length(getter(scope));
});
updating = false;
});
}
function destroyWidgetOnScopeDestroy(scope, widget) {
var deregister = scope.$on('$destroy', function () {
deregister();
if (widget) {
if (widget.element) {
widget.destroy();
}
widget = null;
}
});
return deregister;
}
function propagateClassToWidgetWrapper(widget, element) {
if (!(window.MutationObserver && widget.wrapper)) {
return;
}
var prevClassList = [].slice.call($(element)[0].classList);
var mo = new MutationObserver(function (changes) {
suspend();
if (!widget) {
return;
}
changes.forEach(function (chg) {
var w = $(widget.wrapper)[0];
switch (chg.attributeName) {
case 'class':
var currClassList = [].slice.call(chg.target.classList);
currClassList.forEach(function (cls) {
if (prevClassList.indexOf(cls) < 0) {
w.classList.add(cls);
if (kendo.ui.ComboBox && widget instanceof kendo.ui.ComboBox) {
widget.input[0].classList.add(cls);
}
}
});
prevClassList.forEach(function (cls) {
if (currClassList.indexOf(cls) < 0) {
w.classList.remove(cls);
if (kendo.ui.ComboBox && widget instanceof kendo.ui.ComboBox) {
widget.input[0].classList.remove(cls);
}
}
});
prevClassList = currClassList;
break;
case 'disabled':
if (typeof widget.enable == 'function' && !widget.element.attr('readonly')) {
widget.enable(!$(chg.target).attr('disabled'));
}
break;
case 'readonly':
if (typeof widget.readonly == 'function' && !widget.element.attr('disabled')) {
widget.readonly(!!$(chg.target).attr('readonly'));
}
break;
}
});
resume();
});
function suspend() {
mo.disconnect();
}
function resume() {
mo.observe($(element)[0], { attributes: true });
}
resume();
widget.first('destroy', suspend);
}
function setupRebind(widget, scope, element, originalElement, rebindAttr, destroyRegister, attrs) {
var unregister = scope.$watch(rebindAttr, function (newValue, oldValue) {
if (!widget._muteRebind && newValue !== oldValue) {
unregister();
var templateOptions = WIDGET_TEMPLATE_OPTIONS[widget.options.name];
if (templateOptions) {
templateOptions.forEach(function (name) {
var templateContents = scope.$eval(attrs['k' + name]);
if (templateContents) {
originalElement.append($(templateContents).attr(kendo.toHyphens('k' + name), ''));
}
});
}
var _wrapper = $(widget.wrapper)[0];
var _element = $(widget.element)[0];
var isUpload = widget.options.name === 'Upload';
if (isUpload) {
element = $(_element);
}
var compile = element.injector().get('$compile');
widget._destroy();
if (destroyRegister) {
destroyRegister();
}
widget = null;
if (_element) {
if (_wrapper) {
_wrapper.parentNode.replaceChild(_element, _wrapper);
}
$(element).replaceWith(originalElement);
}
compile(originalElement)(scope);
}
}, true);
digest(scope);
}
module.factory('directiveFactory', [
'$compile',
function (compile) {
var kendoRenderedTimeout;
var RENDERED = false;
$defaultCompile = compile;
var create = function (role, origAttr) {
return {
restrict: 'AC',
require: [
'?ngModel',
'^?form'
],
scope: false,
controller: [
'$scope',
'$attrs',
'$element',
function ($scope, $attrs) {
var that = this;
that.template = function (key, value) {
$attrs[key] = kendo.stringify(value);
};
$scope.$on('$destroy', function () {
that.template = null;
that = null;
});
}
],
link: function (scope, element, attrs, controllers) {
var $element = $(element);
var roleattr = role.replace(/([A-Z])/g, '-$1');
$element.attr(roleattr, $element.attr('data-' + roleattr));
$element[0].removeAttribute('data-' + roleattr);
var widget = createWidget(scope, element, attrs, role, origAttr, controllers);
if (!widget) {
return;
}
if (kendoRenderedTimeout) {
clearTimeout(kendoRenderedTimeout);
}
kendoRenderedTimeout = setTimeout(function () {
scope.$emit('kendoRendered');
if (!RENDERED) {
RENDERED = true;
$('form').each(function () {
var form = $(this).controller('form');
if (form) {
form.$setPristine();
}
});
}
});
}
};
};
return { create: create };
}
]);
var TAGNAMES = {
Editor: 'textarea',
NumericTextBox: 'input',
DatePicker: 'input',
DateTimePicker: 'input',
TimePicker: 'input',
AutoComplete: 'input',
ColorPicker: 'input',
MaskedTextBox: 'input',
MultiSelect: 'input',
Upload: 'input',
Validator: 'form',
Button: 'button',
MobileButton: 'a',
MobileBackButton: 'a',
MobileDetailButton: 'a',
ListView: 'ul',
MobileListView: 'ul',
TreeView: 'ul',
Menu: 'ul',
ContextMenu: 'ul',
ActionSheet: 'ul'
};
var SKIP_SHORTCUTS = [
'MobileView',
'MobileDrawer',
'MobileLayout',
'MobileSplitView',
'MobilePane',
'MobileModalView'
];
var MANUAL_DIRECTIVES = [
'MobileApplication',
'MobileView',
'MobileModalView',
'MobileLayout',
'MobileActionSheet',
'MobileDrawer',
'MobileSplitView',
'MobilePane',
'MobileScrollView',
'MobilePopOver'
];
angular.forEach([
'MobileNavBar',
'MobileButton',
'MobileBackButton',
'MobileDetailButton',
'MobileTabStrip',
'MobileScrollView',
'MobileScroller'
], function (widget) {
MANUAL_DIRECTIVES.push(widget);
widget = 'kendo' + widget;
module.directive(widget, function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
createWidget(scope, element, attrs, widget, widget);
}
};
});
});
function createDirectives(klass, isMobile) {
function make(directiveName, widgetName) {
module.directive(directiveName, [
'directiveFactory',
function (directiveFactory) {
return directiveFactory.create(widgetName, directiveName);
}
]);
}
var name = isMobile ? 'Mobile' : '';
name += klass.fn.options.name;
var className = name;
var shortcut = 'kendo' + name.charAt(0) + name.substr(1).toLowerCase();
name = 'kendo' + name;
var dashed = name.replace(/([A-Z])/g, '-$1');
if (SKIP_SHORTCUTS.indexOf(name.replace('kendo', '')) == -1) {
var names = name === shortcut ? [name] : [
name,
shortcut
];
angular.forEach(names, function (directiveName) {
module.directive(directiveName, function () {
return {
restrict: 'E',
replace: true,
template: function (element, attributes) {
var tag = TAGNAMES[className] || 'div';
var scopeField = attributes.kScopeField || attributes.scopeField;
return '<' + tag + ' ' + dashed + (scopeField ? '="' + scopeField + '"' : '') + '>' + element.html() + '' + tag + '>';
}
};
});
});
}
if (MANUAL_DIRECTIVES.indexOf(name.replace('kendo', '')) > -1) {
return;
}
make(name, name);
if (shortcut != name) {
make(shortcut, name);
}
}
function kendoWidgetInstance(el) {
el = $(el);
return kendo.widgetInstance(el, kendo.ui) || kendo.widgetInstance(el, kendo.mobile.ui) || kendo.widgetInstance(el, kendo.dataviz.ui);
}
function digest(scope, func) {
var root = scope.$root || scope;
var isDigesting = /^\$(digest|apply)$/.test(root.$$phase);
if (func) {
if (isDigesting) {
func();
} else {
root.$apply(func);
}
} else if (!isDigesting) {
root.$digest();
}
}
function destroyScope(scope, el) {
scope.$destroy();
if (el) {
$(el).removeData('$scope').removeData('$$kendoScope').removeData('$isolateScope').removeData('$isolateScopeNoTemplate').removeClass('ng-scope');
}
}
var pendingPatches = [];
function defadvice(klass, methodName, func) {
if ($.isArray(klass)) {
return angular.forEach(klass, function (klass) {
defadvice(klass, methodName, func);
});
}
if (typeof klass == 'string') {
var a = klass.split('.');
var x = kendo;
while (x && a.length > 0) {
x = x[a.shift()];
}
if (!x) {
pendingPatches.push([
klass,
methodName,
func
]);
return false;
}
klass = x.prototype;
}
var origMethod = klass[methodName];
klass[methodName] = function () {
var self = this, args = arguments;
return func.apply({
self: self,
next: function () {
return origMethod.apply(self, arguments.length > 0 ? arguments : args);
}
}, args);
};
return true;
}
kendo.onWidgetRegistered(function (entry) {
pendingPatches = $.grep(pendingPatches, function (args) {
return !defadvice.apply(null, args);
});
createDirectives(entry.widget, entry.prefix == 'Mobile');
});
defadvice([
'ui.Widget',
'mobile.ui.Widget'
], 'angular', function (cmd, arg) {
var self = this.self;
if (cmd == 'init') {
if (!arg && OPTIONS_NOW) {
arg = OPTIONS_NOW;
}
OPTIONS_NOW = null;
if (arg && arg.$angular) {
self.$angular_scope = arg.$angular[0];
self.$angular_init(self.element, arg);
}
return;
}
var scope = self.$angular_scope;
if (scope) {
withoutTimeout(function () {
var x = arg(), elements = x.elements, data = x.data;
if (elements.length > 0) {
switch (cmd) {
case 'cleanup':
angular.forEach(elements, function (el) {
var itemScope = $(el).data('$$kendoScope');
if (itemScope && itemScope !== scope && itemScope.$$kendoScope) {
destroyScope(itemScope, el);
}
});
break;
case 'compile':
var injector = self.element.injector();
var compile = injector ? injector.get('$compile') : $defaultCompile;
angular.forEach(elements, function (el, i) {
var itemScope;
if (x.scopeFrom) {
itemScope = x.scopeFrom;
} else {
var vars = data && data[i];
if (vars !== undefined) {
itemScope = $.extend(scope.$new(), vars);
itemScope.$$kendoScope = true;
} else {
itemScope = scope;
}
}
$(el).data('$$kendoScope', itemScope);
compile(el)(itemScope);
});
digest(scope);
break;
}
}
});
}
});
defadvice('ui.Widget', '$angular_getLogicValue', function () {
return this.self.value();
});
defadvice('ui.Widget', '$angular_setLogicValue', function (val) {
this.self.value(val);
});
defadvice('ui.Select', '$angular_getLogicValue', function () {
var item = this.self.dataItem(), valueField = this.self.options.dataValueField;
if (item) {
if (this.self.options.valuePrimitive) {
if (!!valueField) {
return item[valueField];
} else {
return item;
}
} else {
return item.toJSON();
}
} else {
return null;
}
});
defadvice('ui.Select', '$angular_setLogicValue', function (val) {
var self = this.self;
var options = self.options;
var valueField = options.dataValueField;
var text = options.text || '';
if (val === undefined) {
val = '';
}
if (valueField && !options.valuePrimitive && val) {
text = val[options.dataTextField] || '';
val = val[valueField || options.dataTextField];
}
if (self.options.autoBind === false && !self.listView.bound()) {
if (!text && val && options.valuePrimitive) {
self.value(val);
} else {
self._preselect(val, text);
}
} else {
self.value(val);
}
});
defadvice('ui.MultiSelect', '$angular_getLogicValue', function () {
var value = this.self.dataItems().slice(0);
var valueField = this.self.options.dataValueField;
if (valueField && this.self.options.valuePrimitive) {
value = $.map(value, function (item) {
return item[valueField];
});
}
return value;
});
defadvice('ui.MultiSelect', '$angular_setLogicValue', function (val) {
if (val == null) {
val = [];
}
var self = this.self;
var options = self.options;
var valueField = options.dataValueField;
var data = val;
if (valueField && !options.valuePrimitive) {
val = $.map(val, function (item) {
return item[valueField];
});
}
if (options.autoBind === false && !options.valuePrimitive && !self.listView.bound()) {
self._preselect(data, val);
} else {
self.value(val);
}
});
defadvice('ui.AutoComplete', '$angular_getLogicValue', function () {
var options = this.self.options;
var values = this.self.value().split(options.separator);
var valuePrimitive = options.valuePrimitive;
var data = this.self.dataSource.data();
var dataItems = [];
for (var idx = 0, length = data.length; idx < length; idx++) {
var item = data[idx];
var dataValue = options.dataTextField ? item[options.dataTextField] : item;
for (var j = 0; j < values.length; j++) {
if (dataValue === values[j]) {
if (valuePrimitive) {
dataItems.push(dataValue);
} else {
dataItems.push(item.toJSON());
}
break;
}
}
}
return dataItems;
});
defadvice('ui.AutoComplete', '$angular_setLogicValue', function (value) {
if (value == null) {
value = [];
}
var self = this.self, dataTextField = self.options.dataTextField;
if (dataTextField && !self.options.valuePrimitive) {
if (value.length !== undefined) {
value = $.map(value, function (item) {
return item[dataTextField];
});
} else {
value = value[dataTextField];
}
}
self.value(value);
});
defadvice('ui.Widget', '$angular_init', function (element, options) {
var self = this.self;
if (options && !$.isArray(options)) {
var scope = self.$angular_scope;
for (var i = self.events.length; --i >= 0;) {
var event = self.events[i];
var handler = options[event];
if (handler && typeof handler == 'string') {
options[event] = self.$angular_makeEventHandler(event, scope, handler);
}
}
}
});
defadvice('ui.Widget', '$angular_makeEventHandler', function (event, scope, handler) {
handler = $parse(handler);
return function (e) {
digest(scope, function () {
handler(scope, { kendoEvent: e });
});
};
});
defadvice([
'ui.Grid',
'ui.ListView',
'ui.TreeView'
], '$angular_makeEventHandler', function (event, scope, handler) {
if (event != 'change') {
return this.next();
}
handler = $parse(handler);
return function (ev) {
var widget = ev.sender;
var options = widget.options;
var cell, multiple, locals = { kendoEvent: ev }, elems, items, columns, colIdx;
if (angular.isString(options.selectable)) {
cell = options.selectable.indexOf('cell') !== -1;
multiple = options.selectable.indexOf('multiple') !== -1;
}
elems = locals.selected = this.select();
items = locals.data = [];
columns = locals.columns = [];
for (var i = 0; i < elems.length; i++) {
var item = cell ? elems[i].parentNode : elems[i];
var dataItem = widget.dataItem(item);
if (cell) {
if (angular.element.inArray(dataItem, items) < 0) {
items.push(dataItem);
}
colIdx = angular.element(elems[i]).index();
if (angular.element.inArray(colIdx, columns) < 0) {
columns.push(colIdx);
}
} else {
items.push(dataItem);
}
}
if (!multiple) {
locals.dataItem = locals.data = items[0];
locals.angularDataItem = kendo.proxyModelSetters(locals.dataItem);
locals.selected = elems[0];
}
digest(scope, function () {
handler(scope, locals);
});
};
});
defadvice('ui.Grid', '$angular_init', function (element, options) {
this.next();
if (options.columns) {
var settings = $.extend({}, kendo.Template, options.templateSettings);
angular.forEach(options.columns, function (col) {
if (col.field && !col.template && !col.format && !col.values && (col.encoded === undefined || col.encoded)) {
col.template = '
#: ' + kendo.expr(col.field, settings.paramName) + '#';
}
});
}
});
{
defadvice('mobile.ui.ButtonGroup', 'value', function (mew) {
var self = this.self;
if (mew != null) {
self.select(self.element.children('li.km-button').eq(mew));
self.trigger('change');
self.trigger('select', { index: self.selectedIndex });
}
return self.selectedIndex;
});
defadvice('mobile.ui.ButtonGroup', '_select', function () {
this.next();
this.self.trigger('change');
});
}
module.directive('kendoMobileApplication', function () {
return {
terminal: true,
link: function (scope, element, attrs) {
createWidget(scope, element, attrs, 'kendoMobileApplication', 'kendoMobileApplication');
}
};
}).directive('kendoMobileView', function () {
return {
scope: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileView', 'kendoMobileView');
},
post: function (scope, element, attrs) {
attrs._instance._layout();
attrs._instance._scroller();
}
}
};
}).directive('kendoMobileDrawer', function () {
return {
scope: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileDrawer', 'kendoMobileDrawer');
},
post: function (scope, element, attrs) {
attrs._instance._layout();
attrs._instance._scroller();
}
}
};
}).directive('kendoMobileModalView', function () {
return {
scope: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileModalView', 'kendoMobileModalView');
},
post: function (scope, element, attrs) {
attrs._instance._layout();
attrs._instance._scroller();
}
}
};
}).directive('kendoMobileSplitView', function () {
return {
terminal: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
attrs._instance = createWidget(scope, element, attrs, 'kendoMobileSplitView', 'kendoMobileSplitView');
},
post: function (scope, element, attrs) {
attrs._instance._layout();
}
}
};
}).directive('kendoMobilePane', function () {
return {
terminal: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
createWidget(scope, element, attrs, 'kendoMobilePane', 'kendoMobilePane');
}
}
};
}).directive('kendoMobileLayout', function () {
return {
link: {
pre: function (scope, element, attrs) {
createWidget(scope, element, attrs, 'kendoMobileLayout', 'kendoMobileLayout');
}
}
};
}).directive('kendoMobileActionSheet', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.find('a[k-action]').each(function () {
$(this).attr('data-' + kendo.ns + 'action', $(this).attr('k-action'));
});
createWidget(scope, element, attrs, 'kendoMobileActionSheet', 'kendoMobileActionSheet');
}
};
}).directive('kendoMobilePopOver', function () {
return {
terminal: true,
link: {
pre: function (scope, element, attrs) {
attrs.defaultOptions = scope.viewOptions;
createWidget(scope, element, attrs, 'kendoMobilePopOver', 'kendoMobilePopOver');
}
}
};
}).directive('kendoViewTitle', function () {
return {
restrict: 'E',
replace: true,
template: function (element) {
return '
' + element.html() + '';
}
};
}).directive('kendoMobileHeader', function () {
return {
restrict: 'E',
link: function (scope, element) {
element.addClass('km-header').attr('data-role', 'header');
}
};
}).directive('kendoMobileFooter', function () {
return {
restrict: 'E',
link: function (scope, element) {
element.addClass('km-footer').attr('data-role', 'footer');
}
};
}).directive('kendoMobileScrollViewPage', function () {
return {
restrict: 'E',
replace: true,
template: function (element) {
return '
' + element.html() + '
';
}
};
});
angular.forEach([
'align',
'icon',
'rel',
'transition',
'actionsheetContext'
], function (attr) {
var kAttr = 'k' + attr.slice(0, 1).toUpperCase() + attr.slice(1);
module.directive(kAttr, function () {
return {
restrict: 'A',
priority: 2,
link: function (scope, element, attrs) {
element.attr(kendo.attr(kendo.toHyphens(attr)), scope.$eval(attrs[kAttr]));
}
};
});
});
var WIDGET_TEMPLATE_OPTIONS = {
'TreeMap': ['Template'],
'MobileListView': [
'HeaderTemplate',
'Template'
],
'MobileScrollView': [
'EmptyTemplate',
'Template'
],
'Grid': [
'AltRowTemplate',
'DetailTemplate',
'RowTemplate'
],
'ListView': [
'EditTemplate',
'Template',
'AltTemplate'
],
'Pager': [
'SelectTemplate',
'LinkTemplate'
],
'PivotGrid': [
'ColumnHeaderTemplate',
'DataCellTemplate',
'RowHeaderTemplate'
],
'Scheduler': [
'AllDayEventTemplate',
'DateHeaderTemplate',
'EventTemplate',
'MajorTimeHeaderTemplate',
'MinorTimeHeaderTemplate'
],
'TreeView': ['Template'],
'Validator': ['ErrorTemplate']
};
(function () {
var templateDirectives = {};
angular.forEach(WIDGET_TEMPLATE_OPTIONS, function (templates, widget) {
angular.forEach(templates, function (template) {
if (!templateDirectives[template]) {
templateDirectives[template] = [];
}
templateDirectives[template].push('?^^kendo' + widget);
});
});
angular.forEach(templateDirectives, function (parents, directive) {
var templateName = 'k' + directive;
var attrName = kendo.toHyphens(templateName);
module.directive(templateName, function () {
return {
restrict: 'A',
require: parents,
terminal: true,
compile: function ($element, $attrs) {
if ($attrs[templateName] !== '') {
return;
}
$element.removeAttr(attrName);
var template = $element[0].outerHTML;
return function (scope, element, attrs, controllers) {
var controller;
while (!controller && controllers.length) {
controller = controllers.shift();
}
if (!controller) {
$log.warn(attrName + ' without a matching parent widget found. It can be one of the following: ' + parents.join(', '));
} else {
controller.template(templateName, template);
$element.remove();
}
};
}
};
});
});
}());
}(window.kendo.jQuery, window.angular));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.webcomponents', ['kendo.core'], f);
}(function () {
var __meta__ = {
id: 'webcomponents',
name: 'Web Components',
category: 'framework',
description: 'Adds Kendo UI custom elements for Web Components',
depends: ['core']
};
(function ($, angular, undefined) {
if (!kendo.support.customElements || kendo.webComponents.length) {
return;
}
if (angular && (angular.version.major == 1 || angular.injector)) {
return;
}
var TAGNAMES = {
editor: 'textarea',
numerictextbox: 'input',
datepicker: 'input',
datetimepicker: 'input',
timepicker: 'input',
autocomplete: 'input',
colorpicker: 'input',
maskedtextbox: 'input',
dropdownlist: 'select',
multiselect: 'select',
upload: 'input',
validator: 'form',
button: 'button',
mobilebutton: 'a',
mobilebackbutton: 'a',
mobiledetailbutton: 'a',
listview: 'ul',
mobilelistview: 'ul',
treeview: 'ul',
menu: 'ul',
contextmenu: 'ul',
actionsheet: 'ul'
};
var EVENT_PREFIX = 'on-';
var registered = [];
kendo.onWidgetRegistered(function (entry) {
var elementName = entry.prefix + entry.widget.prototype.options.name.toLowerCase();
if (registered.indexOf(elementName) === -1) {
registered.push(elementName);
registerElement(elementName, entry.widget);
}
});
var jsonRegExp = /^\s*(?:\{(?:.|\r\n|\n)*\}|\[(?:.|\r\n|\n)*\])\s*$/;
var jsonFormatRegExp = /^\{(\d+)(:[^\}]+)?\}|^\[[A-Za-z_]*\]$/;
var numberRegExp = /^(\+|-?)\d+(\.?)\d*$/;
function parseOption(element, option) {
var value = element.getAttribute(option);
if (value === null) {
value = undefined;
} else if (value === 'null') {
value = null;
} else if (value === 'true') {
value = true;
} else if (value === 'false') {
value = false;
} else if (numberRegExp.test(value)) {
value = parseFloat(value);
} else if (jsonRegExp.test(value) && !jsonFormatRegExp.test(value)) {
value = new Function('return (' + value + ')')();
}
return value;
}
function parseOptions(element, options) {
var result = {};
Object.keys(options).concat('dataSource').forEach(function (name) {
if (element.hasAttribute(kendo.toHyphens(name))) {
result[name] = parseOption(element, kendo.toHyphens(name));
}
});
return result;
}
function cloneEvent(e) {
var result = {};
Object.keys(e).forEach(function (key) {
if (key[0] != '_') {
result[key] = e[key];
}
});
return result;
}
function eventHandler(eventName, e) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent(eventName, false, true, cloneEvent(e));
this.dispatchEvent(event);
if (event.defaultPrevented) {
e.preventDefault();
}
}
function expose(component, obj) {
var props = Object.keys(obj);
for (var idx = 0; idx <= props.length; idx++) {
if (typeof obj[props[idx]] === 'function') {
if (!component[props[idx]]) {
component[props[idx]] = obj[props[idx]].bind(component.widget);
}
} else {
if (props[idx] === 'options') {
continue;
}
component[props[idx]] = component[props[idx]] || obj[props[idx]];
}
}
}
function registerElement(name, widget) {
var options = widget.prototype.options;
var prototype = Object.create(HTMLElement.prototype);
Object.defineProperty(prototype, 'options', {
get: function () {
return this.widget.options;
},
set: function (options) {
var instance = this.widget;
options = $.extend(true, {}, instance.options, options);
var _wrapper = $(instance.wrapper)[0];
var _element = $(instance.element)[0];
instance._destroy();
var newElement = document.createElement(TAGNAMES[name] || 'div');
if (_wrapper && _element) {
_wrapper.parentNode.replaceChild(_element, _wrapper);
$(_element).replaceWith(newElement);
}
if (instance.value) {
options.value = instance.value();
}
instance.init(newElement, options);
this.bindEvents();
}
});
prototype.bindEvents = function () {
widget.prototype.events.forEach(function (eventName) {
this.widget.bind(eventName, eventHandler.bind(this, eventName));
if (this.hasAttribute(EVENT_PREFIX + eventName)) {
this.bind(eventName, function (e) {
window[this.getAttribute(EVENT_PREFIX + eventName)].call(this, e);
}.bind(this));
}
}.bind(this));
};
prototype.attachedCallback = function () {
var that = this;
var element = document.createElement(TAGNAMES[name] || 'div');
$(element).append(that.childNodes);
$(element).attr('class', $(that).attr('class'));
$(element).attr('style', $(that).attr('style'));
that.appendChild(element);
that.widget = new widget(element, parseOptions(that, options));
var obj = that.widget;
do {
expose(that, obj);
} while (obj = Object.getPrototypeOf(obj));
this.bindEvents();
};
prototype.detachedCallback = function () {
kendo.destroy(this.element);
};
kendo.webComponents.push('kendo-' + name);
document.registerElement('kendo-' + name, { prototype: prototype });
}
}(window.kendo.jQuery, window.angular));
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.angular2', [
'kendo.core',
'kendo.webcomponents'
], f);
}(function () {
var __meta__ = {
id: 'angular2',
name: 'Angular 2',
category: 'framework',
description: 'Supports angular2 value accessors',
depends: ['core']
};
(function (kendo, System) {
if (!System || !System.register) {
return;
}
var __decorate = this && this.__decorate || function (decorators, target, key, desc) {
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') {
return Reflect.decorate(decorators, target, key, desc);
}
switch (arguments.length) {
case 2:
return decorators.reduceRight(function (o, d) {
return d && d(o) || o;
}, target);
case 3:
return decorators.reduceRight(function (o, d) {
return d && d(target, key), void 0;
}, void 0);
case 4:
return decorators.reduceRight(function (o, d) {
return d && d(target, key, o) || o;
}, desc);
}
};
var __metadata = this && this.__metadata || function (k, v) {
if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function') {
return Reflect.metadata(k, v);
}
};
System.register('kendo/angular2', ['angular2/angular2'], function (exports_1) {
var angular2_1;
var KendoValueAccessor;
return {
setters: [function (_angular2_1) {
angular2_1 = _angular2_1;
}],
execute: function () {
KendoValueAccessor = function () {
function KendoValueAccessor(cd, elementRef) {
var _this = this;
this.elementRef = elementRef;
this.onChange = function (_) {
};
this.onTouched = function () {
};
this.element = elementRef.nativeElement;
this.element.addEventListener('change', function () {
_this.onChange(_this.element.value());
});
this.element.addEventListener('spin', function () {
_this.onChange(_this.element.value());
});
cd.valueAccessor = this;
this.cd = cd;
cd.valueAccessor = this;
}
KendoValueAccessor.prototype.writeValue = function (value) {
this.element.value(value);
};
KendoValueAccessor.prototype.registerOnChange = function (fn) {
this.onChange = fn;
};
KendoValueAccessor.prototype.registerOnTouched = function (fn) {
this.onTouched = fn;
};
KendoValueAccessor = __decorate([
angular2_1.Directive({ selector: kendo.webComponents.join(',') }),
__metadata('design:paramtypes', [
angular2_1.NgControl,
angular2_1.ElementRef
])
], KendoValueAccessor);
return KendoValueAccessor;
}();
exports_1('KendoValueAccessor', KendoValueAccessor);
}
};
});
}(window.kendo, window.System));
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));
(function (f, define) {
define('kendo.dataviz', [
'kendo.core',
'kendo.fx',
'kendo.router',
'kendo.view',
'kendo.data.odata',
'kendo.data.xml',
'kendo.data',
'kendo.data.signalr',
'kendo.binder',
'kendo.userevents',
'kendo.draganddrop',
'kendo.mobile.scroller',
'kendo.popup',
'kendo.tooltip',
'kendo.drawing',
'kendo.dataviz.core',
'kendo.dataviz.themes',
'kendo.dataviz.chart',
'kendo.dataviz.chart.polar',
'kendo.dataviz.chart.funnel',
'kendo.dataviz.gauge',
'kendo.dataviz.barcode',
'kendo.dataviz.qrcode',
'kendo.dataviz.stock',
'kendo.dataviz.sparkline',
'kendo.dataviz.map',
'kendo.dataviz.diagram',
'kendo.dataviz.treemap',
'kendo.angular',
'kendo.webcomponents',
'kendo.angular2'
], f);
}(function () {
'bundle all';
return window.kendo;
}, typeof define == 'function' && define.amd ? define : function (a1, a2, a3) {
(a3 || a2)();
}));