/*!
* bpmn-js - bpmn-modeler v0.25.0
* Copyright 2014 - 2017 camunda Services GmbH and other contributors
*
* Released under the bpmn.io license
* http://bpmn.io/license
*
* Source Code: https://github.com/bpmn-io/bpmn-js
*
* Date: 2017-11-21
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.BpmnJS = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o' +
'' +
' ' +
' ' +
'' +
'' +
'' +
' ' +
' ' +
' ' +
' ' +
'';
/**
* A modeler for BPMN 2.0 diagrams.
*
*
* ## Extending the Modeler
*
* In order to extend the viewer pass extension modules to bootstrap via the
* `additionalModules` option. An extension module is an object that exposes
* named services.
*
* The following example depicts the integration of a simple
* logging component that integrates with interaction events:
*
*
* ```javascript
*
* // logging component
* function InteractionLogger(eventBus) {
* eventBus.on('element.hover', function(event) {
* console.log()
* })
* }
*
* InteractionLogger.$inject = [ 'eventBus' ]; // minification save
*
* // extension module
* var extensionModule = {
* __init__: [ 'interactionLogger' ],
* interactionLogger: [ 'type', InteractionLogger ]
* };
*
* // extend the viewer
* var bpmnModeler = new Modeler({ additionalModules: [ extensionModule ] });
* bpmnModeler.importXML(...);
* ```
*
*
* ## Customizing / Replacing Components
*
* You can replace individual diagram components by redefining them in override modules.
* This works for all components, including those defined in the core.
*
* Pass in override modules via the `options.additionalModules` flag like this:
*
* ```javascript
* function CustomContextPadProvider(contextPad) {
*
* contextPad.registerProvider(this);
*
* this.getContextPadEntries = function(element) {
* // no entries, effectively disable the context pad
* return {};
* };
* }
*
* CustomContextPadProvider.$inject = [ 'contextPad' ];
*
* var overrideModule = {
* contextPadProvider: [ 'type', CustomContextPadProvider ]
* };
*
* var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]});
* ```
*
* @param {Object} [options] configuration options to pass to the viewer
* @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
* @param {String|Number} [options.width] the width of the viewer
* @param {String|Number} [options.height] the height of the viewer
* @param {Object} [options.moddleExtensions] extension packages to provide
* @param {Array} [options.modules] a list of modules to override the default modules
* @param {Array} [options.additionalModules] a list of modules to use with the default modules
*/
function Modeler(options) {
Viewer.call(this, options);
// hook ID collection into the modeler
this.on('import.parse.complete', function(event) {
if (!event.error) {
this._collectIds(event.definitions, event.context);
}
}, this);
this.on('diagram.destroy', function() {
this.get('moddle').ids.clear();
}, this);
}
inherits(Modeler, Viewer);
module.exports = Modeler;
module.exports.Viewer = Viewer;
module.exports.NavigatedViewer = NavigatedViewer;
/**
* Create a new diagram to start modeling.
*
* @param {Function} [done]
*/
Modeler.prototype.createDiagram = function(done) {
return this.importXML(initialDiagram, done);
};
/**
* Create a moddle instance, attaching ids to it.
*
* @param {Object} options
*/
Modeler.prototype._createModdle = function(options) {
var moddle = Viewer.prototype._createModdle.call(this, options);
// attach ids to moddle to be able to track
// and validated ids in the BPMN 2.0 XML document
// tree
moddle.ids = new Ids([ 32, 36, 1 ]);
return moddle;
};
/**
* Collect ids processed during parsing of the
* definitions object.
*
* @param {ModdleElement} definitions
* @param {Context} context
*/
Modeler.prototype._collectIds = function(definitions, context) {
var moddle = definitions.$model,
ids = moddle.ids,
id;
// remove references from previous import
ids.clear();
for (id in context.elementsById) {
ids.claim(id, context.elementsById[id]);
}
};
Modeler.prototype._interactionModules = [
// non-modeling components
_dereq_(256),
_dereq_(257),
_dereq_(260)
];
Modeler.prototype._modelingModules = [
// modeling components
_dereq_(139),
_dereq_(145),
_dereq_(206),
_dereq_(224),
_dereq_(10),
_dereq_(18),
_dereq_(12),
_dereq_(22),
_dereq_(27),
_dereq_(66),
_dereq_(72),
_dereq_(77),
_dereq_(87)
];
// modules the modeler is composed of
//
// - viewer modules
// - interaction modules
// - modeling modules
Modeler.prototype._modules = [].concat(
Modeler.prototype._modules,
Modeler.prototype._interactionModules,
Modeler.prototype._modelingModules);
},{"10":10,"12":12,"139":139,"145":145,"18":18,"2":2,"206":206,"22":22,"224":224,"256":256,"257":257,"260":260,"27":27,"288":288,"289":289,"3":3,"66":66,"72":72,"77":77,"87":87}],2:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var Viewer = _dereq_(3);
/**
* A viewer that includes mouse navigation facilities
*
* @param {Object} options
*/
function NavigatedViewer(options) {
Viewer.call(this, options);
}
inherits(NavigatedViewer, Viewer);
module.exports = NavigatedViewer;
NavigatedViewer.prototype._navigationModules = [
_dereq_(260),
_dereq_(256)
];
NavigatedViewer.prototype._modules = [].concat(
NavigatedViewer.prototype._modules,
NavigatedViewer.prototype._navigationModules);
},{"256":256,"260":260,"289":289,"3":3}],3:[function(_dereq_,module,exports){
/**
* The code in the area
* must not be changed.
*
* @see http://bpmn.io/license for more information.
*/
'use strict';
var assign = _dereq_(431),
omit = _dereq_(436),
isNumber = _dereq_(425);
var domify = _dereq_(449),
domQuery = _dereq_(452),
domRemove = _dereq_(453);
var innerSVG = _dereq_(480);
var Diagram = _dereq_(117),
BpmnModdle = _dereq_(99);
var inherits = _dereq_(289);
var Importer = _dereq_(90);
function checkValidationError(err) {
// check if we can help the user by indicating wrong BPMN 2.0 xml
// (in case he or the exporting tool did not get that right)
var pattern = /unparsable content <([^>]+)> detected([\s\S]*)$/;
var match = pattern.exec(err.message);
if (match) {
err.message =
'unparsable content <' + match[1] + '> detected; ' +
'this may indicate an invalid BPMN 2.0 diagram file' + match[2];
}
return err;
}
var DEFAULT_OPTIONS = {
width: '100%',
height: '100%',
position: 'relative'
};
/**
* Ensure the passed argument is a proper unit (defaulting to px)
*/
function ensureUnit(val) {
return val + (isNumber(val) ? 'px' : '');
}
/**
* A viewer for BPMN 2.0 diagrams.
*
* Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include
* additional features.
*
*
* ## Extending the Viewer
*
* In order to extend the viewer pass extension modules to bootstrap via the
* `additionalModules` option. An extension module is an object that exposes
* named services.
*
* The following example depicts the integration of a simple
* logging component that integrates with interaction events:
*
*
* ```javascript
*
* // logging component
* function InteractionLogger(eventBus) {
* eventBus.on('element.hover', function(event) {
* console.log()
* })
* }
*
* InteractionLogger.$inject = [ 'eventBus' ]; // minification save
*
* // extension module
* var extensionModule = {
* __init__: [ 'interactionLogger' ],
* interactionLogger: [ 'type', InteractionLogger ]
* };
*
* // extend the viewer
* var bpmnViewer = new Viewer({ additionalModules: [ extensionModule ] });
* bpmnViewer.importXML(...);
* ```
*
* @param {Object} [options] configuration options to pass to the viewer
* @param {DOMElement} [options.container] the container to render the viewer in, defaults to body.
* @param {String|Number} [options.width] the width of the viewer
* @param {String|Number} [options.height] the height of the viewer
* @param {Object} [options.moddleExtensions] extension packages to provide
* @param {Array} [options.modules] a list of modules to override the default modules
* @param {Array} [options.additionalModules] a list of modules to use with the default modules
*/
function Viewer(options) {
options = assign({}, DEFAULT_OPTIONS, options);
this._moddle = this._createModdle(options);
this._container = this._createContainer(options);
/* */
addProjectLogo(this._container);
/* */
this._init(this._container, this._moddle, options);
}
inherits(Viewer, Diagram);
module.exports = Viewer;
/**
* Parse and render a BPMN 2.0 diagram.
*
* Once finished the viewer reports back the result to the
* provided callback function with (err, warnings).
*
* ## Life-Cycle Events
*
* During import the viewer will fire life-cycle events:
*
* * import.parse.start (about to read model from xml)
* * import.parse.complete (model read; may have worked or not)
* * import.render.start (graphical import start)
* * import.render.complete (graphical import finished)
* * import.done (everything done)
*
* You can use these events to hook into the life-cycle.
*
* @param {String} xml the BPMN 2.0 xml
* @param {Function} [done] invoked with (err, warnings=[])
*/
Viewer.prototype.importXML = function(xml, done) {
// done is optional
done = done || function() {};
var self = this;
// hook in pre-parse listeners +
// allow xml manipulation
xml = this._emit('import.parse.start', { xml: xml }) || xml;
this._moddle.fromXML(xml, 'bpmn:Definitions', function(err, definitions, context) {
// hook in post parse listeners +
// allow definitions manipulation
definitions = self._emit('import.parse.complete', {
error: err,
definitions: definitions,
context: context
}) || definitions;
var parseWarnings = context.warnings;
if (err) {
err = checkValidationError(err);
self._emit('import.done', { error: err, warnings: parseWarnings });
return done(err, parseWarnings);
}
self.importDefinitions(definitions, function(err, importWarnings) {
var allWarnings = [].concat(parseWarnings, importWarnings || []);
self._emit('import.done', { error: err, warnings: allWarnings });
done(err, allWarnings);
});
});
};
/**
* Export the currently displayed BPMN 2.0 diagram as
* a BPMN 2.0 XML document.
*
* @param {Object} [options] export options
* @param {Boolean} [options.format=false] output formated XML
* @param {Boolean} [options.preamble=true] output preamble
*
* @param {Function} done invoked with (err, xml)
*/
Viewer.prototype.saveXML = function(options, done) {
if (!done) {
done = options;
options = {};
}
var definitions = this._definitions;
if (!definitions) {
return done(new Error('no definitions loaded'));
}
this._moddle.toXML(definitions, options, done);
};
/**
* Export the currently displayed BPMN 2.0 diagram as
* an SVG image.
*
* @param {Object} [options]
* @param {Function} done invoked with (err, svgStr)
*/
Viewer.prototype.saveSVG = function(options, done) {
if (!done) {
done = options;
options = {};
}
var canvas = this.get('canvas');
var contentNode = canvas.getDefaultLayer(),
defsNode = domQuery('defs', canvas._svg);
var contents = innerSVG(contentNode),
defs = defsNode ? '' + innerSVG(defsNode) + ' ' : '';
var bbox = contentNode.getBBox();
var svg =
'\n' +
'\n' +
'\n' +
'' +
defs + contents +
' ';
done(null, svg);
};
/**
* Get a named diagram service.
*
* @example
*
* var elementRegistry = viewer.get('elementRegistry');
* var startEventShape = elementRegistry.get('StartEvent_1');
*
* @param {String} name
*
* @return {Object} diagram service instance
*
* @method Viewer#get
*/
/**
* Invoke a function in the context of this viewer.
*
* @example
*
* viewer.invoke(function(elementRegistry) {
* var startEventShape = elementRegistry.get('StartEvent_1');
* });
*
* @param {Function} fn to be invoked
*
* @return {Object} the functions return value
*
* @method Viewer#invoke
*/
/**
* Remove all drawn elements from the viewer.
*
* After calling this method the viewer can still
* be reused for opening another diagram.
*
* @method Viewer#clear
*/
Viewer.prototype.importDefinitions = function(definitions, done) {
// use try/catch to not swallow synchronous exceptions
// that may be raised during model parsing
try {
if (this._definitions) {
// clear existing rendered diagram
this.clear();
}
// update definitions
this._definitions = definitions;
// perform graphical import
Importer.importBpmnDiagram(this, definitions, done);
} catch (e) {
// handle synchronous errors
done(e);
}
};
Viewer.prototype.getModules = function() {
return this._modules;
};
/**
* Destroy the viewer instance and remove all its
* remainders from the document tree.
*/
Viewer.prototype.destroy = function() {
// diagram destroy
Diagram.prototype.destroy.call(this);
// dom detach
domRemove(this._container);
};
/**
* Register an event listener
*
* Remove a previously added listener via {@link #off(event, callback)}.
*
* @param {String} event
* @param {Number} [priority]
* @param {Function} callback
* @param {Object} [that]
*/
Viewer.prototype.on = function(event, priority, callback, target) {
return this.get('eventBus').on(event, priority, callback, target);
};
/**
* De-register an event listener
*
* @param {String} event
* @param {Function} callback
*/
Viewer.prototype.off = function(event, callback) {
this.get('eventBus').off(event, callback);
};
Viewer.prototype.attachTo = function(parentNode) {
if (!parentNode) {
throw new Error('parentNode required');
}
// ensure we detach from the
// previous, old parent
this.detach();
// unwrap jQuery if provided
if (parentNode.get && parentNode.constructor.prototype.jquery) {
parentNode = parentNode.get(0);
}
if (typeof parentNode === 'string') {
parentNode = domQuery(parentNode);
}
parentNode.appendChild(this._container);
this._emit('attach', {});
};
Viewer.prototype.detach = function() {
var container = this._container,
parentNode = container.parentNode;
if (!parentNode) {
return;
}
this._emit('detach', {});
parentNode.removeChild(container);
};
Viewer.prototype._init = function(container, moddle, options) {
var baseModules = options.modules || this.getModules(),
additionalModules = options.additionalModules || [],
staticModules = [
{
bpmnjs: [ 'value', this ],
moddle: [ 'value', moddle ]
}
];
var diagramModules = [].concat(staticModules, baseModules, additionalModules);
var diagramOptions = assign(omit(options, 'additionalModules'), {
canvas: assign({}, options.canvas, { container: container }),
modules: diagramModules
});
// invoke diagram constructor
Diagram.call(this, diagramOptions);
if (options && options.container) {
this.attachTo(options.container);
}
};
/**
* Emit an event on the underlying {@link EventBus}
*
* @param {String} type
* @param {Object} event
*
* @return {Object} event processing result (if any)
*/
Viewer.prototype._emit = function(type, event) {
return this.get('eventBus').fire(type, event);
};
Viewer.prototype._createContainer = function(options) {
var container = domify('
');
assign(container.style, {
width: ensureUnit(options.width),
height: ensureUnit(options.height),
position: options.position
});
return container;
};
Viewer.prototype._createModdle = function(options) {
var moddleOptions = assign({}, this._moddleExtensions, options.moddleExtensions);
return new BpmnModdle(moddleOptions);
};
// modules the viewer is composed of
Viewer.prototype._modules = [
_dereq_(4),
_dereq_(248),
_dereq_(233),
_dereq_(211)
];
// default moddle extensions the viewer is composed of
Viewer.prototype._moddleExtensions = {};
/* */
var PoweredBy = _dereq_(96),
domEvent = _dereq_(450);
/**
* Adds the project logo to the diagram container as
* required by the bpmn.io license.
*
* @see http://bpmn.io/license
*
* @param {Element} container
*/
function addProjectLogo(container) {
var logoData = PoweredBy.BPMNIO_LOGO;
var linkMarkup =
'' +
' ' +
' ';
var linkElement = domify(linkMarkup);
container.appendChild(linkElement);
domEvent.bind(linkElement, 'click', function(event) {
PoweredBy.open();
event.preventDefault();
});
}
/* */
},{"117":117,"211":211,"233":233,"248":248,"289":289,"4":4,"425":425,"431":431,"436":436,"449":449,"450":450,"452":452,"453":453,"480":480,"90":90,"96":96,"99":99}],4:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(7),
_dereq_(92)
]
};
},{"7":7,"92":92}],5:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289),
isObject = _dereq_(426),
assign = _dereq_(431),
forEach = _dereq_(303),
every = _dereq_(300),
some = _dereq_(309);
var BaseRenderer = _dereq_(128),
TextUtil = _dereq_(280),
DiUtil = _dereq_(93);
var getBusinessObject = _dereq_(95).getBusinessObject,
is = _dereq_(95).is;
var RenderUtil = _dereq_(278);
var componentsToPath = RenderUtil.componentsToPath,
createLine = RenderUtil.createLine;
var domQuery = _dereq_(452);
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478),
svgClasses = _dereq_(475);
var rotate = _dereq_(279).rotate,
transform = _dereq_(279).transform,
translate = _dereq_(279).translate;
var Ids = _dereq_(288),
RENDERER_IDS = new Ids();
var TASK_BORDER_RADIUS = 10;
var INNER_OUTER_DIST = 3;
var LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: 12
};
function BpmnRenderer(eventBus, styles, pathMap, canvas, priority) {
BaseRenderer.call(this, eventBus, priority);
var rendererId = RENDERER_IDS.next();
var textUtil = new TextUtil({
style: LABEL_STYLE,
size: { width: 100 }
});
var markers = {};
var computeStyle = styles.computeStyle;
function addMarker(id, options) {
var attrs = assign({
fill: 'black',
strokeWidth: 1,
strokeLinecap: 'round',
strokeDasharray: 'none'
}, options.attrs);
var ref = options.ref || { x: 0, y: 0 };
var scale = options.scale || 1;
// fix for safari / chrome / firefox bug not correctly
// resetting stroke dash array
if (attrs.strokeDasharray === 'none') {
attrs.strokeDasharray = [10000, 1];
}
var marker = svgCreate('marker');
svgAttr(options.element, attrs);
svgAppend(marker, options.element);
svgAttr(marker, {
id: id,
viewBox: '0 0 20 20',
refX: ref.x,
refY: ref.y,
markerWidth: 20 * scale,
markerHeight: 20 * scale,
orient: 'auto'
});
var defs = domQuery('defs', canvas._svg);
if (!defs) {
defs = svgCreate('defs');
svgAppend(canvas._svg, defs);
}
svgAppend(defs, marker);
markers[id] = marker;
}
function marker(type, fill, stroke) {
var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
if (!markers[id]) {
createMarker(type, fill, stroke);
}
return 'url(#' + id + ')';
}
function createMarker(type, fill, stroke) {
var id = type + '-' + fill + '-' + stroke + '-' + rendererId;
if (type === 'sequenceflow-end') {
var sequenceflowEnd = svgCreate('path');
svgAttr(sequenceflowEnd, { d: 'M 1 5 L 11 10 L 1 15 Z' });
addMarker(id, {
element: sequenceflowEnd,
ref: { x: 11, y: 10 },
scale: 0.5,
attrs: {
fill: stroke,
stroke: stroke
}
});
}
if (type === 'messageflow-start') {
var messageflowStart = svgCreate('circle');
svgAttr(messageflowStart, { cx: 6, cy: 6, r: 3.5 });
addMarker(id, {
element: messageflowStart,
attrs: {
fill: fill,
stroke: stroke
},
ref: { x: 6, y: 6 }
});
}
if (type === 'messageflow-end') {
var messageflowEnd = svgCreate('path');
svgAttr(messageflowEnd, { d: 'm 1 5 l 0 -3 l 7 3 l -7 3 z' });
addMarker(id, {
element: messageflowEnd,
attrs: {
fill: fill,
stroke: stroke,
strokeLinecap: 'butt'
},
ref: { x: 8.5, y: 5 }
});
}
if (type === 'association-start') {
var associationStart = svgCreate('path');
svgAttr(associationStart, { d: 'M 11 5 L 1 10 L 11 15' });
addMarker(id, {
element: associationStart,
attrs: {
fill: 'none',
stroke: stroke,
strokeWidth: 1.5
},
ref: { x: 1, y: 10 },
scale: 0.5
});
}
if (type === 'association-end') {
var associationEnd = svgCreate('path');
svgAttr(associationEnd, { d: 'M 1 5 L 11 10 L 1 15' });
addMarker(id, {
element: associationEnd,
attrs: {
fill: 'none',
stroke: stroke,
strokeWidth: 1.5
},
ref: { x: 12, y: 10 },
scale: 0.5
});
}
if (type === 'conditional-flow-marker') {
var conditionalflowMarker = svgCreate('path');
svgAttr(conditionalflowMarker, { d: 'M 0 10 L 8 6 L 16 10 L 8 14 Z' });
addMarker(id, {
element: conditionalflowMarker,
attrs: {
fill: fill,
stroke: stroke
},
ref: { x: -1, y: 10 },
scale: 0.5
});
}
if (type === 'conditional-default-flow-marker') {
var conditionaldefaultflowMarker = svgCreate('path');
svgAttr(conditionaldefaultflowMarker, { d: 'M 6 4 L 10 16' });
addMarker(id, {
element: conditionaldefaultflowMarker,
attrs: {
stroke: stroke
},
ref: { x: 0, y: 10 },
scale: 0.5
});
}
}
function drawCircle(parentGfx, width, height, offset, attrs) {
if (isObject(offset)) {
attrs = offset;
offset = 0;
}
offset = offset || 0;
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var cx = width / 2,
cy = height / 2;
var circle = svgCreate('circle');
svgAttr(circle, {
cx: cx,
cy: cy,
r: Math.round((width + height) / 4 - offset)
});
svgAttr(circle, attrs);
svgAppend(parentGfx, circle);
return circle;
}
function drawRect(parentGfx, width, height, r, offset, attrs) {
if (isObject(offset)) {
attrs = offset;
offset = 0;
}
offset = offset || 0;
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var rect = svgCreate('rect');
svgAttr(rect, {
x: offset,
y: offset,
width: width - offset * 2,
height: height - offset * 2,
rx: r,
ry: r
});
svgAttr(rect, attrs);
svgAppend(parentGfx, rect);
return rect;
}
function drawDiamond(parentGfx, width, height, attrs) {
var x_2 = width / 2;
var y_2 = height / 2;
var points = [{ x: x_2, y: 0 }, { x: width, y: y_2 }, { x: x_2, y: height }, { x: 0, y: y_2 }];
var pointsString = points.map(function(point) {
return point.x + ',' + point.y;
}).join(' ');
attrs = computeStyle(attrs, {
stroke: 'black',
strokeWidth: 2,
fill: 'white'
});
var polygon = svgCreate('polygon');
svgAttr(polygon, {
points: pointsString
});
svgAttr(polygon, attrs);
svgAppend(parentGfx, polygon);
return polygon;
}
function drawLine(parentGfx, waypoints, attrs) {
attrs = computeStyle(attrs, [ 'no-fill' ], {
stroke: 'black',
strokeWidth: 2,
fill: 'none'
});
var line = createLine(waypoints, attrs);
svgAppend(parentGfx, line);
return line;
}
function drawPath(parentGfx, d, attrs) {
attrs = computeStyle(attrs, [ 'no-fill' ], {
strokeWidth: 2,
stroke: 'black'
});
var path = svgCreate('path');
svgAttr(path, { d: d });
svgAttr(path, attrs);
svgAppend(parentGfx, path);
return path;
}
function drawMarker(type, parentGfx, path, attrs) {
return drawPath(parentGfx, path, assign({ 'data-marker': type }, attrs));
}
function as(type) {
return function(parentGfx, element) {
return handlers[type](parentGfx, element);
};
}
function renderer(type) {
return handlers[type];
}
function renderEventContent(element, parentGfx) {
var event = getSemantic(element);
var isThrowing = isThrowEvent(event);
if (isTypedEvent(event, 'bpmn:MessageEventDefinition')) {
return renderer('bpmn:MessageEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:TimerEventDefinition')) {
return renderer('bpmn:TimerEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:ConditionalEventDefinition')) {
return renderer('bpmn:ConditionalEventDefinition')(parentGfx, element);
}
if (isTypedEvent(event, 'bpmn:SignalEventDefinition')) {
return renderer('bpmn:SignalEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: false })) {
return renderer('bpmn:MultipleEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition') &&
isTypedEvent(event, 'bpmn:TerminateEventDefinition', { parallelMultiple: true })) {
return renderer('bpmn:ParallelMultipleEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:EscalationEventDefinition')) {
return renderer('bpmn:EscalationEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:LinkEventDefinition')) {
return renderer('bpmn:LinkEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:ErrorEventDefinition')) {
return renderer('bpmn:ErrorEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CancelEventDefinition')) {
return renderer('bpmn:CancelEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:CompensateEventDefinition')) {
return renderer('bpmn:CompensateEventDefinition')(parentGfx, element, isThrowing);
}
if (isTypedEvent(event, 'bpmn:TerminateEventDefinition')) {
return renderer('bpmn:TerminateEventDefinition')(parentGfx, element, isThrowing);
}
return null;
}
function renderLabel(parentGfx, label, options) {
var text = textUtil.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(parentGfx, text);
return text;
}
function renderEmbeddedLabel(parentGfx, element, align) {
var semantic = getSemantic(element);
return renderLabel(parentGfx, semantic.name, {
box: element,
align: align,
padding: 5,
style: {
fill: getStrokeColor(element)
}
});
}
function renderExternalLabel(parentGfx, element) {
var semantic = getSemantic(element);
var box = {
width: 90,
height: 30,
x: element.width / 2 + element.x,
y: element.height / 2 + element.y
};
return renderLabel(parentGfx, semantic.name, {
box: box,
fitBox: true,
style: { fontSize: '11px' }
});
}
function renderLaneLabel(parentGfx, text, element) {
var textBox = renderLabel(parentGfx, text, {
box: { height: 30, width: element.height },
align: 'center-middle',
style: {
fill: getStrokeColor(element)
}
});
var top = -1 * element.height;
transform(textBox, 0, -top, 270);
}
function createPathFromConnection(connection) {
var waypoints = connection.waypoints;
var pathData = 'm ' + waypoints[0].x + ',' + waypoints[0].y;
for (var i = 1; i < waypoints.length; i++) {
pathData += 'L' + waypoints[i].x + ',' + waypoints[i].y + ' ';
}
return pathData;
}
var handlers = this.handlers = {
'bpmn:Event': function(parentGfx, element, attrs) {
return drawCircle(parentGfx, element.width, element.height, attrs);
},
'bpmn:StartEvent': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var semantic = getSemantic(element);
if (!semantic.isInterrupting) {
attrs = {
strokeDasharray: '6',
strokeLinecap: 'round'
};
}
var circle = renderer('bpmn:Event')(parentGfx, element, attrs);
renderEventContent(element, parentGfx);
return circle;
},
'bpmn:MessageEventDefinition': function(parentGfx, element, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_MESSAGE', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.235,
my: 0.315
}
});
var fill = isThrowing ? getStrokeColor(element) : getFillColor(element);
var stroke = isThrowing ? getFillColor(element) : getStrokeColor(element);
var messagePath = drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: stroke
});
return messagePath;
},
'bpmn:TimerEventDefinition': function(parentGfx, element) {
var circle = drawCircle(parentGfx, element.width, element.height, 0.2 * element.height, {
strokeWidth: 2,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData = pathMap.getScaledPath('EVENT_TIMER_WH', {
xScaleFactor: 0.75,
yScaleFactor: 0.75,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.5,
my: 0.5
}
});
drawPath(parentGfx, pathData, {
strokeWidth: 2,
strokeLinecap: 'square',
stroke: getStrokeColor(element)
});
for (var i = 0;i < 12;i++) {
var linePathData = pathMap.getScaledPath('EVENT_TIMER_LINE', {
xScaleFactor: 0.75,
yScaleFactor: 0.75,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.5,
my: 0.5
}
});
var width = element.width / 2;
var height = element.height / 2;
drawPath(parentGfx, linePathData, {
strokeWidth: 1,
strokeLinecap: 'square',
transform: 'rotate(' + (i * 30) + ',' + height + ',' + width + ')',
stroke: getStrokeColor(element)
});
}
return circle;
},
'bpmn:EscalationEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_ESCALATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.2
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:ConditionalEventDefinition': function(parentGfx, event) {
var pathData = pathMap.getScaledPath('EVENT_CONDITIONAL', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.222
}
});
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
stroke: getStrokeColor(event)
});
},
'bpmn:LinkEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_LINK', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.57,
my: 0.263
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:ErrorEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_ERROR', {
xScaleFactor: 1.1,
yScaleFactor: 1.1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.2,
my: 0.722
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:CancelEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_CANCEL_45', {
xScaleFactor: 1.0,
yScaleFactor: 1.0,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.638,
my: -0.055
}
});
var fill = isThrowing ? 'black' : 'none';
var path = drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill
});
rotate(path, 45);
return path;
},
'bpmn:CompensateEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_COMPENSATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.22,
my: 0.5
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:SignalEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_SIGNAL', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.5,
my: 0.2
}
});
var fill = isThrowing ? getStrokeColor(event) : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill,
stroke: getStrokeColor(event)
});
},
'bpmn:MultipleEventDefinition': function(parentGfx, event, isThrowing) {
var pathData = pathMap.getScaledPath('EVENT_MULTIPLE', {
xScaleFactor: 1.1,
yScaleFactor: 1.1,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.222,
my: 0.36
}
});
var fill = isThrowing ? 'black' : 'none';
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: fill
});
},
'bpmn:ParallelMultipleEventDefinition': function(parentGfx, event) {
var pathData = pathMap.getScaledPath('EVENT_PARALLEL_MULTIPLE', {
xScaleFactor: 1.2,
yScaleFactor: 1.2,
containerWidth: event.width,
containerHeight: event.height,
position: {
mx: 0.458,
my: 0.194
}
});
return drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(event),
stroke: getStrokeColor(event)
});
},
'bpmn:EndEvent': function(parentGfx, element) {
var circle = renderer('bpmn:Event')(parentGfx, element, {
strokeWidth: 4,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
renderEventContent(element, parentGfx, true);
return circle;
},
'bpmn:TerminateEventDefinition': function(parentGfx, element) {
var circle = drawCircle(parentGfx, element.width, element.height, 8, {
strokeWidth: 4,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return circle;
},
'bpmn:IntermediateEvent': function(parentGfx, element) {
var outer = renderer('bpmn:Event')(parentGfx, element, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
/* inner */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, {
strokeWidth: 1,
fill: getFillColor(element, 'none'),
stroke: getStrokeColor(element)
});
renderEventContent(element, parentGfx);
return outer;
},
'bpmn:IntermediateCatchEvent': as('bpmn:IntermediateEvent'),
'bpmn:IntermediateThrowEvent': as('bpmn:IntermediateEvent'),
'bpmn:Activity': function(parentGfx, element, attrs) {
return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, attrs);
},
'bpmn:Task': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
renderEmbeddedLabel(parentGfx, element, 'center-middle');
attachTaskMarkers(parentGfx, element);
return rect;
},
'bpmn:ServiceTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathDataBG = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
abspos: {
x: 12,
y: 18
}
});
/* service bg */ drawPath(parentGfx, pathDataBG, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var fillPathData = pathMap.getScaledPath('TASK_TYPE_SERVICE_FILL', {
abspos: {
x: 17.2,
y: 18
}
});
/* service fill */ drawPath(parentGfx, fillPathData, {
strokeWidth: 0,
fill: getFillColor(element)
});
var pathData = pathMap.getScaledPath('TASK_TYPE_SERVICE', {
abspos: {
x: 17,
y: 22
}
});
/* service */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:UserTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var x = 15;
var y = 12;
var pathData = pathMap.getScaledPath('TASK_TYPE_USER_1', {
abspos: {
x: x,
y: y
}
});
/* user path */ drawPath(parentGfx, pathData, {
strokeWidth: 0.5,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData2 = pathMap.getScaledPath('TASK_TYPE_USER_2', {
abspos: {
x: x,
y: y
}
});
/* user2 path */ drawPath(parentGfx, pathData2, {
strokeWidth: 0.5,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var pathData3 = pathMap.getScaledPath('TASK_TYPE_USER_3', {
abspos: {
x: x,
y: y
}
});
/* user3 path */ drawPath(parentGfx, pathData3, {
strokeWidth: 0.5,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:ManualTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_MANUAL', {
abspos: {
x: 17,
y: 15
}
});
/* manual path */ drawPath(parentGfx, pathData, {
strokeWidth: 0.5, // 0.25,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:SendTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: 21,
containerHeight: 14,
position: {
mx: 0.285,
my: 0.357
}
});
/* send path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(element),
stroke: getFillColor(element)
});
return task;
},
'bpmn:ReceiveTask' : function(parentGfx, element) {
var semantic = getSemantic(element);
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData;
if (semantic.instantiate) {
drawCircle(parentGfx, 28, 28, 20 * 0.22, { strokeWidth: 1 });
pathData = pathMap.getScaledPath('TASK_TYPE_INSTANTIATING_SEND', {
abspos: {
x: 7.77,
y: 9.52
}
});
} else {
pathData = pathMap.getScaledPath('TASK_TYPE_SEND', {
xScaleFactor: 0.9,
yScaleFactor: 0.9,
containerWidth: 21,
containerHeight: 14,
position: {
mx: 0.3,
my: 0.4
}
});
}
/* receive path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:ScriptTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var pathData = pathMap.getScaledPath('TASK_TYPE_SCRIPT', {
abspos: {
x: 15,
y: 20
}
});
/* script path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:BusinessRuleTask': function(parentGfx, element) {
var task = renderer('bpmn:Task')(parentGfx, element);
var headerPathData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_HEADER', {
abspos: {
x: 8,
y: 8
}
});
var businessHeaderPath = drawPath(parentGfx, headerPathData);
svgAttr(businessHeaderPath, {
strokeWidth: 1,
fill: getFillColor(element, '#aaaaaa'),
stroke: getStrokeColor(element)
});
var headerData = pathMap.getScaledPath('TASK_TYPE_BUSINESS_RULE_MAIN', {
abspos: {
x: 8,
y: 8
}
});
var businessPath = drawPath(parentGfx, headerData);
svgAttr(businessPath, {
strokeWidth: 1,
stroke: getStrokeColor(element)
});
return task;
},
'bpmn:SubProcess': function(parentGfx, element, attrs) {
attrs = assign({
fillOpacity: 0.95,
fill: getFillColor(element),
stroke: getStrokeColor(element)
}, attrs);
var rect = renderer('bpmn:Activity')(parentGfx, element, attrs);
var expanded = DiUtil.isExpanded(element);
var isEventSubProcess = DiUtil.isEventSubProcess(element);
if (isEventSubProcess) {
svgAttr(rect, {
strokeDasharray: '1,2'
});
}
renderEmbeddedLabel(parentGfx, element, expanded ? 'center-top' : 'center-middle');
if (expanded) {
attachTaskMarkers(parentGfx, element);
} else {
attachTaskMarkers(parentGfx, element, ['SubProcessMarker']);
}
return rect;
},
'bpmn:AdHocSubProcess': function(parentGfx, element) {
return renderer('bpmn:SubProcess')(parentGfx, element);
},
'bpmn:Transaction': function(parentGfx, element) {
var outer = renderer('bpmn:SubProcess')(parentGfx, element);
var innerAttrs = styles.style([ 'no-fill', 'no-events' ], {
stroke: getStrokeColor(element)
});
/* inner path */ drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS - 2, INNER_OUTER_DIST, innerAttrs);
return outer;
},
'bpmn:CallActivity': function(parentGfx, element) {
return renderer('bpmn:SubProcess')(parentGfx, element, {
strokeWidth: 5
});
},
'bpmn:Participant': function(parentGfx, element) {
var attrs = {
fillOpacity: 0.95,
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var lane = renderer('bpmn:Lane')(parentGfx, element, attrs);
var expandedPool = DiUtil.isExpanded(element);
if (expandedPool) {
drawLine(parentGfx, [
{ x: 30, y: 0 },
{ x: 30, y: element.height }
], {
stroke: getStrokeColor(element)
});
var text = getSemantic(element).name;
renderLaneLabel(parentGfx, text, element);
} else {
// Collapsed pool draw text inline
var text2 = getSemantic(element).name;
renderLabel(parentGfx, text2, {
box: element, align: 'center-middle',
style: {
fill: getStrokeColor(element)
}
});
}
var participantMultiplicity = !!(getSemantic(element).participantMultiplicity);
if (participantMultiplicity) {
renderer('ParticipantMultiplicityMarker')(parentGfx, element);
}
return lane;
},
'bpmn:Lane': function(parentGfx, element, attrs) {
var rect = drawRect(parentGfx, element.width, element.height, 0, assign({
fill: getFillColor(element),
stroke: getStrokeColor(element)
}, attrs));
var semantic = getSemantic(element);
if (semantic.$type === 'bpmn:Lane') {
var text = semantic.name;
renderLaneLabel(parentGfx, text, element);
}
return rect;
},
'bpmn:InclusiveGateway': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var diamond = drawDiamond(parentGfx, element.width, element.height, attrs);
/* circle path */
drawCircle(parentGfx, element.width, element.height, element.height * 0.24, {
strokeWidth: 2.5,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return diamond;
},
'bpmn:ExclusiveGateway': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var diamond = drawDiamond(parentGfx, element.width, element.height, attrs);
var pathData = pathMap.getScaledPath('GATEWAY_EXCLUSIVE', {
xScaleFactor: 0.4,
yScaleFactor: 0.4,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.32,
my: 0.3
}
});
if ((getDi(element).isMarkerVisible)) {
drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
}
return diamond;
},
'bpmn:ComplexGateway': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var diamond = drawDiamond(parentGfx, element.width, element.height, attrs);
var pathData = pathMap.getScaledPath('GATEWAY_COMPLEX', {
xScaleFactor: 0.5,
yScaleFactor:0.5,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.46,
my: 0.26
}
});
/* complex path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return diamond;
},
'bpmn:ParallelGateway': function(parentGfx, element) {
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var diamond = drawDiamond(parentGfx, element.width, element.height, attrs);
var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
xScaleFactor: 0.6,
yScaleFactor:0.6,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.46,
my: 0.2
}
});
/* parallel path */ drawPath(parentGfx, pathData, {
strokeWidth: 1,
fill: getStrokeColor(element),
stroke: getStrokeColor(element)
});
return diamond;
},
'bpmn:EventBasedGateway': function(parentGfx, element) {
var semantic = getSemantic(element);
var attrs = {
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
var diamond = drawDiamond(parentGfx, element.width, element.height, attrs);
/* outer circle path */ drawCircle(parentGfx, element.width, element.height, element.height * 0.20, {
strokeWidth: 1,
fill: 'none',
stroke: getStrokeColor(element)
});
var type = semantic.eventGatewayType;
var instantiate = !!semantic.instantiate;
function drawEvent() {
var pathData = pathMap.getScaledPath('GATEWAY_EVENT_BASED', {
xScaleFactor: 0.18,
yScaleFactor: 0.18,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.36,
my: 0.44
}
});
var attrs = {
strokeWidth: 2,
fill: getFillColor(element, 'none'),
stroke: getStrokeColor(element)
};
/* event path */ drawPath(parentGfx, pathData, attrs);
}
if (type === 'Parallel') {
var pathData = pathMap.getScaledPath('GATEWAY_PARALLEL', {
xScaleFactor: 0.4,
yScaleFactor:0.4,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.474,
my: 0.296
}
});
var parallelPath = drawPath(parentGfx, pathData);
svgAttr(parallelPath, {
strokeWidth: 1,
fill: 'none'
});
} else if (type === 'Exclusive') {
if (!instantiate) {
var innerCircle = drawCircle(parentGfx, element.width, element.height, element.height * 0.26);
svgAttr(innerCircle, {
strokeWidth: 1,
fill: 'none',
stroke: getStrokeColor(element)
});
}
drawEvent();
}
return diamond;
},
'bpmn:Gateway': function(parentGfx, element) {
return drawDiamond(parentGfx, element.width, element.height);
},
'bpmn:SequenceFlow': function(parentGfx, element) {
var pathData = createPathFromConnection(element);
var fill = getFillColor(element),
stroke = getStrokeColor(element);
var attrs = {
strokeLinejoin: 'round',
markerEnd: marker('sequenceflow-end', fill, stroke),
stroke: getStrokeColor(element)
};
var path = drawPath(parentGfx, pathData, attrs);
var sequenceFlow = getSemantic(element);
var source = element.source.businessObject;
// conditional flow marker
if (sequenceFlow.conditionExpression && source.$instanceOf('bpmn:Activity')) {
svgAttr(path, {
markerStart: marker('conditional-flow-marker', fill, stroke)
});
}
// default marker
if (source.default && (source.$instanceOf('bpmn:Gateway') || source.$instanceOf('bpmn:Activity')) &&
source.default === sequenceFlow) {
svgAttr(path, {
markerStart: marker('conditional-default-flow-marker', fill, stroke)
});
}
return path;
},
'bpmn:Association': function(parentGfx, element, attrs) {
var semantic = getSemantic(element);
var fill = getFillColor(element),
stroke = getStrokeColor(element);
attrs = assign({
strokeDasharray: '0.5, 5',
strokeLinecap: 'round',
strokeLinejoin: 'round',
stroke: getStrokeColor(element)
}, attrs || {});
if (semantic.associationDirection === 'One' ||
semantic.associationDirection === 'Both') {
attrs.markerEnd = marker('association-end', fill, stroke);
}
if (semantic.associationDirection === 'Both') {
attrs.markerStart = marker('association-start', fill, stroke);
}
return drawLine(parentGfx, element.waypoints, attrs);
},
'bpmn:DataInputAssociation': function(parentGfx, element) {
var fill = getFillColor(element),
stroke = getStrokeColor(element);
return renderer('bpmn:Association')(parentGfx, element, {
markerEnd: marker('association-end', fill, stroke)
});
},
'bpmn:DataOutputAssociation': function(parentGfx, element) {
var fill = getFillColor(element),
stroke = getStrokeColor(element);
return renderer('bpmn:Association')(parentGfx, element, {
markerEnd: marker('association-end', fill, stroke)
});
},
'bpmn:MessageFlow': function(parentGfx, element) {
var semantic = getSemantic(element),
di = getDi(element);
var fill = getFillColor(element),
stroke = getStrokeColor(element);
var pathData = createPathFromConnection(element);
var attrs = {
markerEnd: marker('messageflow-end', fill, stroke),
markerStart: marker('messageflow-start', fill, stroke),
strokeDasharray: '10, 12',
strokeLinecap: 'round',
strokeLinejoin: 'round',
strokeWidth: '1.5px',
stroke: getStrokeColor(element)
};
var path = drawPath(parentGfx, pathData, attrs);
if (semantic.messageRef) {
var midPoint = path.getPointAtLength(path.getTotalLength() / 2);
var markerPathData = pathMap.getScaledPath('MESSAGE_FLOW_MARKER', {
abspos: {
x: midPoint.x,
y: midPoint.y
}
});
var messageAttrs = { strokeWidth: 1 };
if (di.messageVisibleKind === 'initiating') {
messageAttrs.fill = 'white';
messageAttrs.stroke = 'black';
} else {
messageAttrs.fill = '#888';
messageAttrs.stroke = 'white';
}
drawPath(parentGfx, markerPathData, messageAttrs);
}
return path;
},
'bpmn:DataObject': function(parentGfx, element) {
var pathData = pathMap.getScaledPath('DATA_OBJECT_PATH', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.474,
my: 0.296
}
});
var elementObject = drawPath(parentGfx, pathData, {
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
var semantic = getSemantic(element);
if (isCollection(semantic)) {
renderDataItemCollection(parentGfx, element);
}
return elementObject;
},
'bpmn:DataObjectReference': as('bpmn:DataObject'),
'bpmn:DataInput': function(parentGfx, element) {
var arrowPathData = pathMap.getRawPath('DATA_ARROW');
// page
var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
/* input arrow path */ drawPath(parentGfx, arrowPathData, { strokeWidth: 1 });
return elementObject;
},
'bpmn:DataOutput': function(parentGfx, element) {
var arrowPathData = pathMap.getRawPath('DATA_ARROW');
// page
var elementObject = renderer('bpmn:DataObject')(parentGfx, element);
/* output arrow path */ drawPath(parentGfx, arrowPathData, {
strokeWidth: 1,
fill: 'black'
});
return elementObject;
},
'bpmn:DataStoreReference': function(parentGfx, element) {
var DATA_STORE_PATH = pathMap.getScaledPath('DATA_STORE', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0,
my: 0.133
}
});
var elementStore = drawPath(parentGfx, DATA_STORE_PATH, {
strokeWidth: 2,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
return elementStore;
},
'bpmn:BoundaryEvent': function(parentGfx, element) {
var semantic = getSemantic(element),
cancel = semantic.cancelActivity;
var attrs = {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
};
if (!cancel) {
attrs.strokeDasharray = '6';
attrs.strokeLinecap = 'round';
}
var outer = renderer('bpmn:Event')(parentGfx, element, attrs);
/* inner path */ drawCircle(parentGfx, element.width, element.height, INNER_OUTER_DIST, assign(attrs, { fill: 'none' }));
renderEventContent(element, parentGfx);
return outer;
},
'bpmn:Group': function(parentGfx, element) {
return drawRect(parentGfx, element.width, element.height, TASK_BORDER_RADIUS, {
strokeWidth: 1,
strokeDasharray: '8,3,1,3',
fill: 'none',
pointerEvents: 'none'
});
},
'label': function(parentGfx, element) {
return renderExternalLabel(parentGfx, element);
},
'bpmn:TextAnnotation': function(parentGfx, element) {
var style = {
'fill': 'none',
'stroke': 'none'
};
var textElement = drawRect(parentGfx, element.width, element.height, 0, 0, style);
var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.0,
my: 0.0
}
});
drawPath(parentGfx, textPathData, {
stroke: getStrokeColor(element)
});
var text = getSemantic(element).text || '';
renderLabel(parentGfx, text, { box: element, align: 'left-top', padding: 5 });
return textElement;
},
'ParticipantMultiplicityMarker': function(parentGfx, element) {
var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2) / element.width),
my: (element.height - 15) / element.height
}
});
drawMarker('participant-multiplicity', parentGfx, markerPath);
},
'SubProcessMarker': function(parentGfx, element) {
var markerRect = drawRect(parentGfx, 14, 14, 0, {
strokeWidth: 1,
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
// Process marker is placed in the middle of the box
// therefore fixed values can be used here
translate(markerRect, element.width / 2 - 7.5, element.height - 20);
var markerPath = pathMap.getScaledPath('MARKER_SUB_PROCESS', {
xScaleFactor: 1.5,
yScaleFactor: 1.5,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: (element.width / 2 - 7.5) / element.width,
my: (element.height - 20) / element.height
}
});
drawMarker('sub-process', parentGfx, markerPath, {
fill: getFillColor(element),
stroke: getStrokeColor(element)
});
},
'ParallelMarker': function(parentGfx, element, position) {
var markerPath = pathMap.getScaledPath('MARKER_PARALLEL', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2 + position.parallel) / element.width),
my: (element.height - 20) / element.height
}
});
drawMarker('parallel', parentGfx, markerPath);
},
'SequentialMarker': function(parentGfx, element, position) {
var markerPath = pathMap.getScaledPath('MARKER_SEQUENTIAL', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2 + position.seq) / element.width),
my: (element.height - 19) / element.height
}
});
drawMarker('sequential', parentGfx, markerPath);
},
'CompensationMarker': function(parentGfx, element, position) {
var markerMath = pathMap.getScaledPath('MARKER_COMPENSATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2 + position.compensation) / element.width),
my: (element.height - 13) / element.height
}
});
drawMarker('compensation', parentGfx, markerMath, { strokeWidth: 1 });
},
'LoopMarker': function(parentGfx, element, position) {
var markerPath = pathMap.getScaledPath('MARKER_LOOP', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2 + position.loop) / element.width),
my: (element.height - 7) / element.height
}
});
drawMarker('loop', parentGfx, markerPath, {
strokeWidth: 1,
fill: 'none',
strokeLinecap: 'round',
strokeMiterlimit: 0.5
});
},
'AdhocMarker': function(parentGfx, element, position) {
var markerPath = pathMap.getScaledPath('MARKER_ADHOC', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: ((element.width / 2 + position.adhoc) / element.width),
my: (element.height - 15) / element.height
}
});
drawMarker('adhoc', parentGfx, markerPath, {
strokeWidth: 1,
fill: 'black'
});
}
};
function attachTaskMarkers(parentGfx, element, taskMarkers) {
var obj = getSemantic(element);
var subprocess = taskMarkers && taskMarkers.indexOf('SubProcessMarker') !== -1;
var position;
if (subprocess) {
position = {
seq: -21,
parallel: -22,
compensation: -42,
loop: -18,
adhoc: 10
};
} else {
position = {
seq: -3,
parallel: -6,
compensation: -27,
loop: 0,
adhoc: 10
};
}
forEach(taskMarkers, function(marker) {
renderer(marker)(parentGfx, element, position);
});
if (obj.isForCompensation) {
renderer('CompensationMarker')(parentGfx, element, position);
}
if (obj.$type === 'bpmn:AdHocSubProcess') {
renderer('AdhocMarker')(parentGfx, element, position);
}
var loopCharacteristics = obj.loopCharacteristics,
isSequential = loopCharacteristics && loopCharacteristics.isSequential;
if (loopCharacteristics) {
if (isSequential === undefined) {
renderer('LoopMarker')(parentGfx, element, position);
}
if (isSequential === false) {
renderer('ParallelMarker')(parentGfx, element, position);
}
if (isSequential === true) {
renderer('SequentialMarker')(parentGfx, element, position);
}
}
}
function renderDataItemCollection(parentGfx, element) {
var yPosition = (element.height - 16) / element.height;
var pathData = pathMap.getScaledPath('DATA_OBJECT_COLLECTION_PATH', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.451,
my: yPosition
}
});
/* collection path */ drawPath(parentGfx, pathData, {
strokeWidth: 2
});
}
// extension API, use at your own risk
this._drawPath = drawPath;
}
inherits(BpmnRenderer, BaseRenderer);
BpmnRenderer.$inject = [ 'eventBus', 'styles', 'pathMap', 'canvas' ];
module.exports = BpmnRenderer;
BpmnRenderer.prototype.canRender = function(element) {
return is(element, 'bpmn:BaseElement');
};
BpmnRenderer.prototype.drawShape = function(parentGfx, element) {
var type = element.type;
var h = this.handlers[type];
/* jshint -W040 */
return h(parentGfx, element);
};
BpmnRenderer.prototype.drawConnection = function(parentGfx, element) {
var type = element.type;
var h = this.handlers[type];
/* jshint -W040 */
return h(parentGfx, element);
};
BpmnRenderer.prototype.getShapePath = function(element) {
if (is(element, 'bpmn:Event')) {
return getCirclePath(element);
}
if (is(element, 'bpmn:Activity')) {
return getRoundRectPath(element, TASK_BORDER_RADIUS);
}
if (is(element, 'bpmn:Gateway')) {
return getDiamondPath(element);
}
return getRectPath(element);
};
///////// helper functions /////////////////////////////
/**
* Checks if eventDefinition of the given element matches with semantic type.
*
* @return {boolean} true if element is of the given semantic type
*/
function isTypedEvent(event, eventDefinitionType, filter) {
function matches(definition, filter) {
return every(filter, function(val, key) {
// we want a == conversion here, to be able to catch
// undefined == false and friends
/* jshint -W116 */
return definition[key] == val;
});
}
return some(event.eventDefinitions, function(definition) {
return definition.$type === eventDefinitionType && matches(event, filter);
});
}
function isThrowEvent(event) {
return (event.$type === 'bpmn:IntermediateThrowEvent') || (event.$type === 'bpmn:EndEvent');
}
function isCollection(element) {
var dataObject = element.dataObjectRef;
return element.isCollection || (dataObject && dataObject.isCollection);
}
function getDi(element) {
return element.businessObject.di;
}
function getSemantic(element) {
return element.businessObject;
}
/////// cropping path customizations /////////////////////////
function getCirclePath(shape) {
var cx = shape.x + shape.width / 2,
cy = shape.y + shape.height / 2,
radius = shape.width / 2;
var circlePath = [
['M', cx, cy],
['m', 0, -radius],
['a', radius, radius, 0, 1, 1, 0, 2 * radius],
['a', radius, radius, 0, 1, 1, 0, -2 * radius],
['z']
];
return componentsToPath(circlePath);
}
function getRoundRectPath(shape, borderRadius) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var roundRectPath = [
['M', x + borderRadius, y],
['l', width - borderRadius * 2, 0],
['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, borderRadius],
['l', 0, height - borderRadius * 2],
['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, borderRadius],
['l', borderRadius * 2 - width, 0],
['a', borderRadius, borderRadius, 0, 0, 1, -borderRadius, -borderRadius],
['l', 0, borderRadius * 2 - height],
['a', borderRadius, borderRadius, 0, 0, 1, borderRadius, -borderRadius],
['z']
];
return componentsToPath(roundRectPath);
}
function getDiamondPath(shape) {
var width = shape.width,
height = shape.height,
x = shape.x,
y = shape.y,
halfWidth = width / 2,
halfHeight = height / 2;
var diamondPath = [
['M', x + halfWidth, y],
['l', halfWidth, halfHeight],
['l', -halfWidth, halfHeight],
['l', -halfWidth, -halfHeight],
['z']
];
return componentsToPath(diamondPath);
}
function getRectPath(shape) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var rectPath = [
['M', x, y],
['l', width, 0],
['l', 0, height],
['l', -width, 0],
['z']
];
return componentsToPath(rectPath);
}
function getFillColor(element, defaultColor) {
var bo = getBusinessObject(element);
return bo.di.get('fill') || defaultColor || 'white';
}
function getStrokeColor(element, defaultColor) {
var bo = getBusinessObject(element);
return bo.di.get('stroke') || defaultColor || 'black';
}
},{"128":128,"278":278,"279":279,"280":280,"288":288,"289":289,"300":300,"303":303,"309":309,"426":426,"431":431,"452":452,"472":472,"474":474,"475":475,"478":478,"93":93,"95":95}],6:[function(_dereq_,module,exports){
'use strict';
/**
* Map containing SVG paths needed by BpmnRenderer.
*/
function PathMap() {
/**
* Contains a map of path elements
*
* Path definition
* A parameterized path is defined like this:
*
* 'GATEWAY_PARALLEL': {
* d: 'm {mx},{my} {e.x0},0 0,{e.x1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
'-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
* height: 17.5,
* width: 17.5,
* heightElements: [2.5, 7.5],
* widthElements: [2.5, 7.5]
* }
*
* It's important to specify a correct height and width for the path as the scaling
* is based on the ratio between the specified height and width in this object and the
* height and width that is set as scale target (Note x,y coordinates will be scaled with
* individual ratios).
* The 'heightElements ' and 'widthElements ' array must contain the values that will be scaled.
* The scaling is based on the computed ratios.
* Coordinates on the y axis should be in the heightElement 's array, they will be scaled using
* the computed ratio coefficient.
* In the parameterized path the scaled values can be accessed through the 'e' object in {} brackets.
*
* The values for the y axis can be accessed in the path string using {e.y0}, {e.y1}, ....
* The values for the x axis can be accessed in the path string using {e.x0}, {e.x1}, ....
*
* The numbers x0, x1 respectively y0, y1, ... map to the corresponding array index.
*
*/
this.pathMap = {
'EVENT_MESSAGE': {
d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}',
height: 36,
width: 36,
heightElements: [6, 14],
widthElements: [10.5, 21]
},
'EVENT_SIGNAL': {
d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x1},0 Z',
height: 36,
width: 36,
heightElements: [18],
widthElements: [10, 20]
},
'EVENT_ESCALATION': {
d: 'M {mx},{my} l {e.x0},{e.y0} l -{e.x0},-{e.y1} l -{e.x0},{e.y1} Z',
height: 36,
width: 36,
heightElements: [20, 7],
widthElements: [8]
},
'EVENT_CONDITIONAL': {
d: 'M {e.x0},{e.y0} l {e.x1},0 l 0,{e.y2} l -{e.x1},0 Z ' +
'M {e.x2},{e.y3} l {e.x0},0 ' +
'M {e.x2},{e.y4} l {e.x0},0 ' +
'M {e.x2},{e.y5} l {e.x0},0 ' +
'M {e.x2},{e.y6} l {e.x0},0 ' +
'M {e.x2},{e.y7} l {e.x0},0 ' +
'M {e.x2},{e.y8} l {e.x0},0 ',
height: 36,
width: 36,
heightElements: [8.5, 14.5, 18, 11.5, 14.5, 17.5, 20.5, 23.5, 26.5],
widthElements: [10.5, 14.5, 12.5]
},
'EVENT_LINK': {
d: 'm {mx},{my} 0,{e.y0} -{e.x1},0 0,{e.y1} {e.x1},0 0,{e.y0} {e.x0},-{e.y2} -{e.x0},-{e.y2} z',
height: 36,
width: 36,
heightElements: [4.4375, 6.75, 7.8125],
widthElements: [9.84375, 13.5]
},
'EVENT_ERROR': {
d: 'm {mx},{my} {e.x0},-{e.y0} {e.x1},-{e.y1} {e.x2},{e.y2} {e.x3},-{e.y3} -{e.x4},{e.y4} -{e.x5},-{e.y5} z',
height: 36,
width: 36,
heightElements: [0.023, 8.737, 8.151, 16.564, 10.591, 8.714],
widthElements: [0.085, 6.672, 6.97, 4.273, 5.337, 6.636]
},
'EVENT_CANCEL_45': {
d: 'm {mx},{my} -{e.x1},0 0,{e.x0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
'0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
height: 36,
width: 36,
heightElements: [4.75, 8.5],
widthElements: [4.75, 8.5]
},
'EVENT_COMPENSATION': {
d: 'm {mx},{my} {e.x0},-{e.y0} 0,{e.y1} z m {e.x1},-{e.y2} {e.x2},-{e.y3} 0,{e.y1} -{e.x2},-{e.y3} z',
height: 36,
width: 36,
heightElements: [6.5, 13, 0.4, 6.1],
widthElements: [9, 9.3, 8.7]
},
'EVENT_TIMER_WH': {
d: 'M {mx},{my} l {e.x0},-{e.y0} m -{e.x0},{e.y0} l {e.x1},{e.y1} ',
height: 36,
width: 36,
heightElements: [10, 2],
widthElements: [3, 7]
},
'EVENT_TIMER_LINE': {
d: 'M {mx},{my} ' +
'm {e.x0},{e.y0} l -{e.x1},{e.y1} ',
height: 36,
width: 36,
heightElements: [10, 3],
widthElements: [0, 0]
},
'EVENT_MULTIPLE': {
d:'m {mx},{my} {e.x1},-{e.y0} {e.x1},{e.y0} -{e.x0},{e.y1} -{e.x2},0 z',
height: 36,
width: 36,
heightElements: [6.28099, 12.56199],
widthElements: [3.1405, 9.42149, 12.56198]
},
'EVENT_PARALLEL_MULTIPLE': {
d:'m {mx},{my} {e.x0},0 0,{e.y1} {e.x1},0 0,{e.y0} -{e.x1},0 0,{e.y1} ' +
'-{e.x0},0 0,-{e.y1} -{e.x1},0 0,-{e.y0} {e.x1},0 z',
height: 36,
width: 36,
heightElements: [2.56228, 7.68683],
widthElements: [2.56228, 7.68683]
},
'GATEWAY_EXCLUSIVE': {
d:'m {mx},{my} {e.x0},{e.y0} {e.x1},{e.y0} {e.x2},0 {e.x4},{e.y2} ' +
'{e.x4},{e.y1} {e.x2},0 {e.x1},{e.y3} {e.x0},{e.y3} ' +
'{e.x3},0 {e.x5},{e.y1} {e.x5},{e.y2} {e.x3},0 z',
height: 17.5,
width: 17.5,
heightElements: [8.5, 6.5312, -6.5312, -8.5],
widthElements: [6.5, -6.5, 3, -3, 5, -5]
},
'GATEWAY_PARALLEL': {
d:'m {mx},{my} 0,{e.y1} -{e.x1},0 0,{e.y0} {e.x1},0 0,{e.y1} {e.x0},0 ' +
'0,-{e.y1} {e.x1},0 0,-{e.y0} -{e.x1},0 0,-{e.y1} -{e.x0},0 z',
height: 30,
width: 30,
heightElements: [5, 12.5],
widthElements: [5, 12.5]
},
'GATEWAY_EVENT_BASED': {
d:'m {mx},{my} {e.x0},{e.y0} {e.x0},{e.y1} {e.x1},{e.y2} {e.x2},0 z',
height: 11,
width: 11,
heightElements: [-6, 6, 12, -12],
widthElements: [9, -3, -12]
},
'GATEWAY_COMPLEX': {
d:'m {mx},{my} 0,{e.y0} -{e.x0},-{e.y1} -{e.x1},{e.y2} {e.x0},{e.y1} -{e.x2},0 0,{e.y3} ' +
'{e.x2},0 -{e.x0},{e.y1} l {e.x1},{e.y2} {e.x0},-{e.y1} 0,{e.y0} {e.x3},0 0,-{e.y0} {e.x0},{e.y1} ' +
'{e.x1},-{e.y2} -{e.x0},-{e.y1} {e.x2},0 0,-{e.y3} -{e.x2},0 {e.x0},-{e.y1} -{e.x1},-{e.y2} ' +
'-{e.x0},{e.y1} 0,-{e.y0} -{e.x3},0 z',
height: 17.125,
width: 17.125,
heightElements: [4.875, 3.4375, 2.125, 3],
widthElements: [3.4375, 2.125, 4.875, 3]
},
'DATA_OBJECT_PATH': {
d:'m 0,0 {e.x1},0 {e.x0},{e.y0} 0,{e.y1} -{e.x2},0 0,-{e.y2} {e.x1},0 0,{e.y0} {e.x0},0',
height: 61,
width: 51,
heightElements: [10, 50, 60],
widthElements: [10, 40, 50, 60]
},
'DATA_OBJECT_COLLECTION_PATH': {
d:'m {mx}, {my} ' +
'm 0 15 l 0 -15 ' +
'm 4 15 l 0 -15 ' +
'm 4 15 l 0 -15 ',
height: 61,
width: 51,
heightElements: [12],
widthElements: [1, 6, 12, 15]
},
'DATA_ARROW': {
d:'m 5,9 9,0 0,-3 5,5 -5,5 0,-3 -9,0 z',
height: 61,
width: 51,
heightElements: [],
widthElements: []
},
'DATA_STORE': {
d:'m {mx},{my} ' +
'l 0,{e.y2} ' +
'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
'l 0,-{e.y2} ' +
'c -{e.x0},-{e.y1} -{e.x1},-{e.y1} -{e.x2},0' +
'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0 ' +
'm -{e.x2},{e.y0}' +
'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0' +
'm -{e.x2},{e.y0}' +
'c {e.x0},{e.y1} {e.x1},{e.y1} {e.x2},0',
height: 61,
width: 61,
heightElements: [7, 10, 45],
widthElements: [2, 58, 60]
},
'TEXT_ANNOTATION': {
d: 'm {mx}, {my} m 10,0 l -10,0 l 0,{e.y0} l 10,0',
height: 30,
width: 10,
heightElements: [30],
widthElements: [10]
},
'MARKER_SUB_PROCESS': {
d: 'm{mx},{my} m 7,2 l 0,10 m -5,-5 l 10,0',
height: 10,
width: 10,
heightElements: [],
widthElements: []
},
'MARKER_PARALLEL': {
d: 'm{mx},{my} m 3,2 l 0,10 m 3,-10 l 0,10 m 3,-10 l 0,10',
height: 10,
width: 10,
heightElements: [],
widthElements: []
},
'MARKER_SEQUENTIAL': {
d: 'm{mx},{my} m 0,3 l 10,0 m -10,3 l 10,0 m -10,3 l 10,0',
height: 10,
width: 10,
heightElements: [],
widthElements: []
},
'MARKER_COMPENSATION': {
d: 'm {mx},{my} 7,-5 0,10 z m 7.1,-0.3 6.9,-4.7 0,10 -6.9,-4.7 z',
height: 10,
width: 21,
heightElements: [],
widthElements: []
},
'MARKER_LOOP': {
d: 'm {mx},{my} c 3.526979,0 6.386161,-2.829858 6.386161,-6.320661 0,-3.490806 -2.859182,-6.320661 ' +
'-6.386161,-6.320661 -3.526978,0 -6.38616,2.829855 -6.38616,6.320661 0,1.745402 ' +
'0.714797,3.325567 1.870463,4.469381 0.577834,0.571908 1.265885,1.034728 2.029916,1.35457 ' +
'l -0.718163,-3.909793 m 0.718163,3.909793 -3.885211,0.802902',
height: 13.9,
width: 13.7,
heightElements: [],
widthElements: []
},
'MARKER_ADHOC': {
d: 'm {mx},{my} m 0.84461,2.64411 c 1.05533,-1.23780996 2.64337,-2.07882 4.29653,-1.97997996 2.05163,0.0805 ' +
'3.85579,1.15803 5.76082,1.79107 1.06385,0.34139996 2.24454,0.1438 3.18759,-0.43767 0.61743,-0.33642 ' +
'1.2775,-0.64078 1.7542,-1.17511 0,0.56023 0,1.12046 0,1.6807 -0.98706,0.96237996 -2.29792,1.62393996 ' +
'-3.6918,1.66181996 -1.24459,0.0927 -2.46671,-0.2491 -3.59505,-0.74812 -1.35789,-0.55965 ' +
'-2.75133,-1.33436996 -4.27027,-1.18121996 -1.37741,0.14601 -2.41842,1.13685996 -3.44288,1.96782996 z',
height: 4,
width: 15,
heightElements: [],
widthElements: []
},
'TASK_TYPE_SEND': {
d: 'm {mx},{my} l 0,{e.y1} l {e.x1},0 l 0,-{e.y1} z l {e.x0},{e.y0} l {e.x0},-{e.y0}',
height: 14,
width: 21,
heightElements: [6, 14],
widthElements: [10.5, 21]
},
'TASK_TYPE_SCRIPT': {
d: 'm {mx},{my} c 9.966553,-6.27276 -8.000926,-7.91932 2.968968,-14.938 l -8.802728,0 ' +
'c -10.969894,7.01868 6.997585,8.66524 -2.968967,14.938 z ' +
'm -7,-12 l 5,0 ' +
'm -4.5,3 l 4.5,0 ' +
'm -3,3 l 5,0' +
'm -4,3 l 5,0',
height: 15,
width: 12.6,
heightElements: [6, 14],
widthElements: [10.5, 21]
},
'TASK_TYPE_USER_1': {
d: 'm {mx},{my} c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 ' +
'-4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 ' +
'0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 ' +
'h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 z' +
'm -8,6 l 0,5.5 m 11,0 l 0,-5'
},
'TASK_TYPE_USER_2': {
d: 'm {mx},{my} m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 ' +
'-2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 '
},
'TASK_TYPE_USER_3': {
d: 'm {mx},{my} m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 ' +
'4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 ' +
'-4.20799998,3.36699999 -4.20699998,4.34799999 z'
},
'TASK_TYPE_MANUAL': {
d: 'm {mx},{my} c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 ' +
'-0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 ' +
'0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 ' +
'-1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 ' +
'0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 ' +
'-10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 ' +
'2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 ' +
'-0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 ' +
'-1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 ' +
'-0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 ' +
'0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 ' +
'-5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z'
},
'TASK_TYPE_INSTANTIATING_SEND': {
d: 'm {mx},{my} l 0,8.4 l 12.6,0 l 0,-8.4 z l 6.3,3.6 l 6.3,-3.6'
},
'TASK_TYPE_SERVICE': {
d: 'm {mx},{my} v -1.71335 c 0.352326,-0.0705 0.703932,-0.17838 1.047628,-0.32133 ' +
'0.344416,-0.14465 0.665822,-0.32133 0.966377,-0.52145 l 1.19431,1.18005 1.567487,-1.57688 ' +
'-1.195028,-1.18014 c 0.403376,-0.61394 0.683079,-1.29908 0.825447,-2.01824 l 1.622133,-0.01 ' +
'v -2.2196 l -1.636514,0.01 c -0.07333,-0.35153 -0.178319,-0.70024 -0.323564,-1.04372 ' +
'-0.145244,-0.34406 -0.321407,-0.6644 -0.522735,-0.96217 l 1.131035,-1.13631 -1.583305,-1.56293 ' +
'-1.129598,1.13589 c -0.614052,-0.40108 -1.302883,-0.68093 -2.022633,-0.82247 l 0.0093,-1.61852 ' +
'h -2.241173 l 0.0042,1.63124 c -0.353763,0.0736 -0.705369,0.17977 -1.049785,0.32371 -0.344415,0.14437 ' +
'-0.665102,0.32092 -0.9635006,0.52046 l -1.1698628,-1.15823 -1.5667691,1.5792 1.1684265,1.15669 ' +
'c -0.4026573,0.61283 -0.68308,1.29797 -0.8247287,2.01713 l -1.6588041,0.003 v 2.22174 ' +
'l 1.6724648,-0.006 c 0.073327,0.35077 0.1797598,0.70243 0.3242851,1.04472 0.1452428,0.34448 ' +
'0.3214064,0.6644 0.5227339,0.96066 l -1.1993431,1.19723 1.5840256,1.56011 1.1964668,-1.19348 ' +
'c 0.6140517,0.40346 1.3028827,0.68232 2.0233517,0.82331 l 7.19e-4,1.69892 h 2.226848 z ' +
'm 0.221462,-3.9957 c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
'0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
'0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
},
'TASK_TYPE_SERVICE_FILL': {
d: 'm {mx},{my} c -1.788948,0.7502 -3.8576,-0.0928 -4.6097055,-1.87438 -0.7521065,-1.78321 ' +
'0.090598,-3.84627 1.8802645,-4.59604 1.78823,-0.74936 3.856881,0.0929 4.608987,1.87437 ' +
'0.752106,1.78165 -0.0906,3.84612 -1.879546,4.59605 z'
},
'TASK_TYPE_BUSINESS_RULE_HEADER': {
d: 'm {mx},{my} 0,4 20,0 0,-4 z'
},
'TASK_TYPE_BUSINESS_RULE_MAIN': {
d: 'm {mx},{my} 0,12 20,0 0,-12 z' +
'm 0,8 l 20,0 ' +
'm -13,-4 l 0,8'
},
'MESSAGE_FLOW_MARKER': {
d: 'm {mx},{my} m -10.5 ,-7 l 0,14 l 21,0 l 0,-14 z l 10.5,6 l 10.5,-6'
}
};
this.getRawPath = function getRawPath(pathId) {
return this.pathMap[pathId].d;
};
/**
* Scales the path to the given height and width.
* Use case
* Use case is to scale the content of elements (event, gateways) based
* on the element bounding box's size.
*
* Why not transform
* Scaling a path with transform() will also scale the stroke and IE does not support
* the option 'non-scaling-stroke' to prevent this.
* Also there are use cases where only some parts of a path should be
* scaled.
*
* @param {String} pathId The ID of the path.
* @param {Object} param
* Example param object scales the path to 60% size of the container (data.width, data.height).
*
* {
* xScaleFactor: 0.6,
* yScaleFactor:0.6,
* containerWidth: data.width,
* containerHeight: data.height,
* position: {
* mx: 0.46,
* my: 0.2,
* }
* }
*
*
* targetpathwidth = xScaleFactor * containerWidth
* targetpathheight = yScaleFactor * containerHeight
* Position is used to set the starting coordinate of the path. M is computed:
*
* position.x * containerWidth
* position.y * containerHeight
*
* Center of the container position: {
* mx: 0.5,
* my: 0.5,
* }
* Upper left corner of the container
* position: {
* mx: 0.0,
* my: 0.0,
* }
*
*
*
*
*/
this.getScaledPath = function getScaledPath(pathId, param) {
var rawPath = this.pathMap[pathId];
// positioning
// compute the start point of the path
var mx, my;
if (param.abspos) {
mx = param.abspos.x;
my = param.abspos.y;
} else {
mx = param.containerWidth * param.position.mx;
my = param.containerHeight * param.position.my;
}
var coordinates = {}; //map for the scaled coordinates
if (param.position) {
// path
var heightRatio = (param.containerHeight / rawPath.height) * param.yScaleFactor;
var widthRatio = (param.containerWidth / rawPath.width) * param.xScaleFactor;
//Apply height ratio
for (var heightIndex = 0; heightIndex < rawPath.heightElements.length; heightIndex++) {
coordinates['y' + heightIndex] = rawPath.heightElements[heightIndex] * heightRatio;
}
//Apply width ratio
for (var widthIndex = 0; widthIndex < rawPath.widthElements.length; widthIndex++) {
coordinates['x' + widthIndex] = rawPath.widthElements[widthIndex] * widthRatio;
}
}
//Apply value to raw path
var path = format(
rawPath.d, {
mx: mx,
my: my,
e: coordinates
}
);
return path;
};
}
module.exports = PathMap;
////////// helpers //////////
// copied from https://github.com/adobe-webplatform/Snap.svg/blob/master/src/svg.js
var tokenRegex = /\{([^\}]+)\}/g,
objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g; // matches .xxxxx or ["xxxxx"] to run over object properties
function replacer(all, key, obj) {
var res = obj;
key.replace(objNotationRegex, function(all, name, quote, quotedName, isFunc) {
name = name || quotedName;
if (res) {
if (name in res) {
res = res[name];
}
typeof res == 'function' && isFunc && (res = res());
}
});
res = (res == null || res == obj ? all : res) + '';
return res;
}
function format(str, obj) {
return String(str).replace(tokenRegex, function(all, key) {
return replacer(all, key, obj);
});
}
},{}],7:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'bpmnRenderer' ],
bpmnRenderer: [ 'type', _dereq_(5) ],
pathMap: [ 'type', _dereq_(6) ]
};
},{"5":5,"6":6}],8:[function(_dereq_,module,exports){
var AutoResize = _dereq_(136);
var inherits = _dereq_(289);
var is = _dereq_(95).is;
/**
* Sub class of the AutoResize module which implements a BPMN
* specific resize function.
*/
function BpmnAutoResize(eventBus, elementRegistry, modeling, rules) {
AutoResize.call(this, eventBus, elementRegistry, modeling, rules);
}
BpmnAutoResize.$inject = [ 'eventBus', 'elementRegistry', 'modeling', 'rules' ];
inherits(BpmnAutoResize, AutoResize);
module.exports = BpmnAutoResize;
/**
* Resize shapes and lanes
*
* @param {djs.model.Shape} target
* @param {Object} newBounds
*/
BpmnAutoResize.prototype.resize = function(target, newBounds) {
if (is(target, 'bpmn:Participant')) {
this._modeling.resizeLane(target, newBounds);
} else {
this._modeling.resizeShape(target, newBounds);
}
};
},{"136":136,"289":289,"95":95}],9:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
var inherits = _dereq_(289);
var forEach = _dereq_(303);
var AutoResizeProvider = _dereq_(137);
/**
* This module is a provider for automatically resizing parent BPMN elements
*/
function BpmnAutoResizeProvider(eventBus, modeling) {
AutoResizeProvider.call(this, eventBus);
this._modeling = modeling;
}
inherits(BpmnAutoResizeProvider, AutoResizeProvider);
BpmnAutoResizeProvider.$inject = [ 'eventBus', 'modeling' ];
module.exports = BpmnAutoResizeProvider;
/**
* Check if the given target can be expanded
*
* @param {djs.model.Shape} target
*
* @return {boolean}
*/
BpmnAutoResizeProvider.prototype.canResize = function(elements, target) {
if (!is(target, 'bpmn:Participant') && !is(target, 'bpmn:Lane') && !(is(target, 'bpmn:SubProcess'))) {
return false;
}
var canResize = true;
forEach(elements, function(element) {
if (is(element, 'bpmn:Lane') || element.labelTarget) {
canResize = false;
return;
}
});
return canResize;
};
},{"137":137,"289":289,"303":303,"95":95}],10:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'bpmnAutoResize', 'bpmnAutoResizeProvider' ],
bpmnAutoResize: [ 'type', _dereq_(8) ],
bpmnAutoResizeProvider: [ 'type', _dereq_(9) ]
};
},{"8":8,"9":9}],11:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303),
isArray = _dereq_(422),
is = _dereq_(95).is,
isExpanded = _dereq_(93).isExpanded,
isAny = _dereq_(68).isAny,
getChildLanes = _dereq_(67).getChildLanes,
isEventSubProcess = _dereq_(93).isEventSubProcess,
hasPrimaryModifier = _dereq_(274).hasPrimaryModifier;
/**
* A provider for BPMN 2.0 elements context pad
*/
function ContextPadProvider(eventBus, contextPad, modeling, elementFactory,
connect, create, popupMenu,
canvas, rules, translate) {
contextPad.registerProvider(this);
this._contextPad = contextPad;
this._modeling = modeling;
this._elementFactory = elementFactory;
this._connect = connect;
this._create = create;
this._popupMenu = popupMenu;
this._canvas = canvas;
this._rules = rules;
this._translate = translate;
eventBus.on('create.end', 250, function(event) {
var shape = event.context.shape;
if (!hasPrimaryModifier(event)) {
return;
}
var entries = contextPad.getEntries(shape);
if (entries.replace) {
entries.replace.action.click(event, shape);
}
});
}
ContextPadProvider.$inject = [
'eventBus',
'contextPad',
'modeling',
'elementFactory',
'connect',
'create',
'popupMenu',
'canvas',
'rules',
'translate'
];
module.exports = ContextPadProvider;
ContextPadProvider.prototype.getContextPadEntries = function(element) {
var contextPad = this._contextPad,
modeling = this._modeling,
elementFactory = this._elementFactory,
connect = this._connect,
create = this._create,
popupMenu = this._popupMenu,
canvas = this._canvas,
rules = this._rules,
translate = this._translate;
var actions = {};
if (element.type === 'label') {
return actions;
}
var businessObject = element.businessObject;
function startConnect(event, element, autoActivate) {
connect.start(event, element, autoActivate);
}
function removeElement(e) {
modeling.removeElements([ element ]);
}
function getReplaceMenuPosition(element) {
var Y_OFFSET = 5;
var diagramContainer = canvas.getContainer(),
pad = contextPad.getPad(element).html;
var diagramRect = diagramContainer.getBoundingClientRect(),
padRect = pad.getBoundingClientRect();
var top = padRect.top - diagramRect.top;
var left = padRect.left - diagramRect.left;
var pos = {
x: left,
y: top + padRect.height + Y_OFFSET
};
return pos;
}
/**
* Create an append action
*
* @param {String} type
* @param {String} className
* @param {String} [title]
* @param {Object} [options]
*
* @return {Object} descriptor
*/
function appendAction(type, className, title, options) {
if (typeof title !== 'string') {
options = title;
title = translate('Append {type}', { type: type.replace(/^bpmn\:/, '') });
}
function appendListener(event, element) {
var shape = elementFactory.createShape(assign({ type: type }, options));
create.start(event, shape, element);
}
return {
group: 'model',
className: className,
title: title,
action: {
dragstart: appendListener,
click: appendListener
}
};
}
function splitLaneHandler(count) {
return function(event, element) {
// actual split
modeling.splitLane(element, count);
// refresh context pad after split to
// get rid of split icons
contextPad.open(element, true);
};
}
if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(businessObject)) {
var childLanes = getChildLanes(element);
assign(actions, {
'lane-insert-above': {
group: 'lane-insert-above',
className: 'bpmn-icon-lane-insert-above',
title: translate('Add Lane above'),
action: {
click: function(event, element) {
modeling.addLane(element, 'top');
}
}
}
});
if (childLanes.length < 2) {
if (element.height >= 120) {
assign(actions, {
'lane-divide-two': {
group: 'lane-divide',
className: 'bpmn-icon-lane-divide-two',
title: translate('Divide into two Lanes'),
action: {
click: splitLaneHandler(2)
}
}
});
}
if (element.height >= 180) {
assign(actions, {
'lane-divide-three': {
group: 'lane-divide',
className: 'bpmn-icon-lane-divide-three',
title: translate('Divide into three Lanes'),
action: {
click: splitLaneHandler(3)
}
}
});
}
}
assign(actions, {
'lane-insert-below': {
group: 'lane-insert-below',
className: 'bpmn-icon-lane-insert-below',
title: translate('Add Lane below'),
action: {
click: function(event, element) {
modeling.addLane(element, 'bottom');
}
}
}
});
}
if (is(businessObject, 'bpmn:FlowNode')) {
if (is(businessObject, 'bpmn:EventBasedGateway')) {
assign(actions, {
'append.receive-task': appendAction('bpmn:ReceiveTask', 'bpmn-icon-receive-task'),
'append.message-intermediate-event': appendAction('bpmn:IntermediateCatchEvent',
'bpmn-icon-intermediate-event-catch-message',
{ eventDefinitionType: 'bpmn:MessageEventDefinition' }),
'append.timer-intermediate-event': appendAction('bpmn:IntermediateCatchEvent',
'bpmn-icon-intermediate-event-catch-timer',
{ eventDefinitionType: 'bpmn:TimerEventDefinition' }),
'append.condtion-intermediate-event': appendAction('bpmn:IntermediateCatchEvent',
'bpmn-icon-intermediate-event-catch-condition',
{ eventDefinitionType: 'bpmn:ConditionalEventDefinition' }),
'append.signal-intermediate-event': appendAction('bpmn:IntermediateCatchEvent',
'bpmn-icon-intermediate-event-catch-signal',
{ eventDefinitionType: 'bpmn:SignalEventDefinition' })
});
} else
if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) {
assign(actions, {
'append.compensation-activity':
appendAction('bpmn:Task', 'bpmn-icon-task', translate('Append compensation activity'), {
isForCompensation: true
})
});
} else
if (!is(businessObject, 'bpmn:EndEvent') &&
!businessObject.isForCompensation &&
!isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') &&
!isEventSubProcess(businessObject)) {
assign(actions, {
'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none'),
'append.gateway': appendAction('bpmn:ExclusiveGateway', 'bpmn-icon-gateway-xor'),
'append.append-task': appendAction('bpmn:Task', 'bpmn-icon-task'),
'append.intermediate-event': appendAction('bpmn:IntermediateThrowEvent',
'bpmn-icon-intermediate-event-none')
});
}
}
var replaceMenu;
if (popupMenu._providers['bpmn-replace']) {
replaceMenu = popupMenu.create('bpmn-replace', element);
}
if (replaceMenu && !replaceMenu.isEmpty()) {
// Replace menu entry
assign(actions, {
'replace': {
group: 'edit',
className: 'bpmn-icon-screw-wrench',
title: translate('Change type'),
action: {
click: function(event, element) {
replaceMenu.open(assign(getReplaceMenuPosition(element), {
cursor: { x: event.x, y: event.y }
}), element);
}
}
}
});
}
if (isAny(businessObject, [
'bpmn:FlowNode',
'bpmn:InteractionNode',
'bpmn:DataObjectReference',
'bpmn:DataStoreReference'
]) ) {
assign(actions, {
'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'),
'connect': {
group: 'connect',
className: 'bpmn-icon-connection-multi',
title: translate('Connect using ' +
(businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') +
'Association'),
action: {
click: startConnect,
dragstart: startConnect
}
}
});
}
if (isAny(businessObject, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ])) {
assign(actions, {
'connect': {
group: 'connect',
className: 'bpmn-icon-connection-multi',
title: translate('Connect using DataInputAssociation'),
action: {
click: startConnect,
dragstart: startConnect
}
}
});
}
// delete element entry, only show if allowed by rules
var deleteAllowed = rules.allowed('elements.delete', { elements: [ element ] });
if (isArray(deleteAllowed)) {
// was the element returned as a deletion candidate?
deleteAllowed = deleteAllowed[0] === element;
}
if (deleteAllowed) {
assign(actions, {
'delete': {
group: 'edit',
className: 'bpmn-icon-trash',
title: translate('Remove'),
action: {
click: removeElement,
dragstart: removeElement
}
}
});
}
return actions;
};
function isEventType(eventBo, type, definition) {
var isType = eventBo.$instanceOf(type);
var isDefinition = false;
var definitions = eventBo.eventDefinitions || [];
forEach(definitions, function(def) {
if (def.$type === definition) {
isDefinition = true;
}
});
return isType && isDefinition;
}
},{"274":274,"303":303,"422":422,"431":431,"67":67,"68":68,"93":93,"95":95}],12:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(114),
_dereq_(153),
_dereq_(233),
_dereq_(151),
_dereq_(157),
_dereq_(74)
],
__init__: [ 'contextPadProvider' ],
contextPadProvider: [ 'type', _dereq_(11) ]
};
},{"11":11,"114":114,"151":151,"153":153,"157":157,"233":233,"74":74}],13:[function(_dereq_,module,exports){
'use strict';
var ModelUtil = _dereq_(95),
getBusinessObject = ModelUtil.getBusinessObject,
is = ModelUtil.is;
var ModelCloneHelper = _dereq_(97);
var ModelCloneUtils = _dereq_(98),
getProperties = ModelCloneUtils.getProperties;
var IGNORED_PROPERTIES = ModelCloneUtils.IGNORED_PROPERTIES;
var filter = _dereq_(301),
forEach = _dereq_(303);
function setProperties(descriptor, data, properties) {
forEach(properties, function(property) {
if (data[property] !== undefined) {
descriptor[property] = data[property];
}
});
}
function removeProperties(element, properties) {
forEach(properties, function(prop) {
if (element[prop]) {
delete element[prop];
}
});
}
function BpmnCopyPaste(
bpmnFactory, eventBus, copyPaste,
clipboard, canvas, bpmnRules) {
var helper = new ModelCloneHelper(eventBus);
copyPaste.registerDescriptor(function(element, descriptor) {
var businessObject = descriptor.oldBusinessObject = getBusinessObject(element);
var colors = {};
descriptor.type = element.type;
setProperties(descriptor, businessObject.di, [ 'isExpanded' ]);
setProperties(colors, businessObject.di, [ 'fill', 'stroke' ]);
descriptor.colors = colors;
if (element.type === 'label') {
return descriptor;
}
setProperties(descriptor, businessObject, [
'type',
'processRef',
'triggeredByEvent'
]);
if (businessObject.default) {
descriptor.default = businessObject.default.id;
}
return descriptor;
});
eventBus.on('element.paste', function(context) {
var descriptor = context.descriptor,
createdElements = context.createdElements,
parent = descriptor.parent,
rootElement = canvas.getRootElement(),
oldBusinessObject = descriptor.oldBusinessObject,
newBusinessObject,
source,
target,
canConnect;
newBusinessObject = bpmnFactory.create(oldBusinessObject.$type);
var properties = getProperties(oldBusinessObject.$descriptor);
properties = filter(properties, function(property) {
return IGNORED_PROPERTIES.indexOf(property.replace(/bpmn:/, '')) === -1;
});
descriptor.businessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);
if (descriptor.type === 'label') {
return;
}
if (is(parent, 'bpmn:Process')) {
descriptor.parent = is(rootElement, 'bpmn:Collaboration') ? rootElement : parent;
}
if (descriptor.type === 'bpmn:DataOutputAssociation' ||
descriptor.type === 'bpmn:DataInputAssociation' ||
descriptor.type === 'bpmn:MessageFlow') {
descriptor.parent = rootElement;
}
if (is(parent, 'bpmn:Lane')) {
descriptor.parent = parent.parent;
}
// make sure that the correct type of connection is created
if (descriptor.waypoints) {
source = createdElements[descriptor.source];
target = createdElements[descriptor.target];
if (source && target) {
source = source.element;
target = target.element;
}
canConnect = bpmnRules.canConnect(source, target);
if (canConnect) {
descriptor.type = canConnect.type;
}
}
// remove the id or else we cannot paste multiple times
delete newBusinessObject.id;
// assign an ID
bpmnFactory._ensureId(newBusinessObject);
if (descriptor.type === 'bpmn:Participant' && descriptor.processRef) {
descriptor.processRef = newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
}
setProperties(newBusinessObject, descriptor, [
'isExpanded',
'triggeredByEvent'
]);
removeProperties(descriptor, [
'triggeredByEvent'
]);
});
}
BpmnCopyPaste.$inject = [
'bpmnFactory',
'eventBus',
'copyPaste',
'clipboard',
'canvas',
'bpmnRules'
];
module.exports = BpmnCopyPaste;
},{"301":301,"303":303,"95":95,"97":97,"98":98}],14:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(155)
],
__init__: [ 'bpmnCopyPaste' ],
bpmnCopyPaste: [ 'type', _dereq_(13) ]
};
},{"13":13,"155":155}],15:[function(_dereq_,module,exports){
'use strict';
var filter = _dereq_(301);
var isAny = _dereq_(68).isAny;
/**
* Registers element exclude filters for elements that currently do
* not support distribution.
*/
function BpmnDistributeElements(distributeElements) {
distributeElements.registerFilter(function(elements) {
return filter(elements, function(element) {
var cannotDistribute = isAny(element, [
'bpmn:Association',
'bpmn:BoundaryEvent',
'bpmn:DataInputAssociation',
'bpmn:DataOutputAssociation',
'bpmn:Lane',
'bpmn:MessageFlow',
'bpmn:Participant',
'bpmn:SequenceFlow',
'bpmn:TextAnnotation'
]);
return !(element.labelTarget || cannotDistribute);
});
});
}
BpmnDistributeElements.$inject = [ 'distributeElements' ];
module.exports = BpmnDistributeElements;
},{"301":301,"68":68}],16:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(159)
],
__init__: [ 'bpmnDistributeElements' ],
bpmnDistributeElements: [ 'type', _dereq_(15) ]
};
},{"15":15,"159":159}],17:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var EditorActions = _dereq_(163);
var filter = _dereq_(301);
var is = _dereq_(95).is;
var getBBox = _dereq_(266).getBBox;
function BpmnEditorActions(
injector,
canvas, elementRegistry, selection,
spaceTool,
lassoTool,
handTool,
globalConnect,
distributeElements,
alignElements,
directEditing,
searchPad,
modeling) {
injector.invoke(EditorActions, this);
this.register({
selectElements: function() {
// select all elements except for the invisible
// root element
var rootElement = canvas.getRootElement();
var elements = elementRegistry.filter(function(element) {
return element !== rootElement;
});
selection.select(elements);
return elements;
},
spaceTool: function() {
spaceTool.toggle();
},
lassoTool: function() {
lassoTool.toggle();
},
handTool: function() {
handTool.toggle();
},
globalConnectTool: function() {
globalConnect.toggle();
},
distributeElements: function(opts) {
var currentSelection = selection.get(),
type = opts.type;
if (currentSelection.length) {
distributeElements.trigger(currentSelection, type);
}
},
alignElements: function(opts) {
var currentSelection = selection.get(),
aligneableElements = [],
type = opts.type;
if (currentSelection.length) {
aligneableElements = filter(currentSelection, function(element) {
return !is(element, 'bpmn:Lane');
});
alignElements.trigger(aligneableElements, type);
}
},
setColor: function(opts) {
var currentSelection = selection.get();
if (currentSelection.length) {
modeling.setColor(currentSelection, opts);
}
},
directEditing: function() {
var currentSelection = selection.get();
if (currentSelection.length) {
directEditing.activate(currentSelection[0]);
}
},
find: function() {
searchPad.toggle();
},
moveToOrigin: function() {
var rootElement = canvas.getRootElement(),
boundingBox,
elements;
if (is(rootElement, 'bpmn:Collaboration')) {
elements = elementRegistry.filter(function(element) {
return is(element.parent, 'bpmn:Collaboration');
});
} else {
elements = elementRegistry.filter(function(element) {
return element !== rootElement && !is(element.parent, 'bpmn:SubProcess');
});
}
boundingBox = getBBox(elements);
modeling.moveElements(elements, { x: -boundingBox.x, y: -boundingBox.y }, rootElement);
}
});
}
inherits(BpmnEditorActions, EditorActions);
BpmnEditorActions.$inject = [
'injector',
'canvas', 'elementRegistry', 'selection',
'spaceTool',
'lassoTool',
'handTool',
'globalConnect',
'distributeElements',
'alignElements',
'directEditing',
'searchPad',
'modeling'
];
module.exports = BpmnEditorActions;
},{"163":163,"266":266,"289":289,"301":301,"95":95}],18:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(133),
_dereq_(164),
_dereq_(168),
_dereq_(176),
_dereq_(240),
_dereq_(20),
_dereq_(14),
_dereq_(16),
_dereq_(84),
_dereq_(66)
],
editorActions: [ 'type', _dereq_(17) ]
};
},{"133":133,"14":14,"16":16,"164":164,"168":168,"17":17,"176":176,"20":20,"240":240,"66":66,"84":84}],19:[function(_dereq_,module,exports){
'use strict';
var isAny = _dereq_(68).isAny;
/**
* Extention of GlobalConnect tool that implements BPMN specific rules about
* connection start elements.
*/
function BpmnGlobalConnect(globalConnect) {
globalConnect.registerProvider(this);
}
BpmnGlobalConnect.$inject = [ 'globalConnect' ];
module.exports = BpmnGlobalConnect;
/**
* Checks if given element can be used for starting connection.
*
* @param {Element} source
* @return {Boolean}
*/
BpmnGlobalConnect.prototype.canStartConnect = function(source) {
if (nonExistantOrLabel(source)) {
return null;
}
var businessObject = source.businessObject;
return isAny(businessObject, [
'bpmn:FlowNode',
'bpmn:InteractionNode',
'bpmn:DataObjectReference',
'bpmn:DataStoreReference'
]);
};
function nonExistantOrLabel(element) {
return !element || isLabel(element);
}
function isLabel(element) {
return element.labelTarget;
}
},{"68":68}],20:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(166)
],
__init__: [ 'bpmnGlobalConnect' ],
bpmnGlobalConnect: [ 'type', _dereq_(19) ]
};
},{"166":166,"19":19}],21:[function(_dereq_,module,exports){
'use strict';
/**
* BPMN 2.0 specific key bindings.
*
* @param {Keyboard} keyboard
* @param {EditorActions} editorActions
*/
function BpmnKeyBindings(keyboard, editorActions) {
keyboard.addListener(function(key, modifiers) {
// ctrl + a -> select all elements
if (key === 65 && keyboard.isCmd(modifiers)) {
editorActions.trigger('selectElements');
return true;
}
// ctrl + f -> search labels
if (key === 70 && keyboard.isCmd(modifiers)) {
editorActions.trigger('find');
return true;
}
if (keyboard.hasModifier(modifiers)) {
return;
}
// s -> activate space tool
if (key === 83) {
editorActions.trigger('spaceTool');
return true;
}
// l -> activate lasso tool
if (key === 76) {
editorActions.trigger('lassoTool');
return true;
}
// h -> activate hand tool
if (key === 72) {
editorActions.trigger('handTool');
return true;
}
// c -> activate global connect tool
if (key === 67) {
editorActions.trigger('globalConnectTool');
return true;
}
// e -> activate direct editing
if (key === 69) {
editorActions.trigger('directEditing');
return true;
}
});
}
BpmnKeyBindings.$inject = [
'keyboard',
'editorActions'
];
module.exports = BpmnKeyBindings;
},{}],22:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(172)
],
__init__: [ 'bpmnKeyBindings' ],
bpmnKeyBindings: [ 'type', _dereq_(21) ]
};
},{"172":172,"21":21}],23:[function(_dereq_,module,exports){
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
var getBusinessObject = _dereq_(95).getBusinessObject,
is = _dereq_(95).is;
var translate = _dereq_(279).translate;
var MARKER_HIDDEN = 'djs-element-hidden',
MARKER_LABEL_HIDDEN = 'djs-label-hidden';
function getStrokeColor(element, defaultColor) {
var bo = getBusinessObject(element);
return bo.di.get('stroke') || defaultColor || 'black';
}
function LabelEditingPreview(eventBus, canvas, elementRegistry, pathMap) {
var self = this;
var defaultLayer = canvas.getDefaultLayer();
var element, absoluteElementBBox, gfx;
eventBus.on('directEditing.activate', function(context) {
var activeProvider = context.active;
element = activeProvider.element.label || activeProvider.element;
// text annotation
if (is(element, 'bpmn:TextAnnotation')) {
absoluteElementBBox = canvas.getAbsoluteBBox(element);
gfx = svgCreate('g');
var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: element.height,
position: {
mx: 0.0,
my: 0.0
}
});
var path = self.path = svgCreate('path');
svgAttr(path, {
d: textPathData,
strokeWidth: 2,
stroke: getStrokeColor(element)
});
svgAppend(gfx, path);
svgAppend(defaultLayer, gfx);
translate(gfx, element.x, element.y);
}
if (is(element, 'bpmn:TextAnnotation') ||
element.labelTarget) {
canvas.addMarker(element, MARKER_HIDDEN);
} else if (is(element, 'bpmn:Task') ||
is(element, 'bpmn:CallActivity') ||
is(element, 'bpmn:SubProcess') ||
is(element, 'bpmn:Participant')) {
canvas.addMarker(element, MARKER_LABEL_HIDDEN);
}
});
eventBus.on('directEditing.resize', function(context) {
// text annotation
if (is(element, 'bpmn:TextAnnotation')) {
var height = context.height,
dy = context.dy;
var newElementHeight = Math.max(element.height / absoluteElementBBox.height * (height + dy), 0);
var textPathData = pathMap.getScaledPath('TEXT_ANNOTATION', {
xScaleFactor: 1,
yScaleFactor: 1,
containerWidth: element.width,
containerHeight: newElementHeight,
position: {
mx: 0.0,
my: 0.0
}
});
svgAttr(self.path, {
d: textPathData
});
}
});
eventBus.on([ 'directEditing.complete', 'directEditing.cancel' ], function(context) {
var activeProvider = context.active;
if (activeProvider) {
canvas.removeMarker(activeProvider.element.label || activeProvider.element, MARKER_HIDDEN);
canvas.removeMarker(element, MARKER_LABEL_HIDDEN);
}
element = undefined;
absoluteElementBBox = undefined;
if (gfx) {
svgRemove(gfx);
gfx = undefined;
}
});
}
LabelEditingPreview.$inject = [ 'eventBus', 'canvas', 'elementRegistry', 'pathMap' ];
module.exports = LabelEditingPreview;
},{"279":279,"472":472,"474":474,"478":478,"481":481,"95":95}],24:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
var UpdateLabelHandler = _dereq_(26);
var LabelUtil = _dereq_(25);
var is = _dereq_(95).is,
isExpanded = _dereq_(93).isExpanded;
var SMALL_FONT_SIZE = 11,
SMALL_LINE_HEIGHT = 13,
MEDIUM_FONT_SIZE = 12,
MEDIUM_LINE_HEIGHT = 14;
function LabelEditingProvider(eventBus, canvas, directEditing, commandStack, resizeHandles) {
this._canvas = canvas;
this._commandStack = commandStack;
directEditing.registerProvider(this);
commandStack.registerHandler('element.updateLabel', UpdateLabelHandler);
// listen to dblclick on non-root elements
eventBus.on('element.dblclick', function(event) {
directEditing.activate(event.element);
resizeHandles.removeResizers();
});
// complete on followup canvas operation
eventBus.on([ 'element.mousedown', 'drag.init', 'canvas.viewbox.changing' ], function(event) {
directEditing.complete();
});
// cancel on command stack changes
eventBus.on([ 'commandStack.changed' ], function(e) {
directEditing.cancel();
});
if ('ontouchstart' in document.documentElement) {
// we deactivate automatic label editing on mobile devices
// as it breaks the user interaction workflow
// TODO(nre): we should temporarily focus the edited element here
// and release the focused viewport after the direct edit operation is finished
} else {
eventBus.on('create.end', 500, function(event) {
var element = event.shape,
canExecute = event.context.canExecute;
if (!canExecute) {
return;
}
if (is(element, 'bpmn:Task') || is(element, 'bpmn:TextAnnotation') ||
(is(element, 'bpmn:SubProcess') && !isExpanded(element))) {
directEditing.activate(element);
resizeHandles.removeResizers();
}
});
}
}
LabelEditingProvider.$inject = [ 'eventBus', 'canvas', 'directEditing', 'commandStack', 'resizeHandles' ];
module.exports = LabelEditingProvider;
/**
* Activate direct editing for activities and text annotations.
*
* @param {djs.model.Base} element
*
* @return {Object} an object with properties bounds (position and size), text and options
*/
LabelEditingProvider.prototype.activate = function(element) {
// text
var text = LabelUtil.getLabel(element);
if (text === undefined) {
return;
}
var context = {
text: text
};
// bounds
var bounds = this.getEditingBBox(element);
assign(context, bounds);
// options
var target = element.label || element;
var options = {};
// tasks
if (is(element, 'bpmn:Task') ||
is(element, 'bpmn:Participant') ||
is(element, 'bpmn:Lane') ||
(is(element, 'bpmn:CallActivity') && !isExpanded(element)) ||
(is(element, 'bpmn:SubProcess') && !isExpanded(element))) {
assign(options, {
centerVertically: true
});
}
// external labels
if (target.labelTarget) {
assign(options, {
autoResize: true
});
}
// text annotations
if (is(element, 'bpmn:TextAnnotation')) {
assign(options, {
resizable: true,
autoResize: true
});
}
assign(context, {
options: options
});
return context;
};
/**
* Get the editing bounding box based on the element's size and position
*
* @param {djs.model.Base} element
*
* @return {Object} an object containing information about position and size (fixed or minimum and/or maximum)
*/
LabelEditingProvider.prototype.getEditingBBox = function(element) {
var canvas = this._canvas;
var target = element.label || element;
var bbox = canvas.getAbsoluteBBox(target);
var mid = {
x: bbox.x + bbox.width / 2,
y: bbox.y + bbox.height / 2
};
// default position
var bounds = { x: bbox.x, y: bbox.y };
var zoom = canvas.zoom();
// take zoom into account
var smallFontSize = SMALL_FONT_SIZE * zoom,
smallLineHeight = SMALL_LINE_HEIGHT * zoom,
mediumFontSize = MEDIUM_FONT_SIZE * zoom,
mediumLineHeight = MEDIUM_LINE_HEIGHT * zoom;
var style = {};
// adjust for expanded pools AND lanes
if ((is(element, 'bpmn:Participant') && isExpanded(element))
|| is(element, 'bpmn:Lane')) {
assign(bounds, {
width: bbox.height,
height: 30 * zoom,
x: bbox.x - bbox.height / 2 + (15 * zoom),
y: mid.y - (30 * zoom) / 2
});
assign(style, {
fontSize: mediumFontSize + 'px',
lineHeight: mediumLineHeight + 'px',
paddingTop: (7 * zoom) + 'px',
paddingBottom: (7 * zoom) + 'px',
paddingLeft: (5 * zoom) + 'px',
paddingRight: (5 * zoom) + 'px',
transform: 'rotate(-90deg)'
});
}
// internal labels for tasks and collapsed call activities, sub processes and participants
if (
is(element, 'bpmn:Task') ||
(is(element, 'bpmn:CallActivity') && !isExpanded(element)) ||
(is(element, 'bpmn:SubProcess') && !isExpanded(element)) ||
(is(element, 'bpmn:Participant') && !isExpanded(element))
) {
assign(bounds, {
width: bbox.width,
height: bbox.height
});
assign(style, {
fontSize: mediumFontSize + 'px',
lineHeight: mediumLineHeight + 'px',
paddingTop: (7 * zoom) + 'px',
paddingBottom: (7 * zoom) + 'px',
paddingLeft: (5 * zoom) + 'px',
paddingRight: (5 * zoom) + 'px'
});
}
// internal labels for expanded sub processes
if (is(element, 'bpmn:SubProcess') && isExpanded(element)) {
assign(bounds, {
width: bbox.width,
x: bbox.x
});
assign(style, {
fontSize: mediumFontSize + 'px',
lineHeight: mediumLineHeight + 'px',
paddingTop: (7 * zoom) + 'px',
paddingBottom: (7 * zoom) + 'px',
paddingLeft: (5 * zoom) + 'px',
paddingRight: (5 * zoom) + 'px'
});
}
// external labels for events, data elements, gateways and connections
if (target.labelTarget) {
var width = 90 * zoom,
paddingTop = 7 * zoom,
paddingBottom = 4 * zoom;
assign(bounds, {
width: width,
height: bbox.height + paddingTop + paddingBottom,
x: mid.x - width / 2,
y: bbox.y - paddingTop
});
assign(style, {
fontSize: smallFontSize + 'px',
lineHeight: smallLineHeight + 'px',
paddingTop: paddingTop + 'px',
paddingBottom: paddingBottom + 'px'
});
}
// text annotations
if (is(element, 'bpmn:TextAnnotation')) {
assign(bounds, {
width: bbox.width,
height: bbox.height,
minWidth: 30 * zoom,
minHeight: 10 * zoom
});
assign(style, {
textAlign: 'left',
paddingTop: (7 * zoom) + 'px',
paddingBottom: (7 * zoom) + 'px',
paddingLeft: (5 * zoom) + 'px',
paddingRight: (5 * zoom) + 'px',
fontSize: mediumFontSize + 'px',
lineHeight: mediumLineHeight + 'px'
});
}
return { bounds: bounds, style: style };
};
LabelEditingProvider.prototype.update = function(element, newLabel, activeContextText, bounds) {
var absoluteElementBBox = this._canvas.getAbsoluteBBox(element);
this._commandStack.execute('element.updateLabel', {
element: element,
newLabel: newLabel,
bounds: {
x: element.x,
y: element.y,
width: element.width / absoluteElementBBox.width * bounds.width,
height: element.height / absoluteElementBBox.height * bounds.height
}
});
};
},{"25":25,"26":26,"431":431,"93":93,"95":95}],25:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
function getLabelAttr(semantic) {
if (is(semantic, 'bpmn:FlowElement') ||
is(semantic, 'bpmn:Participant') ||
is(semantic, 'bpmn:Lane') ||
is(semantic, 'bpmn:SequenceFlow') ||
is(semantic, 'bpmn:MessageFlow')) {
return 'name';
}
if (is(semantic, 'bpmn:TextAnnotation')) {
return 'text';
}
}
module.exports.getLabel = function(element) {
var semantic = element.businessObject,
attr = getLabelAttr(semantic);
if (attr) {
return semantic[attr] || '';
}
};
module.exports.setLabel = function(element, text, isExternal) {
var semantic = element.businessObject,
attr = getLabelAttr(semantic);
if (attr) {
semantic[attr] = text;
}
// show external label if not empty
if (isExternal) {
element.hidden = !text;
}
return element;
};
},{"95":95}],26:[function(_dereq_,module,exports){
'use strict';
var LabelUtil = _dereq_(25);
var TextUtil = _dereq_(280);
var hasExternalLabel = _dereq_(94).hasExternalLabel;
var getBusinessObject = _dereq_(95).getBusinessObject,
is = _dereq_(95).is;
var NULL_DIMENSIONS = {
width: 0,
height: 0
};
/**
* A handler that updates the text of a BPMN element.
*/
function UpdateLabelHandler(modeling) {
var textUtil = new TextUtil();
/**
* Set the label and return the changed elements.
*
* Element parameter can be label itself or connection (i.e. sequence flow).
*
* @param {djs.model.Base} element
* @param {String} text
*/
function setText(element, text) {
// external label if present
var label = element.label || element;
var labelTarget = element.labelTarget || element;
LabelUtil.setLabel(label, text, labelTarget !== label);
return [ label, labelTarget ];
}
function execute(ctx) {
ctx.oldLabel = LabelUtil.getLabel(ctx.element);
return setText(ctx.element, ctx.newLabel);
}
function revert(ctx) {
return setText(ctx.element, ctx.oldLabel);
}
function postExecute(ctx) {
var element = ctx.element,
label = element.label || element,
bounds = ctx.bounds;
// ignore internal labels for elements except text annotations
if (!hasExternalLabel(element) && !is(element, 'bpmn:TextAnnotation')) {
return;
}
var bo = getBusinessObject(label);
var text = bo.name || bo.text;
if (!text) {
return;
}
// get layouted text bounds and resize external
// external label accordingly
var newBounds = is(element, 'bpmn:TextAnnotation') ? bounds : getLayoutedBounds(label, text, textUtil);
modeling.resizeShape(label, newBounds, NULL_DIMENSIONS);
}
// API
this.execute = execute;
this.revert = revert;
this.postExecute = postExecute;
}
UpdateLabelHandler.$inject = [ 'modeling' ];
module.exports = UpdateLabelHandler;
// TODO(nikku): repeating code (search for )
var EXTERNAL_LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: '11px'
};
function getLayoutedBounds(bounds, text, textUtil) {
var layoutedLabelDimensions = textUtil.getDimensions(text, {
box: {
width: 90,
height: 30,
x: bounds.width / 2 + bounds.x,
y: bounds.height / 2 + bounds.y
},
style: EXTERNAL_LABEL_STYLE
});
// resize label shape to fit label text
return {
x: Math.round(bounds.x + bounds.width / 2 - layoutedLabelDimensions.width / 2),
y: Math.round(bounds.y),
width: Math.ceil(layoutedLabelDimensions.width),
height: Math.ceil(layoutedLabelDimensions.height)
};
}
},{"25":25,"280":280,"94":94,"95":95}],27:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(121),
_dereq_(147),
_dereq_(224),
_dereq_(114)
],
__init__: [
'labelEditingProvider',
'labelEditingPreview'
],
labelEditingProvider: [ 'type', _dereq_(24) ],
labelEditingPreview: [ 'type', _dereq_(23) ]
};
},{"114":114,"121":121,"147":147,"224":224,"23":23,"24":24}],28:[function(_dereq_,module,exports){
'use strict';
var map = _dereq_(305),
assign = _dereq_(431),
pick = _dereq_(438);
function BpmnFactory(moddle) {
this._model = moddle;
}
BpmnFactory.$inject = [ 'moddle' ];
BpmnFactory.prototype._needsId = function(element) {
return element.$instanceOf('bpmn:RootElement') ||
element.$instanceOf('bpmn:FlowElement') ||
element.$instanceOf('bpmn:MessageFlow') ||
element.$instanceOf('bpmn:DataAssociation') ||
element.$instanceOf('bpmn:Artifact') ||
element.$instanceOf('bpmn:Participant') ||
element.$instanceOf('bpmn:Lane') ||
element.$instanceOf('bpmn:Process') ||
element.$instanceOf('bpmn:Collaboration') ||
element.$instanceOf('bpmndi:BPMNShape') ||
element.$instanceOf('bpmndi:BPMNEdge') ||
element.$instanceOf('bpmndi:BPMNDiagram') ||
element.$instanceOf('bpmndi:BPMNPlane') ||
element.$instanceOf('bpmn:Property');
};
BpmnFactory.prototype._ensureId = function(element) {
// generate semantic ids for elements
// bpmn:SequenceFlow -> SequenceFlow_ID
var prefix = (element.$type || '').replace(/^[^:]*:/g, '') + '_';
if (!element.id && this._needsId(element)) {
element.id = this._model.ids.nextPrefixed(prefix, element);
}
};
BpmnFactory.prototype.create = function(type, attrs) {
var element = this._model.create(type, attrs || {});
this._ensureId(element);
return element;
};
BpmnFactory.prototype.createDiLabel = function() {
return this.create('bpmndi:BPMNLabel', {
bounds: this.createDiBounds()
});
};
BpmnFactory.prototype.createDiShape = function(semantic, bounds, attrs) {
return this.create('bpmndi:BPMNShape', assign({
bpmnElement: semantic,
bounds: this.createDiBounds(bounds)
}, attrs));
};
BpmnFactory.prototype.createDiBounds = function(bounds) {
return this.create('dc:Bounds', bounds);
};
BpmnFactory.prototype.createDiWaypoints = function(waypoints) {
return map(waypoints, function(pos) {
return this.createDiWaypoint(pos);
}, this);
};
BpmnFactory.prototype.createDiWaypoint = function(point) {
return this.create('dc:Point', pick(point, [ 'x', 'y' ]));
};
BpmnFactory.prototype.createDiEdge = function(semantic, waypoints, attrs) {
return this.create('bpmndi:BPMNEdge', assign({
bpmnElement: semantic
}, attrs));
};
BpmnFactory.prototype.createDiPlane = function(semantic) {
return this.create('bpmndi:BPMNPlane', {
bpmnElement: semantic
});
};
module.exports = BpmnFactory;
},{"305":305,"431":431,"438":438}],29:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var assign = _dereq_(431);
var BaseLayouter = _dereq_(250),
ManhattanLayout = _dereq_(253);
var LayoutUtil = _dereq_(252);
var isExpanded = _dereq_(93).isExpanded;
var getMid = LayoutUtil.getMid,
getOrientation = LayoutUtil.getOrientation;
var is = _dereq_(95).is;
function BpmnLayouter() {}
inherits(BpmnLayouter, BaseLayouter);
module.exports = BpmnLayouter;
BpmnLayouter.prototype.layoutConnection = function(connection, hints) {
hints = hints || {};
var source = connection.source,
target = connection.target,
waypoints = connection.waypoints,
start = hints.connectionStart,
end = hints.connectionEnd;
var manhattanOptions,
updatedWaypoints;
if (!start) {
start = getConnectionDocking(waypoints && waypoints[0], source);
}
if (!end) {
end = getConnectionDocking(waypoints && waypoints[waypoints.length - 1], target);
}
// TODO(nikku): support vertical modeling
// and invert preferredLayouts accordingly
if (is(connection, 'bpmn:Association') ||
is(connection, 'bpmn:DataAssociation')) {
if (waypoints && !isCompensationAssociation(connection)) {
return [].concat([ start ], waypoints.slice(1, -1), [ end ]);
}
}
// manhattan layout sequence / message flows
if (is(connection, 'bpmn:MessageFlow')) {
manhattanOptions = {
preferredLayouts: [ 'v:v' ]
};
if (is(target, 'bpmn:Participant')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ]
};
}
if (isExpandedSubProcess(target)) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ]
};
}
if (isExpandedSubProcess(source) && is(target, 'bpmn:FlowNode')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ],
preserveDocking: isExpandedSubProcess(target) ? 'source' : 'target'
};
}
if (is(source, 'bpmn:Participant') && is(target, 'bpmn:FlowNode')) {
manhattanOptions = {
preferredLayouts: [ 'straight', 'v:v' ],
preserveDocking: 'target'
};
}
if (is(target, 'bpmn:Event')) {
manhattanOptions = {
preferredLayouts: [ 'v:v' ]
};
}
} else
// layout all connection between flow elements h:h,
//
// except for
//
// (1) outgoing of BoundaryEvents -> layout h:v or v:h based on attach orientation
// (2) incoming / outgoing of Gateway -> v:h (outgoing), h:v (incoming)
//
if (is(connection, 'bpmn:SequenceFlow') ||
isCompensationAssociation(connection)) {
// make sure boundary event connections do
// not look ugly =:>
if (is(source, 'bpmn:BoundaryEvent')) {
var orientation = getAttachOrientation(source);
if (/left|right/.test(orientation)) {
manhattanOptions = {
preferredLayouts: [ 'h:v' ]
};
} else
if (/top|bottom/.test(orientation)) {
manhattanOptions = {
preferredLayouts: [ 'v:h' ]
};
}
} else
if (is(source, 'bpmn:Gateway')) {
manhattanOptions = {
preferredLayouts: [ 'v:h' ]
};
} else
if (is(target, 'bpmn:Gateway')) {
manhattanOptions = {
preferredLayouts: [ 'h:v' ]
};
}
// apply horizontal love <3
else {
manhattanOptions = {
preferredLayouts: [ 'h:h' ]
};
}
}
if (manhattanOptions) {
manhattanOptions = assign(manhattanOptions, hints);
updatedWaypoints =
ManhattanLayout.repairConnection(
source, target,
start, end,
waypoints,
manhattanOptions);
}
return updatedWaypoints || [ start, end ];
};
function getAttachOrientation(attachedElement) {
var hostElement = attachedElement.host,
padding = -10;
return getOrientation(getMid(attachedElement), hostElement, padding);
}
function getConnectionDocking(point, shape) {
return point ? (point.original || point) : getMid(shape);
}
function isCompensationAssociation(connection) {
var source = connection.source,
target = connection.target;
return is(target, 'bpmn:Activity') &&
is(source, 'bpmn:BoundaryEvent') &&
target.businessObject.isForCompensation;
}
function isExpandedSubProcess(element) {
return is(element, 'bpmn:SubProcess') && isExpanded(element);
}
},{"250":250,"252":252,"253":253,"289":289,"431":431,"93":93,"95":95}],30:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303),
inherits = _dereq_(289);
var Collections = _dereq_(263),
Model = _dereq_(254);
var getBusinessObject = _dereq_(95).getBusinessObject,
is = _dereq_(95).is;
var CommandInterceptor = _dereq_(119);
/**
* A handler responsible for updating the underlying BPMN 2.0 XML + DI
* once changes on the diagram happen
*/
function BpmnUpdater(eventBus, bpmnFactory, connectionDocking, translate) {
CommandInterceptor.call(this, eventBus);
this._bpmnFactory = bpmnFactory;
this._translate = translate;
var self = this;
////// connection cropping /////////////////////////
// crop connection ends during create/update
function cropConnection(e) {
var context = e.context,
connection;
if (!context.cropped) {
connection = context.connection;
connection.waypoints = connectionDocking.getCroppedWaypoints(connection);
context.cropped = true;
}
}
this.executed([
'connection.layout',
'connection.create',
'connection.reconnectEnd',
'connection.reconnectStart'
], cropConnection);
this.reverted([ 'connection.layout' ], function(e) {
delete e.context.cropped;
});
////// BPMN + DI update /////////////////////////
// update parent
function updateParent(e) {
var context = e.context;
self.updateParent(context.shape || context.connection, context.oldParent);
}
function reverseUpdateParent(e) {
var context = e.context;
var element = context.shape || context.connection,
// oldParent is the (old) new parent, because we are undoing
oldParent = context.parent || context.newParent;
self.updateParent(element, oldParent);
}
this.executed([
'shape.move',
'shape.create',
'shape.delete',
'connection.create',
'connection.move',
'connection.delete'
], ifBpmn(updateParent));
this.reverted([
'shape.move',
'shape.create',
'shape.delete',
'connection.create',
'connection.move',
'connection.delete'
], ifBpmn(reverseUpdateParent));
/*
* ## Updating Parent
*
* When morphing a Process into a Collaboration or vice-versa,
* make sure that both the *semantic* and *di* parent of each element
* is updated.
*
*/
function updateRoot(event) {
var context = event.context,
oldRoot = context.oldRoot,
children = oldRoot.children;
forEach(children, function(child) {
if (is(child, 'bpmn:BaseElement')) {
self.updateParent(child);
}
});
}
this.executed([ 'canvas.updateRoot' ], updateRoot);
this.reverted([ 'canvas.updateRoot' ], updateRoot);
// update bounds
function updateBounds(e) {
var shape = e.context.shape;
if (!is(shape, 'bpmn:BaseElement')) {
return;
}
self.updateBounds(shape);
}
this.executed([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
// exclude labels because they're handled separately during shape.changed
if (event.context.shape.type === 'label') {
return;
}
updateBounds(event);
}));
this.reverted([ 'shape.move', 'shape.create', 'shape.resize' ], ifBpmn(function(event) {
// exclude labels because they're handled separately during shape.changed
if (event.context.shape.type === 'label') {
return;
}
updateBounds(event);
}));
// Handle labels separately. This is necessary, because the label bounds have to be updated
// every time its shape changes, not only on move, create and resize.
eventBus.on('shape.changed', function(event) {
if (event.element.type === 'label') {
updateBounds({ context: { shape: event.element } });
}
});
// attach / detach connection
function updateConnection(e) {
self.updateConnection(e.context);
}
this.executed([
'connection.create',
'connection.move',
'connection.delete',
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(updateConnection));
this.reverted([
'connection.create',
'connection.move',
'connection.delete',
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(updateConnection));
// update waypoints
function updateConnectionWaypoints(e) {
self.updateConnectionWaypoints(e.context.connection);
}
this.executed([
'connection.layout',
'connection.move',
'connection.updateWaypoints',
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(updateConnectionWaypoints));
this.reverted([
'connection.layout',
'connection.move',
'connection.updateWaypoints',
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(updateConnectionWaypoints));
// update Default & Conditional flows
this.executed([
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(function(e) {
var context = e.context,
connection = context.connection,
businessObject = getBusinessObject(connection),
oldSource = getBusinessObject(context.oldSource),
oldTarget = getBusinessObject(context.oldTarget),
newSource = getBusinessObject(connection.source),
newTarget = getBusinessObject(connection.target);
if (oldSource === newSource || oldTarget === newTarget) {
return;
}
// on reconnectStart -> default flow
if (oldSource && oldSource.default === businessObject) {
context.default = oldSource.default;
oldSource.default = undefined;
}
// on reconnectEnd -> default flow
if ((businessObject.sourceRef && businessObject.sourceRef.default) &&
!(is(newTarget, 'bpmn:Activity') ||
is(newTarget, 'bpmn:EndEvent') ||
is(newTarget, 'bpmn:Gateway') ||
is(newTarget, 'bpmn:IntermediateThrowEvent')) ) {
context.default = businessObject.sourceRef.default;
businessObject.sourceRef.default = undefined;
}
// on reconnectStart -> conditional flow
if (oldSource && (businessObject.conditionExpression) &&
!(is(newSource, 'bpmn:Activity') ||
is(newSource, 'bpmn:Gateway')) ) {
context.conditionExpression = businessObject.conditionExpression;
businessObject.conditionExpression = undefined;
}
// on reconnectEnd -> conditional flow
if (oldTarget && (businessObject.conditionExpression) &&
!(is(newTarget, 'bpmn:Activity') ||
is(newTarget, 'bpmn:EndEvent') ||
is(newTarget, 'bpmn:Gateway') ||
is(newTarget, 'bpmn:IntermediateThrowEvent')) ) {
context.conditionExpression = businessObject.conditionExpression;
businessObject.conditionExpression = undefined;
}
}));
this.reverted([
'connection.reconnectEnd',
'connection.reconnectStart'
], ifBpmn(function(e) {
var context = e.context,
connection = context.connection,
businessObject = getBusinessObject(connection),
newSource = getBusinessObject(connection.source);
// default flow
if (context.default) {
if (is(newSource, 'bpmn:ExclusiveGateway') || is(newSource, 'bpmn:InclusiveGateway') ||
is(newSource, 'bpmn:Activity')) {
newSource.default = context.default;
}
}
// conditional flow
if (context.conditionExpression && is(newSource, 'bpmn:Activity')) {
businessObject.conditionExpression = context.conditionExpression;
}
}));
// update attachments
function updateAttachment(e) {
self.updateAttachment(e.context);
}
this.executed([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
this.reverted([ 'element.updateAttachment' ], ifBpmn(updateAttachment));
}
inherits(BpmnUpdater, CommandInterceptor);
module.exports = BpmnUpdater;
BpmnUpdater.$inject = [ 'eventBus', 'bpmnFactory', 'connectionDocking', 'translate' ];
/////// implementation //////////////////////////////////
BpmnUpdater.prototype.updateAttachment = function(context) {
var shape = context.shape,
businessObject = shape.businessObject,
host = shape.host;
businessObject.attachedToRef = host && host.businessObject;
};
BpmnUpdater.prototype.updateParent = function(element, oldParent) {
// do not update BPMN 2.0 label parent
if (element instanceof Model.Label) {
return;
}
var parentShape = element.parent;
var businessObject = element.businessObject,
parentBusinessObject = parentShape && parentShape.businessObject,
parentDi = parentBusinessObject && parentBusinessObject.di;
if (is(element, 'bpmn:FlowNode')) {
this.updateFlowNodeRefs(businessObject, parentBusinessObject, oldParent && oldParent.businessObject);
}
if (is(element, 'bpmn:DataOutputAssociation')) {
if (element.source) {
parentBusinessObject = element.source.businessObject;
} else {
parentBusinessObject = null;
}
}
if (is(element, 'bpmn:DataInputAssociation')) {
if (element.target) {
parentBusinessObject = element.target.businessObject;
} else {
parentBusinessObject = null;
}
}
this.updateSemanticParent(businessObject, parentBusinessObject);
if (is(element, 'bpmn:DataObjectReference') && businessObject.dataObjectRef) {
this.updateSemanticParent(businessObject.dataObjectRef, parentBusinessObject);
}
this.updateDiParent(businessObject.di, parentDi);
};
BpmnUpdater.prototype.updateBounds = function(shape) {
var di = shape.businessObject.di;
var bounds = (shape instanceof Model.Label) ? this._getLabel(di).bounds : di.bounds;
assign(bounds, {
x: shape.x,
y: shape.y,
width: shape.width,
height: shape.height
});
};
BpmnUpdater.prototype.updateFlowNodeRefs = function(businessObject, newContainment, oldContainment) {
if (oldContainment === newContainment) {
return;
}
var oldRefs, newRefs;
if (is (oldContainment, 'bpmn:Lane')) {
oldRefs = oldContainment.get('flowNodeRef');
Collections.remove(oldRefs, businessObject);
}
if (is(newContainment, 'bpmn:Lane')) {
newRefs = newContainment.get('flowNodeRef');
Collections.add(newRefs, businessObject);
}
};
// update existing sourceElement and targetElement di information
BpmnUpdater.prototype.updateDiConnection = function(di, newSource, newTarget) {
if (di.sourceElement && di.sourceElement.bpmnElement !== newSource) {
di.sourceElement = newSource && newSource.di;
}
if (di.targetElement && di.targetElement.bpmnElement !== newTarget) {
di.targetElement = newTarget && newTarget.di;
}
};
BpmnUpdater.prototype.updateDiParent = function(di, parentDi) {
if (parentDi && !is(parentDi, 'bpmndi:BPMNPlane')) {
parentDi = parentDi.$parent;
}
if (di.$parent === parentDi) {
return;
}
var planeElements = (parentDi || di.$parent).get('planeElement');
if (parentDi) {
planeElements.push(di);
di.$parent = parentDi;
} else {
Collections.remove(planeElements, di);
di.$parent = null;
}
};
function getDefinitions(element) {
while (element && !is(element, 'bpmn:Definitions')) {
element = element.$parent;
}
return element;
}
BpmnUpdater.prototype.getLaneSet = function(container) {
var laneSet, laneSets;
// bpmn:Lane
if (is(container, 'bpmn:Lane')) {
laneSet = container.childLaneSet;
if (!laneSet) {
laneSet = this._bpmnFactory.create('bpmn:LaneSet');
container.childLaneSet = laneSet;
laneSet.$parent = container;
}
return laneSet;
}
// bpmn:Participant
if (is(container, 'bpmn:Participant')) {
container = container.processRef;
}
// bpmn:FlowElementsContainer
laneSets = container.get('laneSets');
laneSet = laneSets[0];
if (!laneSet) {
laneSet = this._bpmnFactory.create('bpmn:LaneSet');
laneSet.$parent = container;
laneSets.push(laneSet);
}
return laneSet;
};
BpmnUpdater.prototype.updateSemanticParent = function(businessObject, newParent, visualParent) {
var containment,
translate = this._translate;
if (businessObject.$parent === newParent) {
return;
}
if (is(businessObject, 'bpmn:Lane')) {
if (newParent) {
newParent = this.getLaneSet(newParent);
}
containment = 'lanes';
} else
if (is(businessObject, 'bpmn:FlowElement')) {
if (newParent) {
if (is(newParent, 'bpmn:Participant')) {
newParent = newParent.processRef;
} else
if (is(newParent, 'bpmn:Lane')) {
do {
// unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
newParent = newParent.$parent.$parent;
} while (is(newParent, 'bpmn:Lane'));
}
}
containment = 'flowElements';
} else
if (is(businessObject, 'bpmn:Artifact')) {
while (newParent &&
!is(newParent, 'bpmn:Process') &&
!is(newParent, 'bpmn:SubProcess') &&
!is(newParent, 'bpmn:Collaboration')) {
if (is(newParent, 'bpmn:Participant')) {
newParent = newParent.processRef;
break;
} else {
newParent = newParent.$parent;
}
}
containment = 'artifacts';
} else
if (is(businessObject, 'bpmn:MessageFlow')) {
containment = 'messageFlows';
} else
if (is(businessObject, 'bpmn:Participant')) {
containment = 'participants';
// make sure the participants process is properly attached / detached
// from the XML document
var process = businessObject.processRef,
definitions;
if (process) {
definitions = getDefinitions(businessObject.$parent || newParent);
if (businessObject.$parent) {
Collections.remove(definitions.get('rootElements'), process);
process.$parent = null;
}
if (newParent) {
Collections.add(definitions.get('rootElements'), process);
process.$parent = definitions;
}
}
} else
if (is(businessObject, 'bpmn:DataOutputAssociation')) {
containment = 'dataOutputAssociations';
} else
if (is(businessObject, 'bpmn:DataInputAssociation')) {
containment = 'dataInputAssociations';
}
if (!containment) {
throw new Error(translate(
'no parent for {element} in {parent}',
{
element: businessObject.id,
parent: newParent.id
}
));
}
var children;
if (businessObject.$parent) {
// remove from old parent
children = businessObject.$parent.get(containment);
Collections.remove(children, businessObject);
}
if (!newParent) {
businessObject.$parent = null;
} else {
// add to new parent
children = newParent.get(containment);
children.push(businessObject);
businessObject.$parent = newParent;
}
if (visualParent) {
var diChildren = visualParent.get(containment);
Collections.remove(children, businessObject);
if (newParent) {
if (!diChildren) {
diChildren = [];
newParent.set(containment, diChildren);
}
diChildren.push(businessObject);
}
}
};
BpmnUpdater.prototype.updateConnectionWaypoints = function(connection) {
connection.businessObject.di.set('waypoint', this._bpmnFactory.createDiWaypoints(connection.waypoints));
};
BpmnUpdater.prototype.updateConnection = function(context) {
var connection = context.connection,
businessObject = getBusinessObject(connection),
newSource = getBusinessObject(connection.source),
newTarget = getBusinessObject(connection.target),
visualParent;
if (!is(businessObject, 'bpmn:DataAssociation')) {
var inverseSet = is(businessObject, 'bpmn:SequenceFlow');
if (businessObject.sourceRef !== newSource) {
if (inverseSet) {
Collections.remove(businessObject.sourceRef && businessObject.sourceRef.get('outgoing'), businessObject);
if (newSource && newSource.get('outgoing')) {
newSource.get('outgoing').push(businessObject);
}
}
businessObject.sourceRef = newSource;
}
if (businessObject.targetRef !== newTarget) {
if (inverseSet) {
Collections.remove(businessObject.targetRef && businessObject.targetRef.get('incoming'), businessObject);
if (newTarget && newTarget.get('incoming')) {
newTarget.get('incoming').push(businessObject);
}
}
businessObject.targetRef = newTarget;
}
} else
if (is(businessObject, 'bpmn:DataInputAssociation')) {
// handle obnoxious isMany sourceRef
businessObject.get('sourceRef')[0] = newSource;
visualParent = context.parent || context.newParent || newTarget;
this.updateSemanticParent(businessObject, newTarget, parent.businessObject);
} else
if (is(businessObject, 'bpmn:DataOutputAssociation')) {
visualParent = context.parent || context.newParent || newSource;
this.updateSemanticParent(businessObject, newSource, visualParent);
// targetRef = new target
businessObject.targetRef = newTarget;
}
this.updateConnectionWaypoints(connection);
this.updateDiConnection(businessObject.di, newSource, newTarget);
};
/////// helpers /////////////////////////////////////////
BpmnUpdater.prototype._getLabel = function(di) {
if (!di.label) {
di.label = this._bpmnFactory.createDiLabel();
}
return di.label;
};
/**
* Make sure the event listener is only called
* if the touched element is a BPMN element.
*
* @param {Function} fn
* @return {Function} guarded function
*/
function ifBpmn(fn) {
return function(event) {
var context = event.context,
element = context.shape || context.connection;
if (is(element, 'bpmn:BaseElement')) {
fn(event);
}
};
}
},{"119":119,"254":254,"263":263,"289":289,"303":303,"431":431,"95":95}],31:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303),
inherits = _dereq_(289);
var is = _dereq_(95).is;
var isExpanded = _dereq_(93).isExpanded;
var BaseElementFactory = _dereq_(123),
LabelUtil = _dereq_(94);
/**
* A bpmn-aware factory for diagram-js shapes
*/
function ElementFactory(bpmnFactory, moddle, translate) {
BaseElementFactory.call(this);
this._bpmnFactory = bpmnFactory;
this._moddle = moddle;
this._translate = translate;
}
inherits(ElementFactory, BaseElementFactory);
ElementFactory.$inject = [ 'bpmnFactory', 'moddle', 'translate' ];
module.exports = ElementFactory;
ElementFactory.prototype.baseCreate = BaseElementFactory.prototype.create;
ElementFactory.prototype.create = function(elementType, attrs) {
// no special magic for labels,
// we assume their businessObjects have already been created
// and wired via attrs
if (elementType === 'label') {
return this.baseCreate(elementType, assign({ type: 'label' }, LabelUtil.DEFAULT_LABEL_SIZE, attrs));
}
return this.createBpmnElement(elementType, attrs);
};
ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
var size,
translate = this._translate;
attrs = attrs || {};
var businessObject = attrs.businessObject;
if (!businessObject) {
if (!attrs.type) {
throw new Error(translate('no shape type specified'));
}
businessObject = this._bpmnFactory.create(attrs.type);
}
if (!businessObject.di) {
if (elementType === 'root') {
businessObject.di = this._bpmnFactory.createDiPlane(businessObject, [], {
id: businessObject.id + '_di'
});
} else
if (elementType === 'connection') {
businessObject.di = this._bpmnFactory.createDiEdge(businessObject, [], {
id: businessObject.id + '_di'
});
} else {
businessObject.di = this._bpmnFactory.createDiShape(businessObject, {}, {
id: businessObject.id + '_di'
});
}
}
if (attrs.colors) {
assign(businessObject.di, attrs.colors);
delete attrs.colors;
}
applyAttributes(businessObject, attrs, [
'processRef',
'isInterrupting',
'associationDirection',
'isForCompensation'
]);
if (attrs.isExpanded) {
applyAttribute(businessObject.di, attrs, 'isExpanded');
}
if (is(businessObject, 'bpmn:ExclusiveGateway')) {
businessObject.di.isMarkerVisible = true;
}
var eventDefinitions,
newEventDefinition;
if (attrs.eventDefinitionType) {
eventDefinitions = businessObject.get('eventDefinitions') || [];
newEventDefinition = this._moddle.create(attrs.eventDefinitionType);
eventDefinitions.push(newEventDefinition);
newEventDefinition.$parent = businessObject;
businessObject.eventDefinitions = eventDefinitions;
delete attrs.eventDefinitionType;
}
size = this._getDefaultSize(businessObject);
attrs = assign({
businessObject: businessObject,
id: businessObject.id
}, size, attrs);
return this.baseCreate(elementType, attrs);
};
ElementFactory.prototype._getDefaultSize = function(semantic) {
if (is(semantic, 'bpmn:SubProcess')) {
if (isExpanded(semantic)) {
return { width: 350, height: 200 };
} else {
return { width: 100, height: 80 };
}
}
if (is(semantic, 'bpmn:Task')) {
return { width: 100, height: 80 };
}
if (is(semantic, 'bpmn:Gateway')) {
return { width: 50, height: 50 };
}
if (is(semantic, 'bpmn:Event')) {
return { width: 36, height: 36 };
}
if (is(semantic, 'bpmn:Participant')) {
if (!isExpanded(semantic)) {
return { width: 400, height: 100 };
} else {
return { width: 600, height: 250 };
}
}
if (is(semantic, 'bpmn:Lane')) {
return { width: 400, height: 100 };
}
if (is(semantic, 'bpmn:DataObjectReference')) {
return { width: 36, height: 50 };
}
if (is(semantic, 'bpmn:DataStoreReference')) {
return { width: 50, height: 50 };
}
if (is(semantic, 'bpmn:TextAnnotation')) {
return { width: 100, height: 30 };
}
return { width: 100, height: 80 };
};
ElementFactory.prototype.createParticipantShape = function(collapsed) {
var attrs = { type: 'bpmn:Participant' };
if (!collapsed) {
attrs.processRef = this._bpmnFactory.create('bpmn:Process');
}
return this.createShape(attrs);
};
//////////// helpers ////////////////////////////////////
/**
* Apply attributes from a map to the given element,
* remove attribute from the map on application.
*
* @param {Base} element
* @param {Object} attrs (in/out map of attributes)
* @param {Array} attributeNames name of attributes to apply
*/
function applyAttributes(element, attrs, attributeNames) {
forEach(attributeNames, function(property) {
if (attrs[property] !== undefined) {
applyAttribute(element, attrs, property);
}
});
}
/**
* Apply named property to element and drain it from the attrs
* collection.
*
* @param {Base} element
* @param {Object} attrs (in/out map of attributes)
* @param {String} attributeName to apply
*/
function applyAttribute(element, attrs, attributeName) {
element[attributeName] = attrs[attributeName];
delete attrs[attributeName];
}
},{"123":123,"289":289,"303":303,"431":431,"93":93,"94":94,"95":95}],32:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var BaseModeling = _dereq_(177);
var UpdatePropertiesHandler = _dereq_(65),
UpdateCanvasRootHandler = _dereq_(63),
AddLaneHandler = _dereq_(58),
SplitLaneHandler = _dereq_(62),
ResizeLaneHandler = _dereq_(60),
UpdateFlowNodeRefsHandler = _dereq_(64),
IdClaimHandler = _dereq_(59),
SetColorHandler = _dereq_(61);
/**
* BPMN 2.0 modeling features activator
*
* @param {EventBus} eventBus
* @param {ElementFactory} elementFactory
* @param {CommandStack} commandStack
* @param {BpmnRules} bpmnRules
*/
function Modeling(eventBus, elementFactory, commandStack, bpmnRules) {
BaseModeling.call(this, eventBus, elementFactory, commandStack);
this._bpmnRules = bpmnRules;
}
inherits(Modeling, BaseModeling);
Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack', 'bpmnRules' ];
module.exports = Modeling;
Modeling.prototype.getHandlers = function() {
var handlers = BaseModeling.prototype.getHandlers.call(this);
handlers['element.updateProperties'] = UpdatePropertiesHandler;
handlers['canvas.updateRoot'] = UpdateCanvasRootHandler;
handlers['lane.add'] = AddLaneHandler;
handlers['lane.resize'] = ResizeLaneHandler;
handlers['lane.split'] = SplitLaneHandler;
handlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler;
handlers['id.updateClaim'] = IdClaimHandler;
handlers['element.setColor'] = SetColorHandler;
return handlers;
};
Modeling.prototype.updateLabel = function(element, newLabel) {
this._commandStack.execute('element.updateLabel', {
element: element,
newLabel: newLabel
});
};
Modeling.prototype.connect = function(source, target, attrs, hints) {
var bpmnRules = this._bpmnRules;
if (!attrs) {
attrs = bpmnRules.canConnect(source, target);
}
if (!attrs) {
return;
}
return this.createConnection(source, target, attrs, source.parent, hints);
};
Modeling.prototype.updateProperties = function(element, properties) {
this._commandStack.execute('element.updateProperties', {
element: element,
properties: properties
});
};
Modeling.prototype.resizeLane = function(laneShape, newBounds, balanced) {
this._commandStack.execute('lane.resize', {
shape: laneShape,
newBounds: newBounds,
balanced: balanced
});
};
Modeling.prototype.addLane = function(targetLaneShape, location) {
var context = {
shape: targetLaneShape,
location: location
};
this._commandStack.execute('lane.add', context);
return context.newLane;
};
Modeling.prototype.splitLane = function(targetLane, count) {
this._commandStack.execute('lane.split', {
shape: targetLane,
count: count
});
};
/**
* Transform the current diagram into a collaboration.
*
* @return {djs.model.Root} the new root element
*/
Modeling.prototype.makeCollaboration = function() {
var collaborationElement = this._create('root', {
type: 'bpmn:Collaboration'
});
var context = {
newRoot: collaborationElement
};
this._commandStack.execute('canvas.updateRoot', context);
return collaborationElement;
};
Modeling.prototype.updateLaneRefs = function(flowNodeShapes, laneShapes) {
this._commandStack.execute('lane.updateRefs', {
flowNodeShapes: flowNodeShapes,
laneShapes: laneShapes
});
};
/**
* Transform the current diagram into a process.
*
* @return {djs.model.Root} the new root element
*/
Modeling.prototype.makeProcess = function() {
var processElement = this._create('root', {
type: 'bpmn:Process'
});
var context = {
newRoot: processElement
};
this._commandStack.execute('canvas.updateRoot', context);
};
Modeling.prototype.claimId = function(id, moddleElement) {
this._commandStack.execute('id.updateClaim', {
id: id,
element: moddleElement,
claiming: true
});
};
Modeling.prototype.unclaimId = function(id, moddleElement) {
this._commandStack.execute('id.updateClaim', {
id: id,
element: moddleElement
});
};
Modeling.prototype.setColor = function(elements, colors) {
if (!elements.length) {
elements = [ elements ];
}
this._commandStack.execute('element.setColor', {
elements: elements,
colors: colors
});
};
},{"177":177,"289":289,"58":58,"59":59,"60":60,"61":61,"62":62,"63":63,"64":64,"65":65}],33:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var is = _dereq_(95).is;
var CommandInterceptor = _dereq_(119);
function AppendBehavior(eventBus, elementFactory, bpmnRules) {
CommandInterceptor.call(this, eventBus);
// assign correct shape position unless already set
this.preExecute('shape.append', function(context) {
var source = context.source,
shape = context.shape;
if (!context.position) {
if (is(shape, 'bpmn:TextAnnotation')) {
context.position = {
x: source.x + source.width / 2 + 75,
y: source.y - (50) - shape.height / 2
};
} else {
context.position = {
x: source.x + source.width + 80 + shape.width / 2,
y: source.y + source.height / 2
};
}
}
}, true);
}
AppendBehavior.$inject = [ 'eventBus', 'elementFactory', 'bpmnRules' ];
inherits(AppendBehavior, CommandInterceptor);
module.exports = AppendBehavior;
},{"119":119,"289":289,"95":95}],34:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var forEach = _dereq_(303);
var is = _dereq_(95).is;
var CommandInterceptor = _dereq_(119);
function CopyPasteBehavior(eventBus, modeling, canvas) {
CommandInterceptor.call(this, eventBus);
this.preExecute('elements.paste', 1500, function(context) {
var topParent = context.topParent;
// always grab the latest root
if (!topParent.parent) {
context.topParent = canvas.getRootElement();
}
if (is(topParent, 'bpmn:Lane')) {
do {
// unwrap Lane -> LaneSet -> (Lane | FlowElementsContainer)
topParent = context.topParent = topParent.parent;
} while (is(topParent, 'bpmn:Lane') || !is(topParent, 'bpmn:Participant'));
}
}, true);
this.postExecute('elements.paste', function(context) {
var tree = context.tree,
createdElements = tree.createdElements;
forEach(createdElements, function(data) {
var element = data.element,
businessObject = element.businessObject,
descriptor = data.descriptor,
defaultFlow;
if ((is(businessObject, 'bpmn:ExclusiveGateway') || is(businessObject, 'bpmn:InclusiveGateway') ||
is(businessObject, 'bpmn:Activity')) && descriptor.default) {
defaultFlow = createdElements[descriptor.default];
// if the default flow wasn't created, means that it wasn't copied
if (defaultFlow) {
defaultFlow = defaultFlow.element;
} else {
defaultFlow = undefined;
}
delete element.default;
modeling.updateProperties(element, { default: defaultFlow });
}
});
}, true);
}
CopyPasteBehavior.$inject = [ 'eventBus', 'modeling', 'canvas' ];
inherits(CopyPasteBehavior, CommandInterceptor);
module.exports = CopyPasteBehavior;
},{"119":119,"289":289,"303":303,"95":95}],35:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
/**
* BPMN specific create boundary event behavior
*/
function CreateBoundaryEventBehavior(eventBus, modeling, elementFactory, bpmnFactory) {
CommandInterceptor.call(this, eventBus);
/**
* replace intermediate event with boundary event when
* attaching it to a shape
*/
this.preExecute('shape.create', function(context) {
var shape = context.shape,
host = context.host,
businessObject,
boundaryEvent;
var attrs = {
cancelActivity: true
};
if (host && is(shape, 'bpmn:IntermediateThrowEvent')) {
attrs.attachedToRef = host.businessObject;
businessObject = bpmnFactory.create('bpmn:BoundaryEvent', attrs);
boundaryEvent = {
type: 'bpmn:BoundaryEvent',
businessObject: businessObject
};
context.shape = elementFactory.createShape(boundaryEvent);
}
}, true);
}
CreateBoundaryEventBehavior.$inject = [ 'eventBus', 'modeling', 'elementFactory', 'bpmnFactory' ];
inherits(CreateBoundaryEventBehavior, CommandInterceptor);
module.exports = CreateBoundaryEventBehavior;
},{"119":119,"289":289,"95":95}],36:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
/**
* BPMN specific create data object behavior
*/
function CreateDataObjectBehavior(eventBus, bpmnFactory, moddle) {
CommandInterceptor.call(this, eventBus);
this.preExecute('shape.create', function(event) {
var context = event.context,
shape = context.shape;
if (is(shape, 'bpmn:DataObjectReference') && shape.type !== 'label') {
// create a DataObject every time a DataObjectReference is created
var dataObject = bpmnFactory.create('bpmn:DataObject');
// set the reference to the DataObject
shape.businessObject.dataObjectRef = dataObject;
}
});
}
CreateDataObjectBehavior.$inject = [ 'eventBus', 'bpmnFactory', 'moddle' ];
inherits(CreateDataObjectBehavior, CommandInterceptor);
module.exports = CreateDataObjectBehavior;
},{"119":119,"289":289,"95":95}],37:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
/**
* BPMN specific create participant behavior
*/
function CreateParticipantBehavior(eventBus, modeling, elementFactory, bpmnFactory, canvas) {
CommandInterceptor.call(this, eventBus);
/**
* morph process into collaboration before adding
* participant onto collaboration
*/
this.preExecute('shape.create', function(context) {
var parent = context.parent,
shape = context.shape,
position = context.position;
var rootElement = canvas.getRootElement();
if (is(parent, 'bpmn:Process') && is(shape, 'bpmn:Participant') && !is(rootElement, 'bpmn:Collaboration')) {
// this is going to detach the process root
// and set the returned collaboration element
// as the new root element
var collaborationElement = modeling.makeCollaboration();
// monkey patch the create context
// so that the participant is being dropped
// onto the new collaboration root instead
context.position = position;
context.parent = collaborationElement;
context.processRoot = parent;
}
}, true);
this.execute('shape.create', function(context) {
var processRoot = context.processRoot,
shape = context.shape;
if (processRoot) {
context.oldProcessRef = shape.businessObject.processRef;
// assign the participant processRef
shape.businessObject.processRef = processRoot.businessObject;
}
}, true);
this.revert('shape.create', function(context) {
var processRoot = context.processRoot,
shape = context.shape;
if (processRoot) {
// assign the participant processRef
shape.businessObject.processRef = context.oldProcessRef;
}
}, true);
this.postExecute('shape.create', function(context) {
var processRoot = context.processRoot,
shape = context.shape;
if (processRoot) {
// process root is already detached at this point
var processChildren = processRoot.children.slice();
modeling.moveElements(processChildren, { x: 0, y: 0 }, shape);
}
}, true);
}
CreateParticipantBehavior.$inject = [ 'eventBus', 'modeling', 'elementFactory', 'bpmnFactory', 'canvas' ];
inherits(CreateParticipantBehavior, CommandInterceptor);
module.exports = CreateParticipantBehavior;
},{"119":119,"289":289,"95":95}],38:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var Collections = _dereq_(263);
var find = _dereq_(302);
var is = _dereq_(95).is;
var TARGET_REF_PLACEHOLDER_NAME = '__targetRef_placeholder';
/**
* This behavior makes sure we always set a fake
* DataInputAssociation#targetRef as demanded by the BPMN 2.0
* XSD schema.
*
* The reference is set to a bpmn:Property{ name: '__targetRef_placeholder' }
* which is created on the fly and cleaned up afterwards if not needed
* anymore.
*
* @param {EventBus} eventBus
* @param {BpmnFactory} bpmnFactory
*/
function DataInputAssociationBehavior(eventBus, bpmnFactory) {
CommandInterceptor.call(this, eventBus);
this.executed([
'connection.create',
'connection.delete',
'connection.move',
'connection.reconnectEnd'
], ifDataInputAssociation(fixTargetRef));
this.reverted([
'connection.create',
'connection.delete',
'connection.move',
'connection.reconnectEnd'
], ifDataInputAssociation(fixTargetRef));
function usesTargetRef(element, targetRef, removedConnection) {
var inputAssociations = element.get('dataInputAssociations');
return find(inputAssociations, function(association) {
return association !== removedConnection &&
association.targetRef === targetRef;
});
}
function getTargetRef(element, create) {
var properties = element.get('properties');
var targetRefProp = find(properties, function(p) {
return p.name === TARGET_REF_PLACEHOLDER_NAME;
});
if (!targetRefProp && create) {
targetRefProp = bpmnFactory.create('bpmn:Property', {
name: TARGET_REF_PLACEHOLDER_NAME
});
Collections.add(properties, targetRefProp);
}
return targetRefProp;
}
function cleanupTargetRef(element, connection) {
var targetRefProp = getTargetRef(element);
if (!targetRefProp) {
return;
}
if (!usesTargetRef(element, targetRefProp, connection)) {
Collections.remove(element.get('properties'), targetRefProp);
}
}
/**
* Make sure targetRef is set to a valid property or
* `null` if the connection is detached.
*
* @param {Event} event
*/
function fixTargetRef(event) {
var context = event.context,
connection = context.connection,
connectionBo = connection.businessObject,
target = connection.target,
targetBo = target && target.businessObject,
newTarget = context.newTarget,
newTargetBo = newTarget && newTarget.businessObject,
oldTarget = context.oldTarget || context.target,
oldTargetBo = oldTarget && oldTarget.businessObject;
var dataAssociation = connection.businessObject,
targetRefProp;
if (oldTargetBo && oldTargetBo !== targetBo) {
cleanupTargetRef(oldTargetBo, connectionBo);
}
if (newTargetBo && newTargetBo !== targetBo) {
cleanupTargetRef(newTargetBo, connectionBo);
}
if (targetBo) {
targetRefProp = getTargetRef(targetBo, true);
dataAssociation.targetRef = targetRefProp;
} else {
dataAssociation.targetRef = null;
}
}
}
DataInputAssociationBehavior.$inject = [ 'eventBus', 'bpmnFactory' ];
inherits(DataInputAssociationBehavior, CommandInterceptor);
module.exports = DataInputAssociationBehavior;
/**
* Only call the given function when the event
* touches a bpmn:DataInputAssociation.
*
* @param {Function} fn
* @return {Function}
*/
function ifDataInputAssociation(fn) {
return function(event) {
var context = event.context,
connection = context.connection;
if (is(connection, 'bpmn:DataInputAssociation')) {
return fn(event);
}
};
}
},{"119":119,"263":263,"289":289,"302":302,"95":95}],39:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
var getChildLanes = _dereq_(67).getChildLanes;
var eachElement = _dereq_(266).eachElement;
var LOW_PRIORITY = 500;
/**
* BPMN specific delete lane behavior
*/
function DeleteLaneBehavior(eventBus, modeling, spaceTool) {
CommandInterceptor.call(this, eventBus);
function compensateLaneDelete(shape, oldParent) {
var siblings = getChildLanes(oldParent);
var topAffected = [];
var bottomAffected = [];
eachElement(siblings, function(element) {
if (element.y > shape.y) {
bottomAffected.push(element);
} else {
topAffected.push(element);
}
return element.children;
});
if (!siblings.length) {
return;
}
var offset;
if (bottomAffected.length && topAffected.length) {
offset = shape.height / 2;
} else {
offset = shape.height;
}
var topAdjustments,
bottomAdjustments;
if (topAffected.length) {
topAdjustments = spaceTool.calculateAdjustments(
topAffected, 'y', offset, shape.y - 10);
spaceTool.makeSpace(
topAdjustments.movingShapes,
topAdjustments.resizingShapes,
{ x: 0, y: offset }, 's');
}
if (bottomAffected.length) {
bottomAdjustments = spaceTool.calculateAdjustments(
bottomAffected, 'y', -offset, shape.y + shape.height + 10);
spaceTool.makeSpace(
bottomAdjustments.movingShapes,
bottomAdjustments.resizingShapes,
{ x: 0, y: -offset }, 'n');
}
}
/**
* Adjust sizes of other lanes after lane deletion
*/
this.postExecuted('shape.delete', LOW_PRIORITY, function(event) {
var context = event.context,
hints = context.hints,
shape = context.shape,
oldParent = context.oldParent;
// only compensate lane deletes
if (!is(shape, 'bpmn:Lane')) {
return;
}
// compensate root deletes only
if (hints && hints.nested) {
return;
}
compensateLaneDelete(shape, oldParent);
});
}
DeleteLaneBehavior.$inject = [ 'eventBus', 'modeling', 'spaceTool' ];
inherits(DeleteLaneBehavior, CommandInterceptor);
module.exports = DeleteLaneBehavior;
},{"119":119,"266":266,"289":289,"67":67,"95":95}],40:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var assign = _dereq_(431),
find = _dereq_(302);
var CommandInterceptor = _dereq_(119);
var getApproxIntersection = _dereq_(272).getApproxIntersection;
function isPointInsideBBox(bbox, point) {
var x = point.x,
y = point.y;
return x >= bbox.x &&
x <= bbox.x + bbox.width &&
y >= bbox.y &&
y <= bbox.y + bbox.height;
}
function copy(obj) {
return assign({}, obj);
}
function getMid(bounds) {
return {
x: Math.round(bounds.x + bounds.width / 2),
y: Math.round(bounds.y + bounds.height / 2)
};
}
function DropOnFlow(eventBus, bpmnRules, modeling) {
CommandInterceptor.call(this, eventBus);
/**
* Reconnect start / end of a connection after
* dropping an element on a flow.
*/
function insertShape(shape, targetFlow, position) {
var waypoints = targetFlow.waypoints,
waypointsBefore, waypointsAfter, dockingPoint, source, target, reconnected;
var intersection = getApproxIntersection(waypoints, position);
if (intersection) {
waypointsBefore = waypoints.slice(0, intersection.index);
waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));
dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : position;
// if last waypointBefore is inside shape's bounds, ignore docking point
if (!isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length-1])) {
waypointsBefore.push(copy(dockingPoint));
}
// if first waypointAfter is inside shape's bounds, ignore docking point
if (!isPointInsideBBox(shape, waypointsAfter[0])) {
waypointsAfter.unshift(copy(dockingPoint));
}
}
source = targetFlow.source;
target = targetFlow.target;
if (bpmnRules.canConnect(source, shape, targetFlow)) {
// reconnect source -> inserted shape
modeling.reconnectEnd(targetFlow, shape, waypointsBefore || position);
reconnected = true;
}
if (bpmnRules.canConnect(shape, target, targetFlow)) {
if (!reconnected) {
// reconnect inserted shape -> end
modeling.reconnectStart(targetFlow, shape, waypointsAfter || position);
} else {
modeling.connect(shape, target, { type: targetFlow.type, waypoints: waypointsAfter });
}
}
}
this.preExecute('elements.move', function(context) {
var newParent = context.newParent,
shapes = context.shapes,
delta = context.delta,
shape = shapes[0];
if (!shape || !newParent) {
return;
}
// if the new parent is a connection,
// change it to the new parent's parent
if (newParent && newParent.waypoints) {
context.newParent = newParent = newParent.parent;
}
var shapeMid = getMid(shape);
var newShapeMid = {
x: shapeMid.x + delta.x,
y: shapeMid.y + delta.y
};
// find a connection which intersects with the
// element's mid point
var connection = find(newParent.children, function(element) {
var canInsert = bpmnRules.canInsert(shapes, element);
return canInsert && getApproxIntersection(element.waypoints, newShapeMid);
});
if (connection) {
context.targetFlow = connection;
context.position = newShapeMid;
}
}, true);
this.postExecuted('elements.move', function(context) {
var shapes = context.shapes,
targetFlow = context.targetFlow,
position = context.position;
if (targetFlow) {
insertShape(shapes[0], targetFlow, position);
}
}, true);
this.preExecute('shape.create', function(context) {
var parent = context.parent,
shape = context.shape;
if (bpmnRules.canInsert(shape, parent)) {
context.targetFlow = parent;
context.parent = parent.parent;
}
}, true);
this.postExecute('shape.create', function(context) {
var shape = context.shape,
targetFlow = context.targetFlow,
position = context.position;
if (targetFlow) {
insertShape(shape, targetFlow, position);
}
}, true);
}
inherits(DropOnFlow, CommandInterceptor);
DropOnFlow.$inject = [ 'eventBus', 'bpmnRules', 'modeling' ];
module.exports = DropOnFlow;
},{"119":119,"272":272,"289":289,"302":302,"431":431}],41:[function(_dereq_,module,exports){
'use strict';
var getMid = _dereq_(252).getMid;
var lineIntersect = _dereq_(57);
/**
* Fix broken dockings after DI imports.
*
* @param {EventBus} eventBus
*/
function ImportDockingFix(eventBus) {
function adjustDocking(startPoint, nextPoint, elementMid) {
var elementTop = {
x: elementMid.x,
y: elementMid.y - 50
};
var elementLeft = {
x: elementMid.x - 50,
y: elementMid.y
};
var verticalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementTop),
horizontalIntersect = lineIntersect(startPoint, nextPoint, elementMid, elementLeft);
// original is horizontal or vertical center cross intersection
var centerIntersect;
if (verticalIntersect && horizontalIntersect) {
if (getDistance(verticalIntersect, elementMid) > getDistance(horizontalIntersect, elementMid)) {
centerIntersect = horizontalIntersect;
} else {
centerIntersect = verticalIntersect;
}
} else {
centerIntersect = verticalIntersect || horizontalIntersect;
}
startPoint.original = centerIntersect;
}
function fixDockings(connection) {
var waypoints = connection.waypoints;
adjustDocking(
waypoints[0],
waypoints[1],
getMid(connection.source)
);
adjustDocking(
waypoints[waypoints.length - 1],
waypoints[waypoints.length - 2],
getMid(connection.target)
);
}
eventBus.on('bpmnElement.added', function(e) {
var element = e.element;
if (element.waypoints) {
fixDockings(element);
}
});
}
ImportDockingFix.$inject = [ 'eventBus' ];
module.exports = ImportDockingFix;
/////// helpers //////////////////////////////////
function getDistance(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}
},{"252":252,"57":57}],42:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
inherits = _dereq_(289);
var LabelUtil = _dereq_(94),
LabelLayoutUtil = _dereq_(55),
ModelUtil = _dereq_(95),
is = ModelUtil.is,
getBusinessObject = ModelUtil.getBusinessObject;
var hasExternalLabel = LabelUtil.hasExternalLabel,
getExternalLabelMid = LabelUtil.getExternalLabelMid,
getLabelAdjustment = LabelLayoutUtil.getLabelAdjustment;
var CommandInterceptor = _dereq_(119);
var TextUtil = _dereq_(280);
var DEFAULT_LABEL_DIMENSIONS = {
width: 90,
height: 20
};
/**
* A component that makes sure that external labels are added
* together with respective elements and properly updated (DI wise)
* during move.
*
* @param {EventBus} eventBus
* @param {Modeling} modeling
* @param {BpmnFactory} bpmnFactory
*/
function LabelSupport(eventBus, modeling, bpmnFactory) {
CommandInterceptor.call(this, eventBus);
var textUtil = new TextUtil();
///// create external labels on shape creation
this.postExecute([ 'shape.create', 'connection.create' ], function(e) {
var context = e.context;
var element = context.shape || context.connection,
businessObject = element.businessObject;
if (!hasExternalLabel(businessObject)) {
return;
}
var labelCenter = getExternalLabelMid(element);
// we don't care about x and y
var labelDimensions = getLayoutedBounds(
DEFAULT_LABEL_DIMENSIONS,
businessObject.name || '',
textUtil
);
modeling.createLabel(element, labelCenter, {
id: businessObject.id + '_label',
hidden: !businessObject.name,
businessObject: businessObject,
width: labelDimensions.width,
height: labelDimensions.height
});
});
///// update di information on label creation
this.executed([ 'label.create' ], function(event) {
var element = event.context.shape,
businessObject,
di;
// we want to trigger on real labels only
if (!element.labelTarget) {
return;
}
// we want to trigger on BPMN elements only
if (!is(element.labelTarget || element, 'bpmn:BaseElement')) {
return;
}
businessObject = element.businessObject,
di = businessObject.di;
if (!di.label) {
di.label = bpmnFactory.create('bpmndi:BPMNLabel', {
bounds: bpmnFactory.create('dc:Bounds')
});
}
assign(di.label.bounds, {
x: element.x,
y: element.y,
width: element.width,
height: element.height
});
});
///// update label position on connection change
function getHiddenLabelAdjustment(event) {
var context = event.context,
connection = context.connection,
label = connection.label;
var labelPosition = getExternalLabelMid(connection);
return {
x: labelPosition.x - label.x - label.width / 2,
y: labelPosition.y - label.y - label.height / 2
};
}
function getVisibleLabelAdjustment(event) {
var command = event.command,
context = event.context,
connection = context.connection,
label = connection.label,
hints = assign({}, context.hints),
newWaypoints = context.newWaypoints || connection.waypoints,
oldWaypoints = context.oldWaypoints;
if (typeof hints.startChanged === 'undefined') {
hints.startChanged = (command === 'connection.reconnectStart');
}
if (typeof hints.endChanged === 'undefined') {
hints.endChanged = (command === 'connection.reconnectEnd');
}
return getLabelAdjustment(label, newWaypoints, oldWaypoints, hints);
}
this.postExecute([
'connection.layout',
'connection.reconnectEnd',
'connection.reconnectStart',
'connection.updateWaypoints'
], function(event) {
var label = event.context.connection.label,
labelAdjustment;
if (!label) {
return;
}
if (label.hidden) {
labelAdjustment = getHiddenLabelAdjustment(event);
} else {
labelAdjustment = getVisibleLabelAdjustment(event);
}
modeling.moveShape(label, labelAdjustment);
});
////// keep label position on shape replace
this.postExecute([ 'shape.replace' ], function(event) {
var context = event.context,
newShape = context.newShape,
oldShape = context.oldShape;
var businessObject = getBusinessObject(newShape);
if (businessObject && hasExternalLabel(businessObject)) {
newShape.label.x = oldShape.label.x;
newShape.label.y = oldShape.label.y;
}
});
}
inherits(LabelSupport, CommandInterceptor);
LabelSupport.$inject = [ 'eventBus', 'modeling', 'bpmnFactory' ];
module.exports = LabelSupport;
// TODO(nikku): repeating code (search for )
var EXTERNAL_LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: '11px'
};
function getLayoutedBounds(bounds, text, textUtil) {
var layoutedLabelDimensions = textUtil.getDimensions(text, {
box: {
width: 90,
height: 30,
x: bounds.width / 2 + bounds.x,
y: bounds.height / 2 + bounds.y
},
style: EXTERNAL_LABEL_STYLE
});
// resize label shape to fit label text
return {
x: Math.round(bounds.x + bounds.width / 2 - layoutedLabelDimensions.width / 2),
y: Math.round(bounds.y),
width: Math.ceil(layoutedLabelDimensions.width),
height: Math.ceil(layoutedLabelDimensions.height)
};
}
},{"119":119,"280":280,"289":289,"431":431,"55":55,"94":94,"95":95}],43:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
var COLLAB_ERR_MSG = 'flow elements must be children of pools/participants',
PROCESS_ERR_MSG = 'participants cannot be pasted onto a non-empty process diagram';
function ModelingFeedback(eventBus, tooltips, translate) {
function showError(position, message, timeout) {
tooltips.add({
position: {
x: position.x + 5,
y: position.y + 5
},
type: 'error',
timeout: timeout || 2000,
html: '' + message + '
'
});
}
eventBus.on([ 'shape.move.rejected', 'create.rejected' ], function(event) {
var context = event.context,
shape = context.shape,
target = context.target;
if (is(target, 'bpmn:Collaboration') && is(shape, 'bpmn:FlowNode')) {
showError(event, translate(COLLAB_ERR_MSG));
}
});
eventBus.on([ 'elements.paste.rejected' ], function(event) {
var context = event.context,
position = context.position,
target = context.target;
if (is(target, 'bpmn:Collaboration')) {
showError(position, translate(COLLAB_ERR_MSG));
}
if (is(target, 'bpmn:Process')) {
showError(position, translate(PROCESS_ERR_MSG), 3000);
}
});
}
ModelingFeedback.$inject = [ 'eventBus', 'tooltips', 'translate' ];
module.exports = ModelingFeedback;
},{"95":95}],44:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var lineIntersect = _dereq_(57);
function RemoveElementBehavior(eventBus, bpmnRules, modeling) {
CommandInterceptor.call(this, eventBus);
/**
* Combine sequence flows when deleting an element
* if there is one incoming and one outgoing
* sequence flow
*/
this.preExecute('shape.delete', function(e) {
var shape = e.context.shape;
if (shape.incoming.length == 1 && shape.outgoing.length == 1) {
var inConnection = shape.incoming[0],
outConnection = shape.outgoing[0];
if (bpmnRules.canConnect(inConnection.source, outConnection.target, inConnection)) {
// compute new, combined waypoints
var newWaypoints = getNewWaypoints(inConnection.waypoints, outConnection.waypoints);
modeling.reconnectEnd(inConnection, outConnection.target, newWaypoints);
}
}
});
}
inherits(RemoveElementBehavior, CommandInterceptor);
RemoveElementBehavior.$inject = [ 'eventBus', 'bpmnRules', 'modeling' ];
module.exports = RemoveElementBehavior;
///////// helpers //////////////////////////////
function getDocking(point) {
return point.original || point;
}
function getNewWaypoints(inWaypoints, outWaypoints) {
var intersection = lineIntersect(
getDocking(inWaypoints[inWaypoints.length - 2]),
getDocking(inWaypoints[inWaypoints.length - 1]),
getDocking(outWaypoints[1]),
getDocking(outWaypoints[0]));
if (intersection) {
return [].concat(
inWaypoints.slice(0, inWaypoints.length - 1),
[ intersection ],
outWaypoints.slice(1));
} else {
return [
getDocking(inWaypoints[0]),
getDocking(outWaypoints[outWaypoints.length - 1])
];
}
}
},{"119":119,"289":289,"57":57}],45:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
/**
* BPMN specific remove behavior
*/
function RemoveParticipantBehavior(eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
/**
* morph collaboration diagram into process diagram
* after the last participant has been removed
*/
this.preExecute('shape.delete', function(context) {
var shape = context.shape,
parent = shape.parent;
// activate the behavior if the shape to be removed
// is a participant
if (is(shape, 'bpmn:Participant')) {
context.collaborationRoot = parent;
}
}, true);
this.postExecute('shape.delete', function(context) {
var collaborationRoot = context.collaborationRoot;
if (collaborationRoot && !collaborationRoot.businessObject.participants.length) {
// replace empty collaboration with process diagram
modeling.makeProcess();
}
}, true);
}
RemoveParticipantBehavior.$inject = [ 'eventBus', 'modeling' ];
inherits(RemoveParticipantBehavior, CommandInterceptor);
module.exports = RemoveParticipantBehavior;
},{"119":119,"289":289,"95":95}],46:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
find = _dereq_(302),
inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules) {
CommandInterceptor.call(this, eventBus);
function fixConnection(connection) {
var source = connection.source,
target = connection.target,
parent = connection.parent;
// do not do anything if connection
// is already deleted (may happen due to other
// behaviors plugged-in before)
if (!parent) {
return;
}
var replacementType,
remove;
/**
* Check if incoming or outgoing connections
* can stay or could be substituted with an
* appropriate replacement.
*
* This holds true for SequenceFlow <> MessageFlow.
*/
if (is(connection, 'bpmn:SequenceFlow')) {
if (!bpmnRules.canConnectSequenceFlow(source, target)) {
remove = true;
}
if (bpmnRules.canConnectMessageFlow(source, target)) {
replacementType = 'bpmn:MessageFlow';
}
}
// transform message flows into sequence flows, if possible
if (is(connection, 'bpmn:MessageFlow')) {
if (!bpmnRules.canConnectMessageFlow(source, target)) {
remove = true;
}
if (bpmnRules.canConnectSequenceFlow(source, target)) {
replacementType = 'bpmn:SequenceFlow';
}
}
if (is(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
remove = true;
}
// remove invalid connection,
// unless it has been removed already
if (remove) {
modeling.removeConnection(connection);
}
// replace SequenceFlow <> MessageFlow
if (replacementType) {
modeling.connect(source, target, {
type: replacementType,
waypoints: connection.waypoints.slice()
});
}
}
this.postExecuted('elements.move', function(context) {
var closure = context.closure,
allConnections = closure.allConnections;
forEach(allConnections, fixConnection);
}, true);
this.postExecuted([
'connection.reconnectStart',
'connection.reconnectEnd'
], function(event) {
var connection = event.context.connection;
fixConnection(connection);
});
this.postExecuted('element.updateProperties', function(event) {
var context = event.context,
properties = context.properties,
element = context.element,
businessObject = element.businessObject,
connection;
// remove condition expression when morphing to default flow
if (properties.default) {
connection = find(element.outgoing, { id: element.businessObject.default.id });
if (connection) {
modeling.updateProperties(connection, { conditionExpression: undefined });
}
}
// remove default property from source when morphing to conditional flow
if (properties.conditionExpression && businessObject.sourceRef.default === businessObject) {
modeling.updateProperties(element.source, { default: undefined });
}
});
}
inherits(ReplaceConnectionBehavior, CommandInterceptor);
ReplaceConnectionBehavior.$inject = [ 'eventBus', 'modeling', 'bpmnRules' ];
module.exports = ReplaceConnectionBehavior;
},{"119":119,"289":289,"302":302,"303":303,"95":95}],47:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var forEach = _dereq_(303);
var isEventSubProcess = _dereq_(93).isEventSubProcess;
var is = _dereq_(95).is;
/**
* Defines the behaviour of what happens to the elements inside a container
* that morphs into another BPMN element
*/
function ReplaceElementBehaviour(eventBus, bpmnReplace, bpmnRules, elementRegistry, selection, modeling) {
CommandInterceptor.call(this, eventBus);
this._bpmnReplace = bpmnReplace;
this._elementRegistry = elementRegistry;
this._selection = selection;
this._modeling = modeling;
this.postExecuted([ 'elements.move' ], 500, function(event) {
var context = event.context,
target = context.newParent,
newHost = context.newHost,
elements = [];
forEach(context.closure.topLevel, function(topLevelElements) {
if (isEventSubProcess(topLevelElements)) {
elements = elements.concat(topLevelElements.children);
} else {
elements = elements.concat(topLevelElements);
}
});
// Change target to host when the moving element is a `bpmn:BoundaryEvent`
if (elements.length === 1 && newHost) {
target = newHost;
}
var canReplace = bpmnRules.canReplace(elements, target);
if (canReplace) {
this.replaceElements(elements, canReplace.replacements, newHost);
}
}, this);
// update attachments if the host is replaced
this.postExecute([ 'shape.replace' ], 1500, function(e) {
var context = e.context,
oldShape = context.oldShape,
newShape = context.newShape,
attachers = oldShape.attachers,
canReplace;
if (attachers && attachers.length) {
canReplace = bpmnRules.canReplace(attachers, newShape);
this.replaceElements(attachers, canReplace.replacements);
}
}, this);
this.postExecuted( [ 'shape.replace' ], 1500, function(e) {
var context = e.context,
oldShape = context.oldShape,
newShape = context.newShape;
modeling.unclaimId(oldShape.businessObject.id, oldShape.businessObject);
modeling.updateProperties(newShape, { id: oldShape.id });
});
}
inherits(ReplaceElementBehaviour, CommandInterceptor);
ReplaceElementBehaviour.prototype.replaceElements = function(elements, newElements, newHost) {
var elementRegistry = this._elementRegistry,
bpmnReplace = this._bpmnReplace,
selection = this._selection,
modeling = this._modeling;
forEach(newElements, function(replacement) {
var newElement = {
type: replacement.newElementType
};
var oldElement = elementRegistry.get(replacement.oldElementId);
if (newHost && is(oldElement, 'bpmn:BoundaryEvent')) {
modeling.updateAttachment(oldElement, null);
}
var idx = elements.indexOf(oldElement);
elements[idx] = bpmnReplace.replaceElement(oldElement, newElement, { select: false });
if (newHost && is(elements[idx], 'bpmn:BoundaryEvent')) {
modeling.updateAttachment(elements[idx], newHost);
}
});
if (newElements) {
selection.select(elements);
}
};
ReplaceElementBehaviour.$inject = [
'eventBus', 'bpmnReplace', 'bpmnRules',
'elementRegistry', 'selection', 'modeling'
];
module.exports = ReplaceElementBehaviour;
},{"119":119,"289":289,"303":303,"93":93,"95":95}],48:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
var roundBounds = _dereq_(252).roundBounds;
var hasPrimaryModifier = _dereq_(274).hasPrimaryModifier;
var SLIGHTLY_HIGHER_PRIORITY = 1001;
/**
* Invoke {@link Modeling#resizeLane} instead of
* {@link Modeling#resizeShape} when resizing a Lane
* or Participant shape.
*/
function ResizeLaneBehavior(eventBus, modeling) {
eventBus.on('resize.start', SLIGHTLY_HIGHER_PRIORITY + 500, function(event) {
var context = event.context,
shape = context.shape;
if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) {
// should we resize the opposite lane(s) in
// order to compensate for the resize operation?
context.balanced = !hasPrimaryModifier(event);
}
});
/**
* Intercept resize end and call resize lane function instead.
*/
eventBus.on('resize.end', SLIGHTLY_HIGHER_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
canExecute = context.canExecute,
newBounds = context.newBounds;
if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) {
if (canExecute) {
// ensure we have actual pixel values for new bounds
// (important when zoom level was > 1 during move)
newBounds = roundBounds(newBounds);
// perform the actual resize
modeling.resizeLane(shape, newBounds, context.balanced);
}
// stop propagation
return false;
}
});
}
ResizeLaneBehavior.$inject = [ 'eventBus', 'modeling' ];
module.exports = ResizeLaneBehavior;
},{"252":252,"274":274,"95":95}],49:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119),
getBusinessObject = _dereq_(95).getBusinessObject,
is = _dereq_(95).is,
computeChildrenBBox = _dereq_(223).computeChildrenBBox;
var LOW_PRIORITY = 500;
function ToggleElementCollapseBehaviour(eventBus, elementFactory, modeling, resize) {
CommandInterceptor.call(this, eventBus);
function hideEmptyLables(children) {
if (children.length) {
children.forEach(function(child) {
if (child.type === 'label' && !child.businessObject.name) {
child.hidden = true;
}
});
}
}
function expandedBounds(shape, defaultSize) {
var children = shape.children,
newBounds = defaultSize,
visibleElements,
visibleBBox;
visibleElements = filterVisible(children).concat([ shape ]);
visibleBBox = computeChildrenBBox(visibleElements);
if (visibleBBox) {
// center to visibleBBox with max(defaultSize, childrenBounds)
newBounds.width = Math.max(visibleBBox.width, newBounds.width);
newBounds.height = Math.max(visibleBBox.height, newBounds.height);
newBounds.x = visibleBBox.x + (visibleBBox.width - newBounds.width) / 2;
newBounds.y = visibleBBox.y + (visibleBBox.height - newBounds.height) / 2;
} else {
// center to collapsed shape with defaultSize
newBounds.x = shape.x + (shape.width - newBounds.width) / 2;
newBounds.y = shape.y + (shape.height - newBounds.height) / 2;
}
return newBounds;
}
function collapsedBounds(shape, defaultSize) {
return {
x: shape.x + (shape.width - defaultSize.width) / 2,
y: shape.y + (shape.height - defaultSize.height) / 2,
width: defaultSize.width,
height: defaultSize.height
};
}
this.executed([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var context = e.context,
shape = context.shape;
if (!is(shape, 'bpmn:SubProcess')) {
return;
}
if (!shape.collapsed) {
// all children got made visible through djs, hide empty labels
hideEmptyLables(shape.children);
// remove collapsed marker
getBusinessObject(shape).di.isExpanded = true;
} else {
// place collapsed marker
getBusinessObject(shape).di.isExpanded = false;
}
});
this.reverted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var context = e.context;
var shape = context.shape;
// revert removing/placing collapsed marker
if (!shape.collapsed) {
getBusinessObject(shape).di.isExpanded = true;
} else {
getBusinessObject(shape).di.isExpanded = false;
}
});
this.postExecuted([ 'shape.toggleCollapse' ], LOW_PRIORITY, function(e) {
var shape = e.context.shape,
defaultSize = elementFactory._getDefaultSize(shape),
newBounds;
if (shape.collapsed) {
// resize to default size of collapsed shapes
newBounds = collapsedBounds(shape, defaultSize);
} else {
// resize to bounds of max(visible children, defaultSize)
newBounds = expandedBounds(shape, defaultSize);
}
modeling.resizeShape(shape, newBounds);
});
}
inherits(ToggleElementCollapseBehaviour, CommandInterceptor);
ToggleElementCollapseBehaviour.$inject = [
'eventBus',
'elementFactory',
'modeling'
];
module.exports = ToggleElementCollapseBehaviour;
/////// helpers ///////////////////////////
function filterVisible(elements) {
return elements.filter(function(e) {
return !e.hidden;
});
}
},{"119":119,"223":223,"289":289,"95":95}],50:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
function UnclaimIdBehavior(eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
this.preExecute('elements.delete', function(event) {
var context = event.context,
elements = context.elements;
forEach(elements, function(element) {
modeling.unclaimId(element.businessObject.id, element.businessObject);
});
});
}
inherits(UnclaimIdBehavior, CommandInterceptor);
UnclaimIdBehavior.$inject = [ 'eventBus', 'modeling' ];
module.exports = UnclaimIdBehavior;
},{"119":119,"289":289,"303":303}],51:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is,
getBusinessObject = _dereq_(95).getBusinessObject;
/**
* A behavior that unsets the Default property of
* sequence flow source on element delete, if the
* removed element is the Gateway or Task's default flow.
*
* @param {EventBus} eventBus
* @param {Modeling} modeling
*/
function DeleteSequenceFlowBehavior(eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
this.preExecute('connection.delete', function(event) {
var context = event.context,
connection = context.connection,
source = connection.source;
if (isDefaultFlow(connection, source)) {
modeling.updateProperties(source, {
'default': null
});
}
});
}
inherits(DeleteSequenceFlowBehavior, CommandInterceptor);
DeleteSequenceFlowBehavior.$inject = [ 'eventBus', 'modeling' ];
module.exports = DeleteSequenceFlowBehavior;
/////// helpers ///////////////////////////
function isDefaultFlow(connection, source) {
if (!is(connection, 'bpmn:SequenceFlow')) {
return false;
}
var sourceBo = getBusinessObject(source),
sequenceFlow = getBusinessObject(connection);
return sourceBo.get('default') === sequenceFlow;
}
},{"119":119,"289":289,"95":95}],52:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
var is = _dereq_(95).is;
var LOW_PRIORITY = 500,
HIGH_PRIORITY = 5000;
/**
* BPMN specific delete lane behavior
*/
function UpdateFlowNodeRefsBehavior(eventBus, modeling, translate) {
CommandInterceptor.call(this, eventBus);
/**
* Ok, this is it:
*
* We have to update the Lane#flowNodeRefs _and_
* FlowNode#lanes with every FlowNode move/resize and
* Lane move/resize.
*
* We want to group that stuff to recompute containments
* as efficient as possible.
*
* Yea!
*/
// the update context
var context;
function initContext() {
context = context || new UpdateContext();
context.enter();
return context;
}
function getContext() {
if (!context) {
throw new Error(translate('out of bounds release'));
}
return context;
}
function releaseContext() {
if (!context) {
throw new Error(translate('out of bounds release'));
}
var triggerUpdate = context.leave();
if (triggerUpdate) {
modeling.updateLaneRefs(context.flowNodes, context.lanes);
context = null;
}
return triggerUpdate;
}
var laneRefUpdateEvents = [
'spaceTool',
'lane.add',
'lane.resize',
'lane.split',
'elements.move',
'elements.delete',
'shape.create',
'shape.delete',
'shape.move',
'shape.resize'
];
// listen to a lot of stuff to group lane updates
this.preExecute(laneRefUpdateEvents, HIGH_PRIORITY, function(event) {
initContext();
});
this.postExecuted(laneRefUpdateEvents, LOW_PRIORITY, function(event) {
releaseContext();
});
// Mark flow nodes + lanes that need an update
this.preExecute([
'shape.create',
'shape.move',
'shape.delete',
'shape.resize'
], function(event) {
var context = event.context,
shape = context.shape;
var updateContext = getContext();
// no need to update labels
if (shape.labelTarget) {
return;
}
if (is(shape, 'bpmn:Lane')) {
updateContext.addLane(shape);
}
if (is(shape, 'bpmn:FlowNode')) {
updateContext.addFlowNode(shape);
}
});
}
UpdateFlowNodeRefsBehavior.$inject = [ 'eventBus', 'modeling' , 'translate'];
inherits(UpdateFlowNodeRefsBehavior, CommandInterceptor);
module.exports = UpdateFlowNodeRefsBehavior;
function UpdateContext() {
this.flowNodes = [];
this.lanes = [];
this.counter = 0;
this.addLane = function(lane) {
this.lanes.push(lane);
};
this.addFlowNode = function(flowNode) {
this.flowNodes.push(flowNode);
};
this.enter = function() {
this.counter++;
};
this.leave = function() {
this.counter--;
return !this.counter;
};
}
},{"119":119,"289":289,"95":95}],53:[function(_dereq_,module,exports){
module.exports = {
__init__: [
'appendBehavior',
'copyPasteBehavior',
'createBoundaryEventBehavior',
'createDataObjectBehavior',
'dropOnFlowBehavior',
'createParticipantBehavior',
'dataInputAssociationBehavior',
'deleteLaneBehavior',
'importDockingFix',
'labelBehavior',
'modelingFeedback',
'removeParticipantBehavior',
'replaceConnectionBehavior',
'replaceElementBehaviour',
'resizeLaneBehavior',
'unsetDefaultFlowBehavior',
'updateFlowNodeRefsBehavior',
'removeElementBehavior',
'unclaimIdBehavior',
'toggleElementCollapseBehaviour'
],
appendBehavior: [ 'type', _dereq_(33) ],
copyPasteBehavior: [ 'type', _dereq_(34) ],
createBoundaryEventBehavior: [ 'type', _dereq_(35) ],
createDataObjectBehavior: [ 'type', _dereq_(36) ],
dropOnFlowBehavior: [ 'type', _dereq_(40) ],
createParticipantBehavior: [ 'type', _dereq_(37) ],
dataInputAssociationBehavior: [ 'type', _dereq_(38) ],
deleteLaneBehavior: [ 'type', _dereq_(39) ],
importDockingFix: [ 'type', _dereq_(41) ],
labelBehavior: [ 'type', _dereq_(42) ],
modelingFeedback: [ 'type', _dereq_(43) ],
removeParticipantBehavior: [ 'type', _dereq_(45) ],
replaceConnectionBehavior: [ 'type', _dereq_(46) ],
replaceElementBehaviour: [ 'type', _dereq_(47) ],
resizeLaneBehavior: [ 'type', _dereq_(48) ],
unsetDefaultFlowBehavior: [ 'type', _dereq_(51) ],
updateFlowNodeRefsBehavior: [ 'type', _dereq_(52) ],
removeElementBehavior: [ 'type', _dereq_(44) ],
unclaimIdBehavior: [ 'type', _dereq_(50) ],
toggleElementCollapseBehaviour : [ 'type', _dereq_(49) ]
};
},{"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"50":50,"51":51,"52":52}],54:[function(_dereq_,module,exports){
'use strict';
/**
* Returns the length of a vector
*
* @param {Vector}
* @return {Float}
*/
function vectorLength(v) {
return Math.sqrt( Math.pow(v.x, 2) + Math.pow(v.y, 2) );
}
module.exports.vectorLength = vectorLength;
/**
* Calculates the angle between a line a the yAxis
*
* @param {Array}
* @return {Float}
*/
function getAngle(line) {
// return value is between 0, 180 and -180, -0
// @janstuemmel: maybe replace return a/b with b/a
return Math.atan( (line[1].y - line[0].y) / (line[1].x - line[0].x) );
}
module.exports.getAngle = getAngle;
/**
* Rotates a vector by a given angle
*
* @param {Vector}
* @param {Float} Angle in radians
* @return {Vector}
*/
function rotateVector(vector, angle) {
return (!angle) ? vector : {
x: Math.cos(angle) * vector.x - Math.sin(angle) * vector.y,
y: Math.sin(angle) * vector.x + Math.cos(angle) * vector.y
};
}
module.exports.rotateVector = rotateVector;
/**
* Solves a 2D equation system
* a + r*b = c, where a,b,c are 2D vectors
*
* @param {Vector}
* @param {Vector}
* @param {Vector}
* @return {Float}
*/
function solveLambaSystem(a, b, c) {
// the 2d system
var system = [
{ n: a[0] - c[0], lambda: b[0] },
{ n: a[1] - c[1], lambda: b[1] }
];
// solve
var n = system[0].n * b[0] + system[1].n * b[1],
l = system[0].lambda * b[0] + system[1].lambda * b[1];
return -n/l;
}
/**
* Position of perpendicular foot
*
* @param {Point}
* @param [ {Point}, {Point} ] line defined throug two points
* @return {Point} the perpendicular foot position
*/
function perpendicularFoot(point, line) {
var a = line[0], b = line[1];
// relative position of b from a
var bd = { x: b.x - a.x, y: b.y - a.y };
// solve equation system to the parametrized vectors param real value
var r = solveLambaSystem( [ a.x, a.y ], [ bd.x, bd.y ], [ point.x, point.y ] );
return { x: a.x + r*bd.x, y: a.y + r*bd.y };
}
module.exports.perpendicularFoot = perpendicularFoot;
/**
* Calculates the distance between a point and a line
*
* @param {Point}
* @param [ {Point}, {Point} ] line defined throug two points
* @return {Float} distance
*/
function getDistancePointLine(point, line) {
var pfPoint = perpendicularFoot(point, line);
// distance vector
var connectionVector = {
x: pfPoint.x - point.x,
y: pfPoint.y - point.y
};
return vectorLength(connectionVector);
}
module.exports.getDistancePointLine = getDistancePointLine;
/**
* Calculates the distance between two points
*
* @param {Point}
* @param {Point}
* @return {Float} distance
*/
function getDistancePointPoint(point1, point2) {
return vectorLength({
x: point1.x - point2.x,
y: point1.y - point2.y
});
}
module.exports.getDistancePointPoint = getDistancePointPoint;
},{}],55:[function(_dereq_,module,exports){
'use strict';
var GeometricUtil = _dereq_(54);
var getDistancePointPoint = _dereq_(54).getDistancePointPoint;
var getAttachment = _dereq_(56).getAttachment;
function findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints) {
var index = attachment.segmentIndex;
var offset = newWaypoints.length - oldWaypoints.length;
// segmentMove happend
if (hints.segmentMove) {
var oldSegmentStartIndex = hints.segmentMove.segmentStartIndex,
newSegmentStartIndex = hints.segmentMove.newSegmentStartIndex;
// if label was on moved segment return new segment index
if (index === oldSegmentStartIndex) {
return newSegmentStartIndex;
}
// label is after new segment index
if (index >= newSegmentStartIndex) {
return (index+offset < newSegmentStartIndex) ? newSegmentStartIndex : index+offset;
}
// if label is before new segment index
return index;
}
// bendpointMove happend
if (hints.bendpointMove) {
var insert = hints.bendpointMove.insert,
bendpointIndex = hints.bendpointMove.bendpointIndex,
newIndex;
// waypoints length didnt change
if (offset === 0) {
return index;
}
// label behind new/removed bendpoint
if (index >= bendpointIndex) {
newIndex = insert ? index + 1 : index - 1;
}
// label before new/removed bendpoint
if (index < bendpointIndex) {
newIndex = index;
// decide label should take right or left segment
if (insert && attachment.type !== 'bendpoint' && bendpointIndex-1 === index) {
var rel = relativePositionMidWaypoint(newWaypoints, bendpointIndex);
if (rel < attachment.relativeLocation) {
newIndex++;
}
}
}
return newIndex;
}
// start/end changed
if (offset === 0) {
return index;
}
if (hints.connectionStart) {
return (index === 0) ? 0 : null;
}
if (hints.connectionEnd) {
return (index === oldWaypoints.length - 2) ? newWaypoints.length - 2 : null;
}
// if nothing fits, return null
return null;
}
module.exports.findNewLabelLineStartIndex = findNewLabelLineStartIndex;
/**
* Calculate the required adjustment (move delta) for the given label
* after the connection waypoints got updated.
*
* @param {djs.model.Label} label
* @param {Array} newWaypoints
* @param {Array} oldWaypoints
* @param {Object} hints
*
* @return {Point} delta
*/
function getLabelAdjustment(label, newWaypoints, oldWaypoints, hints) {
var x = 0,
y = 0;
var labelPosition = getLabelMid(label);
// get closest attachment
var attachment = getAttachment(labelPosition, oldWaypoints),
oldLabelLineIndex = attachment.segmentIndex,
newLabelLineIndex = findNewLabelLineStartIndex(oldWaypoints, newWaypoints, attachment, hints);
if (newLabelLineIndex === null) {
return { x: x, y: y };
}
// should never happen
// TODO(@janstuemmel): throw an error here when connectionSegmentMove is refactored
if (newLabelLineIndex < 0 ||
newLabelLineIndex > newWaypoints.length - 2) {
return { x: x, y: y };
}
var oldLabelLine = getLine(oldWaypoints, oldLabelLineIndex),
newLabelLine = getLine(newWaypoints, newLabelLineIndex),
oldFoot = attachment.position;
var relativeFootPosition = getRelativeFootPosition(oldLabelLine, oldFoot),
angleDelta = getAngleDelta(oldLabelLine, newLabelLine);
// special rule if label on bendpoint
if (attachment.type === 'bendpoint') {
var offset = newWaypoints.length - oldWaypoints.length,
oldBendpointIndex = attachment.bendpointIndex,
oldBendpoint = oldWaypoints[oldBendpointIndex];
// bendpoint position hasnt changed, return same position
if (newWaypoints.indexOf(oldBendpoint) !== -1) {
return { x: x, y: y };
}
// new bendpoint and old bendpoint have same index, then just return the offset
if (offset === 0) {
var newBendpoint = newWaypoints[oldBendpointIndex];
return {
x: newBendpoint.x - attachment.position.x,
y: newBendpoint.y - attachment.position.y
};
}
// if bendpoints get removed
if (offset < 0 && oldBendpointIndex !== 0 && oldBendpointIndex < oldWaypoints.length - 1) {
relativeFootPosition = relativePositionMidWaypoint(oldWaypoints, oldBendpointIndex);
}
}
var newFoot = {
x: (newLabelLine[1].x - newLabelLine[0].x) * relativeFootPosition + newLabelLine[0].x,
y: (newLabelLine[1].y - newLabelLine[0].y) * relativeFootPosition + newLabelLine[0].y
};
// the rotated vector to label
var newLabelVector = GeometricUtil.rotateVector({
x: labelPosition.x - oldFoot.x,
y: labelPosition.y - oldFoot.y
}, angleDelta);
// the new relative position
x = newFoot.x + newLabelVector.x - labelPosition.x;
y = newFoot.y + newLabelVector.y - labelPosition.y;
return { x: x, y: y };
}
module.exports.getLabelAdjustment = getLabelAdjustment;
//// HELPERS ///////
function relativePositionMidWaypoint(waypoints, idx) {
var distanceSegment1 = getDistancePointPoint(waypoints[idx-1], waypoints[idx]),
distanceSegment2 = getDistancePointPoint(waypoints[idx], waypoints[idx+1]);
var relativePosition = distanceSegment1 / ( distanceSegment1 + distanceSegment2 );
return relativePosition;
}
function getLabelMid(label) {
return {
x: label.x + label.width / 2,
y: label.y + label.height / 2
};
}
function getAngleDelta(l1, l2) {
var a1 = GeometricUtil.getAngle(l1),
a2 = GeometricUtil.getAngle(l2);
return a2 - a1;
}
function getLine(waypoints, idx) {
return [ waypoints[idx], waypoints[idx+1] ];
}
function getRelativeFootPosition(line, foot) {
var length = getDistancePointPoint(line[0], line[1]),
lengthToFoot = getDistancePointPoint(line[0], foot);
return length === 0 ? 0 : lengthToFoot / length;
}
},{"54":54,"56":56}],56:[function(_dereq_,module,exports){
'use strict';
var sqrt = Math.sqrt,
min = Math.min,
max = Math.max,
abs = Math.abs;
/**
* Calculate the square (power to two) of a number.
*
* @param {Number} n
*
* @return {Number}
*/
function sq(n) {
return Math.pow(n, 2);
}
/**
* Get distance between two points.
*
* @param {Point} p1
* @param {Point} p2
*
* @return {Number}
*/
function getDistance(p1, p2) {
return sqrt(sq(p1.x - p2.x) + sq(p1.y - p2.y));
}
/**
* Return the attachment of the given point on the specified line.
*
* The attachment is either a bendpoint (attached to the given point)
* or segment (attached to a location on a line segment) attachment:
*
* ```javascript
* var pointAttachment = {
* type: 'bendpoint',
* bendpointIndex: 3,
* position: { x: 10, y: 10 } // the attach point on the line
* };
*
* var segmentAttachment = {
* type: 'segment',
* segmentIndex: 2,
* relativeLocation: 0.31, // attach point location between 0 (at start) and 1 (at end)
* position: { x: 10, y: 10 } // the attach point on the line
* };
* ```
*
* @param {Point} point
* @param {Array} line
*
* @return {Object} attachment
*/
function getAttachment(point, line) {
var idx = 0,
segmentStart,
segmentEnd,
segmentStartDistance,
segmentEndDistance,
attachmentPosition,
minDistance,
intersections,
attachment,
attachmentDistance,
closestAttachmentDistance,
closestAttachment;
for (idx = 0; idx < line.length - 1; idx++) {
segmentStart = line[idx];
segmentEnd = line[idx + 1];
if (pointsEqual(segmentStart, segmentEnd)) {
intersections = [ segmentStart ];
} else {
segmentStartDistance = getDistance(point, segmentStart);
segmentEndDistance = getDistance(point, segmentEnd);
minDistance = min(segmentStartDistance, segmentEndDistance);
intersections = getCircleSegmentIntersections(segmentStart, segmentEnd, point, minDistance);
}
if (intersections.length < 1) {
throw new Error('expected between [1, 2] circle -> line intersections');
}
// one intersection -> bendpoint attachment
if (intersections.length === 1) {
attachment = {
type: 'bendpoint',
position: intersections[0],
segmentIndex: idx,
bendpointIndex: pointsEqual(segmentStart, intersections[0]) ? idx : idx + 1
};
}
// two intersections -> segment attachment
if (intersections.length === 2) {
attachmentPosition = mid(intersections[0], intersections[1]);
attachment = {
type: 'segment',
position: attachmentPosition,
segmentIndex: idx,
relativeLocation: getDistance(segmentStart, attachmentPosition) / getDistance(segmentStart, segmentEnd)
};
}
attachmentDistance = getDistance(attachment.position, point);
if (!closestAttachment || closestAttachmentDistance > attachmentDistance) {
closestAttachment = attachment;
closestAttachmentDistance = attachmentDistance;
}
}
return closestAttachment;
}
module.exports.getAttachment = getAttachment;
/**
* Gets the intersection between a circle and a line segment.
*
* @param {Point} s1 segment start
* @param {Point} s2 segment end
* @param {Point} cc circle center
* @param {Number} cr circle radius
*
* @return {Array} intersections
*/
function getCircleSegmentIntersections(s1, s2, cc, cr) {
var baX = s2.x - s1.x;
var baY = s2.y - s1.y;
var caX = cc.x - s1.x;
var caY = cc.y - s1.y;
var a = baX * baX + baY * baY;
var bBy2 = baX * caX + baY * caY;
var c = caX * caX + caY * caY - cr * cr;
var pBy2 = bBy2 / a;
var q = c / a;
var disc = pBy2 * pBy2 - q;
// check against negative value to work around
// negative, very close to zero results (-4e-15)
// being produced in some environments
if (disc < 0 && disc > -0.000001) {
disc = 0;
}
if (disc < 0) {
return [];
}
// if disc == 0 ... dealt with later
var tmpSqrt = sqrt(disc);
var abScalingFactor1 = -pBy2 + tmpSqrt;
var abScalingFactor2 = -pBy2 - tmpSqrt;
var i1 = {
x: s1.x - baX * abScalingFactor1,
y: s1.y - baY * abScalingFactor1
};
if (disc === 0) { // abScalingFactor1 == abScalingFactor2
return [ i1 ];
}
var i2 = {
x: s1.x - baX * abScalingFactor2,
y: s1.y - baY * abScalingFactor2
};
// return only points on line segment
return [ i1, i2 ].filter(function(p) {
return isPointInSegment(p, s1, s2);
});
}
function isPointInSegment(p, segmentStart, segmentEnd) {
return (
fenced(p.x, segmentStart.x, segmentEnd.x) &&
fenced(p.y, segmentStart.y, segmentEnd.y)
);
}
function fenced(n, rangeStart, rangeEnd) {
// use matching threshold to work around
// precisison errors in intersection computation
return (
n >= min(rangeStart, rangeEnd) - EQUAL_THRESHOLD &&
n <= max(rangeStart, rangeEnd) + EQUAL_THRESHOLD
);
}
/**
* Calculate mid of two points.
*
* @param {Point} p1
* @param {Point} p2
*
* @return {Point}
*/
function mid(p1, p2) {
return {
x: (p1.x + p2.x) / 2,
y: (p1.y + p2.y) / 2
};
}
var EQUAL_THRESHOLD = 0.1;
function pointsEqual(p1, p2) {
return (
abs(p1.x - p2.x) <= EQUAL_THRESHOLD &&
abs(p1.y - p2.y) <= EQUAL_THRESHOLD
);
}
},{}],57:[function(_dereq_,module,exports){
'use strict';
/**
* Returns the intersection between two line segments a and b.
*
* @param {Point} l1s
* @param {Point} l1e
* @param {Point} l2s
* @param {Point} l2e
*
* @return {Point}
*/
module.exports = function lineIntersect(l1s, l1e, l2s, l2e) {
// if the lines intersect, the result contains the x and y of the
// intersection (treating the lines as infinite) and booleans for
// whether line segment 1 or line segment 2 contain the point
var denominator, a, b, c, numerator;
denominator = ((l2e.y - l2s.y) * (l1e.x - l1s.x)) - ((l2e.x - l2s.x) * (l1e.y - l1s.y));
if (denominator == 0) {
return null;
}
a = l1s.y - l2s.y;
b = l1s.x - l2s.x;
numerator = ((l2e.x - l2s.x) * a) - ((l2e.y - l2s.y) * b);
c = numerator / denominator;
// if we cast these lines infinitely in
// both directions, they intersect here
return {
x: Math.round(l1s.x + (c * (l1e.x - l1s.x))),
y: Math.round(l1s.y + (c * (l1e.y - l1s.y)))
};
};
},{}],58:[function(_dereq_,module,exports){
'use strict';
var filter = _dereq_(301);
var Elements = _dereq_(266);
var getLanesRoot = _dereq_(67).getLanesRoot,
getChildLanes = _dereq_(67).getChildLanes,
LANE_INDENTATION = _dereq_(67).LANE_INDENTATION;
/**
* A handler that allows us to add a new lane
* above or below an existing one.
*
* @param {Modeling} modeling
*/
function AddLaneHandler(modeling, spaceTool) {
this._modeling = modeling;
this._spaceTool = spaceTool;
}
AddLaneHandler.$inject = [ 'modeling', 'spaceTool' ];
module.exports = AddLaneHandler;
AddLaneHandler.prototype.preExecute = function(context) {
var spaceTool = this._spaceTool,
modeling = this._modeling;
var shape = context.shape,
location = context.location;
var lanesRoot = getLanesRoot(shape);
var isRoot = lanesRoot === shape,
laneParent = isRoot ? shape : shape.parent;
var existingChildLanes = getChildLanes(laneParent);
// (0) add a lane if we currently got none and are adding to root
if (!existingChildLanes.length) {
modeling.createShape({ type: 'bpmn:Lane' }, {
x: shape.x + LANE_INDENTATION,
y: shape.y,
width: shape.width - LANE_INDENTATION,
height: shape.height
}, laneParent);
}
// (1) collect affected elements to create necessary space
var allAffected = [];
Elements.eachElement(lanesRoot, function(element) {
allAffected.push(element);
if (element === shape) {
return [];
}
return filter(element.children, function(c) {
return c !== shape;
});
});
var offset = location === 'top' ? -120 : 120,
lanePosition = location === 'top' ? shape.y : shape.y + shape.height,
spacePos = lanePosition + (location === 'top' ? 10 : -10),
direction = location === 'top' ? 'n' : 's';
var adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: offset }, direction);
// (2) create new lane at open space
context.newLane = modeling.createShape({ type: 'bpmn:Lane' }, {
x: shape.x + (isRoot ? LANE_INDENTATION : 0),
y: lanePosition - (location === 'top' ? 120 : 0),
width: shape.width - (isRoot ? LANE_INDENTATION : 0),
height: 120
}, laneParent);
};
},{"266":266,"301":301,"67":67}],59:[function(_dereq_,module,exports){
'use strict';
function IdClaimHandler(moddle) {
this._moddle = moddle;
}
IdClaimHandler.$inject = [ 'moddle' ];
module.exports = IdClaimHandler;
IdClaimHandler.prototype.execute = function(context) {
var ids = this._moddle.ids,
id = context.id,
element = context.element,
claiming = context.claiming;
if (claiming) {
ids.claim(id, element);
} else {
ids.unclaim(id);
}
};
/**
* Command revert implementation.
*/
IdClaimHandler.prototype.revert = function(context) {
var ids = this._moddle.ids,
id = context.id,
element = context.element,
claiming = context.claiming;
if (claiming) {
ids.unclaim(id);
} else {
ids.claim(id, element);
}
};
},{}],60:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
var getLanesRoot = _dereq_(67).getLanesRoot,
computeLanesResize = _dereq_(67).computeLanesResize;
var eachElement = _dereq_(266).eachElement;
var asTRBL = _dereq_(252).asTRBL,
substractTRBL = _dereq_(223).substractTRBL;
/**
* A handler that resizes a lane.
*
* @param {Modeling} modeling
*/
function ResizeLaneHandler(modeling, spaceTool) {
this._modeling = modeling;
this._spaceTool = spaceTool;
}
ResizeLaneHandler.$inject = [ 'modeling', 'spaceTool' ];
module.exports = ResizeLaneHandler;
ResizeLaneHandler.prototype.preExecute = function(context) {
var shape = context.shape,
newBounds = context.newBounds,
balanced = context.balanced;
if (balanced !== false) {
this.resizeBalanced(shape, newBounds);
} else {
this.resizeSpace(shape, newBounds);
}
};
/**
* Resize balanced, adjusting next / previous lane sizes.
*
* @param {djs.model.Shape} shape
* @param {Bounds} newBounds
*/
ResizeLaneHandler.prototype.resizeBalanced = function(shape, newBounds) {
var modeling = this._modeling;
var resizeNeeded = computeLanesResize(shape, newBounds);
// resize the lane
modeling.resizeShape(shape, newBounds);
// resize other lanes as needed
resizeNeeded.forEach(function(r) {
modeling.resizeShape(r.shape, r.newBounds);
});
};
/**
* Resize, making actual space and moving below / above elements.
*
* @param {djs.model.Shape} shape
* @param {Bounds} newBounds
*/
ResizeLaneHandler.prototype.resizeSpace = function(shape, newBounds) {
var spaceTool = this._spaceTool;
var shapeTrbl = asTRBL(shape),
newTrbl = asTRBL(newBounds);
var trblDiff = substractTRBL(newTrbl, shapeTrbl);
var lanesRoot = getLanesRoot(shape);
var allAffected = [],
allLanes = [];
eachElement(lanesRoot, function(element) {
allAffected.push(element);
if (is(element, 'bpmn:Lane') || is(element, 'bpmn:Participant')) {
allLanes.push(element);
}
return element.children;
});
var change,
spacePos,
direction,
offset,
adjustments;
if (trblDiff.bottom || trblDiff.top) {
change = trblDiff.bottom || trblDiff.top;
spacePos = shape.y + (trblDiff.bottom ? shape.height : 0) + (trblDiff.bottom ? -10 : 10);
direction = trblDiff.bottom ? 's' : 'n';
offset = trblDiff.top > 0 || trblDiff.bottom < 0 ? -change : change;
adjustments = spaceTool.calculateAdjustments(allAffected, 'y', offset, spacePos);
spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: 0, y: change }, direction);
}
if (trblDiff.left || trblDiff.right) {
change = trblDiff.right || trblDiff.left;
spacePos = shape.x + (trblDiff.right ? shape.width : 0) + (trblDiff.right ? -10 : 100);
direction = trblDiff.right ? 'e' : 'w';
offset = trblDiff.left > 0 || trblDiff.right < 0 ? -change : change;
adjustments = spaceTool.calculateAdjustments(allLanes, 'x', offset, spacePos);
spaceTool.makeSpace(adjustments.movingShapes, adjustments.resizingShapes, { x: change, y: 0 }, direction);
}
};
},{"223":223,"252":252,"266":266,"67":67,"95":95}],61:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303);
function SetColorHandler(commandStack) {
this._commandStack = commandStack;
}
SetColorHandler.$inject = [ 'commandStack' ];
module.exports = SetColorHandler;
SetColorHandler.prototype.postExecute = function(context) {
var elements = context.elements,
colors = context.colors || { fill: undefined, stroke: undefined };
var that = this;
var di = {};
if ('fill' in colors) {
assign(di, { fill: colors.fill });
}
if ('stroke' in colors) {
assign(di, { stroke: colors.stroke });
}
forEach(elements, function(element) {
that._commandStack.execute('element.updateProperties', {
element: element,
properties: {
di: di
}
});
});
};
SetColorHandler.prototype.execute = function(context) {};
SetColorHandler.prototype.revert = function(context) {};
},{"303":303,"431":431}],62:[function(_dereq_,module,exports){
'use strict';
var getChildLanes = _dereq_(67).getChildLanes;
var LANE_INDENTATION = _dereq_(67).LANE_INDENTATION;
/**
* A handler that splits a lane into a number of sub-lanes,
* creating new sub lanes, if neccessary.
*
* @param {Modeling} modeling
*/
function SplitLaneHandler(modeling, translate) {
this._modeling = modeling;
this._translate = translate;
}
SplitLaneHandler.$inject = [ 'modeling', 'translate'];
module.exports = SplitLaneHandler;
SplitLaneHandler.prototype.preExecute = function(context) {
var modeling = this._modeling,
translate = this._translate;
var shape = context.shape,
newLanesCount = context.count;
var childLanes = getChildLanes(shape),
existingLanesCount = childLanes.length;
if (existingLanesCount > newLanesCount) {
throw new Error(translate('more than {count} child lanes', { count: newLanesCount }));
}
var newLanesHeight = Math.round(shape.height / newLanesCount);
// Iterate from top to bottom in child lane order,
// resizing existing lanes and creating new ones
// so that they split the parent proportionally.
//
// Due to rounding related errors, the bottom lane
// needs to take up all the remaining space.
var laneY,
laneHeight,
laneBounds,
newLaneAttrs,
idx;
for (idx = 0; idx < newLanesCount; idx++) {
laneY = shape.y + idx * newLanesHeight;
// if bottom lane
if (idx === newLanesCount - 1) {
laneHeight = shape.height - (newLanesHeight * idx);
} else {
laneHeight = newLanesHeight;
}
laneBounds = {
x: shape.x + LANE_INDENTATION,
y: laneY,
width: shape.width - LANE_INDENTATION,
height: laneHeight
};
if (idx < existingLanesCount) {
// resize existing lane
modeling.resizeShape(childLanes[idx], laneBounds);
} else {
// create a new lane at position
newLaneAttrs = {
type: 'bpmn:Lane'
};
modeling.createShape(newLaneAttrs, laneBounds, shape);
}
}
};
},{"67":67}],63:[function(_dereq_,module,exports){
'use strict';
var Collections = _dereq_(263);
function UpdateCanvasRootHandler(canvas, modeling) {
this._canvas = canvas;
this._modeling = modeling;
}
UpdateCanvasRootHandler.$inject = [ 'canvas', 'modeling' ];
module.exports = UpdateCanvasRootHandler;
UpdateCanvasRootHandler.prototype.execute = function(context) {
var canvas = this._canvas;
var newRoot = context.newRoot,
newRootBusinessObject = newRoot.businessObject,
oldRoot = canvas.getRootElement(),
oldRootBusinessObject = oldRoot.businessObject,
bpmnDefinitions = oldRootBusinessObject.$parent,
diPlane = oldRootBusinessObject.di;
// (1) replace process old <> new root
canvas.setRootElement(newRoot, true);
// (2) update root elements
Collections.add(bpmnDefinitions.rootElements, newRootBusinessObject);
newRootBusinessObject.$parent = bpmnDefinitions;
Collections.remove(bpmnDefinitions.rootElements, oldRootBusinessObject);
oldRootBusinessObject.$parent = null;
// (3) wire di
oldRootBusinessObject.di = null;
diPlane.bpmnElement = newRootBusinessObject;
newRootBusinessObject.di = diPlane;
context.oldRoot = oldRoot;
// TODO(nikku): return changed elements?
// return [ newRoot, oldRoot ];
};
UpdateCanvasRootHandler.prototype.revert = function(context) {
var canvas = this._canvas;
var newRoot = context.newRoot,
newRootBusinessObject = newRoot.businessObject,
oldRoot = context.oldRoot,
oldRootBusinessObject = oldRoot.businessObject,
bpmnDefinitions = newRootBusinessObject.$parent,
diPlane = newRootBusinessObject.di;
// (1) replace process old <> new root
canvas.setRootElement(oldRoot, true);
// (2) update root elements
Collections.remove(bpmnDefinitions.rootElements, newRootBusinessObject);
newRootBusinessObject.$parent = null;
Collections.add(bpmnDefinitions.rootElements, oldRootBusinessObject);
oldRootBusinessObject.$parent = bpmnDefinitions;
// (3) wire di
newRootBusinessObject.di = null;
diPlane.bpmnElement = oldRootBusinessObject;
oldRootBusinessObject.di = diPlane;
// TODO(nikku): return changed elements?
// return [ newRoot, oldRoot ];
};
},{"263":263}],64:[function(_dereq_,module,exports){
'use strict';
var collectLanes = _dereq_(67).collectLanes;
var getLanesRoot = _dereq_(67).getLanesRoot;
var is = _dereq_(95).is;
var Collections = _dereq_(263);
var asTRBL = _dereq_(252).asTRBL;
var FLOW_NODE_REFS_ATTR = 'flowNodeRef',
LANES_ATTR = 'lanes';
/**
* A handler that updates lane refs on changed elements
*/
function UpdateFlowNodeRefsHandler(elementRegistry) {
this._elementRegistry = elementRegistry;
}
UpdateFlowNodeRefsHandler.$inject = [ 'elementRegistry' ];
module.exports = UpdateFlowNodeRefsHandler;
UpdateFlowNodeRefsHandler.prototype.computeUpdates = function(flowNodeShapes, laneShapes) {
var handledNodes = {};
var updates = [];
var participantCache = {};
var allFlowNodeShapes = [];
function isInLaneShape(element, laneShape) {
var laneTrbl = asTRBL(laneShape);
var elementMid = {
x: element.x + element.width / 2,
y: element.y + element.height / 2
};
return elementMid.x > laneTrbl.left &&
elementMid.x < laneTrbl.right &&
elementMid.y > laneTrbl.top &&
elementMid.y < laneTrbl.bottom;
}
function addFlowNodeShape(flowNodeShape) {
if (!handledNodes[flowNodeShape.id]) {
allFlowNodeShapes.push(flowNodeShape);
handledNodes[flowNodeShape.id] = flowNodeShape;
}
}
function getAllLaneShapes(flowNodeShape) {
var root = getLanesRoot(flowNodeShape);
if (!participantCache[root.id]) {
participantCache[root.id] = collectLanes(root);
}
return participantCache[root.id];
}
function getNewLanes(flowNodeShape) {
if (!flowNodeShape.parent) {
return [];
}
var allLaneShapes = getAllLaneShapes(flowNodeShape);
return allLaneShapes.filter(function(l) {
return isInLaneShape(flowNodeShape, l);
}).map(function(shape) {
return shape.businessObject;
});
}
laneShapes.forEach(function(laneShape) {
var root = getLanesRoot(laneShape);
if (!root || handledNodes[root.id]) {
return;
}
var children = root.children.filter(function(c) {
return is(c, 'bpmn:FlowNode');
});
children.forEach(addFlowNodeShape);
handledNodes[root.id] = root;
});
flowNodeShapes.forEach(addFlowNodeShape);
allFlowNodeShapes.forEach(function(flowNodeShape) {
var flowNode = flowNodeShape.businessObject;
var lanes = flowNode.get(LANES_ATTR),
remove = lanes.slice(),
add = getNewLanes(flowNodeShape);
updates.push({ flowNode: flowNode, remove: remove, add: add });
});
laneShapes.forEach(function(laneShape) {
var lane = laneShape.businessObject;
// lane got removed XX-)
if (!laneShape.parent) {
lane.get(FLOW_NODE_REFS_ATTR).forEach(function(flowNode) {
updates.push({ flowNode: flowNode, remove: [ lane ], add: [] });
});
}
});
return updates;
};
UpdateFlowNodeRefsHandler.prototype.execute = function(context) {
var updates = context.updates;
if (!updates) {
updates = context.updates = this.computeUpdates(context.flowNodeShapes, context.laneShapes);
}
updates.forEach(function(update) {
var flowNode = update.flowNode,
lanes = flowNode.get(LANES_ATTR);
// unwire old
update.remove.forEach(function(oldLane) {
Collections.remove(lanes, oldLane);
Collections.remove(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
});
// wire new
update.add.forEach(function(newLane) {
Collections.add(lanes, newLane);
Collections.add(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
});
});
// TODO(nikku): return changed elements
// return [ ... ];
};
UpdateFlowNodeRefsHandler.prototype.revert = function(context) {
var updates = context.updates;
updates.forEach(function(update) {
var flowNode = update.flowNode,
lanes = flowNode.get(LANES_ATTR);
// unwire new
update.add.forEach(function(newLane) {
Collections.remove(lanes, newLane);
Collections.remove(newLane.get(FLOW_NODE_REFS_ATTR), flowNode);
});
// wire old
update.remove.forEach(function(oldLane) {
Collections.add(lanes, oldLane);
Collections.add(oldLane.get(FLOW_NODE_REFS_ATTR), flowNode);
});
});
// TODO(nikku): return changed elements
// return [ ... ];
};
},{"252":252,"263":263,"67":67,"95":95}],65:[function(_dereq_,module,exports){
'use strict';
var reduce = _dereq_(439),
keys = _dereq_(433),
forEach = _dereq_(303),
assign = _dereq_(431);
var getBusinessObject = _dereq_(95).getBusinessObject;
var TextUtil = _dereq_(280);
var DEFAULT_FLOW = 'default',
NAME = 'name',
ID = 'id',
DI = 'di';
var NULL_DIMENSIONS = {
width: 0,
height: 0
};
/**
* A handler that implements a BPMN 2.0 property update.
*
* This should be used to set simple properties on elements with
* an underlying BPMN business object.
*
* Use respective diagram-js provided handlers if you would
* like to perform automated modeling.
*/
function UpdatePropertiesHandler(elementRegistry, moddle, translate, modeling) {
this._elementRegistry = elementRegistry;
this._moddle = moddle;
this._translate = translate;
this._modeling = modeling;
this._textUtil = new TextUtil();
}
UpdatePropertiesHandler.$inject = [ 'elementRegistry', 'moddle', 'translate', 'modeling' ];
module.exports = UpdatePropertiesHandler;
////// api /////////////////////////////////////////////
/**
* Updates a BPMN element with a list of new properties
*
* @param {Object} context
* @param {djs.model.Base} context.element the element to update
* @param {Object} context.properties a list of properties to set on the element's
* businessObject (the BPMN model element)
*
* @return {Array} the updated element
*/
UpdatePropertiesHandler.prototype.execute = function(context) {
var element = context.element,
changed = [ element ],
translate = this._translate;
if (!element) {
throw new Error(translate('element required'));
}
var elementRegistry = this._elementRegistry,
ids = this._moddle.ids;
var businessObject = element.businessObject,
properties = unwrapBusinessObjects(context.properties),
oldProperties = context.oldProperties || getProperties(businessObject, properties);
if (isIdChange(properties, businessObject)) {
ids.unclaim(businessObject[ID]);
elementRegistry.updateId(element, properties[ID]);
ids.claim(properties[ID], businessObject);
}
// correctly indicate visual changes on default flow updates
if (DEFAULT_FLOW in properties) {
if (properties[DEFAULT_FLOW]) {
changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id));
}
if (businessObject[DEFAULT_FLOW]) {
changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id));
}
}
if (NAME in properties && element.label) {
changed.push(element.label);
// show the label
element.label.hidden = !properties[NAME];
}
if (DI in properties && businessObject.di) {
setDiProperties(businessObject.di, properties.di);
}
// update properties
setProperties(businessObject, properties);
// store old values
context.oldProperties = oldProperties;
context.changed = changed;
// indicate changed on objects affected by the update
return changed;
};
UpdatePropertiesHandler.prototype.postExecute = function(context) {
var element = context.element,
label = element.label;
var text = label && getBusinessObject(label).name;
if (!text) {
return;
}
// get layouted text bounds and resize external
// external label accordingly
var newLabelBounds = getLayoutedBounds(label, text, this._textUtil);
this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS);
};
/**
* Reverts the update on a BPMN elements properties.
*
* @param {Object} context
*
* @return {djs.model.Base} the updated element
*/
UpdatePropertiesHandler.prototype.revert = function(context) {
var element = context.element,
properties = context.properties,
oldProperties = context.oldProperties,
businessObject = element.businessObject,
elementRegistry = this._elementRegistry,
ids = this._moddle.ids;
if (DI in oldProperties && businessObject.di) {
setDiProperties(businessObject.di, oldProperties.di);
}
// update properties
setProperties(businessObject, oldProperties);
if (isIdChange(properties, businessObject)) {
ids.unclaim(properties[ID]);
elementRegistry.updateId(element, oldProperties[ID]);
ids.claim(oldProperties[ID], businessObject);
}
return context.changed;
};
function isIdChange(properties, businessObject) {
return ID in properties && properties[ID] !== businessObject[ID];
}
function getProperties(businessObject, properties) {
var propertyNames = keys(properties);
return reduce(propertyNames, function(result, key) {
// handle DI seperately
if (key !== DI) {
result[key] = businessObject.get(key);
} else {
result[key] = getDiProperties(businessObject.di, keys(properties.di));
}
return result;
}, {});
}
function getDiProperties(di, propertyNames) {
return reduce(propertyNames, function(result, key) {
result[key] = di.get(key);
return result;
}, {});
}
function setProperties(businessObject, properties) {
forEach(properties, function(value, key) {
businessObject.set(key, value);
});
}
function setDiProperties(di, properties) {
forEach(properties, function(value, key) {
di.set(key, value);
});
}
var referencePropertyNames = [ 'default' ];
/**
* Make sure we unwrap the actual business object
* behind diagram element that may have been
* passed as arguments.
*
* @param {Object} properties
*
* @return {Object} unwrappedProps
*/
function unwrapBusinessObjects(properties) {
var unwrappedProps = assign({}, properties);
referencePropertyNames.forEach(function(name) {
if (name in properties) {
unwrappedProps[name] = getBusinessObject(unwrappedProps[name]);
}
});
return unwrappedProps;
}
// TODO(nikku): repeating code (search for )
var EXTERNAL_LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: '11px'
};
function getLayoutedBounds(bounds, text, textUtil) {
var layoutedLabelDimensions = textUtil.getDimensions(text, {
box: {
width: 90,
height: 30,
x: bounds.width / 2 + bounds.x,
y: bounds.height / 2 + bounds.y
},
style: EXTERNAL_LABEL_STYLE
});
// resize label shape to fit label text
return {
x: Math.round(bounds.x + bounds.width / 2 - layoutedLabelDimensions.width / 2),
y: Math.round(bounds.y),
width: Math.ceil(layoutedLabelDimensions.width),
height: Math.ceil(layoutedLabelDimensions.height)
};
}
},{"280":280,"303":303,"431":431,"433":433,"439":439,"95":95}],66:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'modeling', 'bpmnUpdater' ],
__depends__: [
_dereq_(53),
_dereq_(27),
_dereq_(82),
_dereq_(70),
_dereq_(80),
_dereq_(121),
_dereq_(244),
_dereq_(174),
_dereq_(135),
_dereq_(233),
_dereq_(147),
_dereq_(240)
],
bpmnFactory: [ 'type', _dereq_(28) ],
bpmnUpdater: [ 'type', _dereq_(30) ],
elementFactory: [ 'type', _dereq_(31) ],
modeling: [ 'type', _dereq_(32) ],
layouter: [ 'type', _dereq_(29) ],
connectionDocking: [ 'type', _dereq_(251) ]
};
},{"121":121,"135":135,"147":147,"174":174,"233":233,"240":240,"244":244,"251":251,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"53":53,"70":70,"80":80,"82":82}],67:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is;
var getParent = _dereq_(68).getParent;
var asTRBL = _dereq_(252).asTRBL,
substractTRBL = _dereq_(223).substractTRBL,
resizeTRBL = _dereq_(223).resizeTRBL;
var abs = Math.abs;
function getTRBLResize(oldBounds, newBounds) {
return substractTRBL(asTRBL(newBounds), asTRBL(oldBounds));
}
var LANE_PARENTS = [
'bpmn:Participant',
'bpmn:Process',
'bpmn:SubProcess'
];
var LANE_INDENTATION = 30;
module.exports.LANE_INDENTATION = LANE_INDENTATION;
/**
* Collect all lane shapes in the given paren
*
* @param {djs.model.Shape} shape
* @param {Array} [collectedShapes]
*
* @return {Array}
*/
function collectLanes(shape, collectedShapes) {
collectedShapes = collectedShapes || [];
shape.children.filter(function(s) {
if (is(s, 'bpmn:Lane')) {
collectLanes(s, collectedShapes);
collectedShapes.push(s);
}
});
return collectedShapes;
}
module.exports.collectLanes = collectLanes;
/**
* Return the lane children of the given element.
*
* @param {djs.model.Shape} shape
*
* @return {Array}
*/
function getChildLanes(shape) {
return shape.children.filter(function(c) {
return is(c, 'bpmn:Lane');
});
}
module.exports.getChildLanes = getChildLanes;
/**
* Return the root element containing the given lane shape
*
* @param {djs.model.Shape} shape
*
* @return {djs.model.Shape}
*/
function getLanesRoot(shape) {
return getParent(shape, LANE_PARENTS) || shape;
}
module.exports.getLanesRoot = getLanesRoot;
/**
* Compute the required resize operations for lanes
* adjacent to the given shape, assuming it will be
* resized to the given new bounds.
*
* @param {djs.model.Shape} shape
* @param {Bounds} newBounds
*
* @return {Array}
*/
function computeLanesResize(shape, newBounds) {
var rootElement = getLanesRoot(shape);
var initialShapes = is(rootElement, 'bpmn:Process') ? [] : [ rootElement ];
var allLanes = collectLanes(rootElement, initialShapes),
shapeTrbl = asTRBL(shape),
shapeNewTrbl = asTRBL(newBounds),
trblResize = getTRBLResize(shape, newBounds),
resizeNeeded = [];
allLanes.forEach(function(other) {
if (other === shape) {
return;
}
var topResize = 0,
rightResize = trblResize.right,
bottomResize = 0,
leftResize = trblResize.left;
var otherTrbl = asTRBL(other);
if (trblResize.top) {
if (abs(otherTrbl.bottom - shapeTrbl.top) < 10) {
bottomResize = shapeNewTrbl.top - otherTrbl.bottom;
}
if (abs(otherTrbl.top - shapeTrbl.top) < 5) {
topResize = shapeNewTrbl.top - otherTrbl.top;
}
}
if (trblResize.bottom) {
if (abs(otherTrbl.top - shapeTrbl.bottom) < 10) {
topResize = shapeNewTrbl.bottom - otherTrbl.top;
}
if (abs(otherTrbl.bottom - shapeTrbl.bottom) < 5) {
bottomResize = shapeNewTrbl.bottom - otherTrbl.bottom;
}
}
if (topResize || rightResize || bottomResize || leftResize) {
resizeNeeded.push({
shape: other,
newBounds: resizeTRBL(other, {
top: topResize,
right: rightResize,
bottom: bottomResize,
left: leftResize
})
});
}
});
return resizeNeeded;
}
module.exports.computeLanesResize = computeLanesResize;
},{"223":223,"252":252,"68":68,"95":95}],68:[function(_dereq_,module,exports){
'use strict';
var any = _dereq_(299);
var is = _dereq_(95).is;
/**
* Return true if element has any of the given types.
*
* @param {djs.model.Base} element
* @param {Array} types
*
* @return {Boolean}
*/
function isAny(element, types) {
return any(types, function(t) {
return is(element, t);
});
}
module.exports.isAny = isAny;
/**
* Return the parent of the element with any of the given types.
*
* @param {djs.model.Base} element
* @param {String|Array} anyType
*
* @return {djs.model.Base}
*/
function getParent(element, anyType) {
if (typeof anyType === 'string') {
anyType = [ anyType ];
}
while ((element = element.parent)) {
if (isAny(element, anyType)) {
return element;
}
}
return null;
}
module.exports.getParent = getParent;
},{"299":299,"95":95}],69:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var OrderingProvider = _dereq_(207);
var isAny = _dereq_(68).isAny;
var findIndex = _dereq_(290);
var find = _dereq_(302);
/**
* a simple ordering provider that makes sure:
*
* (1) elements are ordered by a {level} property
* (2) elements with {alwaysOnTop} are always added to the root
*/
function BpmnOrderingProvider(eventBus, translate) {
OrderingProvider.call(this, eventBus);
var orders = [
{ type: 'bpmn:SubProcess', order: { level: 6 } },
{
type: 'bpmn:SequenceFlow',
order: {
level: 5,
containers: [
'bpmn:Participant',
'bpmn:FlowElementsContainer'
]
}
},
// handle DataAssociation(s) like message flows and render them always on top
{ type: 'bpmn:DataAssociation', order: { level: 9, containers: [ 'bpmn:Collaboration', 'bpmn:Process' ] } },
{ type: 'bpmn:MessageFlow', order: { level: 9, containers: [ 'bpmn:Collaboration' ] } },
{
type: 'bpmn:Association',
order: {
level: 6,
containers: [
'bpmn:Participant',
'bpmn:FlowElementsContainer',
'bpmn:Collaboration'
]
}
},
{ type: 'bpmn:BoundaryEvent', order: { level: 8 } },
{ type: 'bpmn:Participant', order: { level: -2 } },
{ type: 'bpmn:Lane', order: { level: -1 } }
];
function computeOrder(element) {
if (element.labelTarget) {
return { level: 10 };
}
var entry = find(orders, function(o) {
return isAny(element, [ o.type ]);
});
return entry && entry.order || { level: 1 };
}
function getOrder(element) {
var order = element.order;
if (!order) {
element.order = order = computeOrder(element);
}
return order;
}
function findActualParent(element, newParent, containers) {
var actualParent = newParent;
while (actualParent) {
if (isAny(actualParent, containers)) {
break;
}
actualParent = actualParent.parent;
}
if (!actualParent) {
throw new Error(translate('no parent for {element} in {parent}', {
element: element.id,
parent: newParent.id
}));
}
return actualParent;
}
this.getOrdering = function(element, newParent) {
var elementOrder = getOrder(element);
if (elementOrder.containers) {
newParent = findActualParent(element, newParent, elementOrder.containers);
}
var currentIndex = newParent.children.indexOf(element);
var insertIndex = findIndex(newParent.children, function(child) {
// do not compare with labels, they are created
// in the wrong order (right after elements) during import and
// mess up the positioning.
if (!element.labelTarget && child.labelTarget) {
return false;
}
return elementOrder.level < getOrder(child).level;
});
// if the element is already in the child list at
// a smaller index, we need to adjust the inser index.
// this takes into account that the element is being removed
// before being re-inserted
if (insertIndex !== -1) {
if (currentIndex !== -1 && currentIndex < insertIndex) {
insertIndex -= 1;
}
}
return {
index: insertIndex,
parent: newParent
};
};
}
BpmnOrderingProvider.$inject = [ 'eventBus', 'translate' ];
inherits(BpmnOrderingProvider, OrderingProvider);
module.exports = BpmnOrderingProvider;
},{"207":207,"289":289,"290":290,"302":302,"68":68}],70:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'bpmnOrderingProvider' ],
__depends__: [
_dereq_(248)
],
bpmnOrderingProvider: [ 'type', _dereq_(69) ]
};
},{"248":248,"69":69}],71:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
/**
* A palette provider for BPMN 2.0 elements.
*/
function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
this._palette = palette;
this._create = create;
this._elementFactory = elementFactory;
this._spaceTool = spaceTool;
this._lassoTool = lassoTool;
this._handTool = handTool;
this._globalConnect = globalConnect;
this._translate = translate;
palette.registerProvider(this);
}
module.exports = PaletteProvider;
PaletteProvider.$inject = [
'palette',
'create',
'elementFactory',
'spaceTool',
'lassoTool',
'handTool',
'globalConnect',
'translate'
];
PaletteProvider.prototype.getPaletteEntries = function(element) {
var actions = {},
create = this._create,
elementFactory = this._elementFactory,
spaceTool = this._spaceTool,
lassoTool = this._lassoTool,
handTool = this._handTool,
globalConnect = this._globalConnect,
translate = this._translate;
function createAction(type, group, className, title, options) {
function createListener(event) {
var shape = elementFactory.createShape(assign({ type: type }, options));
if (options) {
shape.businessObject.di.isExpanded = options.isExpanded;
}
create.start(event, shape);
}
var shortType = type.replace(/^bpmn\:/, '');
return {
group: group,
className: className,
title: title || translate('Create {type}', { type: shortType }),
action: {
dragstart: createListener,
click: createListener
}
};
}
function createParticipant(event, collapsed) {
create.start(event, elementFactory.createParticipantShape(collapsed));
}
assign(actions, {
'hand-tool': {
group: 'tools',
className: 'bpmn-icon-hand-tool',
title: translate('Activate the hand tool'),
action: {
click: function(event) {
handTool.activateHand(event);
}
}
},
'lasso-tool': {
group: 'tools',
className: 'bpmn-icon-lasso-tool',
title: translate('Activate the lasso tool'),
action: {
click: function(event) {
lassoTool.activateSelection(event);
}
}
},
'space-tool': {
group: 'tools',
className: 'bpmn-icon-space-tool',
title: translate('Activate the create/remove space tool'),
action: {
click: function(event) {
spaceTool.activateSelection(event);
}
}
},
'global-connect-tool': {
group: 'tools',
className: 'bpmn-icon-connection-multi',
title: translate('Activate the global connect tool'),
action: {
click: function(event) {
globalConnect.toggle(event);
}
}
},
'tool-separator': {
group: 'tools',
separator: true
},
'create.start-event': createAction(
'bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none'
),
'create.intermediate-event': createAction('bpmn:IntermediateThrowEvent', 'event',
'bpmn-icon-intermediate-event-none', translate('Create IntermediateThrowEvent/BoundaryEvent')
),
'create.end-event': createAction(
'bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none'
),
'create.exclusive-gateway': createAction(
'bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-xor'
),
'create.task': createAction(
'bpmn:Task', 'activity', 'bpmn-icon-task'
),
'create.data-object': createAction(
'bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object'
),
'create.data-store': createAction(
'bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store'
),
'create.subprocess-expanded': createAction(
'bpmn:SubProcess', 'activity', 'bpmn-icon-subprocess-expanded', translate('Create expanded SubProcess'),
{ isExpanded: true }
),
'create.participant-expanded': {
group: 'collaboration',
className: 'bpmn-icon-participant',
title: translate('Create Pool/Participant'),
action: {
dragstart: createParticipant,
click: createParticipant
}
}
});
return actions;
};
},{"431":431}],72:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(213),
_dereq_(157),
_dereq_(240),
_dereq_(176),
_dereq_(168),
_dereq_(248),
_dereq_(20)
],
__init__: [ 'paletteProvider' ],
paletteProvider: [ 'type', _dereq_(71) ]
};
},{"157":157,"168":168,"176":176,"20":20,"213":213,"240":240,"248":248,"71":71}],73:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is,
isEventSubProcess = _dereq_(93).isEventSubProcess,
getBusinessObject = _dereq_(95).getBusinessObject,
isExpanded = _dereq_(93).isExpanded,
isDifferentType = _dereq_(75).isDifferentType;
var forEach = _dereq_(303),
filter = _dereq_(301),
reject = _dereq_(307);
var replaceOptions = _dereq_(79);
/**
* This module is an element agnostic replace menu provider for the popup menu.
*/
function ReplaceMenuProvider(popupMenu, modeling, moddle, bpmnReplace, rules, translate) {
this._popupMenu = popupMenu;
this._modeling = modeling;
this._moddle = moddle;
this._bpmnReplace = bpmnReplace;
this._rules = rules;
this._translate = translate;
this.register();
}
ReplaceMenuProvider.$inject = [ 'popupMenu', 'modeling', 'moddle', 'bpmnReplace', 'rules', 'translate' ];
/**
* Register replace menu provider in the popup menu
*/
ReplaceMenuProvider.prototype.register = function() {
this._popupMenu.registerProvider('bpmn-replace', this);
};
/**
* Get all entries from replaceOptions for the given element and apply filters
* on them. Get for example only elements, which are different from the current one.
*
* @param {djs.model.Base} element
*
* @return {Array} a list of menu entry items
*/
ReplaceMenuProvider.prototype.getEntries = function(element) {
var businessObject = element.businessObject;
var rules = this._rules;
var entries;
if (!rules.allowed('shape.replace', { element: element })) {
return [];
}
var differentType = isDifferentType(element);
// start events outside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && !isEventSubProcess(businessObject.$parent)) {
entries = filter(replaceOptions.START_EVENT, differentType);
return this._createEntries(element, entries);
}
// expanded/collapsed pools
if (is(businessObject, 'bpmn:Participant')) {
entries = filter(replaceOptions.PARTICIPANT, function(entry) {
return isExpanded(businessObject) !== entry.target.isExpanded;
});
return this._createEntries(element, entries);
}
// start events inside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
entries = filter(replaceOptions.EVENT_SUB_PROCESS_START_EVENT, function(entry) {
var target = entry.target;
var isInterrupting = target.isInterrupting !== false;
var isInterruptingEqual = getBusinessObject(element).isInterrupting === isInterrupting;
// filters elements which types and event definition are equal but have have different interrupting types
return differentType(entry) || !differentType(entry) && !isInterruptingEqual;
});
return this._createEntries(element, entries);
}
// end events
if (is(businessObject, 'bpmn:EndEvent')) {
entries = filter(replaceOptions.END_EVENT, function(entry) {
var target = entry.target;
// hide cancel end events outside transactions
if (target.eventDefinitionType == 'bpmn:CancelEventDefinition' && !is(businessObject.$parent, 'bpmn:Transaction')) {
return false;
}
return differentType(entry);
});
return this._createEntries(element, entries);
}
// boundary events
if (is(businessObject, 'bpmn:BoundaryEvent')) {
entries = filter(replaceOptions.BOUNDARY_EVENT, function(entry) {
var target = entry.target;
if (target.eventDefinition == 'bpmn:CancelEventDefinition' &&
!is(businessObject.attachedToRef, 'bpmn:Transaction')) {
return false;
}
var cancelActivity = target.cancelActivity !== false;
var isCancelActivityEqual = businessObject.cancelActivity == cancelActivity;
return differentType(entry) || !differentType(entry) && !isCancelActivityEqual;
});
return this._createEntries(element, entries);
}
// intermediate events
if (is(businessObject, 'bpmn:IntermediateCatchEvent') ||
is(businessObject, 'bpmn:IntermediateThrowEvent')) {
entries = filter(replaceOptions.INTERMEDIATE_EVENT, differentType);
return this._createEntries(element, entries);
}
// gateways
if (is(businessObject, 'bpmn:Gateway')) {
entries = filter(replaceOptions.GATEWAY, differentType);
return this._createEntries(element, entries);
}
// transactions
if (is(businessObject, 'bpmn:Transaction')) {
entries = filter(replaceOptions.TRANSACTION, differentType);
return this._createEntries(element, entries);
}
// expanded event sub processes
if (isEventSubProcess(businessObject) && isExpanded(businessObject)) {
entries = filter(replaceOptions.EVENT_SUB_PROCESS, differentType);
return this._createEntries(element, entries);
}
// expanded sub processes
if (is(businessObject, 'bpmn:SubProcess') && isExpanded(businessObject)) {
entries = filter(replaceOptions.SUBPROCESS_EXPANDED, differentType);
return this._createEntries(element, entries);
}
// collapsed ad hoc sub processes
if (is(businessObject, 'bpmn:AdHocSubProcess') && !isExpanded(businessObject)) {
entries = filter(replaceOptions.TASK, function(entry) {
var target = entry.target;
var isTargetSubProcess = target.type === 'bpmn:SubProcess';
var isTargetExpanded = target.isExpanded === true;
return isDifferentType(element, target) && ( !isTargetSubProcess || isTargetExpanded );
});
return this._createEntries(element, entries);
}
// sequence flows
if (is(businessObject, 'bpmn:SequenceFlow')) {
return this._createSequenceFlowEntries(element, replaceOptions.SEQUENCE_FLOW);
}
// flow nodes
if (is(businessObject, 'bpmn:FlowNode')) {
entries = filter(replaceOptions.TASK, differentType);
// collapsed SubProcess can not be replaced with itself
if (is(businessObject, 'bpmn:SubProcess') && !isExpanded(businessObject)) {
entries = reject(entries, function(entry) {
return entry.label === 'Sub Process (collapsed)';
});
}
return this._createEntries(element, entries);
}
return [];
};
/**
* Get a list of header items for the given element. This includes buttons
* for multi instance markers and for the ad hoc marker.
*
* @param {djs.model.Base} element
*
* @return {Array} a list of menu entry items
*/
ReplaceMenuProvider.prototype.getHeaderEntries = function(element) {
var headerEntries = [];
if (is(element, 'bpmn:Activity') && !isEventSubProcess(element)) {
headerEntries = headerEntries.concat(this._getLoopEntries(element));
}
if (is(element, 'bpmn:SubProcess') &&
!is(element, 'bpmn:Transaction') &&
!isEventSubProcess(element)) {
headerEntries.push(this._getAdHocEntry(element));
}
return headerEntries;
};
/**
* Creates an array of menu entry objects for a given element and filters the replaceOptions
* according to a filter function.
*
* @param {djs.model.Base} element
* @param {Object} replaceOptions
*
* @return {Array} a list of menu items
*/
ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions) {
var menuEntries = [];
var self = this;
forEach(replaceOptions, function(definition) {
var entry = self._createMenuEntry(definition, element);
menuEntries.push(entry);
});
return menuEntries;
};
/**
* Creates an array of menu entry objects for a given sequence flow.
*
* @param {djs.model.Base} element
* @param {Object} replaceOptions
* @return {Array} a list of menu items
*/
ReplaceMenuProvider.prototype._createSequenceFlowEntries = function(element, replaceOptions) {
var businessObject = getBusinessObject(element);
var menuEntries = [];
var modeling = this._modeling,
moddle = this._moddle;
var self = this;
forEach(replaceOptions, function(entry) {
switch (entry.actionName) {
case 'replace-with-default-flow':
if (businessObject.sourceRef.default !== businessObject &&
(is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
is(businessObject.sourceRef, 'bpmn:Activity'))) {
menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element.source, { default: businessObject });
}));
}
break;
case 'replace-with-conditional-flow':
if (!businessObject.conditionExpression && is(businessObject.sourceRef, 'bpmn:Activity')) {
menuEntries.push(self._createMenuEntry(entry, element, function() {
var conditionExpression = moddle.create('bpmn:FormalExpression', { body: '' });
modeling.updateProperties(element, { conditionExpression: conditionExpression });
}));
}
break;
default:
// default flows
if (is(businessObject.sourceRef, 'bpmn:Activity') && businessObject.conditionExpression) {
return menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element, { conditionExpression: undefined });
}));
}
// conditional flows
if ((is(businessObject.sourceRef, 'bpmn:ExclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:InclusiveGateway') ||
is(businessObject.sourceRef, 'bpmn:ComplexGateway') ||
is(businessObject.sourceRef, 'bpmn:Activity')) &&
businessObject.sourceRef.default === businessObject) {
return menuEntries.push(self._createMenuEntry(entry, element, function() {
modeling.updateProperties(element.source, { default: undefined });
}));
}
}
});
return menuEntries;
};
/**
* Creates and returns a single menu entry item.
*
* @param {Object} definition a single replace options definition object
* @param {djs.model.Base} element
* @param {Function} [action] an action callback function which gets called when
* the menu entry is being triggered.
*
* @return {Object} menu entry item
*/
ReplaceMenuProvider.prototype._createMenuEntry = function(definition, element, action) {
var translate = this._translate;
var replaceElement = this._bpmnReplace.replaceElement;
var replaceAction = function() {
return replaceElement(element, definition.target);
};
action = action || replaceAction;
var menuEntry = {
label: translate(definition.label),
className: definition.className,
id: definition.actionName,
action: action
};
return menuEntry;
};
/**
* Get a list of menu items containing buttons for multi instance markers
*
* @param {djs.model.Base} element
*
* @return {Array} a list of menu items
*/
ReplaceMenuProvider.prototype._getLoopEntries = function(element) {
var self = this;
var translate = this._translate;
function toggleLoopEntry(event, entry) {
var loopCharacteristics;
if (entry.active) {
loopCharacteristics = undefined;
} else {
loopCharacteristics = self._moddle.create(entry.options.loopCharacteristics);
if (entry.options.isSequential) {
loopCharacteristics.isSequential = entry.options.isSequential;
}
}
self._modeling.updateProperties(element, { loopCharacteristics: loopCharacteristics });
}
var businessObject = getBusinessObject(element),
loopCharacteristics = businessObject.loopCharacteristics;
var isSequential,
isLoop,
isParallel;
if (loopCharacteristics) {
isSequential = loopCharacteristics.isSequential;
isLoop = loopCharacteristics.isSequential === undefined;
isParallel = loopCharacteristics.isSequential !== undefined && !loopCharacteristics.isSequential;
}
var loopEntries = [
{
id: 'toggle-parallel-mi',
className: 'bpmn-icon-parallel-mi-marker',
title: translate('Parallel Multi Instance'),
active: isParallel,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: false
}
},
{
id: 'toggle-sequential-mi',
className: 'bpmn-icon-sequential-mi-marker',
title: translate('Sequential Multi Instance'),
active: isSequential,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:MultiInstanceLoopCharacteristics',
isSequential: true
}
},
{
id: 'toggle-loop',
className: 'bpmn-icon-loop-marker',
title: translate('Loop'),
active: isLoop,
action: toggleLoopEntry,
options: {
loopCharacteristics: 'bpmn:StandardLoopCharacteristics'
}
}
];
return loopEntries;
};
/**
* Get the menu items containing a button for the ad hoc marker
*
* @param {djs.model.Base} element
*
* @return {Object} a menu item
*/
ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
var translate = this._translate;
var businessObject = getBusinessObject(element);
var isAdHoc = is(businessObject, 'bpmn:AdHocSubProcess');
var replaceElement = this._bpmnReplace.replaceElement;
var adHocEntry = {
id: 'toggle-adhoc',
className: 'bpmn-icon-ad-hoc-marker',
title: translate('Ad-hoc'),
active: isAdHoc,
action: function(event, entry) {
if (isAdHoc) {
return replaceElement(element, { type: 'bpmn:SubProcess' });
} else {
return replaceElement(element, { type: 'bpmn:AdHocSubProcess' });
}
}
};
return adHocEntry;
};
module.exports = ReplaceMenuProvider;
},{"301":301,"303":303,"307":307,"75":75,"79":79,"93":93,"95":95}],74:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(215),
_dereq_(80)
],
__init__: [ 'replaceMenuProvider' ],
replaceMenuProvider: [ 'type', _dereq_(73) ]
};
},{"215":215,"73":73,"80":80}],75:[function(_dereq_,module,exports){
'use strict';
var getBusinessObject = _dereq_(95).getBusinessObject;
var isExpanded = _dereq_(93).isExpanded;
/**
* Returns true, if an element is from a different type
* than a target definition. Takes into account the type,
* event definition type and triggeredByEvent property.
*
* @param {djs.model.Base} element
*
* @return {Boolean}
*/
function isDifferentType(element) {
return function(entry) {
var target = entry.target;
var businessObject = getBusinessObject(element),
eventDefinition = businessObject.eventDefinitions && businessObject.eventDefinitions[0];
var isTypeEqual = businessObject.$type === target.type;
var isEventDefinitionEqual = (
(eventDefinition && eventDefinition.$type) === target.eventDefinitionType
);
var isTriggeredByEventEqual = (
businessObject.triggeredByEvent === target.triggeredByEvent
);
var isExpandedEqual = (
target.isExpanded === undefined ||
target.isExpanded === isExpanded(businessObject)
);
return !isTypeEqual || !isEventDefinitionEqual || !isTriggeredByEventEqual || !isExpandedEqual;
};
}
module.exports.isDifferentType = isDifferentType;
},{"93":93,"95":95}],76:[function(_dereq_,module,exports){
'use strict';
var CommandInterceptor = _dereq_(119);
var inherits = _dereq_(289);
var assign = _dereq_(431),
forEach = _dereq_(303);
var domQuery = _dereq_(452);
var svgAttr = _dereq_(474);
var LOW_PRIORITY = 250;
function BpmnReplacePreview(eventBus, elementRegistry, elementFactory, canvas, previewSupport) {
CommandInterceptor.call(this, eventBus);
/**
* Replace the visuals of all elements in the context which can be replaced
*
* @param {Object} context
*/
function replaceVisual(context) {
var replacements = context.canExecute.replacements;
forEach(replacements, function(replacement) {
var id = replacement.oldElementId;
var newElement = {
type: replacement.newElementType
};
// if the visual of the element is already replaced
if (context.visualReplacements[id]) {
return;
}
var element = elementRegistry.get(id);
assign(newElement, { x: element.x, y: element.y });
// create a temporary shape
var tempShape = elementFactory.createShape(newElement);
canvas.addShape(tempShape, element.parent);
// select the original SVG element related to the element and hide it
var gfx = domQuery('[data-element-id=' + element.id + ']', context.dragGroup);
if (gfx) {
svgAttr(gfx, { display: 'none' });
}
// clone the gfx of the temporary shape and add it to the drag group
var dragger = previewSupport.addDragger(tempShape, context.dragGroup);
context.visualReplacements[id] = dragger;
canvas.removeShape(tempShape);
});
}
/**
* Restore the original visuals of the previously replaced elements
*
* @param {Object} context
*/
function restoreVisual(context) {
var visualReplacements = context.visualReplacements;
forEach(visualReplacements, function(dragger, id) {
var originalGfx = domQuery('[data-element-id=' + id + ']', context.dragGroup);
if (originalGfx) {
svgAttr(originalGfx, { display: 'inline' });
}
dragger.remove();
if (visualReplacements[id]) {
delete visualReplacements[id];
}
});
}
eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
var context = event.context,
canExecute = context.canExecute;
if (!context.visualReplacements) {
context.visualReplacements = {};
}
if (canExecute.replacements) {
replaceVisual(context);
} else {
restoreVisual(context);
}
});
}
BpmnReplacePreview.$inject = [ 'eventBus', 'elementRegistry', 'elementFactory', 'canvas', 'previewSupport' ];
inherits(BpmnReplacePreview, CommandInterceptor);
module.exports = BpmnReplacePreview;
},{"119":119,"289":289,"303":303,"431":431,"452":452,"474":474}],77:[function(_dereq_,module,exports){
module.exports = {
__depends__: [ _dereq_(217) ],
__init__: [ 'bpmnReplacePreview' ],
bpmnReplacePreview: [ 'type', _dereq_(76) ]
};
},{"217":217,"76":76}],78:[function(_dereq_,module,exports){
'use strict';
var pick = _dereq_(438),
assign = _dereq_(431),
intersection = _dereq_(292),
filter = _dereq_(301),
has = _dereq_(432);
var is = _dereq_(95).is,
isExpanded = _dereq_(93).isExpanded,
isEventSubProcess = _dereq_(93).isEventSubProcess,
ModelCloneUtils = _dereq_(98),
getProperties = ModelCloneUtils.getProperties;
var IGNORED_PROPERTIES = ModelCloneUtils.IGNORED_PROPERTIES;
var ModelCloneHelper = _dereq_(97);
var CUSTOM_PROPERTIES = [
'cancelActivity',
'instantiate',
'eventGatewayType',
'triggeredByEvent',
'isInterrupting'
];
function toggeling(element, target) {
var oldCollapsed = has(element, 'collapsed') ?
element.collapsed : !isExpanded(element);
var targetCollapsed;
if (has(target, 'collapsed') || has(target, 'isExpanded')) {
// property is explicitly set so use it
targetCollapsed = has(target, 'collapsed') ?
target.collapsed : !target.isExpanded;
} else {
// keep old state
targetCollapsed = oldCollapsed;
}
if (oldCollapsed !== targetCollapsed) {
element.collapsed = oldCollapsed;
return true;
}
return false;
}
/**
* This module takes care of replacing BPMN elements
*/
function BpmnReplace(bpmnFactory, replace, selection, modeling, eventBus) {
var helper = new ModelCloneHelper(eventBus);
/**
* Prepares a new business object for the replacement element
* and triggers the replace operation.
*
* @param {djs.model.Base} element
* @param {Object} target
* @param {Object} [hints]
*
* @return {djs.model.Base} the newly created element
*/
function replaceElement(element, target, hints) {
hints = hints || {};
var type = target.type,
oldBusinessObject = element.businessObject;
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
if (type === 'bpmn:SubProcess') {
if (toggeling(element, target)) {
// expanding or collapsing process
modeling.toggleCollapse(element);
return element;
}
}
}
var newBusinessObject = bpmnFactory.create(type);
var newElement = {
type: type,
businessObject: newBusinessObject
};
var elementProps = getProperties(oldBusinessObject.$descriptor),
newElementProps = getProperties(newBusinessObject.$descriptor, true),
properties = intersection(elementProps, newElementProps);
// initialize special properties defined in target definition
assign(newBusinessObject, pick(target, CUSTOM_PROPERTIES));
properties = filter(properties, function(property) {
var propName = property.replace(/bpmn:/, '');
// so the applied properties from 'target' don't get lost
if (newBusinessObject[property] !== undefined) {
return false;
}
// retain loop characteristics if the target element is not an event sub process
if (propName === 'loopCharacteristics') {
return !isEventSubProcess(newBusinessObject);
}
if ((propName === 'processRef' && target.isExpanded === false) ||
propName === 'triggeredByEvent' ||
propName === 'eventDefinitions') {
return false;
}
return IGNORED_PROPERTIES.indexOf(propName) === -1;
});
newBusinessObject = helper.clone(oldBusinessObject, newBusinessObject, properties);
// initialize custom BPMN extensions
if (target.eventDefinitionType) {
newElement.eventDefinitionType = target.eventDefinitionType;
}
if (is(oldBusinessObject, 'bpmn:Activity')) {
if (is(oldBusinessObject, 'bpmn:SubProcess')) {
// no toggeling, so keep old state
newElement.isExpanded = isExpanded(oldBusinessObject);
}
// else if property is explicitly set, use it
else if (has(target, 'isExpanded')) {
newElement.isExpanded = target.isExpanded;
}
// TODO: need also to respect min/max Size
// copy size, from an expanded subprocess to an expanded alternative subprocess
// except bpmn:Task, because Task is always expanded
if ((isExpanded(oldBusinessObject) && !is(oldBusinessObject, 'bpmn:Task')) && newElement.isExpanded) {
newElement.width = element.width;
newElement.height = element.height;
}
}
// transform collapsed/expanded pools
if (is(oldBusinessObject, 'bpmn:Participant')) {
// create expanded pool
if (target.isExpanded === true) {
newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
} else {
// remove children when transforming to collapsed pool
hints.moveChildren = false;
}
// apply same size
newElement.width = element.width;
newElement.height = element.height;
}
newBusinessObject.name = oldBusinessObject.name;
// retain default flow's reference between inclusive <-> exclusive gateways and activities
if ((is(oldBusinessObject, 'bpmn:ExclusiveGateway') || is(oldBusinessObject, 'bpmn:InclusiveGateway') ||
is(oldBusinessObject, 'bpmn:Activity')) &&
(is(newBusinessObject, 'bpmn:ExclusiveGateway') || is(newBusinessObject, 'bpmn:InclusiveGateway') ||
is(newBusinessObject, 'bpmn:Activity')))
{
newBusinessObject.default = oldBusinessObject.default;
}
if ('fill' in oldBusinessObject.di || 'stroke' in oldBusinessObject.di) {
assign(newElement, { colors: pick(oldBusinessObject.di, [ 'fill', 'stroke' ]) });
}
newElement = replace.replaceElement(element, newElement, hints);
if (hints.select !== false) {
selection.select(newElement);
}
return newElement;
}
this.replaceElement = replaceElement;
}
BpmnReplace.$inject = [ 'bpmnFactory', 'replace', 'selection', 'modeling', 'eventBus' ];
module.exports = BpmnReplace;
},{"292":292,"301":301,"431":431,"432":432,"438":438,"93":93,"95":95,"97":97,"98":98}],79:[function(_dereq_,module,exports){
'use strict';
module.exports.START_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'bpmn-icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-none-intermediate-throwing',
className: 'bpmn-icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-none-end',
className: 'bpmn-icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message Start Event',
actionName: 'replace-with-message-start',
className: 'bpmn-icon-start-event-message',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Start Event',
actionName: 'replace-with-timer-start',
className: 'bpmn-icon-start-event-timer',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Conditional Start Event',
actionName: 'replace-with-conditional-start',
className: 'bpmn-icon-start-event-condition',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Signal Start Event',
actionName: 'replace-with-signal-start',
className: 'bpmn-icon-start-event-signal',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
}
];
module.exports.INTERMEDIATE_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'bpmn-icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-none-intermediate-throw',
className: 'bpmn-icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-none-end',
className: 'bpmn-icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message Intermediate Catch Event',
actionName: 'replace-with-message-intermediate-catch',
className: 'bpmn-icon-intermediate-event-catch-message',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Message Intermediate Throw Event',
actionName: 'replace-with-message-intermediate-throw',
className: 'bpmn-icon-intermediate-event-throw-message',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Intermediate Catch Event',
actionName: 'replace-with-timer-intermediate-catch',
className: 'bpmn-icon-intermediate-event-catch-timer',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Escalation Intermediate Throw Event',
actionName: 'replace-with-escalation-intermediate-throw',
className: 'bpmn-icon-intermediate-event-throw-escalation',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Conditional Intermediate Catch Event',
actionName: 'replace-with-conditional-intermediate-catch',
className: 'bpmn-icon-intermediate-event-catch-condition',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Link Intermediate Catch Event',
actionName: 'replace-with-link-intermediate-catch',
className: 'bpmn-icon-intermediate-event-catch-link',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinitionType: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Link Intermediate Throw Event',
actionName: 'replace-with-link-intermediate-throw',
className: 'bpmn-icon-intermediate-event-throw-link',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinitionType: 'bpmn:LinkEventDefinition'
}
},
{
label: 'Compensation Intermediate Throw Event',
actionName: 'replace-with-compensation-intermediate-throw',
className: 'bpmn-icon-intermediate-event-throw-compensation',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinitionType: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal Intermediate Catch Event',
actionName: 'replace-with-signal-intermediate-catch',
className: 'bpmn-icon-intermediate-event-catch-signal',
target: {
type: 'bpmn:IntermediateCatchEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Signal Intermediate Throw Event',
actionName: 'replace-with-signal-intermediate-throw',
className: 'bpmn-icon-intermediate-event-throw-signal',
target: {
type: 'bpmn:IntermediateThrowEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
}
];
module.exports.END_EVENT = [
{
label: 'Start Event',
actionName: 'replace-with-none-start',
className: 'bpmn-icon-start-event-none',
target: {
type: 'bpmn:StartEvent'
}
},
{
label: 'Intermediate Throw Event',
actionName: 'replace-with-none-intermediate-throw',
className: 'bpmn-icon-intermediate-event-none',
target: {
type: 'bpmn:IntermediateThrowEvent'
}
},
{
label: 'End Event',
actionName: 'replace-with-none-end',
className: 'bpmn-icon-end-event-none',
target: {
type: 'bpmn:EndEvent'
}
},
{
label: 'Message End Event',
actionName: 'replace-with-message-end',
className: 'bpmn-icon-end-event-message',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Escalation End Event',
actionName: 'replace-with-escalation-end',
className: 'bpmn-icon-end-event-escalation',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Error End Event',
actionName: 'replace-with-error-end',
className: 'bpmn-icon-end-event-error',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Cancel End Event',
actionName: 'replace-with-cancel-end',
className: 'bpmn-icon-end-event-cancel',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:CancelEventDefinition'
}
},
{
label: 'Compensation End Event',
actionName: 'replace-with-compensation-end',
className: 'bpmn-icon-end-event-compensation',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Signal End Event',
actionName: 'replace-with-signal-end',
className: 'bpmn-icon-end-event-signal',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Terminate End Event',
actionName: 'replace-with-terminate-end',
className: 'bpmn-icon-end-event-terminate',
target: {
type: 'bpmn:EndEvent',
eventDefinitionType: 'bpmn:TerminateEventDefinition'
}
}
];
module.exports.GATEWAY = [
{
label: 'Exclusive Gateway',
actionName: 'replace-with-exclusive-gateway',
className: 'bpmn-icon-gateway-xor',
target: {
type: 'bpmn:ExclusiveGateway'
}
},
{
label: 'Parallel Gateway',
actionName: 'replace-with-parallel-gateway',
className: 'bpmn-icon-gateway-parallel',
target: {
type: 'bpmn:ParallelGateway'
}
},
{
label: 'Inclusive Gateway',
actionName: 'replace-with-inclusive-gateway',
className: 'bpmn-icon-gateway-or',
target: {
type: 'bpmn:InclusiveGateway'
}
},
{
label: 'Complex Gateway',
actionName: 'replace-with-complex-gateway',
className: 'bpmn-icon-gateway-complex',
target: {
type: 'bpmn:ComplexGateway'
}
},
{
label: 'Event based Gateway',
actionName: 'replace-with-event-based-gateway',
className: 'bpmn-icon-gateway-eventbased',
target: {
type: 'bpmn:EventBasedGateway',
instantiate: false,
eventGatewayType: 'Exclusive'
}
}
// Gateways deactivated until https://github.com/bpmn-io/bpmn-js/issues/194
// {
// label: 'Event based instantiating Gateway',
// actionName: 'replace-with-exclusive-event-based-gateway',
// className: 'bpmn-icon-exclusive-event-based',
// target: {
// type: 'bpmn:EventBasedGateway'
// },
// options: {
// businessObject: { instantiate: true, eventGatewayType: 'Exclusive' }
// }
// },
// {
// label: 'Parallel Event based instantiating Gateway',
// actionName: 'replace-with-parallel-event-based-instantiate-gateway',
// className: 'bpmn-icon-parallel-event-based-instantiate-gateway',
// target: {
// type: 'bpmn:EventBasedGateway'
// },
// options: {
// businessObject: { instantiate: true, eventGatewayType: 'Parallel' }
// }
// }
];
module.exports.SUBPROCESS_EXPANDED = [
{
label: 'Transaction',
actionName: 'replace-with-transaction',
className: 'bpmn-icon-transaction',
target: {
type: 'bpmn:Transaction',
isExpanded: true
}
},
{
label: 'Event Sub Process',
actionName: 'replace-with-event-subprocess',
className: 'bpmn-icon-event-subprocess-expanded',
target: {
type: 'bpmn:SubProcess',
triggeredByEvent: true,
isExpanded: true
}
},
{
label: 'Sub Process (collapsed)',
actionName: 'replace-with-collapsed-subprocess',
className: 'bpmn-icon-subprocess-collapsed',
target: {
type: 'bpmn:SubProcess',
isExpanded: false
}
}
];
module.exports.TRANSACTION = [
{
label: 'Sub Process',
actionName: 'replace-with-subprocess',
className: 'bpmn-icon-subprocess-expanded',
target: {
type: 'bpmn:SubProcess',
isExpanded: true
}
},
{
label: 'Event Sub Process',
actionName: 'replace-with-event-subprocess',
className: 'bpmn-icon-event-subprocess-expanded',
target: {
type: 'bpmn:SubProcess',
triggeredByEvent: true,
isExpanded: true
}
}
];
module.exports.EVENT_SUB_PROCESS = [
{
label: 'Sub Process',
actionName: 'replace-with-subprocess',
className: 'bpmn-icon-subprocess-expanded',
target: {
type: 'bpmn:SubProcess',
isExpanded: true
}
},
{
label: 'Transaction',
actionName: 'replace-with-transaction',
className: 'bpmn-icon-transaction',
target: {
type: 'bpmn:Transaction',
isExpanded: true
}
}
];
module.exports.TASK = [
{
label: 'Task',
actionName: 'replace-with-task',
className: 'bpmn-icon-task',
target: {
type: 'bpmn:Task'
}
},
{
label: 'Send Task',
actionName: 'replace-with-send-task',
className: 'bpmn-icon-send',
target: {
type: 'bpmn:SendTask'
}
},
{
label: 'Receive Task',
actionName: 'replace-with-receive-task',
className: 'bpmn-icon-receive',
target: {
type: 'bpmn:ReceiveTask'
}
},
{
label: 'User Task',
actionName: 'replace-with-user-task',
className: 'bpmn-icon-user',
target: {
type: 'bpmn:UserTask'
}
},
{
label: 'Manual Task',
actionName: 'replace-with-manual-task',
className: 'bpmn-icon-manual',
target: {
type: 'bpmn:ManualTask'
}
},
{
label: 'Business Rule Task',
actionName: 'replace-with-rule-task',
className: 'bpmn-icon-business-rule',
target: {
type: 'bpmn:BusinessRuleTask'
}
},
{
label: 'Service Task',
actionName: 'replace-with-service-task',
className: 'bpmn-icon-service',
target: {
type: 'bpmn:ServiceTask'
}
},
{
label: 'Script Task',
actionName: 'replace-with-script-task',
className: 'bpmn-icon-script',
target: {
type: 'bpmn:ScriptTask'
}
},
{
label: 'Call Activity',
actionName: 'replace-with-call-activity',
className: 'bpmn-icon-call-activity',
target: {
type: 'bpmn:CallActivity'
}
},
{
label: 'Sub Process (collapsed)',
actionName: 'replace-with-collapsed-subprocess',
className: 'bpmn-icon-subprocess-collapsed',
target: {
type: 'bpmn:SubProcess',
isExpanded: false
}
},
{
label: 'Sub Process (expanded)',
actionName: 'replace-with-expanded-subprocess',
className: 'bpmn-icon-subprocess-expanded',
target: {
type: 'bpmn:SubProcess',
isExpanded: true
}
}
];
module.exports.BOUNDARY_EVENT = [
{
label: 'Message Boundary Event',
actionName: 'replace-with-message-boundary',
className: 'bpmn-icon-intermediate-event-catch-message',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Boundary Event',
actionName: 'replace-with-timer-boundary',
className: 'bpmn-icon-intermediate-event-catch-timer',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Escalation Boundary Event',
actionName: 'replace-with-escalation-boundary',
className: 'bpmn-icon-intermediate-event-catch-escalation',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Conditional Boundary Event',
actionName: 'replace-with-conditional-boundary',
className: 'bpmn-icon-intermediate-event-catch-condition',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Error Boundary Event',
actionName: 'replace-with-error-boundary',
className: 'bpmn-icon-intermediate-event-catch-error',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Cancel Boundary Event',
actionName: 'replace-with-cancel-boundary',
className: 'bpmn-icon-intermediate-event-catch-cancel',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:CancelEventDefinition'
}
},
{
label: 'Signal Boundary Event',
actionName: 'replace-with-signal-boundary',
className: 'bpmn-icon-intermediate-event-catch-signal',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Compensation Boundary Event',
actionName: 'replace-with-compensation-boundary',
className: 'bpmn-icon-intermediate-event-catch-compensation',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Message Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-message-boundary',
className: 'bpmn-icon-intermediate-event-catch-non-interrupting-message',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition',
cancelActivity: false
}
},
{
label: 'Timer Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-timer-boundary',
className: 'bpmn-icon-intermediate-event-catch-non-interrupting-timer',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition',
cancelActivity: false
}
},
{
label: 'Escalation Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-escalation-boundary',
className: 'bpmn-icon-intermediate-event-catch-non-interrupting-escalation',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition',
cancelActivity: false
}
},
{
label: 'Conditional Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-conditional-boundary',
className: 'bpmn-icon-intermediate-event-catch-non-interrupting-condition',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition',
cancelActivity: false
}
},
{
label: 'Signal Boundary Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-signal-boundary',
className: 'bpmn-icon-intermediate-event-catch-non-interrupting-signal',
target: {
type: 'bpmn:BoundaryEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition',
cancelActivity: false
}
}
];
module.exports.EVENT_SUB_PROCESS_START_EVENT = [
{
label: 'Message Start Event',
actionName: 'replace-with-message-start',
className: 'bpmn-icon-start-event-message',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition'
}
},
{
label: 'Timer Start Event',
actionName: 'replace-with-timer-start',
className: 'bpmn-icon-start-event-timer',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition'
}
},
{
label: 'Conditional Start Event',
actionName: 'replace-with-conditional-start',
className: 'bpmn-icon-start-event-condition',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition'
}
},
{
label: 'Signal Start Event',
actionName: 'replace-with-signal-start',
className: 'bpmn-icon-start-event-signal',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition'
}
},
{
label: 'Error Start Event',
actionName: 'replace-with-error-start',
className: 'bpmn-icon-start-event-error',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:ErrorEventDefinition'
}
},
{
label: 'Escalation Start Event',
actionName: 'replace-with-escalation-start',
className: 'bpmn-icon-start-event-escalation',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition'
}
},
{
label: 'Compensation Start Event',
actionName: 'replace-with-compensation-start',
className: 'bpmn-icon-start-event-compensation',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:CompensateEventDefinition'
}
},
{
label: 'Message Start Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-message-start',
className: 'bpmn-icon-start-event-non-interrupting-message',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:MessageEventDefinition',
isInterrupting: false
}
},
{
label: 'Timer Start Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-timer-start',
className: 'bpmn-icon-start-event-non-interrupting-timer',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:TimerEventDefinition',
isInterrupting: false
}
},
{
label: 'Conditional Start Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-conditional-start',
className: 'bpmn-icon-start-event-non-interrupting-condition',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:ConditionalEventDefinition',
isInterrupting: false
}
},
{
label: 'Signal Start Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-signal-start',
className: 'bpmn-icon-start-event-non-interrupting-signal',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:SignalEventDefinition',
isInterrupting: false
}
},
{
label: 'Escalation Start Event (non-interrupting)',
actionName: 'replace-with-non-interrupting-escalation-start',
className: 'bpmn-icon-start-event-non-interrupting-escalation',
target: {
type: 'bpmn:StartEvent',
eventDefinitionType: 'bpmn:EscalationEventDefinition',
isInterrupting: false
}
}
];
module.exports.SEQUENCE_FLOW = [
{
label: 'Sequence Flow',
actionName: 'replace-with-sequence-flow',
className: 'bpmn-icon-connection'
},
{
label: 'Default Flow',
actionName: 'replace-with-default-flow',
className: 'bpmn-icon-default-flow'
},
{
label: 'Conditional Flow',
actionName: 'replace-with-conditional-flow',
className: 'bpmn-icon-conditional-flow'
}
];
module.exports.PARTICIPANT = [
{
label: 'Expanded Pool',
actionName: 'replace-with-expanded-pool',
className: 'bpmn-icon-participant',
target: {
type: 'bpmn:Participant',
isExpanded: true
}
},
{
label: 'Collapsed Pool',
actionName: 'replace-with-collapsed-pool',
// TODO(@janstuemmel): maybe design new icon
className: 'bpmn-icon-lane',
target: {
type: 'bpmn:Participant',
isExpanded: false
}
}
];
},{}],80:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(219),
_dereq_(233)
],
bpmnReplace: [ 'type', _dereq_(78) ]
};
},{"219":219,"233":233,"78":78}],81:[function(_dereq_,module,exports){
'use strict';
var find = _dereq_(302),
any = _dereq_(299),
every = _dereq_(300),
filter = _dereq_(301),
forEach = _dereq_(303),
inherits = _dereq_(289);
var is = _dereq_(95).is,
isAny = _dereq_(68).isAny,
getBusinessObject = _dereq_(95).getBusinessObject,
isExpanded = _dereq_(93).isExpanded,
isEventSubProcess = _dereq_(93).isEventSubProcess,
isInterrupting = _dereq_(93).isInterrupting,
hasErrorEventDefinition = _dereq_(93).hasErrorEventDefinition,
hasEscalationEventDefinition = _dereq_(93).hasEscalationEventDefinition,
hasCompensateEventDefinition = _dereq_(93).hasCompensateEventDefinition;
var RuleProvider = _dereq_(225);
var isBoundaryAttachment = _dereq_(86).getBoundaryAttachment;
/**
* BPMN specific modeling rule
*/
function BpmnRules(eventBus) {
RuleProvider.call(this, eventBus);
}
inherits(BpmnRules, RuleProvider);
BpmnRules.$inject = [ 'eventBus' ];
module.exports = BpmnRules;
BpmnRules.prototype.init = function() {
this.addRule('connection.create', function(context) {
var source = context.source,
target = context.target;
return canConnect(source, target);
});
this.addRule('connection.reconnectStart', function(context) {
var connection = context.connection,
source = context.hover || context.source,
target = connection.target;
return canConnect(source, target, connection);
});
this.addRule('connection.reconnectEnd', function(context) {
var connection = context.connection,
source = connection.source,
target = context.hover || context.target;
return canConnect(source, target, connection);
});
this.addRule('connection.updateWaypoints', function(context) {
// OK! but visually ignore
return null;
});
this.addRule('shape.resize', function(context) {
var shape = context.shape,
newBounds = context.newBounds;
return canResize(shape, newBounds);
});
this.addRule('elements.move', function(context) {
var target = context.target,
shapes = context.shapes,
position = context.position;
return canAttach(shapes, target, null, position) ||
canReplace(shapes, target, position) ||
canMove(shapes, target, position) ||
canInsert(shapes, target, position);
});
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
var target = context.target,
shape = context.shape,
source = context.source,
position = context.position;
return canAttach([ shape ], target, source, position) || canCreate(shape, target, source, position);
});
this.addRule('element.copy', function(context) {
var collection = context.collection,
element = context.element;
return canCopy(collection, element);
});
this.addRule('element.paste', function(context) {
var parent = context.parent,
element = context.element,
position = context.position,
source = context.source,
target = context.target;
if (source || target) {
return canConnect(source, target);
}
return canAttach([ element ], parent, null, position) || canCreate(element, parent, null, position);
});
this.addRule('elements.paste', function(context) {
var tree = context.tree,
target = context.target;
return canPaste(tree, target);
});
this.addRule([ 'elements.delete' ], function(context) {
// do not allow deletion of labels
return filter(context.elements, function(e) {
return !isLabel(e);
});
});
};
BpmnRules.prototype.canConnectMessageFlow = canConnectMessageFlow;
BpmnRules.prototype.canConnectSequenceFlow = canConnectSequenceFlow;
BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
BpmnRules.prototype.canMove = canMove;
BpmnRules.prototype.canAttach = canAttach;
BpmnRules.prototype.canReplace = canReplace;
BpmnRules.prototype.canDrop = canDrop;
BpmnRules.prototype.canInsert = canInsert;
BpmnRules.prototype.canCreate = canCreate;
BpmnRules.prototype.canConnect = canConnect;
BpmnRules.prototype.canResize = canResize;
BpmnRules.prototype.canCopy = canCopy;
/**
* Utility functions for rule checking
*/
function nonExistantOrLabel(element) {
return !element || isLabel(element);
}
function isSame(a, b) {
return a === b;
}
function getOrganizationalParent(element) {
var bo = getBusinessObject(element);
while (bo && !is(bo, 'bpmn:Process')) {
if (is(bo, 'bpmn:Participant')) {
return bo.processRef || bo;
}
bo = bo.$parent;
}
return bo;
}
function isTextAnnotation(element) {
return is(element, 'bpmn:TextAnnotation');
}
function isCompensationBoundary(element) {
return is(element, 'bpmn:BoundaryEvent') &&
hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
}
function isForCompensation(e) {
return getBusinessObject(e).isForCompensation;
}
function isSameOrganization(a, b) {
var parentA = getOrganizationalParent(a),
parentB = getOrganizationalParent(b);
return parentA === parentB;
}
function isMessageFlowSource(element) {
return is(element, 'bpmn:InteractionNode') &&
!isForCompensation(element) && (
!is(element, 'bpmn:Event') || (
is(element, 'bpmn:ThrowEvent') &&
hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
)
);
}
function isMessageFlowTarget(element) {
return is(element, 'bpmn:InteractionNode') &&
!isForCompensation(element) && (
!is(element, 'bpmn:Event') || (
is(element, 'bpmn:CatchEvent') &&
hasEventDefinitionOrNone(element, 'bpmn:MessageEventDefinition')
)
);
}
function getScopeParent(element) {
var bo = getBusinessObject(element);
if (is(bo, 'bpmn:Participant')) {
return null;
}
while (bo) {
bo = bo.$parent;
if (is(bo, 'bpmn:FlowElementsContainer')) {
return bo;
}
}
return bo;
}
function isSameScope(a, b) {
var scopeParentA = getScopeParent(a),
scopeParentB = getScopeParent(b);
return scopeParentA && (scopeParentA === scopeParentB);
}
function hasEventDefinition(element, eventDefinition) {
var bo = getBusinessObject(element);
return !!find(bo.eventDefinitions || [], function(definition) {
return is(definition, eventDefinition);
});
}
function hasEventDefinitionOrNone(element, eventDefinition) {
var bo = getBusinessObject(element);
return (bo.eventDefinitions || []).every(function(definition) {
return is(definition, eventDefinition);
});
}
function isSequenceFlowSource(element) {
return is(element, 'bpmn:FlowNode') &&
!is(element, 'bpmn:EndEvent') &&
!isEventSubProcess(element) &&
!(is(element, 'bpmn:IntermediateThrowEvent') &&
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
) &&
!isCompensationBoundary(element) &&
!isForCompensation(element);
}
function isSequenceFlowTarget(element) {
return is(element, 'bpmn:FlowNode') &&
!is(element, 'bpmn:StartEvent') &&
!is(element, 'bpmn:BoundaryEvent') &&
!isEventSubProcess(element) &&
!(is(element, 'bpmn:IntermediateCatchEvent') &&
hasEventDefinition(element, 'bpmn:LinkEventDefinition')
) &&
!isForCompensation(element);
}
function isEventBasedTarget(element) {
return is(element, 'bpmn:ReceiveTask') || (
is(element, 'bpmn:IntermediateCatchEvent') && (
hasEventDefinition(element, 'bpmn:MessageEventDefinition') ||
hasEventDefinition(element, 'bpmn:TimerEventDefinition') ||
hasEventDefinition(element, 'bpmn:ConditionalEventDefinition') ||
hasEventDefinition(element, 'bpmn:SignalEventDefinition')
)
);
}
function isLabel(element) {
return element.labelTarget;
}
function isConnection(element) {
return element.waypoints;
}
function getParents(element) {
var parents = [];
while (element) {
element = element.parent;
if (element) {
parents.push(element);
}
}
return parents;
}
function isParent(possibleParent, element) {
var allParents = getParents(element);
return allParents.indexOf(possibleParent) !== -1;
}
function canConnect(source, target, connection) {
if (nonExistantOrLabel(source) || nonExistantOrLabel(target)) {
return null;
}
// See https://github.com/bpmn-io/bpmn-js/issues/178
// as a workround we disallow connections with same
// target and source element.
// This rule must be removed if a auto layout for this
// connections is implemented.
if (isSame(source, target)) {
return false;
}
if (!is(connection, 'bpmn:DataAssociation')) {
if (canConnectMessageFlow(source, target)) {
return { type: 'bpmn:MessageFlow' };
}
if (canConnectSequenceFlow(source, target)) {
return { type: 'bpmn:SequenceFlow' };
}
}
var connectDataAssociation = canConnectDataAssociation(source, target);
if (connectDataAssociation) {
return connectDataAssociation;
}
if (isCompensationBoundary(source) && isForCompensation(target)) {
return {
type: 'bpmn:Association',
associationDirection: 'One'
};
}
if (canConnectAssociation(source, target)) {
return {
type: 'bpmn:Association'
};
}
return false;
}
/**
* Can an element be dropped into the target element
*
* @return {Boolean}
*/
function canDrop(element, target, position) {
// can move labels everywhere
if (isLabel(element) && !isConnection(target)) {
return true;
}
// disallow to create elements on collapsed pools
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
return false;
}
// allow to create new participants on
// on existing collaboration and process diagrams
if (is(element, 'bpmn:Participant')) {
return is(target, 'bpmn:Process') || is(target, 'bpmn:Collaboration');
}
// allow creating lanes on participants and other lanes only
if (is(element, 'bpmn:Lane')) {
return is(target, 'bpmn:Participant') || is(target, 'bpmn:Lane');
}
if (is(element, 'bpmn:BoundaryEvent')) {
return false;
}
// drop flow elements onto flow element containers
// and participants
if (is(element, 'bpmn:FlowElement')) {
if (is(target, 'bpmn:FlowElementsContainer')) {
return isExpanded(target);
}
return isAny(target, [ 'bpmn:Participant', 'bpmn:Lane' ]);
}
// account for the fact that data associations are always
// rendered and moved to top (Process or Collaboration level)
//
// artifacts may be placed wherever, too
if (isAny(element, [ 'bpmn:Artifact', 'bpmn:DataAssociation' ])) {
return isAny(target, [
'bpmn:Collaboration',
'bpmn:Lane',
'bpmn:Participant',
'bpmn:Process',
'bpmn:SubProcess' ]);
}
if (is(element, 'bpmn:MessageFlow')) {
return is(target, 'bpmn:Collaboration')
|| element.source.parent == target
|| element.target.parent == target;
}
return false;
}
function canPaste(tree, target) {
var topLevel = tree[0],
participants;
if (is(target, 'bpmn:Collaboration')) {
return every(topLevel, function(e) {
return e.type === 'bpmn:Participant';
});
}
if (is(target, 'bpmn:Process')) {
participants = any(topLevel, function(e) {
return e.type === 'bpmn:Participant';
});
return !(participants && target.children.length > 0);
}
// disallow to create elements on collapsed pools
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
return false;
}
if (is(target, 'bpmn:FlowElementsContainer')) {
return isExpanded(target);
}
return isAny(target, [
'bpmn:Collaboration',
'bpmn:Lane',
'bpmn:Participant',
'bpmn:Process',
'bpmn:SubProcess' ]);
}
function isBoundaryEvent(element) {
return !isLabel(element) && is(element, 'bpmn:BoundaryEvent');
}
function isLane(element) {
return is(element, 'bpmn:Lane');
}
/**
* We treat IntermediateThrowEvents as boundary events during create,
* this must be reflected in the rules.
*/
function isBoundaryCandidate(element) {
return isBoundaryEvent(element) ||
(is(element, 'bpmn:IntermediateThrowEvent') && !element.parent);
}
function canAttach(elements, target, source, position) {
if (!Array.isArray(elements)) {
elements = [ elements ];
}
// disallow appending as boundary event
if (source) {
return false;
}
// only (re-)attach one element at a time
if (elements.length !== 1) {
return false;
}
var element = elements[0];
// do not attach labels
if (isLabel(element)) {
return false;
}
// only handle boundary events
if (!isBoundaryCandidate(element)) {
return false;
}
// allow default move operation
if (!target) {
return true;
}
// disallow drop on event sub processes
if (isEventSubProcess(target)) {
return false;
}
// only allow drop on non compensation activities
if (!is(target, 'bpmn:Activity') || isForCompensation(target)) {
return false;
}
// only attach to subprocess border
if (position && !isBoundaryAttachment(position, target)) {
return false;
}
return 'attach';
}
/**
* Defines how to replace elements for a given target.
*
* Returns an array containing all elements which will be replaced.
*
* @example
*
* [{ id: 'IntermediateEvent_2',
* type: 'bpmn:StartEvent'
* },
* { id: 'IntermediateEvent_5',
* type: 'bpmn:EndEvent'
* }]
*
* @param {Array} elements
* @param {Object} target
*
* @return {Object} an object containing all elements which have to be replaced
*/
function canReplace(elements, target, position) {
if (!target) {
return false;
}
var canExecute = {
replacements: []
};
forEach(elements, function(element) {
if (!isEventSubProcess(target)) {
if (is(element, 'bpmn:StartEvent') &&
element.type !== 'label' &&
canDrop(element, target)) {
// replace a non-interrupting start event by a blank interrupting start event
// when the target is not an event sub process
if (!isInterrupting(element)) {
canExecute.replacements.push({
oldElementId: element.id,
newElementType: 'bpmn:StartEvent'
});
}
// replace an error/escalation/compansate start event by a blank interrupting start event
// when the target is not an event sub process
if (hasErrorEventDefinition(element) ||
hasEscalationEventDefinition(element) ||
hasCompensateEventDefinition(element)) {
canExecute.replacements.push({
oldElementId: element.id,
newElementType: 'bpmn:StartEvent'
});
}
}
}
if (!is(target, 'bpmn:Transaction')) {
if (hasEventDefinition(element, 'bpmn:CancelEventDefinition') &&
element.type !== 'label') {
if (is(element, 'bpmn:EndEvent') && canDrop(element, target)) {
canExecute.replacements.push({
oldElementId: element.id,
newElementType: 'bpmn:EndEvent'
});
}
if (is(element, 'bpmn:BoundaryEvent') && canAttach(element, target, null, position)) {
canExecute.replacements.push({
oldElementId: element.id,
newElementType: 'bpmn:BoundaryEvent'
});
}
}
}
});
return canExecute.replacements.length ? canExecute : false;
}
function canMove(elements, target) {
// do not move selection containing boundary events
if (any(elements, isBoundaryEvent)) {
return false;
}
// do not move selection containing lanes
if (any(elements, isLane)) {
return false;
}
// allow default move check to start move operation
if (!target) {
return true;
}
return elements.every(function(element) {
return canDrop(element, target);
});
}
function canCreate(shape, target, source, position) {
if (!target) {
return false;
}
if (isLabel(target)) {
return null;
}
if (isSame(source, target)) {
return false;
}
// ensure we do not drop the element
// into source
if (source && isParent(source, target)) {
return false;
}
return canDrop(shape, target, position) || canInsert(shape, target, position);
}
function canResize(shape, newBounds) {
if (is(shape, 'bpmn:SubProcess')) {
return (!!isExpanded(shape)) && (
!newBounds || (newBounds.width >= 100 && newBounds.height >= 80)
);
}
if (is(shape, 'bpmn:Lane')) {
return !newBounds || (newBounds.width >= 130 && newBounds.height >= 60);
}
if (is(shape, 'bpmn:Participant')) {
return !newBounds || (newBounds.width >= 250 && newBounds.height >= 50);
}
if (isTextAnnotation(shape)) {
return true;
}
return false;
}
/**
* Check, whether one side of the relationship
* is a text annotation.
*/
function isOneTextAnnotation(source, target) {
var sourceTextAnnotation = isTextAnnotation(source),
targetTextAnnotation = isTextAnnotation(target);
return (
(sourceTextAnnotation || targetTextAnnotation) &&
(sourceTextAnnotation !== targetTextAnnotation)
);
}
function canConnectAssociation(source, target) {
// do not connect connections
if (isConnection(source) || isConnection(target)) {
return false;
}
// compensation boundary events are exception
if (isCompensationBoundary(source) && isForCompensation(target)) {
return true;
}
// don't connect parent <-> child
if (isParent(target, source) || isParent(source, target)) {
return false;
}
// allow connection of associations between and
return isOneTextAnnotation(source, target);
}
function canConnectMessageFlow(source, target) {
return isMessageFlowSource(source) &&
isMessageFlowTarget(target) &&
!isSameOrganization(source, target);
}
function canConnectSequenceFlow(source, target) {
return isSequenceFlowSource(source) &&
isSequenceFlowTarget(target) &&
isSameScope(source, target) &&
!(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
}
function canConnectDataAssociation(source, target) {
if (isAny(source, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
isAny(target, [ 'bpmn:Activity', 'bpmn:ThrowEvent' ])) {
return { type: 'bpmn:DataInputAssociation' };
}
if (isAny(target, [ 'bpmn:DataObjectReference', 'bpmn:DataStoreReference' ]) &&
isAny(source, [ 'bpmn:Activity', 'bpmn:CatchEvent' ])) {
return { type: 'bpmn:DataOutputAssociation' };
}
return false;
}
function canInsert(shape, flow, position) {
if (Array.isArray(shape)) {
if (shape.length !== 1) {
return false;
}
shape = shape[0];
}
// return true if we can drop on the
// underlying flow parent
//
// at this point we are not really able to talk
// about connection rules (yet)
return (
isAny(flow, [ 'bpmn:SequenceFlow', 'bpmn:MessageFlow' ]) &&
!isLabel(flow) &&
is(shape, 'bpmn:FlowNode') &&
!is(shape, 'bpmn:BoundaryEvent') &&
canDrop(shape, flow.parent, position));
}
function contains(collection, element) {
return (collection && element) && collection.indexOf(element) !== -1;
}
function canCopy(collection, element) {
if (is(element, 'bpmn:Lane') && !contains(collection, element.parent)) {
return false;
}
if (is(element, 'bpmn:BoundaryEvent') && !contains(collection, element.host)) {
return false;
}
return true;
}
},{"225":225,"289":289,"299":299,"300":300,"301":301,"302":302,"303":303,"68":68,"86":86,"93":93,"95":95}],82:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(227)
],
__init__: [ 'bpmnRules' ],
bpmnRules: [ 'type', _dereq_(81) ]
};
},{"227":227,"81":81}],83:[function(_dereq_,module,exports){
'use strict';
var map = _dereq_(305),
filter = _dereq_(301),
sortBy = _dereq_(310);
var labelUtil = _dereq_(25);
/**
* Provides ability to search through BPMN elements
*/
function BpmnSearchProvider(elementRegistry, searchPad, canvas) {
this._elementRegistry = elementRegistry;
this._canvas = canvas;
searchPad.registerProvider(this);
}
module.exports = BpmnSearchProvider;
BpmnSearchProvider.$inject = [
'elementRegistry',
'searchPad',
'canvas'
];
/**
* Finds all elements that match given pattern
*
* :
* {
* primaryTokens: >,
* secondaryTokens: >,
* element:
* }
*
* :
* {
* normal|matched:
* }
*
* @param {String} pattern
* @return {Array}
*/
BpmnSearchProvider.prototype.find = function(pattern) {
var rootElement = this._canvas.getRootElement();
var elements = this._elementRegistry.filter(function(element) {
if (element.labelTarget) {
return false;
}
return true;
});
// do not include root element
elements = filter(elements, function(element) {
return element !== rootElement;
});
elements = map(elements, function(element) {
return {
primaryTokens: matchAndSplit(labelUtil.getLabel(element), pattern),
secondaryTokens: matchAndSplit(element.id, pattern),
element: element
};
});
// exclude non-matched elements
elements = filter(elements, function(element) {
return hasMatched(element.primaryTokens) || hasMatched(element.secondaryTokens);
});
elements = sortBy(elements, function(element) {
return labelUtil.getLabel(element.element) + element.element.id;
});
return elements;
};
function hasMatched(tokens) {
var matched = filter(tokens, function(t) {
return !!t.matched;
});
return matched.length > 0;
}
function matchAndSplit(text, pattern) {
var tokens = [],
originalText = text;
if (!text) {
return tokens;
}
text = text.toLowerCase();
pattern = pattern.toLowerCase();
var i = text.indexOf(pattern);
if (i > -1) {
if (i !== 0) {
tokens.push({
normal: originalText.substr(0, i)
});
}
tokens.push({
matched: originalText.substr(i, pattern.length)
});
if (pattern.length + i < text.length) {
tokens.push({
normal: originalText.substr(pattern.length + i, text.length)
});
}
} else {
tokens.push({
normal: originalText
});
}
return tokens;
}
},{"25":25,"301":301,"305":305,"310":310}],84:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(229)
],
__init__: [ 'bpmnSearch'],
bpmnSearch: [ 'type', _dereq_(83) ]
};
},{"229":229,"83":83}],85:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var abs = Math.abs;
var forEach = _dereq_(303),
filter = _dereq_(301),
assign = _dereq_(431);
var getBoundingBox = _dereq_(266).getBBox;
var is = _dereq_(95).is,
isAny = _dereq_(68).isAny,
isExpanded = _dereq_(93).isExpanded;
var Snapping = _dereq_(236),
SnapUtil = _dereq_(235);
var asTRBL = _dereq_(252).asTRBL;
var round = Math.round;
var mid = SnapUtil.mid,
topLeft = SnapUtil.topLeft,
bottomRight = SnapUtil.bottomRight,
isSnapped = SnapUtil.isSnapped,
setSnapped = SnapUtil.setSnapped;
var getBoundaryAttachment = _dereq_(86).getBoundaryAttachment,
getParticipantSizeConstraints = _dereq_(86).getParticipantSizeConstraints,
getLanesRoot = _dereq_(67).getLanesRoot;
var HIGH_PRIORITY = 1500;
/**
* BPMN specific snapping functionality
*
* * snap on process elements if a pool is created inside a
* process diagram
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
*/
function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistry) {
// instantiate super
Snapping.call(this, eventBus, canvas);
/**
* Drop participant on process <> process elements snapping
*/
eventBus.on('create.start', function(event) {
var context = event.context,
shape = context.shape,
rootElement = canvas.getRootElement();
// snap participant around existing elements (if any)
if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) {
initParticipantSnapping(context, shape, rootElement.children);
}
});
eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
participantSnapBox = context.participantSnapBox;
if (!isSnapped(event) && participantSnapBox) {
snapParticipant(participantSnapBox, shape, event);
}
});
eventBus.on('shape.move.start', function(event) {
var context = event.context,
shape = context.shape,
rootElement = canvas.getRootElement();
// snap participant around existing elements (if any)
if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) {
initParticipantSnapping(context, shape, rootElement.children);
}
});
function canAttach(shape, target, position) {
return bpmnRules.canAttach([ shape ], target, null, position) === 'attach';
}
function canConnect(source, target) {
return bpmnRules.canConnect(source, target);
}
/**
* Snap boundary events to elements border
*/
eventBus.on([
'create.move',
'create.end',
'shape.move.move',
'shape.move.end'
], HIGH_PRIORITY, function(event) {
var context = event.context,
target = context.target,
shape = context.shape;
if (target && !isSnapped(event) && canAttach(shape, target, event)) {
snapBoundaryEvent(event, shape, target);
}
});
/**
* Adjust parent for flowElements to the target participant
* when droping onto lanes.
*/
eventBus.on([
'shape.move.hover',
'shape.move.move',
'shape.move.end',
'create.hover',
'create.move',
'create.end'
], HIGH_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
hover = event.hover;
if (is(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
event.hover = getLanesRoot(hover);
event.hoverGfx = elementRegistry.getGraphics(event.hover);
}
});
/**
* Snap sequence flows.
*/
eventBus.on([
'connect.move',
'connect.hover',
'connect.end'
], HIGH_PRIORITY, function(event) {
var context = event.context,
source = context.source,
target = context.target;
var connection = canConnect(source, target) || {};
if (!context.initialSourcePosition) {
context.initialSourcePosition = context.sourcePosition;
}
if (target && connection.type === 'bpmn:SequenceFlow') {
// snap source
context.sourcePosition = mid(source);
// snap target
assign(event, mid(target));
} else {
// otherwise reset source snap
context.sourcePosition = context.initialSourcePosition;
}
});
eventBus.on([
'create.move',
'shape.move.move'
], function(event) {
var context = event.context,
shape = context.shape,
target = context.target;
var threshold = 30;
if (is(shape, 'bpmn:Lane')) {
if (isAny(target, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
var childLanes = filter(target.children, function(c) {
return is(c, 'bpmn:Lane');
});
var y = event.y,
targetTrbl;
var insert = childLanes.reduce(function(insert, l) {
var laneTrbl = asTRBL(l);
if (abs(laneTrbl.top - y) < threshold) {
insert = assign(insert || {}, { before: { element: l, y: laneTrbl.top } });
} else
if (abs(laneTrbl.bottom - y) < threshold) {
insert = assign(insert || {}, { after: { element: l, y: laneTrbl.bottom } });
} else
if (laneTrbl.top < y && laneTrbl.bottom > y) {
if (abs(laneTrbl.top - y) > abs(laneTrbl.bottom - y)) {
insert = assign(insert || {}, { after: { element: l, y: laneTrbl.bottom } });
} else {
insert = assign(insert || {}, { before: { element: l, y: laneTrbl.top } });
}
}
return insert;
}, false);
if (!insert) {
targetTrbl = asTRBL(target);
if (abs(targetTrbl.top - y) < threshold) {
insert = { before: { element: target, y: targetTrbl.top } };
} else
if (abs(targetTrbl.bottom - y) < threshold) {
insert = { after: { element: target, y: targetTrbl.bottom } };
} else {
insert = { into: { element: target, y: (targetTrbl.top + targetTrbl.bottom) / 2 } };
}
}
if (insert.before && insert.after) {
console.log('insert between', insert.before.element.id, 'and', insert.after.element.id);
setSnapped(event, 'x', insert.before.element.x + insert.before.element.width / 2);
setSnapped(event, 'y', insert.before.y);
} else
if (insert.after) {
console.log('insert after', insert.after.element.id);
setSnapped(event, 'x', insert.after.element.x + insert.after.element.width / 2);
setSnapped(event, 'y', insert.after.y);
} else
if (insert.before) {
console.log('insert before', insert.before.element.id);
setSnapped(event, 'x', insert.before.element.x + insert.before.element.width / 2);
setSnapped(event, 'y', insert.before.y);
} else
if (insert.into) {
console.log('insert into', insert.into.element.id);
setSnapped(event, 'x', insert.into.element.x + insert.into.element.width / 2);
setSnapped(event, 'y', insert.into.y);
}
}
}
});
eventBus.on('resize.start', HIGH_PRIORITY, function(event) {
var context = event.context,
shape = context.shape;
if (is(shape, 'bpmn:SubProcess') && isExpanded(shape)) {
context.minDimensions = { width: 140, height: 120 };
}
if (is(shape, 'bpmn:Participant')) {
context.minDimensions = { width: 300, height: 150 };
}
if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) {
context.resizeConstraints = getParticipantSizeConstraints(shape, context.direction, context.balanced);
}
if (is(shape, 'bpmn:TextAnnotation')) {
context.minDimensions = { width: 50, height: 30 };
}
});
}
inherits(BpmnSnapping, Snapping);
BpmnSnapping.$inject = [ 'eventBus', 'canvas', 'bpmnRules', 'elementRegistry' ];
module.exports = BpmnSnapping;
BpmnSnapping.prototype.initSnap = function(event) {
var context = event.context,
shape = event.shape,
shapeMid,
shapeBounds,
shapeTopLeft,
shapeBottomRight,
snapContext;
snapContext = Snapping.prototype.initSnap.call(this, event);
if (is(shape, 'bpmn:Participant')) {
// assign higher priority for outer snaps on participants
snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]);
}
if (shape) {
shapeMid = mid(shape, event);
shapeBounds = {
width: shape.width,
height: shape.height,
x: isNaN(shape.x) ? round(shapeMid.x - shape.width / 2) : shape.x,
y: isNaN(shape.y) ? round(shapeMid.y - shape.height / 2) : shape.y
};
shapeTopLeft = topLeft(shapeBounds);
shapeBottomRight = bottomRight(shapeBounds);
snapContext.setSnapOrigin('top-left', {
x: shapeTopLeft.x - event.x,
y: shapeTopLeft.y - event.y
});
snapContext.setSnapOrigin('bottom-right', {
x: shapeBottomRight.x - event.x,
y: shapeBottomRight.y - event.y
});
forEach(shape.outgoing, function(c) {
var docking = c.waypoints[0];
docking = docking.original || docking;
snapContext.setSnapOrigin(c.id + '-docking', {
x: docking.x - event.x,
y: docking.y - event.y
});
});
forEach(shape.incoming, function(c) {
var docking = c.waypoints[c.waypoints.length - 1];
docking = docking.original || docking;
snapContext.setSnapOrigin(c.id + '-docking', {
x: docking.x - event.x,
y: docking.y - event.y
});
});
}
var source = context.source;
if (source) {
snapContext.addDefaultSnap('mid', mid(source));
}
};
BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) {
// use target parent as snap target
if (is(shape, 'bpmn:BoundaryEvent') && shape.type !== 'label') {
target = target.parent;
}
// add sequence flow parents as snap targets
if (is(target, 'bpmn:SequenceFlow')) {
this.addTargetSnaps(snapPoints, shape, target.parent);
}
var siblings = this.getSiblings(shape, target) || [];
forEach(siblings, function(sibling) {
// do not snap to lanes
if (is(sibling, 'bpmn:Lane')) {
return;
}
if (sibling.waypoints) {
forEach(sibling.waypoints, function(waypoint, i) {
var nextWaypoint = sibling.waypoints[i+1];
if (!nextWaypoint) {
return;
}
if (nextWaypoint.x === waypoint.x || nextWaypoint.y === waypoint.y) {
snapPoints.add('mid', waypoint);
}
});
return;
}
snapPoints.add('mid', mid(sibling));
if (is(sibling, 'bpmn:Participant')) {
snapPoints.add('top-left', topLeft(sibling));
snapPoints.add('bottom-right', bottomRight(sibling));
}
});
forEach(shape.incoming, function(c) {
if (siblings.indexOf(c.source) === -1) {
snapPoints.add('mid', mid(c.source));
}
var docking = c.waypoints[0];
snapPoints.add(c.id + '-docking', docking.original || docking);
});
forEach(shape.outgoing, function(c) {
if (siblings.indexOf(c.target) === -1) {
snapPoints.add('mid', mid(c.target));
}
var docking = c.waypoints[c.waypoints.length - 1];
snapPoints.add(c.id + '-docking', docking.original || docking);
});
};
/////// participant snapping //////////////////
function initParticipantSnapping(context, shape, elements) {
if (!elements.length) {
return;
}
var snapBox = getBoundingBox(elements.filter(function(e) {
return !e.labelTarget && !e.waypoints;
}));
snapBox.x -= 50;
snapBox.y -= 20;
snapBox.width += 70;
snapBox.height += 40;
// adjust shape height to include bounding box
shape.width = Math.max(shape.width, snapBox.width);
shape.height = Math.max(shape.height, snapBox.height);
context.participantSnapBox = snapBox;
}
function snapParticipant(snapBox, shape, event, offset) {
offset = offset || 0;
var shapeHalfWidth = shape.width / 2 - offset,
shapeHalfHeight = shape.height / 2;
var currentTopLeft = {
x: event.x - shapeHalfWidth - offset,
y: event.y - shapeHalfHeight
};
var currentBottomRight = {
x: event.x + shapeHalfWidth + offset,
y: event.y + shapeHalfHeight
};
var snapTopLeft = snapBox,
snapBottomRight = bottomRight(snapBox);
if (currentTopLeft.x >= snapTopLeft.x) {
setSnapped(event, 'x', snapTopLeft.x + offset + shapeHalfWidth);
} else
if (currentBottomRight.x <= snapBottomRight.x) {
setSnapped(event, 'x', snapBottomRight.x - offset - shapeHalfWidth);
}
if (currentTopLeft.y >= snapTopLeft.y) {
setSnapped(event, 'y', snapTopLeft.y + shapeHalfHeight);
} else
if (currentBottomRight.y <= snapBottomRight.y) {
setSnapped(event, 'y', snapBottomRight.y - shapeHalfHeight);
}
}
/////// boundary event snapping /////////////////////////
function snapBoundaryEvent(event, shape, target) {
var targetTRBL = asTRBL(target);
var direction = getBoundaryAttachment(event, target);
if (/top/.test(direction)) {
setSnapped(event, 'y', targetTRBL.top);
} else
if (/bottom/.test(direction)) {
setSnapped(event, 'y', targetTRBL.bottom);
}
if (/left/.test(direction)) {
setSnapped(event, 'x', targetTRBL.left);
} else
if (/right/.test(direction)) {
setSnapped(event, 'x', targetTRBL.right);
}
}
},{"235":235,"236":236,"252":252,"266":266,"289":289,"301":301,"303":303,"431":431,"67":67,"68":68,"86":86,"93":93,"95":95}],86:[function(_dereq_,module,exports){
'use strict';
var getOrientation = _dereq_(252).getOrientation;
function getBoundaryAttachment(position, targetBounds) {
var orientation = getOrientation(position, targetBounds, -15);
if (orientation !== 'intersect') {
return orientation;
} else {
return null;
}
}
module.exports.getBoundaryAttachment = getBoundaryAttachment;
// participant snapping box implementation /////////////////
var is = _dereq_(95).is;
var asTRBL = _dereq_(252).asTRBL;
var collectLanes = _dereq_(67).collectLanes,
getLanesRoot = _dereq_(67).getLanesRoot;
var abs = Math.abs,
min = Math.min,
max = Math.max;
function addToTrbl(trbl, attr, value, choice) {
var current = trbl[attr];
// make sure to set the value if it does not exist
// or apply the correct value by comparing against
// choice(value, currentValue)
trbl[attr] = current === undefined ? value : choice(value, current);
}
function addMin(trbl, attr, value) {
return addToTrbl(trbl, attr, value, min);
}
function addMax(trbl, attr, value) {
return addToTrbl(trbl, attr, value, max);
}
var LANE_MIN_HEIGHT = 60,
LANE_MIN_WIDTH = 300,
LANE_RIGHT_PADDING = 20,
LANE_LEFT_PADDING = 50,
LANE_TOP_PADDING = 20,
LANE_BOTTOM_PADDING = 20;
function getParticipantSizeConstraints(laneShape, resizeDirection, balanced) {
var lanesRoot = getLanesRoot(laneShape);
var isFirst = true,
isLast = true;
///// max top/bottom size for lanes
var allLanes = collectLanes(lanesRoot, [ lanesRoot ]);
var laneTrbl = asTRBL(laneShape);
var maxTrbl = {},
minTrbl = {};
if (/e/.test(resizeDirection)) {
minTrbl.right = laneTrbl.left + LANE_MIN_WIDTH;
} else
if (/w/.test(resizeDirection)) {
minTrbl.left = laneTrbl.right - LANE_MIN_WIDTH;
}
allLanes.forEach(function(other) {
var otherTrbl = asTRBL(other);
if (/n/.test(resizeDirection)) {
if (otherTrbl.top < (laneTrbl.top - 10)) {
isFirst = false;
}
// max top size (based on next element)
if (balanced && abs(laneTrbl.top - otherTrbl.bottom) < 10) {
addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_HEIGHT);
}
// min top size (based on self or nested element)
if (abs(laneTrbl.top - otherTrbl.top) < 5) {
addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_HEIGHT);
}
}
if (/s/.test(resizeDirection)) {
if (otherTrbl.bottom > (laneTrbl.bottom + 10)) {
isLast = false;
}
// max bottom size (based on previous element)
if (balanced && abs(laneTrbl.bottom - otherTrbl.top) < 10) {
addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_HEIGHT);
}
// min bottom size (based on self or nested element)
if (abs(laneTrbl.bottom - otherTrbl.bottom) < 5) {
addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_HEIGHT);
}
}
});
///// max top/bottom/left/right size based on flow nodes
var flowElements = lanesRoot.children.filter(function(s) {
return !s.hidden && !s.waypoints && (is(s, 'bpmn:FlowElement') || is(s, 'bpmn:Artifact'));
});
flowElements.forEach(function(flowElement) {
var flowElementTrbl = asTRBL(flowElement);
if (isFirst && /n/.test(resizeDirection)) {
addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING);
}
if (/e/.test(resizeDirection)) {
addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING);
}
if (isLast && /s/.test(resizeDirection)) {
addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING);
}
if (/w/.test(resizeDirection)) {
addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING);
}
});
return {
min: minTrbl,
max: maxTrbl
};
}
module.exports.getParticipantSizeConstraints = getParticipantSizeConstraints;
},{"252":252,"67":67,"95":95}],87:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'snapping' ],
snapping: [ 'type', _dereq_(85) ]
};
},{"85":85}],88:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
map = _dereq_(305);
var LabelUtil = _dereq_(94);
var TextUtil = _dereq_(280);
var is = _dereq_(95).is;
var hasExternalLabel = LabelUtil.hasExternalLabel,
getExternalLabelBounds = LabelUtil.getExternalLabelBounds,
isExpanded = _dereq_(93).isExpanded,
elementToString = _dereq_(91).elementToString;
function elementData(semantic, attrs) {
return assign({
id: semantic.id,
type: semantic.$type,
businessObject: semantic
}, attrs);
}
function collectWaypoints(waypoints) {
return map(waypoints, function(p) {
return { x: p.x, y: p.y };
});
}
function notYetDrawn(translate, semantic, refSemantic, property) {
return new Error(translate('element {element} referenced by {referenced}#{property} not yet drawn', {
element: elementToString(refSemantic),
referenced: elementToString(semantic),
property: property
}));
}
/**
* An importer that adds bpmn elements to the canvas
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {ElementFactory} elementFactory
* @param {ElementRegistry} elementRegistry
*/
function BpmnImporter(eventBus, canvas, elementFactory, elementRegistry, translate) {
this._eventBus = eventBus;
this._canvas = canvas;
this._elementFactory = elementFactory;
this._elementRegistry = elementRegistry;
this._translate = translate;
this._textUtil = new TextUtil();
}
BpmnImporter.$inject = [ 'eventBus', 'canvas', 'elementFactory', 'elementRegistry', 'translate' ];
module.exports = BpmnImporter;
/**
* Add bpmn element (semantic) to the canvas onto the
* specified parent shape.
*/
BpmnImporter.prototype.add = function(semantic, parentElement) {
var di = semantic.di,
element,
translate = this._translate,
hidden;
// ROOT ELEMENT
// handle the special case that we deal with a
// invisible root element (process or collaboration)
if (is(di, 'bpmndi:BPMNPlane')) {
// add a virtual element (not being drawn)
element = this._elementFactory.createRoot(elementData(semantic));
this._canvas.setRootElement(element);
}
// SHAPE
else if (is(di, 'bpmndi:BPMNShape')) {
var collapsed = !isExpanded(semantic);
hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
var bounds = semantic.di.bounds;
element = this._elementFactory.createShape(elementData(semantic, {
collapsed: collapsed,
hidden: hidden,
x: Math.round(bounds.x),
y: Math.round(bounds.y),
width: Math.round(bounds.width),
height: Math.round(bounds.height)
}));
if (is(semantic, 'bpmn:BoundaryEvent')) {
this._attachBoundary(semantic, element);
}
this._canvas.addShape(element, parentElement);
}
// CONNECTION
else if (is(di, 'bpmndi:BPMNEdge')) {
var source = this._getSource(semantic),
target = this._getTarget(semantic);
hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
element = this._elementFactory.createConnection(elementData(semantic, {
hidden: hidden,
source: source,
target: target,
waypoints: collectWaypoints(semantic.di.waypoint)
}));
if (is(semantic, 'bpmn:DataAssociation')) {
// render always on top; this ensures DataAssociations
// are rendered correctly across different "hacks" people
// love to model such as cross participant / sub process
// associations
parentElement = null;
}
this._canvas.addConnection(element, parentElement);
} else {
throw new Error(translate('unknown di {di} for element {semantic}', {
di: elementToString(di),
semantic: elementToString(semantic)
}));
}
// (optional) LABEL
if (hasExternalLabel(semantic)) {
this.addLabel(semantic, element);
}
this._eventBus.fire('bpmnElement.added', { element: element });
return element;
};
/**
* Attach the boundary element to the given host
*
* @param {ModdleElement} boundarySemantic
* @param {djs.model.Base} boundaryElement
*/
BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
var translate = this._translate;
var hostSemantic = boundarySemantic.attachedToRef;
if (!hostSemantic) {
throw new Error(translate('missing {semantic}#attachedToRef', {
semantic: elementToString(boundarySemantic)
}));
}
var host = this._elementRegistry.get(hostSemantic.id),
attachers = host && host.attachers;
if (!host) {
throw notYetDrawn(translate, boundarySemantic, hostSemantic, 'attachedToRef');
}
// wire element.host <> host.attachers
boundaryElement.host = host;
if (!attachers) {
host.attachers = attachers = [];
}
if (attachers.indexOf(boundaryElement) === -1) {
attachers.push(boundaryElement);
}
};
/**
* add label for an element
*/
BpmnImporter.prototype.addLabel = function(semantic, element) {
var bounds,
text,
label;
bounds = getExternalLabelBounds(semantic, element);
text = semantic.name;
if (text) {
// get corrected bounds from actual layouted text
bounds = getLayoutedBounds(bounds, text, this._textUtil);
}
label = this._elementFactory.createLabel(elementData(semantic, {
id: semantic.id + '_label',
labelTarget: element,
type: 'label',
hidden: element.hidden || !semantic.name,
x: Math.round(bounds.x),
y: Math.round(bounds.y),
width: Math.round(bounds.width),
height: Math.round(bounds.height)
}));
return this._canvas.addShape(label, element.parent);
};
/**
* Return the drawn connection end based on the given side.
*
* @throws {Error} if the end is not yet drawn
*/
BpmnImporter.prototype._getEnd = function(semantic, side) {
var element,
refSemantic,
type = semantic.$type,
translate = this._translate;
refSemantic = semantic[side + 'Ref'];
// handle mysterious isMany DataAssociation#sourceRef
if (side === 'source' && type === 'bpmn:DataInputAssociation') {
refSemantic = refSemantic && refSemantic[0];
}
// fix source / target for DataInputAssociation / DataOutputAssociation
if (side === 'source' && type === 'bpmn:DataOutputAssociation' ||
side === 'target' && type === 'bpmn:DataInputAssociation') {
refSemantic = semantic.$parent;
}
element = refSemantic && this._getElement(refSemantic);
if (element) {
return element;
}
if (refSemantic) {
throw notYetDrawn(translate, semantic, refSemantic, side + 'Ref');
} else {
throw new Error(translate('{semantic}#{side} Ref not specified', {
semantic: elementToString(semantic),
side: side
}));
}
};
BpmnImporter.prototype._getSource = function(semantic) {
return this._getEnd(semantic, 'source');
};
BpmnImporter.prototype._getTarget = function(semantic) {
return this._getEnd(semantic, 'target');
};
BpmnImporter.prototype._getElement = function(semantic) {
return this._elementRegistry.get(semantic.id);
};
// TODO(nikku): repeating code (search for )
var EXTERNAL_LABEL_STYLE = {
fontFamily: 'Arial, sans-serif',
fontSize: '11px'
};
function getLayoutedBounds(bounds, text, textUtil) {
var layoutedLabelDimensions = textUtil.getDimensions(text, {
box: {
width: 90,
height: 30,
x: bounds.width / 2 + bounds.x,
y: bounds.height / 2 + bounds.y
},
style: EXTERNAL_LABEL_STYLE
});
// resize label shape to fit label text
return {
x: Math.round(bounds.x + bounds.width / 2 - layoutedLabelDimensions.width / 2),
y: Math.round(bounds.y),
width: Math.ceil(layoutedLabelDimensions.width),
height: Math.ceil(layoutedLabelDimensions.height)
};
}
},{"280":280,"305":305,"431":431,"91":91,"93":93,"94":94,"95":95}],89:[function(_dereq_,module,exports){
'use strict';
var filter = _dereq_(301),
find = _dereq_(302),
forEach = _dereq_(303);
var Refs = _dereq_(466);
var elementToString = _dereq_(91).elementToString;
var diRefs = new Refs({ name: 'bpmnElement', enumerable: true }, { name: 'di' });
/**
* Returns true if an element has the given meta-model type
*
* @param {ModdleElement} element
* @param {String} type
*
* @return {Boolean}
*/
function is(element, type) {
return element.$instanceOf(type);
}
/**
* Find a suitable display candidate for definitions where the DI does not
* correctly specify one.
*/
function findDisplayCandidate(definitions) {
return find(definitions.rootElements, function(e) {
return is(e, 'bpmn:Process') || is(e, 'bpmn:Collaboration');
});
}
function BpmnTreeWalker(handler, translate) {
// list of containers already walked
var handledElements = {};
// list of elements to handle deferred to ensure
// prerequisites are drawn
var deferred = [];
///// Helpers /////////////////////////////////
function contextual(fn, ctx) {
return function(e) {
fn(e, ctx);
};
}
function handled(element) {
handledElements[element.id] = element;
}
function isHandled(element) {
return handledElements[element.id];
}
function visit(element, ctx) {
var gfx = element.gfx;
// avoid multiple rendering of elements
if (gfx) {
throw new Error(
translate('already rendered {element}', { element: elementToString(element) })
);
}
// call handler
return handler.element(element, ctx);
}
function visitRoot(element, diagram) {
return handler.root(element, diagram);
}
function visitIfDi(element, ctx) {
try {
var gfx = element.di && visit(element, ctx);
handled(element);
return gfx;
} catch (e) {
logError(e.message, { element: element, error: e });
console.error(translate('failed to import {element}', { element: elementToString(element) }));
console.error(e);
}
}
function logError(message, context) {
handler.error(message, context);
}
////// DI handling ////////////////////////////
function registerDi(di) {
var bpmnElement = di.bpmnElement;
if (bpmnElement) {
if (bpmnElement.di) {
logError(
translate('multiple DI elements defined for {element}', {
element: elementToString(bpmnElement)
}),
{ element: bpmnElement }
);
} else {
diRefs.bind(bpmnElement, 'di');
bpmnElement.di = di;
}
} else {
logError(
translate('no bpmnElement referenced in {element}', {
element: elementToString(di)
}),
{ element: di }
);
}
}
function handleDiagram(diagram) {
handlePlane(diagram.plane);
}
function handlePlane(plane) {
registerDi(plane);
forEach(plane.planeElement, handlePlaneElement);
}
function handlePlaneElement(planeElement) {
registerDi(planeElement);
}
////// Semantic handling //////////////////////
/**
* Handle definitions and return the rendered diagram (if any)
*
* @param {ModdleElement} definitions to walk and import
* @param {ModdleElement} [diagram] specific diagram to import and display
*
* @throws {Error} if no diagram to display could be found
*/
function handleDefinitions(definitions, diagram) {
// make sure we walk the correct bpmnElement
var diagrams = definitions.diagrams;
if (diagram && diagrams.indexOf(diagram) === -1) {
throw new Error(translate('diagram not part of bpmn:Definitions'));
}
if (!diagram && diagrams && diagrams.length) {
diagram = diagrams[0];
}
// no diagram -> nothing to import
if (!diagram) {
throw new Error(translate('no diagram to display'));
}
// load DI from selected diagram only
handleDiagram(diagram);
var plane = diagram.plane;
if (!plane) {
throw new Error(translate(
'no plane for {element}',
{ element: elementToString(diagram) }
));
}
var rootElement = plane.bpmnElement;
// ensure we default to a suitable display candidate (process or collaboration),
// even if non is specified in DI
if (!rootElement) {
rootElement = findDisplayCandidate(definitions);
if (!rootElement) {
throw new Error(translate('no process or collaboration to display'));
} else {
logError(
translate('correcting missing bpmnElement on {plane} to {rootElement}', {
plane: elementToString(plane),
rootElement: elementToString(rootElement)
})
);
// correct DI on the fly
plane.bpmnElement = rootElement;
registerDi(plane);
}
}
var ctx = visitRoot(rootElement, plane);
if (is(rootElement, 'bpmn:Process')) {
handleProcess(rootElement, ctx);
} else if (is(rootElement, 'bpmn:Collaboration')) {
handleCollaboration(rootElement, ctx);
// force drawing of everything not yet drawn that is part of the target DI
handleUnhandledProcesses(definitions.rootElements, ctx);
} else {
throw new Error(
translate('unsupported bpmnElement for {plane}: {rootElement}', {
plane: elementToString(plane),
rootElement: elementToString(rootElement)
})
);
}
// handle all deferred elements
handleDeferred(deferred);
}
function handleDeferred(deferred) {
forEach(deferred, function(d) { d(); });
}
function handleProcess(process, context) {
handleFlowElementsContainer(process, context);
handleIoSpecification(process.ioSpecification, context);
handleArtifacts(process.artifacts, context);
// log process handled
handled(process);
}
function handleUnhandledProcesses(rootElements) {
// walk through all processes that have not yet been drawn and draw them
// if they contain lanes with DI information.
// we do this to pass the free-floating lane test cases in the MIWG test suite
var processes = filter(rootElements, function(e) {
return !isHandled(e) && is(e, 'bpmn:Process') && e.laneSets;
});
processes.forEach(contextual(handleProcess));
}
function handleMessageFlow(messageFlow, context) {
visitIfDi(messageFlow, context);
}
function handleMessageFlows(messageFlows, context) {
forEach(messageFlows, contextual(handleMessageFlow, context));
}
function handleDataAssociation(association, context) {
visitIfDi(association, context);
}
function handleDataInput(dataInput, context) {
visitIfDi(dataInput, context);
}
function handleDataOutput(dataOutput, context) {
visitIfDi(dataOutput, context);
}
function handleArtifact(artifact, context) {
// bpmn:TextAnnotation
// bpmn:Group
// bpmn:Association
visitIfDi(artifact, context);
}
function handleArtifacts(artifacts, context) {
forEach(artifacts, function(e) {
if (is(e, 'bpmn:Association')) {
deferred.push(function() {
handleArtifact(e, context);
});
} else {
handleArtifact(e, context);
}
});
}
function handleIoSpecification(ioSpecification, context) {
if (!ioSpecification) {
return;
}
forEach(ioSpecification.dataInputs, contextual(handleDataInput, context));
forEach(ioSpecification.dataOutputs, contextual(handleDataOutput, context));
}
function handleSubProcess(subProcess, context) {
handleFlowElementsContainer(subProcess, context);
handleArtifacts(subProcess.artifacts, context);
}
function handleFlowNode(flowNode, context) {
var childCtx = visitIfDi(flowNode, context);
if (is(flowNode, 'bpmn:SubProcess')) {
handleSubProcess(flowNode, childCtx || context);
}
if (is(flowNode, 'bpmn:Activity')) {
handleIoSpecification(flowNode.ioSpecification, context);
}
// defer handling of associations
// affected types:
//
// * bpmn:Activity
// * bpmn:ThrowEvent
// * bpmn:CatchEvent
//
deferred.push(function() {
forEach(flowNode.dataInputAssociations, contextual(handleDataAssociation, context));
forEach(flowNode.dataOutputAssociations, contextual(handleDataAssociation, context));
});
}
function handleSequenceFlow(sequenceFlow, context) {
visitIfDi(sequenceFlow, context);
}
function handleDataElement(dataObject, context) {
visitIfDi(dataObject, context);
}
function handleBoundaryEvent(dataObject, context) {
visitIfDi(dataObject, context);
}
function handleLane(lane, context) {
var newContext = visitIfDi(lane, context);
if (lane.childLaneSet) {
handleLaneSet(lane.childLaneSet, newContext || context);
}
wireFlowNodeRefs(lane);
}
function handleLaneSet(laneSet, context) {
forEach(laneSet.lanes, contextual(handleLane, context));
}
function handleLaneSets(laneSets, context) {
forEach(laneSets, contextual(handleLaneSet, context));
}
function handleFlowElementsContainer(container, context) {
if (container.laneSets) {
handleLaneSets(container.laneSets, context);
}
handleFlowElements(container.flowElements, context);
}
function handleFlowElements(flowElements, context) {
forEach(flowElements, function(e) {
if (is(e, 'bpmn:SequenceFlow')) {
deferred.push(function() {
handleSequenceFlow(e, context);
});
} else if (is(e, 'bpmn:BoundaryEvent')) {
deferred.unshift(function() {
handleBoundaryEvent(e, context);
});
} else if (is(e, 'bpmn:FlowNode')) {
handleFlowNode(e, context);
} else if (is(e, 'bpmn:DataObject')) {
// SKIP (assume correct referencing via DataObjectReference)
} else if (is(e, 'bpmn:DataStoreReference')) {
handleDataElement(e, context);
} else if (is(e, 'bpmn:DataObjectReference')) {
handleDataElement(e, context);
} else {
logError(
translate('unrecognized flowElement {element} in context {context}', {
element: elementToString(e),
context: (context ? elementToString(context.businessObject) : 'null')
}),
{ element: e, context: context }
);
}
});
}
function handleParticipant(participant, context) {
var newCtx = visitIfDi(participant, context);
var process = participant.processRef;
if (process) {
handleProcess(process, newCtx || context);
}
}
function handleCollaboration(collaboration) {
forEach(collaboration.participants, contextual(handleParticipant));
handleArtifacts(collaboration.artifacts);
// handle message flows latest in the process
deferred.push(function() {
handleMessageFlows(collaboration.messageFlows);
});
}
function wireFlowNodeRefs(lane) {
// wire the virtual flowNodeRefs <-> relationship
forEach(lane.flowNodeRef, function(flowNode) {
var lanes = flowNode.get('lanes');
if (lanes) {
lanes.push(lane);
}
});
}
///// API ////////////////////////////////
return {
handleDefinitions: handleDefinitions
};
}
module.exports = BpmnTreeWalker;
},{"301":301,"302":302,"303":303,"466":466,"91":91}],90:[function(_dereq_,module,exports){
'use strict';
var BpmnTreeWalker = _dereq_(89);
/**
* Import the definitions into a diagram.
*
* Errors and warnings are reported through the specified callback.
*
* @param {Diagram} diagram
* @param {ModdleElement} definitions
* @param {Function} done the callback, invoked with (err, [ warning ]) once the import is done
*/
function importBpmnDiagram(diagram, definitions, done) {
var importer = diagram.get('bpmnImporter'),
eventBus = diagram.get('eventBus'),
translate = diagram.get('translate');
var error,
warnings = [];
/**
* Walk the diagram semantically, importing (=drawing)
* all elements you encounter.
*
* @param {ModdleElement} definitions
*/
function render(definitions) {
var visitor = {
root: function(element) {
return importer.add(element);
},
element: function(element, parentShape) {
return importer.add(element, parentShape);
},
error: function(message, context) {
warnings.push({ message: message, context: context });
}
};
var walker = new BpmnTreeWalker(visitor, translate);
// traverse BPMN 2.0 document model,
// starting at definitions
walker.handleDefinitions(definitions);
}
eventBus.fire('import.render.start', { definitions: definitions });
try {
render(definitions);
} catch (e) {
error = e;
}
eventBus.fire('import.render.complete', {
error: error,
warnings: warnings
});
done(error, warnings);
}
module.exports.importBpmnDiagram = importBpmnDiagram;
},{"89":89}],91:[function(_dereq_,module,exports){
'use strict';
module.exports.elementToString = function(e) {
if (!e) {
return '';
}
return '<' + e.$type + (e.id ? ' id="' + e.id : '') + '" />';
};
},{}],92:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(248)
],
bpmnImporter: [ 'type', _dereq_(88) ]
};
},{"248":248,"88":88}],93:[function(_dereq_,module,exports){
'use strict';
var is = _dereq_(95).is,
getBusinessObject = _dereq_(95).getBusinessObject;
var forEach = _dereq_(303);
module.exports.isExpanded = function(element) {
if (is(element, 'bpmn:CallActivity')) {
return false;
}
if (is(element, 'bpmn:SubProcess')) {
return !!getBusinessObject(element).di.isExpanded;
}
if (is(element, 'bpmn:Participant')) {
return !!getBusinessObject(element).processRef;
}
return true;
};
module.exports.isInterrupting = function(element) {
return element && getBusinessObject(element).isInterrupting !== false;
};
module.exports.isEventSubProcess = function(element) {
return element && !!getBusinessObject(element).triggeredByEvent;
};
function hasEventDefinition(element, eventType) {
var bo = getBusinessObject(element),
hasEventDefinition = false;
if (bo.eventDefinitions) {
forEach(bo.eventDefinitions, function(event) {
if (is(event, eventType)) {
hasEventDefinition = true;
}
});
}
return hasEventDefinition;
}
module.exports.hasEventDefinition = hasEventDefinition;
module.exports.hasErrorEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:ErrorEventDefinition');
};
module.exports.hasEscalationEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:EscalationEventDefinition');
};
module.exports.hasCompensateEventDefinition = function(element) {
return hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
};
},{"303":303,"95":95}],94:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
var is = _dereq_(95).is;
var DEFAULT_LABEL_SIZE = module.exports.DEFAULT_LABEL_SIZE = {
width: 90,
height: 20
};
var FLOW_LABEL_INDENT = module.exports.FLOW_LABEL_INDENT = 15;
/**
* Returns true if the given semantic has an external label
*
* @param {BpmnElement} semantic
* @return {Boolean} true if has label
*/
module.exports.hasExternalLabel = function(semantic) {
return is(semantic, 'bpmn:Event') ||
is(semantic, 'bpmn:Gateway') ||
is(semantic, 'bpmn:DataStoreReference') ||
is(semantic, 'bpmn:DataObjectReference') ||
is(semantic, 'bpmn:SequenceFlow') ||
is(semantic, 'bpmn:MessageFlow');
};
/**
* Get the position for sequence flow labels
*
* @param {Array} waypoints
* @return {Point} the label position
*/
function getFlowLabelPosition(waypoints) {
// get the waypoints mid
var mid = waypoints.length / 2 - 1;
var first = waypoints[Math.floor(mid)];
var second = waypoints[Math.ceil(mid + 0.01)];
// get position
var position = getWaypointsMid(waypoints);
// calculate angle
var angle = Math.atan( (second.y - first.y) / (second.x - first.x) );
var x = position.x,
y = position.y;
if ( Math.abs(angle) < Math.PI / 2 ) {
y -= FLOW_LABEL_INDENT;
} else {
x += FLOW_LABEL_INDENT;
}
return { x: x, y: y };
}
module.exports.getFlowLabelPosition = getFlowLabelPosition;
/**
* Get the middle of a number of waypoints
*
* @param {Array} waypoints
* @return {Point} the mid point
*/
function getWaypointsMid(waypoints) {
var mid = waypoints.length / 2 - 1;
var first = waypoints[Math.floor(mid)];
var second = waypoints[Math.ceil(mid + 0.01)];
return {
x: first.x + (second.x - first.x) / 2,
y: first.y + (second.y - first.y) / 2
};
}
module.exports.getWaypointsMid = getWaypointsMid;
function getExternalLabelMid(element) {
if (element.waypoints) {
return getFlowLabelPosition(element.waypoints);
} else {
return {
x: element.x + element.width / 2,
y: element.y + element.height + DEFAULT_LABEL_SIZE.height / 2
};
}
}
module.exports.getExternalLabelMid = getExternalLabelMid;
/**
* Returns the bounds of an elements label, parsed from the elements DI or
* generated from its bounds.
*
* @param {BpmnElement} semantic
* @param {djs.model.Base} element
*/
module.exports.getExternalLabelBounds = function(semantic, element) {
var mid,
size,
bounds,
di = semantic.di,
label = di.label;
if (label && label.bounds) {
bounds = label.bounds;
size = {
width: Math.max(DEFAULT_LABEL_SIZE.width, bounds.width),
height: bounds.height
};
mid = {
x: bounds.x + bounds.width / 2,
y: bounds.y + bounds.height / 2
};
} else {
mid = getExternalLabelMid(element);
size = DEFAULT_LABEL_SIZE;
}
return assign({
x: mid.x - size.width / 2,
y: mid.y - size.height / 2
}, size);
};
},{"431":431,"95":95}],95:[function(_dereq_,module,exports){
'use strict';
/**
* Is an element of the given BPMN type?
*
* @param {djs.model.Base|ModdleElement} element
* @param {String} type
*
* @return {Boolean}
*/
function is(element, type) {
var bo = getBusinessObject(element);
return bo && (typeof bo.$instanceOf === 'function') && bo.$instanceOf(type);
}
module.exports.is = is;
/**
* Return the business object for a given element.
*
* @param {djs.model.Base|ModdleElement} element
*
* @return {ModdleElement}
*/
function getBusinessObject(element) {
return (element && element.businessObject) || element;
}
module.exports.getBusinessObject = getBusinessObject;
},{}],96:[function(_dereq_,module,exports){
/**
* This file must not be changed or exchanged.
*
* @see http://bpmn.io/license for more information.
*/
'use strict';
var domify = _dereq_(449);
var domDelegate = _dereq_(448);
/* jshint -W101 */
// inlined ../resources/bpmnjs.png
var logoData = module.exports.BPMNIO_LOGO = 'iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAMAAADypuvZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADBQTFRFiMte9PrwldFwfcZPqtqN0+zEyOe1XLgjvuKncsJAZ70y6fXh3vDT////UrQV////G2zN+AAAABB0Uk5T////////////////////AOAjXRkAAAHDSURBVHjavJZJkoUgDEBJmAX8979tM8u3E6x20VlYJfFFMoL4vBDxATxZcakIOJTWSmxvKWVIkJ8jHvlRv1F2LFrVISCZI+tCtQx+XfewgVTfyY3plPiQEAzI3zWy+kR6NBhFBYeBuscJLOUuA2WVLpCjVIaFzrNQZArxAZKUQm6gsj37L9Cb7dnIBUKxENaaMJQqMpDXvSL+ktxdGRm2IsKgJGGPg7atwUG5CcFUEuSv+CwQqizTrvDTNXdMU2bMiDWZd8d7QIySWVRsb2vBBioxOFt4OinPBapL+neAb5KL5IJ8szOza2/DYoipUCx+CjO0Bpsv0V6mktNZ+k8rlABlWG0FrOpKYVo8DT3dBeLEjUBAj7moDogVii7nSS9QzZnFcOVBp1g2PyBQ3Vr5aIapN91VJy33HTJLC1iX2FY6F8gRdaAeIEfVONgtFCzZTmoLEdOjBDfsIOA6128gw3eu1shAajdZNAORxuQDJN5A5PbEG6gNIu24QJD5iNyRMZIr6bsHbCtCU/OaOaSvgkUyDMdDa1BXGf5HJ1To+/Ym6mCKT02Y+/Sa126ZKyd3jxhzpc1r8zVL6YM1Qy/kR4ABAFJ6iQUnivhAAAAAAElFTkSuQmCC';
/* jshint +W101 */
function css(attrs) {
return attrs.join(';');
}
var LIGHTBOX_STYLES = css([
'z-index: 1001',
'position: fixed',
'top: 0',
'left: 0',
'right: 0',
'bottom: 0'
]);
var BACKDROP_STYLES = css([
'width: 100%',
'height: 100%',
'background: rgba(0,0,0,0.2)'
]);
var NOTICE_STYLES = css([
'position: absolute',
'left: 50%',
'top: 40%',
'margin: 0 -130px',
'width: 260px',
'padding: 10px',
'background: white',
'border: solid 1px #AAA',
'border-radius: 3px',
'font-family: Helvetica, Arial, sans-serif',
'font-size: 14px',
'line-height: 1.2em'
]);
var LIGHTBOX_MARKUP =
'' +
'
' +
'
' +
'
' +
' ' +
' ' +
'Web-based tooling for BPMN, DMN and CMMN diagrams ' +
'powered by
bpmn.io .' +
'
' +
'
';
var lightbox;
function open() {
if (!lightbox) {
lightbox = domify(LIGHTBOX_MARKUP);
domDelegate.bind(lightbox, '.backdrop', 'click', function(event) {
document.body.removeChild(lightbox);
});
}
document.body.appendChild(lightbox);
}
module.exports.open = open;
},{"448":448,"449":449}],97:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
filter = _dereq_(301),
any = _dereq_(299),
sort = _dereq_(310),
isArray = _dereq_(422);
var IGNORED_PROPERTIES = _dereq_(98).IGNORED_PROPERTIES;
function isAllowedIn(extProp, type) {
var allowedIn = extProp.meta.allowedIn;
// '*' is a wildcard, which means any element is allowed to use this property
if (allowedIn.length === 1 && allowedIn[0] === '*') {
return true;
}
return allowedIn.indexOf(type) !== -1;
}
function isType(element, types) {
return any(types, function(type) {
return typeof element === type;
});
}
/**
* A bpmn properties cloning interface
*
*/
function ModelCloneHelper(eventBus) {
this._eventBus = eventBus;
}
module.exports = ModelCloneHelper;
ModelCloneHelper.prototype.clone = function(refElement, newElement, properties) {
// hasNestedProperty: property allows us to avoid ending up with empty (xml) tags
// f.ex: if extensionElements.values is empty, don't set it
var context = {
newElement: newElement,
hasNestedProperty: false
};
// we want the extensionElements to be cloned last
// so that they can check certain properties
properties = sort(properties, function(prop) {
return prop === 'bpmn:extensionElements';
});
forEach(properties, function(propName) {
var refElementProp = refElement.get(propName),
newElementProp = newElement.get(propName),
propDescriptor = newElement.$model.getPropertyDescriptor(newElement, propName),
newProperty, name;
// we're not interested in cloning:
// - same values from simple types
// - cloning id's
// - cloning reference elements
if (newElementProp === refElementProp ||
(propDescriptor && (propDescriptor.isId || propDescriptor.isReference))) {
return;
}
// if the property is of type 'boolean', 'string', 'number' or 'null', just set it
if (isType(refElementProp, [ 'boolean', 'string', 'number' ]) || refElementProp === null) {
newElement.set(propName, refElementProp);
return;
}
if (isArray(refElementProp)) {
forEach(refElementProp, function(extElement) {
var newProp;
context.refTopLevelProperty = extElement;
newProp = this._deepClone(extElement, context);
if (context.hasNestedProperty) {
newProp.$parent = newElement;
newElementProp.push(newProp);
}
context.hasNestedProperty = false;
}, this);
} else {
name = propName.replace(/bpmn:/, '');
context.refTopLevelProperty = refElementProp;
newProperty = this._deepClone(refElementProp, context);
if (context.hasNestedProperty) {
newElement[name] = newProperty;
}
context.hasNestedProperty = false;
}
}, this);
return newElement;
};
ModelCloneHelper.prototype._deepClone = function _deepClone(propertyElement, context) {
var eventBus = this._eventBus;
var newProp = propertyElement.$model.create(propertyElement.$type);
var properties = filter(Object.keys(propertyElement), function(prop) {
var descriptor = newProp.$model.getPropertyDescriptor(newProp, prop);
if (descriptor && (descriptor.isId || descriptor.isReference)) {
return false;
}
// we need to make sure we don't clone certain properties
// which we cannot easily know if they hold references or not
if (IGNORED_PROPERTIES.indexOf(prop) !== -1) {
return false;
}
// make sure we don't copy the type
return prop !== '$type';
});
if (!properties.length) {
context.hasNestedProperty = true;
}
forEach(properties, function(propName) {
// check if the propertyElement has this property defined
if (propertyElement[propName] !== undefined &&
(propertyElement[propName].$type || isArray(propertyElement[propName]))) {
if (isArray(propertyElement[propName])) {
newProp[propName] = [];
forEach(propertyElement[propName], function(property) {
var extProp = propertyElement.$model.getTypeDescriptor(property.$type),
newDeepProp;
// we're not going to copy undefined types
if (!extProp) {
return;
}
var canClone = eventBus.fire('property.clone', {
newElement: context.newElement,
refTopLevelProperty: context.refTopLevelProperty,
propertyDescriptor: extProp
});
if (!canClone) {
// if can clone is 'undefined' or 'false'
// check for the meta information if it is allowed
if (propertyElement.$type === 'bpmn:ExtensionElements' &&
extProp.meta && extProp.meta.allowedIn &&
!isAllowedIn(extProp, context.newElement.$type)) {
return false;
}
}
newDeepProp = this._deepClone(property, context);
newDeepProp.$parent = newProp;
if (!newProp[propName]) {
newProp[propName] = [];
}
context.hasNestedProperty = true;
newProp[propName].push(newDeepProp);
}, this);
} else if (propertyElement[propName].$type) {
newProp[propName] = this._deepClone(propertyElement[propName], context);
if (newProp[propName]) {
context.hasNestedProperty = true;
newProp[propName].$parent = newProp;
}
}
} else {
context.hasNestedProperty = true;
// just assign directly if it's a value
newProp[propName] = propertyElement[propName];
}
}, this);
return newProp;
};
},{"299":299,"301":301,"303":303,"310":310,"422":422,"98":98}],98:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
/**
* These are the properties that should be ignored when cloning elements.
*
* @type {Array}
*/
module.exports.IGNORED_PROPERTIES = [
'lanes',
'incoming',
'outgoing',
'artifacts',
'default',
'flowElements'
];
function getProperties(descriptor, keepDefault) {
var properties = [];
forEach(descriptor.properties, function(property) {
if (keepDefault && property.default) {
return;
}
properties.push(property.ns.name);
});
return properties;
}
module.exports.getProperties = getProperties;
},{"303":303}],99:[function(_dereq_,module,exports){
module.exports = _dereq_(101);
},{"101":101}],100:[function(_dereq_,module,exports){
'use strict';
var isString = _dereq_(428),
isFunction = _dereq_(423),
assign = _dereq_(431);
var Moddle = _dereq_(457),
XmlReader = _dereq_(455),
XmlWriter = _dereq_(456);
/**
* A sub class of {@link Moddle} with support for import and export of BPMN 2.0 xml files.
*
* @class BpmnModdle
* @extends Moddle
*
* @param {Object|Array} packages to use for instantiating the model
* @param {Object} [options] additional options to pass over
*/
function BpmnModdle(packages, options) {
Moddle.call(this, packages, options);
}
BpmnModdle.prototype = Object.create(Moddle.prototype);
module.exports = BpmnModdle;
/**
* Instantiates a BPMN model tree from a given xml string.
*
* @param {String} xmlStr
* @param {String} [typeName='bpmn:Definitions'] name of the root element
* @param {Object} [options] options to pass to the underlying reader
* @param {Function} done callback that is invoked with (err, result, parseContext)
* once the import completes
*/
BpmnModdle.prototype.fromXML = function(xmlStr, typeName, options, done) {
if (!isString(typeName)) {
done = options;
options = typeName;
typeName = 'bpmn:Definitions';
}
if (isFunction(options)) {
done = options;
options = {};
}
var reader = new XmlReader(assign({ model: this, lax: true }, options));
var rootHandler = reader.handler(typeName);
reader.fromXML(xmlStr, rootHandler, done);
};
/**
* Serializes a BPMN 2.0 object tree to XML.
*
* @param {String} element the root element, typically an instance of `bpmn:Definitions`
* @param {Object} [options] to pass to the underlying writer
* @param {Function} done callback invoked with (err, xmlStr) once the import completes
*/
BpmnModdle.prototype.toXML = function(element, options, done) {
if (isFunction(options)) {
done = options;
options = {};
}
var writer = new XmlWriter(options);
try {
var result = writer.toXML(element);
done(null, result);
} catch (e) {
done(e);
}
};
},{"423":423,"428":428,"431":431,"455":455,"456":456,"457":457}],101:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
var BpmnModdle = _dereq_(100);
var packages = {
bpmn: _dereq_(103),
bpmndi: _dereq_(104),
dc: _dereq_(105),
di: _dereq_(106),
bioc: _dereq_(102)
};
module.exports = function(additionalPackages, options) {
return new BpmnModdle(assign({}, packages, additionalPackages), options);
};
},{"100":100,"102":102,"103":103,"104":104,"105":105,"106":106,"431":431}],102:[function(_dereq_,module,exports){
module.exports={
"name": "bpmn.io colors for BPMN",
"uri": "http://bpmn.io/schema/bpmn/biocolor/1.0",
"prefix": "bioc",
"types": [
{
"name": "ColoredShape",
"extends": [ "bpmndi:BPMNShape" ],
"properties": [
{
"name": "stroke",
"isAttr": true,
"type": "String"
},
{
"name": "fill",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ColoredEdge",
"extends": [ "bpmndi:BPMNEdge" ],
"properties": [
{
"name": "stroke",
"isAttr": true,
"type": "String"
},
{
"name": "fill",
"isAttr": true,
"type": "String"
}
]
}
],
"enumerations": [],
"associations": []
}
},{}],103:[function(_dereq_,module,exports){
module.exports={
"name": "BPMN20",
"uri": "http://www.omg.org/spec/BPMN/20100524/MODEL",
"associations": [],
"types": [
{
"name": "Interface",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "operations",
"type": "Operation",
"isMany": true
},
{
"name": "implementationRef",
"type": "String",
"isAttr": true
}
]
},
{
"name": "Operation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "inMessageRef",
"type": "Message",
"isReference": true
},
{
"name": "outMessageRef",
"type": "Message",
"isReference": true
},
{
"name": "errorRef",
"type": "Error",
"isMany": true,
"isReference": true
},
{
"name": "implementationRef",
"type": "String",
"isAttr": true
}
]
},
{
"name": "EndPoint",
"superClass": [
"RootElement"
]
},
{
"name": "Auditing",
"superClass": [
"BaseElement"
]
},
{
"name": "GlobalTask",
"superClass": [
"CallableElement"
],
"properties": [
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
}
]
},
{
"name": "Monitoring",
"superClass": [
"BaseElement"
]
},
{
"name": "Performer",
"superClass": [
"ResourceRole"
]
},
{
"name": "Process",
"superClass": [
"FlowElementsContainer",
"CallableElement"
],
"properties": [
{
"name": "processType",
"type": "ProcessType",
"isAttr": true
},
{
"name": "isClosed",
"isAttr": true,
"type": "Boolean"
},
{
"name": "auditing",
"type": "Auditing"
},
{
"name": "monitoring",
"type": "Monitoring"
},
{
"name": "properties",
"type": "Property",
"isMany": true
},
{
"name": "laneSets",
"type": "LaneSet",
"isMany": true,
"replaces": "FlowElementsContainer#laneSets"
},
{
"name": "flowElements",
"type": "FlowElement",
"isMany": true,
"replaces": "FlowElementsContainer#flowElements"
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
},
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
},
{
"name": "correlationSubscriptions",
"type": "CorrelationSubscription",
"isMany": true
},
{
"name": "supports",
"type": "Process",
"isMany": true,
"isReference": true
},
{
"name": "definitionalCollaborationRef",
"type": "Collaboration",
"isAttr": true,
"isReference": true
},
{
"name": "isExecutable",
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "LaneSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "lanes",
"type": "Lane",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Lane",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "partitionElementRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
},
{
"name": "partitionElement",
"type": "BaseElement"
},
{
"name": "flowNodeRef",
"type": "FlowNode",
"isMany": true,
"isReference": true
},
{
"name": "childLaneSet",
"type": "LaneSet",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "GlobalManualTask",
"superClass": [
"GlobalTask"
]
},
{
"name": "ManualTask",
"superClass": [
"Task"
]
},
{
"name": "UserTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "renderings",
"type": "Rendering",
"isMany": true
},
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Rendering",
"superClass": [
"BaseElement"
]
},
{
"name": "HumanPerformer",
"superClass": [
"Performer"
]
},
{
"name": "PotentialOwner",
"superClass": [
"HumanPerformer"
]
},
{
"name": "GlobalUserTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "renderings",
"type": "Rendering",
"isMany": true
}
]
},
{
"name": "Gateway",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "gatewayDirection",
"type": "GatewayDirection",
"default": "Unspecified",
"isAttr": true
}
]
},
{
"name": "EventBasedGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "instantiate",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "eventGatewayType",
"type": "EventBasedGatewayType",
"isAttr": true,
"default": "Exclusive"
}
]
},
{
"name": "ComplexGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "activationCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExclusiveGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "InclusiveGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParallelGateway",
"superClass": [
"Gateway"
]
},
{
"name": "RootElement",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "Relationship",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "type",
"isAttr": true,
"type": "String"
},
{
"name": "direction",
"type": "RelationshipDirection",
"isAttr": true
},
{
"name": "source",
"isMany": true,
"isReference": true,
"type": "Element"
},
{
"name": "target",
"isMany": true,
"isReference": true,
"type": "Element"
}
]
},
{
"name": "BaseElement",
"isAbstract": true,
"properties": [
{
"name": "id",
"isAttr": true,
"type": "String",
"isId": true
},
{
"name": "documentation",
"type": "Documentation",
"isMany": true
},
{
"name": "extensionDefinitions",
"type": "ExtensionDefinition",
"isMany": true,
"isReference": true
},
{
"name": "extensionElements",
"type": "ExtensionElements"
}
]
},
{
"name": "Extension",
"properties": [
{
"name": "mustUnderstand",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "definition",
"type": "ExtensionDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExtensionDefinition",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "extensionAttributeDefinitions",
"type": "ExtensionAttributeDefinition",
"isMany": true
}
]
},
{
"name": "ExtensionAttributeDefinition",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "type",
"isAttr": true,
"type": "String"
},
{
"name": "isReference",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "extensionDefinition",
"type": "ExtensionDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExtensionElements",
"properties": [
{
"name": "valueRef",
"isAttr": true,
"isReference": true,
"type": "Element"
},
{
"name": "values",
"type": "Element",
"isMany": true
},
{
"name": "extensionAttributeDefinition",
"type": "ExtensionAttributeDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Documentation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "text",
"type": "String",
"isBody": true
},
{
"name": "textFormat",
"default": "text/plain",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Event",
"isAbstract": true,
"superClass": [
"FlowNode",
"InteractionNode"
],
"properties": [
{
"name": "properties",
"type": "Property",
"isMany": true
}
]
},
{
"name": "IntermediateCatchEvent",
"superClass": [
"CatchEvent"
]
},
{
"name": "IntermediateThrowEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "EndEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "StartEvent",
"superClass": [
"CatchEvent"
],
"properties": [
{
"name": "isInterrupting",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "ThrowEvent",
"isAbstract": true,
"superClass": [
"Event"
],
"properties": [
{
"name": "dataInputs",
"type": "DataInput",
"isMany": true
},
{
"name": "dataInputAssociations",
"type": "DataInputAssociation",
"isMany": true
},
{
"name": "inputSet",
"type": "InputSet"
},
{
"name": "eventDefinitions",
"type": "EventDefinition",
"isMany": true
},
{
"name": "eventDefinitionRef",
"type": "EventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "CatchEvent",
"isAbstract": true,
"superClass": [
"Event"
],
"properties": [
{
"name": "parallelMultiple",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "dataOutputs",
"type": "DataOutput",
"isMany": true
},
{
"name": "dataOutputAssociations",
"type": "DataOutputAssociation",
"isMany": true
},
{
"name": "outputSet",
"type": "OutputSet"
},
{
"name": "eventDefinitions",
"type": "EventDefinition",
"isMany": true
},
{
"name": "eventDefinitionRef",
"type": "EventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "BoundaryEvent",
"superClass": [
"CatchEvent"
],
"properties": [
{
"name": "cancelActivity",
"default": true,
"isAttr": true,
"type": "Boolean"
},
{
"name": "attachedToRef",
"type": "Activity",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "EventDefinition",
"isAbstract": true,
"superClass": [
"RootElement"
]
},
{
"name": "CancelEventDefinition",
"superClass": [
"EventDefinition"
]
},
{
"name": "ErrorEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "errorRef",
"type": "Error",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TerminateEventDefinition",
"superClass": [
"EventDefinition"
]
},
{
"name": "EscalationEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "escalationRef",
"type": "Escalation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Escalation",
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "escalationCode",
"isAttr": true,
"type": "String"
}
],
"superClass": [
"RootElement"
]
},
{
"name": "CompensateEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "waitForCompletion",
"isAttr": true,
"type": "Boolean"
},
{
"name": "activityRef",
"type": "Activity",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TimerEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "timeDate",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "timeCycle",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "timeDuration",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "LinkEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "target",
"type": "LinkEventDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "source",
"type": "LinkEventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "MessageEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ConditionalEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "condition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "SignalEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "signalRef",
"type": "Signal",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Signal",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ImplicitThrowEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "DataState",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ItemAwareElement",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "itemSubjectRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "dataState",
"type": "DataState"
}
]
},
{
"name": "DataAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "assignment",
"type": "Assignment",
"isMany": true
},
{
"name": "sourceRef",
"type": "ItemAwareElement",
"isMany": true,
"isReference": true
},
{
"name": "targetRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "transformation",
"type": "FormalExpression",
"xml": {
"serialize": "property"
}
}
]
},
{
"name": "DataInput",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "inputSetRef",
"type": "InputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "inputSetWithOptional",
"type": "InputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "inputSetWithWhileExecuting",
"type": "InputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
}
]
},
{
"name": "DataOutput",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "outputSetRef",
"type": "OutputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "outputSetWithOptional",
"type": "OutputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "outputSetWithWhileExecuting",
"type": "OutputSet",
"isVirtual": true,
"isMany": true,
"isReference": true
}
]
},
{
"name": "InputSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "dataInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "optionalInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "whileExecutingInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "outputSetRefs",
"type": "OutputSet",
"isMany": true,
"isReference": true
}
]
},
{
"name": "OutputSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "inputSetRefs",
"type": "InputSet",
"isMany": true,
"isReference": true
},
{
"name": "optionalOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
},
{
"name": "whileExecutingOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
}
]
},
{
"name": "Property",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "DataInputAssociation",
"superClass": [
"DataAssociation"
]
},
{
"name": "DataOutputAssociation",
"superClass": [
"DataAssociation"
]
},
{
"name": "InputOutputSpecification",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataInputs",
"type": "DataInput",
"isMany": true
},
{
"name": "dataOutputs",
"type": "DataOutput",
"isMany": true
},
{
"name": "inputSets",
"type": "InputSet",
"isMany": true
},
{
"name": "outputSets",
"type": "OutputSet",
"isMany": true
}
]
},
{
"name": "DataObject",
"superClass": [
"FlowElement",
"ItemAwareElement"
],
"properties": [
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "InputOutputBinding",
"properties": [
{
"name": "inputDataRef",
"type": "InputSet",
"isAttr": true,
"isReference": true
},
{
"name": "outputDataRef",
"type": "OutputSet",
"isAttr": true,
"isReference": true
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Assignment",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "from",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "to",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "DataStore",
"superClass": [
"RootElement",
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "capacity",
"isAttr": true,
"type": "Integer"
},
{
"name": "isUnlimited",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "DataStoreReference",
"superClass": [
"ItemAwareElement",
"FlowElement"
],
"properties": [
{
"name": "dataStoreRef",
"type": "DataStore",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "DataObjectReference",
"superClass": [
"ItemAwareElement",
"FlowElement"
],
"properties": [
{
"name": "dataObjectRef",
"type": "DataObject",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ConversationLink",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "sourceRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ConversationAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerConversationNodeRef",
"type": "ConversationNode",
"isAttr": true,
"isReference": true
},
{
"name": "outerConversationNodeRef",
"type": "ConversationNode",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CallConversation",
"superClass": [
"ConversationNode"
],
"properties": [
{
"name": "calledCollaborationRef",
"type": "Collaboration",
"isAttr": true,
"isReference": true
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
}
]
},
{
"name": "Conversation",
"superClass": [
"ConversationNode"
]
},
{
"name": "SubConversation",
"superClass": [
"ConversationNode"
],
"properties": [
{
"name": "conversationNodes",
"type": "ConversationNode",
"isMany": true
}
]
},
{
"name": "ConversationNode",
"isAbstract": true,
"superClass": [
"InteractionNode",
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRefs",
"type": "Participant",
"isMany": true,
"isReference": true
},
{
"name": "messageFlowRefs",
"type": "MessageFlow",
"isMany": true,
"isReference": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
}
]
},
{
"name": "GlobalConversation",
"superClass": [
"Collaboration"
]
},
{
"name": "PartnerEntity",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
}
]
},
{
"name": "PartnerRole",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
}
]
},
{
"name": "CorrelationProperty",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "correlationPropertyRetrievalExpression",
"type": "CorrelationPropertyRetrievalExpression",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "type",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Error",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "errorCode",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "CorrelationKey",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "correlationPropertyRef",
"type": "CorrelationProperty",
"isMany": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Expression",
"superClass": [
"BaseElement"
],
"isAbstract": false,
"properties": [
{
"name": "body",
"type": "String",
"isBody": true
}
]
},
{
"name": "FormalExpression",
"superClass": [
"Expression"
],
"properties": [
{
"name": "language",
"isAttr": true,
"type": "String"
},
{
"name": "evaluatesToTypeRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Message",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "itemRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ItemDefinition",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "itemKind",
"type": "ItemKind",
"isAttr": true
},
{
"name": "structureRef",
"type": "String",
"isAttr": true
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "import",
"type": "Import",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "FlowElement",
"isAbstract": true,
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "auditing",
"type": "Auditing"
},
{
"name": "monitoring",
"type": "Monitoring"
},
{
"name": "categoryValueRef",
"type": "CategoryValue",
"isMany": true,
"isReference": true
}
]
},
{
"name": "SequenceFlow",
"superClass": [
"FlowElement"
],
"properties": [
{
"name": "isImmediate",
"isAttr": true,
"type": "Boolean"
},
{
"name": "conditionExpression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "sourceRef",
"type": "FlowNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "FlowNode",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "FlowElementsContainer",
"isAbstract": true,
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "laneSets",
"type": "LaneSet",
"isMany": true
},
{
"name": "flowElements",
"type": "FlowElement",
"isMany": true
}
]
},
{
"name": "CallableElement",
"isAbstract": true,
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "ioSpecification",
"type": "InputOutputSpecification",
"xml": {
"serialize": "property"
}
},
{
"name": "supportedInterfaceRef",
"type": "Interface",
"isMany": true,
"isReference": true
},
{
"name": "ioBinding",
"type": "InputOutputBinding",
"isMany": true,
"xml": {
"serialize": "property"
}
}
]
},
{
"name": "FlowNode",
"isAbstract": true,
"superClass": [
"FlowElement"
],
"properties": [
{
"name": "incoming",
"type": "SequenceFlow",
"isMany": true,
"isReference": true
},
{
"name": "outgoing",
"type": "SequenceFlow",
"isMany": true,
"isReference": true
},
{
"name": "lanes",
"type": "Lane",
"isVirtual": true,
"isMany": true,
"isReference": true
}
]
},
{
"name": "CorrelationPropertyRetrievalExpression",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "messagePath",
"type": "FormalExpression"
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CorrelationPropertyBinding",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataPath",
"type": "FormalExpression"
},
{
"name": "correlationPropertyRef",
"type": "CorrelationProperty",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Resource",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "resourceParameters",
"type": "ResourceParameter",
"isMany": true
}
]
},
{
"name": "ResourceParameter",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isRequired",
"isAttr": true,
"type": "Boolean"
},
{
"name": "type",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CorrelationSubscription",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "correlationKeyRef",
"type": "CorrelationKey",
"isAttr": true,
"isReference": true
},
{
"name": "correlationPropertyBinding",
"type": "CorrelationPropertyBinding",
"isMany": true
}
]
},
{
"name": "MessageFlow",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "sourceRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "MessageFlowAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerMessageFlowRef",
"type": "MessageFlow",
"isAttr": true,
"isReference": true
},
{
"name": "outerMessageFlowRef",
"type": "MessageFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "InteractionNode",
"isAbstract": true,
"properties": [
{
"name": "incomingConversationLinks",
"type": "ConversationLink",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "outgoingConversationLinks",
"type": "ConversationLink",
"isVirtual": true,
"isMany": true,
"isReference": true
}
]
},
{
"name": "Participant",
"superClass": [
"InteractionNode",
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "interfaceRef",
"type": "Interface",
"isMany": true,
"isReference": true
},
{
"name": "participantMultiplicity",
"type": "ParticipantMultiplicity"
},
{
"name": "endPointRefs",
"type": "EndPoint",
"isMany": true,
"isReference": true
},
{
"name": "processRef",
"type": "Process",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParticipantAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
},
{
"name": "outerParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParticipantMultiplicity",
"properties": [
{
"name": "minimum",
"default": 0,
"isAttr": true,
"type": "Integer"
},
{
"name": "maximum",
"default": 1,
"isAttr": true,
"type": "Integer"
}
]
},
{
"name": "Collaboration",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isClosed",
"isAttr": true,
"type": "Boolean"
},
{
"name": "participants",
"type": "Participant",
"isMany": true
},
{
"name": "messageFlows",
"type": "MessageFlow",
"isMany": true
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
},
{
"name": "conversations",
"type": "ConversationNode",
"isMany": true
},
{
"name": "conversationAssociations",
"type": "ConversationAssociation"
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
},
{
"name": "messageFlowAssociations",
"type": "MessageFlowAssociation",
"isMany": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
},
{
"name": "choreographyRef",
"type": "Choreography",
"isMany": true,
"isReference": true
},
{
"name": "conversationLinks",
"type": "ConversationLink",
"isMany": true
}
]
},
{
"name": "ChoreographyActivity",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "participantRefs",
"type": "Participant",
"isMany": true,
"isReference": true
},
{
"name": "initiatingParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
},
{
"name": "loopType",
"type": "ChoreographyLoopType",
"default": "None",
"isAttr": true
}
]
},
{
"name": "CallChoreography",
"superClass": [
"ChoreographyActivity"
],
"properties": [
{
"name": "calledChoreographyRef",
"type": "Choreography",
"isAttr": true,
"isReference": true
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
}
]
},
{
"name": "SubChoreography",
"superClass": [
"ChoreographyActivity",
"FlowElementsContainer"
],
"properties": [
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
}
]
},
{
"name": "ChoreographyTask",
"superClass": [
"ChoreographyActivity"
],
"properties": [
{
"name": "messageFlowRef",
"type": "MessageFlow",
"isMany": true,
"isReference": true
}
]
},
{
"name": "Choreography",
"superClass": [
"FlowElementsContainer",
"Collaboration"
]
},
{
"name": "GlobalChoreographyTask",
"superClass": [
"Choreography"
],
"properties": [
{
"name": "initiatingParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TextAnnotation",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "text",
"type": "String"
},
{
"name": "textFormat",
"default": "text/plain",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Group",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "categoryValueRef",
"type": "CategoryValue",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Association",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "associationDirection",
"type": "AssociationDirection",
"isAttr": true
},
{
"name": "sourceRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Category",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "categoryValue",
"type": "CategoryValue",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Artifact",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "CategoryValue",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "categorizedFlowElements",
"type": "FlowElement",
"isVirtual": true,
"isMany": true,
"isReference": true
},
{
"name": "value",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Activity",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "isForCompensation",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
},
{
"name": "ioSpecification",
"type": "InputOutputSpecification",
"xml": {
"serialize": "property"
}
},
{
"name": "boundaryEventRefs",
"type": "BoundaryEvent",
"isMany": true,
"isReference": true
},
{
"name": "properties",
"type": "Property",
"isMany": true
},
{
"name": "dataInputAssociations",
"type": "DataInputAssociation",
"isMany": true
},
{
"name": "dataOutputAssociations",
"type": "DataOutputAssociation",
"isMany": true
},
{
"name": "startQuantity",
"default": 1,
"isAttr": true,
"type": "Integer"
},
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
},
{
"name": "completionQuantity",
"default": 1,
"isAttr": true,
"type": "Integer"
},
{
"name": "loopCharacteristics",
"type": "LoopCharacteristics"
}
]
},
{
"name": "ServiceTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "SubProcess",
"superClass": [
"Activity",
"FlowElementsContainer",
"InteractionNode"
],
"properties": [
{
"name": "triggeredByEvent",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
}
]
},
{
"name": "LoopCharacteristics",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "MultiInstanceLoopCharacteristics",
"superClass": [
"LoopCharacteristics"
],
"properties": [
{
"name": "isSequential",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "behavior",
"type": "MultiInstanceBehavior",
"default": "All",
"isAttr": true
},
{
"name": "loopCardinality",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "loopDataInputRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "loopDataOutputRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "inputDataItem",
"type": "DataInput",
"xml": {
"serialize": "property"
}
},
{
"name": "outputDataItem",
"type": "DataOutput",
"xml": {
"serialize": "property"
}
},
{
"name": "complexBehaviorDefinition",
"type": "ComplexBehaviorDefinition",
"isMany": true
},
{
"name": "completionCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "oneBehaviorEventRef",
"type": "EventDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "noneBehaviorEventRef",
"type": "EventDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "StandardLoopCharacteristics",
"superClass": [
"LoopCharacteristics"
],
"properties": [
{
"name": "testBefore",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "loopCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "loopMaximum",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "CallActivity",
"superClass": [
"Activity"
],
"properties": [
{
"name": "calledElement",
"type": "String",
"isAttr": true
}
]
},
{
"name": "Task",
"superClass": [
"Activity",
"InteractionNode"
]
},
{
"name": "SendTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ReceiveTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "instantiate",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ScriptTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "scriptFormat",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"type": "String"
}
]
},
{
"name": "BusinessRuleTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "AdHocSubProcess",
"superClass": [
"SubProcess"
],
"properties": [
{
"name": "completionCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "ordering",
"type": "AdHocOrdering",
"isAttr": true
},
{
"name": "cancelRemainingInstances",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "Transaction",
"superClass": [
"SubProcess"
],
"properties": [
{
"name": "protocol",
"isAttr": true,
"type": "String"
},
{
"name": "method",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "GlobalScriptTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "scriptLanguage",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "GlobalBusinessRuleTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ComplexBehaviorDefinition",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "condition",
"type": "FormalExpression"
},
{
"name": "event",
"type": "ImplicitThrowEvent"
}
]
},
{
"name": "ResourceRole",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "resourceRef",
"type": "Resource",
"isReference": true
},
{
"name": "resourceParameterBindings",
"type": "ResourceParameterBinding",
"isMany": true
},
{
"name": "resourceAssignmentExpression",
"type": "ResourceAssignmentExpression"
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ResourceParameterBinding",
"properties": [
{
"name": "expression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "parameterRef",
"type": "ResourceParameter",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ResourceAssignmentExpression",
"properties": [
{
"name": "expression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
],
"superClass": [
"BaseElement"
]
},
{
"name": "Import",
"properties": [
{
"name": "importType",
"isAttr": true,
"type": "String"
},
{
"name": "location",
"isAttr": true,
"type": "String"
},
{
"name": "namespace",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Definitions",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "targetNamespace",
"isAttr": true,
"type": "String"
},
{
"name": "expressionLanguage",
"default": "http://www.w3.org/1999/XPath",
"isAttr": true,
"type": "String"
},
{
"name": "typeLanguage",
"default": "http://www.w3.org/2001/XMLSchema",
"isAttr": true,
"type": "String"
},
{
"name": "imports",
"type": "Import",
"isMany": true
},
{
"name": "extensions",
"type": "Extension",
"isMany": true
},
{
"name": "rootElements",
"type": "RootElement",
"isMany": true
},
{
"name": "diagrams",
"isMany": true,
"type": "bpmndi:BPMNDiagram"
},
{
"name": "exporter",
"isAttr": true,
"type": "String"
},
{
"name": "relationships",
"type": "Relationship",
"isMany": true
},
{
"name": "exporterVersion",
"isAttr": true,
"type": "String"
}
]
}
],
"enumerations": [
{
"name": "ProcessType",
"literalValues": [
{
"name": "None"
},
{
"name": "Public"
},
{
"name": "Private"
}
]
},
{
"name": "GatewayDirection",
"literalValues": [
{
"name": "Unspecified"
},
{
"name": "Converging"
},
{
"name": "Diverging"
},
{
"name": "Mixed"
}
]
},
{
"name": "EventBasedGatewayType",
"literalValues": [
{
"name": "Parallel"
},
{
"name": "Exclusive"
}
]
},
{
"name": "RelationshipDirection",
"literalValues": [
{
"name": "None"
},
{
"name": "Forward"
},
{
"name": "Backward"
},
{
"name": "Both"
}
]
},
{
"name": "ItemKind",
"literalValues": [
{
"name": "Physical"
},
{
"name": "Information"
}
]
},
{
"name": "ChoreographyLoopType",
"literalValues": [
{
"name": "None"
},
{
"name": "Standard"
},
{
"name": "MultiInstanceSequential"
},
{
"name": "MultiInstanceParallel"
}
]
},
{
"name": "AssociationDirection",
"literalValues": [
{
"name": "None"
},
{
"name": "One"
},
{
"name": "Both"
}
]
},
{
"name": "MultiInstanceBehavior",
"literalValues": [
{
"name": "None"
},
{
"name": "One"
},
{
"name": "All"
},
{
"name": "Complex"
}
]
},
{
"name": "AdHocOrdering",
"literalValues": [
{
"name": "Parallel"
},
{
"name": "Sequential"
}
]
}
],
"prefix": "bpmn",
"xml": {
"tagAlias": "lowerCase",
"typePrefix": "t"
}
}
},{}],104:[function(_dereq_,module,exports){
module.exports={
"name": "BPMNDI",
"uri": "http://www.omg.org/spec/BPMN/20100524/DI",
"types": [
{
"name": "BPMNDiagram",
"properties": [
{
"name": "plane",
"type": "BPMNPlane",
"redefines": "di:Diagram#rootElement"
},
{
"name": "labelStyle",
"type": "BPMNLabelStyle",
"isMany": true
}
],
"superClass": [
"di:Diagram"
]
},
{
"name": "BPMNPlane",
"properties": [
{
"name": "bpmnElement",
"isAttr": true,
"isReference": true,
"type": "bpmn:BaseElement",
"redefines": "di:DiagramElement#modelElement"
}
],
"superClass": [
"di:Plane"
]
},
{
"name": "BPMNShape",
"properties": [
{
"name": "bpmnElement",
"isAttr": true,
"isReference": true,
"type": "bpmn:BaseElement",
"redefines": "di:DiagramElement#modelElement"
},
{
"name": "isHorizontal",
"isAttr": true,
"type": "Boolean"
},
{
"name": "isExpanded",
"isAttr": true,
"type": "Boolean"
},
{
"name": "isMarkerVisible",
"isAttr": true,
"type": "Boolean"
},
{
"name": "label",
"type": "BPMNLabel"
},
{
"name": "isMessageVisible",
"isAttr": true,
"type": "Boolean"
},
{
"name": "participantBandKind",
"type": "ParticipantBandKind",
"isAttr": true
},
{
"name": "choreographyActivityShape",
"type": "BPMNShape",
"isAttr": true,
"isReference": true
}
],
"superClass": [
"di:LabeledShape"
]
},
{
"name": "BPMNEdge",
"properties": [
{
"name": "label",
"type": "BPMNLabel"
},
{
"name": "bpmnElement",
"isAttr": true,
"isReference": true,
"type": "bpmn:BaseElement",
"redefines": "di:DiagramElement#modelElement"
},
{
"name": "sourceElement",
"isAttr": true,
"isReference": true,
"type": "di:DiagramElement",
"redefines": "di:Edge#source"
},
{
"name": "targetElement",
"isAttr": true,
"isReference": true,
"type": "di:DiagramElement",
"redefines": "di:Edge#target"
},
{
"name": "messageVisibleKind",
"type": "MessageVisibleKind",
"isAttr": true,
"default": "initiating"
}
],
"superClass": [
"di:LabeledEdge"
]
},
{
"name": "BPMNLabel",
"properties": [
{
"name": "labelStyle",
"type": "BPMNLabelStyle",
"isAttr": true,
"isReference": true,
"redefines": "di:DiagramElement#style"
}
],
"superClass": [
"di:Label"
]
},
{
"name": "BPMNLabelStyle",
"properties": [
{
"name": "font",
"type": "dc:Font"
}
],
"superClass": [
"di:Style"
]
}
],
"enumerations": [
{
"name": "ParticipantBandKind",
"literalValues": [
{
"name": "top_initiating"
},
{
"name": "middle_initiating"
},
{
"name": "bottom_initiating"
},
{
"name": "top_non_initiating"
},
{
"name": "middle_non_initiating"
},
{
"name": "bottom_non_initiating"
}
]
},
{
"name": "MessageVisibleKind",
"literalValues": [
{
"name": "initiating"
},
{
"name": "non_initiating"
}
]
}
],
"associations": [],
"prefix": "bpmndi"
}
},{}],105:[function(_dereq_,module,exports){
module.exports={
"name": "DC",
"uri": "http://www.omg.org/spec/DD/20100524/DC",
"types": [
{
"name": "Boolean"
},
{
"name": "Integer"
},
{
"name": "Real"
},
{
"name": "String"
},
{
"name": "Font",
"properties": [
{
"name": "name",
"type": "String",
"isAttr": true
},
{
"name": "size",
"type": "Real",
"isAttr": true
},
{
"name": "isBold",
"type": "Boolean",
"isAttr": true
},
{
"name": "isItalic",
"type": "Boolean",
"isAttr": true
},
{
"name": "isUnderline",
"type": "Boolean",
"isAttr": true
},
{
"name": "isStrikeThrough",
"type": "Boolean",
"isAttr": true
}
]
},
{
"name": "Point",
"properties": [
{
"name": "x",
"type": "Real",
"default": "0",
"isAttr": true
},
{
"name": "y",
"type": "Real",
"default": "0",
"isAttr": true
}
]
},
{
"name": "Bounds",
"properties": [
{
"name": "x",
"type": "Real",
"default": "0",
"isAttr": true
},
{
"name": "y",
"type": "Real",
"default": "0",
"isAttr": true
},
{
"name": "width",
"type": "Real",
"isAttr": true
},
{
"name": "height",
"type": "Real",
"isAttr": true
}
]
}
],
"prefix": "dc",
"associations": []
}
},{}],106:[function(_dereq_,module,exports){
module.exports={
"name": "DI",
"uri": "http://www.omg.org/spec/DD/20100524/DI",
"types": [
{
"name": "DiagramElement",
"isAbstract": true,
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true,
"isId": true
},
{
"name": "extension",
"type": "Extension"
},
{
"name": "owningDiagram",
"type": "Diagram",
"isReadOnly": true,
"isVirtual": true,
"isReference": true
},
{
"name": "owningElement",
"type": "DiagramElement",
"isReadOnly": true,
"isVirtual": true,
"isReference": true
},
{
"name": "modelElement",
"isReadOnly": true,
"isVirtual": true,
"isReference": true,
"type": "Element"
},
{
"name": "style",
"type": "Style",
"isReadOnly": true,
"isVirtual": true,
"isReference": true
},
{
"name": "ownedElement",
"type": "DiagramElement",
"isReadOnly": true,
"isVirtual": true,
"isMany": true
}
]
},
{
"name": "Node",
"isAbstract": true,
"superClass": [
"DiagramElement"
]
},
{
"name": "Edge",
"isAbstract": true,
"superClass": [
"DiagramElement"
],
"properties": [
{
"name": "source",
"type": "DiagramElement",
"isReadOnly": true,
"isVirtual": true,
"isReference": true
},
{
"name": "target",
"type": "DiagramElement",
"isReadOnly": true,
"isVirtual": true,
"isReference": true
},
{
"name": "waypoint",
"isUnique": false,
"isMany": true,
"type": "dc:Point",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "Diagram",
"isAbstract": true,
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true,
"isId": true
},
{
"name": "rootElement",
"type": "DiagramElement",
"isReadOnly": true,
"isVirtual": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "documentation",
"isAttr": true,
"type": "String"
},
{
"name": "resolution",
"isAttr": true,
"type": "Real"
},
{
"name": "ownedStyle",
"type": "Style",
"isReadOnly": true,
"isVirtual": true,
"isMany": true
}
]
},
{
"name": "Shape",
"isAbstract": true,
"superClass": [
"Node"
],
"properties": [
{
"name": "bounds",
"type": "dc:Bounds"
}
]
},
{
"name": "Plane",
"isAbstract": true,
"superClass": [
"Node"
],
"properties": [
{
"name": "planeElement",
"type": "DiagramElement",
"subsettedProperty": "DiagramElement-ownedElement",
"isMany": true
}
]
},
{
"name": "LabeledEdge",
"isAbstract": true,
"superClass": [
"Edge"
],
"properties": [
{
"name": "ownedLabel",
"type": "Label",
"isReadOnly": true,
"subsettedProperty": "DiagramElement-ownedElement",
"isVirtual": true,
"isMany": true
}
]
},
{
"name": "LabeledShape",
"isAbstract": true,
"superClass": [
"Shape"
],
"properties": [
{
"name": "ownedLabel",
"type": "Label",
"isReadOnly": true,
"subsettedProperty": "DiagramElement-ownedElement",
"isVirtual": true,
"isMany": true
}
]
},
{
"name": "Label",
"isAbstract": true,
"superClass": [
"Node"
],
"properties": [
{
"name": "bounds",
"type": "dc:Bounds"
}
]
},
{
"name": "Style",
"isAbstract": true,
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true,
"isId": true
}
]
},
{
"name": "Extension",
"properties": [
{
"name": "values",
"type": "Element",
"isMany": true
}
]
}
],
"associations": [],
"prefix": "di",
"xml": {
"tagAlias": "lowerCase"
}
}
},{}],107:[function(_dereq_,module,exports){
/**
* Module dependencies.
*/
try {
var index = _dereq_(111);
} catch (err) {
var index = _dereq_(111);
}
/**
* Whitespace regexp.
*/
var re = /\s+/;
/**
* toString reference.
*/
var toString = Object.prototype.toString;
/**
* Wrap `el` in a `ClassList`.
*
* @param {Element} el
* @return {ClassList}
* @api public
*/
module.exports = function(el){
return new ClassList(el);
};
/**
* Initialize a new ClassList for `el`.
*
* @param {Element} el
* @api private
*/
function ClassList(el) {
if (!el || !el.nodeType) {
throw new Error('A DOM element reference is required');
}
this.el = el;
this.list = el.classList;
}
/**
* Add class `name` if not already present.
*
* @param {String} name
* @return {ClassList}
* @api public
*/
ClassList.prototype.add = function(name){
// classList
if (this.list) {
this.list.add(name);
return this;
}
// fallback
var arr = this.array();
var i = index(arr, name);
if (!~i) arr.push(name);
this.el.className = arr.join(' ');
return this;
};
/**
* Remove class `name` when present, or
* pass a regular expression to remove
* any which match.
*
* @param {String|RegExp} name
* @return {ClassList}
* @api public
*/
ClassList.prototype.remove = function(name){
if ('[object RegExp]' == toString.call(name)) {
return this.removeMatching(name);
}
// classList
if (this.list) {
this.list.remove(name);
return this;
}
// fallback
var arr = this.array();
var i = index(arr, name);
if (~i) arr.splice(i, 1);
this.el.className = arr.join(' ');
return this;
};
/**
* Remove all classes matching `re`.
*
* @param {RegExp} re
* @return {ClassList}
* @api private
*/
ClassList.prototype.removeMatching = function(re){
var arr = this.array();
for (var i = 0; i < arr.length; i++) {
if (re.test(arr[i])) {
this.remove(arr[i]);
}
}
return this;
};
/**
* Toggle class `name`, can force state via `force`.
*
* For browsers that support classList, but do not support `force` yet,
* the mistake will be detected and corrected.
*
* @param {String} name
* @param {Boolean} force
* @return {ClassList}
* @api public
*/
ClassList.prototype.toggle = function(name, force){
// classList
if (this.list) {
if ("undefined" !== typeof force) {
if (force !== this.list.toggle(name, force)) {
this.list.toggle(name); // toggle again to correct
}
} else {
this.list.toggle(name);
}
return this;
}
// fallback
if ("undefined" !== typeof force) {
if (!force) {
this.remove(name);
} else {
this.add(name);
}
} else {
if (this.has(name)) {
this.remove(name);
} else {
this.add(name);
}
}
return this;
};
/**
* Return an array of classes.
*
* @return {Array}
* @api public
*/
ClassList.prototype.array = function(){
var className = this.el.getAttribute('class') || '';
var str = className.replace(/^\s+|\s+$/g, '');
var arr = str.split(re);
if ('' === arr[0]) arr.shift();
return arr;
};
/**
* Check if class `name` is present.
*
* @param {String} name
* @return {ClassList}
* @api public
*/
ClassList.prototype.has =
ClassList.prototype.contains = function(name){
return this.list
? this.list.contains(name)
: !! ~index(this.array(), name);
};
},{"111":111}],108:[function(_dereq_,module,exports){
var matches = _dereq_(112)
module.exports = function (element, selector, checkYoSelf, root) {
element = checkYoSelf ? {parentNode: element} : element
root = root || document
// Make sure `element !== document` and `element != null`
// otherwise we get an illegal invocation
while ((element = element.parentNode) && element !== document) {
if (matches(element, selector))
return element
// After `matches` on the edge case that
// the selector matches the root
// (when the root is not the document)
if (element === root)
return
}
}
},{"112":112}],109:[function(_dereq_,module,exports){
/**
* Module dependencies.
*/
try {
var closest = _dereq_(108);
} catch(err) {
var closest = _dereq_(108);
}
try {
var event = _dereq_(110);
} catch(err) {
var event = _dereq_(110);
}
/**
* Delegate event `type` to `selector`
* and invoke `fn(e)`. A callback function
* is returned which may be passed to `.unbind()`.
*
* @param {Element} el
* @param {String} selector
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @return {Function}
* @api public
*/
exports.bind = function(el, selector, type, fn, capture){
return event.bind(el, type, function(e){
var target = e.target || e.srcElement;
e.delegateTarget = closest(target, selector, true, el);
if (e.delegateTarget) fn.call(el, e);
}, capture);
};
/**
* Unbind event `type`'s callback `fn`.
*
* @param {Element} el
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @api public
*/
exports.unbind = function(el, type, fn, capture){
event.unbind(el, type, fn, capture);
};
},{"108":108,"110":110}],110:[function(_dereq_,module,exports){
var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
prefix = bind !== 'addEventListener' ? 'on' : '';
/**
* Bind `el` event `type` to `fn`.
*
* @param {Element} el
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @return {Function}
* @api public
*/
exports.bind = function(el, type, fn, capture){
el[bind](prefix + type, fn, capture || false);
return fn;
};
/**
* Unbind `el` event `type`'s callback `fn`.
*
* @param {Element} el
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @return {Function}
* @api public
*/
exports.unbind = function(el, type, fn, capture){
el[unbind](prefix + type, fn, capture || false);
return fn;
};
},{}],111:[function(_dereq_,module,exports){
module.exports = function(arr, obj){
if (arr.indexOf) return arr.indexOf(obj);
for (var i = 0; i < arr.length; ++i) {
if (arr[i] === obj) return i;
}
return -1;
};
},{}],112:[function(_dereq_,module,exports){
/**
* Module dependencies.
*/
try {
var query = _dereq_(113);
} catch (err) {
var query = _dereq_(113);
}
/**
* Element prototype.
*/
var proto = Element.prototype;
/**
* Vendor function.
*/
var vendor = proto.matches
|| proto.webkitMatchesSelector
|| proto.mozMatchesSelector
|| proto.msMatchesSelector
|| proto.oMatchesSelector;
/**
* Expose `match()`.
*/
module.exports = match;
/**
* Match `el` to `selector`.
*
* @param {Element} el
* @param {String} selector
* @return {Boolean}
* @api public
*/
function match(el, selector) {
if (!el || el.nodeType !== 1) return false;
if (vendor) return vendor.call(el, selector);
var nodes = query.all(selector, el.parentNode);
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == el) return true;
}
return false;
}
},{"113":113}],113:[function(_dereq_,module,exports){
function one(selector, el) {
return el.querySelector(selector);
}
exports = module.exports = function(selector, el){
el = el || document;
return one(selector, el);
};
exports.all = function(selector, el){
el = el || document;
return el.querySelectorAll(selector);
};
exports.engine = function(obj){
if (!obj.one) throw new Error('.one callback required');
if (!obj.all) throw new Error('.all callback required');
one = obj.one;
exports.all = obj.all;
return exports;
};
},{}],114:[function(_dereq_,module,exports){
module.exports = {
__depends__: [ _dereq_(170) ],
__init__: [ 'directEditing' ],
directEditing: [ 'type', _dereq_(115) ]
};
},{"115":115,"170":170}],115:[function(_dereq_,module,exports){
'use strict';
var bind = _dereq_(312),
find = _dereq_(302);
var TextBox = _dereq_(116);
/**
* A direct editing component that allows users
* to edit an elements text directly in the diagram
*
* @param {EventBus} eventBus the event bus
*/
function DirectEditing(eventBus, canvas) {
this._eventBus = eventBus;
this._providers = [];
this._textbox = new TextBox({
container: canvas.getContainer(),
keyHandler: bind(this._handleKey, this),
resizeHandler: bind(this._handleResize, this)
});
}
DirectEditing.$inject = [ 'eventBus', 'canvas' ];
/**
* Register a direct editing provider
* @param {Object} provider the provider, must expose an #activate(element) method that returns
* an activation context ({ bounds: {x, y, width, height }, text }) if
* direct editing is available for the given element.
* Additionally the provider must expose a #update(element, value) method
* to receive direct editing updates.
*/
DirectEditing.prototype.registerProvider = function(provider) {
this._providers.push(provider);
};
/**
* Returns true if direct editing is currently active
*
* @return {Boolean}
*/
DirectEditing.prototype.isActive = function() {
return !!this._active;
};
/**
* Cancel direct editing, if it is currently active
*/
DirectEditing.prototype.cancel = function() {
if (!this._active) {
return;
}
this._fire('cancel');
this.close();
};
DirectEditing.prototype._fire = function(event, context) {
this._eventBus.fire('directEditing.' + event, context || { active: this._active });
};
DirectEditing.prototype.close = function() {
this._textbox.destroy();
this._fire('deactivate');
this._active = null;
this.resizable = undefined;
};
DirectEditing.prototype.complete = function() {
var active = this._active;
if (!active) {
return;
}
var text = this.getValue();
var bounds = this.$textbox.getBoundingClientRect();
if (text !== active.context.text || this.resizable) {
active.provider.update(active.element, text, active.context.text, {
x: bounds.top,
y: bounds.left,
width: bounds.width,
height: bounds.height
});
}
this._fire('complete');
this.close();
};
DirectEditing.prototype.getValue = function() {
return this._textbox.getValue();
};
DirectEditing.prototype._handleKey = function(e) {
// stop bubble
e.stopPropagation();
var key = e.keyCode || e.charCode;
// ESC
if (key === 27) {
e.preventDefault();
return this.cancel();
}
// Enter
if (key === 13 && !e.shiftKey) {
e.preventDefault();
return this.complete();
}
};
DirectEditing.prototype._handleResize = function(event) {
this._fire('resize', event);
};
/**
* Activate direct editing on the given element
*
* @param {Object} ElementDescriptor the descriptor for a shape or connection
* @return {Boolean} true if the activation was possible
*/
DirectEditing.prototype.activate = function(element) {
if (this.isActive()) {
this.cancel();
}
// the direct editing context
var context;
var provider = find(this._providers, function(p) {
return (context = p.activate(element)) ? p : null;
});
// check if activation took place
if (context) {
this.$textbox = this._textbox.create(
context.bounds,
context.style,
context.text,
context.options
);
this._active = {
element: element,
context: context,
provider: provider
};
if (context.options && context.options.resizable) {
this.resizable = true;
}
this._fire('activate');
}
return !!context;
};
module.exports = DirectEditing;
},{"116":116,"302":302,"312":312}],116:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
bind = _dereq_(312),
pick = _dereq_(438);
var domAttr = _dereq_(444),
domClasses = _dereq_(445),
domEvent = _dereq_(450),
domRemove = _dereq_(453);
var min = Math.min,
max = Math.max;
function preventDefault(e) {
e.preventDefault();
}
function stopPropagation(e) {
e.stopPropagation();
}
function isTextNode(node) {
return node.nodeType === Node.TEXT_NODE;
}
function toArray(nodeList) {
return [].slice.call(nodeList);
}
/**
* Initializes a container for a content editable div.
*
* Structure:
*
* container
* parent
* content
* handle
*
* @param {object} options
* @param {DOMElement} options.container The DOM element to append the contentContainer to
* @param {Function} options.keyHandler Handler for key events
* @param {Function} options.resizeHandler Handler for resize events
*/
function TextBox(options) {
this.container = options.container;
this.parent = document.createElement('div');
this.content = document.createElement('div');
this.parent.appendChild(this.content);
domAttr(this.content, 'contentEditable', 'true');
this.keyHandler = options.keyHandler || function() {};
this.resizeHandler = options.resizeHandler || function() {};
this.autoResize = bind(this.autoResize, this);
this.handlePaste = bind(this.handlePaste, this);
}
module.exports = TextBox;
/**
* Create a text box with the given position, size, style and text content
*
* @param {Object} bounds
* @param {Number} bounds.x absolute x position
* @param {Number} bounds.y absolute y position
* @param {Number} [bounds.width] fixed width value
* @param {Number} [bounds.height] fixed height value
* @param {Number} [bounds.maxWidth] maximum width value
* @param {Number} [bounds.maxHeight] maximum height value
* @param {Number} [bounds.minWidth] minimum width value
* @param {Number} [bounds.minHeight] minimum height value
* @param {Object} [style]
* @param {String} value text content
*
* @return {DOMElement} The created content DOM element
*/
TextBox.prototype.create = function(bounds, style, value, options) {
var self = this;
var parent = this.parent,
content = this.content,
container = this.container;
options = this.options = options || {};
style = this.style = style || {};
var parentStyle = pick(style, [
'width',
'height',
'maxWidth',
'maxHeight',
'minWidth',
'minHeight',
'left',
'top',
'backgroundColor',
'position',
'overflow',
'border',
'wordWrap',
'textAlign',
'outline',
'transform'
]);
assign(parent.style, {
width: bounds.width + 'px',
height: bounds.height + 'px',
maxWidth: bounds.maxWidth + 'px',
maxHeight: bounds.maxHeight + 'px',
minWidth: bounds.minWidth + 'px',
minHeight: bounds.minHeight + 'px',
left: bounds.x + 'px',
top: bounds.y + 'px',
backgroundColor: '#ffffff',
position: 'absolute',
overflow: 'visible',
border: '1px solid #ccc',
wordWrap: 'normal',
textAlign: 'center',
outline: 'none'
}, parentStyle);
var contentStyle = pick(style, [
'fontSize',
'lineHeight',
'padding',
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft'
]);
assign(content.style, {
boxSizing: 'border-box',
width: '100%',
outline: 'none'
}, contentStyle);
if (options.centerVertically) {
assign(content.style, {
position: 'absolute',
top: '50%',
transform: 'translate(0, -50%)'
}, contentStyle);
}
content.innerText = value;
domEvent.bind(content, 'keydown', this.keyHandler);
domEvent.bind(content, 'mousedown', stopPropagation);
domEvent.bind(content, 'paste', self.handlePaste);
if (options.autoResize) {
domEvent.bind(content, 'input', this.autoResize);
}
if (options.resizable) {
this.resizable(style);
}
container.appendChild(parent);
// set selection to end of text
this.setSelection(content.lastChild, content.lastChild && content.lastChild.length);
return parent;
};
/**
* Intercept paste events to remove formatting from pasted text.
*/
TextBox.prototype.handlePaste = function(e) {
var self = this;
var options = this.options,
style = this.style;
e.preventDefault();
var text;
if (e.clipboardData) {
// Chrome, Firefox, Safari
text = e.clipboardData.getData('text/plain');
} else {
// Internet Explorer
text = window.clipboardData.getData('Text');
}
// insertHTML command not supported by Internet Explorer
var success = document.execCommand('insertHTML', false, text);
if (!success) {
// Internet Explorer
var range = this.getSelection(),
startContainer = range.startContainer,
endContainer = range.endContainer,
startOffset = range.startOffset,
endOffset = range.endOffset,
commonAncestorContainer = range.commonAncestorContainer;
var childNodesArray = toArray(commonAncestorContainer.childNodes);
var container,
offset;
if (isTextNode(commonAncestorContainer)) {
var containerTextContent = startContainer.textContent;
startContainer.textContent =
containerTextContent.substring(0, startOffset)
+ text
+ containerTextContent.substring(endOffset);
container = startContainer;
offset = startOffset + text.length;
} else if (startContainer === this.content && endContainer === this.content) {
var textNode = document.createTextNode(text);
this.content.insertBefore(textNode, childNodesArray[startOffset]);
container = textNode;
offset = textNode.textContent.length;
} else {
var startContainerChildIndex = childNodesArray.indexOf(startContainer),
endContainerChildIndex = childNodesArray.indexOf(endContainer);
childNodesArray.forEach(function(childNode, index) {
if (index === startContainerChildIndex) {
childNode.textContent =
startContainer.textContent.substring(0, startOffset) +
text +
endContainer.textContent.substring(endOffset);
} else if (index > startContainerChildIndex && index <= endContainerChildIndex) {
domRemove(childNode);
}
});
container = startContainer;
offset = startOffset + text.length;
}
if (container && offset !== undefined) {
// is necessary in Internet Explorer
setTimeout(function() {
self.setSelection(container, offset);
});
}
}
if (options.autoResize) {
var hasResized = this.autoResize(style);
if (hasResized) {
this.resizeHandler(hasResized);
}
}
};
/**
* Automatically resize element vertically to fit its content.
*/
TextBox.prototype.autoResize = function() {
var parent = this.parent,
content = this.content;
var fontSize = parseInt(this.style.fontSize) || 12;
if (content.scrollHeight > parent.offsetHeight ||
content.scrollHeight < parent.offsetHeight - fontSize) {
var bounds = parent.getBoundingClientRect();
var height = content.scrollHeight;
parent.style.height = height + 'px';
this.resizeHandler({
width: bounds.width,
height: bounds.height,
dx: 0,
dy: height - bounds.height
});
}
};
/**
* Make an element resizable by adding a resize handle.
*/
TextBox.prototype.resizable = function() {
var self = this;
var parent = this.parent,
resizeHandle = this.resizeHandle;
var minWidth = parseInt(this.style.minWidth) || 0,
minHeight = parseInt(this.style.minHeight) || 0,
maxWidth = parseInt(this.style.maxWidth) || Infinity,
maxHeight = parseInt(this.style.maxHeight) || Infinity;
if (!resizeHandle) {
resizeHandle = this.resizeHandle = document.createElement('div');
domClasses(resizeHandle).add('djs-direct-editing-resize-handle');
var startX, startY, startWidth, startHeight;
var onMouseDown = function(e) {
preventDefault(e);
stopPropagation(e);
startX = e.clientX;
startY = e.clientY;
var bounds = parent.getBoundingClientRect();
startWidth = bounds.width;
startHeight = bounds.height;
domEvent.bind(document, 'mousemove', onMouseMove);
domEvent.bind(document, 'mouseup', onMouseUp);
};
var onMouseMove = function(e) {
preventDefault(e);
stopPropagation(e);
var newWidth = min(max(startWidth + e.clientX - startX, minWidth), maxWidth);
var newHeight = min(max(startHeight + e.clientY - startY, minHeight), maxHeight);
parent.style.width = newWidth + 'px';
parent.style.height = newHeight + 'px';
self.resizeHandler({
width: startWidth,
height: startHeight,
dx: e.clientX - startX,
dy: e.clientY - startY
});
};
var onMouseUp = function(e) {
preventDefault(e);
stopPropagation(e);
domEvent.unbind(document,'mousemove', onMouseMove, false);
domEvent.unbind(document, 'mouseup', onMouseUp, false);
};
domEvent.bind(resizeHandle, 'mousedown', onMouseDown);
}
assign(resizeHandle.style, {
position: 'absolute',
bottom: '0px',
right: '0px',
cursor: 'nwse-resize',
width: '0',
height: '0',
borderTop: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent',
borderRight: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
borderBottom: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid #ccc',
borderLeft: (parseInt(this.style.fontSize) / 4 || 3) + 'px solid transparent'
});
parent.appendChild(resizeHandle);
};
/**
* Clear content and style of the textbox, unbind listeners and
* reset CSS style.
*/
TextBox.prototype.destroy = function() {
var parent = this.parent,
content = this.content,
resizeHandle = this.resizeHandle;
// clear content
content.innerText = '';
// clear styles
parent.removeAttribute('style');
content.removeAttribute('style');
domEvent.unbind(content, 'keydown', this.keyHandler);
domEvent.unbind(content, 'mousedown', stopPropagation);
domEvent.unbind(content, 'input', this.autoResize);
domEvent.unbind(content, 'paste', this.handlePaste);
if (resizeHandle) {
resizeHandle.removeAttribute('style');
domRemove(resizeHandle);
}
domRemove(parent);
};
TextBox.prototype.getValue = function() {
return this.content.innerText;
};
TextBox.prototype.getSelection = function() {
var selection = window.getSelection(),
range = selection.getRangeAt(0);
return range;
};
TextBox.prototype.setSelection = function(container, offset) {
var range = document.createRange();
if (container === null) {
range.selectNodeContents(this.content);
} else {
range.setStart(container, offset);
range.setEnd(container, offset);
}
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
};
},{"312":312,"431":431,"438":438,"444":444,"445":445,"450":450,"453":453}],117:[function(_dereq_,module,exports){
module.exports = _dereq_(118);
},{"118":118}],118:[function(_dereq_,module,exports){
'use strict';
var di = _dereq_(282);
/**
* Bootstrap an injector from a list of modules, instantiating a number of default components
*
* @ignore
* @param {Array} bootstrapModules
*
* @return {didi.Injector} a injector to use to access the components
*/
function bootstrap(bootstrapModules) {
var modules = [],
components = [];
function hasModule(m) {
return modules.indexOf(m) >= 0;
}
function addModule(m) {
modules.push(m);
}
function visit(m) {
if (hasModule(m)) {
return;
}
(m.__depends__ || []).forEach(visit);
if (hasModule(m)) {
return;
}
addModule(m);
(m.__init__ || []).forEach(function(c) {
components.push(c);
});
}
bootstrapModules.forEach(visit);
var injector = new di.Injector(modules);
components.forEach(function(c) {
try {
// eagerly resolve component (fn or string)
injector[typeof c === 'string' ? 'get' : 'invoke'](c);
} catch (e) {
console.error('Failed to instantiate component');
console.error(e.stack);
throw e;
}
});
return injector;
}
/**
* Creates an injector from passed options.
*
* @ignore
* @param {Object} options
* @return {didi.Injector}
*/
function createInjector(options) {
options = options || {};
var configModule = {
'config': ['value', options]
};
var coreModule = _dereq_(127);
var modules = [ configModule, coreModule ].concat(options.modules || []);
return bootstrap(modules);
}
/**
* The main diagram-js entry point that bootstraps the diagram with the given
* configuration.
*
* To register extensions with the diagram, pass them as Array to the constructor.
*
* @class djs.Diagram
* @memberOf djs
* @constructor
*
* @example
*
* Creating a plug-in that logs whenever a shape is added to the canvas.
*
* // plug-in implemenentation
* function MyLoggingPlugin(eventBus) {
* eventBus.on('shape.added', function(event) {
* console.log('shape ', event.shape, ' was added to the diagram');
* });
* }
*
* // export as module
* module.exports = {
* __init__: [ 'myLoggingPlugin' ],
* myLoggingPlugin: [ 'type', MyLoggingPlugin ]
* };
*
*
* // instantiate the diagram with the new plug-in
*
* var diagram = new Diagram({ modules: [ require('path-to-my-logging-plugin') ] });
*
* diagram.invoke([ 'canvas', function(canvas) {
* // add shape to drawing canvas
* canvas.addShape({ x: 10, y: 10 });
* });
*
* // 'shape ... was added to the diagram' logged to console
*
* @param {Object} options
* @param {Array} [options.modules] external modules to instantiate with the diagram
* @param {didi.Injector} [injector] an (optional) injector to bootstrap the diagram with
*/
function Diagram(options, injector) {
// create injector unless explicitly specified
this.injector = injector = injector || createInjector(options);
// API
/**
* Resolves a diagram service
*
* @method Diagram#get
*
* @param {String} name the name of the diagram service to be retrieved
* @param {Boolean} [strict=true] if false, resolve missing services to null
*/
this.get = injector.get;
/**
* Executes a function into which diagram services are injected
*
* @method Diagram#invoke
*
* @param {Function|Object[]} fn the function to resolve
* @param {Object} locals a number of locals to use to resolve certain dependencies
*/
this.invoke = injector.invoke;
// init
// indicate via event
/**
* An event indicating that all plug-ins are loaded.
*
* Use this event to fire other events to interested plug-ins
*
* @memberOf Diagram
*
* @event diagram.init
*
* @example
*
* eventBus.on('diagram.init', function() {
* eventBus.fire('my-custom-event', { foo: 'BAR' });
* });
*
* @type {Object}
*/
this.get('eventBus').fire('diagram.init');
}
module.exports = Diagram;
/**
* Destroys the diagram
*
* @method Diagram#destroy
*/
Diagram.prototype.destroy = function() {
this.get('eventBus').fire('diagram.destroy');
};
/**
* Clear the diagram, removing all contents.
*/
Diagram.prototype.clear = function() {
this.get('eventBus').fire('diagram.clear');
};
},{"127":127,"282":282}],119:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
isFunction = _dereq_(423),
isArray = _dereq_(422),
isNumber = _dereq_(425);
var DEFAULT_PRIORITY = 1000;
function isObject(element) {
return typeof element === 'object';
}
/**
* A utility that can be used to plug-in into the command execution for
* extension and/or validation.
*
* @param {EventBus} eventBus
*
* @example
*
* var inherits = require('inherits');
*
* var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
*
* function CommandLogger(eventBus) {
* CommandInterceptor.call(this, eventBus);
*
* this.preExecute(function(event) {
* console.log('command pre-execute', event);
* });
* }
*
* inherits(CommandLogger, CommandInterceptor);
*
*/
function CommandInterceptor(eventBus) {
this._eventBus = eventBus;
}
CommandInterceptor.$inject = [ 'eventBus' ];
module.exports = CommandInterceptor;
function unwrapEvent(fn, that) {
return function(event) {
return fn.call(that || null, event.context, event.command, event);
};
}
/**
* Register an interceptor for a command execution
*
* @param {String|Array} [events] list of commands to register on
* @param {String} [hook] command hook, i.e. preExecute, executed to listen on
* @param {Number} [priority] the priority on which to hook into the execution
* @param {Function} handlerFn interceptor to be invoked with (event)
* @param {Boolean} unwrap if true, unwrap the event and pass (context, command, event) to the
* listener instead
* @param {Object} [that] Pass context (`this`) to the handler function
*/
CommandInterceptor.prototype.on = function(events, hook, priority, handlerFn, unwrap, that) {
if (isFunction(hook) || isNumber(hook)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = hook;
hook = null;
}
if (isFunction(priority)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = DEFAULT_PRIORITY;
}
if (isObject(unwrap)) {
that = unwrap;
unwrap = false;
}
if (!isFunction(handlerFn)) {
throw new Error('handlerFn must be a function');
}
if (!isArray(events)) {
events = [ events ];
}
var eventBus = this._eventBus;
forEach(events, function(event) {
// concat commandStack(.event)?(.hook)?
var fullEvent = [ 'commandStack', event, hook ].filter(function(e) { return e; }).join('.');
eventBus.on(fullEvent, priority, unwrap ? unwrapEvent(handlerFn, that) : handlerFn, that);
});
};
var hooks = [
'canExecute',
'preExecute',
'preExecuted',
'execute',
'executed',
'postExecute',
'postExecuted',
'revert',
'reverted'
];
/*
* Install hook shortcuts
*
* This will generate the CommandInterceptor#(preExecute|...|reverted) methods
* which will in term forward to CommandInterceptor#on.
*/
forEach(hooks, function(hook) {
/**
* {canExecute|preExecute|preExecuted|execute|executed|postExecute|postExecuted|revert|reverted}
*
* A named hook for plugging into the command execution
*
* @param {String|Array} [events] list of commands to register on
* @param {Number} [priority] the priority on which to hook into the execution
* @param {Function} handlerFn interceptor to be invoked with (event)
* @param {Boolean} [unwrap=false] if true, unwrap the event and pass (context, command, event) to the
* listener instead
* @param {Object} [that] Pass context (`this`) to the handler function
*/
CommandInterceptor.prototype[hook] = function(events, priority, handlerFn, unwrap, that) {
if (isFunction(events) || isNumber(events)) {
that = unwrap;
unwrap = handlerFn;
handlerFn = priority;
priority = events;
events = null;
}
this.on(events, hook, priority, handlerFn, unwrap, that);
};
});
},{"303":303,"422":422,"423":423,"425":425}],120:[function(_dereq_,module,exports){
'use strict';
var unique = _dereq_(296),
isArray = _dereq_(422),
assign = _dereq_(431);
var InternalEvent = _dereq_(125).Event;
/**
* A service that offers un- and redoable execution of commands.
*
* The command stack is responsible for executing modeling actions
* in a un- and redoable manner. To do this it delegates the actual
* command execution to {@link CommandHandler}s.
*
* Command handlers provide {@link CommandHandler#execute(ctx)} and
* {@link CommandHandler#revert(ctx)} methods to un- and redo a command
* identified by a command context.
*
*
* ## Life-Cycle events
*
* In the process the command stack fires a number of life-cycle events
* that other components to participate in the command execution.
*
* * preExecute
* * preExecuted
* * execute
* * executed
* * postExecute
* * postExecuted
* * revert
* * reverted
*
* A special event is used for validating, whether a command can be
* performed prior to its execution.
*
* * canExecute
*
* Each of the events is fired as `commandStack.{eventName}` and
* `commandStack.{commandName}.{eventName}`, respectively. This gives
* components fine grained control on where to hook into.
*
* The event object fired transports `command`, the name of the
* command and `context`, the command context.
*
*
* ## Creating Command Handlers
*
* Command handlers should provide the {@link CommandHandler#execute(ctx)}
* and {@link CommandHandler#revert(ctx)} methods to implement
* redoing and undoing of a command.
*
* A command handler _must_ ensure undo is performed properly in order
* not to break the undo chain. It must also return the shapes that
* got changed during the `execute` and `revert` operations.
*
* Command handlers may execute other modeling operations (and thus
* commands) in their `preExecute` and `postExecute` phases. The command
* stack will properly group all commands together into a logical unit
* that may be re- and undone atomically.
*
* Command handlers must not execute other commands from within their
* core implementation (`execute`, `revert`).
*
*
* ## Change Tracking
*
* During the execution of the CommandStack it will keep track of all
* elements that have been touched during the command's execution.
*
* At the end of the CommandStack execution it will notify interested
* components via an 'elements.changed' event with all the dirty
* elements.
*
* The event can be picked up by components that are interested in the fact
* that elements have been changed. One use case for this is updating
* their graphical representation after moving / resizing or deletion.
*
* @see CommandHandler
*
* @param {EventBus} eventBus
* @param {Injector} injector
*/
function CommandStack(eventBus, injector) {
/**
* A map of all registered command handlers.
*
* @type {Object}
*/
this._handlerMap = {};
/**
* A stack containing all re/undoable actions on the diagram
*
* @type {Array}
*/
this._stack = [];
/**
* The current index on the stack
*
* @type {Number}
*/
this._stackIdx = -1;
/**
* Current active commandStack execution
*
* @type {Object}
*/
this._currentExecution = {
actions: [],
dirty: []
};
this._injector = injector;
this._eventBus = eventBus;
this._uid = 1;
eventBus.on([ 'diagram.destroy', 'diagram.clear' ], this.clear, this);
}
CommandStack.$inject = [ 'eventBus', 'injector' ];
module.exports = CommandStack;
/**
* Execute a command
*
* @param {String} command the command to execute
* @param {Object} context the environment to execute the command in
*/
CommandStack.prototype.execute = function(command, context) {
if (!command) {
throw new Error('command required');
}
var action = { command: command, context: context };
this._pushAction(action);
this._internalExecute(action);
this._popAction(action);
};
/**
* Ask whether a given command can be executed.
*
* Implementors may hook into the mechanism on two ways:
*
* * in event listeners:
*
* Users may prevent the execution via an event listener.
* It must prevent the default action for `commandStack.(.)canExecute` events.
*
* * in command handlers:
*
* If the method {@link CommandHandler#canExecute} is implemented in a handler
* it will be called to figure out whether the execution is allowed.
*
* @param {String} command the command to execute
* @param {Object} context the environment to execute the command in
*
* @return {Boolean} true if the command can be executed
*/
CommandStack.prototype.canExecute = function(command, context) {
var action = { command: command, context: context };
var handler = this._getHandler(command);
var result = this._fire(command, 'canExecute', action);
// handler#canExecute will only be called if no listener
// decided on a result already
if (result === undefined) {
if (!handler) {
return false;
}
if (handler.canExecute) {
result = handler.canExecute(context);
}
}
return result;
};
/**
* Clear the command stack, erasing all undo / redo history
*/
CommandStack.prototype.clear = function() {
this._stack.length = 0;
this._stackIdx = -1;
this._fire('changed');
};
/**
* Undo last command(s)
*/
CommandStack.prototype.undo = function() {
var action = this._getUndoAction(),
next;
if (action) {
this._pushAction(action);
while (action) {
this._internalUndo(action);
next = this._getUndoAction();
if (!next || next.id !== action.id) {
break;
}
action = next;
}
this._popAction();
}
};
/**
* Redo last command(s)
*/
CommandStack.prototype.redo = function() {
var action = this._getRedoAction(),
next;
if (action) {
this._pushAction(action);
while (action) {
this._internalExecute(action, true);
next = this._getRedoAction();
if (!next || next.id !== action.id) {
break;
}
action = next;
}
this._popAction();
}
};
/**
* Register a handler instance with the command stack
*
* @param {String} command
* @param {CommandHandler} handler
*/
CommandStack.prototype.register = function(command, handler) {
this._setHandler(command, handler);
};
/**
* Register a handler type with the command stack
* by instantiating it and injecting its dependencies.
*
* @param {String} command
* @param {Function} a constructor for a {@link CommandHandler}
*/
CommandStack.prototype.registerHandler = function(command, handlerCls) {
if (!command || !handlerCls) {
throw new Error('command and handlerCls must be defined');
}
var handler = this._injector.instantiate(handlerCls);
this.register(command, handler);
};
CommandStack.prototype.canUndo = function() {
return !!this._getUndoAction();
};
CommandStack.prototype.canRedo = function() {
return !!this._getRedoAction();
};
////// stack access //////////////////////////////////////
CommandStack.prototype._getRedoAction = function() {
return this._stack[this._stackIdx + 1];
};
CommandStack.prototype._getUndoAction = function() {
return this._stack[this._stackIdx];
};
////// internal functionality /////////////////////////////
CommandStack.prototype._internalUndo = function(action) {
var self = this;
var command = action.command,
context = action.context;
var handler = this._getHandler(command);
// guard against illegal nested command stack invocations
this._atomicDo(function() {
self._fire(command, 'revert', action);
if (handler.revert) {
self._markDirty(handler.revert(context));
}
self._revertedAction(action);
self._fire(command, 'reverted', action);
});
};
CommandStack.prototype._fire = function(command, qualifier, event) {
if (arguments.length < 3) {
event = qualifier;
qualifier = null;
}
var names = qualifier ? [ command + '.' + qualifier, qualifier ] : [ command ],
i, name, result;
event = assign(new InternalEvent(), event);
for (i = 0; (name = names[i]); i++) {
result = this._eventBus.fire('commandStack.' + name, event);
if (event.cancelBubble) {
break;
}
}
return result;
};
CommandStack.prototype._createId = function() {
return this._uid++;
};
CommandStack.prototype._atomicDo = function(fn) {
var execution = this._currentExecution;
execution.atomic = true;
try {
fn();
} finally {
execution.atomic = false;
}
};
CommandStack.prototype._internalExecute = function(action, redo) {
var self = this;
var command = action.command,
context = action.context;
var handler = this._getHandler(command);
if (!handler) {
throw new Error('no command handler registered for <' + command + '>');
}
this._pushAction(action);
if (!redo) {
this._fire(command, 'preExecute', action);
if (handler.preExecute) {
handler.preExecute(context);
}
this._fire(command, 'preExecuted', action);
}
// guard against illegal nested command stack invocations
this._atomicDo(function() {
self._fire(command, 'execute', action);
if (handler.execute) {
// actual execute + mark return results as dirty
self._markDirty(handler.execute(context));
}
// log to stack
self._executedAction(action, redo);
self._fire(command, 'executed', action);
});
if (!redo) {
this._fire(command, 'postExecute', action);
if (handler.postExecute) {
handler.postExecute(context);
}
this._fire(command, 'postExecuted', action);
}
this._popAction(action);
};
CommandStack.prototype._pushAction = function(action) {
var execution = this._currentExecution,
actions = execution.actions;
var baseAction = actions[0];
if (execution.atomic) {
throw new Error('illegal invocation in or phase (action: ' + action.command + ')');
}
if (!action.id) {
action.id = (baseAction && baseAction.id) || this._createId();
}
actions.push(action);
};
CommandStack.prototype._popAction = function() {
var execution = this._currentExecution,
actions = execution.actions,
dirty = execution.dirty;
actions.pop();
if (!actions.length) {
this._eventBus.fire('elements.changed', { elements: unique(dirty) });
dirty.length = 0;
this._fire('changed');
}
};
CommandStack.prototype._markDirty = function(elements) {
var execution = this._currentExecution;
if (!elements) {
return;
}
elements = isArray(elements) ? elements : [ elements ];
execution.dirty = execution.dirty.concat(elements);
};
CommandStack.prototype._executedAction = function(action, redo) {
var stackIdx = ++this._stackIdx;
if (!redo) {
this._stack.splice(stackIdx, this._stack.length, action);
}
};
CommandStack.prototype._revertedAction = function(action) {
this._stackIdx--;
};
CommandStack.prototype._getHandler = function(command) {
return this._handlerMap[command];
};
CommandStack.prototype._setHandler = function(command, handler) {
if (!command || !handler) {
throw new Error('command and handler required');
}
if (this._handlerMap[command]) {
throw new Error('overriding handler for command <' + command + '>');
}
this._handlerMap[command] = handler;
};
},{"125":125,"296":296,"422":422,"431":431}],121:[function(_dereq_,module,exports){
module.exports = {
commandStack: [ 'type', _dereq_(120) ]
};
},{"120":120}],122:[function(_dereq_,module,exports){
'use strict';
var isNumber = _dereq_(425),
assign = _dereq_(431),
forEach = _dereq_(303),
every = _dereq_(300),
debounce = _dereq_(313),
reduce = _dereq_(306);
var Collections = _dereq_(263),
Elements = _dereq_(266);
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClasses = _dereq_(475),
svgCreate = _dereq_(478),
svgTransform = _dereq_(482);
var createMatrix = _dereq_(479).createMatrix;
function round(number, resolution) {
return Math.round(number * resolution) / resolution;
}
function ensurePx(number) {
return isNumber(number) ? number + 'px' : number;
}
/**
* Creates a HTML container element for a SVG element with
* the given configuration
*
* @param {Object} options
* @return {HTMLElement} the container element
*/
function createContainer(options) {
options = assign({}, { width: '100%', height: '100%' }, options);
var container = options.container || document.body;
// create a around the svg element with the respective size
// this way we can always get the correct container size
// (this is impossible for
elements at the moment)
var parent = document.createElement('div');
parent.setAttribute('class', 'djs-container');
assign(parent.style, {
position: 'relative',
overflow: 'hidden',
width: ensurePx(options.width),
height: ensurePx(options.height)
});
container.appendChild(parent);
return parent;
}
function createGroup(parent, cls, childIndex) {
var group = svgCreate('g');
svgClasses(group).add(cls);
var index = childIndex !== undefined ? childIndex : parent.childNodes.length - 1;
parent.insertBefore(group, parent.childNodes[index]);
return group;
}
var BASE_LAYER = 'base';
var REQUIRED_MODEL_ATTRS = {
shape: [ 'x', 'y', 'width', 'height' ],
connection: [ 'waypoints' ]
};
/**
* The main drawing canvas.
*
* @class
* @constructor
*
* @emits Canvas#canvas.init
*
* @param {Object} config
* @param {EventBus} eventBus
* @param {GraphicsFactory} graphicsFactory
* @param {ElementRegistry} elementRegistry
*/
function Canvas(config, eventBus, graphicsFactory, elementRegistry) {
this._eventBus = eventBus;
this._elementRegistry = elementRegistry;
this._graphicsFactory = graphicsFactory;
this._init(config || {});
}
Canvas.$inject = [ 'config.canvas', 'eventBus', 'graphicsFactory', 'elementRegistry' ];
module.exports = Canvas;
Canvas.prototype._init = function(config) {
var eventBus = this._eventBus;
// Creates a element that is wrapped into a .
// This way we are always able to correctly figure out the size of the svg element
// by querying the parent node.
//
// (It is not possible to get the size of a svg element cross browser @ 2014-04-01)
//
//
//
// ...
//
//
// html container
var container = this._container = createContainer(config);
var svg = this._svg = svgCreate('svg');
svgAttr(svg, { width: '100%', height: '100%' });
svgAppend(container, svg);
var viewport = this._viewport = createGroup(svg, 'viewport');
this._layers = {};
// debounce canvas.viewbox.changed events
// for smoother diagram interaction
if (config.deferUpdate !== false) {
this._viewboxChanged = debounce(this._viewboxChanged, 300);
}
eventBus.on('diagram.init', function() {
/**
* An event indicating that the canvas is ready to be drawn on.
*
* @memberOf Canvas
*
* @event canvas.init
*
* @type {Object}
* @property {SVGElement} svg the created svg element
* @property {SVGElement} viewport the direct parent of diagram elements and shapes
*/
eventBus.fire('canvas.init', {
svg: svg,
viewport: viewport
});
// fire this in order for certain components to check
// if they need to be adjusted due the canvas size
this.resized();
}, this);
eventBus.on('diagram.destroy', 500, this._destroy, this);
eventBus.on('diagram.clear', 500, this._clear, this);
};
Canvas.prototype._destroy = function(emit) {
this._eventBus.fire('canvas.destroy', {
svg: this._svg,
viewport: this._viewport
});
var parent = this._container.parentNode;
if (parent) {
parent.removeChild(this._container);
}
delete this._svg;
delete this._container;
delete this._layers;
delete this._rootElement;
delete this._viewport;
};
Canvas.prototype._clear = function() {
var self = this;
var allElements = this._elementRegistry.getAll();
// remove all elements
allElements.forEach(function(element) {
var type = Elements.getType(element);
if (type === 'root') {
self.setRootElement(null, true);
} else {
self._removeElement(element, type);
}
});
// force recomputation of view box
delete this._cachedViewbox;
};
/**
* Returns the default layer on which
* all elements are drawn.
*
* @returns {SVGElement}
*/
Canvas.prototype.getDefaultLayer = function() {
return this.getLayer(BASE_LAYER, 0);
};
/**
* Returns a layer that is used to draw elements
* or annotations on it.
*
* Non-existing layers retrieved through this method
* will be created. During creation, the optional index
* may be used to create layers below or above existing layers.
* A layer with a certain index is always created above all
* existing layers with the same index.
*
* @param {String} name
* @param {Number} index
*
* @returns {SVGElement}
*/
Canvas.prototype.getLayer = function(name, index) {
if (!name) {
throw new Error('must specify a name');
}
var layer = this._layers[name];
if (!layer) {
layer = this._layers[name] = this._createLayer(name, index);
}
// throw an error if layer creation / retrival is
// requested on different index
if (typeof index !== 'undefined' && layer.index !== index) {
throw new Error('layer <' + name + '> already created at index <' + index + '>');
}
return layer.group;
};
/**
* Creates a given layer and returns it.
*
* @param {String} name
* @param {Number} [index=0]
*
* @return {Object} layer descriptor with { index, group: SVGGroup }
*/
Canvas.prototype._createLayer = function(name, index) {
if (!index) {
index = 0;
}
var childIndex = reduce(this._layers, function(childIndex, layer) {
if (index >= layer.index) {
childIndex++;
}
return childIndex;
}, 0);
return {
group: createGroup(this._viewport, 'layer-' + name, childIndex),
index: index
};
};
/**
* Returns the html element that encloses the
* drawing canvas.
*
* @return {DOMNode}
*/
Canvas.prototype.getContainer = function() {
return this._container;
};
/////////////// markers ///////////////////////////////////
Canvas.prototype._updateMarker = function(element, marker, add) {
var container;
if (!element.id) {
element = this._elementRegistry.get(element);
}
// we need to access all
container = this._elementRegistry._elements[element.id];
if (!container) {
return;
}
forEach([ container.gfx, container.secondaryGfx ], function(gfx) {
if (gfx) {
// invoke either addClass or removeClass based on mode
if (add) {
svgClasses(gfx).add(marker);
} else {
svgClasses(gfx).remove(marker);
}
}
});
/**
* An event indicating that a marker has been updated for an element
*
* @event element.marker.update
* @type {Object}
* @property {djs.model.Element} element the shape
* @property {Object} gfx the graphical representation of the shape
* @property {String} marker
* @property {Boolean} add true if the marker was added, false if it got removed
*/
this._eventBus.fire('element.marker.update', { element: element, gfx: container.gfx, marker: marker, add: !!add });
};
/**
* Adds a marker to an element (basically a css class).
*
* Fires the element.marker.update event, making it possible to
* integrate extension into the marker life-cycle, too.
*
* @example
* canvas.addMarker('foo', 'some-marker');
*
* var fooGfx = canvas.getGraphics('foo');
*
* fooGfx; //
...
*
* @param {String|djs.model.Base} element
* @param {String} marker
*/
Canvas.prototype.addMarker = function(element, marker) {
this._updateMarker(element, marker, true);
};
/**
* Remove a marker from an element.
*
* Fires the element.marker.update event, making it possible to
* integrate extension into the marker life-cycle, too.
*
* @param {String|djs.model.Base} element
* @param {String} marker
*/
Canvas.prototype.removeMarker = function(element, marker) {
this._updateMarker(element, marker, false);
};
/**
* Check the existence of a marker on element.
*
* @param {String|djs.model.Base} element
* @param {String} marker
*/
Canvas.prototype.hasMarker = function(element, marker) {
if (!element.id) {
element = this._elementRegistry.get(element);
}
var gfx = this.getGraphics(element);
return svgClasses(gfx).has(marker);
};
/**
* Toggles a marker on an element.
*
* Fires the element.marker.update event, making it possible to
* integrate extension into the marker life-cycle, too.
*
* @param {String|djs.model.Base} element
* @param {String} marker
*/
Canvas.prototype.toggleMarker = function(element, marker) {
if (this.hasMarker(element, marker)) {
this.removeMarker(element, marker);
} else {
this.addMarker(element, marker);
}
};
Canvas.prototype.getRootElement = function() {
if (!this._rootElement) {
this.setRootElement({ id: '__implicitroot', children: [] });
}
return this._rootElement;
};
//////////////// root element handling ///////////////////////////
/**
* Sets a given element as the new root element for the canvas
* and returns the new root element.
*
* @param {Object|djs.model.Root} element
* @param {Boolean} [override] whether to override the current root element, if any
*
* @return {Object|djs.model.Root} new root element
*/
Canvas.prototype.setRootElement = function(element, override) {
if (element) {
this._ensureValid('root', element);
}
var currentRoot = this._rootElement,
elementRegistry = this._elementRegistry,
eventBus = this._eventBus;
if (currentRoot) {
if (!override) {
throw new Error('rootElement already set, need to specify override');
}
// simulate element remove event sequence
eventBus.fire('root.remove', { element: currentRoot });
eventBus.fire('root.removed', { element: currentRoot });
elementRegistry.remove(currentRoot);
}
if (element) {
var gfx = this.getDefaultLayer();
// resemble element add event sequence
eventBus.fire('root.add', { element: element });
elementRegistry.add(element, gfx, this._svg);
eventBus.fire('root.added', { element: element, gfx: gfx });
}
this._rootElement = element;
return element;
};
///////////// add functionality ///////////////////////////////
Canvas.prototype._ensureValid = function(type, element) {
if (!element.id) {
throw new Error('element must have an id');
}
if (this._elementRegistry.get(element.id)) {
throw new Error('element with id ' + element.id + ' already exists');
}
var requiredAttrs = REQUIRED_MODEL_ATTRS[type];
var valid = every(requiredAttrs, function(attr) {
return typeof element[attr] !== 'undefined';
});
if (!valid) {
throw new Error(
'must supply { ' + requiredAttrs.join(', ') + ' } with ' + type);
}
};
Canvas.prototype._setParent = function(element, parent, parentIndex) {
Collections.add(parent.children, element, parentIndex);
element.parent = parent;
};
/**
* Adds an element to the canvas.
*
* This wires the parent <-> child relationship between the element and
* a explicitly specified parent or an implicit root element.
*
* During add it emits the events
*
* * <{type}.add> (element, parent)
* * <{type}.added> (element, gfx)
*
* Extensions may hook into these events to perform their magic.
*
* @param {String} type
* @param {Object|djs.model.Base} element
* @param {Object|djs.model.Base} [parent]
* @param {Number} [parentIndex]
*
* @return {Object|djs.model.Base} the added element
*/
Canvas.prototype._addElement = function(type, element, parent, parentIndex) {
parent = parent || this.getRootElement();
var eventBus = this._eventBus,
graphicsFactory = this._graphicsFactory;
this._ensureValid(type, element);
eventBus.fire(type + '.add', { element: element, parent: parent });
this._setParent(element, parent, parentIndex);
// create graphics
var gfx = graphicsFactory.create(type, element);
this._elementRegistry.add(element, gfx);
// update its visual
graphicsFactory.update(type, element, gfx);
eventBus.fire(type + '.added', { element: element, gfx: gfx });
return element;
};
/**
* Adds a shape to the canvas
*
* @param {Object|djs.model.Shape} shape to add to the diagram
* @param {djs.model.Base} [parent]
* @param {Number} [parentIndex]
*
* @return {djs.model.Shape} the added shape
*/
Canvas.prototype.addShape = function(shape, parent, parentIndex) {
return this._addElement('shape', shape, parent, parentIndex);
};
/**
* Adds a connection to the canvas
*
* @param {Object|djs.model.Connection} connection to add to the diagram
* @param {djs.model.Base} [parent]
* @param {Number} [parentIndex]
*
* @return {djs.model.Connection} the added connection
*/
Canvas.prototype.addConnection = function(connection, parent, parentIndex) {
return this._addElement('connection', connection, parent, parentIndex);
};
/**
* Internal remove element
*/
Canvas.prototype._removeElement = function(element, type) {
var elementRegistry = this._elementRegistry,
graphicsFactory = this._graphicsFactory,
eventBus = this._eventBus;
element = elementRegistry.get(element.id || element);
if (!element) {
// element was removed already
return;
}
eventBus.fire(type + '.remove', { element: element });
graphicsFactory.remove(element);
// unset parent <-> child relationship
Collections.remove(element.parent && element.parent.children, element);
element.parent = null;
eventBus.fire(type + '.removed', { element: element });
elementRegistry.remove(element);
return element;
};
/**
* Removes a shape from the canvas
*
* @param {String|djs.model.Shape} shape or shape id to be removed
*
* @return {djs.model.Shape} the removed shape
*/
Canvas.prototype.removeShape = function(shape) {
/**
* An event indicating that a shape is about to be removed from the canvas.
*
* @memberOf Canvas
*
* @event shape.remove
* @type {Object}
* @property {djs.model.Shape} element the shape descriptor
* @property {Object} gfx the graphical representation of the shape
*/
/**
* An event indicating that a shape has been removed from the canvas.
*
* @memberOf Canvas
*
* @event shape.removed
* @type {Object}
* @property {djs.model.Shape} element the shape descriptor
* @property {Object} gfx the graphical representation of the shape
*/
return this._removeElement(shape, 'shape');
};
/**
* Removes a connection from the canvas
*
* @param {String|djs.model.Connection} connection or connection id to be removed
*
* @return {djs.model.Connection} the removed connection
*/
Canvas.prototype.removeConnection = function(connection) {
/**
* An event indicating that a connection is about to be removed from the canvas.
*
* @memberOf Canvas
*
* @event connection.remove
* @type {Object}
* @property {djs.model.Connection} element the connection descriptor
* @property {Object} gfx the graphical representation of the connection
*/
/**
* An event indicating that a connection has been removed from the canvas.
*
* @memberOf Canvas
*
* @event connection.removed
* @type {Object}
* @property {djs.model.Connection} element the connection descriptor
* @property {Object} gfx the graphical representation of the connection
*/
return this._removeElement(connection, 'connection');
};
/**
* Return the graphical object underlaying a certain diagram element
*
* @param {String|djs.model.Base} element descriptor of the element
* @param {Boolean} [secondary=false] whether to return the secondary connected element
*
* @return {SVGElement}
*/
Canvas.prototype.getGraphics = function(element, secondary) {
return this._elementRegistry.getGraphics(element, secondary);
};
/**
* Perform a viewbox update via a given change function.
*
* @param {Function} changeFn
*/
Canvas.prototype._changeViewbox = function(changeFn) {
// notify others of the upcoming viewbox change
this._eventBus.fire('canvas.viewbox.changing');
// perform actual change
changeFn.apply(this);
// reset the cached viewbox so that
// a new get operation on viewbox or zoom
// triggers a viewbox re-computation
this._cachedViewbox = null;
// notify others of the change; this step
// may or may not be debounced
this._viewboxChanged();
};
Canvas.prototype._viewboxChanged = function() {
this._eventBus.fire('canvas.viewbox.changed', { viewbox: this.viewbox() });
};
/**
* Gets or sets the view box of the canvas, i.e. the
* area that is currently displayed.
*
* The getter may return a cached viewbox (if it is currently
* changing). To force a recomputation, pass `false` as the first argument.
*
* @example
*
* canvas.viewbox({ x: 100, y: 100, width: 500, height: 500 })
*
* // sets the visible area of the diagram to (100|100) -> (600|100)
* // and and scales it according to the diagram width
*
* var viewbox = canvas.viewbox(); // pass `false` to force recomputing the box.
*
* console.log(viewbox);
* // {
* // inner: Dimensions,
* // outer: Dimensions,
* // scale,
* // x, y,
* // width, height
* // }
*
* // if the current diagram is zoomed and scrolled, you may reset it to the
* // default zoom via this method, too:
*
* var zoomedAndScrolledViewbox = canvas.viewbox();
*
* canvas.viewbox({
* x: 0,
* y: 0,
* width: zoomedAndScrolledViewbox.outer.width,
* height: zoomedAndScrolledViewbox.outer.height
* });
*
* @param {Object} [box] the new view box to set
* @param {Number} box.x the top left X coordinate of the canvas visible in view box
* @param {Number} box.y the top left Y coordinate of the canvas visible in view box
* @param {Number} box.width the visible width
* @param {Number} box.height
*
* @return {Object} the current view box
*/
Canvas.prototype.viewbox = function(box) {
if (box === undefined && this._cachedViewbox) {
return this._cachedViewbox;
}
var viewport = this._viewport,
innerBox,
outerBox = this.getSize(),
matrix,
scale,
x, y;
if (!box) {
// compute the inner box based on the
// diagrams default layer. This allows us to exclude
// external components, such as overlays
innerBox = this.getDefaultLayer().getBBox();
var transform = svgTransform(viewport);
matrix = transform ? transform.matrix : createMatrix();
scale = round(matrix.a, 1000);
x = round(-matrix.e || 0, 1000);
y = round(-matrix.f || 0, 1000);
box = this._cachedViewbox = {
x: x ? x / scale : 0,
y: y ? y / scale : 0,
width: outerBox.width / scale,
height: outerBox.height / scale,
scale: scale,
inner: {
width: innerBox.width,
height: innerBox.height,
x: innerBox.x,
y: innerBox.y
},
outer: outerBox
};
return box;
} else {
this._changeViewbox(function() {
scale = Math.min(outerBox.width / box.width, outerBox.height / box.height);
var matrix = this._svg.createSVGMatrix()
.scale(scale)
.translate(-box.x, -box.y);
svgTransform(viewport, matrix);
});
}
return box;
};
/**
* Gets or sets the scroll of the canvas.
*
* @param {Object} [delta] the new scroll to apply.
*
* @param {Number} [delta.dx]
* @param {Number} [delta.dy]
*/
Canvas.prototype.scroll = function(delta) {
var node = this._viewport;
var matrix = node.getCTM();
if (delta) {
this._changeViewbox(function() {
delta = assign({ dx: 0, dy: 0 }, delta || {});
matrix = this._svg.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix);
setCTM(node, matrix);
});
}
return { x: matrix.e, y: matrix.f };
};
/**
* Gets or sets the current zoom of the canvas, optionally zooming
* to the specified position.
*
* The getter may return a cached zoom level. Call it with `false` as
* the first argument to force recomputation of the current level.
*
* @param {String|Number} [newScale] the new zoom level, either a number, i.e. 0.9,
* or `fit-viewport` to adjust the size to fit the current viewport
* @param {String|Point} [center] the reference point { x: .., y: ..} to zoom to, 'auto' to zoom into mid or null
*
* @return {Number} the current scale
*/
Canvas.prototype.zoom = function(newScale, center) {
if (!newScale) {
return this.viewbox(newScale).scale;
}
if (newScale === 'fit-viewport') {
return this._fitViewport(center);
}
var outer,
matrix;
this._changeViewbox(function() {
if (typeof center !== 'object') {
outer = this.viewbox().outer;
center = {
x: outer.width / 2,
y: outer.height / 2
};
}
matrix = this._setZoom(newScale, center);
});
return round(matrix.a, 1000);
};
function setCTM(node, m) {
var mstr = 'matrix(' + m.a + ',' + m.b + ',' + m.c + ',' + m.d + ',' + m.e + ',' + m.f + ')';
node.setAttribute('transform', mstr);
}
Canvas.prototype._fitViewport = function(center) {
var vbox = this.viewbox(),
outer = vbox.outer,
inner = vbox.inner,
newScale,
newViewbox;
// display the complete diagram without zooming in.
// instead of relying on internal zoom, we perform a
// hard reset on the canvas viewbox to realize this
//
// if diagram does not need to be zoomed in, we focus it around
// the diagram origin instead
if (inner.x >= 0 &&
inner.y >= 0 &&
inner.x + inner.width <= outer.width &&
inner.y + inner.height <= outer.height &&
!center) {
newViewbox = {
x: 0,
y: 0,
width: Math.max(inner.width + inner.x, outer.width),
height: Math.max(inner.height + inner.y, outer.height)
};
} else {
newScale = Math.min(1, outer.width / inner.width, outer.height / inner.height);
newViewbox = {
x: inner.x + (center ? inner.width / 2 - outer.width / newScale / 2 : 0),
y: inner.y + (center ? inner.height / 2 - outer.height / newScale / 2 : 0),
width: outer.width / newScale,
height: outer.height / newScale
};
}
this.viewbox(newViewbox);
return this.viewbox(false).scale;
};
Canvas.prototype._setZoom = function(scale, center) {
var svg = this._svg,
viewport = this._viewport;
var matrix = svg.createSVGMatrix();
var point = svg.createSVGPoint();
var centerPoint,
originalPoint,
currentMatrix,
scaleMatrix,
newMatrix;
currentMatrix = viewport.getCTM();
var currentScale = currentMatrix.a;
if (center) {
centerPoint = assign(point, center);
// revert applied viewport transformations
originalPoint = centerPoint.matrixTransform(currentMatrix.inverse());
// create scale matrix
scaleMatrix = matrix
.translate(originalPoint.x, originalPoint.y)
.scale(1 / currentScale * scale)
.translate(-originalPoint.x, -originalPoint.y);
newMatrix = currentMatrix.multiply(scaleMatrix);
} else {
newMatrix = matrix.scale(scale);
}
setCTM(this._viewport, newMatrix);
return newMatrix;
};
/**
* Returns the size of the canvas
*
* @return {Dimensions}
*/
Canvas.prototype.getSize = function() {
return {
width: this._container.clientWidth,
height: this._container.clientHeight
};
};
/**
* Return the absolute bounding box for the given element
*
* The absolute bounding box may be used to display overlays in the
* callers (browser) coordinate system rather than the zoomed in/out
* canvas coordinates.
*
* @param {ElementDescriptor} element
* @return {Bounds} the absolute bounding box
*/
Canvas.prototype.getAbsoluteBBox = function(element) {
var vbox = this.viewbox();
var bbox;
// connection
// use svg bbox
if (element.waypoints) {
var gfx = this.getGraphics(element);
bbox = gfx.getBBox();
}
// shapes
// use data
else {
bbox = element;
}
var x = bbox.x * vbox.scale - vbox.x * vbox.scale;
var y = bbox.y * vbox.scale - vbox.y * vbox.scale;
var width = bbox.width * vbox.scale;
var height = bbox.height * vbox.scale;
return {
x: x,
y: y,
width: width,
height: height
};
};
/**
* Fires an event in order other modules can react to the
* canvas resizing
*/
Canvas.prototype.resized = function() {
// force recomputation of view box
delete this._cachedViewbox;
this._eventBus.fire('canvas.resized');
};
},{"263":263,"266":266,"300":300,"303":303,"306":306,"313":313,"425":425,"431":431,"472":472,"474":474,"475":475,"478":478,"479":479,"482":482}],123:[function(_dereq_,module,exports){
'use strict';
var Model = _dereq_(254);
var assign = _dereq_(431);
/**
* A factory for diagram-js shapes
*/
function ElementFactory() {
this._uid = 12;
}
module.exports = ElementFactory;
ElementFactory.prototype.createRoot = function(attrs) {
return this.create('root', attrs);
};
ElementFactory.prototype.createLabel = function(attrs) {
return this.create('label', attrs);
};
ElementFactory.prototype.createShape = function(attrs) {
return this.create('shape', attrs);
};
ElementFactory.prototype.createConnection = function(attrs) {
return this.create('connection', attrs);
};
/**
* Create a model element with the given type and
* a number of pre-set attributes.
*
* @param {String} type
* @param {Object} attrs
* @return {djs.model.Base} the newly created model instance
*/
ElementFactory.prototype.create = function(type, attrs) {
attrs = assign({}, attrs || {});
if (!attrs.id) {
attrs.id = type + '_' + (this._uid++);
}
return Model.create(type, attrs);
};
},{"254":254,"431":431}],124:[function(_dereq_,module,exports){
'use strict';
var ELEMENT_ID = 'data-element-id';
var svgAttr = _dereq_(474);
/**
* @class
*
* A registry that keeps track of all shapes in the diagram.
*/
function ElementRegistry(eventBus) {
this._elements = {};
this._eventBus = eventBus;
}
ElementRegistry.$inject = [ 'eventBus' ];
module.exports = ElementRegistry;
/**
* Register a pair of (element, gfx, (secondaryGfx)).
*
* @param {djs.model.Base} element
* @param {SVGElement} gfx
* @param {SVGElement} [secondaryGfx] optional other element to register, too
*/
ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) {
var id = element.id;
this._validateId(id);
// associate dom node with element
svgAttr(gfx, ELEMENT_ID, id);
if (secondaryGfx) {
svgAttr(secondaryGfx, ELEMENT_ID, id);
}
this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx };
};
/**
* Removes an element from the registry.
*
* @param {djs.model.Base} element
*/
ElementRegistry.prototype.remove = function(element) {
var elements = this._elements,
id = element.id || element,
container = id && elements[id];
if (container) {
// unset element id on gfx
svgAttr(container.gfx, ELEMENT_ID, '');
if (container.secondaryGfx) {
svgAttr(container.secondaryGfx, ELEMENT_ID, '');
}
delete elements[id];
}
};
/**
* Update the id of an element
*
* @param {djs.model.Base} element
* @param {String} newId
*/
ElementRegistry.prototype.updateId = function(element, newId) {
this._validateId(newId);
if (typeof element === 'string') {
element = this.get(element);
}
this._eventBus.fire('element.updateId', {
element: element,
newId: newId
});
var gfx = this.getGraphics(element),
secondaryGfx = this.getGraphics(element, true);
this.remove(element);
element.id = newId;
this.add(element, gfx, secondaryGfx);
};
/**
* Return the model element for a given id or graphics.
*
* @example
*
* elementRegistry.get('SomeElementId_1');
* elementRegistry.get(gfx);
*
*
* @param {String|SVGElement} filter for selecting the element
*
* @return {djs.model.Base}
*/
ElementRegistry.prototype.get = function(filter) {
var id;
if (typeof filter === 'string') {
id = filter;
} else {
id = filter && svgAttr(filter, ELEMENT_ID);
}
var container = this._elements[id];
return container && container.element;
};
/**
* Return all elements that match a given filter function.
*
* @param {Function} fn
*
* @return {Array
}
*/
ElementRegistry.prototype.filter = function(fn) {
var filtered = [];
this.forEach(function(element, gfx) {
if (fn(element, gfx)) {
filtered.push(element);
}
});
return filtered;
};
/**
* Return all rendered model elements.
*
* @return {Array}
*/
ElementRegistry.prototype.getAll = function() {
return this.filter(function(e) { return e; });
};
/**
* Iterate over all diagram elements.
*
* @param {Function} fn
*/
ElementRegistry.prototype.forEach = function(fn) {
var map = this._elements;
Object.keys(map).forEach(function(id) {
var container = map[id],
element = container.element,
gfx = container.gfx;
return fn(element, gfx);
});
};
/**
* Return the graphical representation of an element or its id.
*
* @example
* elementRegistry.getGraphics('SomeElementId_1');
* elementRegistry.getGraphics(rootElement); //
*
* elementRegistry.getGraphics(rootElement, true); //
*
*
* @param {String|djs.model.Base} filter
* @param {Boolean} [secondary=false] whether to return the secondary connected element
*
* @return {SVGElement}
*/
ElementRegistry.prototype.getGraphics = function(filter, secondary) {
var id = filter.id || filter;
var container = this._elements[id];
return container && (secondary ? container.secondaryGfx : container.gfx);
};
/**
* Validate the suitability of the given id and signals a problem
* with an exception.
*
* @param {String} id
*
* @throws {Error} if id is empty or already assigned
*/
ElementRegistry.prototype._validateId = function(id) {
if (!id) {
throw new Error('element must have an id');
}
if (this._elements[id]) {
throw new Error('element with id ' + id + ' already added');
}
};
},{"474":474}],125:[function(_dereq_,module,exports){
'use strict';
var isFunction = _dereq_(423),
isArray = _dereq_(422),
isNumber = _dereq_(425),
bind = _dereq_(312),
assign = _dereq_(431);
var FN_REF = '__fn';
var DEFAULT_PRIORITY = 1000;
var slice = Array.prototype.slice;
/**
* A general purpose event bus.
*
* This component is used to communicate across a diagram instance.
* Other parts of a diagram can use it to listen to and broadcast events.
*
*
* ## Registering for Events
*
* The event bus provides the {@link EventBus#on} and {@link EventBus#once}
* methods to register for events. {@link EventBus#off} can be used to
* remove event registrations. Listeners receive an instance of {@link Event}
* as the first argument. It allows them to hook into the event execution.
*
* ```javascript
*
* // listen for event
* eventBus.on('foo', function(event) {
*
* // access event type
* event.type; // 'foo'
*
* // stop propagation to other listeners
* event.stopPropagation();
*
* // prevent event default
* event.preventDefault();
* });
*
* // listen for event with custom payload
* eventBus.on('bar', function(event, payload) {
* console.log(payload);
* });
*
* // listen for event returning value
* eventBus.on('foobar', function(event) {
*
* // stop event propagation + prevent default
* return false;
*
* // stop event propagation + return custom result
* return {
* complex: 'listening result'
* };
* });
*
*
* // listen with custom priority (default=1000, higher is better)
* eventBus.on('priorityfoo', 1500, function(event) {
* console.log('invoked first!');
* });
*
*
* // listen for event and pass the context (`this`)
* eventBus.on('foobar', function(event) {
* this.foo();
* }, this);
* ```
*
*
* ## Emitting Events
*
* Events can be emitted via the event bus using {@link EventBus#fire}.
*
* ```javascript
*
* // false indicates that the default action
* // was prevented by listeners
* if (eventBus.fire('foo') === false) {
* console.log('default has been prevented!');
* };
*
*
* // custom args + return value listener
* eventBus.on('sum', function(event, a, b) {
* return a + b;
* });
*
* // you can pass custom arguments + retrieve result values.
* var sum = eventBus.fire('sum', 1, 2);
* console.log(sum); // 3
* ```
*/
function EventBus() {
this._listeners = {};
// cleanup on destroy on lowest priority to allow
// message passing until the bitter end
this.on('diagram.destroy', 1, this._destroy, this);
}
module.exports = EventBus;
/**
* Register an event listener for events with the given name.
*
* The callback will be invoked with `event, ...additionalArguments`
* that have been passed to {@link EventBus#fire}.
*
* Returning false from a listener will prevent the events default action
* (if any is specified). To stop an event from being processed further in
* other listeners execute {@link Event#stopPropagation}.
*
* Returning anything but `undefined` from a listener will stop the listener propagation.
*
* @param {String|Array} events
* @param {Number} [priority=1000] the priority in which this listener is called, larger is higher
* @param {Function} callback
* @param {Object} [that] Pass context (`this`) to the callback
*/
EventBus.prototype.on = function(events, priority, callback, that) {
events = isArray(events) ? events : [ events ];
if (isFunction(priority)) {
that = callback;
callback = priority;
priority = DEFAULT_PRIORITY;
}
if (!isNumber(priority)) {
throw new Error('priority must be a number');
}
var actualCallback = callback;
if (that) {
actualCallback = bind(callback, that);
// make sure we remember and are able to remove
// bound callbacks via {@link #off} using the original
// callback
actualCallback[FN_REF] = callback[FN_REF] || callback;
}
var self = this,
listener = { priority: priority, callback: actualCallback };
events.forEach(function(e) {
self._addListener(e, listener);
});
};
/**
* Register an event listener that is executed only once.
*
* @param {String} event the event name to register for
* @param {Function} callback the callback to execute
* @param {Object} [that] Pass context (`this`) to the callback
*/
EventBus.prototype.once = function(event, priority, callback, that) {
var self = this;
if (isFunction(priority)) {
that = callback;
callback = priority;
priority = DEFAULT_PRIORITY;
}
if (!isNumber(priority)) {
throw new Error('priority must be a number');
}
function wrappedCallback() {
self.off(event, wrappedCallback);
return callback.apply(that, arguments);
}
// make sure we remember and are able to remove
// bound callbacks via {@link #off} using the original
// callback
wrappedCallback[FN_REF] = callback;
this.on(event, priority, wrappedCallback);
};
/**
* Removes event listeners by event and callback.
*
* If no callback is given, all listeners for a given event name are being removed.
*
* @param {String} event
* @param {Function} [callback]
*/
EventBus.prototype.off = function(event, callback) {
var listeners = this._getListeners(event),
listener,
listenerCallback,
idx;
if (callback) {
// move through listeners from back to front
// and remove matching listeners
for (idx = listeners.length - 1; (listener = listeners[idx]); idx--) {
listenerCallback = listener.callback;
if (listenerCallback === callback || listenerCallback[FN_REF] === callback) {
listeners.splice(idx, 1);
}
}
} else {
// clear listeners
listeners.length = 0;
}
};
/**
* Fires a named event.
*
* @example
*
* // fire event by name
* events.fire('foo');
*
* // fire event object with nested type
* var event = { type: 'foo' };
* events.fire(event);
*
* // fire event with explicit type
* var event = { x: 10, y: 20 };
* events.fire('element.moved', event);
*
* // pass additional arguments to the event
* events.on('foo', function(event, bar) {
* alert(bar);
* });
*
* events.fire({ type: 'foo' }, 'I am bar!');
*
* @param {String} [name] the optional event name
* @param {Object} [event] the event object
* @param {...Object} additional arguments to be passed to the callback functions
*
* @return {Boolean} the events return value, if specified or false if the
* default action was prevented by listeners
*/
EventBus.prototype.fire = function(type, data) {
var event,
listeners,
returnValue,
args;
args = slice.call(arguments);
if (typeof type === 'object') {
event = type;
type = event.type;
}
if (!type) {
throw new Error('no event type specified');
}
listeners = this._listeners[type];
if (!listeners) {
return;
}
// we make sure we fire instances of our home made
// events here. We wrap them only once, though
if (data instanceof Event) {
// we are fine, we alread have an event
event = data;
} else {
event = new Event();
event.init(data);
}
// ensure we pass the event as the first parameter
args[0] = event;
// original event type (in case we delegate)
var originalType = event.type;
// update event type before delegation
if (type !== originalType) {
event.type = type;
}
try {
returnValue = this._invokeListeners(event, args, listeners);
} finally {
// reset event type after delegation
if (type !== originalType) {
event.type = originalType;
}
}
// set the return value to false if the event default
// got prevented and no other return value exists
if (returnValue === undefined && event.defaultPrevented) {
returnValue = false;
}
return returnValue;
};
EventBus.prototype.handleError = function(error) {
return this.fire('error', { error: error }) === false;
};
EventBus.prototype._destroy = function() {
this._listeners = {};
};
EventBus.prototype._invokeListeners = function(event, args, listeners) {
var idx,
listener,
returnValue;
for (idx = 0; (listener = listeners[idx]); idx++) {
// handle stopped propagation
if (event.cancelBubble) {
break;
}
returnValue = this._invokeListener(event, args, listener);
}
return returnValue;
};
EventBus.prototype._invokeListener = function(event, args, listener) {
var returnValue;
try {
// returning false prevents the default action
returnValue = invokeFunction(listener.callback, args);
// stop propagation on return value
if (returnValue !== undefined) {
event.returnValue = returnValue;
event.stopPropagation();
}
// prevent default on return false
if (returnValue === false) {
event.preventDefault();
}
} catch (e) {
if (!this.handleError(e)) {
console.error('unhandled error in event listener');
console.error(e.stack);
throw e;
}
}
return returnValue;
};
/*
* Add new listener with a certain priority to the list
* of listeners (for the given event).
*
* The semantics of listener registration / listener execution are
* first register, first serve: New listeners will always be inserted
* after existing listeners with the same priority.
*
* Example: Inserting two listeners with priority 1000 and 1300
*
* * before: [ 1500, 1500, 1000, 1000 ]
* * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
*
* @param {String} event
* @param {Object} listener { priority, callback }
*/
EventBus.prototype._addListener = function(event, newListener) {
var listeners = this._getListeners(event),
existingListener,
idx;
// ensure we order listeners by priority from
// 0 (high) to n > 0 (low)
for (idx = 0; (existingListener = listeners[idx]); idx++) {
if (existingListener.priority < newListener.priority) {
// prepend newListener at before existingListener
listeners.splice(idx, 0, newListener);
return;
}
}
listeners.push(newListener);
};
EventBus.prototype._getListeners = function(name) {
var listeners = this._listeners[name];
if (!listeners) {
this._listeners[name] = listeners = [];
}
return listeners;
};
/**
* A event that is emitted via the event bus.
*/
function Event() { }
module.exports.Event = Event;
Event.prototype.stopPropagation = function() {
this.cancelBubble = true;
};
Event.prototype.preventDefault = function() {
this.defaultPrevented = true;
};
Event.prototype.init = function(data) {
assign(this, data || {});
};
/**
* Invoke function. Be fast...
*
* @param {Function} fn
* @param {Array} args
*
* @return {Any}
*/
function invokeFunction(fn, args) {
return fn.apply(null, args);
}
},{"312":312,"422":422,"423":423,"425":425,"431":431}],126:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
reduce = _dereq_(306);
var GraphicsUtil = _dereq_(269);
var translate = _dereq_(279).translate;
var domClear = _dereq_(446);
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClasses = _dereq_(475),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
/**
* A factory that creates graphical elements
*
* @param {EventBus} eventBus
* @param {ElementRegistry} elementRegistry
*/
function GraphicsFactory(eventBus, elementRegistry) {
this._eventBus = eventBus;
this._elementRegistry = elementRegistry;
}
GraphicsFactory.$inject = [ 'eventBus' , 'elementRegistry' ];
module.exports = GraphicsFactory;
GraphicsFactory.prototype._getChildren = function(element) {
var gfx = this._elementRegistry.getGraphics(element);
var childrenGfx;
// root element
if (!element.parent) {
childrenGfx = gfx;
} else {
childrenGfx = GraphicsUtil.getChildren(gfx);
if (!childrenGfx) {
childrenGfx = svgCreate('g');
svgClasses(childrenGfx).add('djs-children');
svgAppend(gfx.parentNode, childrenGfx);
}
}
return childrenGfx;
};
/**
* Clears the graphical representation of the element and returns the
* cleared visual (the element).
*/
GraphicsFactory.prototype._clear = function(gfx) {
var visual = GraphicsUtil.getVisual(gfx);
domClear(visual);
return visual;
};
/**
* Creates a gfx container for shapes and connections
*
* The layout is as follows:
*
*
*
*
*
*
*
*
*
*
*
*
*
* @param {Object} parent
* @param {String} type the type of the element, i.e. shape | connection
*/
GraphicsFactory.prototype._createContainer = function(type, parentGfx) {
var outerGfx = svgCreate('g');
svgClasses(outerGfx).add('djs-group');
svgAppend(parentGfx, outerGfx);
var gfx = svgCreate('g');
svgClasses(gfx).add('djs-element');
svgClasses(gfx).add('djs-' + type);
svgAppend(outerGfx, gfx);
// create visual
var visual = svgCreate('g');
svgClasses(visual).add('djs-visual');
svgAppend(gfx, visual);
return gfx;
};
GraphicsFactory.prototype.create = function(type, element) {
var childrenGfx = this._getChildren(element.parent);
return this._createContainer(type, childrenGfx);
};
GraphicsFactory.prototype.updateContainments = function(elements) {
var self = this,
elementRegistry = this._elementRegistry,
parents;
parents = reduce(elements, function(map, e) {
if (e.parent) {
map[e.parent.id] = e.parent;
}
return map;
}, {});
// update all parents of changed and reorganized their children
// in the correct order (as indicated in our model)
forEach(parents, function(parent) {
var childGfx = self._getChildren(parent),
children = parent.children;
if (!children) {
return;
}
forEach(children.slice().reverse(), function(c) {
var gfx = elementRegistry.getGraphics(c);
prependTo(gfx.parentNode, childGfx);
});
});
};
GraphicsFactory.prototype.drawShape = function(visual, element) {
var eventBus = this._eventBus;
return eventBus.fire('render.shape', { gfx: visual, element: element });
};
GraphicsFactory.prototype.getShapePath = function(element) {
var eventBus = this._eventBus;
return eventBus.fire('render.getShapePath', element);
};
GraphicsFactory.prototype.drawConnection = function(visual, element) {
var eventBus = this._eventBus;
return eventBus.fire('render.connection', { gfx: visual, element: element });
};
GraphicsFactory.prototype.getConnectionPath = function(waypoints) {
var eventBus = this._eventBus;
return eventBus.fire('render.getConnectionPath', waypoints);
};
GraphicsFactory.prototype.update = function(type, element, gfx) {
// Do not update root element
if (!element.parent) {
return;
}
var visual = this._clear(gfx);
// redraw
if (type === 'shape') {
this.drawShape(visual, element);
// update positioning
translate(gfx, element.x, element.y);
} else
if (type === 'connection') {
this.drawConnection(visual, element);
} else {
throw new Error('unknown type: ' + type);
}
if (element.hidden) {
svgAttr(gfx, 'display', 'none');
} else {
svgAttr(gfx, 'display', 'block');
}
};
GraphicsFactory.prototype.remove = function(element) {
var gfx = this._elementRegistry.getGraphics(element);
// remove
svgRemove(gfx.parentNode);
};
////////// helpers ///////////
function prependTo(newNode, parentNode) {
parentNode.insertBefore(newNode, parentNode.firstChild);
}
},{"269":269,"279":279,"303":303,"306":306,"446":446,"472":472,"474":474,"475":475,"478":478,"481":481}],127:[function(_dereq_,module,exports){
module.exports = {
__depends__: [ _dereq_(131) ],
__init__: [ 'canvas' ],
canvas: [ 'type', _dereq_(122) ],
elementRegistry: [ 'type', _dereq_(124) ],
elementFactory: [ 'type', _dereq_(123) ],
eventBus: [ 'type', _dereq_(125) ],
graphicsFactory: [ 'type', _dereq_(126) ]
};
},{"122":122,"123":123,"124":124,"125":125,"126":126,"131":131}],128:[function(_dereq_,module,exports){
'use strict';
var DEFAULT_RENDER_PRIORITY = 1000;
/**
* The base implementation of shape and connection renderers.
*
* @param {EventBus} eventBus
* @param {Number} [renderPriority=1000]
*/
function BaseRenderer(eventBus, renderPriority) {
var self = this;
renderPriority = renderPriority || DEFAULT_RENDER_PRIORITY;
eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) {
var type = evt.type,
element = context.element,
visuals = context.gfx;
if (self.canRender(element)) {
if (type === 'render.shape') {
return self.drawShape(visuals, element);
} else {
return self.drawConnection(visuals, element);
}
}
});
eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) {
if (self.canRender(element)) {
if (evt.type === 'render.getShapePath') {
return self.getShapePath(element);
} else {
return self.getConnectionPath(element);
}
}
});
}
/**
* Should check whether *this* renderer can render
* the element/connection.
*
* @param {element} element
*
* @returns {Boolean}
*/
BaseRenderer.prototype.canRender = function() {};
/**
* Provides the shape's snap svg element to be drawn on the `canvas`.
*
* @param {djs.Graphics} visuals
* @param {Shape} shape
*
* @returns {Snap.svg} [returns a Snap.svg paper element ]
*/
BaseRenderer.prototype.drawShape = function() {};
/**
* Provides the shape's snap svg element to be drawn on the `canvas`.
*
* @param {djs.Graphics} visuals
* @param {Connection} connection
*
* @returns {Snap.svg} [returns a Snap.svg paper element ]
*/
BaseRenderer.prototype.drawConnection = function() {};
/**
* Gets the SVG path of a shape that represents it's visual bounds.
*
* @param {Shape} shape
*
* @return {string} svg path
*/
BaseRenderer.prototype.getShapePath = function() {};
/**
* Gets the SVG path of a connection that represents it's visual bounds.
*
* @param {Connection} connection
*
* @return {string} svg path
*/
BaseRenderer.prototype.getConnectionPath = function() {};
module.exports = BaseRenderer;
},{}],129:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var BaseRenderer = _dereq_(128);
var renderUtil = _dereq_(278);
var componentsToPath = renderUtil.componentsToPath,
createLine = renderUtil.createLine;
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478);
// apply default renderer with lowest possible priority
// so that it only kicks in if noone else could render
var DEFAULT_RENDER_PRIORITY = 1;
/**
* The default renderer used for shapes and connections.
*
* @param {EventBus} eventBus
* @param {Styles} styles
*/
function DefaultRenderer(eventBus, styles) {
//
BaseRenderer.call(this, eventBus, DEFAULT_RENDER_PRIORITY);
this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5, stroke: 'fuchsia' });
this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 });
}
inherits(DefaultRenderer, BaseRenderer);
DefaultRenderer.prototype.canRender = function() {
return true;
};
DefaultRenderer.prototype.drawShape = function drawShape(visuals, element) {
var rect = svgCreate('rect');
svgAttr(rect, {
x: 0,
y: 0,
width: element.width || 0,
height: element.height || 0
});
svgAttr(rect, this.SHAPE_STYLE);
svgAppend(visuals, rect);
return rect;
};
DefaultRenderer.prototype.drawConnection = function drawConnection(visuals, connection) {
var line = createLine(connection.waypoints, this.CONNECTION_STYLE);
svgAppend(visuals, line);
return line;
};
DefaultRenderer.prototype.getShapePath = function getShapePath(shape) {
var x = shape.x,
y = shape.y,
width = shape.width,
height = shape.height;
var shapePath = [
['M', x, y],
['l', width, 0],
['l', 0, height],
['l', -width, 0],
['z']
];
return componentsToPath(shapePath);
};
DefaultRenderer.prototype.getConnectionPath = function getConnectionPath(connection) {
var waypoints = connection.waypoints;
var idx, point, connectionPath = [];
for (idx = 0; (point = waypoints[idx]); idx++) {
// take invisible docking into account
// when creating the path
point = point.original || point;
connectionPath.push([ idx === 0 ? 'M' : 'L', point.x, point.y ]);
}
return componentsToPath(connectionPath);
};
DefaultRenderer.$inject = [ 'eventBus', 'styles' ];
module.exports = DefaultRenderer;
},{"128":128,"278":278,"289":289,"472":472,"474":474,"478":478}],130:[function(_dereq_,module,exports){
'use strict';
var isArray = _dereq_(422),
assign = _dereq_(431),
reduce = _dereq_(306);
/**
* A component that manages shape styles
*/
function Styles() {
var defaultTraits = {
'no-fill': {
fill: 'none'
},
'no-border': {
strokeOpacity: 0.0
},
'no-events': {
pointerEvents: 'none'
}
};
var self = this;
/**
* Builds a style definition from a className, a list of traits and an object of additional attributes.
*
* @param {String} className
* @param {Array} traits
* @param {Object} additionalAttrs
*
* @return {Object} the style defintion
*/
this.cls = function(className, traits, additionalAttrs) {
var attrs = this.style(traits, additionalAttrs);
return assign(attrs, { 'class': className });
};
/**
* Builds a style definition from a list of traits and an object of additional attributes.
*
* @param {Array} traits
* @param {Object} additionalAttrs
*
* @return {Object} the style defintion
*/
this.style = function(traits, additionalAttrs) {
if (!isArray(traits) && !additionalAttrs) {
additionalAttrs = traits;
traits = [];
}
var attrs = reduce(traits, function(attrs, t) {
return assign(attrs, defaultTraits[t] || {});
}, {});
return additionalAttrs ? assign(attrs, additionalAttrs) : attrs;
};
this.computeStyle = function(custom, traits, defaultStyles) {
if (!isArray(traits)) {
defaultStyles = traits;
traits = [];
}
return self.style(traits || [], assign({}, defaultStyles, custom || {}));
};
}
module.exports = Styles;
},{"306":306,"422":422,"431":431}],131:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'defaultRenderer' ],
defaultRenderer: [ 'type', _dereq_(129) ],
styles: [ 'type', _dereq_(130) ]
};
},{"129":129,"130":130}],132:[function(_dereq_,module,exports){
'use strict';
var filter = _dereq_(301),
forEach = _dereq_(303),
sortBy = _dereq_(310);
function last(arr) {
return arr && arr[arr.length - 1];
}
function sortTopOrMiddle(element) {
return element.y;
}
function sortLeftOrCenter(element) {
return element.x;
}
/**
* Sorting functions for different types of alignment
*
* @type {Object}
*
* @return {Function}
*/
var ALIGNMENT_SORTING = {
left: sortLeftOrCenter,
center: sortLeftOrCenter,
right: function(element) {
return element.x + element.width;
},
top: sortTopOrMiddle,
middle: sortTopOrMiddle,
bottom: function(element) {
return element.y + element.height;
}
};
function AlignElements(modeling) {
this._modeling = modeling;
}
module.exports = AlignElements;
AlignElements.$inject = [ 'modeling' ];
/**
* Get the relevant "axis" and "dimension" related to the current type of alignment
*
* @param {String} type left|right|center|top|bottom|middle
*
* @return {Object} { axis, dimension }
*/
AlignElements.prototype._getOrientationDetails = function(type) {
var vertical = [ 'top', 'bottom', 'middle' ],
axis = 'x',
dimension = 'width';
if (vertical.indexOf(type) !== -1) {
axis = 'y';
dimension = 'height';
}
return {
axis: axis,
dimension: dimension
};
};
AlignElements.prototype._isType = function(type, types) {
return types.indexOf(type) !== -1;
};
/**
* Get a point on the relevant axis where elements should align to
*
* @param {String} type left|right|center|top|bottom|middle
* @param {Array} sortedElements
*
* @return {Object}
*/
AlignElements.prototype._alignmentPosition = function(type, sortedElements) {
var orientation = this._getOrientationDetails(type),
axis = orientation.axis,
dimension = orientation.dimension,
alignment = {},
centers = {},
hasSharedCenters = false,
centeredElements,
firstElement,
lastElement;
function getMiddleOrTop(first, last) {
return Math.round((first[axis] + last[axis] + last[dimension]) / 2);
}
if (this._isType(type, [ 'left', 'top' ])) {
alignment[type] = sortedElements[0][axis];
} else if (this._isType(type, [ 'right', 'bottom' ])) {
lastElement = last(sortedElements);
alignment[type] = lastElement[axis] + lastElement[dimension];
} else if (this._isType(type, [ 'center', 'middle' ])) {
// check if there is a center shared by more than one shape
// if not, just take the middle of the range
forEach(sortedElements, function(element) {
var center = element[axis] + Math.round(element[dimension] / 2);
if (centers[center]) {
centers[center].elements.push(element);
} else {
centers[center] = {
elements: [ element ],
center: center
};
}
});
centeredElements = sortBy(centers, function(center) {
if (center.elements.length > 1) {
hasSharedCenters = true;
}
return center.elements.length;
});
if (hasSharedCenters) {
alignment[type] = last(centeredElements).center;
return alignment;
}
firstElement = sortedElements[0];
sortedElements = sortBy(sortedElements, function(element) {
return element[axis] + element[dimension];
});
lastElement = last(sortedElements);
alignment[type] = getMiddleOrTop(firstElement, lastElement);
}
return alignment;
};
/**
* Executes the alignment of a selection of elements
*
* @param {Array} elements [description]
* @param {String} type left|right|center|top|bottom|middle
*/
AlignElements.prototype.trigger = function(elements, type) {
var modeling = this._modeling;
var filteredElements = filter(elements, function(element) {
return !(element.waypoints || element.host || element.labelTarget);
});
var sortFn = ALIGNMENT_SORTING[type];
var sortedElements = sortBy(filteredElements, sortFn);
var alignment = this._alignmentPosition(type, sortedElements);
modeling.alignElements(sortedElements, alignment);
};
},{"301":301,"303":303,"310":310}],133:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__init__: [ 'alignElements' ],
alignElements: [ 'type', _dereq_(132) ]
};
},{"132":132}],134:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
flatten = _dereq_(291),
union = _dereq_(294),
filter = _dereq_(301),
groupBy = _dereq_(304),
map = _dereq_(305);
var saveClear = _dereq_(277).saveClear,
Collections = _dereq_(263);
var getNewAttachShapeDelta = _dereq_(261).getNewAttachShapeDelta;
var inherits = _dereq_(289);
var HIGH_PRIORITY = 1500;
var CommandInterceptor = _dereq_(119);
function AttachSupport(eventBus, modeling, movePreview, rules) {
CommandInterceptor.call(this, eventBus);
// remove all the attached elements from the shapes to be validated
// add all the attached shapes to the overall list of moved shapes
eventBus.on('shape.move.start', HIGH_PRIORITY, function(e) {
var context = e.context,
shapes = context.shapes,
validatedShapes = context.validatedShapes;
context.shapes = addAttached(shapes);
context.validatedShapes = removeAttached(validatedShapes);
});
// move all attachments after the other shapes are done moving
this.postExecuted([ 'elements.move' ], function(event) {
var context = event.context,
delta = context.delta,
newParent = context.newParent,
closure = context.closure,
enclosedElements = closure.enclosedElements,
attachers = getAttachers(enclosedElements);
// ensure we move all attachers with their hosts
// if they have not been moved already
forEach(attachers, function(attacher) {
if (!enclosedElements[attacher.id]) {
modeling.moveShape(attacher, delta, newParent);
if (attacher.label) {
modeling.moveShape(attacher.label, delta, newParent);
}
}
});
});
// perform the attaching after shapes are done moving
this.postExecuted([ 'elements.move' ], function(e) {
var context = e.context,
shapes = context.shapes,
newHost = context.newHost,
attachers;
// we only support attachment / detachment of one element
if (shapes.length > 1) {
return;
}
if (newHost) {
attachers = shapes;
} else {
attachers = filter(shapes, function(s) {
return !!s.host;
});
}
forEach(attachers, function(attacher) {
modeling.updateAttachment(attacher, newHost);
});
});
// ensure invalid attachment connections are removed
this.postExecuted([ 'elements.move' ], function(e) {
var shapes = e.context.shapes;
forEach(shapes, function(shape) {
forEach(shape.attachers, function(attacher) {
// remove invalid outgoing connections
forEach(attacher.outgoing.slice(), function(connection) {
var allowed = rules.allowed('connection.reconnectStart', {
connection: connection,
source: connection.source,
target: connection.target
});
if (!allowed) {
modeling.removeConnection(connection);
}
});
// remove invalid incoming connections
forEach(attacher.incoming.slice(), function(connection) {
var allowed = rules.allowed('connection.reconnectEnd', {
connection: connection,
source: connection.source,
target: connection.target
});
if (!allowed) {
modeling.removeConnection(connection);
}
});
});
});
});
this.postExecute([ 'shape.create' ], function(e) {
var context = e.context,
shape = context.shape,
host = context.host;
if (host) {
modeling.updateAttachment(shape, host);
}
});
// update attachments if the host is replaced
this.postExecute([ 'shape.replace' ], function(e) {
var context = e.context,
oldShape = context.oldShape,
newShape = context.newShape;
// move the attachers to the new host
saveClear(oldShape.attachers, function(attacher) {
var allowed = rules.allowed('elements.move', {
target: newShape,
shapes: [attacher]
});
if (allowed === 'attach') {
modeling.updateAttachment(attacher, newShape);
} else {
modeling.removeShape(attacher);
}
});
// move attachers if new host has different size
if (newShape.attachers.length) {
forEach(newShape.attachers, function(attacher) {
var delta = getNewAttachShapeDelta(attacher, oldShape, newShape);
modeling.moveShape(attacher, delta, attacher.parent);
});
}
});
// move shape on host resize
this.postExecute([ 'shape.resize' ], function(event) {
var context = event.context,
shape = context.shape,
oldBounds = context.oldBounds,
newBounds = context.newBounds,
attachers = shape.attachers;
if (!attachers.length) {
return;
}
forEach(attachers, function(attacher) {
var delta = getNewAttachShapeDelta(attacher, oldBounds, newBounds);
modeling.moveShape(attacher, delta, attacher.parent);
if (attacher.label) {
modeling.moveShape(attacher.label, delta, attacher.label.parent);
}
});
});
// remove attachments
this.preExecute([ 'shape.delete' ], function(event) {
var shape = event.context.shape;
saveClear(shape.attachers, function(attacher) {
modeling.removeShape(attacher);
});
if (shape.host) {
modeling.updateAttachment(shape, null);
}
});
// Prevent attachers and their labels from moving, when the space tool is performed.
// Otherwise the attachers and their labels would be moved twice.
eventBus.on('spaceTool.move', function(event) {
var movingShapes = event.context.movingShapes;
// Collect all attachers which would be moved using the space tool
var movingAttachers = filter(movingShapes, function(shape) {
return shape.host && shape.host.id;
});
forEach(movingAttachers, function(shape) {
// Remove all attachers and their labels from the movingShapes, because they
// already will be moved along with the host.
Collections.remove(movingShapes, shape);
if (shape.label) {
Collections.remove(movingShapes, shape.label);
}
});
});
}
inherits(AttachSupport, CommandInterceptor);
AttachSupport.$inject = [ 'eventBus', 'modeling', 'movePreview', 'rules' ];
module.exports = AttachSupport;
/**
* Return attachers of the given shapes
*
* @param {Array} shapes
* @return {Array}
*/
function getAttachers(shapes) {
return flatten(map(shapes, function(s) {
return s.attachers || [];
}));
}
/**
* Return a combined list of elements and
* attachers.
*
* @param {Array} elements
* @return {Array} filtered
*/
function addAttached(elements) {
var attachers = getAttachers(elements);
return union(elements, attachers);
}
/**
* Return a filtered list of elements that do not
* contain attached elements with hosts being part
* of the selection.
*
* @param {Array} elements
*
* @return {Array} filtered
*/
function removeAttached(elements) {
var ids = groupBy(elements, 'id');
return filter(elements, function(element) {
while (element) {
// host in selection
if (element.host && ids[element.host.id]) {
return false;
}
element = element.parent;
}
return true;
});
}
},{"119":119,"261":261,"263":263,"277":277,"289":289,"291":291,"294":294,"301":301,"303":303,"304":304,"305":305}],135:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(206),
_dereq_(174)
],
__init__: [ 'attachSupport'],
attachSupport: [ 'type', _dereq_(134) ]
};
},{"134":134,"174":174,"206":206}],136:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var getBoundingBox = _dereq_(266).getBBox;
var asTRBL = _dereq_(252).asTRBL,
asBounds = _dereq_(252).asBounds;
var assign = _dereq_(431),
forEach = _dereq_(303),
values = _dereq_(440),
flatten = _dereq_(291),
groupBy = _dereq_(304);
var CommandInterceptor = _dereq_(119);
/**
* An auto resize component that takes care of expanding a parent element
* if child elements are created or moved close the parents edge.
*
* @param {EventBus} eventBus
* @param {ElementRegistry} elementRegistry
* @param {Modeling} modeling
* @param {Rules} rules
*/
function AutoResize(eventBus, elementRegistry, modeling, rules) {
CommandInterceptor.call(this, eventBus);
this._elementRegistry = elementRegistry;
this._modeling = modeling;
this._rules = rules;
var self = this;
this.postExecuted([ 'shape.create' ], function(event) {
var context = event.context,
hints = context.hints,
shape = context.shape,
parent = context.parent || context.newParent;
if (hints && hints.root === false) {
return;
}
self._expand([ shape ], parent);
});
this.postExecuted([ 'elements.move' ], function(event) {
var context = event.context,
elements = flatten(values(context.closure.topLevel)),
hints = context.hints;
if (hints && hints.autoResize === false) {
return;
}
var expandings = groupBy(elements, function(element) {
return element.parent.id;
});
forEach(expandings, function(elements, parentId) {
self._expand(elements, parentId);
});
});
}
AutoResize.$inject = [ 'eventBus', 'elementRegistry', 'modeling', 'rules' ];
inherits(AutoResize, CommandInterceptor);
module.exports = AutoResize;
/**
* Calculate the new bounds of the target shape, given
* a number of elements have been moved or added into the parent.
*
* This method considers the current size, the added elements as well as
* the provided padding for the new bounds.
*
* @param {Array} elements
* @param {djs.model.Shape} target
*/
AutoResize.prototype._getOptimalBounds = function(elements, target) {
var offset = this.getOffset(target),
padding = this.getPadding(target);
var elementsTrbl = asTRBL(getBoundingBox(elements)),
targetTrbl = asTRBL(target);
var newTrbl = {};
if (elementsTrbl.top - targetTrbl.top < padding.top) {
newTrbl.top = elementsTrbl.top - offset.top;
}
if (elementsTrbl.left - targetTrbl.left < padding.left) {
newTrbl.left = elementsTrbl.left - offset.left;
}
if (targetTrbl.right - elementsTrbl.right < padding.right) {
newTrbl.right = elementsTrbl.right + offset.right;
}
if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) {
newTrbl.bottom = elementsTrbl.bottom + offset.bottom;
}
return asBounds(assign({}, targetTrbl, newTrbl));
};
/**
* Expand the target shape respecting rules, offset and padding
*
* @param {Array} elements
* @param {djs.model.Shape|String} target|targetId
*/
AutoResize.prototype._expand = function(elements, target) {
if (typeof target === 'string') {
target = this._elementRegistry.get(target);
}
var allowed = this._rules.allowed('element.autoResize', {
elements: elements,
target: target
});
if (!allowed) {
return;
}
// calculate the new bounds
var newBounds = this._getOptimalBounds(elements, target);
if (!boundsChanged(newBounds, target)) {
return;
}
// resize the parent shape
this.resize(target, newBounds);
var parent = target.parent;
// recursively expand parent elements
if (parent) {
this._expand([ target ], parent);
}
};
/**
* Get the amount to expand the given shape in each direction.
*
* @param {djs.model.Shape} shape
*
* @return {Object} {top, bottom, left, right}
*/
AutoResize.prototype.getOffset = function(shape) {
return { top: 60, bottom: 60, left: 100, right: 100 };
};
/**
* Get the activation threshold for each side for which
* resize triggers.
*
* @param {djs.model.Shape} shape
*
* @return {Object} {top, bottom, left, right}
*/
AutoResize.prototype.getPadding = function(shape) {
return { top: 2, bottom: 2, left: 15, right: 15 };
};
/**
* Perform the actual resize operation.
*
* @param {djs.model.Shape} target
* @param {Object} newBounds
*/
AutoResize.prototype.resize = function(target, newBounds) {
this._modeling.resizeShape(target, newBounds);
};
function boundsChanged(newBounds, oldBounds) {
return (
newBounds.x !== oldBounds.x ||
newBounds.y !== oldBounds.y ||
newBounds.width !== oldBounds.width ||
newBounds.height !== oldBounds.height
);
}
},{"119":119,"252":252,"266":266,"289":289,"291":291,"303":303,"304":304,"431":431,"440":440}],137:[function(_dereq_,module,exports){
var RuleProvider = _dereq_(225);
var inherits = _dereq_(289);
/**
* This is a base rule provider for the element.autoResize rule.
*/
function AutoResizeProvider(eventBus) {
RuleProvider.call(this, eventBus);
var self = this;
this.addRule('element.autoResize', function(context) {
return self.canResize(context.elements, context.target);
});
}
AutoResizeProvider.$inject = [ 'eventBus' ];
inherits(AutoResizeProvider, RuleProvider);
module.exports = AutoResizeProvider;
/**
* Needs to be implemented by sub classes to allow actual auto resize
*
* @param {Array} elements
* @param {djs.model.Shape} target
*
* @return {Boolean}
*/
AutoResizeProvider.prototype.canResize = function(elements, target) {
return false;
};
},{"225":225,"289":289}],138:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
var EventUtil = _dereq_(267);
/**
* Initiates canvas scrolling if current cursor point is close to a border.
* Cancelled when current point moves back inside the scrolling borders
* or cancelled manually.
*
* Default options :
* scrollThresholdIn: [ 20, 20, 20, 20 ],
* scrollThresholdOut: [ 0, 0, 0, 0 ],
* scrollRepeatTimeout: 15,
* scrollStep: 10
*
* Threshold order:
* [ left, top, right, bottom ]
*/
function AutoScroll(config, eventBus, canvas, mouseTracking) {
this._canvas = canvas;
this._mouseTracking = mouseTracking;
this._opts = assign({
scrollThresholdIn: [ 20, 20, 20, 20 ],
scrollThresholdOut: [ 0, 0, 0, 0 ],
scrollRepeatTimeout: 15,
scrollStep: 10
}, config);
var self = this;
eventBus.on('drag.move', function(e) {
var point = self._toBorderPoint(e);
self.startScroll(point);
});
eventBus.on([ 'drag.cleanup' ], function() {
self.stopScroll();
});
}
AutoScroll.$inject = [ 'config.autoScroll', 'eventBus', 'canvas', 'mouseTracking'];
module.exports = AutoScroll;
/**
* Starts scrolling loop.
* Point is given in global scale in canvas container box plane.
*
* @param {Object} point { x: X, y: Y }
*/
AutoScroll.prototype.startScroll = function(point) {
var canvas = this._canvas;
var opts = this._opts;
var self = this;
var clientRect = canvas.getContainer().getBoundingClientRect();
var diff = [
point.x,
point.y,
clientRect.width - point.x,
clientRect.height - point.y
];
this.stopScroll();
var dx = 0,
dy = 0;
for (var i = 0; i < 4; i++) {
if (between(diff[i], opts.scrollThresholdOut[i], opts.scrollThresholdIn[i])) {
if (i === 0) {
dx = opts.scrollStep;
} else if (i == 1) {
dy = opts.scrollStep;
} else if (i == 2) {
dx = -opts.scrollStep;
} else if (i == 3) {
dy = -opts.scrollStep;
}
}
}
if (dx !== 0 || dy !== 0) {
canvas.scroll({ dx: dx, dy: dy });
this._scrolling = setTimeout(function() {
self.startScroll(point);
}, opts.scrollRepeatTimeout);
}
};
function between(val, start, end) {
if (start < val && val < end) {
return true;
}
return false;
}
/**
* Stops scrolling loop.
*/
AutoScroll.prototype.stopScroll = function() {
clearTimeout(this._scrolling);
};
/**
* Overrides defaults options.
*
* @param {Object} options
*/
AutoScroll.prototype.setOptions = function(options) {
this._opts = assign({}, this._opts, options);
};
/**
* Converts event to a point in canvas container plane in global scale.
*
* @param {Event} event
* @return {Point}
*/
AutoScroll.prototype._toBorderPoint = function(event) {
var clientRect = this._canvas._container.getBoundingClientRect();
var globalPosition = EventUtil.toPoint(event.originalEvent);
return {
x: globalPosition.x - clientRect.left,
y: globalPosition.y - clientRect.top
};
};
},{"267":267,"431":431}],139:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(162),
_dereq_(203)
],
__init__: [ 'autoScroll' ],
autoScroll: [ 'type', _dereq_(138) ]
};
},{"138":138,"162":162,"203":203}],140:[function(_dereq_,module,exports){
'use strict';
var Geometry = _dereq_(268),
BendpointUtil = _dereq_(142);
var MARKER_OK = 'connect-ok',
MARKER_NOT_OK = 'connect-not-ok',
MARKER_CONNECT_HOVER = 'connect-hover',
MARKER_CONNECT_UPDATING = 'djs-updating';
var COMMAND_BENDPOINT_UPDATE = 'connection.updateWaypoints',
COMMAND_RECONNECT_START = 'connection.reconnectStart',
COMMAND_RECONNECT_END = 'connection.reconnectEnd';
var round = Math.round;
var svgClasses = _dereq_(475),
svgRemove = _dereq_(481);
var translate = _dereq_(279).translate;
/**
* A component that implements moving of bendpoints
*/
function BendpointMove(injector, eventBus, canvas, dragging, graphicsFactory, rules, modeling) {
// optional connection docking integration
var connectionDocking = injector.get('connectionDocking', false);
// API
this.start = function(event, connection, bendpointIndex, insert) {
var type,
context,
waypoints = connection.waypoints,
gfx = canvas.getGraphics(connection);
if (!insert && bendpointIndex === 0) {
type = COMMAND_RECONNECT_START;
} else
if (!insert && bendpointIndex === waypoints.length - 1) {
type = COMMAND_RECONNECT_END;
} else {
type = COMMAND_BENDPOINT_UPDATE;
}
context = {
connection: connection,
bendpointIndex: bendpointIndex,
insert: insert,
type: type
};
dragging.init(event, 'bendpoint.move', {
data: {
connection: connection,
connectionGfx: gfx,
context: context
}
});
};
// DRAGGING IMPLEMENTATION
function redrawConnection(data) {
graphicsFactory.update('connection', data.connection, data.connectionGfx);
}
function filterRedundantWaypoints(waypoints) {
// alter copy of waypoints, not original
waypoints = waypoints.slice();
var idx = 0,
point,
previousPoint,
nextPoint;
while (waypoints[idx]) {
point = waypoints[idx];
previousPoint = waypoints[idx - 1];
nextPoint = waypoints[idx + 1];
if (Geometry.pointDistance(point, nextPoint) === 0 ||
Geometry.pointsOnLine(previousPoint, nextPoint, point)) {
// remove point, if overlapping with {nextPoint}
// or on line with {previousPoint} -> {point} -> {nextPoint}
waypoints.splice(idx, 1);
} else {
idx++;
}
}
return waypoints;
}
eventBus.on('bendpoint.move.start', function(e) {
var context = e.context,
connection = context.connection,
originalWaypoints = connection.waypoints,
waypoints = originalWaypoints.slice(),
insert = context.insert,
idx = context.bendpointIndex;
context.originalWaypoints = originalWaypoints;
if (insert) {
// insert placeholder for bendpoint to-be-added
waypoints.splice(idx, 0, null);
}
connection.waypoints = waypoints;
// add dragger gfx
context.draggerGfx = BendpointUtil.addBendpoint(canvas.getLayer('overlays'));
svgClasses(context.draggerGfx).add('djs-dragging');
canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
});
eventBus.on('bendpoint.move.hover', function(e) {
var context = e.context;
context.hover = e.hover;
if (e.hover) {
canvas.addMarker(e.hover, MARKER_CONNECT_HOVER);
// asks whether reconnect / bendpoint move / bendpoint add
// is allowed at the given position
var allowed = context.allowed = rules.allowed(context.type, context);
if (allowed) {
canvas.removeMarker(context.hover, MARKER_NOT_OK);
canvas.addMarker(context.hover, MARKER_OK);
context.target = context.hover;
} else if (allowed === false) {
canvas.removeMarker(context.hover, MARKER_OK);
canvas.addMarker(context.hover, MARKER_NOT_OK);
context.target = null;
}
}
});
eventBus.on([
'bendpoint.move.out',
'bendpoint.move.cleanup'
], function(e) {
// remove connect marker
// if it was added
var hover = e.context.hover;
if (hover) {
canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
canvas.removeMarker(hover, e.context.target ? MARKER_OK : MARKER_NOT_OK);
}
});
eventBus.on('bendpoint.move.move', function(e) {
var context = e.context,
moveType = context.type,
connection = e.connection,
source, target;
connection.waypoints[context.bendpointIndex] = { x: e.x, y: e.y };
if (connectionDocking) {
if (context.hover) {
if (moveType === COMMAND_RECONNECT_START) {
source = context.hover;
}
if (moveType === COMMAND_RECONNECT_END) {
target = context.hover;
}
}
connection.waypoints = connectionDocking.getCroppedWaypoints(connection, source, target);
}
// add dragger gfx
translate(context.draggerGfx, e.x, e.y);
redrawConnection(e);
});
eventBus.on([
'bendpoint.move.end',
'bendpoint.move.cancel'
], function(e) {
var context = e.context,
hover = context.hover,
connection = context.connection;
// remove dragger gfx
svgRemove(context.draggerGfx);
context.newWaypoints = connection.waypoints.slice();
connection.waypoints = context.originalWaypoints;
canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
if (hover) {
canvas.removeMarker(hover, MARKER_OK);
canvas.removeMarker(hover, MARKER_NOT_OK);
}
});
eventBus.on('bendpoint.move.end', function(e) {
var context = e.context,
waypoints = context.newWaypoints,
bendpointIndex = context.bendpointIndex,
bendpoint = waypoints[bendpointIndex],
allowed = context.allowed,
hints;
// ensure we have actual pixel values bendpoint
// coordinates (important when zoom level was > 1 during move)
bendpoint.x = round(bendpoint.x);
bendpoint.y = round(bendpoint.y);
if (allowed && context.type === COMMAND_RECONNECT_START) {
modeling.reconnectStart(context.connection, context.target, bendpoint);
} else
if (allowed && context.type === COMMAND_RECONNECT_END) {
modeling.reconnectEnd(context.connection, context.target, bendpoint);
} else
if (allowed !== false && context.type === COMMAND_BENDPOINT_UPDATE) {
// pass hints on the actual moved bendpoint
// this is useful for connection and label layouting
hints = {
bendpointMove: {
insert: e.context.insert,
bendpointIndex: bendpointIndex
}
};
modeling.updateWaypoints(context.connection, filterRedundantWaypoints(waypoints), hints);
} else {
redrawConnection(e);
return false;
}
});
eventBus.on('bendpoint.move.cancel', function(e) {
redrawConnection(e);
});
}
BendpointMove.$inject = [ 'injector', 'eventBus', 'canvas', 'dragging', 'graphicsFactory', 'rules', 'modeling' ];
module.exports = BendpointMove;
},{"142":142,"268":268,"279":279,"475":475,"481":481}],141:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303),
isArray = _dereq_(422);
var abs= Math.abs,
round = Math.round;
var TOLERANCE = 10;
function BendpointSnapping(eventBus) {
function snapTo(values, value) {
if (isArray(values)) {
var i = values.length;
while (i--) if (abs(values[i] - value) <= TOLERANCE) {
return values[i];
}
} else {
values = +values;
var rem = value % values;
if (rem < TOLERANCE) {
return value - rem;
}
if (rem > values - TOLERANCE) {
return value - rem + values;
}
}
return value;
}
function mid(element) {
if (element.width) {
return {
x: round(element.width / 2 + element.x),
y: round(element.height / 2 + element.y)
};
}
}
////////// connection segment snapping //////////////////////////////////////
function getConnectionSegmentSnaps(context) {
var snapPoints = context.snapPoints,
connection = context.connection,
waypoints = connection.waypoints,
segmentStart = context.segmentStart,
segmentStartIndex = context.segmentStartIndex,
segmentEnd = context.segmentEnd,
segmentEndIndex = context.segmentEndIndex,
axis = context.axis;
if (snapPoints) {
return snapPoints;
}
var referenceWaypoints = [
waypoints[segmentStartIndex - 1],
segmentStart,
segmentEnd,
waypoints[segmentEndIndex + 1]
];
if (segmentStartIndex < 2) {
referenceWaypoints.unshift(mid(connection.source));
}
if (segmentEndIndex > waypoints.length - 3) {
referenceWaypoints.unshift(mid(connection.target));
}
context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
forEach(referenceWaypoints, function(p) {
// we snap on existing bendpoints only,
// not placeholders that are inserted during add
if (p) {
p = p.original || p;
if (axis === 'y') {
snapPoints.horizontal.push(p.y);
}
if (axis === 'x') {
snapPoints.vertical.push(p.x);
}
}
});
return snapPoints;
}
eventBus.on('connectionSegment.move.move', 1500, function(event) {
var context = event.context,
snapPoints = getConnectionSegmentSnaps(context),
x = event.x,
y = event.y,
sx, sy;
if (!snapPoints) {
return;
}
// snap
sx = snapTo(snapPoints.vertical, x);
sy = snapTo(snapPoints.horizontal, y);
// correction x/y
var cx = (x - sx),
cy = (y - sy);
// update delta
assign(event, {
dx: event.dx - cx,
dy: event.dy - cy,
x: sx,
y: sy
});
});
///////// bendpoint snapping /////////////////////////////
function getBendpointSnaps(context) {
var snapPoints = context.snapPoints,
waypoints = context.connection.waypoints,
bendpointIndex = context.bendpointIndex;
if (snapPoints) {
return snapPoints;
}
var referenceWaypoints = [ waypoints[bendpointIndex - 1], waypoints[bendpointIndex + 1] ];
context.snapPoints = snapPoints = { horizontal: [] , vertical: [] };
forEach(referenceWaypoints, function(p) {
// we snap on existing bendpoints only,
// not placeholders that are inserted during add
if (p) {
p = p.original || p;
snapPoints.horizontal.push(p.y);
snapPoints.vertical.push(p.x);
}
});
return snapPoints;
}
eventBus.on('bendpoint.move.move', 1500, function(event) {
var context = event.context,
snapPoints = getBendpointSnaps(context),
target = context.target,
targetMid = target && mid(target),
x = event.x,
y = event.y,
sx, sy;
if (!snapPoints) {
return;
}
// snap
sx = snapTo(targetMid ? snapPoints.vertical.concat([ targetMid.x ]) : snapPoints.vertical, x);
sy = snapTo(targetMid ? snapPoints.horizontal.concat([ targetMid.y ]) : snapPoints.horizontal, y);
// correction x/y
var cx = (x - sx),
cy = (y - sy);
// update delta
assign(event, {
dx: event.dx - cx,
dy: event.dy - cy,
x: event.x - cx,
y: event.y - cy
});
});
}
BendpointSnapping.$inject = [ 'eventBus' ];
module.exports = BendpointSnapping;
},{"303":303,"422":422,"431":431}],142:[function(_dereq_,module,exports){
'use strict';
var Events = _dereq_(267),
Geometry = _dereq_(268);
var BENDPOINT_CLS = module.exports.BENDPOINT_CLS = 'djs-bendpoint';
var SEGMENT_DRAGGER_CLS = module.exports.SEGMENT_DRAGGER_CLS = 'djs-segment-dragger';
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClasses = _dereq_(475),
svgCreate = _dereq_(478);
var rotate = _dereq_(279).rotate,
translate = _dereq_(279).translate;
module.exports.toCanvasCoordinates = function(canvas, event) {
var position = Events.toPoint(event),
clientRect = canvas._container.getBoundingClientRect(),
offset;
// canvas relative position
offset = {
x: clientRect.left,
y: clientRect.top
};
// update actual event payload with canvas relative measures
var viewbox = canvas.viewbox();
return {
x: viewbox.x + (position.x - offset.x) / viewbox.scale,
y: viewbox.y + (position.y - offset.y) / viewbox.scale
};
};
module.exports.addBendpoint = function(parentGfx, cls) {
var groupGfx = svgCreate('g');
svgClasses(groupGfx).add(BENDPOINT_CLS);
svgAppend(parentGfx, groupGfx);
var visual = svgCreate('circle');
svgAttr(visual, {
cx: 0,
cy: 0,
r: 4
});
svgClasses(visual).add('djs-visual');
svgAppend(groupGfx, visual);
var hit = svgCreate('circle');
svgAttr(hit, {
cx: 0,
cy: 0,
r: 10
});
svgClasses(hit).add('djs-hit');
svgAppend(groupGfx, hit);
if (cls) {
svgClasses(groupGfx).add(cls);
}
return groupGfx;
};
function createParallelDragger(parentGfx, position, alignment) {
var draggerGfx = svgCreate('g');
svgAppend(parentGfx, draggerGfx);
var width = 14,
height = 3,
padding = 6,
hitWidth = width + padding,
hitHeight = height + padding;
var visual = svgCreate('rect');
svgAttr(visual, {
x: -width / 2,
y: -height / 2,
width: width,
height: height
});
svgClasses(visual).add('djs-visual');
svgAppend(draggerGfx, visual);
var hit = svgCreate('rect');
svgAttr(hit, {
x: -hitWidth / 2,
y: -hitHeight / 2,
width: hitWidth,
height: hitHeight
});
svgClasses(hit).add('djs-hit');
svgAppend(draggerGfx, hit);
rotate(draggerGfx, alignment === 'h' ? 90 : 0, 0, 0);
return draggerGfx;
}
module.exports.addSegmentDragger = function(parentGfx, segmentStart, segmentEnd) {
var groupGfx = svgCreate('g'),
mid = Geometry.getMidPoint(segmentStart, segmentEnd),
alignment = Geometry.pointsAligned(segmentStart, segmentEnd);
svgAppend(parentGfx, groupGfx);
createParallelDragger(groupGfx, mid, alignment);
svgClasses(groupGfx).add(SEGMENT_DRAGGER_CLS);
svgClasses(groupGfx).add(alignment === 'h' ? 'vertical' : 'horizontal');
translate(groupGfx, mid.x, mid.y);
return groupGfx;
};
},{"267":267,"268":268,"279":279,"472":472,"474":474,"475":475,"478":478}],143:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var domEvent = _dereq_(450),
domQuery = _dereq_(452),
BendpointUtil = _dereq_(142);
var pointsAligned = _dereq_(268).pointsAligned,
getMidPoint = _dereq_(268).getMidPoint;
var BENDPOINT_CLS = BendpointUtil.BENDPOINT_CLS,
SEGMENT_DRAGGER_CLS = BendpointUtil.SEGMENT_DRAGGER_CLS;
var getApproxIntersection = _dereq_(272).getApproxIntersection;
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClasses = _dereq_(475),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
var translate = _dereq_(279).translate;
/**
* A service that adds editable bendpoints to connections.
*/
function Bendpoints(eventBus, canvas, interactionEvents,
bendpointMove, connectionSegmentMove) {
function getConnectionIntersection(waypoints, event) {
var localPosition = BendpointUtil.toCanvasCoordinates(canvas, event),
intersection = getApproxIntersection(waypoints, localPosition);
return intersection;
}
function isIntersectionMiddle(intersection, waypoints, treshold) {
var idx = intersection.index,
p = intersection.point,
p0, p1, mid, aligned, xDelta, yDelta;
if (idx <= 0 || intersection.bendpoint) {
return false;
}
p0 = waypoints[idx - 1];
p1 = waypoints[idx];
mid = getMidPoint(p0, p1),
aligned = pointsAligned(p0, p1);
xDelta = Math.abs(p.x - mid.x);
yDelta = Math.abs(p.y - mid.y);
return aligned && xDelta <= treshold && yDelta <= treshold;
}
function activateBendpointMove(event, connection) {
var waypoints = connection.waypoints,
intersection = getConnectionIntersection(waypoints, event);
if (!intersection) {
return;
}
if (isIntersectionMiddle(intersection, waypoints, 10)) {
connectionSegmentMove.start(event, connection, intersection.index);
} else {
bendpointMove.start(event, connection, intersection.index, !intersection.bendpoint);
}
}
function bindInteractionEvents(node, eventName, element) {
domEvent.bind(node, eventName, function(event) {
interactionEvents.triggerMouseEvent(eventName, event, element);
event.stopPropagation();
});
}
function getBendpointsContainer(element, create) {
var layer = canvas.getLayer('overlays'),
gfx = domQuery('.djs-bendpoints[data-element-id=' + element.id + ']', layer);
if (!gfx && create) {
gfx = svgCreate('g');
svgAttr(gfx, { 'data-element-id': element.id });
svgClasses(gfx).add('djs-bendpoints');
svgAppend(layer, gfx);
bindInteractionEvents(gfx, 'mousedown', element);
bindInteractionEvents(gfx, 'click', element);
bindInteractionEvents(gfx, 'dblclick', element);
}
return gfx;
}
function createBendpoints(gfx, connection) {
connection.waypoints.forEach(function(p, idx) {
var bendpoint = BendpointUtil.addBendpoint(gfx);
svgAppend(gfx, bendpoint);
translate(bendpoint, p.x, p.y);
});
// add floating bendpoint
BendpointUtil.addBendpoint(gfx, 'floating');
}
function createSegmentDraggers(gfx, connection) {
var waypoints = connection.waypoints;
var segmentStart,
segmentEnd;
for (var i = 1; i < waypoints.length; i++) {
segmentStart = waypoints[i - 1];
segmentEnd = waypoints[i];
if (pointsAligned(segmentStart, segmentEnd)) {
BendpointUtil.addSegmentDragger(gfx, segmentStart, segmentEnd);
}
}
}
function clearBendpoints(gfx) {
forEach(domQuery.all('.' + BENDPOINT_CLS, gfx), function(node) {
svgRemove(node);
});
}
function clearSegmentDraggers(gfx) {
forEach(domQuery.all('.' + SEGMENT_DRAGGER_CLS, gfx), function(node) {
svgRemove(node);
});
}
function addHandles(connection) {
var gfx = getBendpointsContainer(connection);
if (!gfx) {
gfx = getBendpointsContainer(connection, true);
createBendpoints(gfx, connection);
createSegmentDraggers(gfx, connection);
}
return gfx;
}
function updateHandles(connection) {
var gfx = getBendpointsContainer(connection);
if (gfx) {
clearSegmentDraggers(gfx);
clearBendpoints(gfx);
createSegmentDraggers(gfx, connection);
createBendpoints(gfx, connection);
}
}
eventBus.on('connection.changed', function(event) {
updateHandles(event.element);
});
eventBus.on('connection.remove', function(event) {
var gfx = getBendpointsContainer(event.element);
if (gfx) {
svgRemove(gfx);
}
});
eventBus.on('element.marker.update', function(event) {
var element = event.element,
bendpointsGfx;
if (!element.waypoints) {
return;
}
bendpointsGfx = addHandles(element);
if (event.add) {
svgClasses(bendpointsGfx).add(event.marker);
} else {
svgClasses(bendpointsGfx).remove(event.marker);
}
});
eventBus.on('element.mousemove', function(event) {
var element = event.element,
waypoints = element.waypoints,
bendpointsGfx,
floating,
intersection;
if (waypoints) {
bendpointsGfx = getBendpointsContainer(element, true);
floating = domQuery('.floating', bendpointsGfx);
if (!floating) {
return;
}
intersection = getConnectionIntersection(waypoints, event.originalEvent);
if (intersection) {
translate(floating, intersection.point.x, intersection.point.y);
}
}
});
eventBus.on('element.mousedown', function(event) {
var originalEvent = event.originalEvent,
element = event.element,
waypoints = element.waypoints;
if (!waypoints) {
return;
}
activateBendpointMove(originalEvent, element, waypoints);
});
eventBus.on('selection.changed', function(event) {
var newSelection = event.newSelection,
primary = newSelection[0];
if (primary && primary.waypoints) {
addHandles(primary);
}
});
eventBus.on('element.hover', function(event) {
var element = event.element;
if (element.waypoints) {
addHandles(element);
interactionEvents.registerEvent(event.gfx, 'mousemove', 'element.mousemove');
}
});
eventBus.on('element.out', function(event) {
interactionEvents.unregisterEvent(event.gfx, 'mousemove', 'element.mousemove');
});
// update bendpoint container data attribute on element ID change
eventBus.on('element.updateId', function(context) {
var element = context.element,
newId = context.newId;
if (element.waypoints) {
var bendpointContainer = getBendpointsContainer(element);
if (bendpointContainer) {
svgAttr(bendpointContainer, { 'data-element-id': newId });
}
}
});
// API
this.addHandles = addHandles;
this.updateHandles = updateHandles;
this.getBendpointsContainer = getBendpointsContainer;
}
Bendpoints.$inject = [
'eventBus', 'canvas', 'interactionEvents',
'bendpointMove', 'connectionSegmentMove'
];
module.exports = Bendpoints;
},{"142":142,"268":268,"272":272,"279":279,"303":303,"450":450,"452":452,"472":472,"474":474,"475":475,"478":478,"481":481}],144:[function(_dereq_,module,exports){
'use strict';
var Geometry = _dereq_(268),
BendpointUtil = _dereq_(142),
LayoutUtil = _dereq_(252);
var MARKER_CONNECT_HOVER = 'connect-hover',
MARKER_CONNECT_UPDATING = 'djs-updating';
var svgClasses = _dereq_(475),
svgRemove = _dereq_(481);
var translate = _dereq_(279).translate;
function axisAdd(point, axis, delta) {
return axisSet(point, axis, point[axis] + delta);
}
function axisSet(point, axis, value) {
return {
x: (axis === 'x' ? value : point.x),
y: (axis === 'y' ? value : point.y)
};
}
function axisFenced(position, segmentStart, segmentEnd, axis) {
var maxValue = Math.max(segmentStart[axis], segmentEnd[axis]),
minValue = Math.min(segmentStart[axis], segmentEnd[axis]);
var padding = 20;
var fencedValue = Math.min(Math.max(minValue + padding, position[axis]), maxValue - padding);
return axisSet(segmentStart, axis, fencedValue);
}
function flipAxis(axis) {
return axis === 'x' ? 'y' : 'x';
}
/**
* Get the docking point on the given element.
*
* Compute a reasonable docking, if non exists.
*
* @param {Point} point
* @param {djs.model.Shape} referenceElement
* @param {String} moveAxis (x|y)
*
* @return {Point}
*/
function getDocking(point, referenceElement, moveAxis) {
var referenceMid,
inverseAxis;
if (point.original) {
return point.original;
} else {
referenceMid = LayoutUtil.getMid(referenceElement);
inverseAxis = flipAxis(moveAxis);
return axisSet(point, inverseAxis, referenceMid[inverseAxis]);
}
}
/**
* A component that implements moving of bendpoints
*/
function ConnectionSegmentMove(injector, eventBus, canvas, dragging, graphicsFactory, rules, modeling) {
// optional connection docking integration
var connectionDocking = injector.get('connectionDocking', false);
// API
this.start = function(event, connection, idx) {
var context,
gfx = canvas.getGraphics(connection),
segmentStartIndex = idx - 1,
segmentEndIndex = idx,
waypoints = connection.waypoints,
segmentStart = waypoints[segmentStartIndex],
segmentEnd = waypoints[segmentEndIndex],
direction,
axis;
direction = Geometry.pointsAligned(segmentStart, segmentEnd);
// do not move diagonal connection
if (!direction) {
return;
}
// the axis where we are going to move things
axis = direction === 'v' ? 'y' : 'x';
if (segmentStartIndex === 0) {
segmentStart = getDocking(segmentStart, connection.source, axis);
}
if (segmentEndIndex === waypoints.length - 1) {
segmentEnd = getDocking(segmentEnd, connection.target, axis);
}
context = {
connection: connection,
segmentStartIndex: segmentStartIndex,
segmentEndIndex: segmentEndIndex,
segmentStart: segmentStart,
segmentEnd: segmentEnd,
axis: axis
};
dragging.init(event, {
x: (segmentStart.x + segmentEnd.x)/2,
y: (segmentStart.y + segmentEnd.y)/2
}, 'connectionSegment.move', {
cursor: axis === 'x' ? 'resize-ew' : 'resize-ns',
data: {
connection: connection,
connectionGfx: gfx,
context: context
}
});
};
/**
* Crop connection if connection cropping is provided.
*
* @param {Connection} connection
* @param {Array} newWaypoints
*
* @return {Array} cropped connection waypoints
*/
function cropConnection(connection, newWaypoints) {
// crop connection, if docking service is provided only
if (!connectionDocking) {
return newWaypoints;
}
var oldWaypoints = connection.waypoints,
croppedWaypoints;
// temporary set new waypoints
connection.waypoints = newWaypoints;
croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);
// restore old waypoints
connection.waypoints = oldWaypoints;
return croppedWaypoints;
}
// DRAGGING IMPLEMENTATION
function redrawConnection(data) {
graphicsFactory.update('connection', data.connection, data.connectionGfx);
}
function updateDragger(context, segmentOffset, event) {
var newWaypoints = context.newWaypoints,
segmentStartIndex = context.segmentStartIndex + segmentOffset,
segmentStart = newWaypoints[segmentStartIndex],
segmentEndIndex = context.segmentEndIndex + segmentOffset,
segmentEnd = newWaypoints[segmentEndIndex],
axis = flipAxis(context.axis);
// make sure the dragger does not move
// outside the connection
var draggerPosition = axisFenced(event, segmentStart, segmentEnd, axis);
// update dragger
translate(context.draggerGfx, draggerPosition.x, draggerPosition.y);
}
/**
* Filter waypoints for redundant ones (i.e. on the same axis).
* Returns the filtered waypoints and the offset related to the segment move.
*
* @param {Array} waypoints
* @param {Integer} segmentStartIndex of moved segment start
*
* @return {Object} { filteredWaypoints, segmentOffset }
*/
function filterRedundantWaypoints(waypoints, segmentStartIndex) {
var segmentOffset = 0;
var filteredWaypoints = waypoints.filter(function(r, idx) {
if (Geometry.pointsOnLine(waypoints[idx - 1], waypoints[idx + 1], r)) {
// remove point and increment offset
segmentOffset = idx <= segmentStartIndex ? segmentOffset - 1 : segmentOffset;
return false;
}
// dont remove point
return true;
});
return {
waypoints: filteredWaypoints,
segmentOffset: segmentOffset
};
}
eventBus.on('connectionSegment.move.start', function(e) {
var context = e.context,
connection = e.connection,
layer = canvas.getLayer('overlays');
context.originalWaypoints = connection.waypoints.slice();
// add dragger gfx
context.draggerGfx = BendpointUtil.addSegmentDragger(layer, context.segmentStart, context.segmentEnd);
svgClasses(context.draggerGfx).add('djs-dragging');
canvas.addMarker(connection, MARKER_CONNECT_UPDATING);
});
eventBus.on('connectionSegment.move.move', function(e) {
var context = e.context,
connection = context.connection,
segmentStartIndex = context.segmentStartIndex,
segmentEndIndex = context.segmentEndIndex,
segmentStart = context.segmentStart,
segmentEnd = context.segmentEnd,
axis = context.axis;
var newWaypoints = context.originalWaypoints.slice(),
newSegmentStart = axisAdd(segmentStart, axis, e['d' + axis]),
newSegmentEnd = axisAdd(segmentEnd, axis, e['d' + axis]);
// original waypoint count and added / removed
// from start waypoint delta. We use the later
// to retrieve the updated segmentStartIndex / segmentEndIndex
var waypointCount = newWaypoints.length,
segmentOffset = 0;
// move segment start / end by axis delta
newWaypoints[segmentStartIndex] = newSegmentStart;
newWaypoints[segmentEndIndex] = newSegmentEnd;
var sourceToSegmentOrientation,
targetToSegmentOrientation;
// handle first segment
if (segmentStartIndex < 2) {
sourceToSegmentOrientation = LayoutUtil.getOrientation(connection.source, newSegmentStart);
// first bendpoint, remove first segment if intersecting
if (segmentStartIndex === 1) {
if (sourceToSegmentOrientation === 'intersect') {
newWaypoints.shift();
newWaypoints[0] = newSegmentStart;
segmentOffset--;
}
}
// docking point, add segment if not intersecting anymore
else {
if (sourceToSegmentOrientation !== 'intersect') {
newWaypoints.unshift(segmentStart);
segmentOffset++;
}
}
}
// handle last segment
if (segmentEndIndex > waypointCount - 3) {
targetToSegmentOrientation = LayoutUtil.getOrientation(connection.target, newSegmentEnd);
// last bendpoint, remove last segment if intersecting
if (segmentEndIndex === waypointCount - 2) {
if (targetToSegmentOrientation === 'intersect') {
newWaypoints.pop();
newWaypoints[newWaypoints.length - 1] = newSegmentEnd;
}
}
// last bendpoint, remove last segment if intersecting
else {
if (targetToSegmentOrientation !== 'intersect') {
newWaypoints.push(segmentEnd);
}
}
}
// update connection waypoints
context.newWaypoints = connection.waypoints = cropConnection(connection, newWaypoints);
// update dragger position
updateDragger(context, segmentOffset, e);
// save segmentOffset in context
context.newSegmentStartIndex = segmentStartIndex + segmentOffset;
// redraw connection
redrawConnection(e);
});
eventBus.on('connectionSegment.move.hover', function(e) {
e.context.hover = e.hover;
canvas.addMarker(e.hover, MARKER_CONNECT_HOVER);
});
eventBus.on([
'connectionSegment.move.out',
'connectionSegment.move.cleanup'
], function(e) {
// remove connect marker
// if it was added
var hover = e.context.hover;
if (hover) {
canvas.removeMarker(hover, MARKER_CONNECT_HOVER);
}
});
eventBus.on('connectionSegment.move.cleanup', function(e) {
var context = e.context,
connection = context.connection;
// remove dragger gfx
if (context.draggerGfx) {
svgRemove(context.draggerGfx);
}
canvas.removeMarker(connection, MARKER_CONNECT_UPDATING);
});
eventBus.on([
'connectionSegment.move.cancel',
'connectionSegment.move.end'
], function(e) {
var context = e.context,
connection = context.connection;
connection.waypoints = context.originalWaypoints;
redrawConnection(e);
});
eventBus.on('connectionSegment.move.end', function(e) {
var context = e.context,
connection = context.connection,
newWaypoints = context.newWaypoints,
newSegmentStartIndex = context.newSegmentStartIndex;
// ensure we have actual pixel values bendpoint
// coordinates (important when zoom level was > 1 during move)
newWaypoints = newWaypoints.map(function(p) {
return {
original: p.original,
x: Math.round(p.x),
y: Math.round(p.y)
};
});
// apply filter redunant waypoints
var filtered = filterRedundantWaypoints(newWaypoints, newSegmentStartIndex);
// get filtered waypoints
var filteredWaypoints = filtered.waypoints,
croppedWaypoints = cropConnection(connection, filteredWaypoints),
segmentOffset = filtered.segmentOffset;
var hints = {
segmentMove: {
segmentStartIndex: context.segmentStartIndex,
newSegmentStartIndex: newSegmentStartIndex + segmentOffset
}
};
modeling.updateWaypoints(connection, croppedWaypoints, hints);
});
}
ConnectionSegmentMove.$inject = [
'injector', 'eventBus', 'canvas',
'dragging', 'graphicsFactory', 'rules',
'modeling'
];
module.exports = ConnectionSegmentMove;
},{"142":142,"252":252,"268":268,"279":279,"475":475,"481":481}],145:[function(_dereq_,module,exports){
module.exports = {
__depends__: [ _dereq_(162), _dereq_(227) ],
__init__: [ 'bendpoints', 'bendpointSnapping' ],
bendpoints: [ 'type', _dereq_(143) ],
bendpointMove: [ 'type', _dereq_(140) ],
connectionSegmentMove: [ 'type', _dereq_(144) ],
bendpointSnapping: [ 'type', _dereq_(141) ]
};
},{"140":140,"141":141,"143":143,"144":144,"162":162,"227":227}],146:[function(_dereq_,module,exports){
'use strict';
var getElementType = _dereq_(266).getType;
/**
* Adds change support to the diagram, including
*
*
* redrawing shapes and connections on change
*
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {ElementRegistry} elementRegistry
* @param {GraphicsFactory} graphicsFactory
*/
function ChangeSupport(eventBus, canvas, elementRegistry, graphicsFactory) {
// redraw shapes / connections on change
eventBus.on('element.changed', function(event) {
var element = event.element;
// element might have been deleted and replaced by new element with same ID
// thus check for parent of element except for root element
if (element.parent || element === canvas.getRootElement()) {
event.gfx = elementRegistry.getGraphics(element);
}
// shape + gfx may have been deleted
if (!event.gfx) {
return;
}
eventBus.fire(getElementType(element) + '.changed', event);
});
eventBus.on('elements.changed', function(event) {
var elements = event.elements;
elements.forEach(function(e) {
eventBus.fire('element.changed', { element: e });
});
graphicsFactory.updateContainments(elements);
});
eventBus.on('shape.changed', function(event) {
graphicsFactory.update('shape', event.element, event.gfx);
});
eventBus.on('connection.changed', function(event) {
graphicsFactory.update('connection', event.element, event.gfx);
});
}
ChangeSupport.$inject = [ 'eventBus', 'canvas', 'elementRegistry', 'graphicsFactory' ];
module.exports = ChangeSupport;
},{"266":266}],147:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'changeSupport'],
changeSupport: [ 'type', _dereq_(146) ]
};
},{"146":146}],148:[function(_dereq_,module,exports){
'use strict';
/**
* A clip board stub
*/
function Clipboard() {}
module.exports = Clipboard;
Clipboard.prototype.get = function() {
return this._data;
};
Clipboard.prototype.set = function(data) {
this._data = data;
};
Clipboard.prototype.clear = function() {
var data = this._data;
delete this._data;
return data;
};
Clipboard.prototype.isEmpty = function() {
return !this._data;
};
},{}],149:[function(_dereq_,module,exports){
module.exports = {
clipboard: [ 'type', _dereq_(148) ]
};
},{"148":148}],150:[function(_dereq_,module,exports){
'use strict';
var LayoutUtil = _dereq_(252);
var MARKER_OK = 'connect-ok',
MARKER_NOT_OK = 'connect-not-ok';
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
function Connect(eventBus, dragging, modeling, rules, canvas, graphicsFactory) {
// TODO(nre): separate UI and events
// rules
function canConnect(source, target) {
return rules.allowed('connection.create', {
source: source,
target: target
});
}
// layouting
function crop(start, end, source, target) {
var sourcePath = graphicsFactory.getShapePath(source),
targetPath = target && graphicsFactory.getShapePath(target),
connectionPath = graphicsFactory.getConnectionPath({ waypoints: [ start, end ] });
start = LayoutUtil.getElementLineIntersection(sourcePath, connectionPath, true) || start;
end = (target && LayoutUtil.getElementLineIntersection(targetPath, connectionPath, false)) || end;
return [ start, end ];
}
// event handlers
eventBus.on('connect.move', function(event) {
var context = event.context,
source = context.source,
target = context.target,
visual = context.visual,
sourcePosition = context.sourcePosition,
endPosition,
waypoints;
// update connection visuals during drag
endPosition = {
x: event.x,
y: event.y
};
waypoints = crop(sourcePosition, endPosition, source, target);
svgAttr(visual, { 'points': [ waypoints[0].x, waypoints[0].y, waypoints[1].x, waypoints[1].y ] });
});
eventBus.on('connect.hover', function(event) {
var context = event.context,
source = context.source,
hover = event.hover,
canExecute;
canExecute = context.canExecute = canConnect(source, hover);
// simply ignore hover
if (canExecute === null) {
return;
}
context.target = hover;
canvas.addMarker(hover, canExecute ? MARKER_OK : MARKER_NOT_OK);
});
eventBus.on([ 'connect.out', 'connect.cleanup' ], function(event) {
var context = event.context;
if (context.target) {
canvas.removeMarker(context.target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
}
context.target = null;
});
eventBus.on('connect.cleanup', function(event) {
var context = event.context;
if (context.visual) {
svgRemove(context.visual);
}
});
eventBus.on('connect.start', function(event) {
var context = event.context,
visual;
visual = svgCreate('polyline');
svgAttr(visual, {
'stroke': '#333',
'strokeDasharray': [ 1 ],
'strokeWidth': 2,
'pointer-events': 'none'
});
svgAppend(canvas.getDefaultLayer(), visual);
context.visual = visual;
});
eventBus.on('connect.end', function(event) {
var context = event.context,
source = context.source,
sourcePosition = context.sourcePosition,
target = context.target,
targetPosition = {
x: event.x,
y: event.y
},
canExecute = context.canExecute || canConnect(source, target);
if (!canExecute) {
return false;
}
var attrs = null,
hints = {
connectionStart: sourcePosition,
connectionEnd: targetPosition
};
if (typeof canExecute === 'object') {
attrs = canExecute;
}
modeling.connect(source, target, attrs, hints);
});
// API
/**
* Start connect operation.
*
* @param {DOMEvent} event
* @param {djs.model.Base} source
* @param {Point} [sourcePosition]
* @param {Boolean} [autoActivate=false]
*/
this.start = function(event, source, sourcePosition, autoActivate) {
if (typeof sourcePosition !== 'object') {
autoActivate = sourcePosition;
sourcePosition = LayoutUtil.getMid(source);
}
dragging.init(event, 'connect', {
autoActivate: autoActivate,
data: {
shape: source,
context: {
source: source,
sourcePosition: sourcePosition
}
}
});
};
}
Connect.$inject = [ 'eventBus', 'dragging', 'modeling', 'rules', 'canvas', 'graphicsFactory' ];
module.exports = Connect;
},{"252":252,"472":472,"474":474,"478":478,"481":481}],151:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(233),
_dereq_(227),
_dereq_(162)
],
connect: [ 'type', _dereq_(150) ]
};
},{"150":150,"162":162,"227":227,"233":233}],152:[function(_dereq_,module,exports){
'use strict';
var isFunction = _dereq_(423),
isArray = _dereq_(422),
forEach = _dereq_(303),
domDelegate = _dereq_(448),
domEvent = _dereq_(450),
domAttr = _dereq_(444),
domQuery = _dereq_(452),
domClasses = _dereq_(445),
domify = _dereq_(449);
var entrySelector = '.entry';
/**
* A context pad that displays element specific, contextual actions next
* to a diagram element.
*
* @param {EventBus} eventBus
* @param {Overlays} overlays
*/
function ContextPad(eventBus, overlays) {
this._providers = [];
this._eventBus = eventBus;
this._overlays = overlays;
this._current = null;
this._init();
}
ContextPad.$inject = [ 'eventBus', 'overlays' ];
module.exports = ContextPad;
/**
* Registers events needed for interaction with other components
*/
ContextPad.prototype._init = function() {
var eventBus = this._eventBus;
var self = this;
eventBus.on('selection.changed', function(e) {
var selection = e.newSelection;
if (selection.length === 1) {
self.open(selection[0]);
} else {
self.close();
}
});
eventBus.on('elements.delete', function(event) {
var elements = event.elements;
forEach(elements, function(e) {
if (self.isOpen(e)) {
self.close();
}
});
});
eventBus.on('element.changed', function(event) {
var element = event.element,
current = self._current;
// force reopen if element for which we are currently opened changed
if (current && current.element === element) {
self.open(element, true);
}
});
};
/**
* Register a provider with the context pad
*
* @param {ContextPadProvider} provider
*/
ContextPad.prototype.registerProvider = function(provider) {
this._providers.push(provider);
};
/**
* Returns the context pad entries for a given element
*
* @param {djs.element.Base} element
*
* @return {Array} list of entries
*/
ContextPad.prototype.getEntries = function(element) {
var entries = {};
// loop through all providers and their entries.
// group entries by id so that overriding an entry is possible
forEach(this._providers, function(provider) {
var e = provider.getContextPadEntries(element);
forEach(e, function(entry, id) {
entries[id] = entry;
});
});
return entries;
};
/**
* Trigger an action available on the opened context pad
*
* @param {String} action
* @param {Event} event
* @param {Boolean} [autoActivate=false]
*/
ContextPad.prototype.trigger = function(action, event, autoActivate) {
var element = this._current.element,
entries = this._current.entries,
entry,
handler,
originalEvent,
button = event.delegateTarget || event.target;
if (!button) {
return event.preventDefault();
}
entry = entries[domAttr(button, 'data-action')];
handler = entry.action;
originalEvent = event.originalEvent || event;
// simple action (via callback function)
if (isFunction(handler)) {
if (action === 'click') {
return handler(originalEvent, element, autoActivate);
}
} else {
if (handler[action]) {
return handler[action](originalEvent, element, autoActivate);
}
}
// silence other actions
event.preventDefault();
};
/**
* Open the context pad for the given element
*
* @param {djs.model.Base} element
* @param {Boolean} force if true, force reopening the context pad
*/
ContextPad.prototype.open = function(element, force) {
if (!force && this.isOpen(element)) {
return;
}
this.close();
this._updateAndOpen(element);
};
ContextPad.prototype._updateAndOpen = function(element) {
var entries = this.getEntries(element),
pad = this.getPad(element),
html = pad.html;
forEach(entries, function(entry, id) {
var grouping = entry.group || 'default',
control = domify(entry.html || '
'),
container;
domAttr(control, 'data-action', id);
container = domQuery('[data-group=' + grouping + ']', html);
if (!container) {
container = domify('
');
html.appendChild(container);
}
container.appendChild(control);
if (entry.className) {
addClasses(control, entry.className);
}
if (entry.title) {
domAttr(control, 'title', entry.title);
}
if (entry.imageUrl) {
control.appendChild(domify(' '));
}
});
domClasses(html).add('open');
this._current = {
element: element,
pad: pad,
entries: entries
};
this._eventBus.fire('contextPad.open', { current: this._current });
};
ContextPad.prototype.getPad = function(element) {
if (this.isOpen()) {
return this._current.pad;
}
var self = this;
var overlays = this._overlays;
var html = domify('
');
domDelegate.bind(html, entrySelector, 'click', function(event) {
self.trigger('click', event);
});
domDelegate.bind(html, entrySelector, 'dragstart', function(event) {
self.trigger('dragstart', event);
});
// stop propagation of mouse events
domEvent.bind(html, 'mousedown', function(event) {
event.stopPropagation();
});
this._overlayId = overlays.add(element, 'context-pad', {
position: {
right: -9,
top: -6
},
html: html
});
var pad = overlays.get(this._overlayId);
this._eventBus.fire('contextPad.create', { element: element, pad: pad });
return pad;
};
/**
* Close the context pad
*/
ContextPad.prototype.close = function() {
if (!this.isOpen()) {
return;
}
this._overlays.remove(this._overlayId);
this._overlayId = null;
this._eventBus.fire('contextPad.close', { current: this._current });
this._current = null;
};
/**
* Check if pad is open. If element is given, will check
* if pad is opened with given element.
*
* @param {Element} element
* @return {Boolean}
*/
ContextPad.prototype.isOpen = function(element) {
return !!this._current && (!element ? true : this._current.element === element);
};
////////// helpers /////////////////////////////
function addClasses(element, classNames) {
var classes = domClasses(element);
var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
actualClassNames.forEach(function(cls) {
classes.add(cls);
});
}
},{"303":303,"422":422,"423":423,"444":444,"445":445,"448":448,"449":449,"450":450,"452":452}],153:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(170),
_dereq_(211)
],
contextPad: [ 'type', _dereq_(152) ]
};
},{"152":152,"170":170,"211":211}],154:[function(_dereq_,module,exports){
'use strict';
var isArray = _dereq_(422),
forEach = _dereq_(303),
map = _dereq_(305),
find = _dereq_(302),
findIndex = _dereq_(290),
sortBy = _dereq_(310),
reduce = _dereq_(306);
var getBBox = _dereq_(266).getBBox;
var PositionUtil = _dereq_(276);
var CopyPasteUtil = _dereq_(264),
ElementsUtil = _dereq_(266);
function CopyPaste(eventBus, modeling, elementFactory, rules, clipboard, canvas) {
this._eventBus = eventBus;
this._modeling = modeling;
this._elementFactory = elementFactory;
this._rules = rules;
this._canvas = canvas;
this._clipboard = clipboard;
this._descriptors = [];
// Element creation priorities:
// - 1: Independent shapes
// - 2: Attached shapes
// - 3: Connections
// - 4: labels
this.registerDescriptor(function(element, descriptor) {
// Base priority
descriptor.priority = 1;
descriptor.id = element.id;
if (element.parent) {
descriptor.parent = element.parent.id;
}
if (element.labelTarget) {
// Labels priority
descriptor.priority = 4;
descriptor.labelTarget = element.labelTarget.id;
}
if (element.host) {
// Attached shapes priority
descriptor.priority = 2;
descriptor.host = element.host.id;
}
if (typeof element.x === 'number') {
descriptor.x = element.x;
descriptor.y = element.y;
}
if (element.width) {
descriptor.width = element.width;
descriptor.height = element.height;
}
if (element.waypoints) {
// Connections priority
descriptor.priority = 3;
descriptor.waypoints = [];
forEach(element.waypoints, function(waypoint) {
var wp = {
x: waypoint.x,
y: waypoint.y
};
if (waypoint.original) {
wp.original = {
x: waypoint.original.x,
y: waypoint.original.y
};
}
descriptor.waypoints.push(wp);
});
}
if (element.source && element.target) {
descriptor.source = element.source.id;
descriptor.target = element.target.id;
}
return descriptor;
});
}
CopyPaste.$inject = [
'eventBus',
'modeling',
'elementFactory',
'rules',
'clipboard',
'canvas'
];
module.exports = CopyPaste;
/**
* Copy a number of elements.
*
* @param {djs.model.Base} selectedElements
*
* @return {Object} the copied tree
*/
CopyPaste.prototype.copy = function(selectedElements) {
var clipboard = this._clipboard,
tree, bbox;
if (!isArray(selectedElements)) {
selectedElements = selectedElements ? [ selectedElements ] : [];
}
if (!selectedElements.length) {
return;
}
tree = this.createTree(selectedElements);
bbox = this._bbox = PositionUtil.center(getBBox(tree.allShapes));
// not needed after computing the center position of the copied elements
delete tree.allShapes;
forEach(tree, function(elements) {
forEach(elements, function(element) {
var delta, labelTarget;
// set label's relative position to their label target
if (element.labelTarget) {
labelTarget = find(elements, { id: element.labelTarget });
// just grab the delta from the first waypoint
if (labelTarget.waypoints) {
delta = PositionUtil.delta(element, labelTarget.waypoints[0]);
} else {
delta = PositionUtil.delta(element, labelTarget);
}
} else
if (element.priority === 3) {
// connections have priority 3
delta = [];
forEach(element.waypoints, function(waypoint) {
var waypointDelta = PositionUtil.delta(waypoint, bbox);
delta.push(waypointDelta);
}, this);
} else {
delta = PositionUtil.delta(element, bbox);
}
element.delta = delta;
});
});
this._eventBus.fire('elements.copy', { context: { tree: tree } });
// if tree is empty, means that nothing can be or is allowed to be copied
if (Object.keys(tree).length === 0) {
clipboard.clear();
} else {
clipboard.set(tree);
}
this._eventBus.fire('elements.copied', { context: { tree: tree } });
return tree;
};
// Allow pasting under the cursor
CopyPaste.prototype.paste = function(context) {
var clipboard = this._clipboard,
modeling = this._modeling,
eventBus = this._eventBus,
rules = this._rules;
var tree = clipboard.get(),
topParent = context.element,
position = context.point,
newTree, canPaste;
if (clipboard.isEmpty()) {
return;
}
newTree = reduce(tree, function(pasteTree, elements, depthStr) {
var depth = parseInt(depthStr, 10);
if (isNaN(depth)) {
return pasteTree;
}
pasteTree[depth] = elements;
return pasteTree;
}, {}, this);
canPaste = rules.allowed('elements.paste', {
tree: newTree,
target: topParent
});
if (!canPaste) {
eventBus.fire('elements.paste.rejected', {
context: {
tree: newTree,
position: position,
target: topParent
}
});
return;
}
modeling.pasteElements(newTree, topParent, position);
};
CopyPaste.prototype._computeDelta = function(elements, element) {
var bbox = this._bbox,
delta = {};
// set label's relative position to their label target
if (element.labelTarget) {
return PositionUtil.delta(element, element.labelTarget);
}
// connections have prority 3
if (element.priority === 3) {
delta = [];
forEach(element.waypoints, function(waypoint) {
var waypointDelta = PositionUtil.delta(waypoint, bbox);
delta.push(waypointDelta);
}, this);
} else {
delta = PositionUtil.delta(element, bbox);
}
return delta;
};
/**
* Checks if the element in question has a relations to other elements.
* Possible dependants: connections, labels, attachers
*
* @param {Array} elements
* @param {Object} element
*
* @return {Boolean}
*/
CopyPaste.prototype.hasRelations = function(elements, element) {
var source, target, labelTarget;
if (element.waypoints) {
source = find(elements, { id: element.source.id });
target = find(elements, { id: element.target.id });
if (!source || !target) {
return false;
}
}
if (element.labelTarget) {
labelTarget = find(elements, { id: element.labelTarget.id });
if (!labelTarget) {
return false;
}
}
return true;
};
CopyPaste.prototype.registerDescriptor = function(descriptor) {
if (typeof descriptor !== 'function') {
throw new Error('the descriptor must be a function');
}
if (this._descriptors.indexOf(descriptor) !== -1) {
throw new Error('this descriptor is already registered');
}
this._descriptors.push(descriptor);
};
CopyPaste.prototype._executeDescriptors = function(data) {
if (!data.descriptor) {
data.descriptor = {};
}
forEach(this._descriptors, function(descriptor) {
data.descriptor = descriptor(data.element, data.descriptor);
});
return data;
};
/**
* Creates a tree like structure from an arbitrary collection of elements
*
* @example
* tree: {
* 0: [
* { id: 'shape_12da', priority: 1, ... },
* { id: 'shape_01bj', priority: 1, ... },
* { id: 'connection_79fa', source: 'shape_12da', target: 'shape_01bj', priority: 3, ... },
* ],
* 1: [ ... ]
* };
*
* @param {Array} elements
* @return {Object}
*/
CopyPaste.prototype.createTree = function(elements) {
var rules = this._rules;
var tree = {},
includedElements = [],
_elements;
var topLevel = CopyPasteUtil.getTopLevel(elements);
tree.allShapes = [];
function canCopy(collection, element) {
return rules.allowed('element.copy', {
collection: collection,
element: element
});
}
function includeElement(data) {
var idx = findIndex(includedElements, { element: data.element }),
element;
if (idx !== -1) {
element = includedElements[idx];
} else {
return includedElements.push(data);
}
// makes sure that it has the correct depth
if (element.depth < data.depth) {
includedElements.splice(idx, 1);
includedElements.push(data);
}
}
ElementsUtil.eachElement(topLevel, function(element, i, depth) {
var nestedChildren = element.children;
// don't add labels directly
if (element.labelTarget) {
return;
}
function getNested(lists) {
forEach(lists, function(list) {
if (list && list.length) {
forEach(list, function(elem) {
// fetch element's label
if (elem.label) {
includeElement({
element: elem.label,
depth: depth
});
}
includeElement({
element: elem,
depth: depth
});
});
}
});
}
// fetch element's label
if (element.label) {
includeElement({
element: element.label,
depth: depth
});
}
getNested([ element.attachers, element.incoming, element.outgoing ]);
includeElement({
element: element,
depth: depth
});
if (nestedChildren) {
return nestedChildren;
}
});
includedElements = map(includedElements, function(data) {
// this is where other registered descriptors hook in
return this._executeDescriptors(data);
}, this);
// order the elements to check if the ones dependant on others (by relationship)
// can be copied. f.ex: label needs it's label target
includedElements = sortBy(includedElements, function(data) {
return data.descriptor.priority;
});
_elements = map(includedElements, function(data) {
return data.element;
});
forEach(includedElements, function(data) {
var depth = data.depth;
if (!this.hasRelations(tree.allShapes, data.element)) {
return;
}
if (!canCopy(_elements, data.element)) {
return;
}
tree.allShapes.push(data.element);
// create depth branches
if (!tree[depth]) {
tree[depth] = [];
}
tree[depth].push(data.descriptor);
}, this);
return tree;
};
},{"264":264,"266":266,"276":276,"290":290,"302":302,"303":303,"305":305,"306":306,"310":310,"422":422}],155:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(149),
_dereq_(227),
_dereq_(203)
],
__init__: [ 'copyPaste' ],
copyPaste: [ 'type', _dereq_(154) ]
};
},{"149":149,"154":154,"203":203,"227":227}],156:[function(_dereq_,module,exports){
'use strict';
var LOW_PRIORITY = 750;
var MARKER_OK = 'drop-ok',
MARKER_NOT_OK = 'drop-not-ok',
MARKER_ATTACH = 'attach-ok',
MARKER_NEW_PARENT = 'new-parent';
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClasses = _dereq_(475),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
var translate = _dereq_(279).translate;
function Create(eventBus, dragging, rules, modeling, canvas, styles, graphicsFactory) {
// rules
function canCreate(shape, target, source, position) {
if (source) {
return rules.allowed('shape.append', {
source: source,
shape: shape,
target: target,
position: position
});
} else {
return rules.allowed('shape.create', {
shape: shape,
target: target,
position: position
});
}
}
/** set drop marker on an element */
function setMarker(element, marker) {
[ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {
if (m === marker) {
canvas.addMarker(element, m);
} else {
canvas.removeMarker(element, m);
}
});
}
// visual helpers
function createVisual(shape) {
var group, preview, visual;
group = svgCreate('g');
svgAttr(group, styles.cls('djs-drag-group', [ 'no-events' ]));
svgAppend(canvas.getDefaultLayer(), group);
preview = svgCreate('g');
svgClasses(preview).add('djs-dragger');
svgAppend(group, preview);
translate(preview, shape.width / -2, shape.height / -2);
var visualGroup = svgCreate('g');
svgClasses(visualGroup).add('djs-visual');
svgAppend(preview, visualGroup);
visual = visualGroup;
// hijack renderer to draw preview
graphicsFactory.drawShape(visual, shape);
return group;
}
// event handlers
eventBus.on('create.move', function(event) {
var context = event.context,
hover = event.hover,
canExecute;
var position = {
x: event.x,
y: event.y
};
canExecute = context.canExecute = hover && canCreate(context.shape, hover, context.source, position);
// ignore hover visually if canExecute is null
if (hover && canExecute !== null) {
context.target = hover;
if (canExecute === 'attach') {
setMarker(hover, MARKER_ATTACH);
} else {
setMarker(hover, context.canExecute ? MARKER_NEW_PARENT : MARKER_NOT_OK);
}
}
});
eventBus.on('create.move', LOW_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
visual = context.visual;
// lazy init drag visual once we received the first real
// drag move event (this allows us to get the proper canvas local coordinates)
if (!visual) {
visual = context.visual = createVisual(shape);
}
translate(visual, event.x, event.y);
});
eventBus.on([ 'create.end', 'create.out', 'create.cleanup' ], function(event) {
var context = event.context,
target = context.target;
if (target) {
setMarker(target, null);
}
});
eventBus.on('create.end', function(event) {
var context = event.context,
source = context.source,
shape = context.shape,
target = context.target,
canExecute = context.canExecute,
isAttach,
position = {
x: event.x,
y: event.y
};
if (!canExecute) {
return false;
}
if (source) {
shape = modeling.appendShape(source, shape, position, target);
} else {
isAttach = canExecute === 'attach';
shape = modeling.createShape(shape, position, target, isAttach);
}
// make sure we provide the actual attached
// shape with the context so that selection and
// other components can use it right after the create
// operation ends
context.shape = shape;
});
eventBus.on('create.cleanup', function(event) {
var context = event.context;
if (context.visual) {
svgRemove(context.visual);
}
});
// API
this.start = function(event, shape, source) {
dragging.init(event, 'create', {
cursor: 'grabbing',
autoActivate: true,
data: {
shape: shape,
context: {
shape: shape,
source: source
}
}
});
};
}
Create.$inject = [ 'eventBus', 'dragging', 'rules', 'modeling', 'canvas', 'styles', 'graphicsFactory' ];
module.exports = Create;
},{"279":279,"472":472,"474":474,"475":475,"478":478,"481":481}],157:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(162),
_dereq_(233),
_dereq_(227)
],
create: [ 'type', _dereq_(156) ]
};
},{"156":156,"162":162,"227":227,"233":233}],158:[function(_dereq_,module,exports){
'use strict';
var sortBy = _dereq_(310),
forEach = _dereq_(303),
filter = _dereq_(301);
var AXIS_DIMENSIONS = {
horizontal: [ 'x', 'width' ],
vertical: [ 'y', 'height' ]
};
var THRESHOLD = 5;
/**
* Groups and filters elements and then trigger even distribution.
*/
function DistributeElements(modeling) {
this._modeling = modeling;
this._filters = [];
// register filter for filtering big elements
this.registerFilter(function(elements, axis, dimension) {
var elementsSize = 0,
numOfShapes = 0,
avgDimension;
forEach(elements, function(element) {
if (element.waypoints || element.labelTarget) {
return;
}
elementsSize += element[dimension];
numOfShapes += 1;
});
avgDimension = Math.round(elementsSize / numOfShapes);
return filter(elements, function(element) {
return element[dimension] < (avgDimension + 50);
});
});
}
module.exports = DistributeElements;
DistributeElements.$inject = [ 'modeling' ];
/**
* Registers filter functions that allow external parties to filter
* out certain elements.
*
* @param {Function} filterFn
*/
DistributeElements.prototype.registerFilter = function(filterFn) {
if (typeof filterFn !== 'function') {
throw new Error('the filter has to be a function');
}
this._filters.push(filterFn);
};
/**
* Distributes the elements with a given orientation
*
* @param {Array} elements [description]
* @param {String} orientation [description]
*/
DistributeElements.prototype.trigger = function(elements, orientation) {
var modeling = this._modeling;
var groups,
distributableElements;
if (elements.length < 3) {
return;
}
this._setOrientation(orientation);
distributableElements = this._filterElements(elements);
groups = this._createGroups(distributableElements);
// nothing to distribute
if (groups.length <= 2) {
return;
}
modeling.distributeElements(groups, this._axis, this._dimension);
return groups;
};
/**
* Filters the elements with provided filters by external parties
*
* @param {Array[Elements]} elements
*
* @return {Array[Elements]}
*/
DistributeElements.prototype._filterElements = function(elements) {
var filters = this._filters,
axis = this._axis,
dimension = this._dimension,
distributableElements = [].concat(elements);
if (!filters.length) {
return elements;
}
forEach(filters, function(filterFn) {
distributableElements = filterFn(distributableElements, axis, dimension);
});
return distributableElements;
};
/**
* Create range (min, max) groups. Also tries to group elements
* together that share the same range.
*
* @example
* var distributableElements = [
* {
* range: {
* min: 100,
* max: 200
* },
* elements: [ { id: 'shape1', .. }]
* }
* ]
*
* @param {Array} elements
*
* @return {Array[Objects]}
*/
DistributeElements.prototype._createGroups = function(elements) {
var rangeGroups = [],
axis = this._axis,
dimension = this._dimension;
if (!axis) {
throw new Error('must have a defined "axis" and "dimension"');
}
// sort by 'left->right' or 'top->bottom'
var sortedElements = sortBy(elements, axis);
forEach(sortedElements, function(element, idx) {
var elementRange = this._findRange(element, axis, dimension),
range;
var previous = rangeGroups[rangeGroups.length - 1];
if (previous && this._hasIntersection(previous.range, elementRange)) {
rangeGroups[rangeGroups.length - 1].elements.push(element);
} else {
range = { range: elementRange, elements: [ element ] };
rangeGroups.push(range);
}
}, this);
return rangeGroups;
};
/**
* Maps a direction to the according axis and dimension
*
* @param {String} direction 'horizontal' or 'vertical'
*/
DistributeElements.prototype._setOrientation = function(direction) {
var orientation = AXIS_DIMENSIONS[direction];
this._axis = orientation[0];
this._dimension = orientation[1];
};
/**
* Checks if the two ranges intercept each other
*
* @param {Object} rangeA {min, max}
* @param {Object} rangeB {min, max}
*
* @return {Boolean}
*/
DistributeElements.prototype._hasIntersection = function(rangeA, rangeB) {
return Math.max(rangeA.min, rangeA.max) >= Math.min(rangeB.min, rangeB.max) &&
Math.min(rangeA.min, rangeA.max) <= Math.max(rangeB.min, rangeB.max);
};
/**
* Returns the min and max values for an element
*
* @param {[type]} element [description]
* @param {[type]} axis [description]
* @param {[type]} dimension [description]
*
* @return {[type]} [description]
*/
DistributeElements.prototype._findRange = function(element) {
var axis = element[this._axis],
dimension = element[this._dimension];
return {
min: axis + THRESHOLD,
max: axis + dimension - THRESHOLD
};
};
},{"301":301,"303":303,"310":310}],159:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__init__: [ 'distributeElements' ],
distributeElements: [ 'type', _dereq_(158) ]
};
},{"158":158}],160:[function(_dereq_,module,exports){
'use strict';
/* global TouchEvent */
var round = Math.round;
var assign = _dereq_(431);
var domEvent = _dereq_(450),
Event = _dereq_(267),
ClickTrap = _dereq_(262),
Cursor = _dereq_(265);
var EventBusEvent = _dereq_(125).Event;
var DRAG_ACTIVE_CLS = 'djs-drag-active';
function suppressEvent(event) {
if (event instanceof MouseEvent) {
Event.stopEvent(event, true);
} else {
Event.preventDefault(event);
}
}
function getLength(point) {
return Math.sqrt(Math.pow(point.x, 2) + Math.pow(point.y, 2));
}
function substract(p1, p2) {
return {
x: p1.x - p2.x,
y: p1.y - p2.y
};
}
/**
* A helper that fires canvas localized drag events and realizes
* the general "drag-and-drop" look and feel.
*
* Calling {@link Dragging#activate} activates dragging on a canvas.
*
* It provides the following:
*
* * emits life cycle events, namespaced with a prefix assigned
* during dragging activation
* * sets and restores the cursor
* * sets and restores the selection
* * ensures there can be only one drag operation active at a time
*
* Dragging may be canceled manually by calling {@link Dragging#cancel}
* or by pressing ESC.
*
*
* ## Life-cycle events
*
* Dragging can be in three different states, off, initialized
* and active.
*
* (1) off: no dragging operation is in progress
* (2) initialized: a new drag operation got initialized but not yet
* started (i.e. because of no initial move)
* (3) started: dragging is in progress
*
* Eventually dragging will be off again after a drag operation has
* been ended or canceled via user click or ESC key press.
*
* To indicate transitions between these states dragging emits generic
* life-cycle events with the `drag.` prefix _and_ events namespaced
* to a prefix choosen by a user during drag initialization.
*
* The following events are emitted (appropriately prefixed) via
* the {@link EventBus}.
*
* * `init`
* * `start`
* * `move`
* * `end`
* * `ended` (dragging already in off state)
* * `cancel` (only if previously started)
* * `canceled` (dragging already in off state, only if previously started)
* * `cleanup`
*
*
* @example
*
* function MyDragComponent(eventBus, dragging) {
*
* eventBus.on('mydrag.start', function(event) {
* console.log('yes, we start dragging');
* });
*
* eventBus.on('mydrag.move', function(event) {
* console.log('canvas local coordinates', event.x, event.y, event.dx, event.dy);
*
* // local drag data is passed with the event
* event.context.foo; // "BAR"
*
* // the original mouse event, too
* event.originalEvent; // MouseEvent(...)
* });
*
* eventBus.on('element.click', function(event) {
* dragging.init(event, 'mydrag', {
* cursor: 'grabbing',
* data: {
* context: {
* foo: "BAR"
* }
* }
* });
* });
* }
*/
function Dragging(eventBus, canvas, selection) {
var defaultOptions = {
threshold: 5,
trapClick: true
};
// the currently active drag operation
// dragging is active as soon as this context exists.
//
// it is visually _active_ only when a context.active flag is set to true.
var context;
/* convert a global event into local coordinates */
function toLocalPoint(globalPosition) {
var viewbox = canvas.viewbox();
var clientRect = canvas._container.getBoundingClientRect();
return {
x: viewbox.x + round((globalPosition.x - clientRect.left) / viewbox.scale),
y: viewbox.y + round((globalPosition.y - clientRect.top) / viewbox.scale)
};
}
// helpers
function fire(type, dragContext) {
dragContext = dragContext || context;
var event = assign(new EventBusEvent(), dragContext.payload, dragContext.data);
// default integration
if (eventBus.fire('drag.' + type, event) === false) {
return false;
}
return eventBus.fire(dragContext.prefix + '.' + type, event);
}
// event listeners
function move(event, activate) {
var payload = context.payload,
displacement = context.displacement;
var globalStart = context.globalStart,
globalCurrent = Event.toPoint(event),
globalDelta = substract(globalCurrent, globalStart);
var localStart = context.localStart,
localCurrent = toLocalPoint(globalCurrent),
localDelta = substract(localCurrent, localStart);
// activate context explicitly or once threshold is reached
if (!context.active && (activate || getLength(globalDelta) > context.threshold)) {
// fire start event with original
// starting coordinates
assign(payload, {
x: localStart.x + displacement.x,
y: localStart.y + displacement.y,
dx: 0,
dy: 0
}, { originalEvent: event });
if (false === fire('start')) {
return cancel();
}
context.active = true;
// unset selection and remember old selection
// the previous (old) selection will always passed
// with the event via the event.previousSelection property
if (!context.keepSelection) {
payload.previousSelection = selection.get();
selection.select(null);
}
// allow custom cursor
if (context.cursor) {
Cursor.set(context.cursor);
}
// indicate dragging via marker on root element
canvas.addMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
}
suppressEvent(event);
if (context.active) {
// update payload with actual coordinates
assign(payload, {
x: localCurrent.x + displacement.x,
y: localCurrent.y + displacement.y,
dx: localDelta.x,
dy: localDelta.y
}, { originalEvent: event });
// emit move event
fire('move');
}
}
function end(event) {
var previousContext,
returnValue = true;
if (context.active) {
if (event) {
context.payload.originalEvent = event;
// suppress original event (click, ...)
// because we just ended a drag operation
suppressEvent(event);
}
// implementations may stop restoring the
// original state (selections, ...) by preventing the
// end events default action
returnValue = fire('end');
}
if (returnValue === false) {
fire('rejected');
}
previousContext = cleanup(returnValue !== true);
// last event to be fired when all drag operations are done
// at this point in time no drag operation is in progress anymore
fire('ended', previousContext);
}
// cancel active drag operation if the user presses
// the ESC key on the keyboard
function checkCancel(event) {
if (event.which === 27) {
event.preventDefault();
cancel();
}
}
// prevent ghost click that might occur after a finished
// drag and drop session
function trapClickAndEnd(event) {
var untrap;
// trap the click in case we are part of an active
// drag operation. This will effectively prevent
// the ghost click that cannot be canceled otherwise.
if (context.active) {
untrap = ClickTrap.install();
setTimeout(untrap, 400);
}
end(event);
}
function trapTouch(event) {
move(event);
}
// update the drag events hover (djs.model.Base) and hoverGfx (Snap)
// properties during hover and out and fire {prefix}.hover and {prefix}.out properties
// respectively
function hover(event) {
var payload = context.payload;
payload.hoverGfx = event.gfx;
payload.hover = event.element;
fire('hover');
}
function out(event) {
fire('out');
var payload = context.payload;
payload.hoverGfx = null;
payload.hover = null;
}
// life-cycle methods
function cancel(restore) {
var previousContext;
if (!context) {
return;
}
var wasActive = context.active;
if (wasActive) {
fire('cancel');
}
previousContext = cleanup(restore);
if (wasActive) {
// last event to be fired when all drag operations are done
// at this point in time no drag operation is in progress anymore
fire('canceled', previousContext);
}
}
function cleanup(restore) {
var previousContext,
endDrag;
fire('cleanup');
// reset cursor
Cursor.unset();
if (context.trapClick) {
endDrag = trapClickAndEnd;
} else {
endDrag = end;
}
// reset dom listeners
domEvent.unbind(document, 'mousemove', move);
domEvent.unbind(document, 'mousedown', endDrag, true);
domEvent.unbind(document, 'mouseup', endDrag, true);
domEvent.unbind(document, 'keyup', checkCancel);
domEvent.unbind(document, 'touchstart', trapTouch, true);
domEvent.unbind(document, 'touchcancel', cancel, true);
domEvent.unbind(document, 'touchmove', move, true);
domEvent.unbind(document, 'touchend', end, true);
eventBus.off('element.hover', hover);
eventBus.off('element.out', out);
// remove drag marker on root element
canvas.removeMarker(canvas.getRootElement(), DRAG_ACTIVE_CLS);
// restore selection, unless it has changed
var previousSelection = context.payload.previousSelection;
if (restore !== false && previousSelection && !selection.get().length) {
selection.select(previousSelection);
}
previousContext = context;
context = null;
return previousContext;
}
/**
* Initialize a drag operation.
*
* If `localPosition` is given, drag events will be emitted
* relative to it.
*
* @param {MouseEvent|TouchEvent} [event]
* @param {Point} [localPosition] actual diagram local position this drag operation should start at
* @param {String} prefix
* @param {Object} [options]
*/
function init(event, relativeTo, prefix, options) {
// only one drag operation may be active, at a time
if (context) {
cancel(false);
}
if (typeof relativeTo === 'string') {
options = prefix;
prefix = relativeTo;
relativeTo = null;
}
options = assign({}, defaultOptions, options || {});
var data = options.data || {},
originalEvent,
globalStart,
endDrag;
if (options.trapClick) {
endDrag = trapClickAndEnd;
} else {
endDrag = end;
}
if (event) {
originalEvent = Event.getOriginal(event) || event;
globalStart = Event.toPoint(event);
suppressEvent(event);
} else {
originalEvent = null;
globalStart = { x: 0, y: 0 };
}
var localStart = toLocalPoint(globalStart);
if (!relativeTo) {
relativeTo = localStart;
}
context = assign({
prefix: prefix,
data: data,
payload: {},
globalStart: globalStart,
displacement: substract(relativeTo, localStart),
localStart: localStart
}, options);
// skip dom registration if trigger
// is set to manual (during testing)
if (!options.manual) {
// add dom listeners
// fixes TouchEvent not being available on desktop Firefox
if (typeof TouchEvent !== 'undefined' && originalEvent instanceof TouchEvent) {
domEvent.bind(document, 'touchstart', trapTouch, true);
domEvent.bind(document, 'touchcancel', cancel, true);
domEvent.bind(document, 'touchmove', move, true);
domEvent.bind(document, 'touchend', end, true);
} else {
// assume we use the mouse to interact per default
domEvent.bind(document, 'mousemove', move);
domEvent.bind(document, 'mousedown', endDrag, true);
domEvent.bind(document, 'mouseup', endDrag, true);
}
domEvent.bind(document, 'keyup', checkCancel);
eventBus.on('element.hover', hover);
eventBus.on('element.out', out);
}
fire('init');
if (options.autoActivate) {
move(event, true);
}
}
// cancel on diagram destruction
eventBus.on('diagram.destroy', cancel);
// API
this.init = init;
this.move = move;
this.hover = hover;
this.out = out;
this.end = end;
this.cancel = cancel;
// for introspection
this.context = function() {
return context;
};
this.setOptions = function(options) {
assign(defaultOptions, options);
};
}
Dragging.$inject = [ 'eventBus', 'canvas', 'selection' ];
module.exports = Dragging;
},{"125":125,"262":262,"265":265,"267":267,"431":431,"450":450}],161:[function(_dereq_,module,exports){
'use strict';
var domClosest = _dereq_(447);
var Event = _dereq_(267);
function getGfx(target) {
var node = domClosest(target, 'svg, .djs-element', true);
return node;
}
/**
* Browsers may swallow the hover event if users are to
* fast with the mouse.
*
* @see http://stackoverflow.com/questions/7448468/why-cant-i-reliably-capture-a-mouseout-event
*
* The fix implemented in this component ensure that we
* have a hover state after a successive drag.move event.
*
* @param {EventBus} eventBus
* @param {Dragging} dragging
* @param {ElementRegistry} elementRegistry
*/
function HoverFix(eventBus, dragging, elementRegistry) {
var self = this;
// we wait for a specific sequence of events before
// emitting a fake drag.hover event.
//
// Event Sequence:
//
// drag.start
// drag.move
// drag.move >> ensure we are hovering
//
eventBus.on('drag.start', function(event) {
eventBus.once('drag.move', function() {
eventBus.once('drag.move', function(event) {
self.ensureHover(event);
});
});
});
/**
* Make sure we are god damn hovering!
*
* @param {Event} dragging event
*/
this.ensureHover = function(event) {
if (event.hover) {
return;
}
var originalEvent = event.originalEvent,
position,
target,
element,
gfx;
if (!(originalEvent instanceof MouseEvent)) {
return;
}
position = Event.toPoint(originalEvent);
// damn expensive operation, ouch!
target = document.elementFromPoint(position.x, position.y);
gfx = getGfx(target);
if (gfx) {
element = elementRegistry.get(gfx);
dragging.hover({ element: element, gfx: gfx });
}
};
}
HoverFix.$inject = [ 'eventBus', 'dragging', 'elementRegistry' ];
module.exports = HoverFix;
},{"267":267,"447":447}],162:[function(_dereq_,module,exports){
module.exports = {
__init__: [
'hoverFix'
],
__depends__: [
_dereq_(233)
],
dragging: [ 'type', _dereq_(160) ],
hoverFix: [ 'type', _dereq_(161) ]
};
},{"160":160,"161":161,"233":233}],163:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
isArray = _dereq_(422);
var NOT_REGISTERED_ERROR = 'is not a registered action',
IS_REGISTERED_ERROR = 'is already registered';
/**
* An interface that provides access to modeling actions by decoupling
* the one who requests the action to be triggered and the trigger itself.
*
* It's possible to add new actions by registering them with ´registerAction´ and likewise
* unregister existing ones with ´unregisterAction´.
*
*/
function EditorActions(eventBus, commandStack, modeling, selection,
zoomScroll, copyPaste, canvas, rules, mouseTracking) {
this._actions = {
undo: function() {
commandStack.undo();
},
redo: function() {
commandStack.redo();
},
copy: function() {
var selectedElements = selection.get();
copyPaste.copy(selectedElements);
},
paste: function() {
var context = mouseTracking.getHoverContext();
copyPaste.paste(context);
},
stepZoom: function(opts) {
zoomScroll.stepZoom(opts.value);
},
zoom: function(opts) {
canvas.zoom(opts.value);
},
removeSelection: function() {
var selectedElements = selection.get();
if (selectedElements.length) {
var allowed = rules.allowed('elements.delete', { elements: selectedElements }),
removableElements;
if (allowed === false) {
return;
}
else if (isArray(allowed)) {
removableElements = allowed;
}
else {
removableElements = selectedElements;
}
if (removableElements.length) {
modeling.removeElements(removableElements.slice());
}
}
},
moveCanvas: function(opts) {
var dx = 0,
dy = 0,
invertY = opts.invertY,
speed = opts.speed;
var actualSpeed = speed / Math.min(Math.sqrt(canvas.viewbox().scale), 1);
switch (opts.direction) {
case 'left': // Left
dx = actualSpeed;
break;
case 'up': // Up
dy = actualSpeed;
break;
case 'right': // Right
dx = -actualSpeed;
break;
case 'down': // Down
dy = -actualSpeed;
break;
}
if (dy && invertY) {
dy = -dy;
}
canvas.scroll({ dx: dx, dy: dy });
}
};
}
EditorActions.$inject = [
'eventBus',
'commandStack',
'modeling',
'selection',
'zoomScroll',
'copyPaste',
'canvas',
'rules',
'mouseTracking'
];
module.exports = EditorActions;
/**
* Triggers a registered action
*
* @param {String} action
* @param {Object} opts
*
* @return {Unknown} Returns what the registered listener returns
*/
EditorActions.prototype.trigger = function(action, opts) {
if (!this._actions[action]) {
throw error(action, NOT_REGISTERED_ERROR);
}
return this._actions[action](opts);
};
/**
* Registers a collections of actions.
* The key of the object will be the name of the action.
*
* @example
* ´´´
* var actions = {
* spaceTool: function() {
* spaceTool.activateSelection();
* },
* lassoTool: function() {
* lassoTool.activateSelection();
* }
* ];
*
* editorActions.register(actions);
*
* editorActions.isRegistered('spaceTool'); // true
* ´´´
*
* @param {Object} actions
*/
EditorActions.prototype.register = function(actions, listener) {
if (typeof actions === 'string') {
return this._registerAction(actions, listener);
}
forEach(actions, function(listener, action) {
this._registerAction(action, listener);
}, this);
};
/**
* Registers a listener to an action key
*
* @param {String} action
* @param {Function} listener
*/
EditorActions.prototype._registerAction = function(action, listener) {
if (this.isRegistered(action)) {
throw error(action, IS_REGISTERED_ERROR);
}
this._actions[action] = listener;
};
/**
* Unregister an existing action
*
* @param {String} action
*/
EditorActions.prototype.unregister = function(action) {
if (!this.isRegistered(action)) {
throw error(action, NOT_REGISTERED_ERROR);
}
this._actions[action] = undefined;
};
/**
* Returns the number of actions that are currently registered
*
* @return {Number}
*/
EditorActions.prototype.length = function() {
return Object.keys(this._actions).length;
};
/**
* Checks wether the given action is registered
*
* @param {String} action
*
* @return {Boolean}
*/
EditorActions.prototype.isRegistered = function(action) {
return !!this._actions[action];
};
function error(action, message) {
return new Error(action + ' ' + message);
}
},{"303":303,"422":422}],164:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(233),
_dereq_(155),
_dereq_(260)
],
__init__: [ 'editorActions' ],
editorActions: [ 'type', _dereq_(163) ]
};
},{"155":155,"163":163,"233":233,"260":260}],165:[function(_dereq_,module,exports){
'use strict';
var MARKER_OK = 'connect-ok',
MARKER_NOT_OK = 'connect-not-ok';
function GlobalConnect(eventBus, dragging, connect, canvas, toolManager) {
var self = this;
this._dragging = dragging;
toolManager.registerTool('global-connect', {
tool: 'global-connect',
dragging: 'global-connect.drag'
});
eventBus.on('global-connect.hover', function(event) {
var context = event.context,
startTarget = event.hover;
var canStartConnect = context.canStartConnect = self.canStartConnect(startTarget);
// simply ignore hover
if (canStartConnect === null) {
return;
}
context.startTarget = startTarget;
canvas.addMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
});
eventBus.on([ 'global-connect.out', 'global-connect.cleanup' ], function(event) {
var startTarget = event.context.startTarget,
canStartConnect = event.context.canStartConnect;
if (startTarget) {
canvas.removeMarker(startTarget, canStartConnect ? MARKER_OK : MARKER_NOT_OK);
}
});
eventBus.on([ 'global-connect.ended' ], function(event) {
var context = event.context,
startTarget = context.startTarget,
startPosition = {
x: event.x,
y: event.y
};
var canStartConnect = self.canStartConnect(startTarget);
if (!canStartConnect) {
return;
}
eventBus.once('element.out', function() {
eventBus.once([ 'connect.ended', 'connect.canceled' ], function() {
eventBus.fire('global-connect.drag.ended');
});
connect.start(null, startTarget, startPosition);
});
return false;
});
}
GlobalConnect.$inject = [ 'eventBus', 'dragging', 'connect', 'canvas', 'toolManager' ];
module.exports = GlobalConnect;
/**
* Initiates tool activity.
*/
GlobalConnect.prototype.start = function(event) {
this._dragging.init(event, 'global-connect', {
trapClick: false,
data: {
context: {}
}
});
};
GlobalConnect.prototype.toggle = function() {
if (this.isActive()) {
this._dragging.cancel();
} else {
this.start();
}
};
GlobalConnect.prototype.isActive = function() {
var context = this._dragging.context();
return context && /^global-connect/.test(context.prefix);
};
GlobalConnect.prototype.registerProvider = function(provider) {
this._provider = provider;
};
/**
* Check if source shape can initiate connection.
*
* @param {Shape} startTarget
* @return {Boolean}
*/
GlobalConnect.prototype.canStartConnect = function(startTarget) {
return this._provider.canStartConnect(startTarget);
};
},{}],166:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(151),
_dereq_(227),
_dereq_(162),
_dereq_(242)
],
globalConnect: [ 'type', _dereq_(165) ]
};
},{"151":151,"162":162,"165":165,"227":227,"242":242}],167:[function(_dereq_,module,exports){
'use strict';
var hasPrimaryModifier = _dereq_(274).hasPrimaryModifier;
var HIGH_PRIORITY = 1500;
var HAND_CURSOR = 'grab';
function HandTool(eventBus, canvas, dragging, toolManager) {
this._dragging = dragging;
toolManager.registerTool('hand', {
tool: 'hand',
dragging: 'hand.move'
});
eventBus.on('element.mousedown', HIGH_PRIORITY, function(event) {
if (hasPrimaryModifier(event)) {
this.activateMove(event.originalEvent);
return false;
}
}, this);
eventBus.on('hand.end', function(event) {
var target = event.originalEvent.target;
// only reactive on diagram click
// on some occasions, event.hover is not set and we have to check if the target is an svg
if (!event.hover && !(target instanceof SVGElement)) {
return false;
}
eventBus.once('hand.ended', function() {
this.activateMove(event.originalEvent, { reactivate: true });
}, this);
}, this);
eventBus.on('hand.move.move', function(event) {
var scale = canvas.viewbox().scale;
canvas.scroll({
dx: event.dx * scale,
dy: event.dy * scale
});
});
eventBus.on('hand.move.end', function(event) {
var context = event.context,
reactivate = context.reactivate;
// Don't reactivate if the user is using the keyboard keybinding
if (!hasPrimaryModifier(event) && reactivate) {
eventBus.once('hand.move.ended', function(event) {
this.activateHand(event.originalEvent, true, true);
}, this);
}
return false;
}, this);
}
HandTool.$inject = [
'eventBus',
'canvas',
'dragging',
'toolManager'
];
module.exports = HandTool;
HandTool.prototype.activateMove = function(event, autoActivate, context) {
if (typeof autoActivate === 'object') {
context = autoActivate;
autoActivate = false;
}
this._dragging.init(event, 'hand.move', {
autoActivate: autoActivate,
cursor: HAND_CURSOR,
data: {
context: context || {}
}
});
};
HandTool.prototype.activateHand = function(event, autoActivate, reactivate) {
this._dragging.init(event, 'hand', {
trapClick: false,
autoActivate: autoActivate,
cursor: HAND_CURSOR,
data: {
context: {
reactivate: reactivate
}
}
});
};
HandTool.prototype.toggle = function() {
if (this.isActive()) {
this._dragging.cancel();
} else {
this.activateHand();
}
};
HandTool.prototype.isActive = function() {
var context = this._dragging.context();
return context && /^hand/.test(context.prefix);
};
},{"274":274}],168:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__depends__: [ _dereq_(242) ],
__init__: [ 'handTool' ],
handTool: [ 'type', _dereq_(167) ]
};
},{"167":167,"242":242}],169:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
domDelegate = _dereq_(448);
var isPrimaryButton = _dereq_(274).isPrimaryButton;
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478);
var domQuery = _dereq_(452);
var renderUtil = _dereq_(278);
var createLine = renderUtil.createLine,
updateLine = renderUtil.updateLine;
var LOW_PRIORITY = 500;
/**
* A plugin that provides interaction events for diagram elements.
*
* It emits the following events:
*
* * element.hover
* * element.out
* * element.click
* * element.dblclick
* * element.mousedown
*
* Each event is a tuple { element, gfx, originalEvent }.
*
* Canceling the event via Event#preventDefault() prevents the original DOM operation.
*
* @param {EventBus} eventBus
*/
function InteractionEvents(eventBus, elementRegistry, styles) {
var HIT_STYLE = styles.cls('djs-hit', [ 'no-fill', 'no-border' ], {
stroke: 'white',
strokeWidth: 15
});
/**
* Fire an interaction event.
*
* @param {String} type local event name, e.g. element.click.
* @param {DOMEvent} event native event
* @param {djs.model.Base} [element] the diagram element to emit the event on;
* defaults to the event target
*/
function fire(type, event, element) {
// only react on left mouse button interactions
// for interaction events
if (!isPrimaryButton(event)) {
return;
}
var target, gfx, returnValue;
if (!element) {
target = event.delegateTarget || event.target;
if (target) {
gfx = target;
element = elementRegistry.get(gfx);
}
} else {
gfx = elementRegistry.getGraphics(element);
}
if (!gfx || !element) {
return;
}
returnValue = eventBus.fire(type, { element: element, gfx: gfx, originalEvent: event });
if (returnValue === false) {
event.stopPropagation();
event.preventDefault();
}
}
// TODO(nikku): document this
var handlers = {};
function mouseHandler(type) {
var fn = handlers[type];
if (!fn) {
fn = handlers[type] = function(event) {
fire(type, event);
};
}
return fn;
}
var bindings = {
mouseover: 'element.hover',
mouseout: 'element.out',
click: 'element.click',
dblclick: 'element.dblclick',
mousedown: 'element.mousedown',
mouseup: 'element.mouseup'
};
///// manual event trigger
/**
* Trigger an interaction event (based on a native dom event)
* on the target shape or connection.
*
* @param {String} eventName the name of the triggered DOM event
* @param {MouseEvent} event
* @param {djs.model.Base} targetElement
*/
function triggerMouseEvent(eventName, event, targetElement) {
// i.e. element.mousedown...
var localEventName = bindings[eventName];
if (!localEventName) {
throw new Error('unmapped DOM event name <' + eventName + '>');
}
return fire(localEventName, event, targetElement);
}
var elementSelector = 'svg, .djs-element';
///// event registration
function registerEvent(node, event, localEvent) {
var handler = mouseHandler(localEvent);
handler.$delegate = domDelegate.bind(node, elementSelector, event, handler);
}
function unregisterEvent(node, event, localEvent) {
domDelegate.unbind(node, event, mouseHandler(localEvent).$delegate);
}
function registerEvents(svg) {
forEach(bindings, function(val, key) {
registerEvent(svg, key, val);
});
}
function unregisterEvents(svg) {
forEach(bindings, function(val, key) {
unregisterEvent(svg, key, val);
});
}
eventBus.on('canvas.destroy', function(event) {
unregisterEvents(event.svg);
});
eventBus.on('canvas.init', function(event) {
registerEvents(event.svg);
});
eventBus.on([ 'shape.added', 'connection.added' ], function(event) {
var element = event.element,
gfx = event.gfx,
hit;
if (element.waypoints) {
hit = createLine(element.waypoints);
} else {
hit = svgCreate('rect');
svgAttr(hit, {
x: 0,
y: 0,
width: element.width,
height: element.height
});
}
svgAttr(hit, HIT_STYLE);
svgAppend(gfx, hit);
});
// Update djs-hit on change.
// A low priortity is necessary, because djs-hit of labels has to be updated
// after the label bounds have been updated in the renderer.
eventBus.on('shape.changed', LOW_PRIORITY, function(event) {
var element = event.element,
gfx = event.gfx,
hit = domQuery('.djs-hit', gfx);
svgAttr(hit, {
width: element.width,
height: element.height
});
});
eventBus.on('connection.changed', function(event) {
var element = event.element,
gfx = event.gfx,
hit = domQuery('.djs-hit', gfx);
updateLine(hit, element.waypoints);
});
// API
this.fire = fire;
this.triggerMouseEvent = triggerMouseEvent;
this.mouseHandler = mouseHandler;
this.registerEvent = registerEvent;
this.unregisterEvent = unregisterEvent;
}
InteractionEvents.$inject = [ 'eventBus', 'elementRegistry', 'styles' ];
module.exports = InteractionEvents;
/**
* An event indicating that the mouse hovered over an element
*
* @event element.hover
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
/**
* An event indicating that the mouse has left an element
*
* @event element.out
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
/**
* An event indicating that the mouse has clicked an element
*
* @event element.click
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
/**
* An event indicating that the mouse has double clicked an element
*
* @event element.dblclick
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
/**
* An event indicating that the mouse has gone down on an element.
*
* @event element.mousedown
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
/**
* An event indicating that the mouse has gone up on an element.
*
* @event element.mouseup
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
},{"274":274,"278":278,"303":303,"448":448,"452":452,"472":472,"474":474,"478":478}],170:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'interactionEvents' ],
interactionEvents: [ 'type', _dereq_(169) ]
};
},{"169":169}],171:[function(_dereq_,module,exports){
'use strict';
var domEvent = _dereq_(450),
domMatches = _dereq_(451);
/**
* A keyboard abstraction that may be activated and
* deactivated by users at will, consuming key events
* and triggering diagram actions.
*
* The implementation fires the following key events that allow
* other components to hook into key handling:
*
* - keyboard.bind
* - keyboard.unbind
* - keyboard.init
* - keyboard.destroy
*
* All events contain the fields (node, listeners).
*
* A default binding for the keyboard may be specified via the
* `keyboard.bindTo` configuration option.
*
* @param {Config} config
* @param {EventBus} eventBus
* @param {EditorActions} editorActions
*/
function Keyboard(config, eventBus, editorActions) {
var self = this;
this._config = config || {};
this._eventBus = eventBus;
this._editorActions = editorActions;
this._listeners = [];
// our key handler is a singleton that passes
// (keycode, modifiers) to each listener.
//
// listeners must indicate that they handled a key event
// by returning true. This stops the event propagation.
//
this._keyHandler = function(event) {
var i, l,
target = event.target,
listeners = self._listeners,
code = event.keyCode || event.charCode || -1;
if (target && (domMatches(target, 'input, textarea') || target.contentEditable === 'true')) {
return;
}
for (i = 0; (l = listeners[i]); i++) {
if (l(code, event)) {
event.preventDefault();
event.stopPropagation();
}
}
};
// properly clean dom registrations
eventBus.on('diagram.destroy', function() {
self._fire('destroy');
self.unbind();
self._listeners = null;
});
eventBus.on('diagram.init', function() {
self._fire('init');
if (config && config.bindTo) {
self.bind(config.bindTo);
}
});
this._init();
}
Keyboard.$inject = [
'config.keyboard',
'eventBus',
'editorActions'
];
module.exports = Keyboard;
Keyboard.prototype.bind = function(node) {
// make sure that the keyboard is only bound once to the DOM
this.unbind();
this._node = node;
// bind key events
domEvent.bind(node, 'keydown', this._keyHandler, true);
this._fire('bind');
};
Keyboard.prototype.getBinding = function() {
return this._node;
};
Keyboard.prototype.unbind = function() {
var node = this._node;
if (node) {
this._fire('unbind');
// unbind key events
domEvent.unbind(node, 'keydown', this._keyHandler, true);
}
this._node = null;
};
Keyboard.prototype._fire = function(event) {
this._eventBus.fire('keyboard.' + event, { node: this._node, listeners: this._listeners });
};
Keyboard.prototype._init = function() {
var listeners = this._listeners;
var editorActions = this._editorActions,
config = this._config;
// init default listeners
// undo
// (CTRL|CMD) + Z
function undo(key, modifiers) {
if (isCmd(modifiers) && !isShift(modifiers) && key === 90) {
editorActions.trigger('undo');
return true;
}
}
// redo
// CTRL + Y
// CMD + SHIFT + Z
function redo(key, modifiers) {
if (isCmd(modifiers) && (key === 89 || (key === 90 && isShift(modifiers)))) {
editorActions.trigger('redo');
return true;
}
}
// copy
// CTRL/CMD + C
function copy(key, modifiers) {
if (isCmd(modifiers) && (key === 67)) {
editorActions.trigger('copy');
return true;
}
}
// paste
// CTRL/CMD + V
function paste(key, modifiers) {
if (isCmd(modifiers) && (key === 86)) {
editorActions.trigger('paste');
return true;
}
}
/**
* zoom in one step
* CTRL + +
*
* 107 = numpad plus
* 187 = regular plus
* 171 = regular plus in Firefox (german keyboard layout)
* 61 = regular plus in Firefox (US keyboard layout)
*/
function zoomIn(key, modifiers) {
if ((key === 107 || key === 187 || key === 171 || key === 61) && isCmd(modifiers)) {
editorActions.trigger('stepZoom', { value: 1 });
return true;
}
}
/**
* zoom out one step
* CTRL + -
*
* 109 = numpad minus
* 189 = regular minus
* 173 = regular minus in Firefox (US and german keyboard layout)
*/
function zoomOut(key, modifiers) {
if ((key === 109 || key === 189 || key === 173) && isCmd(modifiers)) {
editorActions.trigger('stepZoom', { value: -1 });
return true;
}
}
/**
* zoom to the default level
* CTRL + 0
*
* 96 = numpad zero
* 48 = regular zero
*/
function zoomDefault(key, modifiers) {
if ((key === 96 || key === 48) && isCmd(modifiers)) {
editorActions.trigger('zoom', { value: 1 });
return true;
}
}
// delete selected element
// DEL
function removeSelection(key, modifiers) {
if (key === 46) {
editorActions.trigger('removeSelection');
return true;
}
}
// move canvas left
// left arrow
//
// 37 = Left
// 38 = Up
// 39 = Right
// 40 = Down
function moveCanvas(key, modifiers) {
if ([37, 38, 39, 40].indexOf(key) >= 0) {
var opts = {
invertY: config.invertY,
speed: (config.speed || 50)
};
switch (key) {
case 37: // Left
opts.direction = 'left';
break;
case 38: // Up
opts.direction = 'up';
break;
case 39: // Right
opts.direction = 'right';
break;
case 40: // Down
opts.direction = 'down';
break;
}
editorActions.trigger('moveCanvas', opts);
return true;
}
}
listeners.push(undo);
listeners.push(redo);
listeners.push(copy);
listeners.push(paste);
listeners.push(removeSelection);
listeners.push(zoomIn);
listeners.push(zoomOut);
listeners.push(zoomDefault);
listeners.push(moveCanvas);
};
/**
* Add a listener function that is notified with (key, modifiers) whenever
* the keyboard is bound and the user presses a key.
*
* @param {Function} listenerFn
*/
Keyboard.prototype.addListener = function(listenerFn) {
this._listeners.push(listenerFn);
};
Keyboard.prototype.hasModifier = hasModifier;
Keyboard.prototype.isCmd = isCmd;
Keyboard.prototype.isShift = isShift;
function hasModifier(modifiers) {
return (modifiers.ctrlKey || modifiers.metaKey || modifiers.shiftKey || modifiers.altKey);
}
function isCmd(modifiers) {
return modifiers.ctrlKey || modifiers.metaKey;
}
function isShift(modifiers) {
return modifiers.shiftKey;
}
},{"450":450,"451":451}],172:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'keyboard' ],
keyboard: [ 'type', _dereq_(171) ]
};
},{"171":171}],173:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
filter = _dereq_(301),
inherits = _dereq_(289);
var LOW_PRIORITY = 250,
HIGH_PRIORITY = 1400;
var CommandInterceptor = _dereq_(119);
/**
* A handler that makes sure labels are properly moved with
* their label targets.
*/
function LabelSupport(eventBus, modeling, movePreview) {
CommandInterceptor.call(this, eventBus);
// remove labels from the collection that are being
// moved with other elements anyway
eventBus.on('shape.move.start', HIGH_PRIORITY, function(e) {
var context = e.context,
shapes = context.shapes,
validatedShapes = context.validatedShapes;
context.shapes = removeLabels(shapes);
context.validatedShapes = removeLabels(validatedShapes);
});
// add labels to visual's group
eventBus.on('shape.move.start', LOW_PRIORITY, function(e) {
var context = e.context,
shapes = context.shapes;
var labels = [];
forEach(shapes, function(element) {
var label = element.label;
if (label && !label.hidden && context.shapes.indexOf(label) === -1) {
labels.push(label);
}
if (element.labelTarget) {
labels.push(element);
}
});
forEach(labels, function(label) {
movePreview.makeDraggable(context, label, true);
});
});
// move labels after the other shapes are done moving
this.postExecuted([ 'elements.move' ], function(e) {
var context = e.context,
closure = context.closure,
enclosedElements = closure.enclosedElements;
// ensure we move all labels with their respective elements
// if they have not been moved already
forEach(enclosedElements, function(element) {
if (element.label && !enclosedElements[element.label.id]) {
modeling.moveShape(element.label, context.delta, element.parent);
}
});
});
}
inherits(LabelSupport, CommandInterceptor);
LabelSupport.$inject = [ 'eventBus', 'modeling', 'movePreview' ];
module.exports = LabelSupport;
/**
* Return a filtered list of elements that do not
* contain attached elements with hosts being part
* of the selection.
*
* @param {Array} elements
*
* @return {Array} filtered
*/
function removeLabels(elements) {
return filter(elements, function(element) {
// filter out labels that are move together
// with their label targets
return elements.indexOf(element.labelTarget) === -1;
});
}
},{"119":119,"289":289,"301":301,"303":303}],174:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(206)
],
__init__: [ 'labelSupport'],
labelSupport: [ 'type', _dereq_(173) ]
};
},{"173":173,"206":206}],175:[function(_dereq_,module,exports){
'use strict';
var values = _dereq_(440);
var getEnclosedElements = _dereq_(266).getEnclosedElements;
var hasSecondaryModifier = _dereq_(274).hasSecondaryModifier;
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478),
svgRemove = _dereq_(481);
var LASSO_TOOL_CURSOR = 'crosshair';
function LassoTool(eventBus, canvas, dragging, elementRegistry, selection, toolManager) {
this._selection = selection;
this._dragging = dragging;
var self = this;
// lasso visuals implementation
/**
* A helper that realizes the selection box visual
*/
var visuals = {
create: function(context) {
var container = canvas.getDefaultLayer(),
frame;
frame = context.frame = svgCreate('rect');
svgAttr(frame, {
class: 'djs-lasso-overlay',
width: 1,
height: 1,
x: 0,
y: 0
});
svgAppend(container, frame);
},
update: function(context) {
var frame = context.frame,
bbox = context.bbox;
svgAttr(frame, {
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height
});
},
remove: function(context) {
if (context.frame) {
svgRemove(context.frame);
}
}
};
toolManager.registerTool('lasso', {
tool: 'lasso.selection',
dragging: 'lasso'
});
eventBus.on('lasso.selection.end', function(event) {
var target = event.originalEvent.target;
// only reactive on diagram click
// on some occasions, event.hover is not set and we have to check if the target is an svg
if (!event.hover && !(target instanceof SVGElement)) {
return;
}
eventBus.once('lasso.selection.ended', function() {
self.activateLasso(event.originalEvent, true);
});
});
// lasso interaction implementation
eventBus.on('lasso.end', function(event) {
var bbox = toBBox(event);
var elements = elementRegistry.filter(function(element) {
return element;
});
self.select(elements, bbox);
});
eventBus.on('lasso.start', function(event) {
var context = event.context;
context.bbox = toBBox(event);
visuals.create(context);
});
eventBus.on('lasso.move', function(event) {
var context = event.context;
context.bbox = toBBox(event);
visuals.update(context);
});
eventBus.on('lasso.cleanup', function(event) {
var context = event.context;
visuals.remove(context);
});
// event integration
eventBus.on('element.mousedown', 1500, function(event) {
if (hasSecondaryModifier(event)) {
self.activateLasso(event.originalEvent);
event.stopPropagation();
}
});
}
LassoTool.$inject = [
'eventBus',
'canvas',
'dragging',
'elementRegistry',
'selection',
'toolManager'
];
module.exports = LassoTool;
LassoTool.prototype.activateLasso = function(event, autoActivate) {
this._dragging.init(event, 'lasso', {
autoActivate: autoActivate,
cursor: LASSO_TOOL_CURSOR,
data: {
context: {}
}
});
};
LassoTool.prototype.activateSelection = function(event) {
this._dragging.init(event, 'lasso.selection', {
trapClick: false,
cursor: LASSO_TOOL_CURSOR,
data: {
context: {}
}
});
};
LassoTool.prototype.select = function(elements, bbox) {
var selectedElements = getEnclosedElements(elements, bbox);
this._selection.select(values(selectedElements));
};
LassoTool.prototype.toggle = function() {
if (this.isActive()) {
this._dragging.cancel();
} else {
this.activateSelection();
}
};
LassoTool.prototype.isActive = function() {
var context = this._dragging.context();
return context && /^lasso/.test(context.prefix);
};
function toBBox(event) {
var start = {
x: event.x - event.dx,
y: event.y - event.dy
};
var end = {
x: event.x,
y: event.y
};
var bbox;
if ((start.x <= end.x && start.y < end.y) ||
(start.x < end.x && start.y <= end.y)) {
bbox = {
x: start.x,
y: start.y,
width: end.x - start.x,
height: end.y - start.y
};
} else if ((start.x >= end.x && start.y < end.y) ||
(start.x > end.x && start.y <= end.y)) {
bbox = {
x: end.x,
y: start.y,
width: start.x - end.x,
height: end.y - start.y
};
} else if ((start.x <= end.x && start.y > end.y) ||
(start.x < end.x && start.y >= end.y)) {
bbox = {
x: start.x,
y: end.y,
width: end.x - start.x,
height: start.y - end.y
};
} else if ((start.x >= end.x && start.y > end.y) ||
(start.x > end.x && start.y >= end.y)) {
bbox = {
x: end.x,
y: end.y,
width: start.x - end.x,
height: start.y - end.y
};
} else {
bbox = {
x: end.x,
y: end.y,
width: 0,
height: 0
};
}
return bbox;
}
},{"266":266,"274":274,"440":440,"472":472,"474":474,"478":478,"481":481}],176:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__depends__: [ _dereq_(242) ],
__init__: [ 'lassoTool' ],
lassoTool: [ 'type', _dereq_(175) ]
};
},{"175":175,"242":242}],177:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var model = _dereq_(254);
/**
* The basic modeling entry point.
*
* @param {EventBus} eventBus
* @param {ElementFactory} elementFactory
* @param {CommandStack} commandStack
*/
function Modeling(eventBus, elementFactory, commandStack) {
this._eventBus = eventBus;
this._elementFactory = elementFactory;
this._commandStack = commandStack;
var self = this;
eventBus.on('diagram.init', function() {
// register modeling handlers
self.registerHandlers(commandStack);
});
}
Modeling.$inject = [ 'eventBus', 'elementFactory', 'commandStack' ];
module.exports = Modeling;
Modeling.prototype.getHandlers = function() {
return {
'shape.append': _dereq_(179),
'shape.create': _dereq_(182),
'shape.delete': _dereq_(185),
'shape.move': _dereq_(190),
'shape.resize': _dereq_(195),
'shape.replace': _dereq_(194),
'shape.toggleCollapse': _dereq_(197),
'spaceTool': _dereq_(196),
'label.create': _dereq_(181),
'connection.create': _dereq_(180),
'connection.delete': _dereq_(183),
'connection.move': _dereq_(188),
'connection.layout': _dereq_(187),
'connection.updateWaypoints': _dereq_(199),
'connection.reconnectStart': _dereq_(193),
'connection.reconnectEnd': _dereq_(193),
'elements.move': _dereq_(189),
'elements.delete': _dereq_(184),
'elements.distribute': _dereq_(186),
'elements.align': _dereq_(178),
'element.updateAttachment': _dereq_(198),
'elements.paste': _dereq_(192)
};
};
/**
* Register handlers with the command stack
*
* @param {CommandStack} commandStack
*/
Modeling.prototype.registerHandlers = function(commandStack) {
forEach(this.getHandlers(), function(handler, id) {
commandStack.registerHandler(id, handler);
});
};
///// modeling helpers /////////////////////////////////////////
Modeling.prototype.moveShape = function(shape, delta, newParent, newParentIndex, hints) {
if (typeof newParentIndex === 'object') {
hints = newParentIndex;
newParentIndex = null;
}
var context = {
shape: shape,
delta: delta,
newParent: newParent,
newParentIndex: newParentIndex,
hints: hints || {}
};
this._commandStack.execute('shape.move', context);
};
/**
* Update the attachment of the given shape.
*
* @param {djs.mode.Base} shape
* @param {djs.model.Base} [newHost]
*/
Modeling.prototype.updateAttachment = function(shape, newHost) {
var context = {
shape: shape,
newHost: newHost
};
this._commandStack.execute('element.updateAttachment', context);
};
/**
* Move a number of shapes to a new target, either setting it as
* the new parent or attaching it.
*
* @param {Array} shapes
* @param {Point} delta
* @param {djs.model.Base} [target]
* @param {Boolean} [isAttach=false]
* @param {Object} [hints]
*/
Modeling.prototype.moveElements = function(shapes, delta, target, isAttach, hints) {
if (typeof isAttach === 'object') {
hints = isAttach;
isAttach = undefined;
}
var newParent = target,
newHost;
if (isAttach === true) {
newHost = target;
newParent = target.parent;
}
if (isAttach === false) {
newHost = null;
}
var context = {
shapes: shapes,
delta: delta,
newParent: newParent,
newHost: newHost,
hints: hints || {}
};
this._commandStack.execute('elements.move', context);
};
Modeling.prototype.moveConnection = function(connection, delta, newParent, newParentIndex, hints) {
if (typeof newParentIndex === 'object') {
hints = newParentIndex;
newParentIndex = undefined;
}
var context = {
connection: connection,
delta: delta,
newParent: newParent,
newParentIndex: newParentIndex,
hints: hints || {}
};
this._commandStack.execute('connection.move', context);
};
Modeling.prototype.layoutConnection = function(connection, hints) {
var context = {
connection: connection,
hints: hints || {}
};
this._commandStack.execute('connection.layout', context);
};
/**
* Create connection.
*
* @param {djs.model.Base} source
* @param {djs.model.Base} target
* @param {Number} [targetIndex]
* @param {Object|djs.model.Connection} connection
* @param {djs.model.Base} parent
* @param {Object} hints
*
* @return {djs.model.Connection} the created connection.
*/
Modeling.prototype.createConnection = function(source, target, parentIndex, connection, parent, hints) {
if (typeof parentIndex === 'object') {
hints = parent;
parent = connection;
connection = parentIndex;
parentIndex = undefined;
}
connection = this._create('connection', connection);
var context = {
source: source,
target: target,
parent: parent,
parentIndex: parentIndex,
connection: connection,
hints: hints
};
this._commandStack.execute('connection.create', context);
return context.connection;
};
Modeling.prototype.createShape = function(shape, position, parent, parentIndex, isAttach, hints) {
if (typeof parentIndex !== 'number') {
hints = isAttach;
isAttach = parentIndex;
}
if (typeof isAttach !== 'boolean') {
hints = isAttach;
isAttach = false;
}
shape = this._create('shape', shape);
var context = {
position: position,
shape: shape,
parent: parent,
parentIndex: parentIndex,
host: shape.host,
hints: hints || {}
};
if (isAttach) {
context.parent = parent.parent;
context.host = parent;
}
this._commandStack.execute('shape.create', context);
return context.shape;
};
Modeling.prototype.createLabel = function(labelTarget, position, label, parent) {
label = this._create('label', label);
var context = {
labelTarget: labelTarget,
position: position,
parent: parent || labelTarget.parent,
shape: label
};
this._commandStack.execute('label.create', context);
return context.shape;
};
Modeling.prototype.appendShape = function(source, shape, position, parent, connection, connectionParent) {
shape = this._create('shape', shape);
var context = {
source: source,
position: position,
parent: parent,
shape: shape,
connection: connection,
connectionParent: connectionParent
};
this._commandStack.execute('shape.append', context);
return context.shape;
};
Modeling.prototype.removeElements = function(elements) {
var context = {
elements: elements
};
this._commandStack.execute('elements.delete', context);
};
Modeling.prototype.distributeElements = function(groups, axis, dimension) {
var context = {
groups: groups,
axis: axis,
dimension: dimension
};
this._commandStack.execute('elements.distribute', context);
};
Modeling.prototype.removeShape = function(shape, hints) {
var context = {
shape: shape,
hints: hints || {}
};
this._commandStack.execute('shape.delete', context);
};
Modeling.prototype.removeConnection = function(connection, hints) {
var context = {
connection: connection,
hints: hints || {}
};
this._commandStack.execute('connection.delete', context);
};
Modeling.prototype.replaceShape = function(oldShape, newShape, hints) {
var context = {
oldShape: oldShape,
newData: newShape,
hints: hints || {}
};
this._commandStack.execute('shape.replace', context);
return context.newShape;
};
Modeling.prototype.pasteElements = function(tree, topParent, position) {
var context = {
tree: tree,
topParent: topParent,
position: position
};
this._commandStack.execute('elements.paste', context);
};
Modeling.prototype.alignElements = function(elements, alignment) {
var context = {
elements: elements,
alignment: alignment
};
this._commandStack.execute('elements.align', context);
};
Modeling.prototype.resizeShape = function(shape, newBounds, minBounds) {
var context = {
shape: shape,
newBounds: newBounds,
minBounds: minBounds
};
this._commandStack.execute('shape.resize', context);
};
Modeling.prototype.createSpace = function(movingShapes, resizingShapes, delta, direction) {
var context = {
movingShapes: movingShapes,
resizingShapes: resizingShapes,
delta: delta,
direction: direction
};
this._commandStack.execute('spaceTool', context);
};
Modeling.prototype.updateWaypoints = function(connection, newWaypoints, hints) {
var context = {
connection: connection,
newWaypoints: newWaypoints,
hints: hints || {}
};
this._commandStack.execute('connection.updateWaypoints', context);
};
Modeling.prototype.reconnectStart = function(connection, newSource, dockingOrPoints) {
var context = {
connection: connection,
newSource: newSource,
dockingOrPoints: dockingOrPoints
};
this._commandStack.execute('connection.reconnectStart', context);
};
Modeling.prototype.reconnectEnd = function(connection, newTarget, dockingOrPoints) {
var context = {
connection: connection,
newTarget: newTarget,
dockingOrPoints: dockingOrPoints
};
this._commandStack.execute('connection.reconnectEnd', context);
};
Modeling.prototype.connect = function(source, target, attrs, hints) {
return this.createConnection(source, target, attrs || {}, source.parent, hints);
};
Modeling.prototype._create = function(type, attrs) {
if (attrs instanceof model.Base) {
return attrs;
} else {
return this._elementFactory.create(type, attrs);
}
};
Modeling.prototype.toggleCollapse = function(shape, hints) {
var context = {
shape: shape,
hints: hints || {}
};
this._commandStack.execute('shape.toggleCollapse', context);
};
},{"178":178,"179":179,"180":180,"181":181,"182":182,"183":183,"184":184,"185":185,"186":186,"187":187,"188":188,"189":189,"190":190,"192":192,"193":193,"194":194,"195":195,"196":196,"197":197,"198":198,"199":199,"254":254,"303":303}],178:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
/**
* A handler that align elements in a certain way.
*
*/
function AlignElements(modeling, canvas) {
this._modeling = modeling;
this._canvas = canvas;
}
AlignElements.$inject = [ 'modeling', 'canvas' ];
module.exports = AlignElements;
AlignElements.prototype.preExecute = function(context) {
var modeling = this._modeling;
var elements = context.elements,
alignment = context.alignment;
forEach(elements, function(element) {
var delta = {
x: 0,
y: 0
};
if (alignment.left) {
delta.x = alignment.left - element.x;
} else if (alignment.right) {
delta.x = (alignment.right - element.width) - element.x;
} else if (alignment.center) {
delta.x = (alignment.center - Math.round(element.width / 2)) - element.x;
} else if (alignment.top) {
delta.y = alignment.top - element.y;
} else if (alignment.bottom) {
delta.y = (alignment.bottom - element.height) - element.y;
} else if (alignment.middle) {
delta.y = (alignment.middle - Math.round(element.height / 2)) - element.y;
}
modeling.moveElements([ element ], delta, element.parent);
});
};
AlignElements.prototype.postExecute = function(context) {
};
},{"303":303}],179:[function(_dereq_,module,exports){
'use strict';
var any = _dereq_(299);
var inherits = _dereq_(289);
/**
* A handler that implements reversible appending of shapes
* to a source shape.
*
* @param {canvas} Canvas
* @param {elementFactory} ElementFactory
* @param {modeling} Modeling
*/
function AppendShapeHandler(modeling) {
this._modeling = modeling;
}
inherits(AppendShapeHandler, _dereq_(191));
AppendShapeHandler.$inject = [ 'modeling' ];
module.exports = AppendShapeHandler;
////// api /////////////////////////////////////////////
/**
* Creates a new shape
*
* @param {Object} context
* @param {ElementDescriptor} context.shape the new shape
* @param {ElementDescriptor} context.source the source object
* @param {ElementDescriptor} context.parent the parent object
* @param {Point} context.position position of the new element
*/
AppendShapeHandler.prototype.preExecute = function(context) {
if (!context.source) {
throw new Error('source required');
}
var parent = context.parent || context.source.parent,
shape = this._modeling.createShape(context.shape, context.position, parent);
context.shape = shape;
};
AppendShapeHandler.prototype.postExecute = function(context) {
var parent = context.connectionParent || context.shape.parent;
if (!existsConnection(context.source, context.shape)) {
// create connection
this._modeling.connect(context.source, context.shape, context.connection, parent);
}
};
function existsConnection(source, target) {
return any(source.outgoing, function(c) {
return c.target === target;
});
}
},{"191":191,"289":289,"299":299}],180:[function(_dereq_,module,exports){
'use strict';
function CreateConnectionHandler(canvas, layouter) {
this._canvas = canvas;
this._layouter = layouter;
}
CreateConnectionHandler.$inject = [ 'canvas', 'layouter' ];
module.exports = CreateConnectionHandler;
////// api /////////////////////////////////////////
/**
* Appends a shape to a target shape
*
* @param {Object} context
* @param {djs.element.Base} context.source the source object
* @param {djs.element.Base} context.target the parent object
* @param {Point} context.position position of the new element
*/
CreateConnectionHandler.prototype.execute = function(context) {
var connection = context.connection,
source = context.source,
target = context.target,
parent = context.parent,
parentIndex = context.parentIndex,
hints = context.hints;
if (!source || !target) {
throw new Error('source and target required');
}
if (!parent) {
throw new Error('parent required');
}
connection.source = source;
connection.target = target;
if (!connection.waypoints) {
connection.waypoints = this._layouter.layoutConnection(connection, hints);
}
// add connection
this._canvas.addConnection(connection, parent, parentIndex);
return connection;
};
CreateConnectionHandler.prototype.revert = function(context) {
var connection = context.connection;
this._canvas.removeConnection(connection);
connection.source = null;
connection.target = null;
};
},{}],181:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CreateShapeHandler = _dereq_(182);
/**
* A handler that attaches a label to a given target shape.
*
* @param {canvas} Canvas
*/
function CreateLabelHandler(canvas) {
CreateShapeHandler.call(this, canvas);
}
inherits(CreateLabelHandler, CreateShapeHandler);
CreateLabelHandler.$inject = [ 'canvas' ];
module.exports = CreateLabelHandler;
////// api /////////////////////////////////////////
var originalExecute = CreateShapeHandler.prototype.execute;
/**
* Appends a label to a target shape.
*
* @method CreateLabelHandler#execute
*
* @param {Object} context
* @param {ElementDescriptor} context.target the element the label is attached to
* @param {ElementDescriptor} context.parent the parent object
* @param {Point} context.position position of the new element
*/
CreateLabelHandler.prototype.execute = function(context) {
var label = context.shape;
ensureValidDimensions(label);
label.labelTarget = context.labelTarget;
return originalExecute.call(this, context);
};
var originalRevert = CreateShapeHandler.prototype.revert;
/**
* Undo append by removing the shape
*/
CreateLabelHandler.prototype.revert = function(context) {
context.shape.labelTarget = null;
return originalRevert.call(this, context);
};
////// helpers /////////////////////////////////////////
function ensureValidDimensions(label) {
// make sure a label has valid { width, height } dimensions
[ 'width', 'height' ].forEach(function(prop) {
if (typeof label[prop] === 'undefined') {
label[prop] = 0;
}
});
}
},{"182":182,"289":289}],182:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
var round = Math.round;
/**
* A handler that implements reversible addition of shapes.
*
* @param {canvas} Canvas
*/
function CreateShapeHandler(canvas) {
this._canvas = canvas;
}
CreateShapeHandler.$inject = [ 'canvas' ];
module.exports = CreateShapeHandler;
////// api /////////////////////////////////////////
/**
* Appends a shape to a target shape
*
* @param {Object} context
* @param {djs.model.Base} context.parent the parent object
* @param {Point} context.position position of the new element
*/
CreateShapeHandler.prototype.execute = function(context) {
var shape = context.shape,
positionOrBounds = context.position,
parent = context.parent,
parentIndex = context.parentIndex;
if (!parent) {
throw new Error('parent required');
}
if (!positionOrBounds) {
throw new Error('position required');
}
// (1) add at event center position _or_ at given bounds
if (positionOrBounds.width !== undefined) {
assign(shape, positionOrBounds);
} else {
assign(shape, {
x: positionOrBounds.x - round(shape.width / 2),
y: positionOrBounds.y - round(shape.height / 2)
});
}
// (2) add to canvas
this._canvas.addShape(shape, parent, parentIndex);
return shape;
};
/**
* Undo append by removing the shape
*/
CreateShapeHandler.prototype.revert = function(context) {
// (3) remove form canvas
this._canvas.removeShape(context.shape);
};
},{"431":431}],183:[function(_dereq_,module,exports){
'use strict';
var Collections = _dereq_(263);
/**
* A handler that implements reversible deletion of Connections.
*
*/
function DeleteConnectionHandler(canvas, modeling) {
this._canvas = canvas;
this._modeling = modeling;
}
DeleteConnectionHandler.$inject = [ 'canvas', 'modeling' ];
module.exports = DeleteConnectionHandler;
/**
* - Remove attached label
*/
DeleteConnectionHandler.prototype.preExecute = function(context) {
var connection = context.connection;
// Remove label
if (connection.label) {
this._modeling.removeShape(connection.label);
}
};
DeleteConnectionHandler.prototype.execute = function(context) {
var connection = context.connection,
parent = connection.parent;
context.parent = parent;
context.parentIndex = Collections.indexOf(parent.children, connection);
context.source = connection.source;
context.target = connection.target;
this._canvas.removeConnection(connection);
connection.source = null;
connection.target = null;
connection.label = null;
return connection;
};
/**
* Command revert implementation.
*/
DeleteConnectionHandler.prototype.revert = function(context) {
var connection = context.connection,
parent = context.parent,
parentIndex = context.parentIndex;
connection.source = context.source;
connection.target = context.target;
// restore previous location in old parent
Collections.add(parent.children, connection, parentIndex);
this._canvas.addConnection(connection, parent);
return connection;
};
},{"263":263}],184:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
inherits = _dereq_(289);
function DeleteElementsHandler(modeling, elementRegistry) {
this._modeling = modeling;
this._elementRegistry = elementRegistry;
}
inherits(DeleteElementsHandler, _dereq_(191));
DeleteElementsHandler.$inject = [ 'modeling', 'elementRegistry' ];
module.exports = DeleteElementsHandler;
DeleteElementsHandler.prototype.postExecute = function(context) {
var modeling = this._modeling,
elementRegistry = this._elementRegistry,
elements = context.elements;
forEach(elements, function(element) {
// element may have been removed with previous
// remove operations already (e.g. in case of nesting)
if (!elementRegistry.get(element.id)) {
return;
}
if (element.waypoints) {
modeling.removeConnection(element);
} else {
modeling.removeShape(element);
}
});
};
},{"191":191,"289":289,"303":303}],185:[function(_dereq_,module,exports){
'use strict';
var Collections = _dereq_(263);
var saveClear = _dereq_(277).saveClear;
/**
* A handler that implements reversible deletion of shapes.
*
*/
function DeleteShapeHandler(canvas, modeling) {
this._canvas = canvas;
this._modeling = modeling;
}
DeleteShapeHandler.$inject = [ 'canvas', 'modeling' ];
module.exports = DeleteShapeHandler;
/**
* - Remove connections
* - Remove all direct children
*/
DeleteShapeHandler.prototype.preExecute = function(context) {
var modeling = this._modeling;
var shape = context.shape,
label = shape.label;
// Clean up on removeShape(label)
if (shape.labelTarget) {
context.labelTarget = shape.labelTarget;
shape.labelTarget = null;
}
// Remove label
if (label) {
this._modeling.removeShape(label, { nested: true });
}
// remove connections
saveClear(shape.incoming, function(connection) {
// To make sure that the connection isn't removed twice
// For example if a container is removed
modeling.removeConnection(connection, { nested: true });
});
saveClear(shape.outgoing, function(connection) {
modeling.removeConnection(connection, { nested: true });
});
// remove child shapes and connections
saveClear(shape.children, function(child) {
if (isConnection(child)) {
modeling.removeConnection(child, { nested: true });
} else {
modeling.removeShape(child, { nested: true });
}
});
};
/**
* Remove shape and remember the parent
*/
DeleteShapeHandler.prototype.execute = function(context) {
var canvas = this._canvas;
var shape = context.shape,
oldParent = shape.parent;
context.oldParent = oldParent;
context.oldParentIndex = Collections.indexOf(oldParent.children, shape);
shape.label = null;
canvas.removeShape(shape);
return shape;
};
/**
* Command revert implementation
*/
DeleteShapeHandler.prototype.revert = function(context) {
var canvas = this._canvas;
var shape = context.shape,
oldParent = context.oldParent,
oldParentIndex = context.oldParentIndex,
labelTarget = context.labelTarget;
// restore previous location in old oldParent
Collections.add(oldParent.children, shape, oldParentIndex);
if (labelTarget) {
labelTarget.label = shape;
}
canvas.addShape(shape, oldParent);
return shape;
};
function isConnection(element) {
return element.waypoints;
}
},{"263":263,"277":277}],186:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
sortBy = _dereq_(310);
/**
* A handler that distributes elements evenly.
*/
function DistributeElements(modeling) {
this._modeling = modeling;
}
DistributeElements.$inject = [ 'modeling' ];
module.exports = DistributeElements;
var OFF_AXIS = {
x: 'y',
y: 'x'
};
DistributeElements.prototype.preExecute = function(context) {
var modeling = this._modeling;
var groups = context.groups,
axis = context.axis,
dimension = context.dimension;
function updateRange(group, element) {
group.range.min = Math.min(element[axis], group.range.min);
group.range.max = Math.max(element[axis] + element[dimension], group.range.max);
}
function center(element) {
return element[axis] + element[dimension] / 2;
}
function lastIdx(arr) {
return arr.length - 1;
}
function rangeDiff(range) {
return range.max - range.min;
}
function centerElement(refCenter, element) {
var delta = { y: 0 };
delta[axis] = refCenter - center(element);
if (delta[axis]) {
delta[OFF_AXIS[axis]] = 0;
modeling.moveElements([ element ], delta, element.parent);
}
}
var firstGroup = groups[0],
lastGroupIdx = lastIdx(groups),
lastGroup = groups[ lastGroupIdx ];
var margin,
spaceInBetween,
groupsSize = 0; // the size of each range
forEach(groups, function(group, idx) {
var sortedElements,
refElem,
refCenter;
if (group.elements.length < 2) {
if (idx && idx !== groups.length - 1) {
updateRange(group, group.elements[0]);
groupsSize += rangeDiff(group.range);
}
return;
}
sortedElements = sortBy(group.elements, axis);
refElem = sortedElements[0];
if (idx === lastGroupIdx) {
refElem = sortedElements[lastIdx(sortedElements)];
}
refCenter = center(refElem);
// wanna update the ranges after the shapes have been centered
group.range = null;
forEach(sortedElements, function(element) {
centerElement(refCenter, element);
if (group.range === null) {
group.range = {
min: element[axis],
max: element[axis] + element[dimension]
};
return;
}
// update group's range after centering the range elements
updateRange(group, element);
});
if (idx && idx !== groups.length - 1) {
groupsSize += rangeDiff(group.range);
}
});
spaceInBetween = Math.abs(lastGroup.range.min - firstGroup.range.max);
margin = Math.round((spaceInBetween - groupsSize) / (groups.length - 1));
if (margin < groups.length - 1) {
return;
}
forEach(groups, function(group, groupIdx) {
var delta = {},
prevGroup;
if (group === firstGroup || group === lastGroup) {
return;
}
prevGroup = groups[groupIdx - 1];
group.range.max = 0;
forEach(group.elements, function(element, idx) {
delta[OFF_AXIS[axis]] = 0;
delta[axis] = (prevGroup.range.max - element[axis]) + margin;
if (group.range.min !== element[axis]) {
delta[axis] += element[axis] - group.range.min;
}
if (delta[axis]) {
modeling.moveElements([ element ], delta, element.parent);
}
group.range.max = Math.max(element[axis] + element[dimension], idx ? group.range.max : 0);
});
});
};
DistributeElements.prototype.postExecute = function(context) {
};
},{"303":303,"310":310}],187:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431);
/**
* A handler that implements reversible moving of shapes.
*/
function LayoutConnectionHandler(layouter, canvas) {
this._layouter = layouter;
this._canvas = canvas;
}
LayoutConnectionHandler.$inject = [ 'layouter', 'canvas' ];
module.exports = LayoutConnectionHandler;
LayoutConnectionHandler.prototype.execute = function(context) {
var connection = context.connection;
var oldWaypoints = connection.waypoints;
assign(context, {
oldWaypoints: oldWaypoints
});
connection.waypoints = this._layouter.layoutConnection(connection, context.hints);
return connection;
};
LayoutConnectionHandler.prototype.revert = function(context) {
var connection = context.connection;
connection.waypoints = context.oldWaypoints;
return connection;
};
},{"431":431}],188:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var Collections = _dereq_(263);
/**
* A handler that implements reversible moving of connections.
*
* The handler differs from the layout connection handler in a sense
* that it preserves the connection layout.
*/
function MoveConnectionHandler() { }
module.exports = MoveConnectionHandler;
MoveConnectionHandler.prototype.execute = function(context) {
var connection = context.connection,
delta = context.delta;
var newParent = context.newParent || connection.parent,
newParentIndex = context.newParentIndex,
oldParent = connection.parent;
// save old parent in context
context.oldParent = oldParent;
context.oldParentIndex = Collections.remove(oldParent.children, connection);
// add to new parent at position
Collections.add(newParent.children, connection, newParentIndex);
// update parent
connection.parent = newParent;
// update waypoint positions
forEach(connection.waypoints, function(p) {
p.x += delta.x;
p.y += delta.y;
if (p.original) {
p.original.x += delta.x;
p.original.y += delta.y;
}
});
return connection;
};
MoveConnectionHandler.prototype.revert = function(context) {
var connection = context.connection,
newParent = connection.parent,
oldParent = context.oldParent,
oldParentIndex = context.oldParentIndex,
delta = context.delta;
// remove from newParent
Collections.remove(newParent.children, connection);
// restore previous location in old parent
Collections.add(oldParent.children, connection, oldParentIndex);
// restore parent
connection.parent = oldParent;
// revert to old waypoint positions
forEach(connection.waypoints, function(p) {
p.x -= delta.x;
p.y -= delta.y;
if (p.original) {
p.original.x -= delta.x;
p.original.y -= delta.y;
}
});
return connection;
};
},{"263":263,"303":303}],189:[function(_dereq_,module,exports){
'use strict';
var MoveHelper = _dereq_(201);
/**
* A handler that implements reversible moving of shapes.
*/
function MoveElementsHandler(modeling) {
this._helper = new MoveHelper(modeling);
}
MoveElementsHandler.$inject = [ 'modeling' ];
module.exports = MoveElementsHandler;
MoveElementsHandler.prototype.preExecute = function(context) {
context.closure = this._helper.getClosure(context.shapes);
};
MoveElementsHandler.prototype.postExecute = function(context) {
var hints = context.hints,
primaryShape;
if (hints && hints.primaryShape) {
primaryShape = hints.primaryShape;
hints.oldParent = primaryShape.parent;
}
this._helper.moveClosure(context.closure, context.delta, context.newParent, context.newHost, primaryShape);
};
MoveElementsHandler.prototype.execute = function(context) { };
MoveElementsHandler.prototype.revert = function(context) { };
},{"201":201}],190:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303),
pick = _dereq_(438);
var MoveHelper = _dereq_(201),
Collections = _dereq_(263);
var getMovedSourceAnchor = _dereq_(200).getMovedSourceAnchor,
getMovedTargetAnchor = _dereq_(200).getMovedTargetAnchor;
/**
* A handler that implements reversible moving of shapes.
*/
function MoveShapeHandler(modeling) {
this._modeling = modeling;
this._helper = new MoveHelper(modeling);
}
MoveShapeHandler.$inject = [ 'modeling' ];
module.exports = MoveShapeHandler;
MoveShapeHandler.prototype.execute = function(context) {
var shape = context.shape,
delta = context.delta,
newParent = context.newParent || shape.parent,
newParentIndex = context.newParentIndex,
oldParent = shape.parent;
context.oldBounds = pick(shape, [ 'x', 'y', 'width', 'height']);
// save old parent in context
context.oldParent = oldParent;
context.oldParentIndex = Collections.remove(oldParent.children, shape);
// add to new parent at position
Collections.add(newParent.children, shape, newParentIndex);
// update shape parent + position
assign(shape, {
parent: newParent,
x: shape.x + delta.x,
y: shape.y + delta.y
});
return shape;
};
MoveShapeHandler.prototype.postExecute = function(context) {
var shape = context.shape,
delta = context.delta,
hints = context.hints;
var modeling = this._modeling;
if (hints.layout !== false) {
forEach(shape.incoming, function(c) {
modeling.layoutConnection(c, {
connectionEnd: getMovedTargetAnchor(c, shape, delta)
});
});
forEach(shape.outgoing, function(c) {
modeling.layoutConnection(c, {
connectionStart: getMovedSourceAnchor(c, shape, delta)
});
});
}
if (hints.recurse !== false) {
this.moveChildren(context);
}
};
MoveShapeHandler.prototype.revert = function(context) {
var shape = context.shape,
oldParent = context.oldParent,
oldParentIndex = context.oldParentIndex,
delta = context.delta;
// restore previous location in old parent
Collections.add(oldParent.children, shape, oldParentIndex);
// revert to old position and parent
assign(shape, {
parent: oldParent,
x: shape.x - delta.x,
y: shape.y - delta.y
});
return shape;
};
MoveShapeHandler.prototype.moveChildren = function(context) {
var delta = context.delta,
shape = context.shape;
this._helper.moveRecursive(shape.children, delta, null);
};
MoveShapeHandler.prototype.getNewParent = function(context) {
return context.newParent || context.shape.parent;
};
},{"200":200,"201":201,"263":263,"303":303,"431":431,"438":438}],191:[function(_dereq_,module,exports){
'use strict';
function NoopHandler() {}
module.exports = NoopHandler;
NoopHandler.prototype.execute = function() {};
NoopHandler.prototype.revert = function() {};
},{}],192:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
map = _dereq_(305),
sortBy = _dereq_(310),
clone = _dereq_(420);
var inherits = _dereq_(289);
function removeProperties(element, properties) {
forEach(properties, function(prop) {
if (element[prop]) {
delete element[prop];
}
});
}
/**
* A handler that implements pasting of elements onto the diagram.
*
* @param {eventBus} EventBus
* @param {canvas} Canvas
* @param {selection} Selection
* @param {elementFactory} ElementFactory
* @param {modeling} Modeling
* @param {rules} Rules
*/
function PasteHandler(eventBus, canvas, selection, elementFactory, modeling, rules) {
this._eventBus = eventBus;
this._canvas = canvas;
this._selection = selection;
this._elementFactory = elementFactory;
this._modeling = modeling;
this._rules = rules;
}
inherits(PasteHandler, _dereq_(191));
PasteHandler.$inject = [
'eventBus',
'canvas',
'selection',
'elementFactory',
'modeling',
'rules'
];
module.exports = PasteHandler;
////// api /////////////////////////////////////////////
/**
* Creates a new shape
*
* @param {Object} context
* @param {Object} context.tree the new shape
* @param {Element} context.topParent the paste target
*/
PasteHandler.prototype.preExecute = function(context) {
var eventBus = this._eventBus;
var tree = context.tree,
topParent = context.topParent,
position = context.position;
tree.createdElements = {};
tree.labels = [];
forEach(tree, function(elements, depthStr) {
var depth = parseInt(depthStr, 10);
if (isNaN(depth)) {
return;
}
// set the parent on the top level elements
if (!depth) {
elements = map(elements, function(descriptor) {
descriptor.parent = topParent;
return descriptor;
});
}
// Order by priority for element creation
elements = sortBy(elements, 'priority');
forEach(elements, function(descriptor) {
var id = descriptor.id,
parent = descriptor.parent,
isAttach = false,
hints,
newPosition;
var element = clone(descriptor);
if (depth) {
element.parent = this._getCreatedElement(parent, tree);
}
// this happens when shapes have not been created due to rules
if (!parent) {
return;
}
eventBus.fire('element.paste', {
createdElements: tree.createdElements,
descriptor: element
});
// in case the parent changed during 'element.paste'
parent = element.parent;
if (element.waypoints) {
element = this._createConnection(element, parent, position, tree);
if (element) {
tree.createdElements[id] = {
element: element,
descriptor: descriptor
};
}
return;
}
// supply not-root information as hint
if (element.parent !== topParent) {
hints = { root: false };
}
// set host
if (element.host) {
isAttach = true;
parent = this._getCreatedElement(element.host, tree);
}
// handle labels
if (element.labelTarget) {
return tree.labels.push(element);
}
newPosition = {
x: Math.round(position.x + element.delta.x + (element.width / 2)),
y: Math.round(position.y + element.delta.y + (element.height / 2))
};
removeProperties(element, [ 'id', 'parent', 'delta', 'host', 'priority' ]);
element = this._createShape(element, parent, newPosition, isAttach, hints);
if (element) {
tree.createdElements[id] = {
element: element,
descriptor: descriptor
};
}
}, this);
}, this);
};
// move label's to their relative position
PasteHandler.prototype.postExecute = function(context) {
var modeling = this._modeling,
selection = this._selection;
var tree = context.tree,
labels = tree.labels,
topLevelElements = [];
forEach(labels, function(labelDescriptor) {
var labelTarget = this._getCreatedElement(labelDescriptor.labelTarget, tree),
label, labelTargetPos, newPosition;
if (!labelTarget) {
return;
}
label = labelTarget.label;
if (!label) {
return;
}
labelTargetPos = {
x: labelTarget.x,
y: labelTarget.y
};
if (labelTarget.waypoints) {
labelTargetPos = labelTarget.waypoints[0];
}
newPosition = {
x: Math.round((labelTargetPos.x - label.x) + labelDescriptor.delta.x),
y: Math.round((labelTargetPos.y - label.y) + labelDescriptor.delta.y)
};
modeling.moveShape(label, newPosition, labelTarget.parent);
}, this);
forEach(tree[0], function(descriptor) {
var id = descriptor.id,
toplevel = tree.createdElements[id];
if (toplevel) {
topLevelElements.push(toplevel.element);
}
});
selection.select(topLevelElements);
};
PasteHandler.prototype._createConnection = function(element, parent, parentCenter, tree) {
var modeling = this._modeling,
rules = this._rules;
var connection, source, target, canPaste;
element.waypoints = map(element.waypoints, function(waypoint, idx) {
return {
x: Math.round(parentCenter.x + element.delta[idx].x),
y: Math.round(parentCenter.y + element.delta[idx].y)
};
});
source = this._getCreatedElement(element.source, tree);
target = this._getCreatedElement(element.target, tree);
if (!source || !target) {
return null;
}
canPaste = rules.allowed('element.paste', {
source: source,
target: target
});
if (!canPaste) {
return null;
}
removeProperties(element, [ 'id', 'parent', 'delta', 'source', 'target', 'width', 'height', 'priority' ]);
connection = modeling.createConnection(source, target, element, parent);
return connection;
};
PasteHandler.prototype._createShape = function(element, parent, position, isAttach, hints) {
var modeling = this._modeling,
elementFactory = this._elementFactory,
rules = this._rules;
var canPaste = rules.allowed('element.paste', {
element: element,
position: position,
parent: parent
});
if (!canPaste) {
return null;
}
var shape = elementFactory.createShape(element);
modeling.createShape(shape, position, parent, isAttach, hints);
return shape;
};
PasteHandler.prototype._getCreatedElement = function(id, tree) {
return tree.createdElements[id] && tree.createdElements[id].element;
};
},{"191":191,"289":289,"303":303,"305":305,"310":310,"420":420}],193:[function(_dereq_,module,exports){
'use strict';
var isArray = _dereq_(422);
/**
* Reconnect connection handler
*/
function ReconnectConnectionHandler() { }
ReconnectConnectionHandler.$inject = [ ];
module.exports = ReconnectConnectionHandler;
ReconnectConnectionHandler.prototype.execute = function(context) {
var newSource = context.newSource,
newTarget = context.newTarget,
connection = context.connection,
dockingOrPoints = context.dockingOrPoints,
oldWaypoints = connection.waypoints,
newWaypoints;
if (!newSource && !newTarget) {
throw new Error('newSource or newTarget are required');
}
if (newSource && newTarget) {
throw new Error('must specify either newSource or newTarget');
}
context.oldWaypoints = oldWaypoints;
if (isArray(dockingOrPoints)) {
newWaypoints = dockingOrPoints;
} else {
newWaypoints = oldWaypoints.slice();
newWaypoints.splice(newSource ? 0 : -1, 1, dockingOrPoints);
}
if (newSource) {
context.oldSource = connection.source;
connection.source = newSource;
}
if (newTarget) {
context.oldTarget = connection.target;
connection.target = newTarget;
}
connection.waypoints = newWaypoints;
return connection;
};
ReconnectConnectionHandler.prototype.revert = function(context) {
var newSource = context.newSource,
newTarget = context.newTarget,
connection = context.connection;
if (newSource) {
connection.source = context.oldSource;
}
if (newTarget) {
connection.target = context.oldTarget;
}
connection.waypoints = context.oldWaypoints;
return connection;
};
},{"422":422}],194:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
/**
* A handler that implements reversible replacing of shapes.
* Internally the old shape will be removed and the new shape will be added.
*
*
* @class
* @constructor
*
* @param {canvas} Canvas
*/
function ReplaceShapeHandler(modeling, rules) {
this._modeling = modeling;
this._rules = rules;
}
ReplaceShapeHandler.$inject = [ 'modeling', 'rules' ];
module.exports = ReplaceShapeHandler;
////// api /////////////////////////////////////////
/**
* Replaces a shape with an replacement Element.
*
* The newData object should contain type, x, y.
*
* If possible also the incoming/outgoing connection
* will be restored.
*
* @param {Object} context
*/
ReplaceShapeHandler.prototype.preExecute = function(context) {
var self = this,
modeling = this._modeling,
rules = this._rules;
var oldShape = context.oldShape,
newData = context.newData,
hints = context.hints,
newShape;
function canReconnect(type, source, target, connection) {
return rules.allowed(type, {
source: source,
target: target,
connection: connection
});
}
// (1) place a new shape at the given position
var position = {
x: newData.x,
y: newData.y
};
newShape = context.newShape = context.newShape || self.createShape(newData, position, oldShape.parent);
// (2) update the host
if (oldShape.host) {
modeling.updateAttachment(newShape, oldShape.host);
}
// (3) adopt all children from the old shape
var children;
if (hints.moveChildren !== false) {
children = oldShape.children.slice();
modeling.moveElements(children, { x: 0, y: 0 }, newShape);
}
// (4) reconnect connections to the new shape (where allowed)
var incoming = oldShape.incoming.slice(),
outgoing = oldShape.outgoing.slice();
forEach(incoming, function(connection) {
var waypoints = connection.waypoints,
docking = waypoints[waypoints.length - 1],
source = connection.source,
allowed = canReconnect('connection.reconnectEnd', source, newShape, connection);
if (allowed) {
self.reconnectEnd(connection, newShape, docking);
}
});
forEach(outgoing, function(connection) {
var waypoints = connection.waypoints,
docking = waypoints[0],
target = connection.target,
allowed = canReconnect('connection.reconnectStart', newShape, target, connection);
if (allowed) {
self.reconnectStart(connection, newShape, docking);
}
});
};
ReplaceShapeHandler.prototype.postExecute = function(context) {
var modeling = this._modeling;
var oldShape = context.oldShape,
newShape = context.newShape;
// if an element gets resized on replace, layout the connection again
forEach(newShape.incoming, function(c) {
modeling.layoutConnection(c, { endChanged: true });
});
forEach(newShape.outgoing, function(c) {
modeling.layoutConnection(c, { startChanged: true });
});
modeling.removeShape(oldShape);
};
ReplaceShapeHandler.prototype.execute = function(context) {};
ReplaceShapeHandler.prototype.revert = function(context) {};
ReplaceShapeHandler.prototype.createShape = function(shape, position, target) {
var modeling = this._modeling;
return modeling.createShape(shape, position, target);
};
ReplaceShapeHandler.prototype.reconnectStart = function(connection, newSource, dockingPoint) {
var modeling = this._modeling;
modeling.reconnectStart(connection, newSource, dockingPoint);
};
ReplaceShapeHandler.prototype.reconnectEnd = function(connection, newTarget, dockingPoint) {
var modeling = this._modeling;
modeling.reconnectEnd(connection, newTarget, dockingPoint);
};
},{"303":303}],195:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
forEach = _dereq_(303);
var getResizedSourceAnchor = _dereq_(200).getResizedSourceAnchor,
getResizedTargetAnchor = _dereq_(200).getResizedTargetAnchor;
/**
* A handler that implements reversible resizing of shapes.
*
* @param {Modeling} modeling
*/
function ResizeShapeHandler(modeling) {
this._modeling = modeling;
}
ResizeShapeHandler.$inject = [ 'modeling' ];
module.exports = ResizeShapeHandler;
/**
* {
* shape: {....}
* newBounds: {
* width: 20,
* height: 40,
* x: 5,
* y: 10
* }
*
* }
*/
ResizeShapeHandler.prototype.execute = function(context) {
var shape = context.shape,
newBounds = context.newBounds,
minBounds = context.minBounds;
if (newBounds.x === undefined || newBounds.y === undefined ||
newBounds.width === undefined || newBounds.height === undefined) {
throw new Error('newBounds must have {x, y, width, height} properties');
}
if (minBounds && (newBounds.width < minBounds.width
|| newBounds.height < minBounds.height)) {
throw new Error('width and height cannot be less than minimum height and width');
} else if (!minBounds
&& newBounds.width < 10 || newBounds.height < 10) {
throw new Error('width and height cannot be less than 10px');
}
// save old bbox in context
context.oldBounds = {
width: shape.width,
height: shape.height,
x: shape.x,
y: shape.y
};
// update shape
assign(shape, {
width: newBounds.width,
height: newBounds.height,
x: newBounds.x,
y: newBounds.y
});
return shape;
};
ResizeShapeHandler.prototype.postExecute = function(context) {
var shape = context.shape,
oldBounds = context.oldBounds;
var modeling = this._modeling;
forEach(shape.incoming, function(c) {
modeling.layoutConnection(c, {
connectionEnd: getResizedTargetAnchor(c, shape, oldBounds)
});
});
forEach(shape.outgoing, function(c) {
modeling.layoutConnection(c, {
connectionStart: getResizedSourceAnchor(c, shape, oldBounds)
});
});
};
ResizeShapeHandler.prototype.revert = function(context) {
var shape = context.shape,
oldBounds = context.oldBounds;
// restore previous bbox
assign(shape, {
width: oldBounds.width,
height: oldBounds.height,
x: oldBounds.x,
y: oldBounds.y
});
return shape;
};
},{"200":200,"303":303,"431":431}],196:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var SpaceUtil = _dereq_(239);
/**
* A handler that implements reversible creating and removing of space.
*
* It executes in two phases:
*
* (1) resize all affected resizeShapes
* (2) move all affected moveElements
*/
function SpaceToolHandler(modeling) {
this._modeling = modeling;
}
SpaceToolHandler.$inject = [ 'modeling' ];
module.exports = SpaceToolHandler;
SpaceToolHandler.prototype.preExecute = function(context) {
// resize
var modeling = this._modeling,
resizingShapes = context.resizingShapes,
delta = context.delta,
direction = context.direction;
forEach(resizingShapes, function(shape) {
var newBounds = SpaceUtil.resizeBounds(shape, direction, delta);
modeling.resizeShape(shape, newBounds);
});
};
SpaceToolHandler.prototype.postExecute = function(context) {
// move
var modeling = this._modeling,
movingShapes = context.movingShapes,
delta = context.delta;
modeling.moveElements(movingShapes, delta, undefined, false, { autoResize: false });
};
SpaceToolHandler.prototype.execute = function(context) {};
SpaceToolHandler.prototype.revert = function(context) {};
},{"239":239,"303":303}],197:[function(_dereq_,module,exports){
'use strict';
/**
* A handler that toggles the collapsed state of an element
* and the visibility of all its children.
*
* @param {Modeling} modeling
*/
function ToggleShapeCollapseHandler(modeling) {
this._modeling = modeling;
}
ToggleShapeCollapseHandler.$inject = [ 'modeling' ];
module.exports = ToggleShapeCollapseHandler;
ToggleShapeCollapseHandler.prototype.execute = function(context) {
var shape = context.shape,
children = shape.children;
// remember previous visibility of children
context.oldChildrenVisibility = getElementsVisibility(children);
// toggle state
shape.collapsed = !shape.collapsed;
// hide/show children
setHidden(children, shape.collapsed);
return [shape].concat(children);
};
ToggleShapeCollapseHandler.prototype.revert = function(context) {
var shape = context.shape,
oldChildrenVisibility = context.oldChildrenVisibility;
var children = shape.children;
// set old visability of children
restoreVisibility(children, oldChildrenVisibility);
// retoggle state
shape.collapsed = !shape.collapsed;
return [shape].concat(children);
};
/////// helpers ///////////////////////////////
/**
* Return a map { elementId -> hiddenState}.
*
* @param {Array} elements
*
* @return {Object}
*/
function getElementsVisibility(elements) {
var result = {};
elements.forEach(function(e) {
result[e.id] = e.hidden;
});
return result;
}
function setHidden(elements, newHidden) {
elements.forEach(function(element) {
element.hidden = newHidden;
});
}
function restoreVisibility(elements, lastState) {
elements.forEach(function(e) {
e.hidden = lastState[e.id];
});
}
},{}],198:[function(_dereq_,module,exports){
'use strict';
var Collections = _dereq_(263);
/**
* A handler that implements reversible attaching/detaching of shapes.
*/
function UpdateAttachmentHandler(modeling) {
this._modeling = modeling;
}
module.exports = UpdateAttachmentHandler;
UpdateAttachmentHandler.$inject = [ 'modeling' ];
UpdateAttachmentHandler.prototype.execute = function(context) {
var shape = context.shape,
newHost = context.newHost,
oldHost = shape.host;
// (0) detach from old host
context.oldHost = oldHost;
context.attacherIdx = removeAttacher(oldHost, shape);
// (1) attach to new host
addAttacher(newHost, shape);
// (2) update host
shape.host = newHost;
return shape;
};
UpdateAttachmentHandler.prototype.revert = function(context) {
var shape = context.shape,
newHost = context.newHost,
oldHost = context.oldHost,
attacherIdx = context.attacherIdx;
// (2) update host
shape.host = oldHost;
// (1) attach to new host
removeAttacher(newHost, shape);
// (0) detach from old host
addAttacher(oldHost, shape, attacherIdx);
return shape;
};
function removeAttacher(host, attacher) {
// remove attacher from host
return Collections.remove(host && host.attachers, attacher);
}
function addAttacher(host, attacher, idx) {
if (!host) {
return;
}
var attachers = host.attachers;
if (!attachers) {
host.attachers = attachers = [];
}
Collections.add(attachers, attacher, idx);
}
},{"263":263}],199:[function(_dereq_,module,exports){
'use strict';
function UpdateWaypointsHandler() { }
module.exports = UpdateWaypointsHandler;
UpdateWaypointsHandler.prototype.execute = function(context) {
var connection = context.connection,
newWaypoints = context.newWaypoints;
context.oldWaypoints = connection.waypoints;
connection.waypoints = newWaypoints;
return connection;
};
UpdateWaypointsHandler.prototype.revert = function(context) {
var connection = context.connection,
oldWaypoints = context.oldWaypoints;
connection.waypoints = oldWaypoints;
return connection;
};
},{}],200:[function(_dereq_,module,exports){
'use strict';
var getNewAttachPoint = _dereq_(261).getNewAttachPoint;
function getResizedSourceAnchor(connection, shape, oldBounds) {
var waypoints = safeGetWaypoints(connection),
oldAnchor = waypoints[0];
return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
}
module.exports.getResizedSourceAnchor = getResizedSourceAnchor;
function getResizedTargetAnchor(connection, shape, oldBounds) {
var waypoints = safeGetWaypoints(connection),
oldAnchor = waypoints[waypoints.length - 1];
return getNewAttachPoint(oldAnchor.original || oldAnchor, oldBounds, shape);
}
module.exports.getResizedTargetAnchor = getResizedTargetAnchor;
function getMovedSourceAnchor(connection, source, moveDelta) {
return getResizedSourceAnchor(connection, source, substractPosition(source, moveDelta));
}
module.exports.getMovedSourceAnchor = getMovedSourceAnchor;
function getMovedTargetAnchor(connection, target, moveDelta) {
return getResizedTargetAnchor(connection, target, substractPosition(target, moveDelta));
}
module.exports.getMovedTargetAnchor = getMovedTargetAnchor;
//////// helpers ////////////////////////////////////
function substractPosition(bounds, delta) {
return {
x: bounds.x - delta.x,
y: bounds.y - delta.y,
width: bounds.width,
height: bounds.height
};
}
/**
* Return waypoints of given connection; throw if non exists (should not happen!!).
*
* @param {Connection} connection
*
* @return {Array}
*/
function safeGetWaypoints(connection) {
var waypoints = connection.waypoints;
if (!waypoints.length) {
throw new Error('connection#' + connection.id + ': no waypoints');
}
return waypoints;
}
},{"261":261}],201:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303);
var Elements = _dereq_(266);
var getMovedSourceAnchor = _dereq_(200).getMovedSourceAnchor,
getMovedTargetAnchor = _dereq_(200).getMovedTargetAnchor;
/**
* A helper that is able to carry out serialized move operations on multiple elements.
*
* @param {Modeling} modeling
*/
function MoveHelper(modeling) {
this._modeling = modeling;
}
module.exports = MoveHelper;
/**
* Move the specified elements and all children by the given delta.
*
* This moves all enclosed connections, too and layouts all affected
* external connections.
*
* @param {Array} elements
* @param {Point} delta
* @param {djs.model.Base} newParent applied to the first level of shapes
*
* @return {Array} list of touched elements
*/
MoveHelper.prototype.moveRecursive = function(elements, delta, newParent) {
if (!elements) {
return [];
} else {
return this.moveClosure(this.getClosure(elements), delta, newParent);
}
};
/**
* Move the given closure of elmements.
*
* @param {Object} closure
* @param {Point} delta
* @param {djs.model.Base} [newParent]
* @param {djs.model.Base} [newHost]
*/
MoveHelper.prototype.moveClosure = function(closure, delta, newParent, newHost, primaryShape) {
var modeling = this._modeling;
var allShapes = closure.allShapes,
allConnections = closure.allConnections,
enclosedConnections = closure.enclosedConnections,
topLevel = closure.topLevel,
keepParent = false;
if (primaryShape && primaryShape.parent === newParent) {
keepParent = true;
}
// move all shapes
forEach(allShapes, function(shape) {
// move the element according to the given delta
modeling.moveShape(shape, delta, topLevel[shape.id] && !keepParent && newParent, {
recurse: false,
layout: false
});
});
// move all child connections / layout external connections
forEach(allConnections, function(c) {
var sourceMoved = !!allShapes[c.source.id],
targetMoved = !!allShapes[c.target.id];
if (enclosedConnections[c.id] && sourceMoved && targetMoved) {
modeling.moveConnection(c, delta, topLevel[c.id] && !keepParent && newParent);
} else {
modeling.layoutConnection(c, {
connectionStart: sourceMoved && getMovedSourceAnchor(c, c.source, delta),
connectionEnd: targetMoved && getMovedTargetAnchor(c, c.target, delta)
});
}
});
};
/**
* Returns the closure for the selected elements
*
* @param {Array} elements
* @return {Object} closure
*/
MoveHelper.prototype.getClosure = function(elements) {
return Elements.getClosure(elements);
};
},{"200":200,"266":266,"303":303}],202:[function(_dereq_,module,exports){
'use strict';
var bind = _dereq_(312);
function MouseTracking(eventBus, canvas) {
this._eventBus = eventBus;
this._canvas = canvas;
this._init();
}
MouseTracking.$inject = [
'eventBus',
'canvas'
];
module.exports = MouseTracking;
MouseTracking.prototype.getHoverContext = function() {
var viewbox = this._canvas.viewbox();
return {
element: this._hoverElement,
point: {
x: viewbox.x + Math.round(this._mouseX / viewbox.scale),
y: viewbox.y + Math.round(this._mouseY / viewbox.scale)
}
};
};
MouseTracking.prototype._init = function() {
var eventBus = this._eventBus,
canvas = this._canvas;
var container = canvas.getContainer();
this._setMousePosition = bind(this._setMousePosition, this);
container.addEventListener('mousemove', this._setMousePosition);
eventBus.on('diagram.destroy', function() {
container.removeEventListener('mousemove', this._setMousePosition);
}, this);
eventBus.on('element.hover', this._setHoverElement, this);
};
MouseTracking.prototype._setHoverElement = function(event) {
this._hoverElement = event.element;
};
MouseTracking.prototype._setMousePosition = function(event) {
this._mouseX = event.layerX;
this._mouseY = event.layerY;
};
},{"312":312}],203:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__init__: [ 'mouseTracking' ],
mouseTracking: [ 'type', _dereq_(202) ]
};
},{"202":202}],204:[function(_dereq_,module,exports){
'use strict';
var assign = _dereq_(431),
filter = _dereq_(301),
groupBy = _dereq_(304);
var LOW_PRIORITY = 500,
MEDIUM_PRIORITY = 1250,
HIGH_PRIORITY = 1500;
var getOriginalEvent = _dereq_(267).getOriginal;
var round = Math.round;
function mid(element) {
return {
x: element.x + round(element.width / 2),
y: element.y + round(element.height / 2)
};
}
/**
* A plugin that makes shapes draggable / droppable.
*
* @param {EventBus} eventBus
* @param {Dragging} dragging
* @param {Modeling} modeling
* @param {Selection} selection
* @param {Rules} rules
*/
function MoveEvents(eventBus, dragging, modeling, selection, rules) {
// rules
function canMove(shapes, delta, position, target) {
return rules.allowed('elements.move', {
shapes: shapes,
delta: delta,
position: position,
target: target
});
}
// move events
// assign a high priority to this handler to setup the environment
// others may hook up later, e.g. at default priority and modify
// the move environment.
//
// This sets up the context with
//
// * shape: the primary shape being moved
// * shapes: a list of shapes to be moved
// * validatedShapes: a list of shapes that are being checked
// against the rules before and during move
//
eventBus.on('shape.move.start', HIGH_PRIORITY, function(event) {
var context = event.context,
shape = event.shape,
shapes = selection.get().slice();
// move only single shape if the dragged element
// is not part of the current selection
if (shapes.indexOf(shape) === -1) {
shapes = [ shape ];
}
// ensure we remove nested elements in the collection
// and add attachers for a proper dragger
shapes = removeNested(shapes);
// attach shapes to drag context
assign(context, {
shapes: shapes,
validatedShapes: shapes,
shape: shape
});
});
// assign a high priority to this handler to setup the environment
// others may hook up later, e.g. at default priority and modify
// the move environment
//
eventBus.on('shape.move.start', MEDIUM_PRIORITY, function(event) {
var context = event.context,
validatedShapes = context.validatedShapes,
canExecute;
canExecute = context.canExecute = canMove(validatedShapes);
// check if we can move the elements
if (!canExecute) {
// suppress move operation
event.stopPropagation();
return false;
}
});
// assign a low priority to this handler
// to let others modify the move event before we update
// the context
//
eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
var context = event.context,
validatedShapes = context.validatedShapes,
hover = event.hover,
delta = { x: event.dx, y: event.dy },
position = { x: event.x, y: event.y },
canExecute;
// check if we can move the elements
canExecute = canMove(validatedShapes, delta, position, hover);
context.delta = delta;
context.canExecute = canExecute;
// simply ignore move over
if (canExecute === null) {
context.target = null;
return;
}
context.target = hover;
});
eventBus.on('shape.move.end', function(event) {
var context = event.context;
var delta = context.delta,
canExecute = context.canExecute,
isAttach = canExecute === 'attach',
shapes = context.shapes;
if (!canExecute) {
return false;
}
// ensure we have actual pixel values deltas
// (important when zoom level was > 1 during move)
delta.x = round(delta.x);
delta.y = round(delta.y);
modeling.moveElements(shapes, delta, context.target, isAttach, { primaryShape: context.shape });
});
// move activation
eventBus.on('element.mousedown', function(event) {
var originalEvent = getOriginalEvent(event);
if (!originalEvent) {
throw new Error('must supply DOM mousedown event');
}
start(originalEvent, event.element);
});
function start(event, element, activate) {
// do not move connections or the root element
if (element.waypoints || !element.parent) {
return;
}
var referencePoint = mid(element);
dragging.init(event, referencePoint, 'shape.move', {
cursor: 'grabbing',
autoActivate: activate,
data: {
shape: element,
context: {}
}
});
}
// API
this.start = start;
}
MoveEvents.$inject = [ 'eventBus', 'dragging', 'modeling', 'selection', 'rules' ];
module.exports = MoveEvents;
/**
* Return a filtered list of elements that do not contain
* those nested into others.
*
* @param {Array} elements
*
* @return {Array} filtered
*/
function removeNested(elements) {
var ids = groupBy(elements, 'id');
return filter(elements, function(element) {
while ((element = element.parent)) {
// parent in selection
if (ids[element.id]) {
return false;
}
}
return true;
});
}
},{"267":267,"301":301,"304":304,"431":431}],205:[function(_dereq_,module,exports){
'use strict';
var flatten = _dereq_(291),
forEach = _dereq_(303),
filter = _dereq_(301),
find = _dereq_(302),
size = _dereq_(308),
groupBy = _dereq_(304),
map = _dereq_(305);
var Elements = _dereq_(266);
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgClear = _dereq_(476),
svgCreate = _dereq_(478);
var translate = _dereq_(279).translate;
var LOW_PRIORITY = 500;
var MARKER_DRAGGING = 'djs-dragging',
MARKER_OK = 'drop-ok',
MARKER_NOT_OK = 'drop-not-ok',
MARKER_NEW_PARENT = 'new-parent',
MARKER_ATTACH = 'attach-ok';
/**
* Provides previews for moving shapes when moving.
*
* @param {EventBus} eventBus
* @param {ElementRegistry} elementRegistry
* @param {Canvas} canvas
* @param {Styles} styles
*/
function MovePreview(eventBus, elementRegistry, canvas, styles, previewSupport) {
function getVisualDragShapes(shapes) {
var elements = getAllDraggedElements(shapes);
var filteredElements = removeEdges(elements);
return filteredElements;
}
function getAllDraggedElements(shapes) {
var allShapes = Elements.selfAndAllChildren(shapes, true);
var allConnections = map(allShapes, function(shape) {
return (shape.incoming || []).concat(shape.outgoing || []);
});
return flatten(allShapes.concat(allConnections), true);
}
/**
* Sets drop marker on an element.
*/
function setMarker(element, marker) {
[ MARKER_ATTACH, MARKER_OK, MARKER_NOT_OK, MARKER_NEW_PARENT ].forEach(function(m) {
if (m === marker) {
canvas.addMarker(element, m);
} else {
canvas.removeMarker(element, m);
}
});
}
function makeDraggable(context, element, addMarker) {
previewSupport.addDragger(element, context.dragGroup);
if (addMarker) {
canvas.addMarker(element, MARKER_DRAGGING);
}
if (context.allDraggedElements) {
context.allDraggedElements.push(element);
} else {
context.allDraggedElements = [ element ];
}
}
// expose to other components
// that plug into the drag behavior
this.makeDraggable = makeDraggable;
// add previews
eventBus.on('shape.move.start', LOW_PRIORITY, function(event) {
var context = event.context,
dragShapes = context.shapes,
allDraggedElements = context.allDraggedElements;
var visuallyDraggedShapes = getVisualDragShapes(dragShapes);
if (!context.dragGroup) {
var dragGroup = svgCreate('g');
svgAttr(dragGroup, styles.cls('djs-drag-group', [ 'no-events' ]));
var defaultLayer = canvas.getDefaultLayer();
svgAppend(defaultLayer, dragGroup);
context.dragGroup = dragGroup;
}
// add previews
visuallyDraggedShapes.forEach(function(shape) {
previewSupport.addDragger(shape, context.dragGroup);
});
// cache all dragged elements / gfx
// so that we can quickly undo their state changes later
if (!allDraggedElements) {
allDraggedElements = getAllDraggedElements(dragShapes);
} else {
allDraggedElements = flatten(allDraggedElements, getAllDraggedElements(dragShapes));
}
// add dragging marker
forEach(allDraggedElements, function(e) {
canvas.addMarker(e, MARKER_DRAGGING);
});
context.allDraggedElements = allDraggedElements;
// determine, if any of the dragged elements have different parents
context.differentParents = haveDifferentParents(dragShapes);
});
// update previews
eventBus.on('shape.move.move', LOW_PRIORITY, function(event) {
var context = event.context,
dragGroup = context.dragGroup,
target = context.target,
parent = context.shape.parent,
canExecute = context.canExecute;
if (target) {
if (canExecute === 'attach') {
setMarker(target, MARKER_ATTACH);
} else if (context.canExecute && target && target.id !== parent.id) {
setMarker(target, MARKER_NEW_PARENT);
} else {
setMarker(target, context.canExecute ? MARKER_OK : MARKER_NOT_OK);
}
}
translate(dragGroup, event.dx, event.dy);
});
eventBus.on([ 'shape.move.out', 'shape.move.cleanup' ], function(event) {
var context = event.context,
target = context.target;
if (target) {
setMarker(target, null);
}
});
// remove previews
eventBus.on('shape.move.cleanup', function(event) {
var context = event.context,
allDraggedElements = context.allDraggedElements,
dragGroup = context.dragGroup;
// remove dragging marker
forEach(allDraggedElements, function(e) {
canvas.removeMarker(e, MARKER_DRAGGING);
});
if (dragGroup) {
svgClear(dragGroup);
}
});
}
MovePreview.$inject = [ 'eventBus', 'elementRegistry', 'canvas', 'styles', 'previewSupport' ];
module.exports = MovePreview;
////////// helpers //////////
// returns elements minus all connections
// where source or target is not elements
function removeEdges(elements) {
var filteredElements = filter(elements, function(element) {
if (!isConnection(element)) {
return true;
} else {
var srcFound = find(elements, element.source);
var targetFound = find(elements, element.target);
return srcFound && targetFound;
}
});
return filteredElements;
}
function haveDifferentParents(elements) {
return size(groupBy(elements, function(e) { return e.parent && e.parent.id; })) !== 1;
}
/**
* Checks if an element is a connection.
*/
function isConnection(element) {
return element.waypoints;
}
},{"266":266,"279":279,"291":291,"301":301,"302":302,"303":303,"304":304,"305":305,"308":308,"472":472,"474":474,"476":476,"478":478}],206:[function(_dereq_,module,exports){
module.exports = {
__depends__: [
_dereq_(170),
_dereq_(233),
_dereq_(209),
_dereq_(227),
_dereq_(162),
_dereq_(217)
],
__init__: [ 'move', 'movePreview' ],
move: [ 'type', _dereq_(204) ],
movePreview: [ 'type', _dereq_(205) ]
};
},{"162":162,"170":170,"204":204,"205":205,"209":209,"217":217,"227":227,"233":233}],207:[function(_dereq_,module,exports){
'use strict';
var inherits = _dereq_(289);
var CommandInterceptor = _dereq_(119);
/**
* An abstract provider that allows modelers to implement a custom
* ordering of diagram elements on the canvas.
*
* It makes sure that the order is always preserved during element
* creation and move operations.
*
* In order to use this behavior, inherit from it and override
* the method {@link OrderingProvider#getOrdering}.
*
* @example
*
* ```javascript
* function CustomOrderingProvider(eventBus) {
* OrderingProvider.call(this, eventBus);
*
* this.getOrdering = function(element, newParent) {
* // always insert elements at the front
* // when moving
* return {
* index: 0,
* parent: newParent
* };
* };
* }
* ```
*
* @param {EventBus} eventBus
*/
function OrderingProvider(eventBus) {
CommandInterceptor.call(this, eventBus);
var self = this;
this.preExecute([ 'shape.create', 'connection.create' ], function(event) {
var context = event.context,
element = context.shape || context.connection,
parent = context.parent;
var ordering = self.getOrdering(element, parent);
if (ordering) {
if (ordering.parent !== undefined) {
context.parent = ordering.parent;
}
context.parentIndex = ordering.index;
}
});
this.preExecute([ 'shape.move', 'connection.move' ], function(event) {
var context = event.context,
element = context.shape || context.connection,
parent = context.newParent || element.parent;
var ordering = self.getOrdering(element, parent);
if (ordering) {
if (ordering.parent !== undefined) {
context.newParent = ordering.parent;
}
context.newParentIndex = ordering.index;
}
});
}
/**
* Return a custom ordering of the element, both in terms
* of parent element and index in the new parent.
*
* Implementors of this method must return an object with
* `parent` _and_ `index` in it.
*
* @param {djs.model.Base} element
* @param {djs.model.Shape} newParent
*
* @return {Object} ordering descriptor
*/
OrderingProvider.prototype.getOrdering = function(element, newParent) {
return null;
};
inherits(OrderingProvider, CommandInterceptor);
module.exports = OrderingProvider;
},{"119":119,"289":289}],208:[function(_dereq_,module,exports){
'use strict';
var getBBox = _dereq_(266).getBBox;
var LOW_PRIORITY = 500;
var svgAppend = _dereq_(472),
svgAttr = _dereq_(474),
svgCreate = _dereq_(478);
var domQuery = _dereq_(452);
var assign = _dereq_(431);
/**
* @class
*
* A plugin that adds an outline to shapes and connections that may be activated and styled
* via CSS classes.
*
* @param {EventBus} eventBus
* @param {Styles} styles
* @param {ElementRegistry} elementRegistry
*/
function Outline(eventBus, styles, elementRegistry) {
this.offset = 6;
var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]);
var self = this;
function createOutline(gfx, bounds) {
var outline = svgCreate('rect');
svgAttr(outline, assign({
x: 10,
y: 10,
width: 100,
height: 100
}, OUTLINE_STYLE));
svgAppend(gfx, outline);
return outline;
}
// A low priortity is necessary, because outlines of labels have to be updated
// after the label bounds have been updated in the renderer.
eventBus.on([ 'shape.added', 'shape.changed' ], LOW_PRIORITY, function(event) {
var element = event.element,
gfx = event.gfx;
var outline = domQuery('.djs-outline', gfx);
if (!outline) {
outline = createOutline(gfx, element);
}
self.updateShapeOutline(outline, element);
});
eventBus.on([ 'connection.added', 'connection.changed' ], function(event) {
var element = event.element,
gfx = event.gfx;
var outline = domQuery('.djs-outline', gfx);
if (!outline) {
outline = createOutline(gfx, element);
}
self.updateConnectionOutline(outline, element);
});
}
/**
* Updates the outline of a shape respecting the dimension of the
* element and an outline offset.
*
* @param {SVGElement} outline
* @param {djs.model.Base} element
*/
Outline.prototype.updateShapeOutline = function(outline, element) {
svgAttr(outline, {
x: -this.offset,
y: -this.offset,
width: element.width + this.offset * 2,
height: element.height + this.offset * 2
});
};
/**
* Updates the outline of a connection respecting the bounding box of
* the connection and an outline offset.
*
* @param {SVGElement} outline
* @param {djs.model.Base} element
*/
Outline.prototype.updateConnectionOutline = function(outline, connection) {
var bbox = getBBox(connection);
svgAttr(outline, {
x: bbox.x - this.offset,
y: bbox.y - this.offset,
width: bbox.width + this.offset * 2,
height: bbox.height + this.offset * 2
});
};
Outline.$inject = ['eventBus', 'styles', 'elementRegistry'];
module.exports = Outline;
},{"266":266,"431":431,"452":452,"472":472,"474":474,"478":478}],209:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__init__: [ 'outline' ],
outline: [ 'type', _dereq_(208) ]
};
},{"208":208}],210:[function(_dereq_,module,exports){
'use strict';
var isArray = _dereq_(422),
isString = _dereq_(428),
isObject = _dereq_(426),
assign = _dereq_(431),
forEach = _dereq_(303),
find = _dereq_(302),
filter = _dereq_(301);
var domify = _dereq_(449),
domClasses = _dereq_(445),
domAttr = _dereq_(444),
domRemove = _dereq_(453),
domClear = _dereq_(446);
var getBBox = _dereq_(266).getBBox;
// document wide unique overlay ids
var ids = new (_dereq_(270))('ov');
var LOW_PRIORITY = 500;
function createRoot(parent) {
var root = domify('
');
parent.insertBefore(root, parent.firstChild);
return root;
}
function setPosition(el, x, y) {
assign(el.style, { left: x + 'px', top: y + 'px' });
}
function setVisible(el, visible) {
el.style.display = visible === false ? 'none' : '';
}
function setTransform(el, transform) {
el.style['transform-origin'] = 'top left';
[ '', '-ms-', '-webkit-' ].forEach(function(prefix) {
el.style[prefix + 'transform'] = transform;
});
}
function isDef(o) {
return typeof o !== 'undefined';
}
/**
* A service that allows users to attach overlays to diagram elements.
*
* The overlay service will take care of overlay positioning during updates.
*
* @example
*
* // add a pink badge on the top left of the shape
* overlays.add(someShape, {
* position: {
* top: -5,
* left: -5
* },
* html: '0
'
* });
*
* // or add via shape id
*
* overlays.add('some-element-id', {
* position: {
* top: -5,
* left: -5
* }
* html: '0
'
* });
*
* // or add with optional type
*
* overlays.add(someShape, 'badge', {
* position: {
* top: -5,
* left: -5
* }
* html: '0
'
* });
*
*
* // remove an overlay
*
* var id = overlays.add(...);
* overlays.remove(id);
*
*
* You may configure overlay defaults during tool by providing a `config` module
* with `overlays.defaults` as an entry:
*
* {
* overlays: {
* defaults: {
* show: {
* minZoom: 0.7,
* maxZoom: 5.0
* },
* scale: {
* min: 1
* }
* }
* }
*
* @param {Object} config
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {ElementRegistry} elementRegistry
*/
function Overlays(config, eventBus, canvas, elementRegistry) {
this._eventBus = eventBus;
this._canvas = canvas;
this._elementRegistry = elementRegistry;
this._ids = ids;
this._overlayDefaults = assign({
// no show constraints
show: null,
// always scale
scale: true
}, config && config.defaults);
/**
* Mapping overlayId -> overlay
*/
this._overlays = {};
/**
* Mapping elementId -> overlay container
*/
this._overlayContainers = [];
// root html element for all overlays
this._overlayRoot = createRoot(canvas.getContainer());
this._init();
}
Overlays.$inject = [
'config.overlays',
'eventBus',
'canvas',
'elementRegistry'
];
module.exports = Overlays;
/**
* Returns the overlay with the specified id or a list of overlays
* for an element with a given type.
*
* @example
*
* // return the single overlay with the given id
* overlays.get('some-id');
*
* // return all overlays for the shape
* overlays.get({ element: someShape });
*
* // return all overlays on shape with type 'badge'
* overlays.get({ element: someShape, type: 'badge' });
*
* // shape can also be specified as id
* overlays.get({ element: 'element-id', type: 'badge' });
*
*
* @param {Object} search
* @param {String} [search.id]
* @param {String|djs.model.Base} [search.element]
* @param {String} [search.type]
*
* @return {Object|Array} the overlay(s)
*/
Overlays.prototype.get = function(search) {
if (isString(search)) {
search = { id: search };
}
if (isString(search.element)) {
search.element = this._elementRegistry.get(search.element);
}
if (search.element) {
var container = this._getOverlayContainer(search.element, true);
// return a list of overlays when searching by element (+type)
if (container) {
return search.type ? filter(container.overlays, { type: search.type }) : container.overlays.slice();
} else {
return [];
}
} else
if (search.type) {
return filter(this._overlays, { type: search.type });
} else {
// return single element when searching by id
return search.id ? this._overlays[search.id] : null;
}
};
/**
* Adds a HTML overlay to an element.
*
* @param {String|djs.model.Base} element attach overlay to this shape
* @param {String} [type] optional type to assign to the overlay
* @param {Object} overlay the overlay configuration
*
* @param {String|DOMElement} overlay.html html element to use as an overlay
* @param {Object} [overlay.show] show configuration
* @param {Number} [overlay.show.minZoom] minimal zoom level to show the overlay
* @param {Number} [overlay.show.maxZoom] maximum zoom level to show the overlay
* @param {Object} overlay.position where to attach the overlay
* @param {Number} [overlay.position.left] relative to element bbox left attachment
* @param {Number} [overlay.position.top] relative to element bbox top attachment
* @param {Number} [overlay.position.bottom] relative to element bbox bottom attachment
* @param {Number} [overlay.position.right] relative to element bbox right attachment
* @param {Boolean|Object} [overlay.scale=true] false to preserve the same size regardless of
* diagram zoom
* @param {Number} [overlay.scale.min]
* @param {Number} [overlay.scale.max]
*
* @return {String} id that may be used to reference the overlay for update or removal
*/
Overlays.prototype.add = function(element, type, overlay) {
if (isObject(type)) {
overlay = type;
type = null;
}
if (!element.id) {
element = this._elementRegistry.get(element);
}
if (!overlay.position) {
throw new Error('must specifiy overlay position');
}
if (!overlay.html) {
throw new Error('must specifiy overlay html');
}
if (!element) {
throw new Error('invalid element specified');
}
var id = this._ids.next();
overlay = assign({}, this._overlayDefaults, overlay, {
id: id,
type: type,
element: element,
html: overlay.html
});
this._addOverlay(overlay);
return id;
};
/**
* Remove an overlay with the given id or all overlays matching the given filter.
*
* @see Overlays#get for filter options.
*
* @param {String} [id]
* @param {Object} [filter]
*/
Overlays.prototype.remove = function(filter) {
var overlays = this.get(filter) || [];
if (!isArray(overlays)) {
overlays = [ overlays ];
}
var self = this;
forEach(overlays, function(overlay) {
var container = self._getOverlayContainer(overlay.element, true);
if (overlay) {
domRemove(overlay.html);
domRemove(overlay.htmlContainer);
delete overlay.htmlContainer;
delete overlay.element;
delete self._overlays[overlay.id];
}
if (container) {
var idx = container.overlays.indexOf(overlay);
if (idx !== -1) {
container.overlays.splice(idx, 1);
}
}
});
};
Overlays.prototype.show = function() {
setVisible(this._overlayRoot);
};
Overlays.prototype.hide = function() {
setVisible(this._overlayRoot, false);
};
Overlays.prototype.clear = function() {
this._overlays = {};
this._overlayContainers = [];
domClear(this._overlayRoot);
};
Overlays.prototype._updateOverlayContainer = function(container) {
var element = container.element,
html = container.html;
// update container left,top according to the elements x,y coordinates
// this ensures we can attach child elements relative to this container
var x = element.x,
y = element.y;
if (element.waypoints) {
var bbox = getBBox(element);
x = bbox.x;
y = bbox.y;
}
setPosition(html, x, y);
domAttr(container.html, 'data-container-id', element.id);
};
Overlays.prototype._updateOverlay = function(overlay) {
var position = overlay.position,
htmlContainer = overlay.htmlContainer,
element = overlay.element;
// update overlay html relative to shape because
// it is already positioned on the element
// update relative
var left = position.left,
top = position.top;
if (position.right !== undefined) {
var width;
if (element.waypoints) {
width = getBBox(element).width;
} else {
width = element.width;
}
left = position.right * -1 + width;
}
if (position.bottom !== undefined) {
var height;
if (element.waypoints) {
height = getBBox(element).height;
} else {
height = element.height;
}
top = position.bottom * -1 + height;
}
setPosition(htmlContainer, left || 0, top || 0);
};
Overlays.prototype._createOverlayContainer = function(element) {
var html = domify('
');
this._overlayRoot.appendChild(html);
var container = {
html: html,
element: element,
overlays: []
};
this._updateOverlayContainer(container);
this._overlayContainers.push(container);
return container;
};
Overlays.prototype._updateRoot = function(viewbox) {
var scale = viewbox.scale || 1;
var matrix = 'matrix(' +
[
scale,
0,
0,
scale,
-1 * viewbox.x * scale,
-1 * viewbox.y * scale
].join(',') +
')';
setTransform(this._overlayRoot, matrix);
};
Overlays.prototype._getOverlayContainer = function(element, raw) {
var container = find(this._overlayContainers, function(c) {
return c.element === element;
});
if (!container && !raw) {
return this._createOverlayContainer(element);
}
return container;
};
Overlays.prototype._addOverlay = function(overlay) {
var id = overlay.id,
element = overlay.element,
html = overlay.html,
htmlContainer,
overlayContainer;
// unwrap jquery (for those who need it)
if (html.get && html.constructor.prototype.jquery) {
html = html.get(0);
}
// create proper html elements from
// overlay HTML strings
if (isString(html)) {
html = domify(html);
}
overlayContainer = this._getOverlayContainer(element);
htmlContainer = domify('');
htmlContainer.appendChild(html);
if (overlay.type) {
domClasses(htmlContainer).add('djs-overlay-' + overlay.type);
}
overlay.htmlContainer = htmlContainer;
overlayContainer.overlays.push(overlay);
overlayContainer.html.appendChild(htmlContainer);
this._overlays[id] = overlay;
this._updateOverlay(overlay);
this._updateOverlayVisibilty(overlay, this._canvas.viewbox());
};
Overlays.prototype._updateOverlayVisibilty = function(overlay, viewbox) {
var show = overlay.show,
minZoom = show && show.minZoom,
maxZoom = show && show.maxZoom,
htmlContainer = overlay.htmlContainer,
visible = true;
if (show) {
if (
(isDef(minZoom) && minZoom > viewbox.scale) ||
(isDef(maxZoom) && maxZoom < viewbox.scale)
) {
visible = false;
}
setVisible(htmlContainer, visible);
}
this._updateOverlayScale(overlay, viewbox);
};
Overlays.prototype._updateOverlayScale = function(overlay, viewbox) {
var shouldScale = overlay.scale,
minScale,
maxScale,
htmlContainer = overlay.htmlContainer;
var scale, transform = '';
if (shouldScale !== true) {
if (shouldScale === false) {
minScale = 1;
maxScale = 1;
} else {
minScale = shouldScale.min;
maxScale = shouldScale.max;
}
if (isDef(minScale) && viewbox.scale < minScale) {
scale = (1 / viewbox.scale || 1) * minScale;
}
if (isDef(maxScale) && viewbox.scale > maxScale) {
scale = (1 / viewbox.scale || 1) * maxScale;
}
}
if (isDef(scale)) {
transform = 'scale(' + scale + ',' + scale + ')';
}
setTransform(htmlContainer, transform);
};
Overlays.prototype._updateOverlaysVisibilty = function(viewbox) {
var self = this;
forEach(this._overlays, function(overlay) {
self._updateOverlayVisibilty(overlay, viewbox);
});
};
Overlays.prototype._init = function() {
var eventBus = this._eventBus;
var self = this;
// scroll/zoom integration
function updateViewbox(viewbox) {
self._updateRoot(viewbox);
self._updateOverlaysVisibilty(viewbox);
self.show();
}
eventBus.on('canvas.viewbox.changing', function(event) {
self.hide();
});
eventBus.on('canvas.viewbox.changed', function(event) {
updateViewbox(event.viewbox);
});
// remove integration
eventBus.on([ 'shape.remove', 'connection.remove' ], function(e) {
var element = e.element;
var overlays = self.get({ element: element });
forEach(overlays, function(o) {
self.remove(o.id);
});
var container = self._getOverlayContainer(element);
if (container) {
domRemove(container.html);
var i = self._overlayContainers.indexOf(container);
if (i !== -1) {
self._overlayContainers.splice(i, 1);
}
}
});
// move integration
eventBus.on('element.changed', LOW_PRIORITY, function(e) {
var element = e.element;
var container = self._getOverlayContainer(element, true);
if (container) {
forEach(container.overlays, function(overlay) {
self._updateOverlay(overlay);
});
self._updateOverlayContainer(container);
}
});
// marker integration, simply add them on the overlays as classes, too.
eventBus.on('element.marker.update', function(e) {
var container = self._getOverlayContainer(e.element, true);
if (container) {
domClasses(container.html)[e.add ? 'add' : 'remove'](e.marker);
}
});
// clear overlays with diagram
eventBus.on('diagram.clear', this.clear, this);
};
},{"266":266,"270":270,"301":301,"302":302,"303":303,"422":422,"426":426,"428":428,"431":431,"444":444,"445":445,"446":446,"449":449,"453":453}],211:[function(_dereq_,module,exports){
module.exports = {
__init__: [ 'overlays' ],
overlays: [ 'type', _dereq_(210) ]
};
},{"210":210}],212:[function(_dereq_,module,exports){
'use strict';
var isFunction = _dereq_(423),
isArray = _dereq_(422),
forEach = _dereq_(303);
var domify = _dereq_(449),
domQuery = _dereq_(452),
domAttr = _dereq_(444),
domClear = _dereq_(446),
domClasses = _dereq_(445),
domMatches = _dereq_(451),
domDelegate = _dereq_(448),
domEvent = _dereq_(450);
var toggleSelector = '.djs-palette-toggle',
entrySelector = '.entry',
elementSelector = toggleSelector + ', ' + entrySelector;
/**
* A palette containing modeling elements.
*/
function Palette(eventBus, canvas, dragging) {
this._eventBus = eventBus;
this._canvas = canvas;
this._dragging = dragging;
this._providers = [];
var self = this;
eventBus.on('tool-manager.update', function(event) {
var tool = event.tool;
self.updateToolHighlight(tool);
});
eventBus.on('i18n.changed', function() {
self._update();
});
}
Palette.$inject = [ 'eventBus', 'canvas', 'dragging' ];
module.exports = Palette;
/**
* Register a provider with the palette
*
* @param {PaletteProvider} provider
*/
Palette.prototype.registerProvider = function(provider) {
this._providers.push(provider);
if (!this._container) {
this._init();
}
this._update();
};
/**
* Returns the palette entries for a given element
*
* @return {Array
} list of entries
*/
Palette.prototype.getEntries = function() {
var entries = {};
// loop through all providers and their entries.
// group entries by id so that overriding an entry is possible
forEach(this._providers, function(provider) {
var e = provider.getPaletteEntries();
forEach(e, function(entry, id) {
entries[id] = entry;
});
});
return entries;
};
/**
* Initialize
*/
Palette.prototype._init = function() {
var canvas = this._canvas,
eventBus = this._eventBus;
var parent = canvas.getContainer(),
container = this._container = domify(Palette.HTML_MARKUP),
self = this;
parent.appendChild(container);
domDelegate.bind(container, elementSelector, 'click', function(event) {
var target = event.delegateTarget;
if (domMatches(target, toggleSelector)) {
return self.toggle();
}
self.trigger('click', event);
});
// prevent drag propagation
domEvent.bind(container, 'mousedown', function(event) {
event.stopPropagation();
});
// prevent drag propagation
domDelegate.bind(container, entrySelector, 'dragstart', function(event) {
self.trigger('dragstart', event);
});
eventBus.fire('palette.create', {
html: container
});
eventBus.on('canvas.resized', this.triggerTwoColumn, this);
};
Palette.prototype._update = function() {
var entriesContainer = domQuery('.djs-palette-entries', this._container),
entries = this._entries = this.getEntries();
domClear(entriesContainer);
forEach(entries, function(entry, id) {
var grouping = entry.group || 'default';
var container = domQuery('[data-group=' + grouping + ']', entriesContainer);
if (!container) {
container = domify('
');
entriesContainer.appendChild(container);
}
var html = entry.html || (
entry.separator ?
' ' :
'
');
var control = domify(html);
container.appendChild(control);
if (!entry.separator) {
domAttr(control, 'data-action', id);
if (entry.title) {
domAttr(control, 'title', entry.title);
}
if (entry.className) {
addClasses(control, entry.className);
}
if (entry.imageUrl) {
control.appendChild(domify(' '));
}
}
});
// open after update
this.open(true);
};
/**
* Trigger an action available on the palette
*
* @param {String} action
* @param {Event} event
*/
Palette.prototype.trigger = function(action, event, autoActivate) {
var entries = this._entries,
entry,
handler,
originalEvent,
button = event.delegateTarget || event.target;
if (!button) {
return event.preventDefault();
}
entry = entries[domAttr(button, 'data-action')];
// when user clicks on the palette and not on an action
if (!entry) {
return;
}
handler = entry.action;
originalEvent = event.originalEvent || event;
// simple action (via callback function)
if (isFunction(handler)) {
if (action === 'click') {
handler(originalEvent, autoActivate);
}
} else {
if (handler[action]) {
handler[action](originalEvent, autoActivate);
}
}
// silence other actions
event.preventDefault();
};
Palette.prototype.triggerTwoColumn = function() {
var canvas = this._canvas;
var parent = canvas.getContainer();
if (parent.clientHeight < 650) {
domClasses(parent).add('two-column');
} else {
domClasses(parent).remove('two-column');
}
};
/**
* Close the palette
*/
Palette.prototype.close = function() {
var canvas = this._canvas;
var parent = canvas.getContainer();
domClasses(this._container).remove('open');
domClasses(parent).remove('two-column');
};
/**
* Open the palette
*/
Palette.prototype.open = function() {
domClasses(this._container).add('open');
this.triggerTwoColumn();
};
Palette.prototype.toggle = function(open) {
if (this.isOpen()) {
this.close();
} else {
this.open();
}
};
Palette.prototype.isActiveTool = function(tool) {
return tool && this._activeTool === tool;
};
Palette.prototype.updateToolHighlight = function(name) {
var entriesContainer,
toolsContainer;
if (!this._toolsContainer) {
entriesContainer = domQuery('.djs-palette-entries', this._container);
this._toolsContainer = domQuery('[data-group=tools]', entriesContainer);
}
toolsContainer = this._toolsContainer;
forEach(toolsContainer.children, function(tool) {
var actionName = tool.getAttribute('data-action');
if (!actionName) {
return;
}
actionName = actionName.replace('-tool', '');
if (tool.classList.contains('entry') && actionName === name) {
domClasses(tool).add('highlighted-entry');
} else {
domClasses(tool).remove('highlighted-entry');
}
});
};
/**
* Return true if the palette is opened.
*
* @example
*
* palette.open();
*
* if (palette.isOpen()) {
* // yes, we are open
* }
*
* @return {boolean} true if palette is opened
*/
Palette.prototype.isOpen = function() {
return this._container && domClasses(this._container).has('open');
};
/* markup definition */
Palette.HTML_MARKUP =
'';
////////// helpers /////////////////////////////
function addClasses(element, classNames) {
var classes = domClasses(element);
var actualClassNames = isArray(classNames) ? classNames : classNames.split(/\s+/g);
actualClassNames.forEach(function(cls) {
classes.add(cls);
});
}
},{"303":303,"422":422,"423":423,"444":444,"445":445,"446":446,"448":448,"449":449,"450":450,"451":451,"452":452}],213:[function(_dereq_,module,exports){
'use strict';
module.exports = {
__depends__: [ _dereq_(242) ],
__init__: [ 'palette' ],
palette: [ 'type', _dereq_(212) ]
};
},{"212":212,"242":242}],214:[function(_dereq_,module,exports){
'use strict';
var forEach = _dereq_(303),
assign = _dereq_(431),
find = _dereq_(302);
var domDelegate = _dereq_(448),
domify = _dereq_(449),
domClasses = _dereq_(445),
domAttr = _dereq_(444),
domRemove = _dereq_(453);
var DATA_REF = 'data-id';
/**
* A popup menu that can be used to display a list of actions anywhere in the canvas.
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
*
* @class
* @constructor
*/
function PopupMenu(eventBus, canvas) {
this._eventBus = eventBus;
this._canvas = canvas;
this._providers = {};
}
PopupMenu.$inject = [ 'eventBus', 'canvas' ];
/**
* Registers a popup menu provider
*
* @param {String} id
* @param {Object} provider
*
* @example
* popupMenu.registerProvider('myMenuID', {
* getEntries: function(element) {
* return [
* {
* id: 'entry-1',
* label: 'My Entry',
* action: 'alert("I have been clicked!")'
* }
* ];
* }
* });
* })
*/
PopupMenu.prototype.registerProvider = function(id, provider) {
this._providers[id] = provider;
};
/**
* Create a popup menu according to a given element. The id refers to the ID
* of the provider that must be registered before.
*
* @param {String} id provider id
* @param {Object} element
*
* @return {PopupMenu} popup menu instance
*/
PopupMenu.prototype.create = function(id, element) {
var provider = this._providers[id];
if (!provider) {
throw new Error('Provider is not registered: ' + id);
}
if (!element) {
throw new Error('Element is missing');
}
var current = this._current = {
provider: provider,
className: id,
element: element
};
if (provider.getHeaderEntries) {
current.headerEntries = provider.getHeaderEntries(element);
}
current.entries = provider.getEntries(element);
return this;
};
/**
* Determine if the popup menu has entries.
*
* @return {Boolean} true if empty
*/
PopupMenu.prototype.isEmpty = function() {
var current = this._current;
return current.entries.length === 0 && current.headerEntries && current.headerEntries.length === 0;
};
/**
* Open popup menu at given position
*
* @param {Object} position
*
* @return {Object} popup menu instance
*/
PopupMenu.prototype.open = function(position) {
if (!position) {
throw new Error('the position argument is missing');
}
// make sure, only one popup menu is open at a time
if (this.isOpen()) {
this.close();
}
var current = this._current,
canvas = this._canvas,
parent = canvas.getContainer();
current.position = position;
current.container = this._createContainer();
if (current.headerEntries) {
var headerEntriesContainer = this._createEntries(current.headerEntries, 'djs-popup-header');
current.container.appendChild(headerEntriesContainer);
}
if (current.entries) {
var entriesContainer = this._createEntries(current.entries, 'djs-popup-body');
current.container.appendChild(entriesContainer);
}
this._attachContainer(current.container, parent, position.cursor);
return this;
};
/**
* Removes the popup menu and unbinds the event handlers.
*/
PopupMenu.prototype.close = function() {
if (!this.isOpen()) {
return;
}
this._unbindHandlers();
domRemove(this._current.container);
this._current.container = null;
};
/**
* Determine if an open popup menu exist.
*
* @return {Boolean} true if open
*/
PopupMenu.prototype.isOpen = function() {
return !!this._current.container;
};
/**
* Trigger an action associated with an entry.
*
* @param {Object} event
*
* @return the result of the action callback, if any
*/
PopupMenu.prototype.trigger = function(event) {
// silence other actions
event.preventDefault();
var element = event.delegateTarget || event.target,
entryId = domAttr(element, DATA_REF);
var entry = this._getEntry(entryId);
if (entry.action) {
return entry.action.call(null, event, entry);
}
};
/**
* Gets an entry instance (either entry or headerEntry) by id.
*
* @param {String} entryId
*
* @return {Object} entry instance
*/
PopupMenu.prototype._getEntry = function(entryId) {
var search = { id: entryId };
var entry = find(this._current.entries, search) || find(this._current.headerEntries, search);
if (!entry) {
throw new Error('entry not found');
}
return entry;
};
/**
* Creates the popup menu container.
*
* @return {Object} a DOM container
*/
PopupMenu.prototype._createContainer = function() {
var container = domify('