@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,300 lines (1,111 loc) • 206 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ({
/***/ 0:
/***/ (function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(867);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 856:
/***/ (function(module, exports) {
module.exports = require("../../kendo.data");
/***/ }),
/***/ 857:
/***/ (function(module, exports) {
module.exports = require("../../kendo.dataviz.core");
/***/ }),
/***/ 858:
/***/ (function(module, exports) {
module.exports = require("../../kendo.dataviz.themes");
/***/ }),
/***/ 867:
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (f, define) {
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(856), __webpack_require__(869), __webpack_require__(870),
__webpack_require__(871),
__webpack_require__(872),
__webpack_require__(868),
__webpack_require__(857),
__webpack_require__(858),
__webpack_require__(873),
__webpack_require__(874),
__webpack_require__(875) ], __WEBPACK_AMD_DEFINE_FACTORY__ = (f), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
})(function () {
(function ($, undefined) {
// Imports ================================================================
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,
outerWidth = kendo._outerWidth,
outerHeight = kendo._outerHeight,
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 = draw.util.defined,
isArray = $.isArray,
isFunction = kendo.isFunction,
isString = Utils.isString,
isPlainObject = $.isPlainObject,
math = Math;
// Constants ==============================================================
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 = '<a class="k-button k-button-icontext #=className#" href="\\#"><span class="#=iconClass# #=imageClass#"></span>#=text#</a>',
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-i-cancel",
className: "k-diagram-cancel",
iconClass: "k-icon"
},
update: {
text: "Update",
imageClass: "k-i-checkmark",
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, connectors) {
var minimumDistance = MAXINT, resCtr, connector;
for (var i = 0; i < connectors.length; i++) {
connector = connectors[i];
if (!isAutoConnector(connector)) {
var dist = point.distanceTo(connector.position());
if (dist < minimumDistance) {
minimumDistance = dist;
resCtr = connector;
}
}
}
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 () {
// the options json object describes the shape perfectly. So this object can serve as shape serialization.
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();
},
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();
},
redrawVisual: function() {
this._redrawVisual();
if (this.options.content) {
this._template();
this.content(this.options.content);
}
},
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();
}
},
/**
* Returns a clone of this shape.
* @returns {Shape}
*/
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) { // we assume the center is always the center of the shape.
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) { // in, out, undefined = both
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();
});
},
/**
* Gets a connector of this shape either by the connector's supposed name or
* via a Point in which case the closest connector will be returned.
* @param nameOrPoint The name of a Connector or a Point.
* @returns {Connector}
*/
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.connectors);
} 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()}); // the trigger modifies the arguments internally.
}
},
_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()) { // rect selection
return Intersect.rects(value, bounds, angle ? angle : 0);
} else { // point
rotatedPoint = value.clone().rotate(bounds.center(), angle); // cloning is important because rotate modifies the point inline.
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)) { // custom template
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);
}
});
/**
* The visual link between two Shapes through the intermediate of Connectors.
*/
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,
toConnector: 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();
}
}
}
}
},
/**
* Gets the Point where the source of the connection resides.
* If the endpoint in Auto-connector the location of the resolved connector will be returned.
* If the endpoint is floating the location of the endpoint is returned.
*/
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) { // detach
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;
}
},
/**
* Gets or sets the PathDefiner of the sourcePoint.
* The left part of this definer is always null since it defines the source tangent.
* @param value
* @returns {*}
*/
sourceDefiner: function (value) {
if (value) {
if (value instanceof diagram.PathDefiner) {
value.left = null;
this._sourceDefiner = value;
this.source(value.point); // refresh implicit here
} 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;
}
},
/**
* Gets the Point where the target of the connection resides.
*/
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) { // detach
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.ConnectionEd