").append(jQuery.parseHTML(responseText)).find(selector) :
+ return this;
+ },
- // Otherwise use the full result
- responseText);
+ onSetTheme: function(oldTheme, newTheme) {
+ // Intentionally empty.
+ // This method is meant to be overriden.
+ },
- }).complete(callback && function (jqXHR, status) {
- self.each(callback, response || [ jqXHR.responseText, status, jqXHR ]);
- });
- }
+ remove: function() {
- return this;
- };
+ this.onRemove();
-// Attach a bunch of functions for handling common AJAX events
- jQuery.each([ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function (i, type) {
- jQuery.fn[ type ] = function (fn) {
- return this.on(type, fn);
- };
- });
+ joint.mvc.views[this.cid] = null;
- jQuery.extend({
+ Backbone.View.prototype.remove.apply(this, arguments);
- // Counter for holding the number of active queries
- active: 0,
+ return this;
+ },
- // Last-Modified header cache for next request
- lastModified: {},
- etag: {},
+ onRemove: function() {
+ // Intentionally empty.
+ // This method is meant to be overriden.
+ }
+ });
- ajaxSettings: {
- url: ajaxLocation,
- type: "GET",
- isLocal: rlocalProtocol.test(ajaxLocParts[ 1 ]),
- global: true,
- processData: true,
- async: true,
- contentType: "application/x-www-form-urlencoded; charset=UTF-8",
- /*
- timeout: 0,
- data: null,
- dataType: null,
- username: null,
- password: null,
- cache: null,
- throws: false,
- traditional: false,
- headers: {},
- */
+ (function() {
- accepts: {
- "*": allTypes,
- text: "text/plain",
- html: "text/html",
- xml: "application/xml, text/xml",
- json: "application/json, text/javascript"
- },
+ joint.mvc.View._extend = joint.mvc.View.extend;
- contents: {
- xml: /xml/,
- html: /html/,
- json: /json/
- },
+ joint.mvc.View.extend = function(protoProps, staticProps) {
- responseFields: {
- xml: "responseXML",
- text: "responseText",
- json: "responseJSON"
- },
+ protoProps = protoProps || {};
- // Data converters
- // Keys separate source (or catchall "*") and destination types with a single space
- converters: {
+ var render = protoProps.render || this.prototype.render || null;
- // Convert anything to text
- "* text": String,
+ protoProps.render = function() {
- // Text to html (true = no transformation)
- "text html": true,
+ if (render) {
+ // Call the original render method.
+ render.apply(this, arguments);
+ }
- // Evaluate text as a json expression
- "text json": jQuery.parseJSON,
+ // Should always call onRender() method.
+ this.onRender();
- // Parse text as xml
- "text xml": jQuery.parseXML
- },
+ // Should always return itself.
+ return this;
+ };
- // For options that shouldn't be deep extended:
- // you can add your own custom options here if
- // and when you create one that shouldn't be
- // deep extended (see ajaxExtend)
- flatOptions: {
- url: true,
- context: true
- }
- },
+ return joint.mvc.View._extend.call(this, protoProps, staticProps);
+ };
- // Creates a full fledged settings object into target
- // with both ajaxSettings and settings fields.
- // If target is omitted, writes into ajaxSettings.
- ajaxSetup: function (target, settings) {
- return settings ?
+ })();
- // Building a settings object
- ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :
- // Extending ajaxSettings
- ajaxExtend(jQuery.ajaxSettings, target);
- },
+ joint.dia.GraphCells = Backbone.Collection.extend({
- ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
- ajaxTransport: addToPrefiltersOrTransports(transports),
+ cellNamespace: joint.shapes,
- // Main method
- ajax: function (url, options) {
+ initialize: function(models, opt) {
- // If url is an object, simulate pre-1.5 signature
- if (typeof url === "object") {
- options = url;
- url = undefined;
+ // Set the optional namespace where all model classes are defined.
+ if (opt.cellNamespace) {
+ this.cellNamespace = opt.cellNamespace;
}
- // Force options to be an object
- options = options || {};
+ this.graph = opt.graph;
+ },
- var transport,
- // URL without anti-cache param
- cacheURL,
- // Response headers
- responseHeadersString,
- responseHeaders,
- // timeout handle
- timeoutTimer,
- // Cross-domain detection vars
- parts,
- // To know if global events are to be dispatched
- fireGlobals,
- // Loop variable
- i,
- // Create the final options object
- s = jQuery.ajaxSetup({}, options),
- // Callbacks context
- callbackContext = s.context || s,
- // Context for global events is callbackContext if it is a DOM node or jQuery collection
- globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
- jQuery(callbackContext) :
- jQuery.event,
- // Deferreds
- deferred = jQuery.Deferred(),
- completeDeferred = jQuery.Callbacks("once memory"),
- // Status-dependent callbacks
- statusCode = s.statusCode || {},
- // Headers (they are sent all at once)
- requestHeaders = {},
- requestHeadersNames = {},
- // The jqXHR state
- state = 0,
- // Default abort message
- strAbort = "canceled",
- // Fake xhr
- jqXHR = {
- readyState: 0,
-
- // Builds headers hashtable if needed
- getResponseHeader: function (key) {
- var match;
- if (state === 2) {
- if (!responseHeaders) {
- responseHeaders = {};
- while ((match = rheaders.exec(responseHeadersString))) {
- responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
- }
- }
- match = responseHeaders[ key.toLowerCase() ];
- }
- return match == null ? null : match;
- },
+ model: function(attrs, options) {
- // Raw string
- getAllResponseHeaders: function () {
- return state === 2 ? responseHeadersString : null;
- },
+ var collection = options.collection;
+ var namespace = collection.cellNamespace;
- // Caches the header
- setRequestHeader: function (name, value) {
- var lname = name.toLowerCase();
- if (!state) {
- name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
- requestHeaders[ name ] = value;
- }
- return this;
- },
+ // Find the model class in the namespace or use the default one.
+ var ModelClass = (attrs.type === 'link')
+ ? joint.dia.Link
+ : joint.util.getByPath(namespace, attrs.type, '.') || joint.dia.Element;
- // Overrides response content-type header
- overrideMimeType: function (type) {
- if (!state) {
- s.mimeType = type;
- }
- return this;
- },
+ var cell = new ModelClass(attrs, options);
+ // Add a reference to the graph. It is necessary to do this here because this is the earliest place
+ // where a new model is created from a plain JS object. For other objects, see `joint.dia.Graph>>_prepareCell()`.
+ cell.graph = collection.graph;
- // Status-dependent callbacks
- statusCode: function (map) {
- var code;
- if (map) {
- if (state < 2) {
- for (code in map) {
- // Lazy-add the new callback in a way that preserves old ones
- statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
- }
- } else {
- // Execute the appropriate callbacks
- jqXHR.always(map[ jqXHR.status ]);
- }
- }
- return this;
- },
+ return cell;
+ },
- // Cancel the request
- abort: function (statusText) {
- var finalText = statusText || strAbort;
- if (transport) {
- transport.abort(finalText);
- }
- done(0, finalText);
- return this;
- }
- };
+ // `comparator` makes it easy to sort cells based on their `z` index.
+ comparator: function(model) {
- // Attach deferreds
- deferred.promise(jqXHR).complete = completeDeferred.add;
- jqXHR.success = jqXHR.done;
- jqXHR.error = jqXHR.fail;
+ return model.get('z') || 0;
+ }
+ });
- // Remove hash character (#7531: and string promotion)
- // Add protocol if not provided (prefilters might expect it)
- // Handle falsy url in the settings object (#10093: consistency with old signature)
- // We also use the url parameter if available
- s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace(rhash, "")
- .replace(rprotocol, ajaxLocParts[ 1 ] + "//");
- // Alias method option to type as per ticket #12004
- s.type = options.method || options.type || s.method || s.type;
+ joint.dia.Graph = Backbone.Model.extend({
- // Extract dataTypes list
- s.dataTypes = jQuery.trim(s.dataType || "*").toLowerCase().match(core_rnotwhite) || [""];
+ _batches: {},
- // A cross-domain request is in order when we have a protocol:host:port mismatch
- if (s.crossDomain == null) {
- parts = rurl.exec(s.url.toLowerCase());
- s.crossDomain = !!( parts &&
- ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
- ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
- ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
- );
- }
+ initialize: function(attrs, opt) {
- // Convert data if not already a string
- if (s.data && s.processData && typeof s.data !== "string") {
- s.data = jQuery.param(s.data, s.traditional);
- }
+ opt = opt || {};
- // Apply prefilters
- inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);
+ // Passing `cellModel` function in the options object to graph allows for
+ // setting models based on attribute objects. This is especially handy
+ // when processing JSON graphs that are in a different than JointJS format.
+ var cells = new joint.dia.GraphCells([], {
+ model: opt.cellModel,
+ cellNamespace: opt.cellNamespace,
+ graph: this
+ });
+ Backbone.Model.prototype.set.call(this, 'cells', cells);
- // If request was aborted inside a prefilter, stop there
- if (state === 2) {
- return jqXHR;
- }
+ // Make all the events fired in the `cells` collection available.
+ // to the outside world.
+ cells.on('all', this.trigger, this);
- // We can fire global events as of now if asked to
- fireGlobals = s.global;
+ // Backbone automatically doesn't trigger re-sort if models attributes are changed later when
+ // they're already in the collection. Therefore, we're triggering sort manually here.
+ this.on('change:z', this._sortOnChangeZ, this);
+ this.on('batch:stop', this._onBatchStop, this);
- // Watch for a new set of requests
- if (fireGlobals && jQuery.active++ === 0) {
- jQuery.event.trigger("ajaxStart");
- }
+ // `joint.dia.Graph` keeps an internal data structure (an adjacency list)
+ // for fast graph queries. All changes that affect the structure of the graph
+ // must be reflected in the `al` object. This object provides fast answers to
+ // questions such as "what are the neighbours of this node" or "what
+ // are the sibling links of this link".
- // Uppercase the type
- s.type = s.type.toUpperCase();
+ // Outgoing edges per node. Note that we use a hash-table for the list
+ // of outgoing edges for a faster lookup.
+ // [node ID] -> Object [edge] -> true
+ this._out = {};
+ // Ingoing edges per node.
+ // [node ID] -> Object [edge] -> true
+ this._in = {};
+ // `_nodes` is useful for quick lookup of all the elements in the graph, without
+ // having to go through the whole cells array.
+ // [node ID] -> true
+ this._nodes = {};
+ // `_edges` is useful for quick lookup of all the links in the graph, without
+ // having to go through the whole cells array.
+ // [edge ID] -> true
+ this._edges = {};
- // Determine if request has content
- s.hasContent = !rnoContent.test(s.type);
+ cells.on('add', this._restructureOnAdd, this);
+ cells.on('remove', this._restructureOnRemove, this);
+ cells.on('reset', this._restructureOnReset, this);
+ cells.on('change:source', this._restructureOnChangeSource, this);
+ cells.on('change:target', this._restructureOnChangeTarget, this);
+ cells.on('remove', this._removeCell, this);
+ },
- // Save the URL in case we're toying with the If-Modified-Since
- // and/or If-None-Match header later on
- cacheURL = s.url;
+ _sortOnChangeZ: function() {
- // More options handling for requests with no content
- if (!s.hasContent) {
+ if (!this.hasActiveBatch('to-front') && !this.hasActiveBatch('to-back')) {
+ this.get('cells').sort();
+ }
+ },
- // If data is available, append data to url
- if (s.data) {
- cacheURL = ( s.url += ( ajax_rquery.test(cacheURL) ? "&" : "?" ) + s.data );
- // #9682: remove data so that it's not used in an eventual retry
- delete s.data;
- }
+ _onBatchStop: function(data) {
- // Add anti-cache in url if needed
- if (s.cache === false) {
- s.url = rts.test(cacheURL) ?
+ var batchName = data && data.batchName;
+ if ((batchName === 'to-front' || batchName === 'to-back') && !this.hasActiveBatch(batchName)) {
+ this.get('cells').sort();
+ }
+ },
- // If there is already a '_' parameter, set its value
- cacheURL.replace(rts, "$1_=" + ajax_nonce++) :
+ _restructureOnAdd: function(cell) {
- // Otherwise add one to the end
- cacheURL + ( ajax_rquery.test(cacheURL) ? "&" : "?" ) + "_=" + ajax_nonce++;
+ if (cell.isLink()) {
+ this._edges[cell.id] = true;
+ var source = cell.get('source');
+ var target = cell.get('target');
+ if (source.id) {
+ (this._out[source.id] || (this._out[source.id] = {}))[cell.id] = true;
}
+ if (target.id) {
+ (this._in[target.id] || (this._in[target.id] = {}))[cell.id] = true;
+ }
+ } else {
+ this._nodes[cell.id] = true;
}
+ },
+
+ _restructureOnRemove: function(cell) {
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if (s.ifModified) {
- if (jQuery.lastModified[ cacheURL ]) {
- jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[ cacheURL ]);
+ if (cell.isLink()) {
+ delete this._edges[cell.id];
+ var source = cell.get('source');
+ var target = cell.get('target');
+ if (source.id && this._out[source.id] && this._out[source.id][cell.id]) {
+ delete this._out[source.id][cell.id];
}
- if (jQuery.etag[ cacheURL ]) {
- jqXHR.setRequestHeader("If-None-Match", jQuery.etag[ cacheURL ]);
+ if (target.id && this._in[target.id] && this._in[target.id][cell.id]) {
+ delete this._in[target.id][cell.id];
}
+ } else {
+ delete this._nodes[cell.id];
}
+ },
- // Set the correct header, if data is being sent
- if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
- jqXHR.setRequestHeader("Content-Type", s.contentType);
- }
+ _restructureOnReset: function(cells) {
- // Set the Accepts header for the server, depending on the dataType
- jqXHR.setRequestHeader(
- "Accept",
- s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
- s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
- s.accepts[ "*" ]
- );
+ // Normalize into an array of cells. The original `cells` is GraphCells Backbone collection.
+ cells = cells.models;
- // Check for headers option
- for (i in s.headers) {
- jqXHR.setRequestHeader(i, s.headers[ i ]);
- }
+ this._out = {};
+ this._in = {};
+ this._nodes = {};
+ this._edges = {};
+
+ _.each(cells, this._restructureOnAdd, this);
+ },
+
+ _restructureOnChangeSource: function(link) {
- // Allow custom headers/mimetypes and early abort
- if (s.beforeSend && ( s.beforeSend.call(callbackContext, jqXHR, s) === false || state === 2 )) {
- // Abort if not done already and return
- return jqXHR.abort();
+ var prevSource = link.previous('source');
+ if (prevSource.id && this._out[prevSource.id]) {
+ delete this._out[prevSource.id][link.id];
+ }
+ var source = link.get('source');
+ if (source.id) {
+ (this._out[source.id] || (this._out[source.id] = {}))[link.id] = true;
}
+ },
- // aborting is no longer a cancellation
- strAbort = "abort";
+ _restructureOnChangeTarget: function(link) {
- // Install callbacks on deferreds
- for (i in { success: 1, error: 1, complete: 1 }) {
- jqXHR[ i ](s[ i ]);
+ var prevTarget = link.previous('target');
+ if (prevTarget.id && this._in[prevTarget.id]) {
+ delete this._in[prevTarget.id][link.id];
+ }
+ var target = link.get('target');
+ if (target.id) {
+ (this._in[target.id] || (this._in[target.id] = {}))[link.id] = true;
}
+ },
- // Get transport
- transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);
+ // Return all outbound edges for the node. Return value is an object
+ // of the form: [edge] -> true
+ getOutboundEdges: function(node) {
- // If no transport, we auto-abort
- if (!transport) {
- done(-1, "No Transport");
- } else {
- jqXHR.readyState = 1;
+ return (this._out && this._out[node]) || {};
+ },
- // Send global event
- if (fireGlobals) {
- globalEventContext.trigger("ajaxSend", [ jqXHR, s ]);
- }
- // Timeout
- if (s.async && s.timeout > 0) {
- timeoutTimer = setTimeout(function () {
- jqXHR.abort("timeout");
- }, s.timeout);
- }
+ // Return all inbound edges for the node. Return value is an object
+ // of the form: [edge] -> true
+ getInboundEdges: function(node) {
- try {
- state = 1;
- transport.send(requestHeaders, done);
- } catch (e) {
- // Propagate exception as error if not done
- if (state < 2) {
- done(-1, e);
- // Simply rethrow otherwise
- } else {
- throw e;
- }
- }
- }
+ return (this._in && this._in[node]) || {};
+ },
- // Callback for when everything is done
- function done(status, nativeStatusText, responses, headers) {
- var isSuccess, success, error, response, modified,
- statusText = nativeStatusText;
+ toJSON: function() {
- // Called once
- if (state === 2) {
- return;
- }
+ // Backbone does not recursively call `toJSON()` on attributes that are themselves models/collections.
+ // It just clones the attributes. Therefore, we must call `toJSON()` on the cells collection explicitely.
+ var json = Backbone.Model.prototype.toJSON.apply(this, arguments);
+ json.cells = this.get('cells').toJSON();
+ return json;
+ },
- // State is "done" now
- state = 2;
+ fromJSON: function(json, opt) {
- // Clear timeout if it exists
- if (timeoutTimer) {
- clearTimeout(timeoutTimer);
- }
+ if (!json.cells) {
+
+ throw new Error('Graph JSON must contain cells array.');
+ }
- // Dereference transport for early garbage collection
- // (no matter how long the jqXHR object will be used)
- transport = undefined;
+ return this.set(json, opt);
+ },
- // Cache response headers
- responseHeadersString = headers || "";
+ set: function(key, val, opt) {
- // Set readyState
- jqXHR.readyState = status > 0 ? 4 : 0;
+ var attrs;
- // Determine if successful
- isSuccess = status >= 200 && status < 300 || status === 304;
+ // Handle both `key`, value and {key: value} style arguments.
+ if (typeof key === 'object') {
+ attrs = key;
+ opt = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
- // Get response data
- if (responses) {
- response = ajaxHandleResponses(s, jqXHR, responses);
- }
+ // Make sure that `cells` attribute is handled separately via resetCells().
+ if (attrs.hasOwnProperty('cells')) {
+ this.resetCells(attrs.cells, opt);
+ attrs = _.omit(attrs, 'cells');
+ }
- // Convert no matter what (that way responseXXX fields are always set)
- response = ajaxConvert(s, response, jqXHR, isSuccess);
+ // The rest of the attributes are applied via original set method.
+ return Backbone.Model.prototype.set.call(this, attrs, opt);
+ },
- // If successful, handle type chaining
- if (isSuccess) {
+ clear: function(opt) {
- // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
- if (s.ifModified) {
- modified = jqXHR.getResponseHeader("Last-Modified");
- if (modified) {
- jQuery.lastModified[ cacheURL ] = modified;
- }
- modified = jqXHR.getResponseHeader("etag");
- if (modified) {
- jQuery.etag[ cacheURL ] = modified;
- }
- }
+ opt = _.extend({}, opt, { clear: true });
- // if no content
- if (status === 204 || s.type === "HEAD") {
- statusText = "nocontent";
+ var collection = this.get('cells');
- // if not modified
- } else if (status === 304) {
- statusText = "notmodified";
+ if (collection.length === 0) return this;
- // If we have data, let's convert it
- } else {
- statusText = response.state;
- success = response.data;
- error = response.error;
- isSuccess = !error;
- }
- } else {
- // We extract error from statusText
- // then normalize statusText and status for non-aborts
- error = statusText;
- if (status || !statusText) {
- statusText = "error";
- if (status < 0) {
- status = 0;
- }
- }
- }
+ this.startBatch('clear', opt);
- // Set data for the fake xhr object
- jqXHR.status = status;
- jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+ // The elements come after the links.
+ var cells = collection.sortBy(function(cell) {
+ return cell.isLink() ? 1 : 2;
+ });
- // Success/Error
- if (isSuccess) {
- deferred.resolveWith(callbackContext, [ success, statusText, jqXHR ]);
- } else {
- deferred.rejectWith(callbackContext, [ jqXHR, statusText, error ]);
- }
+ do {
- // Status-dependent callbacks
- jqXHR.statusCode(statusCode);
- statusCode = undefined;
+ // Remove all the cells one by one.
+ // Note that all the links are removed first, so it's
+ // safe to remove the elements without removing the connected
+ // links first.
+ cells.shift().remove(opt);
- if (fireGlobals) {
- globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError",
- [ jqXHR, s, isSuccess ? success : error ]);
- }
+ } while (cells.length > 0);
- // Complete
- completeDeferred.fireWith(callbackContext, [ jqXHR, statusText ]);
+ this.stopBatch('clear');
- if (fireGlobals) {
- globalEventContext.trigger("ajaxComplete", [ jqXHR, s ]);
- // Handle the global AJAX counter
- if (!( --jQuery.active )) {
- jQuery.event.trigger("ajaxStop");
- }
+ return this;
+ },
+
+ _prepareCell: function(cell, opt) {
+
+ var attrs;
+ if (cell instanceof Backbone.Model) {
+ attrs = cell.attributes;
+ if (!cell.graph && (!opt || !opt.dry)) {
+ // An element can not be member of more than one graph.
+ // A cell stops being the member of the graph after it's explicitely removed.
+ cell.graph = this;
}
+ } else {
+ // In case we're dealing with a plain JS object, we have to set the reference
+ // to the `graph` right after the actual model is created. This happens in the `model()` function
+ // of `joint.dia.GraphCells`.
+ attrs = cell;
}
- return jqXHR;
- },
+ if (!_.isString(attrs.type)) {
+ throw new TypeError('dia.Graph: cell type must be a string.');
+ }
- getJSON: function (url, data, callback) {
- return jQuery.get(url, data, callback, "json");
+ return cell;
},
- getScript: function (url, callback) {
- return jQuery.get(url, undefined, callback, "script");
- }
- });
+ maxZIndex: function() {
- jQuery.each([ "get", "post" ], function (i, method) {
- jQuery[ method ] = function (url, data, callback, type) {
- // shift arguments if data argument was omitted
- if (jQuery.isFunction(data)) {
- type = type || callback;
- callback = data;
- data = undefined;
- }
-
- return jQuery.ajax({
- url: url,
- type: method,
- dataType: type,
- data: data,
- success: callback
- });
- };
- });
+ var lastCell = this.get('cells').last();
+ return lastCell ? (lastCell.get('z') || 0) : 0;
+ },
- /* Handles responses to an ajax request:
- * - finds the right dataType (mediates between content-type and expected dataType)
- * - returns the corresponding response
- */
- function ajaxHandleResponses(s, jqXHR, responses) {
+ addCell: function(cell, opt) {
- var ct, type, finalDataType, firstDataType,
- contents = s.contents,
- dataTypes = s.dataTypes;
+ if (_.isArray(cell)) {
- // Remove auto dataType and get content-type in the process
- while (dataTypes[ 0 ] === "*") {
- dataTypes.shift();
- if (ct === undefined) {
- ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+ return this.addCells(cell, opt);
}
- }
- // Check if we're dealing with a known content-type
- if (ct) {
- for (type in contents) {
- if (contents[ type ] && contents[ type ].test(ct)) {
- dataTypes.unshift(type);
- break;
+ if (cell instanceof Backbone.Model) {
+
+ if (!cell.has('z')) {
+ cell.set('z', this.maxZIndex() + 1);
}
- }
- }
- // Check to see if we have a response for the expected dataType
- if (dataTypes[ 0 ] in responses) {
- finalDataType = dataTypes[ 0 ];
- } else {
- // Try convertible dataTypes
- for (type in responses) {
- if (!dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ]) {
- finalDataType = type;
- break;
- }
- if (!firstDataType) {
- firstDataType = type;
- }
- }
- // Or just use first one
- finalDataType = finalDataType || firstDataType;
- }
+ } else if (_.isUndefined(cell.z)) {
- // If we found a dataType
- // We add the dataType to the list if needed
- // and return the corresponding response
- if (finalDataType) {
- if (finalDataType !== dataTypes[ 0 ]) {
- dataTypes.unshift(finalDataType);
+ cell.z = this.maxZIndex() + 1;
}
- return responses[ finalDataType ];
- }
- }
- /* Chain conversions given the request and the original response
- * Also sets the responseXXX fields on the jqXHR instance
- */
- function ajaxConvert(s, response, jqXHR, isSuccess) {
- var conv2, current, conv, tmp, prev,
- converters = {},
- // Work with a copy of dataTypes in case we need to modify it for conversion
- dataTypes = s.dataTypes.slice();
+ this.get('cells').add(this._prepareCell(cell, opt), opt || {});
- // Create converters map with lowercased keys
- if (dataTypes[ 1 ]) {
- for (conv in s.converters) {
- converters[ conv.toLowerCase() ] = s.converters[ conv ];
- }
- }
+ return this;
+ },
- current = dataTypes.shift();
+ addCells: function(cells, opt) {
- // Convert to each sequential dataType
- while (current) {
+ if (cells.length) {
- if (s.responseFields[ current ]) {
- jqXHR[ s.responseFields[ current ] ] = response;
- }
+ cells = _.flattenDeep(cells);
+ opt.position = cells.length;
- // Apply the dataFilter if provided
- if (!prev && isSuccess && s.dataFilter) {
- response = s.dataFilter(response, s.dataType);
+ this.startBatch('add');
+ _.each(cells, function(cell) {
+ opt.position--;
+ this.addCell(cell, opt);
+ }, this);
+ this.stopBatch('add');
}
- prev = current;
- current = dataTypes.shift();
+ return this;
+ },
- if (current) {
+ // When adding a lot of cells, it is much more efficient to
+ // reset the entire cells collection in one go.
+ // Useful for bulk operations and optimizations.
+ resetCells: function(cells, opt) {
- // There's only work to do if current dataType is non-auto
- if (current === "*") {
+ var preparedCells = _.map(cells, _.bind(this._prepareCell, this, _, opt));
+ this.get('cells').reset(preparedCells, opt);
- current = prev;
+ return this;
+ },
- // Convert response if prev dataType is non-auto and differs from current
- } else if (prev !== "*" && prev !== current) {
+ removeCells: function(cells, opt) {
- // Seek a direct converter
- conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+ if (cells.length) {
- // If none found, seek a pair
- if (!conv) {
- for (conv2 in converters) {
+ this.startBatch('remove');
+ _.invoke(cells, 'remove', opt);
+ this.stopBatch('remove');
+ }
- // If conv2 outputs current
- tmp = conv2.split(" ");
- if (tmp[ 1 ] === current) {
+ return this;
+ },
- // If prev can be converted to accepted input
- conv = converters[ prev + " " + tmp[ 0 ] ] ||
- converters[ "* " + tmp[ 0 ] ];
- if (conv) {
- // Condense equivalence converters
- if (conv === true) {
- conv = converters[ conv2 ];
+ _removeCell: function(cell, collection, options) {
- // Otherwise, insert the intermediate dataType
- } else if (converters[ conv2 ] !== true) {
- current = tmp[ 0 ];
- dataTypes.unshift(tmp[ 1 ]);
- }
- break;
- }
- }
- }
- }
+ options = options || {};
- // Apply converter (if not an equivalence)
- if (conv !== true) {
+ if (!options.clear) {
+ // Applications might provide a `disconnectLinks` option set to `true` in order to
+ // disconnect links when a cell is removed rather then removing them. The default
+ // is to remove all the associated links.
+ if (options.disconnectLinks) {
- // Unless errors are allowed to bubble, catch and return them
- if (conv && s[ "throws" ]) {
- response = conv(response);
- } else {
- try {
- response = conv(response);
- } catch (e) {
- return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
- }
- }
- }
- }
- }
- }
+ this.disconnectLinks(cell, options);
- return { state: "success", data: response };
- }
+ } else {
-// Install script dataType
- jQuery.ajaxSetup({
- accepts: {
- script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
- },
- contents: {
- script: /(?:java|ecma)script/
- },
- converters: {
- "text script": function (text) {
- jQuery.globalEval(text);
- return text;
+ this.removeLinks(cell, options);
+ }
}
- }
- });
-
-// Handle cache's special case and crossDomain
- jQuery.ajaxPrefilter("script", function (s) {
- if (s.cache === undefined) {
- s.cache = false;
- }
- if (s.crossDomain) {
- s.type = "GET";
- }
- });
+ // Silently remove the cell from the cells collection. Silently, because
+ // `joint.dia.Cell.prototype.remove` already triggers the `remove` event which is
+ // then propagated to the graph model. If we didn't remove the cell silently, two `remove` events
+ // would be triggered on the graph model.
+ this.get('cells').remove(cell, { silent: true });
-// Bind script tag hack transport
- jQuery.ajaxTransport("script", function (s) {
- // This transport only deals with cross domain requests
- if (s.crossDomain) {
- var script, callback;
- return {
- send: function (_, complete) {
- script = jQuery("
-
-
-
-
+ <%----%>
+ <%----%>
+ <%----%>
+ <%----%>
+ <%----%>
-
+ <%----%>
diff --git a/editor-service/src/main/webapp/app/bpmn/diagram/controller/BpmnDiagramEditorController.ts b/editor-service/src/main/webapp/app/bpmn/diagram/controller/BpmnDiagramEditorController.ts
index 0103b3f5..28c30fb2 100644
--- a/editor-service/src/main/webapp/app/bpmn/diagram/controller/BpmnDiagramEditorController.ts
+++ b/editor-service/src/main/webapp/app/bpmn/diagram/controller/BpmnDiagramEditorController.ts
@@ -26,9 +26,7 @@ export class BpmnDiagramEditorController extends DiagramEditorController {
document.addEventListener('mouseup', (event) => { this.gesturesController.onMouseUp(event) } );
$("#" + scene.getId()).mousemove((event) => { this.gesturesController.onMouseMove(event) } );
$("#elements-search").on('input', (event) => {
- this.paletteController.searchPaletteReload(event, this.elementTypes, this.nodeTypesMap);
- this.paletteController.initClick(this.diagramEditor.getScene());
- this.paletteController.initDraggable();
+ this.paletteController.searchPaletteReload(event);
} );
(scene as any).on('cell:pointerdown', (cellView, event, x, y): void => {
diff --git a/editor-service/src/main/webapp/app/common/interfaces/editorCore.d.ts b/editor-service/src/main/webapp/app/common/interfaces/editorCore.d.ts
index fb79af9e..dc32dca6 100644
--- a/editor-service/src/main/webapp/app/common/interfaces/editorCore.d.ts
+++ b/editor-service/src/main/webapp/app/common/interfaces/editorCore.d.ts
@@ -19,7 +19,9 @@ declare module "core/editorCore/model/Property" {
name: string;
type: string;
value: string;
+ coordinates;
constructor(name: string, type: string, value: string);
+ setCoordinates(x, y);
}
}
declare module "core/editorCore/model/PropertiesPack" {
@@ -154,6 +156,7 @@ declare module "core/editorCore/model/commands/MultiCommand" {
export class MultiCommand implements Command {
private commands;
constructor(commands: Command[]);
+ add(command: Command): void;
execute(): void;
revert(): void;
isRevertible(): boolean;
@@ -193,32 +196,13 @@ declare module "core/editorCore/model/Link" {
declare module "core/editorCore/model/PropertyEditElement" {
import { Property } from "core/editorCore/model/Property";
export class PropertyEditElement {
- private static propertyTemplate;
- private static template;
- private htmlElement;
- constructor(logicalId: string, jointObjectId: string, properties: Map
);
- getHtmlElement(): any;
- setPosition(x: number, y: number): void;
- private initInputSize();
- private initInputAutosize();
- }
-}
-declare module "core/editorCore/model/DiagramNode" {
- import { PropertyEditElement } from "core/editorCore/model/PropertyEditElement";
- import { DiagramElement } from "core/editorCore/model/DiagramElement";
- export interface DiagramNode extends DiagramElement {
+ public getTextObject() : joint.shapes.basic.Rect;
getX(): number;
getY(): number;
- getImagePath(): string;
- getSize(): string;
- setPosition(x: number, y: number, zoom: number, cellView: joint.dia.CellView): void;
- setSize(width: number, height: number, cellView: joint.dia.CellView): void;
- getPropertyEditElement(): PropertyEditElement;
- initPropertyEditElements(zoom: number): void;
- initResize(bbox: any, x: number, y: number, paddingPercent: any): void;
- completeResize(): void;
- isResizing(): boolean;
- pointermove(cellView: any, evt: any, x: any, y: any): void;
+ public setPosition(x: number, y: number): void;
+ private createTextObject(x: number, y: number, property: Property, graph : joint.dia.Graph);
+ public setProperty(property : Property, graph : joint.dia.Graph): void;
+ public setRelativePosition(deltaX : number, deltaY : number): void;
}
}
declare module "core/editorCore/model/DefaultDiagramNode" {
@@ -226,6 +210,7 @@ declare module "core/editorCore/model/DefaultDiagramNode" {
import { PropertiesPack } from "core/editorCore/model/PropertiesPack";
import { PropertyEditElement } from "core/editorCore/model/PropertyEditElement";
import { DiagramNode } from "core/editorCore/model/DiagramNode";
+ import { DiagramContainer } from "core/editorCore/model/DiagramContainer";
export class DefaultDiagramNode implements DiagramNode {
private logicalId;
private jointObject;
@@ -234,24 +219,29 @@ declare module "core/editorCore/model/DefaultDiagramNode" {
private constPropertiesPack;
private changeableProperties;
private imagePath;
- private propertyEditElement;
+ private propertyEditElements;
+ private parentNode;
private resizeParameters;
private lastMousePosition;
private boundingBox;
- constructor(name: string, type: string, x: number, y: number, width: number, height: number, properties: Map, imagePath: string, id?: string, notDefaultConstProperties?: PropertiesPack);
+ constructor(name: string, type: string, x: number, y: number, width: number, height: number,
+ properties: Map, imagePath: string, id?: string, notDefaultConstProperties?: PropertiesPack);
pointermove(cellView: any, evt: any, x: any, y: any): void;
initPropertyEditElements(zoom: number): void;
- getPropertyEditElement(): PropertyEditElement;
+ getTextProperties(): joint.shapes.basic.Text[];
+ getPropertyEditElements(): Map
getLogicalId(): string;
getName(): string;
getType(): string;
+ getParentNode(): DiagramContainer;
getX(): number;
getY(): number;
getSize(): string;
setPosition(x: number, y: number, zoom: number, cellView: joint.dia.CellView): void;
setSize(width: number, height: number, cellView: joint.dia.CellView): void;
+ setParentNode(parent: DiagramContainer): void;
getImagePath(): string;
- getJointObject(): joint.shapes.devs.ImageWithPorts;
+ getJointObject(): joint.shapes.basic.Generic;
getConstPropertiesPack(): PropertiesPack;
setProperty(key: string, property: Property): void;
getChangeableProperties(): Map;
@@ -268,6 +258,42 @@ declare module "core/editorCore/model/DefaultDiagramNode" {
private static isBottomBorderClicked(bbox, x, y, paddingPercent);
}
}
+declare module "core/editorCore/model/DiagramContainer" {
+ import { DefaultDiagramNode } from "core/editorCore/model/DefaultDiagramNode";
+ import { PropertiesPack } from "core/editorCore/model/PropertiesPack";
+ import { Property } from "core/editorCore/model/Property";
+ import { DiagramNode } from "core/editorCore/model/DiagramNode";
+ export class DiagramContainer extends DefaultDiagramNode {
+ private childrenNodes;
+ constructor(name: string, type: string, x: number, y: number, width: number, height: number,
+ properties: Map, imagePath: string, id?: string,
+ notDefaultConstProperties?: PropertiesPack);
+ getChildrenNodes(): Set;
+ addChild(node: DiagramNode): void;
+ removeChild(node: DiagramNode): void;
+ }
+}
+declare module "core/editorCore/model/DiagramNode" {
+ import { PropertyEditElement } from "core/editorCore/model/PropertyEditElement";
+ import { DiagramElement } from "core/editorCore/model/DiagramElement";
+ import { DiagramContainer } from "core/editorCore/model/DiagramContainer";
+ export interface DiagramNode extends DiagramElement {
+ getX(): number;
+ getY(): number;
+ getImagePath(): string;
+ getSize(): string;
+ getParentNode(): DiagramContainer;
+ setPosition(x: number, y: number, zoom: number, cellView: joint.dia.CellView): void;
+ setSize(width: number, height: number, cellView: joint.dia.CellView): void;
+ setParentNode(parent: DiagramContainer): void;
+ getPropertyEditElements(): Map
+ getTextProperties(): joint.shapes.basic.Text[];
+ initResize(bbox: any, x: number, y: number, paddingPercent: any): void;
+ completeResize(): void;
+ isResizing(): boolean;
+ pointermove(cellView: any, evt: any, x: any, y: any): void;
+ }
+}
declare module "core/editorCore/model/SubprogramNode" {
import { PropertiesPack } from "core/editorCore/model/PropertiesPack";
import { Property } from "core/editorCore/model/Property";
@@ -275,7 +301,9 @@ declare module "core/editorCore/model/SubprogramNode" {
export class SubprogramNode extends DefaultDiagramNode {
private subprogramDiagramId;
private textObject;
- constructor(name: string, type: string, x: number, y: number, width: number, height: number, properties: Map, imagePath: string, subprogramDiagramId: string, id?: string, notDefaultConstProperties?: PropertiesPack);
+ constructor(name: string, type: string, x: number, y: number, width: number, height: number,
+ properties: Map, imagePath: string, subprogramDiagramId: string, id?: string,
+ notDefaultConstProperties?: PropertiesPack);
getSubprogramDiagramId(): string;
getTextObject(): joint.shapes.basic.Text;
setPosition(x: number, y: number, zoom: number, cellView: joint.dia.CellView): void;
@@ -415,12 +443,27 @@ declare module "core/editorCore/model/commands/ChangeCurrentElementCommand" {
isRevertible(): boolean;
}
}
+declare module "core/editorCore/model/commands/EmbedCommand" {
+ import { Command } from "core/editorCore/model/commands/Command";
+ import { DiagramContainer } from "core/editorCore/model/DiagramContainer";
+ import { DiagramNode } from "core/editorCore/model/DiagramNode";
+ export class EmbedCommand implements Command {
+ private child;
+ private parent;
+ private oldParent;
+ constructor(child: DiagramNode, parent: DiagramNode, oldParent: DiagramContainer);
+ execute(): void;
+ revert(): void;
+ isRevertible(): boolean;
+ }
+}
declare module "core/editorCore/model/commands/SceneCommandFactory" {
import { Command } from "core/editorCore/model/commands/Command";
import { DiagramNode } from "core/editorCore/model/DiagramNode";
import { Link } from "core/editorCore/model/Link";
import { DiagramElement } from "core/editorCore/model/DiagramElement";
import { SceneController } from "core/editorCore/controller/SceneController";
+ import { DiagramContainer } from "core/editorCore/model/DiagramContainer";
export class SceneCommandFactory {
private sceneController;
constructor(sceneController: SceneController);
@@ -431,6 +474,14 @@ declare module "core/editorCore/model/commands/SceneCommandFactory" {
makeRemoveLinkCommand(link: Link): Command;
makeMoveCommand(node: DiagramNode, oldX: number, oldY: number, newX: number, newY: number, zoom: number, cellView: joint.dia.CellView): Command;
makeResizeCommand(node: DiagramNode, oldWidth: number, oldHeight: number, newWidth: number, newHeight: number, cellView: joint.dia.CellView): Command;
+ makeEmbedCommand(child: DiagramNode, parent: DiagramContainer, oldParent: DiagramContainer): Command;
+ }
+}
+declare module "core/editorCore/model/ContainerNodeType" {
+ import { NodeType } from "core/editorCore/model/NodeType";
+ import { Property } from "core/editorCore/model/Property";
+ export class ContainerNodeType extends NodeType {
+ constructor(name: string, propertiesMap: Map, image: string, path?: string[]);
}
}
declare module "core/editorCore/controller/SceneController" {
@@ -467,12 +518,14 @@ declare module "core/editorCore/controller/SceneController" {
private cellPointerdownListener(cellView, event, x, y);
private cellPointerupListener(cellView, event, x, y);
private cellPointermoveListener(cellView, event, x, y);
+ private moveNode(cellView, node);
private initDropPaletteElementListener();
private selectElement(jointObject);
private unselectElement(jointObject);
private initCustomContextMenu();
private initDeleteListener();
private removeCurrentElement();
+ private getDependentNodes(node);
private initPropertyEditorListener();
private getElementBelow(event, checker?);
}
@@ -511,11 +564,10 @@ declare module "core/editorCore/model/PaletteTree" {
}
declare module "core/editorCore/model/ElementTypes" {
import { PaletteTree } from "core/editorCore/model/PaletteTree";
- import { NodeType } from "core/editorCore/model/NodeType";
export class ElementTypes {
- uncategorisedTypes: Map;
blockTypes: PaletteTree;
flowTypes: PaletteTree;
+ containerTypes: PaletteTree;
linkPatterns: Map;
constructor();
}
@@ -553,8 +605,7 @@ declare module "core/editorCore/controller/parsers/TypesParser" {
private linkPatterns;
private currentImage;
parse(typesJson: any): ElementTypes;
- private parseElementsTypes(elementsTypes);
- private parseGeneralTypes(generalTypes);
+ private parseGeneralTypes(elementsTypes, category);
private parsePaletteTypes(paletteTypes);
private createNodeTypes(typeObject);
private parseSubtypes(categories, path);
@@ -622,19 +673,23 @@ declare module "core/editorCore/view/SubprogramPaletteView" {
declare module "core/editorCore/controller/PaletteController" {
import { NodeType } from "core/editorCore/model/NodeType";
import { ElementTypes } from "core/editorCore/model/ElementTypes";
- import { PaletteTree } from "core/editorCore/model/PaletteTree";
import { SubprogramDiagramNode } from "core/editorCore/model/SubprogramDiagramNode";
import { DiagramScene } from "core/editorCore/model/DiagramScene";
export class PaletteController {
private subprogramsSelector;
private blocksSelector;
private flowsSelector;
- initDraggable(): void;
- initClick(paper: DiagramScene): void;
+ private paper;
+ private elementTypes;
+ private nodesTypesMap;
+ init(paper: DiagramScene, elementTypes: ElementTypes, nodesTypesMap: Map): void;
+ reload(): void;
+ searchPaletteReload(event: Event): void;
+ private initDraggable();
+ private initClick();
appendSubprogramsPalette(subprogramDiagramNodes: SubprogramDiagramNode[], nodeTypesMap: Map): void;
- appendBlocksPalette(paletteTypes: PaletteTree): void;
- appendFlowsPalette(paletteTypes: PaletteTree): void;
- searchPaletteReload(event: Event, elementTypes: ElementTypes, nodesTypesMap: Map): void;
+ private appendBlocksPalette(paletteTypes);
+ private appendFlowsPalette(paletteTypes);
private appendPaletteContent(selector, content);
private clearPaletteContent(selector);
}
@@ -745,4 +800,4 @@ declare module "core/editorCore/controller/parsers/DiagramJsonParser" {
};
protected parseId(idString: string): string;
}
-}
+}
\ No newline at end of file
diff --git a/editor-service/src/main/webapp/app/common/menu/exporters/DiagramThriftExporter.ts b/editor-service/src/main/webapp/app/common/menu/exporters/DiagramThriftExporter.ts
index aa5b68ed..80eba867 100644
--- a/editor-service/src/main/webapp/app/common/menu/exporters/DiagramThriftExporter.ts
+++ b/editor-service/src/main/webapp/app/common/menu/exporters/DiagramThriftExporter.ts
@@ -43,6 +43,8 @@ export class DiagramThriftExporter extends DiagramExporter {
newNode.logicalId = node.getLogicalId();
newNode.graphicalId = node.getJointObject().id;
newNode.type = node.getType();
+ if (node.getParentNode())
+ newNode.parentId = node.getParentNode().getJointObject().id;
newNode.properties = this.exportProperties(node.getChangeableProperties());
var nameProperty = new TProperty();
@@ -124,15 +126,24 @@ export class DiagramThriftExporter extends DiagramExporter {
}
protected exportProperties(properties: Map) {
- var newProperties = [];
- for (var propertyName in properties) {
- var type: string = properties[propertyName].type;
- var newProperty = new TProperty();
+ let newProperties = [];
+ for (let propertyName in properties) {
+ let property: Property = properties[propertyName];
+
+ let type: string = property.type;
+ let newProperty = new TProperty();
+
+ if (type === "string") {
+ newProperty.x = property.coordinates.x;
+ newProperty.y = property.coordinates.y;
+ }
+
type = (type === "string" || type === "combobox" || type == "checkbox" || type == "dropdown") ?
"QString" : type;
newProperty.name = propertyName;
- newProperty.value = properties[propertyName].value;
+ newProperty.value = property.value;
newProperty.type = type;
+
newProperties.push(newProperty);
}
return newProperties;
diff --git a/editor-service/src/main/webapp/app/common/menu/parsers/DiagramThriftParser.ts b/editor-service/src/main/webapp/app/common/menu/parsers/DiagramThriftParser.ts
index e762bc02..710ae3e4 100644
--- a/editor-service/src/main/webapp/app/common/menu/parsers/DiagramThriftParser.ts
+++ b/editor-service/src/main/webapp/app/common/menu/parsers/DiagramThriftParser.ts
@@ -6,6 +6,8 @@ import {Property} from "core/editorCore/model/Property";
import {Link} from "core/editorCore/model/Link";
import {NodeType} from "core/editorCore/model/NodeType";
import {DefaultDiagramNode} from "core/editorCore/model/DefaultDiagramNode";
+import {DiagramContainer} from "core/editorCore/model/DiagramContainer";
+import {ContainerNodeType} from "core/editorCore/model/ContainerNodeType";
import {DiagramNode} from "core/editorCore/model/DiagramNode";
import {DiagramParts} from "core/editorCore/model/DiagramParts";
import {DiagramJsonParser} from "core/editorCore/controller/parsers/DiagramJsonParser";
@@ -14,6 +16,7 @@ export class DiagramThriftParser extends DiagramJsonParser {
public parse(diagram: TDiagram, nodeTypesMap: Map, linkPatterns: Map): DiagramParts {
var diagramParts: DiagramParts = this.parseNodes(diagram, nodeTypesMap, 0, 0);
diagramParts.linksMap = this.parseLinks(diagram, nodeTypesMap, linkPatterns, 0, 0);
+ this.setEmbedding(diagramParts.nodesMap, diagram.nodes);
return diagramParts;
}
@@ -33,10 +36,10 @@ export class DiagramThriftParser extends DiagramJsonParser {
return 0;
});
- var x: number = 0;
- var y: number = 0;
- var width: number = 0;
- var height: number = 0;
+ let x: number = 0;
+ let y: number = 0;
+ let width: number = 0;
+ let height: number = 0;
for (var j = 0; j < propertiesObject.length; j++) {
var propertyName = propertiesObject[j].name;
@@ -48,6 +51,9 @@ export class DiagramThriftParser extends DiagramJsonParser {
if (typeProperties.hasOwnProperty(propertyName)) {
var property:Property = new Property(typeProperties[propertyName].name,
typeProperties[propertyName].type, propertiesObject[j].value);
+ let xPropertyPosition: number = propertiesObject[j].x;
+ let yPropertyPosition: number = propertiesObject[j].y;
+ property.setCoordinates(xPropertyPosition, yPropertyPosition);
changeableLogicalProperties[propertyName] = property;
} else if (propertyName === "position") {
var position: string = propertiesObject[j].value;
@@ -62,9 +68,13 @@ export class DiagramThriftParser extends DiagramJsonParser {
}
}
- var node: DiagramNode = new DefaultDiagramNode(name, type, x, y, width, height,
- changeableLogicalProperties,
- nodeTypesMap[nodeObject.type].getImage(), nodeObject.graphicalId);
+ var node: DiagramNode;
+ if (nodeTypesMap[type] instanceof ContainerNodeType)
+ node = new DiagramContainer(name, type, x, y, width, height, changeableLogicalProperties,
+ nodeTypesMap[nodeObject.type].getImage(), nodeObject.graphicalId);
+ else
+ node = new DefaultDiagramNode(name, type, x, y, width, height, changeableLogicalProperties,
+ nodeTypesMap[nodeObject.type].getImage(), nodeObject.graphicalId);
return node;
}
@@ -117,7 +127,7 @@ export class DiagramThriftParser extends DiagramJsonParser {
if (targetId !== "ROOT_ID") {
targetObject = {id: targetId};
} else {
- targetObject = this.getTargetPosition(configuration);;
+ targetObject = this.getTargetPosition(configuration);
}
var jointObject: joint.dia.Link = linkPatterns[linkObject.type].clone();
@@ -132,4 +142,14 @@ export class DiagramThriftParser extends DiagramJsonParser {
return new Link(jointObject, nodeType.getShownName(), nodeType.getName(), properties);
}
+ protected setEmbedding(nodesMap: Map, nodeObjects: TDefaultDiagramNode[]) {
+ for (var i = 0; i < nodeObjects.length; i++) {
+ if (!nodeObjects[i].parentId)
+ continue;
+ var child: DiagramNode = nodesMap[nodeObjects[i].graphicalId];
+ var parent: DiagramContainer = nodesMap[nodeObjects[i].parentId];
+ parent.getJointObject().embed(child.getJointObject());
+ child.setParentNode(parent);
+ }
+ }
}
\ No newline at end of file
diff --git a/editor-service/src/main/webapp/app/robots/diagram/controller/RobotsDiagramEditorController.ts b/editor-service/src/main/webapp/app/robots/diagram/controller/RobotsDiagramEditorController.ts
index 9533b378..3e4d2c3b 100644
--- a/editor-service/src/main/webapp/app/robots/diagram/controller/RobotsDiagramEditorController.ts
+++ b/editor-service/src/main/webapp/app/robots/diagram/controller/RobotsDiagramEditorController.ts
@@ -27,9 +27,7 @@ export class RobotsDiagramEditorController extends DiagramEditorController {
document.addEventListener('mouseup', (event) => { this.gesturesController.onMouseUp(event) } );
$("#" + scene.getId()).mousemove((event) => { this.gesturesController.onMouseMove(event) } );
$("#elements-search").on('input', (event) => {
- this.paletteController.searchPaletteReload(event, this.elementTypes, this.nodeTypesMap);
- this.paletteController.initClick(this.diagramEditor.getScene());
- this.paletteController.initDraggable();
+ this.paletteController.searchPaletteReload(event);
} );
(scene as any).on('cell:pointerdown', (cellView, event, x, y): void => {
diff --git a/editor-service/src/main/webapp/images/bpmn/subprocess.svg b/editor-service/src/main/webapp/images/bpmn/subprocess.svg
new file mode 100644
index 00000000..01f4b96f
--- /dev/null
+++ b/editor-service/src/main/webapp/images/bpmn/subprocess.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/editor-service/src/main/webapp/resources/js/require/main.js b/editor-service/src/main/webapp/resources/js/require/main.js
index cf9bae2e..681a7fad 100644
--- a/editor-service/src/main/webapp/resources/js/require/main.js
+++ b/editor-service/src/main/webapp/resources/js/require/main.js
@@ -1,26 +1,57 @@
requirejs.config({
baseUrl: 'resources/js/compiled',
paths: {
- //angular
- angular: '../libs/angular/angular'
+ angular: '../libs/angular/angular',
+
+ jquery: '../libs/jquery/jquery',
+ jqueryui: '../libs/jquery/ui/jquery-ui',
+ jquerytree: '../libs/jquery/treeview/jquery.treeview',
+ jqueryline: '../libs/jquery/line/jquery.line',
+
+ lodash: '../libs/lodash/lodash',
+ backbone: '../libs/backbone/backbone',
+
+ bootstrap: '../libs/bootstrap/bootstrap',
+
+ jointjs: '../libs/joint/joint'
},
shim: {
angular: {
exports: 'angular',
+ },
+ jqueryui: ['jquery'],
+ jquerytree: ['jquery'],
+ jqueryline: ['jquery'],
+ bootstrap: ['jquery']
+
+ },
+ map: {
+ '*': {
+ 'underscore': 'lodash'
}
}
});
requirejs(
- ['angular',
- 'require/app',
- 'robots/RootDiagramController',
- 'robots/diagram/controller/RobotsDiagramEditorController',
- 'robots/twoDModel/implementations/engine/TwoDModelEngineFacadeImpl',
- 'bpmn/diagram/controller/BpmnDiagramEditorController',
- 'core/editorCore/controller/DiagramEditorController']
+ ['jointjs']
+ , function (joint) {
+ console.log('Adding joint to global');
+ this.joint = joint;
+ });
+
+requirejs(
+ ['angular', 'jointjs',
+ 'jquery', 'jqueryui', 'jquerytree', 'jqueryline',
+ 'lodash', 'backbone',
+ 'bootstrap',
+ 'require/app',
+ 'robots/RootDiagramController',
+ 'robots/diagram/controller/RobotsDiagramEditorController',
+ 'robots/twoDModel/implementations/engine/TwoDModelEngineFacadeImpl',
+ 'bpmn/diagram/controller/BpmnDiagramEditorController',
+ 'core/editorCore/controller/DiagramEditorController']
, function (angular) {
- console.log('Bootstraping Angular called');
- angular.bootstrap(document, ['myApp']);
-});
+ console.log('Bootstraping Angular called');
+ angular.bootstrap(document, ['myApp']);
+ });
diff --git a/parent.iml b/parent.iml
index 084831ca..e1561d23 100644
--- a/parent.iml
+++ b/parent.iml
@@ -20,5 +20,16 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/thrift-definitions/struct/Diagram.thrift b/thrift-definitions/struct/Diagram.thrift
index 872b43e9..91cc702c 100644
--- a/thrift-definitions/struct/Diagram.thrift
+++ b/thrift-definitions/struct/Diagram.thrift
@@ -5,18 +5,21 @@ struct TProperty {
2 : string name,
3 : string value,
4 : string type,
- 5 : i32 position
+ 5 : i32 position,
+ 6 : double x,
+ 7 : double y,
}
struct TDefaultDiagramNode {
1 : string id,
2 : string logicalId,
3 : string graphicalId,
- 4 : string jointObject,
- 5 : string type,
- 6 : double x,
- 7 : double y,
- 8 : set properties
+ 4 : string parentId,
+ 5 : string jointObject,
+ 6 : string type,
+ 7 : double x,
+ 8 : double y,
+ 9 : set properties
}
struct TLink {
diff --git a/ui-testing/ui-testing.iml b/ui-testing/ui-testing.iml
index fe03b019..73da7316 100644
--- a/ui-testing/ui-testing.iml
+++ b/ui-testing/ui-testing.iml
@@ -1,5 +1,5 @@
-
+
@@ -21,6 +21,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+