@progress/kendo-ui
Version:
This package is part of the [Kendo UI for jQuery](http://www.telerik.com/kendo-ui) suite.
1,314 lines (1,166 loc) • 99.1 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__(880);
/***/ }),
/***/ 3:
/***/ (function(module, exports) {
module.exports = function() { throw new Error("define cannot be used indirect"); };
/***/ }),
/***/ 859:
/***/ (function(module, exports) {
module.exports = require("../../kendo.drawing");
/***/ }),
/***/ 873:
/***/ (function(module, exports) {
module.exports = require("./svg");
/***/ }),
/***/ 880:
/***/ (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__(859), __webpack_require__(873) ], __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 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,
util = kendo.drawing.util,
defined = util.defined,
inArray = $.inArray,
proxy = $.proxy;
// Constants ==============================================================
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.90,
FRICTION_MOBILE = 0.93,
VELOCITY_MULTIPLIER = 5,
TRANSPARENT = "transparent",
PAN = "pan",
ROTATED = "rotated",
SOURCE = "source",
TARGET = "target",
HANDLE_NAMES = {
"-1": SOURCE,
"1": TARGET
};
diagram.Cursors = Cursors;
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++) {
//todo: define a Lerp function instead
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);
}
});
/**
* Holds the undoredo state when performing a rotation, translation or scaling. The adorner is optional.
* @type {*}
*/
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]);
// the 'layout' property, if implemented, lets the shape itself work out what to do with the new bounds
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);
}
});
/**
* Undo-redo service.
*/
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"],
/**
* Starts the collection of units. Add those with
* the addCompositeItem method and call commit. Or cancel to forget about it.
*/
begin: function () {
this.composite = new CompositeUnit();
},
/**
* Cancels the collection process of unit started with 'begin'.
*/
cancel: function () {
this.composite = undefined;
},
/**
* Commits a batch of units.
*/
commit: function (execute) {
if (this.composite.units.length > 0) {
this._restart(this.composite, execute);
}
this.composite = undefined;
},
/**
* Adds a unit as part of the begin-commit batch.
* @param undoUnit
*/
addCompositeItem: function (undoUnit) {
if (this.composite) {
this.composite.add(undoUnit);
} else {
this.add(undoUnit);
}
},
/**
* Standard addition of a unit. See also the batch version; begin-addCompositeUnit-commit methods.
* @param undoUnit The unit to be added.
* @param execute If false, the unit will be added but not executed.
*/
add: function (undoUnit, execute) {
this._restart(undoUnit, execute);
},
/**
* Returns the number of undoable unit in the stack.
* @returns {Number}
*/
pop: function() {
if (this.index > 0) {
this.stack.pop();
this.index--;
}
},
count: function () {
return this.stack.length;
},
/**
* Rollback of the unit on top of the stack.
*/
undo: function () {
if (this.index > 0) {
this.index--;
this.stack[this.index].undo();
this.trigger("undone");
}
},
/**
* Redo of the last undone action.
*/
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) {
// throw away anything beyond this point if this is a new branch
this.stack.splice(this.index, this.stack.length - this.index);
this.stack.push(composite);
if (execute !== false) {
this.redo();
} else {
this.index++;
}
// check the capacity
if (this.stack.length > this.capacity) {
this.stack.splice(0, this.stack.length - this.capacity);
this.index = this.capacity; //points to the end of the stack
}
},
/**
* Clears the stack.
*/
clear: function () {
this.stack = [];
this.index = 0;
}
});
// Tools =========================================
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;
}
});
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) && !defined(toolService.hoveredItem);
} else {
enabled = meta[options.key + "Key"];
}
}
return options !== false && enabled && !defined(toolService.hoveredAdorner) && !defined(toolService._hoveredConnector);
},
start: function () {
this.scroller.enable();
},
move: function () {
},//the tool itself should not handle the scrolling. Let kendo scroller take care of this part. Check _move
_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;
}
});
/**
* The tool handling the transformations via the adorner.
* @type {*}
*/
var PointerTool = Class.extend({
init: function (toolService) {
this.toolService = toolService;
},
tryActivate: function () {
return true; // the pointer tool is last and handles all others requests.
},
start: function (p, meta) {
var toolService = this.toolService,
diagram = toolService.diagram,
hoveredItem = toolService.hoveredItem;
if (hoveredItem) {
toolService.selectSingle(hoveredItem, meta);
if (hoveredItem.adorner) { //connection
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 () {
var diagram = this.toolService.diagram,
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();
}
}
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 selectable = toolService.diagram.options.selectable;
var enabled = selectable && selectable.multiple !== false;
if (enabled) {
if (selectable.key && selectable.key != "none") {
enabled = meta[selectable.key + "Key"];
} else {
enabled = noMeta(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 toolService = this.toolService,
diagram = toolService.diagram,
connector = toolService._hoveredConnector,
connection = diagram._createConnection({}, connector._c, p);
if (canDrag(connection) && !diagram.trigger(DRAG_START, { shapes: [], connections: [connection], connectionHandle: TARGET }) && diagram._addConnection(connection)) {
toolService._connectionManipulation(connection, connector._c.shape, true);
toolService._removeHover();
toolService.selectSingle(toolService.activeConnection, meta);
if (meta.type == "touchmove") {
diagram._cachedTouchTarget = connector.visual;
}
} else {
connection.source(null);
toolService.end(p);
}
},
move: function (p) {
var toolService = this.toolService;
var connection = toolService.activeConnection;
connection.target(p);
toolService.diagram.trigger(DRAG, { shapes: [], connections: [connection], connectionHandle: TARGET });
return true;
},
end: function (p) {
var toolService = this.toolService,
d = toolService.diagram,
connection = toolService.activeConnection,
hoveredItem = toolService.hoveredItem,
connector = toolService._hoveredConnector,
target,
cachedTouchTarget = d._cachedTouchTarget;
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], connectionHandle: TARGET })) {
connection.updateModel();
d._syncConnectionChanges();
} else {
d.remove(connection, false);
d.undoRedoService.pop();
}
toolService._connectionManipulation();
if(cachedTouchTarget) {
d._connectorsAdorner.visual.remove(cachedTouchTarget);
d._cachedTouchTarget = null;
}
},
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 = selectable !== false &&
item && item.path && !(item.isSelected && meta.ctrlKey);
if (isActive) {
this._c = item;
}
return isActive;
},
start: function (p, meta) {
var toolService = this.toolService;
var connection = this._c;
toolService.selectSingle(connection, meta);
var adorner = connection.adorner;
var handle, name;
if (adorner) {
handle = adorner._hitTest(p);
name = HANDLE_NAMES[handle];
}
if (canDrag(connection) && adorner && !toolService.diagram.trigger(DRAG_START, { shapes: [], connections: [connection], connectionHandle: name })) {
this.handle = handle;
this.handleName = name;
adorner.start(p);
} else {
toolService.startPoint = p;
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], connectionHandle: this.handleName });
return true;
}
},
end: function (p) {
var connection = this._c;
var adorner = connection.adorner;
var toolService = this.toolService;
var diagram = toolService.diagram;
if (adorner) {
if (canDrag(connection)) {
var unit = adorner.stop(p);
if (!diagram.trigger(DRAG_END, { shapes: [], connections: [connection], connectionHandle: this.handleName })) {
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;
}
/**
* The service managing the tools.
* @type {*}
*/
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)
]; // the order matters.
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.diagram.canvas.surface.suspendTracking();
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.diagram.canvas.surface.resumeTracking();
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) {// ctrl or option
if (testKey(key, "a")) {// A: select all
diagram.selectAll();
diagram._destroyToolBar();
return true;
} else if (testKey(key, "z")) {// Z: undo
diagram.undo();
diagram._destroyToolBar();
return true;
} else if (testKey(key, "y")) {// y: redo
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) {// del: deletion
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) {// ESC: stop any action
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;
},
selectSingle: function(item, meta) {
var diagram = this.diagram;
var selectable = diagram.options.selectable;
if (selectable && !item.isSelected && item.options.selectable !== false) {
var addToSelection = meta.ctrlKey && selectable.multiple !== false;
diagram.select(item, { addToSelection: addToSelection });
}
},
_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; // activating the first available tool in the loop.
}
}
},
_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});
// workaround for IE 7 issue in which the elements overflow the container after setting 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; // Shape, connection or connector
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;
// connectors
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) { // hit testing for resizers or rotator, otherwise if (0,0) than pass through.
return;
}
hit = undefined;
} else {
this.hoveredAdorner = undefined;
}
if (!this.activeTool || this.activeTool.type !== "ConnectionTool") {
var selectedConnections = []; // only the connections should have higher presence because the connection edit point is on top of connector.
// TODO: This should be reworked. The connection adorner should be one for all selected connections and should be hit tested prior the connections and shapes itself.
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;
}
}
}
});
// Routing =========================================
/**
* Base class for connection routers.
*/