Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

diagram-js

Package Overview
Dependencies
Maintainers
2
Versions
287
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

diagram-js - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

lib/cmd/CommandStack.js

3

Gruntfile.js

@@ -53,3 +53,4 @@ module.exports = function(grunt) {

watch: false,
debug: true
debug: true,
transform: [ 'brfs' ]
}

@@ -56,0 +57,0 @@ },

@@ -6,10 +6,15 @@ 'use strict';

var AddShapeHandler = require('./cmd/AddShapeHandler'),
AddConnectionHandler = require('./cmd/AddConnectionHandler');
var Model = require('../model');
var remove = require('../util/Collections').remove;
/**
* @type djs.ShapeDescriptor
*/
function round(number, resolution) {
return Math.round(number * resolution) / resolution;
}
function ensurePx(number) {
return _.isNumber(number) ? number + 'px' : number;
}
/**

@@ -20,3 +25,3 @@ * Creates a HTML container element for a SVG element with

* @param {Object} options
* @return {DOMElement} the container element
* @return {HTMLElement} the container element
*/

@@ -35,5 +40,7 @@ function createContainer(options) {

parent.style.position = 'relative';
parent.style.width = _.isNumber(options.width) ? options.width + 'px' : options.width;
parent.style.height = _.isNumber(options.height) ? options.height + 'px' : options.height;
_.extend(parent.style, {
position: 'relative',
width: ensurePx(options.width),
height: ensurePx(options.height)
});

@@ -52,11 +59,14 @@ container.appendChild(parent);

* @param {Object} config
* @param {EventBus} events
* @param {CommandStack} commandStack
* @param {EventBus} eventBus
* @param {GraphicsFactory} graphicsFactory
* @param {ElementRegistry} elementRegistry
*/
function Canvas(config, events, commandStack, graphicsFactory, elementRegistry) {
function Canvas(config, eventBus, graphicsFactory, elementRegistry, snap) {
var options = _.extend(config.canvas || {});
this._snap = snap;
this._eventBus = eventBus;
this._elementRegistry = elementRegistry;
this._graphicsFactory = graphicsFactory;

@@ -75,6 +85,11 @@ // Creates a <svg> element that is wrapped into a <div>.

var container = createContainer(options);
var paper = createPaper(container);
var container = this._container = createContainer(options);
// svg root
var paper = this._paper = createPaper(container);
// drawing root
var root = this._root = paper.group().attr({ 'class' : 'viewport' });
function createPaper(container) {

@@ -85,86 +100,2 @@ return graphicsFactory.createPaper({ container: container, width: '100%', height: '100%' });

/**
* Validate the id of an element, ensuring it is present and not yet assigned
*/
function validateId(element) {
if (!element.id) {
throw new Error('element must have an id');
}
if (elementRegistry.getById(element.id)) {
throw new Error('element with id ' + element.id + ' already exists');
}
}
// register shape add handlers
commandStack.registerHandler('shape.add', AddShapeHandler);
// register connection add handlers
commandStack.registerHandler('connection.add', AddConnectionHandler);
/**
* Adds a shape to the canvas
*
* @method Canvas#addShape
*
* @param {djs.ShapeDescriptor} shape a descriptor for the shape
*
* @return {Canvas} the canvas api
*/
function addShape(shape) {
validateId(shape);
/**
* An event indicating that a new shape has been added to the canvas.
*
* @memberOf Canvas
*
* @event shape.added
* @type {Object}
* @property {djs.ElementDescriptor} element the shape descriptor
* @property {Object} gfx the graphical representation of the shape
*/
commandStack.execute('shape.add', { shape: shape });
/* jshint -W040 */
return this;
}
/**
* Adds a connection to the canvas
*
* @method Canvas#addConnection
*
* @param {djs.ElementDescriptor} connection a descriptor for the connection
*
* @return {Canvas} the canvas api
*/
function addConnection(connection) {
validateId(connection);
/**
* An event indicating that a new connection has been added to the canvas.
*
* @memberOf Canvas
*
* @event connection.added
* @type {Object}
* @property {djs.ElementDescriptor} element the connection descriptor
* @property {Object} gfx the graphical representation of the connection
*/
commandStack.execute('connection.add', { connection: connection });
/* jshint -W040 */
return this;
}
/**
* Sends a shape to the front.

@@ -214,257 +145,479 @@ *

/**
* Returns the underlaying graphics context.
* Returns the root rendering context on which
* all elements have to be drawn.
*
* @method Canvas#getPaper
* @method Canvas#getRoot
*
* @returns {snapsvg.Paper} the global paper object
* @returns {snapsvg.Group}
*/
function getPaper() {
return paper;
function getRoot() {
return root;
}
/**
* Returns the size of the canvas
*
* @return {Object} with x/y coordinates
*/
function getSize() {
eventBus.on('diagram.init', function(event) {
return {
width: container.clientWidth,
height: container.clientHeight
};
}
/**
* An event indicating that the canvas is ready to be drawn on.
*
* @memberOf Canvas
*
* @event canvas.init
*
* @type {Object}
* @property {snapsvg.Paper} paper the initialized drawing paper
*/
eventBus.fire('canvas.init', { root: root, paper: paper });
});
function parseViewBox(str) {
if (!str) {
return;
eventBus.on('diagram.destroy', function() {
if (container) {
var parent = container.parentNode;
parent.removeChild(container);
}
var value = str.split(/\s/);
container = this._container = null;
paper = this._paper = null;
root = this._root = null;
});
return {
x: parseInt(value[0], 10),
y: parseInt(value[1], 10),
width: parseInt(value[2], 10),
height: parseInt(value[3], 10),
};
}
/**
* Gets or sets the view box of the canvas, i.e. the area that is currently displayed
*
* @method Canvas#viewbox
*
* @param {Object} [box] the new view box to set
* @return {Object} the current view box
*/
function viewbox(box) {
// redraw shapes / connections on change
var svg = paper.node,
bbox = svg.getBBox();
var self = this;
function round(i, accuracy) {
if (!i) {
return i;
}
eventBus.on('element.changed', function(event) {
accuracy = accuracy || 100;
return Math.round(i * accuracy) / accuracy;
if (event.element.waypoints) {
eventBus.fire('connection.changed', event);
} else {
eventBus.fire('shape.changed', event);
}
});
var inner = {
width: round(bbox.width + bbox.x),
height: round(bbox.height + bbox.y)
};
eventBus.on('shape.changed', function(event) {
var element = event.element;
graphicsFactory.updateShape(element, event.gfx || self.getGraphics(element));
});
// returns the acutal embedded size of the SVG element
// would be awesome to be able to use svg.client(Width|Height) or
// svg.getBoundingClientRect(). Not supported in IE/Firefox though
var outer = getSize(svg);
eventBus.on('connection.changed', function(event) {
var element = event.element;
graphicsFactory.updateConnection(element, event.gfx || self.getGraphics(element));
});
if (box === undefined) {
box = parseViewBox(svg.getAttribute('viewBox'));
this.getRoot = getRoot;
if (!box) {
box = { x: 0, y: 0, width: outer.width, height: outer.height };
}
this.getContainer = function() {
return this._container;
};
// calculate current scale based on svg bbox (inner) and viewbox (outer)
box.scale = round(Math.min(outer.width / box.width, outer.height / box.height));
this.getGraphics = getGraphics;
box.inner = inner;
box.outer = outer;
} else {
svg.setAttribute('viewBox', [ box.x, box.y, box.width, box.height ].join(' '));
events.fire('canvas.viewbox.changed', { viewbox: viewbox() });
}
this.sendToFront = sendToFront;
}
return box;
/**
* 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
*/
Canvas.prototype.create = function(type, attrs) {
return Model.create(type, attrs);
};
/**
* Ensure that an element has a valid, unique id
*
* @param {djs.model.Base} element
*/
Canvas.prototype._ensureValidId = function(element) {
if (!element.id) {
throw new Error('element must have an id');
}
if (this._elementRegistry.getById(element.id)) {
throw new Error('element with id ' + element.id + ' already exists');
}
};
/**
* Adds a shape to the canvas
*
* @method Canvas#addShape
*
* @param {Object|djs.model.Shape} shape to add to the diagram
*
* @return {djs.model.Shape} the added shape
*/
Canvas.prototype.addShape = function(shape, parent) {
this._ensureValidId(shape);
if (parent) {
shape.parent = parent;
parent.children.push(shape);
}
// create shape gfx
var gfx = this._graphicsFactory.createShape(this._root, shape);
// update its visual
this._graphicsFactory.updateShape(shape, gfx);
/**
* Gets or sets the scroll of the canvas.
* An event indicating that a new shape has been added to the canvas.
*
* @param {Object} [delta] the new scroll to apply.
* @memberOf Canvas
*
* @param {Number} [delta.dx]
* @param {Number} [delta.dy]
* @event shape.added
* @type {Object}
* @property {djs.Shape} element the shape descriptor
* @property {Object} gfx the graphical representation of the shape
*/
this._eventBus.fire('shape.added', { element: shape, gfx: gfx });
return shape;
};
/**
* Adds a connection to the canvas
*
* @method Canvas#addConnection
*
* @param {djs.model.Connection} connection to add to the diagram
*
* @return {djs.model.Connection} the added connection
*/
Canvas.prototype.addConnection = function(connection, parent) {
this._ensureValidId(connection);
if (parent) {
connection.parent = parent;
parent.children.push(connection);
}
// create connection gfx
var gfx = this._graphicsFactory.createConnection(this._root, connection);
// update its visual
this._graphicsFactory.updateConnection(connection, gfx);
/**
* An event indicating that a new connection has been added to the canvas.
*
* @return {Point} the new scroll
* @memberOf Canvas
*
* @event connection.added
* @type {Object}
* @property {djs.ElementDescriptor} element the connection descriptor
* @property {Object} gfx the graphical representation of the connection
*/
function scroll(delta) {
this._eventBus.fire('connection.added', { element: connection, gfx: gfx });
var vbox = viewbox();
return connection;
};
if (delta) {
if (delta.dx) {
vbox.x += delta.dx;
}
if (delta.dy) {
vbox.y += delta.dy;
}
/**
* Internal remove element
*/
Canvas.prototype._removeElement = function(element, type) {
viewbox(vbox);
}
if (_.isString(element)) {
element = this._elementRegistry.getById(element);
}
return { x: vbox.x, y: vbox.y };
var gfx = this._elementRegistry.getGraphicsByElement(element);
this._eventBus.fire(type + '.remove', { element: element, gfx: gfx });
if (gfx) {
gfx.remove();
}
remove(element.parent && element.parent.children, element);
element.parent = null;
this._eventBus.fire(type + '.removed', { element: element, gfx: gfx });
return element;
};
/**
* Removes a shape from the canvas
*
* @method Canvas#removeShape
*
* @param {String|djs.model.Shape} shape or shape id to be removed
*
* @return {djs.model.Shape} the removed shape
*/
Canvas.prototype.removeShape = function(shape) {
/**
* Gets or sets the current zoom of the canvas, optionally zooming to the specified position.
* An event indicating that a shape is about to be removed from the canvas.
*
* @method Canvas#zoom
* @memberOf Canvas
*
* @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
* @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.
*
* @return {Number} the current scale
* @memberOf Canvas
*
* @event shape.removed
* @type {Object}
* @property {djs.model.Shape} element the shape descriptor
* @property {Object} gfx the graphical representation of the shape
*/
function zoom(newScale, center) {
return this._removeElement(shape, 'shape');
};
var vbox = viewbox(),
inner = vbox.inner,
outer = vbox.outer;
/**
* Removes a connection from the canvas
*
* @method Canvas#removeConnection
*
* @param {String|djs.model.Connection} connection or connection id to be removed
*
* @return {djs.model.Connection} the removed connection
*/
Canvas.prototype.removeConnection = function(connection) {
if (newScale === undefined) {
return vbox.scale;
}
/**
* 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
*/
if (newScale === 'fit-viewport') {
newScale = Math.min(outer.width / inner.width, outer.height / inner.height, 1.0);
/**
* 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');
};
// reset viewbox so that everything is visible
_.extend(vbox, { x: 0, y: 0 });
}
if (center === 'auto') {
center = {
x: outer.width / 2 - 1,
y: outer.height / 2 - 1
};
}
Canvas.prototype._fireViewboxChange = function(viewbox) {
this._eventBus.fire('canvas.viewbox.changed', { viewbox: viewbox || this.viewbox() });
};
if (center) {
/**
* Gets or sets the view box of the canvas, i.e. the area that is currently displayed
*
* @method Canvas#viewbox
*
* @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
*
* @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
*
* @return {Object} the current view box
*/
Canvas.prototype.viewbox = function(box) {
// zoom to center (i.e. simulate a maps like behavior)
var root = this._root,
eventBus = this._eventBus;
// center on old zoom
var pox = center.x / vbox.scale + vbox.x;
var poy = center.y / vbox.scale + vbox.y;
var innerBox,
outerBox = this.getSize(),
matrix,
scale,
x, y,
width, height;
// center on new zoom
var pnx = center.x / newScale;
var pny = center.y / newScale;
if (!box) {
innerBox = root.getBBox(true);
// delta = new offset
var px = pox - pnx;
var py = poy - pny;
matrix = root.transform().localMatrix;
scale = round(matrix.a, 1000);
var position = {
x: px,
y: py
};
x = round(-matrix.e || 0, 1000);
y = round(-matrix.f || 0, 1000);
_.extend(vbox, position);
}
return {
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
},
outer: outerBox
};
} else {
scale = Math.max(outerBox.width / box.width, outerBox.height / box.height);
_.extend(vbox, {
width: outer.width / newScale,
height: outer.height / newScale
});
matrix = new this._snap.Matrix().scale(scale).translate(-box.x, -box.y);
root.transform(matrix);
viewbox(vbox);
// return current scale
return newScale;
this._fireViewboxChange();
}
events.on('diagram.init', function(event) {
return box;
};
/**
* An event indicating that the canvas is ready to be drawn on.
*
* @memberOf Canvas
*
* @event canvas.init
*
* @type {Object}
* @property {snapsvg.Paper} paper the initialized drawing paper
*/
events.fire('canvas.init', { paper: paper });
});
events.on('diagram.destroy', function() {
/**
* 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) {
if (container) {
var parent = container.parentNode;
parent.removeChild(container);
}
var node = this._root.node;
var matrix = node.getCTM();
container = null;
paper = null;
});
if (delta) {
delta = _.extend({ dx: 0, dy: 0 }, delta || {});
matrix = this._paper.node.createSVGMatrix().translate(delta.dx, delta.dy).multiply(matrix);
// redraw shapes / connections on change
setCTM(node, matrix);
var self = this;
this._fireViewboxChange();
}
events.on('element.changed', function(event) {
return { x: matrix.e, y: matrix.f };
};
if (event.element.waypoints) {
events.fire('connection.changed', event);
} else {
events.fire('shape.changed', event);
}
});
events.on('shape.changed', function(event) {
var element = event.element;
graphicsFactory.updateShape(element, event.gfx || self.getGraphics(element));
});
/**
* Gets or sets the current zoom of the canvas, optionally zooming to the specified position.
*
* @method Canvas#zoom
*
* @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) {
events.on('connection.changed', function(event) {
var element = event.element;
graphicsFactory.updateConnection(element, event.gfx || self.getGraphics(element));
});
var snap = this._snap;
var vbox = this.viewbox();
this.zoom = zoom;
this.scroll = scroll;
if (newScale === undefined) {
return vbox.scale;
}
this.viewbox = viewbox;
this.addShape = addShape;
var outer = vbox.outer;
this.addConnection = addConnection;
this.getPaper = getPaper;
if (newScale === 'fit-viewport') {
newScale = Math.min(1, outer.width / vbox.inner.width);
}
this.getGraphics = getGraphics;
if (center === 'auto') {
center = {
x: outer.width / 2,
y: outer.height / 2
};
}
this.sendToFront = sendToFront;
var matrix = this._setZoom(newScale, center);
this._fireViewboxChange();
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._setZoom = function(scale, center) {
var svg = this._paper.node,
viewport = this._root.node;
var matrix = svg.createSVGMatrix();
var point = svg.createSVGPoint();
var centerPoint,
originalPoint,
currentMatrix,
scaleMatrix,
newMatrix;
currentMatrix = viewport.getCTM();
var currentScale = currentMatrix.a;
if (center) {
centerPoint = _.extend(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._root.node, 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

@@ -504,6 +657,6 @@ *

'eventBus',
'commandStack',
'graphicsFactory',
'elementRegistry' ];
'elementRegistry',
'snap' ];
module.exports = Canvas;

@@ -15,4 +15,4 @@ 'use strict';

// mapping shape.id -> container
var shapeMap = {};
// mapping element.id -> container
var elementMap = {};

@@ -22,9 +22,10 @@ // mapping gfx.id -> container

function addShape(shape, gfx) {
if (!shape.id) {
throw new Error('[shapes] shape has no id');
function add(element, gfx) {
if (!element.id) {
throw new Error('element has no id');
}
if (!gfx.id) {
throw new Error('[shapes] graphics has no id');
throw new Error('graphics has no id');
}

@@ -36,21 +37,13 @@

if (shapeMap[shape.id]) {
throw new Error('shape with id ' + shape.id + ' already added');
if (elementMap[element.id]) {
throw new Error('element with id ' + element.id + ' already added');
}
shapeMap[shape.id] = graphicsMap[gfx.id] = { shape: shape, gfx: gfx };
elementMap[element.id] = graphicsMap[gfx.id] = { element: element, gfx: gfx };
}
function removeShape(shape) {
var gfx = getGraphicsByElement(shape);
function remove(element) {
var gfx = getGraphicsByElement(element);
if (shape.parent) {
for(var i = 0; i < shape.parent.children.length;i++) {
if(shape.parent.children[i].id === shape.id) {
shape.parent.children.splice(i, 1);
}
}
}
// delete shape.parent.children[];
delete shapeMap[shape.id];
delete elementMap[element.id];
delete graphicsMap[gfx.id];

@@ -66,5 +59,3 @@ }

var container = graphicsMap[id];
if (container) {
return container.shape;
}
return container && container.element;
}

@@ -76,6 +67,4 @@

function getById(id) {
var container = shapeMap[id];
if (container) {
return container.shape;
}
var container = elementMap[id];
return container && container.element;
}

@@ -86,25 +75,24 @@

*/
function getGraphicsByElement(shape) {
var id = _.isString(shape) ? shape : shape.id;
function getGraphicsByElement(element) {
var id = _.isString(element) ? element : element.id;
var container = shapeMap[id];
if (container) {
return container.gfx;
}
var container = elementMap[id];
return container && container.gfx;
}
eventBus.on('shape.added', function(event) {
addShape(event.element, event.gfx);
});
eventBus.on('connection.added', function(event) {
addShape(event.element, event.gfx);
});
_.forEach([ 'shape', 'connection' ], function(type) {
eventBus.on(type + '.added', function(event) {
add(event.element, event.gfx);
});
eventBus.on('shape.removed', function(event) {
removeShape(event.element);
eventBus.on(type + '.removed', function(event) {
remove(event.element, event.gfx);
});
});
eventBus.on('connection.removed', function(event) {
removeShape(event.element);
eventBus.on('diagram.destroy', function(event) {
elementMap = null;
graphicsMap = null;
});

@@ -111,0 +99,0 @@

@@ -66,18 +66,22 @@ 'use strict';

*
* @method Events#on
* @method EventBus#on
*
* @param {String} event
* @param {Number} [priority] the priority in which this listener is called,
* defaults to 1000 but may be changed to override execution order of callbacks
* (> {@link EventPriority#overwrite})
*
* @param {Function} callback
* @param {Number} Set priority to influence the execution order of the callbacks.
* The default priority is 1000. It should only set to higher values (> {@link EventPriority#overwrite}) if
* there is real need for a changed execution priority.
*/
function on(event, callback, priority) {
if(priority && !_.isNumber(priority)) {
console.error('Priority needs to be a number');
function on(event, priority, callback) {
if (_.isFunction(priority)) {
callback = priority;
priority = EventPriority.standard;
}
if(!priority) {
priority = EventPriority.standard;
if (!_.isNumber(priority)) {
throw new Error('priority needs to be a number');
}
var listeners = getListeners(event);

@@ -90,3 +94,3 @@ addEventToArray(listeners, callback, priority);

*
* @method Events#once
* @method EventBus#once
*

@@ -96,3 +100,3 @@ * @param {String} event the event name to register for

*
* @see Events#on
* @see EventBus#on
*/

@@ -118,3 +122,3 @@ function once(event, callback) {

*
* @method Events#off
* @method EventBus#off
*

@@ -146,3 +150,3 @@ * @param {String} event

*
* @method Events#fire
* @method EventBus#fire
*

@@ -220,3 +224,3 @@ * @example

array.sort(function(a, b) {
if(a.priority < b.priority) {
if (a.priority < b.priority) {
return 1;

@@ -223,0 +227,0 @@ } else if (a.priority > b.priority) {

@@ -1,3 +0,1 @@

'use strict';
module.exports = {

@@ -7,3 +5,2 @@ __depends__: [ require('../draw') ],

canvas: [ 'type', require('./Canvas') ],
commandStack: [ 'type', require('./CommandStack') ],
elementRegistry: [ 'type', require('./ElementRegistry') ],

@@ -10,0 +7,0 @@ eventBus: [ 'type', require('./EventBus') ],

@@ -175,3 +175,2 @@ 'use strict';

* @type {Object}
* @property {snapsvg.Paper} paper the initialized drawing paper
*/

@@ -178,0 +177,0 @@ this.get('eventBus').fire('diagram.init');

@@ -25,8 +25,10 @@ 'use strict';

function Renderer(styles) {
this.CONNECTION_STYLE = styles.style([ 'no-fill' ]);
this.SHAPE_STYLE = styles.style({ fill: 'fuchsia' });
this.CONNECTION_STYLE = styles.style([ 'no-fill' ], { strokeWidth: 5 });
this.SHAPE_STYLE = styles.style({ fill: 'white', stroke: 'fuchsia', strokeWidth: 2 });
}
Renderer.prototype.drawShape = function drawShape(gfxGroup, data) {
if (!data.width || !data.height) {
if (data.width === undefined ||
data.height === undefined) {
throw new Error('must specify width and height properties for new shape');

@@ -33,0 +35,0 @@ }

@@ -149,3 +149,3 @@ 'use strict';

matrix.translate(x, y);
this.transform(matrix);
return this.transform(matrix);
};

@@ -152,0 +152,0 @@ });

@@ -12,3 +12,3 @@ require('../core/EventBus');

* Provides bendpoint visualization, hover and interactivity.
*
*
* @class

@@ -29,3 +29,3 @@ *

var paper;
var root;

@@ -65,3 +65,3 @@ function makeDraggable(connection, gfx, bendpointGfx) {

dragCtx.dragGroup = dragGroup = paper.group(graphics.clone()).attr('pointer-events', 'none');
dragCtx.dragGroup = dragGroup = root.group(graphics.clone()).attr('pointer-events', 'none');

@@ -72,6 +72,6 @@ /**

* @memberOf Drag
*
*
* @event shape.dragstart
* @type {Object}
*
*
* @property {djs.ElementDescriptor} element the shape descriptor

@@ -97,6 +97,6 @@ * @property {Object} gfx the graphical representation of the shape

* @memberOf Drag
*
*
* @event shape.dragmove
* @type {Object}
*
*
* @property {djs.ElementDescriptor} element the shape descriptor

@@ -132,6 +132,6 @@ * @property {Object} gfx the graphical representation of the shape

* @memberOf Drag
*
*
* @event shape.dragstart
* @type {Object}
*
*
* @property {djs.ElementDescriptor} element the shape descriptor

@@ -156,3 +156,3 @@ * @property {Object} gfx the graphical representation of the shape

var bendpointGfx = paper.circle(p.x, p.y, 10).addClass('djs-bendpoint').appendTo(gfx);
var bendpointGfx = root.circle(p.x, p.y, 10).addClass('djs-bendpoint').appendTo(gfx);

@@ -168,10 +168,10 @@ makeDraggable(element, gfx, bendpointGfx);

// load paper from initialized canvas
// load root from initialized canvas
events.on('canvas.init', function(event) {
paper = event.paper;
root = event.root;
});
events.on('diagram.destroy', function() {
paper = null;
root = null;
});

@@ -178,0 +178,0 @@ }

@@ -1,3 +0,1 @@

'use strict';
module.exports = {

@@ -4,0 +2,0 @@ __init__: [ 'interactionEvents' ],

@@ -14,5 +14,5 @@ 'use strict';

*
* @param {EventBus} events the event bus to attach to
* @param {EventBus} eventBus
*/
function InteractionEvents(events, styles) {
function InteractionEvents(eventBus, styles) {

@@ -31,17 +31,14 @@ var HIT_STYLE = styles.cls('djs-hit', [ 'no-fill', 'no-border' ], {

var e = _.extend({}, baseEvent, event);
events.fire(eventName, e);
eventBus.fire(eventName, e);
}
function makeSelectable(element, gfx, options) {
var dblclick = options.dblclick,
type = options.type;
var type = options.type,
baseEvent = { element: element, gfx: gfx },
visual = GraphicsUtil.getVisual(gfx),
hit,
bbox;
var baseEvent = { element: element, gfx: gfx };
var visual = GraphicsUtil.getVisual(gfx);
var hit;
if (type === 'shape') {
var bbox = visual.getBBox();
bbox = visual.getBBox();
hit = gfx.rect(bbox.x, bbox.y, bbox.width, bbox.height);

@@ -69,7 +66,7 @@ } else {

gfx.click(function(e) {
visual.click(function(e) {
fire(e, baseEvent, type + '.click');
});
gfx.dblclick(function(e) {
visual.dblclick(function(e) {
fire(e, baseEvent, type + '.dblclick');

@@ -79,17 +76,9 @@ });

function makeConnectionSelectable(connection, gfx) {
makeSelectable(connection, gfx, { type: 'connection' });
}
function registerEvents(eventBus) {
function makeShapeSelectable(shape, gfx) {
makeSelectable(shape, gfx, { type: 'shape' });
}
eventBus.on('canvas.init', function(event) {
var root = event.root;
function registerEvents(events) {
events.on('canvas.init', function(event) {
var paper = event.paper;
// implement direct canvas click
paper.click(function(event) {
root.click(function(event) {

@@ -105,16 +94,16 @@ /**

*/
events.fire('canvas.click', _.extend({}, event, { paper: paper }));
eventBus.fire('canvas.click', _.extend({}, event, { root: root }));
});
});
events.on('shape.added', function(event) {
makeShapeSelectable(event.element, event.gfx);
eventBus.on('shape.added', function(event) {
makeSelectable(event.element, event.gfx, { type: 'shape' });
});
events.on('connection.added', function(event) {
makeConnectionSelectable(event.element, event.gfx);
eventBus.on('connection.added', function(event) {
makeSelectable(event.element, event.gfx, { type: 'connection' });
});
}
registerEvents(events);
registerEvents(eventBus);
}

@@ -121,0 +110,0 @@

@@ -23,3 +23,3 @@ 'use strict';

var paper;
var root;

@@ -67,3 +67,3 @@ function getGfx(s) {

var dragGroup = paper.group().attr(styles.cls('djs-drag-group', [ 'no-events']));
var dragGroup = root.group().attr(styles.cls('djs-drag-group', [ 'no-events']));

@@ -139,10 +139,10 @@ var visuallyDraggedShapes = getVisualDragShapes(dragShapes),

// load paper from initialized canvas
// load root from initialized canvas
events.on('canvas.init', function(event) {
paper = event.paper;
root = event.root;
});
events.on('diagram.destroy', function() {
paper = null;
root = null;
});

@@ -149,0 +149,0 @@ }

@@ -77,3 +77,3 @@ 'use strict';

events.on('canvas.click', function(event) {
if (event.srcElement === event.paper.node) {
if (event.srcElement === event.root.node) {
selection.select(null);

@@ -80,0 +80,0 @@ }

@@ -31,5 +31,5 @@ 'use strict';

var paper = canvas.getPaper();
paper.node.addEventListener('mousemove', paletteMoveListener);
paper.node.addEventListener('mouseup', canvasOnMouseUp);
var root = canvas.getRoot();
root.node.addEventListener('mousemove', paletteMoveListener);
root.node.addEventListener('mouseup', canvasOnMouseUp);

@@ -36,0 +36,0 @@ document.addEventListener('mouseup', canvasOnMouseUpAnywhere);

{
"name": "diagram-js",
"version": "0.2.0",
"version": "0.3.0",
"description": "A modeling framework for the web",

@@ -20,2 +20,3 @@ "scripts": {

"devDependencies": {
"brfs": "^1.1.1",
"didi": "~0.0.4",

@@ -30,2 +31,3 @@ "exposify": "https://github.com/Nikku/exposify/archive/v0.2.0-transform-arguments-0.tar.gz",

"grunt-release": "~0.7.0",
"jasmine-test-container-support": "0.0.1",
"jsondiffpatch": "^0.1.7",

@@ -46,6 +48,9 @@ "karma": "~0.12.8",

"dependencies": {
"eve": "0.4.1",
"lodash": "~2.4.0",
"eve": "0.4.1",
"snapsvg": "https://github.com/Nikku/Snap.svg/archive/v0.2.2-browserify.tar.gz"
"jquery": "~2.1.0",
"snapsvg": "https://github.com/Nikku/Snap.svg/archive/v0.2.2-browserify.tar.gz",
"hammerjs": "^2.0.1",
"object-refs": "^0.1.0"
}
}

@@ -25,5 +25,6 @@ module.exports = function(karma) {

browserify: {
debug: true
debug: true,
transform: [ 'brfs' ]
}
});
};

@@ -62,3 +62,3 @@ 'use strict';

describe('addShape', function() {
describe('#addShape', function() {

@@ -127,60 +127,85 @@ beforeEach(defaultBootstrap);

});
describe('undo / redo', function() {
describe('#removeShape', function() {
it('should undo add of shape', inject(function(canvas, commandStack, elementRegistry) {
beforeEach(defaultBootstrap);
// given
var s1 = { id: 's1', x: 10, y: 20, width: 50, height: 50 };
var s2 = { id: 's2', x: 10, y: 20, width: 50, height: 50, parent: s1 };
var s3 = { id: 's3', x: 10, y: 20, width: 50, height: 50, parent: s1 };
canvas
.addShape(s1)
.addShape(s2)
.addShape(s3);
it('should fire <shape.removed> event', inject(function(canvas, eventBus, elementRegistry) {
// when
commandStack.undo();
commandStack.undo();
// given
var listener = createSpy('listener');
eventBus.on('shape.removed', listener);
// then
expect(elementRegistry.getById('s2')).not.toBeDefined();
expect(elementRegistry.getById('s3')).not.toBeDefined();
}));
canvas.addShape({ id: 'a', x: 10, y: 20, width: 50, height: 50 });
// when
var shape = canvas.removeShape('a');
it('should redo add of shape', inject(function(canvas, commandStack, elementRegistry) {
// then
expect(shape.parent).toBeFalsy();
expect(elementRegistry.getById('a')).not.toBeDefined();
// given
var s1 = { id: 's1', x: 10, y: 20, width: 50, height: 50 };
var s2 = { id: 's2', x: 10, y: 20, width: 50, height: 50, parent: s1 };
var s3 = { id: 's3', x: 10, y: 20, width: 50, height: 50, parent: s1 };
expect(listener).toHaveBeenCalled();
}));
canvas
.addShape(s1)
.addShape(s2)
.addShape(s3);
});
// when
commandStack.undo();
commandStack.undo();
commandStack.redo();
commandStack.undo();
commandStack.redo();
commandStack.redo();
// then
expect(elementRegistry.getById('s2')).toEqual(s2);
expect(elementRegistry.getById('s3')).toEqual(s3);
describe('#addConnection', function() {
expect(s2.parent).toEqual(s1);
expect(s3.parent).toEqual(s1);
}));
beforeEach(defaultBootstrap);
});
it('should fire <connection.added> event', inject(function(canvas, eventBus) {
// given
var listener = createSpy('listener');
canvas.addShape({ id: 's1', x: 10, y: 10, width: 30, height: 30 });
canvas.addShape({ id: 's2', x: 100, y: 100, width: 30, height: 30 });
eventBus.on('connection.added', listener);
// when
canvas.addConnection({ id: 'c1', waypoints: [ { x: 25, y: 25 }, {x: 115, y: 115} ]});
// then
expect(listener).toHaveBeenCalled();
}));
});
describe('#removeConnection', function() {
beforeEach(defaultBootstrap);
it('should fire <connection.removed> event', inject(function(canvas, eventBus, elementRegistry) {
// given
var listener = createSpy('listener');
canvas.addShape({ id: 's1', x: 10, y: 10, width: 30, height: 30 });
canvas.addShape({ id: 's2', x: 100, y: 100, width: 30, height: 30 });
eventBus.on('connection.removed', listener);
canvas.addConnection({ id: 'c1', waypoints: [ { x: 25, y: 25 }, {x: 115, y: 115} ]});
// when
var connection = canvas.removeConnection('c1');
// then
expect(connection.parent).toBeFalsy();
expect(elementRegistry.getById('c1')).not.toBeDefined();
expect(listener).toHaveBeenCalled();
}));
});
describe('viewbox', function() {

@@ -212,2 +237,21 @@

it('should provide default viewbox', inject(function(canvas) {
// given
canvas.addShape({ id: 's0', x: 50, y: 50, width: 300, height: 300 });
// when
var viewbox = canvas.viewbox();
// then
expect(viewbox).toEqual({
x: 0, y: 0,
width: 300, height: 300,
scale: 1.0,
inner: { width: 300, height: 300 },
outer: { width: 300, height: 300 }
});
}));
it('should provide default viewbox / overflowing diagram', inject(function(canvas) {

@@ -230,2 +274,22 @@

}));
it('should provide default viewbox / offset element', inject(function(canvas) {
// given
canvas.addShape({ id: 's0', x: 50, y: 50, width: 150, height: 150 });
// when
var viewbox = canvas.viewbox();
// then
expect(viewbox).toEqual({
x: 0, y: 0,
width: 300, height: 300,
scale: 1.0,
inner: { width: 150, height: 150 },
outer: { width: 300, height: 300 }
});
}));
});

@@ -236,3 +300,3 @@

it('should set new viewbox', inject(function(canvas) {
it('should set viewbox', inject(function(canvas) {

@@ -260,2 +324,71 @@ // given

it('should set viewbox to origin', inject(function(canvas) {
// given
canvas.addShape({ id: 's0', x: 100, y: 100, width: 200, height: 200 });
var viewbox = { x: 100, y: 100, width: 300, height: 300 };
// when
canvas.viewbox(viewbox);
var changedViewbox = canvas.viewbox();
// then
expect(changedViewbox).toEqual({
x: 100, y: 100,
width: 300, height: 300,
scale: 1,
inner: { width: 200, height: 200 },
outer: { width: 300, height: 300 }
});
}));
it('should set viewbox / overflow', inject(function(canvas, eventBus) {
// given
canvas.addShape({ id: 's0', x: 0, y: 0, width: 1200, height: 1200 });
var viewbox = { x: 100, y: 100, width: 600, height: 600 };
// when
canvas.viewbox(viewbox);
// then
var changedViewbox = canvas.viewbox();
expect(changedViewbox).toEqual({
x: 100, y: 100,
width: 600, height: 600,
scale: 0.5,
inner: { width: 1200, height: 1200 },
outer: { width: 300, height: 300 }
});
}));
it('should set viewbox / zoomed in', inject(function(canvas, eventBus) {
// given
canvas.addShape({ id: 's0', x: 0, y: 0, width: 300, height: 300 });
var viewbox = { x: 50, y: 50, width: 200, height: 200 };
// when
canvas.viewbox(viewbox);
// then
var changedViewbox = canvas.viewbox();
expect(changedViewbox).toEqual({
x: 50, y: 50,
width: 200, height: 200,
scale: 1.5,
inner: { width: 300, height: 300 },
outer: { width: 300, height: 300 }
});
}));
it('should fire <canvas.viewbox.changed> event', inject(function(canvas, eventBus) {

@@ -267,3 +400,3 @@

// given
canvas.addShape({ id: 's0', x: 0, y: 0, width: 1200, height: 1200 });
canvas.addShape({ id: 's0', x: 0, y: 0, width: 300, height: 300 });

@@ -280,5 +413,6 @@ var viewbox = { x: 100, y: 100, width: 600, height: 600 };

expect(calls.argsFor(0)[0].viewbox).toEqual({
x: 100, y: 100, width: 600, height: 600,
x: 100, y: 100,
width: 600, height: 600,
scale: 0.5,
inner: { width: 1200, height: 1200 },
inner: { width: 300, height: 300 },
outer: { width: 300, height: 300 }

@@ -299,3 +433,3 @@ });

describe('getter', function() {
describe('setter', function() {

@@ -318,4 +452,54 @@ it('should scroll x/y', inject(function(canvas) {

it('should fire <canvas.viewbox.changed>', inject(function(eventBus, canvas) {
var changedListener = createSpy('listener');
eventBus.on('canvas.viewbox.changed', changedListener);
// given
canvas.addShape({ id: 's0', x: 0, y: 0, width: 300, height: 300 });
var viewbox = canvas.viewbox();
// when
canvas.scroll({ dx: 50, dy: 100 });
// then
var calls = changedListener.calls;
expect(calls.count()).toEqual(1);
// expect { viewbox } event
var newViewbox = calls.argsFor(0)[0].viewbox;
expect(newViewbox.x).toEqual(viewbox.x - 50);
expect(newViewbox.y).toEqual(viewbox.y - 100);
}));
});
describe('getter', function() {
it('should get scroll', inject(function(canvas) {
// given
canvas.addShape({ id: 's0', x: 0, y: 0, width: 300, height: 300 });
var viewbox = canvas.viewbox();
// when
canvas.scroll({ dx: 50, dy: 100 });
var newScroll = canvas.scroll();
// then
expect(newScroll.x).toBe(viewbox.x + 50);
expect(newScroll.y).toBe(viewbox.y + 100);
}));
});
});

@@ -322,0 +506,0 @@

@@ -131,3 +131,3 @@ 'use strict';

e.once('onceEvent', listener);
e.fire('onceEvent', {value: 'a'});
e.fire('onceEvent', { value: 'a' });
expect(listener).toHaveBeenCalled();

@@ -197,8 +197,9 @@

it('higher priority should fire first', function() {
// setup
e.on('foo', listener1, 100);
e.on('foo', listener2, 500);
e.on('foo', listener3, 200);
e.on('foo', 100, listener1);
e.on('foo', 500, listener2);
e.on('foo', 200, listener3);

@@ -213,2 +214,3 @@ // event fired with example data

it('higher priority should fire first and make sure' +

@@ -218,5 +220,5 @@ 'registration order does not affect anything', function() {

// setup
e.on('foo', listener3, 200);
e.on('foo', listener1, 100);
e.on('foo', listener2, 500);
e.on('foo', 200, listener3);
e.on('foo', 100, listener1);
e.on('foo', 500, listener2);

@@ -231,7 +233,8 @@ // event fired with example data

it('event with same priority should fire in registration order', function() {
// setup
e.on('foo', listener3, 100);
e.on('foo', listener2, 100);
e.on('foo', listener1, 100);
e.on('foo', 100, listener3);
e.on('foo', 100, listener2);
e.on('foo', 100, listener1);

@@ -244,2 +247,3 @@ // event fired with example data

it('user should be able to stop propagation' +

@@ -249,5 +253,5 @@ ' to handler with lower priority.', function() {

// setup
e.on('foo', listenerStopPropagation, 200);
e.on('foo', listener1, 100);
e.on('foo', listener2, 500);
e.on('foo', 200, listenerStopPropagation);
e.on('foo', 100, listener1);
e.on('foo', 500, listener2);

@@ -264,8 +268,9 @@ // event fired with example data

it('should set default priority when not set', function() {
// setup
e.on('foo', listener3); //Should use default of 1000
e.on('foo', listener1, 500);
e.on('foo', listener2, 5000);
e.on('foo', listener3); // should use default of 1000
e.on('foo', 500, listener1);
e.on('foo', 5000, listener2);

@@ -272,0 +277,0 @@ // event fired with example data

@@ -81,5 +81,4 @@ 'use strict';

diagram.invoke([ 'canvas', function(canvas) {
canvas
.addShape({ id: 's1', x: 10, y: 10, width: 30, height: 30 })
.addShape({ id: 's2', x: 100, y: 100, width: 30, height: 30 });
canvas.addShape({ id: 's1', x: 10, y: 10, width: 30, height: 30 });
canvas.addShape({ id: 's2', x: 100, y: 100, width: 30, height: 30 });

@@ -86,0 +85,0 @@ canvas.addConnection({ id: 'c1', waypoints: [ { x: 25, y: 25 }, {x: 115, y: 115} ]});

@@ -6,2 +6,6 @@ 'use strict';

// enhance jasmine with test container API
require('jasmine-test-container-support').extend(jasmine);
var OPTIONS, DIAGRAM;

@@ -44,2 +48,4 @@

var testContainer = jasmine.getEnv().getTestContainer();
var _options = options,

@@ -61,3 +67,3 @@ _locals = locals;

_options = _.extend({}, OPTIONS || {}, _options || {});
_options = _.extend({ container: testContainer }, OPTIONS || {}, _options || {});

@@ -64,0 +70,0 @@ var mockModule = {};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc