2017-01-11 07:12:34 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var forEach = require('lodash/collection/forEach'),
|
|
|
|
filter = require('lodash/collection/filter'),
|
2017-01-19 15:13:31 +00:00
|
|
|
any = require('lodash/collection/any'),
|
2017-01-23 13:12:43 +00:00
|
|
|
sort = require('lodash/collection/sortBy'),
|
2017-01-19 15:13:31 +00:00
|
|
|
isArray = require('lodash/lang/isArray');
|
2017-01-11 14:22:32 +00:00
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
var IGNORED_PROPERTIES = require('./ModelCloneUtils').IGNORED_PROPERTIES;
|
2017-01-11 14:22:32 +00:00
|
|
|
|
2017-01-11 14:22:32 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
function isType(element, types) {
|
|
|
|
return any(types, function(type) {
|
|
|
|
return typeof element === type;
|
|
|
|
});
|
2017-01-11 14:22:32 +00:00
|
|
|
}
|
2017-01-11 07:12:34 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A bpmn properties cloning interface
|
|
|
|
*
|
|
|
|
*/
|
2017-01-23 13:12:43 +00:00
|
|
|
function ModelCloneHelper(eventBus) {
|
|
|
|
this._eventBus = eventBus;
|
|
|
|
}
|
2017-01-11 07:12:34 +00:00
|
|
|
|
2017-01-11 14:22:32 +00:00
|
|
|
module.exports = ModelCloneHelper;
|
2017-01-11 07:12:34 +00:00
|
|
|
|
|
|
|
|
2017-01-11 14:22:32 +00:00
|
|
|
ModelCloneHelper.prototype.clone = function(oldElement, newElement, properties) {
|
2017-01-19 15:13:31 +00:00
|
|
|
this._newElement = newElement;
|
2017-01-11 07:12:34 +00:00
|
|
|
|
2017-01-23 13:12:43 +00:00
|
|
|
// we want the extensionElements to be cloned last
|
|
|
|
// so that they can check certain properties
|
|
|
|
properties = sort(properties, function(prop) {
|
|
|
|
return prop === 'bpmn:extensionElements';
|
|
|
|
});
|
|
|
|
|
2017-01-11 07:12:34 +00:00
|
|
|
forEach(properties, function(propName) {
|
2017-01-19 15:13:31 +00:00
|
|
|
var oldElementProp = oldElement.get(propName),
|
|
|
|
newElementProp = newElement.get(propName),
|
|
|
|
propDescriptor = newElement.$model.getPropertyDescriptor(newElement, propName),
|
2017-02-01 09:42:49 +00:00
|
|
|
name;
|
2017-01-19 15:13:31 +00:00
|
|
|
|
|
|
|
// we're not interested in cloning:
|
|
|
|
// - same values from simple types
|
|
|
|
// - cloning id's
|
|
|
|
// - cloning reference elements
|
|
|
|
if (newElementProp === oldElementProp ||
|
|
|
|
(propDescriptor && (propDescriptor.isId || propDescriptor.isReference))) {
|
2017-01-11 07:12:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
// if the property is of type 'boolean', 'string', 'number' or 'null', just set it
|
|
|
|
if (isType(oldElementProp, [ 'boolean', 'string', 'number' ]) || oldElementProp === null) {
|
|
|
|
newElement.set(propName, oldElementProp);
|
2017-01-11 07:12:34 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
if (isArray(oldElementProp)) {
|
2017-01-11 07:12:34 +00:00
|
|
|
|
|
|
|
forEach(oldElementProp, function(extElement) {
|
|
|
|
var newProp = this._deepClone(extElement);
|
|
|
|
|
2017-02-01 09:42:49 +00:00
|
|
|
newProp.$parent = newElement;
|
2017-01-11 14:22:32 +00:00
|
|
|
|
2017-02-01 09:42:49 +00:00
|
|
|
newElementProp.push(newProp);
|
2017-01-11 07:12:34 +00:00
|
|
|
}, this);
|
2017-01-19 15:13:31 +00:00
|
|
|
} else {
|
|
|
|
name = propName.replace(/bpmn:/, '');
|
|
|
|
|
2017-02-01 09:42:49 +00:00
|
|
|
newElement[name] = this._deepClone(oldElementProp);
|
2017-01-11 07:12:34 +00:00
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
return newElement;
|
|
|
|
};
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
ModelCloneHelper.prototype._deepClone = function _deepClone(element) {
|
2017-01-23 13:12:43 +00:00
|
|
|
var eventBus = this._eventBus;
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
var newElement = this._newElement;
|
|
|
|
|
|
|
|
var newProp = element.$model.create(element.$type);
|
|
|
|
|
|
|
|
var properties = filter(Object.keys(element), 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';
|
|
|
|
});
|
2017-01-11 07:12:34 +00:00
|
|
|
|
|
|
|
forEach(properties, function(propName) {
|
2017-01-19 15:13:31 +00:00
|
|
|
// check if the element has this property defined
|
|
|
|
if (element[propName] !== undefined && (element[propName].$type || isArray(element[propName]))) {
|
2017-01-11 07:12:34 +00:00
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
if (isArray(element[propName])) {
|
2017-02-01 09:42:49 +00:00
|
|
|
newProp[propName] = [];
|
2017-01-11 07:12:34 +00:00
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
forEach(element[propName], function(property) {
|
|
|
|
var extProp = element.$model.getTypeDescriptor(property.$type),
|
|
|
|
newDeepProp;
|
|
|
|
|
|
|
|
// we're not going to copy undefined types
|
|
|
|
if (!extProp) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-23 13:12:43 +00:00
|
|
|
var canClone = eventBus.fire('property.clone', {
|
|
|
|
newElement: newElement,
|
|
|
|
propertyDescriptor: extProp
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!canClone) {
|
|
|
|
// if can clone is 'undefined' or 'false'
|
|
|
|
// check for the meta information if it is allowed
|
|
|
|
if (element.$type === 'bpmn:ExtensionElements' &&
|
|
|
|
extProp.meta && extProp.meta.allowedIn &&
|
|
|
|
!isAllowedIn(extProp, newElement.$type)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-01-19 15:13:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
newDeepProp = this._deepClone(property);
|
2017-01-11 14:22:32 +00:00
|
|
|
|
|
|
|
newDeepProp.$parent = newProp;
|
|
|
|
|
|
|
|
newProp[propName].push(newDeepProp);
|
2017-01-11 07:12:34 +00:00
|
|
|
}, this);
|
|
|
|
|
2017-01-19 15:13:31 +00:00
|
|
|
} else if (element[propName].$type) {
|
|
|
|
newProp[propName] = this._deepClone(element[propName]);
|
2017-01-11 14:22:32 +00:00
|
|
|
|
2017-02-01 09:42:49 +00:00
|
|
|
newProp[propName].$parent = newProp;
|
2017-01-11 07:12:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// just assign directly if it's a value
|
2017-01-19 15:13:31 +00:00
|
|
|
newProp[propName] = element[propName];
|
2017-01-11 07:12:34 +00:00
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
return newProp;
|
|
|
|
};
|