// +--------------------------------------------------------------------------+ \\
// ¦ DCR 1.0 - DCR Graphs Library, All Copy Rights Reserved by DCRSolutions ¦ \\
// +--------------------------------------------------------------------------¦ \\
// ¦ Author Shahzad Mirza, DCRSolutions, TEO ¦ \\
// +--------------------------------------------------------------------------+ \\
/* Main DCR Library Function
* @example
* <!doctype html>
* <html>
* <head>
* <meta charset="utf-8">
* <title>Demo App</title>
* </head>
* <body>
* <script src="jquery.js"></script>
* <script src="xml2json.js"></script>
* <script src="DCR.js"></script>
* <script>
* // We can create Apps with the DCR library and can fill out the missing functionalities of DCR Library with these Apps.
* //Example plugin to display Events by Roles in list items fashion
* ;(function (DCR) {
* DCR.fn.showEventsByRoles = function (reqContainer) {
* $('#'+reqContainer).append('<h4>Roles</h4>');
* var outPut = '<ul>';
* for (var i = 0; i < this.Roles.length; i++) {
* var curRoleNEvents = this.getEventsByRole(this.Roles[i]);
* outPut += '<li>'+this.Roles[i]+'</li>';
* if(curRoleNEvents.length>0){
* outPut += '<ul>';
* for (var j = 0; j < curRoleNEvents.length; j++) {
* outPut += '<li>'+curRoleNEvents[j].custom.label+'</li>';
* };
* outPut += '</ul>';
* }
* };
* outPut += '</ul>';
* $('#'+reqContainer).append(outPut);
* }
* })(DCR)
* // Basic Demo application HTML structure and how to include the Library files:
* $(document).ready(function () {
* var myApp = new DCR();
*
* $('#loadGraph').on('click', function () {
* var fileXML = $('#xmlInput').val();
* if(fileXML==" " || fileXML == ""){
* alert('Invalid Input');
* return;
* }
* myApp.loadXML(fileXML).showEventsByRoles("result");
* //graph will reset first by default by this function just in case user clicks several time
* //so each time new graph is to be laoded properly
* });
* })
*
* </script>
* <textarea id="xmlInput"></textarea>
* <input type="submit" id="loadGraph" value="Load DCR Graph" />
* <div id="result"></div>
* </body>
* </html>
*/
(function (window, fun) {
// AMD support
if (typeof define === "function" && define.amd) {
// Define as an anonymous module
define([], function() {
return fun(window);
});
} else {
// DCR adds itself to window
fun(window);
}
}(window, function (window) {
/** global functions */
/** This function will find distance between two points, will return the distance as Number. This function is used in the DCR to create connections mostly.
*
* @function lineDistance
*
* @param {object} point1 First Point containing x and y location
* @param {object} point2 Second Point containing x and y location
*
* @example lineDistance({'x':10, 'y':10}, {'x':20, 'y':20});
* // returns 14.142135623730951
*
* @returns {Number} Returns the distance between two points.
*/
lineDistance = function( point1, point2 ) {
var xs = 0;
var ys = 0;
xs = point2.x - point1.x;
xs = xs * xs;
ys = point2.y - point1.y;
ys = ys * ys;
return Math.sqrt( xs + ys );
}
//this function gets a point on the line at and distance provided in as d
getPointOnLine = function (P1, P2, d, LStart) {
var mag = lineDistance(P1, P2);
if(mag===0){
mag = 0.5;
}
if(LStart==true){
d = -mag-d;
}
var P3x = P2.x + d * (P2.x - P1.x) / mag;
var P3y = P2.y + d * (P2.y - P1.y) / mag;
//console.clear();
//console.log(P3x+'------'+P3y);
return {x:P3x, y:P3y};
}
//this function gets middle point on the line and returns the distance
getMiddlePointOnLine = function (P1, P2){
var xd = P1.x - P2.x;
var yd = P1.y - P2.y;
return Math.sqrt(xd * xd + yd * yd);
}
toNumber = function (n) {
var nStr = (n + "");
if(nStr.indexOf(".") > -1)
nStr = nStr.replace(".","").replace(/\d+$/, function(m){ return --m; });
return nStr.replace(/(\d+)e\+?(\d+)/, function(m, g1, g2){
return g1 + new Array(+g2).join("0") + "";
})
}
replaceSlashes = function (str) {
return (str + '').replace(/\\/g, '\\\\');
}
fixSlashes = function (str) {
//'\\'.replace(/\\/g, '\');
return (str + '').replace(/\\\\/g, '\\');
}
isInteger = function (num) {
return (num ^ 0) === num;
}
isDate = function(date) {
return (new Date(date) !== "Invalid Date") && !isNaN(new Date(date));
}
//http://stackoverflow.com/questions/770523/escaping-strings-in-javascript
addSlashes = function(str) {
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}
/** This function will convert a string to XML friendly version by converting the potential HTML tags to thier unicodes, it is used with the htmlEncode Function
*
* @function htmlEscape
*
* @param {string} str String to esape the HTML tags
*
* @example htmlEscape('<div>test</div>');
* //returns "<div>test</div>"
*
* @returns {string} Returns the escaped string.
*/
htmlEscape = function(str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
//.replace(/'/g, ''')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>')
}
htmlEscapeEntity = function (str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, "<")
.replace(/>/g, '>')
}
/** This function will Encode an HTML string to XML friendly version, it is used with the htmlEncode Function, it create a in-memory div, set it's inner text(which jQuery automatically encodes) then grab the encoded contents back out. The div never exists on the page.
*
* @function htmlEncode
*
* @param {string} value String HTML to be Encoded
*
* @example htmlEncode('<div>test</div>');
* //returns "<div>test</div>"
*
* @see {@link htmlEscape} for further information.
*
* @returns {string} Returns the Encoded string.
*/
htmlEncode = function(value){
//console.log($('<div/>').text(value).html());
value = $('<div/>').text(value).text();
value = htmlEscape(value);
return value;//$('<div/>').text(value).html();
}
/** This function will Decode a string to HTML friendly version.
* @function htmlDecode
*
* @param {string} value String HTML to be Encoded
*
* @example htmlDecode("<div>test</div>");
* //returns '<div>test</div>'
*
* @returns {string} Returns the Decode string as HTML.
*/
htmlDecode = function (value){
//console.log($('<div/>').html(value).text());
return $('<div/>').html(value).text();
}
//converting events to objects/json
function parseXML( events){
var arr = [];
var skipElements = [];
var fragmentElements = [];
//setting up parentId on each event
$(events).each(function(index, el){
var curType = el.getAttribute("type");
if($(el).attr('parentId')==undefined || el.parentNode.nodeName == 'event'){
$(el).attr('parentId',el.parentNode.getAttribute('id'));
$(el).attr('parentGUID',el.parentNode.getAttribute('guid'));
}
if((curType=="form" && el.getAttribute("referId")!=undefined) || ((curType=="form" || curType=="subprocess") && el.getAttribute("fragmentId")!=undefined && el.getElementsByTagName("dcrgraph").length>0)){
//if referred event, remove all of its childs from events array
$(el).find("events event").each(function (idx, elx) {
skipElements.push(elx)
})
}
//console.log(el);
if($.inArray(el, skipElements)<0){
//if this is form element then don't process it now
arr.push($.xml2json(el))
}
});
return arr;
}
//same as events converting function but generic
xmlToJsonArray = function (obj){
var arr = [];
$(obj).each(function(index, el){
if(!this.hasAttribute('type')){
this.setAttribute('type', this.nodeName);
}
this.setAttribute('nodeType', this.nodeName);
arr.push($.xml2json(this));
});
return arr;
}
/** Function to convert a string to XML document format.
*
* @function stringToXML
*
* @param {string} oString - String to be converted as XML document
*
* @example stringToXML('<div>test</div>');
* //returns and XML document
*
* @returns {XML} Returns the string as an XML document.
*/
stringToXML = function (oString) {
//code for IE
if (window.ActiveXObject) {
var oXML = new ActiveXObject("Microsoft.XMLDOM"); oXML.loadXML(oString);
return oXML;
}
// code for Chrome, Safari, Firefox, Opera, etc.
else {
return (new DOMParser()).parseFromString(oString, "text/xml");
}
}
//function to sort array of objects based on the attr to sort provided
sortArray = function (prop, arr, isNumber) {
prop = prop.split('.');
var len = prop.length;
arr.sort(function (a, b) {
var i = 0;
while (i < len) {
a = a[prop[i]];
b = b[prop[i]];
i++;
}
if (isNumber) {
a = parseInt(a, 0);
b = parseInt(b, 0);
}else{
a = a.toLowerCase();
b = b.toLowerCase();
}
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return arr;
}
/** Function to convert an XML document format to string.
*
* @function XMLtoString
*
* @param {string} oXML - XML to be converted as string.
*
* @returns {string} Returns the XML document as string.
*/
XMLtoString = function (oXML) {
return new XMLSerializer().serializeToString(oXML);
}
/** Function to get a value of an attibute's value in the URL.
*
* @function getQueryVariable
*
* @param {string|number} variable - A variable to found its value.
*
* @returns {string|number} Returns the value of an attribute passed.
*/
getQueryVariable = function (variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false);
};
/** Function to check if item exists in and array of objects based on the property provided.
*
* @function checkForMatch
*
* @param {array} array Array in which the item should be looked for
* @param {string} propertyToMatch Item to be matched
* @param {string} valueToMatch Item to be matched with
* @param {boolean} lowerCase Check if the values to be matched should both be compared in lowercase or not
*
* @returns {integer} Returns returns the index of the element, if element not found then returns -1.
*/
checkForMatch = function (array, propertyToMatch, valueToMatch, lowerCase){
if(lowerCase==true){
for(var i = 0; i < array.length; i++){
if(array[i][propertyToMatch].toLowerCase() == valueToMatch.toLowerCase())
return i;
}
}else{
for(var i = 0; i < array.length; i++){
if(array[i][propertyToMatch] == valueToMatch)
return i;
}
}
return -1;
}
/** Function to return value of Object
*
* @function getPropValue
*
* @param {array} array Array in which the item should be looked for
* @param {string} propertyToMatch Item to be matched
*
* @returns {any} Returns value of property.
*/
getPropValue = function(obj, key) {
return key.split(".").reduce(function(o, x) {
return (typeof o == "undefined" || o === null || 0 === undefined) ? undefined : o[x];
}, obj);
}
hasProp = function(obj, key) {
return key.split(".").every(function(x) {
if(typeof obj != "object" || obj === null || obj === undefined || ! x in obj)
return false;
obj = obj[x];
return true;
});
}
function checkPath(base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
//function to find and event in the DCR graphXML, graphXML should be in XML format
findInXML = function (eventId, graphXML){
var curGraph = new DCR();
return curGraph.loadXML(graphXML).getById(eventId)
}
/*function calculateNLevels(childEvent, conEvent){
//set the +1 to N level of the child as of container event
//setting the child event nLevel to the one of the parent level +1
childEvent.nLevel = parseInt(conEvent.nLevel + 1);
}*/
/** Main DCR Class, This will return a new instance of the Graph Class. This new object will be used for creating, editing or using the DCR graphs. This is added in to the Window context to be used and extended by user in plugins
* @class DCR
* @example var myApp = new DCR();
* @returns {object} Returns an object of Graph Class.
*/
function D() {
return new D._Graph;
}
D.about = {
version: 1.1,
details: "DCR Core Library",
author: "Shahzad Mirza",
created: "January 2015",
updated: "19 August 2020"
};
D.toString = function () {
return "You are using DCR Library "+ D.about.version + " by " + D.about.author;
};
/**
* Core Class to handle the Graph Operations.
* This will be returned by the main DCR function to be utilized by the users or apps.
* Graph Prototype will have the most common graph related functions, such as load/write XML
* users can extend this class prototype in plugins.
*
* @class Graph
* @property {string} XML - Stores the XML of the graph.
* @property {array} Events - Stores all the events of graph as objects of Element Class.
* @property {array} ExternalEvents - Stores all external events of graph while loading the XML of the graph, its items are in XML format.
* @property {array} Processes - Stores all the Processes of graph as objects of Element Class.
* @property {array} Parents - Stores Parent elements of graph as objects of Element Class.
* @property {array} Childs - Stores all Child elements of graph as an array which has child object (instance of Element Class) on 0 index and a string id of its parent object on index 1.
* @property {array} Connections - Array to store All Connections of graph as objects of Connection Class.
* @property {array} AllConnections - This array is used as temporary container to store all the connections from XML on loading for supporting Nesting levels.
* @property {array} Roles - Array to store All Roles of graph in string format.
* @property {array} Groups - Array to store All Groups of graph in string format.
* @property {array} AllParameters - This array is used for temporary purposes on loading the graph XML to store all the parameters of the graph supporting Nesting levels.
* @property {array} Parameters - Array to store All the Unique Parameters of graph as an object {id: "a", type: "variable", value: "10"}.
* @property {array} AllParametersSim - This array is used for temporary purposes on loading the graph XML to storing all the parameters of simulation in the graph supporting Nesting levels.
* @property {array} ParametersSim - Array to store All the Unique Parameters for simulation of graph as an object {id: "a", type: "variable", value: "10"}.
* @property {array} EventTypes - Stores Event Types of graph as strings.
* @property {string} Title - Contains the Title of Graph.
* @property {string} Description - Contains Description for the Graph.
* @property {string} Documentation - Contains Documentation for the Graph.
* @property {number} ZoomLevel - An integer of values -9 to 9 ranging (10% to 190%).
* @property {number} FilterLevel - Array to store All Connections of graph.
* @property {array} FilterRoles - Array to store All Roles in string format for filteration of graph.
* @property {array} FilterGroups - Array to store All Groups in string format for filteration of graph.
* @property {number} eCount - Contains Events count.
* @property {number} pCount - Contains Processes count.
* @property {array} ProcessedPs - Temporary container used in loading of graph XML so once traversed processes are not traversed again.
* @property {array} ProcessedProcesses - Temporary container used in writing of graph XML so once XML of processes is writter they are not used again as recursive functions are used for supporting N level of nesting.
* @property {array} ProcessedEvents - Temporary container used in writing of graph XML so once XML of Events is written they are not used again as recursive functions are used for supporting N level of nesting.
* @property {array} AllSharedEvents - While witing XML shared events are stored in this array to be used later.
*
* @returns {object} Returns Graph Object.
*/
var Graph = function () {
//Graph globals
this.XML = null,
this.CurrentEl = null,
this.CurrentREl = null, //to store the cur relation selected
this.Events = [],
this.ReferredEvents = [],
this.ExternalEvents = [],
this.Processes = [],
this.ReferredPros = [],
this.ReferredForms = [],
this.Fragments = [],
this.Parents = [],
this.Childs = [],
this.Connections = [],
this.AllConnections = [],//temporary holds the connection until all the elements of graph are loaded
this.ReferredCons = [],//referredConnections of the graph
this.Highlights = [], //graph highlighted text if any
this.HighlightLayers = [], //graph highlighted layers if any
/* Graph resources*/
this.Roles = [],
this.ReferredRoles = [],
this.Groups = [],
this.ReferredGroups = [],
this.AllParameters = [], //this will store all the prams of graph
this.Parameters = [], //this will store all unique prams of graph
this.GlobalStore = [], //this will store all unique prams of graph
this.ReferredParams = [], //this will store all unique prams of graph
this.EventTypes = [],
this.ReferredEventTypes = [],
this.Phases = [],
this.ReferredPhases = [],
/* Graph related properties*/
this.Title = "DCR",
this.Description = "DCR Process",
this.Documentation = "",
this.Language = "en-US", //code for languages
this.Domain = "process", //standard, legal, process, other
this.ZoomLevel = 0, //this is the current Zoom level of graph
this.MinZoom = -9,
this.MaxZoom = 9,
this.FilterLevel = -1, //this is the current Selection filter level //graphFilterLevel
this.InsightFilter = false, // show insight Fitler Events
this.FilterRoles = [],
this.FilterGroups = [],
this.FilterPhases = [],
this.GlobalFilter = true,
this.Exercise = false,
this.SendText = '',
this.CancelText = '',
this.HideCancel = false,
this.FormShowInitialPhase = 1,
//basic counters
this.eCount = this.Events.length,
this.pCount = this.getProcessCount();
this.fCount = this.getFormCount();
//graph other globals
this.ProcessedPs = [], //used in loading of the XML graph
this.ProcessedProcesses = [], //used in writing the XML of graph
this.ProcessedReferGraphs = [], //used in writing the XML of graph
this.ProcessedEvents = [], //used in writing the XML of graph
this.AllSharedEvents = [], //used in writing the XML of graph, to be used for execution log
this.FormGroupStyle = "Normal",
this.FormLayoutStyle = "Horizontal",
this.DataTypesStatus = "hide", //show, hide
this.GlobalMarking = {}, //global markings
this.BG = "#ffffff",
this.ProsDisabled = false, //boolean used to identify if processes in graph are disabled or not
this.Disabled = false, //boolean used to identify if the graph is to be disabled for editing or not
this.LogContainer = []; //array to store changes of graph for revision history
this.Simulation = {}; //object to store main simulation of graph
this.Time = null; //object to store time related info of graph simulation
this.DeadLine = null; //object to store time related info of graph simulation
this.Delay = null; //object to store time related info of graph simulation
this.Keywords = []; //to store keywords of graph
this.Type = '0',
this.Options = {
labels : {
activity : 'Activity',
process : 'Process'
}
}
}
D._Graph = Graph;
var conproto = D.con = {};
/**
* Main Connection Class, it will be used to create connection b/w Events or Events to Multi Instance Processes
*
* @class Connection
*
* @property {object} graph - Main Graph Object.
* @property {object} from - Stores the connection from object which is instance of Element class.
* @property {object} to - Stores the connection to object which is instance of Element class.
* @property {string} type - Stores the type of connection as string.
* @property {string} guard - Stores Guard for the connection as string.
* @property {number} level - Stores filter level of the connection greater than or equal to Zero.
* @property {string} desc - Stores description of the connection as string.
*
* @param {object} graph - A graph object of the current application is passed so it can be used to update the garph easily.
* @param {object} from - An element object is passed as source element from where connection will originate.
* @param {object} to - An element object is passed as target element to where connection will end up.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn', 'coresponse', update.
* @param {string} guard - Guard for the connection as string.
* @param {number} level - filter level of the connection greater than or equal to Zero.
* @param {string} desc - Description of the connection as string.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'}).connectTo(myApp.createEvent({'id':'Exit'}), 'condition');
* //this will create an Event Element with Enter Id and will create a condition relation from this element to another new Exit element
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'}).connectFrom(myApp.createEvent({'id':'Exit'}), 'condition');
* //this will create an Event Element with Enter Id and will create a condition relation from another Exit element to this Enter
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'}).createConnection(myApp.createEvent({'id':'Exit'}), 'condition', true);
* //this will create an Event Element with Enter Id and will create a condition relation from another Exit element to this Enter
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'});
* myApp.createEvent({'id':'Exit'});
* myApp.createConnection(myApp.getById('Enter'), myApp.getById('Exit'), 'response');
* //this will create an Event Element Enter and Exit, then we can use the app to create a response connection between two by finding them by their Ids.
*
* @returns {object} Returns Connection Object.
*/
var Connection = function (graph, from, to, color, type, guard, level, desc, time, groups, strong, link, businessRule, valueExpression) {
//setting some basic objects for element
this.guid = GUID();
this.graph = graph;
this.from = from;
this.to = to;
this.type = type;
this.level = parseInt(level) || 0;
this.description = desc || '';
this.guard = guard || '';
this.valueExpression = valueExpression || '';
this.time = time || '';
this.link = link || ''; // if any link already exists then set it up
this.strong = strong || false // if a strong relation, as we need to link it with its pair: for strong condition: condition + milestone
this.businessRule = businessRule || false
// this is a linked strong relation
if(this.strong==true && this.link.trim().length==0){
this.link = this.getId(); // get current relation id
}
if(groups != undefined && groups.length>0){
var curGroups = groups.toString().split(',');
var elGroups = [];
for (var i = 0; i < curGroups.length; i++) {
var index = checkForMatch(this.graph.Groups, 'title', curGroups[i]);
if(index>=0){
elGroups.push(this.graph.Groups[index]);
}else{
var newGroup = {'id': GUID(), 'title':curGroups[i], 'sequence':0, 'description': ''};
this.graph.Groups.push(newGroup);
elGroups.push(newGroup);
}
};
this.groups = elGroups; elGroups = [];
}else{
this.groups = [];
}
if(color==undefined || color.length<1){
switch(this.type){
case 'condition':
this.color = '#FFA500';
break;
case 'response':
this.color = '#1E90FF';
break;
case 'coresponse':
this.color = '#795548';
break;
case 'include':
this.color = '#29A81A';
break;
case 'exclude':
this.color = 'red';
break;
case 'milestone':
this.color = '#BC1AF2';
break;
case 'update':
this.color = '#b0bfc3';
break;
case 'spawn':
this.color = '#334960';
break;
default:
this.color = 'black';
break;
}
}else{
this.color = color;
}
//get the obj and set its properties accordingly
//for(var k in objAttrs) this[k]=objAttrs[k];
}
Connection.prototype = conproto;
conproto.constructor = Connection;
//function to check if the curConenction is a referred connection based on the events data
conproto.isReferCon = function () {
for(var i =0 ; i<this.graph.Connections.length; i++){
if(this.graph.Connections[i].guid == this.guid){
if(this.graph.Connections[i].from.isReferEvent()==true && this.graph.Connections[i].to.isReferEvent()==true){
return true;
}
}
}
return false;
}
conproto.remove = function () {
//see if the item is a refer item child then don't allow delte on this.
if(this.isReferCon()==true){
alert('This operation is not allowed for referred element');
return false;
}
if(this.graph.Connections.indexOf(this)!= -1){
// remove connection sibling first and then remove connection
var sibling = this.hasSibling()
if(sibling){ sibling.remove() }
removeHighlights(this.graph, this);
this.graph.Connections.splice(this.graph.Connections.indexOf(this), 1);
}
return true;
}
// function to find siblings for strong relations and to check if there is any missing sibling connection
conproto.hasSibling = function (strong) {
var self = this,
link = this.link || this.getId(),
type = strong && this.type=='milestone'? 'condition': strong && this.type=='exclude'? 'include' : this.type; // if this is sibling and has strong then then for it
return this.graph.Connections.find(function (con) {
return con.link == link && con.type == type && con!=self;
})
}
// check if current connection is sibling
conproto.isSibling = function () {
return (this.type == 'milestone' && typeof this.link !='undefined' && this.link.trim().length>0) || (this.type == 'exclude' && typeof this.link !='undefined' && this.link.trim().length>0)? true: false;
}
// check if current connection has strong sibling
conproto.isOrphanSibling = function () {
return this.isSibling() && !this.hasSibling(true)? true: false;
}
function removeHighlights(graph, item) {
//remove any associated highlights
graph.Highlights = graph.Highlights.filter(function (el) {
//remove the items from highlight
el.items = el.items.filter(function (elx) {
return elx!=item
})
//if no item associated then remove highlight or if item is comment as comments don't have any items associated to them
return el.items.length>0 || el.type === "comment";
})
}
conproto.getId = function (fullPath) {
if(fullPath==true) return this.from.getPath() +"--"+ this.type +"--"+ this.to.getPath()
return this.from.id +"--"+ this.type +"--"+ this.to.id
}
var highlightproto = D.highlight = {};
/**
* Main Highlight Class, it will be used to create highlights for DCR
*
* @class Highlight
*
* @property {string} guid - Unique GUID for highlight.
* @property {Graph} graph - Main Graph Object.
* @property {string} type - Stores the type of highlight as string, default type will be `activity` if none provide, others can be: 'role', 'relation', 'suggestion', 'alias', 'comment', 'note'.
* @property {array} items - Stores items of the highlight with which the it is associated with, e-g `Element`, `Connection` or Roles object.
* @property {array} layers - Stores layers of the highlight with ranges of the selection in that layer associated with highlight e-g: { layer: HighlightLayer, ranges: [HighlighterRange]}
* @property {array} attrs - Stores any custom attributes to be added for nodes of the Highlight { name: NAME OF ATTRIBUTE, value: VALUE OF ATTRIBUTE}
*
* @param {Graph} graph - A graph object of the current application is passed so it can be used to update the garph easily.
* @param {object} props - An object is passed with basic or updates to basic properties of the highlight object.
*
*
* @returns {Highlight} Returns Highlight Object.
*/
var Highlight = function (graph, props) {
//setting some basic objects for element
var self = this;
this.guid = GUID();
this.graph = graph;
this.type = 'activity';
this.items = [];
this.layers = [];
this.attrs = [];
//get the obj and set its properties accordingly
for(var k in props) this[k]=props[k];
//TODO: for now we will use default layer, but when layers are implemented we need to handle accordingly
if(this.layers!=undefined && this.layers.layer!=undefined){
if(!Array.isArray(this.layers.layer)){
//if it is not an array then make it an array
this.layers.layer = [this.layers.layer];
}
this.layers = this.layers.layer.map(function (layer) {
//check if those layers exist in the graph
var index = checkForMatch(graph.HighlightLayers, 'name', layer.name) // TODO: need to escape name if that is required from user
if(index>-1){
//set ranges for those layers
var currentLayer = {
layer: graph.HighlightLayers[index],
ranges: []
}
setHighlightRanges(self, currentLayer, layer.ranges)
return currentLayer;
}
})
}else if(this.layers!=undefined && Array.isArray(this.layers)){
//get the default layer for the graph and add the highlights there,
// TODO: need to update the code to support adding items to other layer, when layers are implemented.
var defaultLayer = graph.getDefaultHighlightLayer();
if(defaultLayer!=undefined){
var hasLayer = getLayerInHighlight(this, defaultLayer);
if(!hasLayer){
var currentLayer = {
layer: defaultLayer,
ranges: []
}
this.layers.push(currentLayer)
//check if ranges have been passed then add those ranges in this layer
setHighlightRanges(this, currentLayer, this.ranges)
}
}
}else{
this.layers = [];
}
if(this.items!=undefined && this.items.item!=undefined){
if(!Array.isArray(this.items.item)){
//if it is not an array then make it an array
this.items.item = [this.items.item];
}
this.items = this.items.item.map(function (item) {
//find the item in graph and add it to highlight
if(item.id == undefined || item.id == "undefined" || typeof item.id == undefined){
console.warn("undefined item id provided for highlight item ", item)
return "undefined";
}
return item.id
}).filter(function (item) {
return item!=="undefined" //remove any items which have undefined id
})
}else if(this.items!=undefined && !Array.isArray(this.items)){
this.items = []
}
}
function setHighlightRanges(highlight, layer, ranges) {
if(ranges!=undefined && ranges.range!=undefined){
if(!Array.isArray(ranges.range)){
//if it is not an array then make it an array
ranges.range = [ranges.range];
}
ranges.range.map(function (range) {
//find the item in graph and add it to highlight
addHighlightRange(highlight, layer, range)
})
}else if(ranges!=undefined && Array.isArray(ranges)){
ranges.map(function (range) {
//find the item in graph and add it to highlight
addHighlightRange(highlight, layer, range)
})
}
}
Highlight.prototype = highlightproto;
highlightproto.constructor = Highlight;
function getLayerInHighlight(highlight, layer) {
return highlight.layers.find(function (item) {
return item.layer.name == layer.name // TODO: need to escape name if that is required from user
})
}
// Functions for Highlight, like add/update/remove etc
// TODO: APIs for highlights
/*
Content
deleteText
getContents
getLength
getText
insertEmbed
insertText
setContents
setText
updateContents
Selection
getBounds
getSelection
setSelection
*/
function addHighlightRange(highlight, layer, range){
if(typeof layer !== 'undefined'){
var range = new HighlightRange(highlight, range);
layer.ranges.push(range)
return range;
}else{
events.fire("error", {
message: "Something went wrong while adding Highlight Range",
module: "DCR",
fileName: "DCR.js",
functionName: "addHighlightRange",
parameters: JSON.stringify({highlight: highlight, layer: layer, range: range})
})
}
}
/** Add a range to Highlight
*
* @memberof Highlight
* @method addRange
*
* @param {HighlightLayer} layer - A `HighlightLayer` object is required to add the range to that layer for current Highlight
* @param {object} range - An object containing range details e-g: {start = null; end = null; text = null}
*
* @returns {Highlight} Returns an object of `Highlight` Class.
*/
highlightproto.addRange = function (layer, range, returnRange) {
//if layer then check if that layer exisit for highlight, else use defualt layer
var hasLayer = getLayerInHighlight(this, layer);
if (!hasLayer) {
hasLayer = {
layer: layer,
ranges: []
};
this.layers.push(hasLayer);
}
if(hasLayer.ranges !=undefined && Array.isArray(hasLayer.ranges)){
var range = addHighlightRange(this, hasLayer, range)
}
return returnRange ? range : this;
}
function removeHighlightRange(highlight, layer, range){
if(typeof layer !== 'undefined'){
var index = layer.ranges.indexOf(range);
if (index !== -1) {
layer.ranges.splice(index, 1);
}
}else{
events.fire("error", {
message: "Something went wrong while removing Highlight Range",
module: "DCR",
fileName: "DCR.js",
functionName: "removeHighlightRange",
parameters: JSON.stringify({highlight: highlight, layer: layer, range: range})
})
}
}
/** Remove a range from Highlight
*
* @memberof Highlight
* @method removeRange
*
* @param {HighlightLayer} layer - A `HighlightLayer` object is required to add the range to that layer for current Highlight
* @param {object} range - An object containing range details e-g: {start = null; end = null; text = null}
*
* @returns {Highlight} Returns an object of `Highlight` Class.
*/
highlightproto.removeRange = function (layer, range) {
//if layer then check if that layer exisit for highlight, else use defualt layer
var hasLayer = getLayerInHighlight(this, layer);
if(hasLayer && hasLayer.ranges !=undefined && Array.isArray(hasLayer.ranges)){
removeHighlightRange(this, hasLayer, range)
}
return this;
}
/** Add an item to Highlight
*
* @memberof Highlight
* @method addItem
*
* @param {object} item - An object either of Class `Element`, `Connection` or Roles of graph is to be passed.
*
* @returns {Highlight} Returns an object of `Highlight` Class.
*/
highlightproto.addItem = function (item) {
if(this.items !=undefined && Array.isArray(this.items)){
this.items.indexOf(item)<0? this.items.push(item): null
}
return this;
}
/** Removes an item from Highlight, if there is no item then the Highlight is removed from Graph Highlights.
*
* @memberof Highlight
* @method removeItem
*
* @param {object} item - An object either of Class `Element`, `Connection` or Roles of graph is to be passed.
*
* @returns {Highlight} Returns an object of `Highlight` Class.
*/
highlightproto.removeItem = function (item) {
removeHighlights(this.graph, item)
return this;
}
//TODO: if we want to remove associated items as well, we have pass removeItems= true
/** Remove Highlight from graph Highlights
*
* @memberof Highlight
* @method remove
*
* @param {bool} removeItems - TODO: If we want to remove associated items as well, we have pass removeItems= true.
*
* @returns {bool} Always returns true.
*/
highlightproto.remove = function (removeItems) {
var self = this;
this.graph.Highlights = this.graph.Highlights.filter(function (el) {
return el!=self
})
return true;
}
/** Get the Id of the items in Highlight
*
* @memberof Highlight
* @method getItemId
*
* @param {object} item - An object either of Class `Element`, `Connection` or Roles of graph is to be passed.
*
* @returns {string} Returns id of the item or undefined.
*/
highlightproto.getItemId = function (item) {
if(item==undefined || item == null) return;
var itemId = undefined;
switch (this.type) {
case 'activity':
itemId = item.id
break;
case 'role':
itemId = replaceSlashes(htmlEncode(item.title))
break;
case 'relation':
itemId = item.getId()
break;
default:
break;
}
return itemId;
}
var highlightrangeproto = D.highlightrange = {};
/**
* Main Highlight Range Class, it will be used to create ranges for highlights in DCR
*
* @class HighlightRange
*
* @property {string} guid - Unique GUID for range.
* @property {Highlight} highlight - `Highlight` object with which the range is associated with.
* @property {number} start - Stores start index of range from text.
* @property {number} end - Stores end index of range from text.
* @property {string} text - Stores Text for the highlight as string.
*
* @param {Highlight} highlight - A graph object of the current application is passed so it can be used to update the garph easily.
* @param {object} props - An object is passed with basic or updates to basic properties of the range object, currently we don't mutate the props but in future it will be supported.
*
*
* @returns {HighlightRange} Returns `HighlightRange` Object.
*/
var HighlightRange = function (highlight, props) {
//setting some basic objects for element
this.guid = GUID();
this.highlight = highlight || null;
this.text = props.text? props.text: '';
this.start = props.start && !isNaN(parseInt(props.start))? parseInt(props.start): 0;
this.end = props.end && !isNaN(parseInt(props.end))? parseInt(props.end): this.text.length;
//TODO: get the obj and set its properties accordingly
// for(var k in props) this[k]=props[k];
}
HighlightRange.prototype = highlightrangeproto;
highlightrangeproto.constructor = HighlightRange;
/** Updates start/End positions for a range
*
* @memberof HighlightRange
* @method updatePosition
*
* @param {object} positions - An object with positions is to be passed, e-g: {start = null; end = null;}.
*
* @returns {HighlightRange} Returns an object of `HighlightRange` Class.
*/
highlightrangeproto.updatePosition = function (positions) {
this.start = positions.start;
this.end = positions.end;
return this;
}
/** Get postion of a range containing start and end value of the range.
*
* @memberof HighlightRange
* @method getPosition
*
* @returns {object} Returns an object with postions of a range.
*/
highlightrangeproto.getPosition = function () {
return {start: this.start, end: this.end};
}
var highlightlayerproto = D.highlightlayer = {};
/**
* Main HighlightLayer Class, it will be used to create layers for Highlights in DCR
*
* @class HighlightLayer
*
* @property {string} guid - Unique GUID for highlight layer.
* @property {Graph} graph - Main Graph Object.
* @property {string} type - Store layer type, will have several layers of Highlighted text in graph, e-g: - the law, the process description (default), the best practice etc.
* @property {string} text - Stores Text for the layer as string, which will be null by default.
* @property {string} name - Stores Name of the layer, default name will be called 'decription'.
*
* @param {Graph} graph - A graph object of the current application is passed so it can be used to update the garph easily.
* @param {object} props - An object is passed with basic or updates to basic properties of the layer object.
*
*
* @returns {HighlightLayer} Returns `HighlightLayer` Object.
*/
var HighlightLayer = function (graph, props) {
//setting some basic objects for element
this.guid = GUID();
this.graph = graph;
this.type = 'default';
this.default = false; //sets the default layer
this.text = null; //text to be marked
this.name = 'description'; //name of layer
//get the obj and set its properties accordingly
for(var k in props) this[k]=props[k];
}
HighlightLayer.prototype = highlightlayerproto;
highlightlayerproto.constructor = HighlightLayer;
/** Set the Highlight layer as default, will unset default value for all other layers
*
* @memberof HighlightLayer
* @method setAsDefault
*
*
* @returns {HighlightLayer} Returns an object of `HighlightLayer` Class.
*/
highlightlayerproto.setAsDefault = function () {
this.graph.HighlightLayers = this.graph.HighlightLayers.map(function (el) {
el.default = false
return el;
})
this.default = true;
return this;
}
/** Remove Highlight layer
*
* @memberof HighlightLayer
* @method remove
*
* @example var myApp = new DCR();
* myApp.addHighlightLayer({
name: 'New Layer',
text: 'some layer text'
})
* myApp.HighlightLayers[0].remove(); // this will remove the Highlight Layer selected from graph Layers array, i-e 1st layer in the array
*
* @returns {boolean} Returns true or false based on operation success.
*/
highlightlayerproto.remove = function () {
var _this = this;
// we can't remove all layers, there must be atleast 1 layer
if (_this.graph.HighlightLayers.length<2) {
return false;
}
// if we have to remove Highlights as well
if (_this.getHighlights()) {
_this.removeHighlights()
}
_this.graph.HighlightLayers = _this.graph.HighlightLayers.filter(function (el) {
return el!=_this;
})
// if removed layer was default then, we need to make 1st layer final
if (_this.graph.HighlightLayers.length>0 && _this.default == true) {
_this.graph.HighlightLayers[0].default = true
}
return true;
}
/** Remove Highlights related to a layer of graph.
*
* @memberof HighlightLayer
* @method removeHighlights
*
* @example var myApp = new DCR();
* myApp.addHighlightLayer({
name: 'New Layer',
text: 'some layer text'
})
* myApp.addHighlight({layers: [myApp.HighlightLayers[0]]}) // adds the highlight to layer
* myApp.HighlightLayers[0].removeHighlights(); // this will remove the Highlights of Layer
*
*/
highlightlayerproto.removeHighlights = function(){
var _this = this
_this.graph.Highlights = _this.graph.Highlights.map(function (item) {
// remove this layer from highlights
item.layers = item.layers.filter(function (layerItem) {
return layerItem.layer!= _this;
})
return item
}).filter(function (item) { // if highlight doesn't have any layer then remove it
return item.layers.length>0
})
}
/** Get Highlights related to a layer of graph.
*
* @memberof HighlightLayer
* @method getHighlights
*
* @returns {array} - An array of graph Highlights.
*
*/
highlightlayerproto.getHighlights = function(){
var _this = this
return _this.graph.Highlights.filter(function (item) {
return item.layers.find(function (layerItem) {
return layerItem.layer == _this
});
})
}
// TODO: highlight layer remove function, no such requirement at the moment
//elemet proto and el object
var elproto = D.el = {};
var graphproto;
/**
* Main Element Class, it will be used to create Events or Processes
*
* @class Element
*
* @param {object} graph - A graph object of the current application is passed so it can be used to update the garph easily.
* @param {object} objAttrs - These are the main object attributes provided by user they can be custom but XML writing for custum attributes is not supported by CORE library yet.
* @param {boolean} isProcess - This is used to create either an event or a processes possible values can be true, false.
*
* @property {object} graph - Contains the main Graph instance to be used for the operation on the Element.
* @property {string} id - Contains the id of the Element.
* @property {number} nLevel - Contains the nesting level of the Element.
* @property {object} runtimes - Contains the run times information of the Element.
* @property {boolean} runtimes.executed - Defines Executed property of Element, if true is stored then Element will be recognized as an Executed event.
* @property {boolean} runtimes.included - Defines Included property of Element, if true is stored then Element will be recognized as an Included event, else Excluded event.
* @property {boolean} runtimes.pending - Defines Pending property of Element, if true is stored then Element will be recognized as an Pending event, which makes its priority high in simulation.
* @property {enum} scope - Contains scope of the Element, default is "private". Element can also be set a "shared", this is used in creating the connections from with in process to outer scope.
* @property {string} sharedId - if an Element is shared then its id is stored here to be used in generating the XML.
* @property {enum} type - Stores the type of Element, i-e "event" or "process" .
* @property {object} custom - Stores all the custom properties of the Elements stored in the custom section of the standard DCR XML structure.
* @property {object} custom.visualization - Stores the visual information of the Element including dimensions and locations.
* @property {object} custom.visualization.dimension - Stores the dimension of the Element as an object.
* @property {number} custom.visualization.dimension.width - Stores width of the Element.
* @property {number} custom.visualization.dimension.height - Stores height of the Element.
* @property {object} custom.visualization.location - Stores the cordinates of the Element.
* @property {number} custom.visualization.location.xLoc - Stores the value of x axis of the Element.
* @property {number} custom.visualization.location.yLoc - Stores the value of y axis of the Element.
* @property {string} custom.label - Stores the label of the Element.
* @property {string} custom.eventDescription - Stores the description of the Element.
* @property {string} custom.eventType - Stores activity type of the Element associated to it in graph.
* @property {array} custom.groups - Stores all the groups associated to the Element.
* @property {array} custom.roles - Stores all the roles associated to the Element.
* @property {number} custom.level - Stores the filter level value of the Element.
*
* @returns {object} Returns Element Object.
*/
var Element = function (graph, objAttrs, isProcess, isForm, isRefer, isFragment) {
//setting some basic objects for element
this.graph = graph;
var defaultColors = {
bg : '#f9f7ed',
textStroke : '#000000',
stroke : '#cccccc'
}
/* TODO: disabled form colors, remove in future
if(isForm==true){
defaultColors = {
bg : '#b1ea98',
textStroke : '#000000',
stroke : '#6aa84f'
}
}else*/ if(isProcess==true){
defaultColors = {
bg : '#83c5ff',
textStroke : '#000000',
stroke : '#4da1e8'
}
}
var defaultLocation = {
xLoc : 100,
yLoc : 100
},
defaultDimension = {
width: 100,
height : 100
};
var eventDefaults = {
visualization : {
location : defaultLocation,
colors : defaultColors,
dimension : defaultDimension
},
groups : [],
roles : [],
phases : [],
interfaces : [],
interfaceType: null,
level : 0,
label : 'Add title',
costs : 0,
eventDescription : '',
purpose : '',
insight: {
use: false,
deadlineExpression: ''
},
guide : '',
eventData : {
dataType : {
sequence : 0,
width : 'medium',
height : 'medium',
min : 0,
max : 255,
placeholder : undefined,
hintText : undefined,
default : false,
type : undefined,
rules : []
}
},
parentObj : null,
actionType : 'Execute'
};
this.custom = eventDefaults;
//get the obj and set its properties accordingly
for(var k in objAttrs) this[k]=objAttrs[k];
//setting up default labels for activity or the processes
var tempLabel = 'Activity '
if(isProcess==true){
var curID = graph.getNewID(true);
if(isRefer==true){
this.isRefer = true;
this.multiInstance = false;
this.referId = objAttrs.graphID ? objAttrs.graphID : null;
this.dcrgraph = objAttrs.graphXML ? objAttrs.graphXML : null;
}
var tempLabel = curID.label;
//setting this as process
this.baseType = 'process';
if(this.dcrgraph!=undefined){
//this.dcrgraph = new Graph().loadXML(XMLtoString(objAttrs.dcrgraph));
}
if(this.multiInstance=="false" || this.multiInstance==false || isForm==true){
this.multiInstance = false;
}else{
this.multiInstance = true;
}
if(this.parentPro==undefined || this.parentPro==null){
this.parentPro = 'root';
}
}else{
if(isForm==true){
var curID = graph.getNewID(false);
// TODO: need to remove this once and for all once fragments are fully settled in
// this.isRefer = true;
// this.referId = objAttrs.graphID ? objAttrs.graphID : null;
// this.dcrgraph = objAttrs.graphXML ? objAttrs.graphXML : null;
// this.isDynamicForm = isDynamicForm(this)
this.type = 'form'
}else{
var curID = graph.getNewID();
if(isFragment){
this.type = 'subprocess'
}
}
if(isFragment){
this.fragmentId = objAttrs.fragmentId!=undefined ? objAttrs.fragmentId : null;
}
var tempLabel = curID.label;
var types = ["nesting", "subprocess", "form", "transactional_subprocess"]
if(types.indexOf(this.type) == -1){
this.type = 'default';
}
if(this.actionType==undefined || this.actionType!='Open'){
this.actionType = 'Execute'
}
this.baseType = 'event'
}
// if form then add these properties
if(this.type == 'form' || isForm == true){
this.cancelText = objAttrs.cancelText ? fixSlashes(objAttrs.cancelText) : '';
this.sendText = objAttrs.sendText ? fixSlashes(objAttrs.sendText) : '';
this.hideCancel = objAttrs.hideCancel && objAttrs.hideCancel == "true" ? true : false;
this.formShowInitialPhase = objAttrs.formShowInitialPhase && objAttrs.formShowInitialPhase == "2" ? 2 : 1;
}
if(this.computation!=undefined){
this.computation = fixSlashes(this.computation);
}else{
this.computation = ''
}
if(!this.dmnXML){
this.dmnXML = '';
}
if(this.interfaceType && (this.interfaceType=="inner" || this.interfaceType=="outer")){
this.interfaceType = this.interfaceType;
}else{
this.interfaceType = null
}
if(this.commonId && this.commonId=="true"){
this.commonId = true;
}else{
this.commonId = false;
}
if(typeof this.defaultValue!='undefined' && this.defaultValue!=undefined){
this.defaultValue.value = fixSlashes(this.defaultValue.value);
}else{
this.defaultValue = null
}
//setting the Event data Type of Event
if(this.custom.eventData != undefined && this.custom.eventData.dataType != undefined){
var curDataType = {};
for(var m in this.custom.eventData.dataType) curDataType[m] = this.custom.eventData.dataType[m];
var dataType = this.custom.eventData.dataType = curDataType;
dataType.type = dataType.text;
dataType.toString = function () {
return this.type;
};
if(dataType.type=='undefined'){
dataType.type = undefined;
}
if(dataType.type!=undefined){
dataType.type = dataType.type.toLowerCase();
}
//fetch the data from the data type
if(dataType.sequence!=undefined && parseInt(dataType.sequence)>0){
//setting the event form Sequence no
dataType.sequence = parseInt(dataType.sequence);
}else{
//setting the event form Sequence no
dataType.sequence = eventDefaults.eventData.dataType.sequence;
}
if(dataType.width==undefined){
dataType.width = eventDefaults.eventData.dataType.width;
}
if(dataType.min==''){
dataType.min = eventDefaults.eventData.dataType.min;
} else {
if (dataType.type != 'date' && dataType.type != 'datetime') {
if (dataType.type == 'float') {
dataType.min = parseFloat(dataType.min);
}
else{
dataType.min = parseInt(dataType.min);
}
}
}
if(dataType.max==''){
dataType.max = eventDefaults.eventData.dataType.max;
}else{
if (dataType.type != 'date' && dataType.type != 'datetime') {
if (dataType.type == 'float') {
dataType.max = parseFloat(dataType.max);
}
else {
dataType.max = parseInt(dataType.max);
}
}
}
if(dataType.type==='bool'){
this.custom.eventData.dataType.default = dataType.default=="true"?true:false;
}else if(dataType.type==='label' && this.custom.eventData.dataType.default !=undefined){
this.custom.eventData.dataType.default = htmlEncode(this.custom.eventData.dataType.default)
}
//console.log('----////------------')
//console.log(this.custom.eventData)
var curItemRules = [];
if(this.custom.eventData.validationRules!=undefined && this.custom.eventData.validationRules.rule!=undefined){
var curRules = this.custom.eventData.validationRules.rule;
if(Array.isArray(curRules)==false){
curRules = [curRules]
}
for (var i = 0; i < curRules.length; i++) {
var curParameters = [];
var curRule = curRules[i];
var curRuleParamters = [];
if(curRule.parameters!=undefined && curRule.parameters.parameter!=undefined){
var curParameters = curRule.parameters.parameter;
if(Array.isArray(curParameters)==false){
curRuleParamters = [curParameters]
}
for (var j = 0; j < curParameters.length; j++) {
if(curParameters[j].required=="true"){
curParameters[j].required = true
}else{
curParameters[j].required = false;
}
curRuleParamters.push(curParameters[j])
};
}
curRule.parameters = curRuleParamters;
curItemRules.push(curRule)
};
// {id:ruleId, details: curRule, customMessage: customMessage, parameters: ruleParams }
}
this.custom.eventData.dataType.rules = curItemRules;
//console.log(curItemRules);
}else{
//setting the event data type
this.custom.eventData = eventDefaults.eventData;
}
if(this.custom.sequence!=undefined && parseInt(this.custom.sequence)>=0){
this.custom.sequence = parseInt(this.custom.sequence);
}else{
this.custom.sequence = this.graph.getMaxSequence() + 1;
}
//test ? expression1 : expression2
this.id = objAttrs.id ? objAttrs.id : curID.id;
this.guid = objAttrs.guid ? objAttrs.guid : GUID();
this.oriId = objAttrs.oriId ? objAttrs.oriId : this.id;
this.custom.label = objAttrs.custom ? (objAttrs.custom.label ? objAttrs.custom.label : tempLabel) : tempLabel;
this.runtimes = objAttrs.runtimes || {included:true, pending:false, executed: false };
this.nLevel = 0;
//this.isShared = false;
this.sharedId = '';
///storing the event description
if(this.custom.eventDescription ==undefined || this.custom.eventDescription.length<1 ){
this.custom.eventDescription = ''
}
if(this.custom.purpose ==undefined || this.custom.purpose.length<1 ){
this.custom.purpose = ''
}
if(this.custom.guide ==undefined || this.custom.guide.length<1 ){
this.custom.guide = ''
}
if(this.custom.insight ==undefined ){
this.custom.insight = {
use: false,
deadlineExpression: ''
}
}else{
this.custom.insight.use = this.custom.insight.use=="true"? true: false;
this.custom.insight.deadlineExpression = typeof this.custom.insight.deadlineExpression !='undefined' && this.custom.insight.deadlineExpression.length>0? fixSlashes(this.custom.insight.deadlineExpression): '';
}
// check if resouces are available, also check if user id is available
var props = ['id', 'message', 'value'];
this.precondition = props.reduce(function(obj, key){
obj[key] = obj[key]!= undefined? fixSlashes(obj[key]): '';
return obj
}, (this.precondition || {}))
if(this.custom.costs !=undefined && isInteger(parseInt(this.custom.costs))==true){
this.custom.costs = toNumber(this.custom.costs);
}else{
this.custom.costs = 0;
}
//setting the Filter Level of Event also checking if any value exists and it is integer or not
if(this.custom.level!=undefined && parseInt(this.custom.level)>0 && this.custom.level == parseInt(this.custom.level)){
this.custom.level = parseInt(this.custom.level);
}else{
this.custom.level = 1;
}
//if this has a parent id then put it in childs
if(this.parentId && this.parentId.length>0 && this.parentGUID!=undefined&& this.parentGUID.length>0){
//put it in the childs array
graph.Childs.push(this);
var curGUID = this.parentGUID;
this.parentObj = $.grep(graph.Events, function(obj){
return obj.guid == curGUID;
});
if(this.parentObj.length<1){
this.parentObj = $.grep(graph.Processes, function(obj){
return obj.guid == curGUID;
});
if(this.parentObj.length>0){
this.parentObj = this.parentObj[0];
}else{
this.parentObj = null;
}
}else{
this.parentObj = this.parentObj[0];
}
}else{
this.parentObj = null;
}
//getting the event refer type and setting boolean to it
if(this.isRefer != undefined && (this.isRefer=='true' || this.isRefer==true)){
this.isRefer = true;
}else{
this.isRefer = false;
}
//getting the event Roles and setting em to event data
if(this.custom.roles != undefined && this.custom.roles.role != undefined && this.custom.roles.role.length >0){
//if more than one roles are assigned then use loop to assign them
//converting the object to array and assigning it to Element Roles
//check for all the elements passed to it and see if they exist in the Roles of this graph or not
//if they don't exist then add them to Roles of this graph
var curRoles = [];
if(typeof this.custom.roles.role ==='string'){
curRoles.push(fixSlashes(this.custom.roles.role))
}else if(typeof this.custom.roles.role ==='object'){
for (var i = 0; i < this.custom.roles.role.length; i++) {
curRoles.push(fixSlashes(this.custom.roles.role[i]))
};
}
//foreach()this.custom.roles.role.toString().split(',')
var elRoles = [];
for (var i = 0; i < curRoles.length; i++) {
var index = checkForMatch(this.graph.Roles, 'title', curRoles[i]);
var index2 = checkForMatch(this.graph.ReferredRoles, 'title', curRoles[i]);
//if item is in simple array then ok
if(index>=0){
elRoles.push(this.graph.Roles[index]);
}else if(index2>=0){
//if item is in referred array then ok
elRoles.push(this.graph.ReferredRoles[index2]);
}else if(checkForMatch(this.graph.ReferredRoles, 'title', curRoles[i])<0){
//if item not found in any array then add it in the simple array
var newRole = {'id': GUID(), 'title':curRoles[i], 'description': '', 'specification': ''};
this.graph.Roles.push(newRole);
elRoles.push(newRole);
}
};
this.custom.roles = elRoles; elRoles = [];
}else{
this.custom.roles = [];
}
//getting the event Groups and setting em to event data
if(this.custom.groups != undefined && this.custom.groups.group != undefined && this.custom.groups.group.length>0){
var curGroups = [];
if(typeof this.custom.groups.group ==='string'){
curGroups.push(fixSlashes(this.custom.groups.group))
}else if(typeof this.custom.groups.group ==='object'){
for (var i = 0; i < this.custom.groups.group.length; i++) {
curGroups.push(fixSlashes(this.custom.groups.group[i]))
};
}
var elGroups = [];
for (var i = 0; i < curGroups.length; i++) {
var index = checkForMatch(this.graph.Groups, 'title', curGroups[i]);
var index2 = checkForMatch(this.graph.ReferredGroups, 'title', curGroups[i]);
//if item is in simple array then ok
if(index>=0){
elGroups.push(this.graph.Groups[index]);
}else if(index2>=0){
//if item is in referred array then ok
elGroups.push(this.graph.ReferredGroups[index2]);
}else if(checkForMatch(this.graph.ReferredGroups, 'title', curGroups[i])<0){
//if item not found in any array then add it in the simple array
var newGroup = {'id': GUID(), 'title':curGroups[i], 'sequence':0, 'description': ''};
this.graph.Groups.push(newGroup);
elGroups.push(newGroup);
}
};
this.custom.groups = elGroups; elGroups = [];
}else{
this.custom.groups = [];
}
//getting the event Phases
if(this.custom.phases != undefined && this.custom.phases.phase != undefined && this.custom.phases.phase.length>0){
var curPhases = [];
if(typeof this.custom.phases.phase ==='string'){
curPhases.push(fixSlashes(this.custom.phases.phase))
}else if(typeof this.custom.phases.phase ==='object'){
for (var i = 0; i < this.custom.phases.phase.length; i++) {
curPhases.push(fixSlashes(this.custom.phases.phase[i]))
};
}
var elPhases = [];
for (var i = 0; i < curPhases.length; i++) {
var index = checkForMatch(this.graph.Phases, 'title', curPhases[i]);
var index2 = checkForMatch(this.graph.ReferredPhases, 'title', curPhases[i]);
//if item is in simple array then ok
if(index>=0){
elPhases.push(this.graph.Phases[index]);
}else if(index2>=0){
//if item is in referred array then ok
elPhases.push(this.graph.ReferredPhases[index2]);
}else if(checkForMatch(this.graph.ReferredPhases, 'title', curPhases[i])<0){
//if item not found in any array then add it in the simple array
//TODO: need to provide unique sequence for new phase
var newPhase = {'id': GUID(), 'title':curPhases[i], 'sequence':0, 'description': ''};
this.graph.Phases.push(newPhase);
elPhases.push(newPhase);
}
};
this.custom.phases = elPhases; elPhases = [];
}else{
this.custom.phases = [];
}
//getting the event Phases
if(this.custom.interfaces != undefined && this.custom.interfaces.interface != undefined){
var curInterfaces = [];
if(this.custom.interfaces.interface instanceof Object && (this.custom.interfaces.interface instanceof Array)==false ){
curInterfaces.push(this.custom.interfaces.interface)
}else if(this.custom.interfaces.interface instanceof Array){
for (var i = 0; i < this.custom.interfaces.interface.length; i++) {
curInterfaces.push(this.custom.interfaces.interface[i])
};
}
this.custom.interfaces = curInterfaces;
}else{
this.custom.interfaces = [];
}
//setting the scope of the event based on the obj details
var itemScope = '';
if(this.scope!=undefined){
itemScope = this.scope;
}else if(this.custom.eventScope != undefined){
itemScope = this.custom.eventScope;
}
if(itemScope != 'private' && itemScope != 'shared'){
itemScope = 'private';
}
this.scope = itemScope;
//setting the Event Type of Event
if(this.custom.eventType != undefined && this.custom.eventType.length>0){
var curEventTypes = [this.custom.eventType.toString()];
for (var i = 0; i < curEventTypes.length; i++) {
var index = checkForMatch(this.graph.EventTypes, 'title', curEventTypes[i]);
var index2 = checkForMatch(this.graph.ReferredEventTypes, 'title', curEventTypes[i]);
//if item is in simple array then ok
if(index>=0){
this.custom.eventType = this.graph.EventTypes[index];
break;
}else if(index2>=0){
//if item is in referred array then ok
this.custom.eventType = this.graph.ReferredEventTypes[index2];
}else{
this.custom.eventType = undefined;
}
};
}else{
//this.custom.eventType = {'id': GUID(), 'title':'none', 'description': ''};
this.custom.eventType = undefined;
}
if(this.custom.eventType!=undefined && this.custom.eventTypeData != undefined && this.custom.eventTypeData.parameter != undefined){
var elParams = this.custom.eventType.parameters;
var newParameters = [], curParameters = [];
//need to check if these are parameters of selected eventtype or not?
for (var i = 0; i < elParams.length; i++) {
var curParam = {}
for (prop in elParams[i]) {
curParam[prop] = elParams[i][prop]
};
newParameters.push(curParam)
};
if(this.custom.eventTypeData.parameter.constructor === Object){
curParameters.push(this.custom.eventTypeData.parameter)
}else if(this.custom.eventTypeData.parameter.constructor === Array){
for (var i = 0; i < this.custom.eventTypeData.parameter.length; i++) {
curParameters.push(this.custom.eventTypeData.parameter[i])
};
}
for (var i = 0; i < curParameters.length; i++) {
var index = checkForMatch(newParameters, 'title', curParameters[i].title);
if(index>=0){
newParameters[index].value = fixSlashes(curParameters[i].value);
newParameters[index].required = curParameters[i].required;
}
};
this.custom.eventTypeData = newParameters, newParameters = [], curParameters = [];
}else{
this.custom.eventTypeData = [];
}
//setting up some base values it not found in xml
if(this.custom.visualization != undefined){
if(this.custom.visualization.location != undefined){
if(this.custom.visualization.location.xLoc != undefined){
this.custom.visualization.location.xLoc = parseInt(this.custom.visualization.location.xLoc);
}else{
this.custom.visualization.location.xLoc = defaultLocation.xLoc;
}
if(this.custom.visualization.location.yLoc != undefined){
this.custom.visualization.location.yLoc = parseInt(this.custom.visualization.location.yLoc);
}else{
this.custom.visualization.location.yLoc = defaultLocation.yLoc;
}
}else{
this.custom.visualization.location = defaultLocation;
}
if(this.custom.visualization.dimension != undefined ){
if(this.custom.visualization.dimension.width != undefined){
this.custom.visualization.dimension.width = parseInt(this.custom.visualization.dimension.width);
}else{
this.custom.visualization.dimension.width = defaultDimension.width;
}
if(this.custom.visualization.dimension.height != undefined){
this.custom.visualization.dimension.height = parseInt(this.custom.visualization.dimension.height);
}else{
this.custom.visualization.dimension.height = defaultDimension.height;
}
}else{
this.custom.visualization.dimension = defaultDimension
}
if(this.custom.visualization.colors != undefined ){
if(this.custom.visualization.colors.bg == undefined){
this.custom.visualization.colors.bg = defaultColors.bg;
}
if(this.custom.visualization.colors.textStroke == undefined){
this.custom.visualization.colors.textStroke = defaultColors.textStroke;
}
if(this.custom.visualization.colors.stroke == undefined){
this.custom.visualization.colors.stroke = defaultColors.stroke;
}
}else{
this.custom.visualization.colors = defaultColors
}
}else{
this.custom.visualization = {
location : defaultLocation,
colors : defaultColors,
dimension : defaultDimension
}
}
};
Element.prototype = elproto;
elproto.constructor = Element;
//attr('description','test')
/*elproto.attr = function (attr, value) {
//check the arguments
if(arguments.length==0){
return this;
}else if(arguments.length==1){
//get the value of that attribute and show it to user //String.prototype.toLowerCase.call("function")
for(var k in attr){
console.log(attr[k]);
}
}
return this;
}*/
/*//function to calculate the Nesting level of an Event/Process
function calculateNLevel (childEvent, conEvent) {
var conLevel = conEvent.nLevel;
var addition = conLevel;
if(addition==0){
addition = 1;
}
childEvent.nLevel = addition + childEvent.nLevel;
}*/
/** Function to create a connection from one object to another object or vice versa
*
* @memberof Element
* @method createConnection
*
* @param {object} secObj - An Element object is passed as source/target element.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn', 'update'.
* @param {boolean} isFrom - This value determines either to create connectoin to second object passed or from it, default connection will be to the second object.
* @param {string} guard - Guard for the connection as string.
* @param {number} level - filter level of the connection greater than or equal to Zero.
* @param {string} desc - Description of the connection as string.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'});
* myApp.createEvent({'id':'Enter'}).createConnection(myApp.createEvent({'id':'Exit'}), 'response', true);
* //this will create an Event Element Enter and Exit, then we can use the function to create a response connection between two setting source as Exit and target as Enter.
* @returns {object} Returns an object of Connection Class.
*/
elproto.createConnection = function (secObj, type, isFrom, guard, level, desc, time, groups, strong, businessRule, valueExpression){
//if to Element is not in the graph then return null
if($.inArray(secObj, this.graph.Events)===-1){
return null;
}
//if to Element is in the graph then
if(isFrom == true){
this.connectFrom(secObj, type, guard, level, desc, time, groups, strong, businessRule, valueExpression);
}else{
this.connectTo(secObj, type, guard, level, desc, time, groups, strong, businessRule, valueExpression);
}
}
/** Function to create a connection from one object to another object
*
* @memberof Element
* @method connectTo
*
* @param {object} to - An Element object is passed as target element to where connection will end up.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn', 'update'.
* @param {string} guard - Guard for the connection as string.
* @param {number} level - filter level of the connection greater than or equal to Zero.
* @param {string} desc - Description of the connection as string.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'}).connectTo(myApp.createEvent({'id':'Exit'}), 'response');
* //this will create an Event Element Enter and Exit, then we can use the function to create a response connection between two.
* @returns {object} Returns an object of Connection Class.
*/
elproto.connectTo = function (to, color, type, guard, level, desc, time, groups, strong, businessRule, valueExpression){
return this.graph.createConnection(this, to, color, type, guard, level, desc, time, groups, strong, businessRule, valueExpression);
}
/** Function to create a connection from one object to another object, similar to connectTo but this is inverse of it as this selects the calling object as target and the object passed as source.
*
* @memberof Element
* @method connectFrom
*
* @param {object} from - An Element object is passed as target element to where connection will end up.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn', 'update'.
* @param {string} guard - Guard for the connection as string.
* @param {number} level - filter level of the connection greater than or equal to Zero.
* @param {string} desc - Description of the connection as string.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Exit'}).connectFrom(myApp.createEvent({'id':'Enter'}), 'condition');
* //this will create an Event Element Enter and Exit, then we can use the function to create a condition connection between two.
* @returns {object} Returns an object of Connection Class.
*/
elproto.connectFrom = function (from, color, type, guard, level, desc, time, groups, strong, businessRule, valueExpression){
return this.graph.createConnection(from, this, color, type, guard, level, desc, time, groups, strong, businessRule, valueExpression);
}
/* elproto.getConnections = function () {
var curObj = this;
var eventCons = $.grep(this.graph.Connections, function (el) {
return el.to == curObj || el.from == curObj;
});
return eventCons;
}
//getting to/from connections
//default will get connections to, but if passed true the connections from will be get
elproto.getConnections = function (getFrom) {
var curObj = this;
var res = [];
if(getFrom==true){
var eventCons = $.grep(this.graph.Connections, function (el) {
return el.to == curObj;
});
for (var i = 0; i < eventCons.length; i++) {
res.push(eventCons[i].from);
};
}else{
var eventCons = $.grep(this.graph.Connections, function (el) {
return el.from == curObj;
});
for (var i = 0; i < eventCons.length; i++) {
res.push(eventCons[i].to);
};
}
return res;
}*/
/** Function to get all to/from Connections of an Element object.
*
* @memberof Element
* @method getConnections
*
* @param {boolean} fromOrTo - Getting to or from connections according to type provided, if passed true from connections will be returned, if false is passed then to will be returned if nothing is passed then all will be returned.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn'.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Exit'}).connectFrom(myApp.createEvent({'id':'Enter'}), 'condition');
* myApp.getById('Exit').getConnections();
* //this will get all the connections to/from the Exit Element
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Exit'}).connectFrom(myApp.createEvent({'id':'Enter'}), 'condition');
* myApp.getById('Exit').getConnections(true);
* //this will get all the connections to the Exit Element
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Exit'}).connectTo(myApp.createEvent({'id':'Enter'}), 'condition');
* myApp.getById('Exit').getConnections(false);
* //this will get all the connections from the Exit Element
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Exit'}).connectTo(myApp.createEvent({'id':'Enter'}), 'condition');
* myApp.getById('Exit').connectTo(myApp.createEvent({'id':'New'}), 'response');
* myApp.getById('Exit').getConnections(false);
* //this will get all the connections from the Exit Element
* @returns {array} Returns an array of Element object to/from which connections are associated
*/
elproto.getConnections = function (fromOrTo, type, getItems) {
var curObj = this;
var res = [], eventCons = [];
//get all connections from
if(fromOrTo===true){
if(type){
eventCons = $.grep(this.graph.Connections, function (el) {
return el.to == curObj && type==el.type;
});
}else{
eventCons = $.grep(this.graph.Connections, function (el) {
return el.to == curObj;
});
}
//if getItems is true then send Elements
if(getItems===true){
for (var i = 0; i < eventCons.length; i++) {
res.push(eventCons[i].from);
};
}if(getItems===false){
for (var i = 0; i < eventCons.length; i++) {
res.push([eventCons, eventCons[i].from]);
};
}else{
//else send connection objects
res = res.concat(eventCons);
}
}else if(fromOrTo===false){ //get all connections to
if(type){
eventCons = $.grep(this.graph.Connections, function (el) {
return el.from == curObj && type==el.type;
});
}else{
eventCons = $.grep(this.graph.Connections, function (el) {
return el.from == curObj;
});
}
//if getItems is true then send Elements
if(getItems===true){
for (var i = 0; i < eventCons.length; i++) {
res.push(eventCons[i].to);
};
}if(getItems===false){
for (var i = 0; i < eventCons.length; i++) {
res.push([eventCons, eventCons[i].to]);
};
}else{
//else send connection objects
res = res.concat(eventCons);
}
}else{ //get all connections
if(type){
eventCons = $.grep(this.graph.Connections, function (el) {
return ((el.to == curObj || el.from == curObj) && type==el.type);
});
}else{
eventCons = $.grep(this.graph.Connections, function (el) {
return el.to == curObj || el.from == curObj;
});
}
//if getItems is true then send Elements
if(getItems===true){
for (var i = 0; i < eventCons.length; i++) {
//if to is in res then ignore it else add it
if($.inArray(eventCons[i].to, res)===-1 && eventCons[i].to !=curObj){
res.push(eventCons[i].to)
}
//if from is in res then ignore it else add it
if($.inArray(eventCons[i].from, res)===-1 && eventCons[i].from !=curObj){
res.push(eventCons[i].from)
}
//if connection is with it self and is not in res then add it
if($.inArray(eventCons[i].from, res)===-1 && eventCons[i].from == eventCons[i].to && eventCons[i].from == curObj){
res.push(eventCons[i].from)
}
};
}if(getItems===false){
for (var i = 0; i < eventCons.length; i++) {
//if to is in res then ignore it else add it
if($.inArray(eventCons[i].to, res)===-1 && eventCons[i].to !=curObj){
res.push([eventCons[i], eventCons[i].to])
}
//if from is in res then ignore it else add it
if($.inArray(eventCons[i].from, res)===-1 && eventCons[i].from !=curObj){
res.push([eventCons[i], eventCons[i].from])
}
//if connection is with it self and is not in res then add it
if($.inArray(eventCons[i].from, res)===-1 && eventCons[i].from == eventCons[i].to && eventCons[i].from == curObj){
res.push([eventCons[i], eventCons[i].from])
}
};
}else{
//else send connection objects
res = res.concat(eventCons);
}
}
return res;
}
//get connections for only the child items in an Element
elproto.getChildConnections = function () {
var curObjChilds = this.getChilds(), formCons = [];
//get all children for form and their connections only
formCons = curObjChilds.map(function (el) {
return el.getConnections().filter(function (con) {
return (curObjChilds.indexOf(con.from) >-1 && curObjChilds.indexOf(con.to) >-1)
})
})
// flat array:[].concat.apply([], formCons);
formCons = [].concat.apply([], formCons);
//remove duplicates
return formCons.filter(function (el, i) {
return formCons.indexOf(el) == i
})
}
/** checks if event already has connection base on connection Type
* for stong condition (for strong condition to allow/disallow strong condition, also handle weak condition accordingly):
* 1. if we have weak condition, we can't create strong condition // done'
* 2. if we have milestone we can't create strong condition //
* 3. if we have strong condition, we can't create weak condition // done '
* 4. if we have strong condition, we can't create milestone // done'
*
* @param {Element} sourceObj
* @param {string} connectionType
*/
elproto.checkIfAvailableConnection = function(sourceObj, connectionType, strong){
if(sourceObj==null || sourceObj==undefined){
return false;
}
var thisObj = this;
if((connectionType == "scondition" || connectionType == "condition" || connectionType == "milestone") && strong && (!thisObj.checkIfAvailableConnection(sourceObj, 'milestone') || !thisObj.checkIfAvailableConnection(sourceObj, 'condition'))){ // if strong check if there is milestone already
return false;
}else if((connectionType == "logicalInclude" || connectionType == "include" || connectionType == "exclude") && strong && (!thisObj.checkIfAvailableConnection(sourceObj, 'exclude') || !thisObj.checkIfAvailableConnection(sourceObj, 'include'))){
return false
}else if(connectionType == "update" && thisObj == sourceObj){ // The source and target events for a value-arrow cannot be the same. (Because that is the same as shorthand for data event and computational event)
return false
}
// find if connections already has this connection b/w objects or not
return this.graph.Connections.filter(function(el){
return el.to == thisObj && el.type == connectionType && ( thisObj == sourceObj ? el.from == thisObj: el.from == sourceObj );
}).length>0? false : true;
}
function getCommonParentPro (sourceObj, targetObj) {
var commonParent = undefined;
var sourceObjParentPros = sourceObj.getParents(true);
var targetObjParentPros = targetObj.getParents(true);
for (var i = 0; i < sourceObjParentPros.length; i++) {
var curSourceParent = sourceObjParentPros[i];
for (var j = 0; j < targetObjParentPros.length; j++) {
var curTargetParent = targetObjParentPros[j];
if(curSourceParent==curTargetParent){
return curTargetParent;
}
};
};
return undefined;
}
/** Set Default value for an Activity having marking as Executed
*
* @param {object} value e-g: {id: "Activity1", value: "1", isNull: "false", type: "int", nodeType: "variable"}
*
* @returns {Element} Returns an Element object.
*/
elproto.setDefaultValue = function(value) {
this.defaultValue = value;
return this;
}
elproto.canHaveDefaultValue = function() {
return this.getDataType().type && this.runtimes.executed? true: false;
}
//return insight of item
elproto.getActivityType = function() {
return typeof this.custom.eventType != 'undefined' && this.custom.eventType != undefined? this.custom.eventType: null;
}
elproto.getActivityTypeTitle = function(trimmed) {
var activityType = this.getActivityType()
var activityTypeTitle = activityType? activityType.title : ''
return trimmed? activityTypeTitle.trim().length>20? activityTypeTitle.substr(0,20) + '...': activityTypeTitle: activityTypeTitle
}
//return insight of item
elproto.getInsight = function() {
return this.custom.insight;
}
//set insight of item
elproto.setInsight = function(value) {
return this.custom.insight = value;
}
//return dmn Xml definitions only or empty string
elproto.prepareDMNXML = function(xml) {
var definitions = stringToXML(xml).getElementsByTagName('definitions')
if(definitions && definitions.length>0){
return XMLtoString(definitions[0])
}
return ''
}
//return resource user id expression of item
elproto.getResourceExpression = function(key) {
return typeof this.precondition != 'undefined' && typeof this.precondition[key] != 'undefined'? this.precondition[key] : null;
}
//set resource user id expression of item
elproto.setResourceExpression = function(key, value) {
if(typeof this.precondition == 'undefined' || typeof this.precondition[key] == 'undefined'){
throw new Error('el>precondition || key['+key+'] not found');
}
// "user" will be __USER__ in expression
// If no user is on the left of = or !=, then add "user" - replace "user" with "__USER__" in expression.
// TODO: enable following when userid expression validation is implemented
/* if(key == 'value' && value.match('=')!=null){
var expression = '__USER__'
// if has user, just replace it with __USER__, else add __USER__
value = value.match('user')==null? expression + value : value.replace('user', expression)
}*/
return this.precondition[key] = value;
}
//return the full path to item
elproto.getPath = function(skipSelf) {
var path = this.getParents().reverse().map(function (el){
return el.id;
})
if(skipSelf!=true){
path.push(this.id);
}
return path.join(".");
}
//check if this element has more than one parent processes then restrict the connections with it
elproto.checkIfChildConAvailable = function(sourceObj) {
if(sourceObj==null || sourceObj==undefined){
return false;
}
var targetObj = this;
var sourceParentPro = sourceObj.getParentProcess(),
targetParentPro = targetObj.getParentProcess();
//check the source and target parent Pro, if parent pro is same then allow connections
if(sourceParentPro==targetParentPro){
return true;
}else{
//if parent pro is not same for both source/target
// check the nested level of source parent pro and target parent pro
// if they are on same level don't allow connection
// if not same
// check if either of the target or source parent lie +1 or -1 N level of any of them if they lie then allow connection else don't
//if source is in root
if(sourceParentPro==undefined){
//then check for target if it is in pro or not
if((targetObj.isProcess()==true && targetObj.getParentProcess()==sourceParentPro) || targetObj.isProcess()==false){
return true // if target is process and is on 0 level then create spawn relation
}else{
return false;
}
}else if(targetParentPro==undefined){
//then check for target if it is in pro or not
//source can't be a process
if(targetObj.isProcess()==true && targetObj.getParentProcess()!=sourceParentPro){
return false;
}else if(sourceObj.isProcess()==false){
//allow connection
return true;
}else{
return false;
}
}else{
//this has to be much more optimized : this means that source and target are inside the processes
if(sourceObj.isProcess()==true){
//if source is process then no connection is allowed
return false;
}else if(targetObj.isProcess()==true){
//if target is a process then check its nLevel with source
//now check if the source parent is nested in the target parent or not
//or target parent pro is nested in the source processes
if(sourceParentPro==targetParentPro){
//allow connection
return true;
}else{
return false;
}
}else if(sourceObj.isProcess()==false && targetObj.isProcess()==false){
//now check if the exist in same branch or not
//Get all Parents and find the Common Parent of each items
//check which one is direct child of topParent
//if anyone is direct child of topParent then allow connection;
var commonParent = getCommonParentPro (sourceObj, targetObj)
var allowCon = false
if(commonParent!=undefined){
commonParent.getDirectChilds().forEach(function(el){
if(sourceObj==el || targetObj==el){
allowCon = true
console.log(el)
}
})
return allowCon
}
}else{
return false;
}
}
return false;
}
return true;
}
// get master activity id with the event
elproto.isCommonActivity = function() {
return this.commonId
}
elproto.setCommonId = function(curID) {
var idExists = this.graph.checkIfIDExists(curID);
if (!idExists || curID == this.id) {
this.id = curID;
this.commonId = true
return true;
}
return false;
}
elproto.resetCommonId = function(callback) {
/* TODO: need to clear requirements with MMQ
var curID = this.id
this.id = this.graph.checkIfIDExists(curID)? this.graph.uniqueEventID(curID, 1): curID;
*/
this.commonId = false;
if(typeof(callback)=='function'){
callback(this)
}
}
// return true if the event is a refer event else returns false
elproto.isReferEvent = function() {
if(this.isRefer!=undefined && this.isRefer==true){
return true;
}
return false;
}
// return true if the event is allowed to be interface Event else returns false
elproto.canBeInterfaceEvent = function() {
if(this.isRefer!=undefined && this.isRefer==true){
return false;
}
if(this.baseType!=undefined && this.baseType=='event' && this.type!=undefined && this.type=="default" && this.isParent()!=true){
return true;
}
return false;
}
elproto.canBeFragment = function() {
if(this.isRefer!=undefined && this.isRefer==true){
return false;
}
if((this.type =="subprocess" || this.type =="form") && this.isRefer==false && (this.fragmentId==null || this.fragmentId==undefined)){
return true;
}
return false;
}
elproto.updateFragmentId = function(fragmentId, callback) {
if((this.type =="subprocess" || this.type =="form") && this.isRefer==false && (fragmentId!=null && fragmentId!=undefined && typeof fragmentId !='undefined')){
this.fragmentId = fragmentId;
return true
}
return false;
}
elproto.removeFragmentId = function() {
this.fragmentId = null;
}
elproto.resetInterfaces = function(force) {
if(this.canBeFragment() || this.isFragment() || force){
this.custom.interfaces = []
return true
}
return false;
}
// return true if the event is a process else returns false
elproto.isProcess = function() {
if(this.baseType!=undefined && this.baseType=='process'){
return true;
}
return false;
}
// return true if the event is a process else returns false
elproto.isFragment = function() {
if(( this.isSubProcess() == true || this.isForm() == true ) && this.fragmentId!=undefined && this.fragmentId == parseInt(this.fragmentId)){
return true;
}
return false;
}
// return true if the event is a form else returns false
elproto.isForm = function() {
if(this.baseType!=undefined && this.baseType=='event' && this.type!=undefined && this.type=="form"){
return true;
}
return false;
}
// return true if the event is a form else returns false
elproto.isNesting = function() {
if(this.baseType!=undefined && this.baseType=='event' && this.type!=undefined && this.type=="nesting"){
return true;
}
return false;
}
// return true if the event is a form else returns false
elproto.isSubProcess = function() {
if(this.baseType!=undefined && this.baseType=='event' && this.type!=undefined && this.type=="subprocess"){
return true;
}
return false;
}
// return true if the event is a form else returns false
elproto.isReferForm = function() {
if(this.isForm()==true && this.isRefer==true){
return true;
}
return false;
}
//return true if the event has some datatype assigned to it
elproto.isDataTypeEvent = function() {
var hasDataType = this.getDataType();
if(hasDataType!=undefined && hasDataType.type!=undefined){
return true;
}
return false;
}
// return true if the event is a refer event else returns false
elproto.isEvent = function() {
if(this.baseType!=undefined && this.baseType=='event'){
return true;
}
return false;
}
// check if element is present in the arrParents or not
elproto.isParent = function() {
var curEl = this;
var elIsParent = $.grep(this.graph.Parents, function(el){
//console.log('this->'+curEl.id+' el->'+el.id);
return curEl.guid == el.guid;
});
if(elIsParent && elIsParent.length >0){
return true;
}else{
return false;
}
}
// check if element is present in the arrChilds or not
elproto.isChild = function() {
var curEl = this;
var elIsChild = $.grep(this.graph.Childs, function(el){
return curEl.guid == el.guid;
});
if(elIsChild && elIsChild.length >0){
return true;
}else{
return false;
}
}
// returns the sequence of an element for DCR Forms
elproto.getSequence = function() {
var curEl = this,
seq = 0;
if(curEl.custom.sequence !=undefined && curEl.custom.sequence == parseInt(curEl.custom.sequence)){
seq = parseInt(curEl.custom.sequence)
}else if(curEl.custom.eventData!=undefined && curEl.custom.eventData.dataType!=undefined && curEl.custom.eventData.dataType.sequence == parseInt(curEl.custom.eventData.dataType.sequence)){
seq = parseInt(curEl.custom.eventData.dataType.sequence)
}
return seq;
}
// returns the datatype object of element
elproto.getDataType = function() {
var curEl = this,
curDataType = undefined;
//custom.eventData != undefined && curEl.custom.eventData === '[object Array]' && curEl.custom.eventData.length > 0
if(curEl.custom.eventData!=undefined && curEl.custom.eventData.dataType!=undefined){
curDataType = curEl.custom.eventData.dataType
}
return curDataType;
}
//return event data object which contains the dataType and other properties of event data
elproto.getEventData = function() {
var curEl = this,
curDataType = undefined;
//custom.eventData != undefined && curEl.custom.eventData === '[object Array]' && curEl.custom.eventData.length > 0
if(curEl.custom.eventData!=undefined && curEl.custom.eventData.dataType!=undefined){
curDataType = curEl.custom.eventData
}
return curDataType;
}
//return event data object which contains the dataType and other properties of event data, remove datatype of the Element by making its type = undefined
elproto.resetEventData = function() {
if(this.isDataTypeEvent()){
this.custom.eventData.dataType.type = undefined
return true;
}
return false;
}
// TODO: get form for an event or an inline form
elproto.getForm = function () {
}
//return event data object which contains the dataType and other properties of event data
elproto.getGraph = function(isFragment, callback) {
if(isFragment==true){
var graph = new DCR();
getGraph (graph, this.fragmentId, undefined, configs, callback, function (graph, restResponse, referPro, callback) {
if(typeof(callback)=='function'){
callback(graph, restResponse)
}
})
}else if(this.isForm()==true || this.isProcess()==true){
var graph = new DCR();
graph.loadXML(this.dcrgraph);
return graph;
}else{
return this.graph;
}
}
// return interfaces for a fragment
// TODO: WIP need to refine this in future
elproto.getInterfaces = function() {
return this.custom.interfaces.map(function (el) {
return el
});
}
//update element interfaces
elproto.updateInterfaces = function (interfaces, items) {
if(!interfaces){return;}
var curInterfaces = JSON.parse(JSON.stringify( interfaces ));
//validate interfaces from subItems in a fragment graph
// TODO: conform if we need to filter and check if items exist in fragment or not.
this.custom.interfaces = curInterfaces.filter(function (el) {
return items.some(function (elm) {
return elm.id===el.from
})
}).filter(function (el) {
return el.to.trim().length>0
});
return this;
};
//update element interface type
elproto.updateInterfaceType = function (type) {
this.interfaceType = (type == "inner" || type == "outer" )? type : null;
return this;
};
//get element interface type
elproto.getInterfaceType = function () {
return this.interfaceType && (this.interfaceType == "inner" || this.interfaceType == "outer" )? this.interfaceType : null;
};
/** Function to return the direct parent of a child Element
*
* @memberof Element
* @method getParent
*
* @returns {object|null} Returns an Element object.
*/
elproto.getParent = function (){
var curObj = this;
var curItem = $.grep(this.graph.Parents, function (el) {
if(curObj.parentObj!=null){
return el.guid == curObj.parentObj.guid;
}
});
if(curItem.length>0){
return curItem[0];
}
return undefined;
}
/** Function to get all parents of current element.
*
* @memberof Element
* @method getParents
*
* @returns {array} Returns an array of Element objects.
*/
elproto.getParents = function (onlyPros){
if(this.getParent()!=undefined){
var allParents = [],
curParent = this.getParent();
while(curParent!=undefined){
if(onlyPros==true){
if(curParent.isProcess()==true){
allParents.push(curParent);
}
}else{
allParents.push(curParent);
}
curParent = curParent.getParent();
}
if(allParents.length>0){
return allParents
}
}
return [];
}
/** Recursive Function to get all childs of current element.
*
* @memberof Element
* @method getChilds
*
* @param {boolean} skipProChilds - If this value is passed true then all the events with in the processes will be skipped and only those processes will be considered childs including their sibling events.
*
* @returns {array} Returns an array of Element objects.
*/
elproto.getChilds = function (skipProChilds, skipReferFormChilds){
var curObj = this;
//first get all direct childs
var allChilds = [];
var curChilds = $.grep(this.graph.Childs, function (el) {
return el.parentGUID == curObj.guid //&& el[0].parentObj.guid = el.parentGUID;
});
for (var i = 0; i < curChilds.length; i++) {
allChilds.push(curChilds[i]);
allChilds = allChilds.concat(curChilds[i].getChilds(false));
};
if(skipProChilds==true){ //this will filter all the events of the process on N level but will not go inside the child Processes
allChilds.forEach(function(el, i){
if(el.baseType=='process'){
el.getChilds(false).forEach(function (elm, indx) {
allChilds = $.grep(allChilds, function (pEl) {
return pEl!=elm;
})
});
}
});
}
if(skipReferFormChilds==true){ //this will filter all the events of the process on N level but will not go inside the child Processes
allChilds.forEach(function(el, i){
if(el.type=='form' && el.isReferForm()==true){
el.getChilds(false).forEach(function (elm, indx) {
allChilds = $.grep(allChilds, function (pEl) {
return pEl!=elm;
})
});
}
});
}
allChilds.sort(function (a, b) {
return a.id.toLowerCase().localeCompare(b.id.toLowerCase());
});
return allChilds;
}
/** Function to get only direct child of current element.
*
* @memberof Element
* @method getDirectChilds
*
* @returns {array} Returns an array of Element objects.
*/
elproto.getDirectChilds = function () {
var curObj = this;
//if traversing to N levels then get all childs of this element
//if(nLevel==true){
//console.log('hello there!');
//}
var curChilds = $.grep(this.graph.Childs, function(el){
return el.parentGUID == curObj.guid;
});
var curElChild = [];
for (var i = 0; i < curChilds.length; i++) {
curElChild.push(curChilds[i]);
};
return curElChild;
}
/** Function to get the parent process of current Element.
*
* @memberof Element
* @method getParentProcess
*
* @returns {object|null} Returns an object of Element objects or null.
*/
elproto.getParentProcess = function (topParent, isSubProcess){
var obj = this;
var curPar = obj.getParents().filter(function (el) {
return isSubProcess==true? el.baseType == "event" && el.type == "subprocess" : el.baseType == 'process';
})
if(curPar.length>0){
if(topParent==true){
var topPar = curPar[0];
for (var i = 0; i < curPar.length; i++) {
if(curPar[i].nLevel<topPar.nLevel){
topPar = curPar[i]
}
};
return topPar;
}else{
return curPar[0];
}
}
return undefined;
}
/** Function to get the parent form of current Element.
*
* @memberof Element
* @method getParentForm
*
* @returns {object|null} Returns an object of Element objects or null.
*/
elproto.getParentForm = function (topParent){
var obj = this;
var curPar = $.grep(obj.getParents(), function (el) {
return el.isForm()==true;
})
if(curPar.length>0){
if(topParent==true){
var topPar = curPar[0];
for (var i = 0; i < curPar.length; i++) {
if(curPar[i].nLevel<topPar.nLevel){
topPar = curPar[i]
}
};
return topPar;
}else{
return curPar[0];
}
}
return undefined;
}
/** Function to get the the default runtimes of current Element.
*
* @memberof Element
* @method getRuntime
*
* @param {string} eRuntime - The runtime for which the value to get against of current element. possible values are: 'included', 'pending', 'executed'.
*
* @returns {boolean|null} Returns a value of requested property or null.
*/
elproto.getRuntime = function (eRuntime){
if(eRuntime.length<1){
return null;
}
switch(eRuntime){
case 'included':
return this.runtimes.included;
case 'pending':
return this.runtimes.pending;
case 'executed':
return this.runtimes.executed;
default:
return null;
}
}
/** Function to get current Nested level of current Element.
*
* @memberof Element
* @method currentLevel
*
* @returns {integer} Returns the nesting level of current level.
*/
elproto.currentLevel = function (){
//current event is determined based on the event's node classes
// the number of classes on the events will determine its level
/*var totalClassCount = this.node.className.baseVal.split(" ");
totalClassCount = $.grep(totalClassCount, function(el){
return el != '' && el != ' ' && el != 'moving';
});
return totalClassCount.length;*/
/*var curObj = this;
var currentLevel = this.nLevel;
//check the object in childs array, if it is not in childs array return nLevel 0
var isChild = $.grep(this.graph.Childs, function(el){
return el[0] == curObj;
});
if(isChild.length==0){
this.nLevel = 0;
return this.nLevel;
}
//if the obj is in childs array then search for its parent and calculate the nesting
//get the parent of this Event
var curCParentID = isChild[0][1];
var curCParent = $.grep(this.graph.Parents, function(el){
return el.id == curCParentID;
});
calculateNLevel (curObj, curCParent[0])
return curObj.nLevel;*/
return this.nLevel;
}
// return raphael object based on the node element provided
elproto.dashedId = function (){
return this.id.replace(new RegExp(" ", 'g'), "-");
}
//sets colors for the element e-g {bg: "#b1ea98"}
elproto.colors = function(props){
if(props){
for(var k in props) this.custom.visualization.colors[k]=props[k];
}else{
this.custom.visualization.colors
}
}
// transfroms an element to defined types, only works for baseTypeEvents
elproto.transformTo = function (type){
if(this.baseType!="event"){
return undefined
}
var types = ["default", "nesting", "subprocess", "form", "transactional_subprocess"]
if(types.indexOf(type)!= -1){
this.type = type;
if(type!=="default"){
//check if this has any datatype, if any then remove it
this.resetEventData();
}
if(type == "subprocess"){
this.custom.roles = [];
}
var formOptions = {'cancelText': '' , 'sendText': '', 'hideCancel': false, 'formShowInitialPhase': 1};
if(type=="form"){
/* TODO: disabled form colors, remove in future
this.colors({
bg : '#b1ea98',
textStroke : '#000000',
stroke : '#6aa84f'
}) */
// if form doesn't already have these properties then add these
for (var key in formOptions) {
if(!this.hasOwnProperty(key)){
this[key] = formOptions[key];
}
}
}else{
// reset the values
for (var key in formOptions) {
if(this.hasOwnProperty(key)){
this[key] = formOptions[key];
}
}
}
}
}
//this function is used to remove the activity or process form Graph
elproto.remove = function (forceDelete) {
var curEl = this,
graph = this.graph;
//see if the item is a refer item child then don't allow delte on this.
if(curEl.isReferEvent()==true && curEl.isProcess()==false && forceDelete!=true && curEl.getParentProcess()!=undefined || (curEl.getParentProcess()!=undefined && curEl.getParentProcess().isRefer==true)){
alert('This operation is not allowed for referred element')
return false;
}
//check if the unnest is allowed on this element or not?
if(graph.checkIfNestingOpAllowed(curEl, false)==false && forceDelete!=true){
//show message nesting operation can't be performed
alert('Some Connections are Restricting the Operations you want to Perfrom, remove connections and try again')
return false;
}
//if current node is parent then remove its class from all of its children
if(curEl.isParent()==true){
if((curEl.isProcess() && curEl.isRefer==true) || (curEl.isForm() && curEl.isRefer==true)){
//then delete all of its childs
console.log('is Refer process');
curEl.getChilds().forEach(function(el, index){
el.removed = true;
//if this parent has childs then remove them form childs array as well
if(graph.Childs.indexOf(el)!= -1){
graph.Childs.splice(graph.Childs.indexOf(el), 1);
}
//if any child is in the parents array remove it from there too
if(graph.Parents.indexOf(el)!= -1){
graph.Parents.splice(graph.Parents.indexOf(el), 1);
}
//if any child is in the parents array remove it from there too
if(graph.Events.indexOf(el)!= -1){
graph.Events.splice(graph.Events.indexOf(el), 1);
}
//if any child is in the parents array remove it from there too
if(graph.Processes.indexOf(el)!= -1){
graph.Processes.splice(graph.Processes.indexOf(el), 1);
}
//if any child is in the parents array remove it from there too
if(graph.ReferredPros.indexOf(el)!= -1){
graph.ReferredPros.splice(graph.ReferredPros.indexOf(el), 1);
}
if(graph.ReferredForms.indexOf(el)!= -1){
graph.ReferredForms.splice(graph.ReferredForms.indexOf(el), 1);
}
for(var i =0 ; i<graph.Connections.length; i++){
//console.log(connections.length);
if(graph.Connections[i].from == el || graph.Connections[i].to ==el){
graph.Connections[i].graph = null
}
}
})
//remove it from referred Pros array
for(var i =0 ; i<graph.ReferredPros.length; i++){
if(graph.ReferredPros[i] == curEl ){
graph.ReferredPros.splice(i,1);
}
}
for(var i =0 ; i<graph.ReferredForms.length; i++){
if(graph.ReferredForms[i] == curEl ){
graph.ReferredForms.splice(i,1);
}
}
}else{
curEl.getDirectChilds().forEach(function(el, index){
//if this parent has childs then remove them form childs array as well
if(graph.Childs.indexOf(el)!= -1){
graph.Childs.splice(graph.Childs.indexOf(el), 1);
}
//if any child is in the parents array remove it from there too
// if(graph.Parents.indexOf(el)!= -1){
// graph.Parents.splice(graph.Parents.indexOf(el), 1);
// }
//can not do this as unnest will not work for referred elements
//el.unNest(); //simple un nest the direct child
el.parentGUID = undefined;
el.parentId = null;
el.parentObj = null;
el.parentPro = 'root';
var curElLevel = el.nLevel - curEl.nLevel;
el.nLevel = 0;
el.getChilds().forEach(function (elx, index) {
// setting the Nested Level of childs of current Event
if (elx.baseType == 'event') {
elx.nLevel = elx.nLevel - curElLevel;
}
})
});
}
//remove it from parents array
for(var i =0 ; i<graph.Parents.length; i++){
if(graph.Parents[i] == curEl ){
graph.Parents.splice(i,1);
}
}
}
//if current event is child then remove it from child array
//remove it from children array
if(graph.Childs.indexOf(curEl)!= -1){
graph.Childs.splice(graph.Childs.indexOf(curEl), 1);
}
graph.Connections.filter(function (el) {
if(el.from == curEl || el.to ==curEl){
//checks if the current object has any relation with any other object
return el;
}
}).forEach(function (el) {
//then remove all the to and from connections related to current object
el.remove()
})
//finally remove it from arrAllEvents
if(checkForMatch(graph.Events, 'guid', curEl.guid)!= -1){
graph.Events.splice(checkForMatch(graph.Events, 'guid', curEl.guid), 1);
}else if(curEl.isProcess()==true && checkForMatch(graph.Processes, 'guid', curEl.guid)!= -1){
graph.Processes.splice(checkForMatch(graph.Processes, 'guid', curEl.guid), 1);
}
removeHighlights(graph, curEl)
graph.eCount = graph.Events.length;
graph.pCount = graph.getProcessCount();
return true;
}
/** Function to get the bounding box of current Element.
*
* @memberof Element
* @method getBBox
*
* @returns {object} Returns an object containing x, y cordinates of current element along with height and width of it.
*/
elproto.getBBox = function () {
var x = this.custom.visualization.location.xLoc,
y = this.custom.visualization.location.yLoc,
width = this.custom.visualization.dimension.width,
height = this.custom.visualization.dimension.height;
return {
x: x,
y: y,
x2: x+ width,
y2: y+ height,
cx: x+ (width/2),
cy: y+ (height/2),
width: width,
height: height
}
}
elproto.printDetails = function () {
//if (Graph.CurrentEl) {
console.log(this.id);
//console.log(this);
//}
return this;
};
elproto.setID = function (id) {
if (/*Graph.CurrentEl && */arguments.length>0) {
console.log('setting ID');
this.id = id;
}
return this;
};
// function to nest an element in to other element
elproto.nestUnder = function (nestTo) {
// see if the item is a refer item child then don't allow delte on this.
if ((nestTo.isRefer == true) || nestTo.isProcess() == true && nestTo.isRefer == true) {
alert('This operation is not allowed for referred element');
return false;
}
// if the this is already a child then return
if (this.isChild() == true) {
alert(this.custom.label + ' is already nested under, first unnest it and then try again.');
return false;
}
// check if a nesting element is to be nested to one of its own childs then restrict it
if (checkForMatch(this.getChilds(), 'guid', nestTo.guid) >= 0) {
alert(this.custom.label + ' can not be nested in its own childs.');
return false;
}
// check if the unnest is allowed on this element or not?
if (this.graph.checkIfNestingOpAllowed(nestTo, true, this) == false) {
// show message nesting operation can't be performed
alert('Some Connections are Restricting the Operations you want to Perfrom, remove connections and try again')
return false;
}
// if source is Process then don't nest it under an event
if (this.isProcess() == true && nestTo.isProcess() == false) {
alert("A process can't be nested in an activity")
return false;
}
// same element can't be nested in itself
if (this == nestTo) {
alert("same element can't be nested in itself")
return false;
}
// if nestTo has a datatype then remove it: Important! If the activity had a data type it should be removed once something is nested inside.
if(nestTo.isDataTypeEvent()){
nestTo.resetEventData()
}
// add parent and child info to arrParents and arrChilds
// check if parent already exists in arrParents then don't add it more than once
if(checkForMatch(this.graph.Parents, 'guid', nestTo.guid)<0){
this.graph.Parents.push(nestTo);
}
// add source as child in arrChilds
this.graph.Childs.push(this);
this.parentGUID = nestTo.guid;
this.parentId = nestTo.id;
this.parentObj = nestTo;
//TODO: need to handle parentPro for subprocess nesting, it will also have impact on import
//update the type for the items
if(((nestTo.type=="default" && nestTo.getChilds().length<2) || nestTo.type==undefined) && nestTo.isProcess()==false){
nestTo.transformTo("subprocess");
}
// setting the Nested Level of selected Event
this.nLevel = parseInt(nestTo.nLevel) + 1
// check if source has any child already
var nestEl = this;
nestEl.getChilds().forEach(function (el, index) {
// setting the Nested Level of childs of current Event
el.nLevel = el.nLevel + nestEl.nLevel;
})
return true;
}
//function to apply runtimes for the event
elproto.applyMarking = function (markingCase, markValue) {
if (markingCase == 'included') {
// for included runtime
if (markValue == true) {
this.runtimes.included = true;
} else {
this.runtimes.included = false;
}
}else if (markingCase == 'pending') {
// for pending runtime
if (markValue == true) {
this.runtimes.pending = true;
} else {
this.runtimes.pending = false;
}
}else if (markingCase == 'executed') {
// for executed runtime
if (markValue == true) {
this.runtimes.executed = true;
} else {
this.runtimes.executed = false;
}
}
}
elproto.unNest = function () {
// see if the item is a refer item child then don't allow delte on this.
if ((this.getParentProcess() != undefined && this.getParentProcess().isRefer != undefined && this.getParentProcess().isRefer == true) || this.isProcess() == true && this.isRefer == true) {
alert('This operation is not allowed for referred element')
return false;
}
// check if the unnest is allowed on this element or not?
if (this.graph.checkIfNestingOpAllowed(this, false) == false) {
// show message nesting operation can't be performed
alert('Some Connections are Restricting the Operations you want to Perfrom, remove connections and try again')
return false;
}
// if the this is already a child then return
if (this.isChild() != true) {
return false;
}
// remove it from children array
for (var i = 0; i < this.graph.Childs.length; i++) {
if (this.graph.Childs[i].guid == this.guid) {
this.graph.Childs.splice(i, 1)
}
}
// remove parent from arrParents
// check if parent has no more child then remove it from arrParents
var sParent = this.getParent()
if (sParent.getChilds().length == 0 ) {
for (var i = 0; i < this.graph.Parents.length; i++) {
if (this.graph.Parents[i].guid == sParent.guid) {
this.graph.Parents.splice(i, 1)
}
}
//update its type as default if it is nesting type
if(sParent.type== "nesting"){
sParent.transformTo("default");
}
}
//this.nLevel = parseInt(nestTo.nLevel) + 1
var delta = this.nLevel - sParent.nLevel;
this.nLevel = 0;
this.getChilds().forEach(function (el, index) {
// setting the Nested Level of childs of current Event
//if (el.baseType == 'event') {
el.nLevel = el.nLevel - delta;
//}
})
this.parentGUID = null;
this.parentId = null;
this.parentObj = null;
return true;
}
elproto.hasRole = function (role) {
return this.custom.roles.find(function(el){return el==role})
}
elproto.isComputeable = function () {
return this.computation!=undefined && this.computation.trim().length>0? true: false
}
//get computation
elproto.getComputedValue = function () {
var self = this;
if(this.isComputeable()){
var item = this.graph.GlobalStore.find(function (el) {
return el.id == self.id
})
return item!=undefined?item.value: null;
}
}
//to extend the library for plugins usage
D.fn = graphproto = Graph.prototype = D.prototype;
/** Function to reset the graph and its properties
*
* @memberof Graph
* @method reset
*
* @example var myApp = new DCR();
* myApp.createEvent(); //An Event will be added to the graph.
* myApp.reset(); // will reset the graph.
* @returns {object} Returns an object of Graph Class.
*/
graphproto.reset = function () {
//creating a new graph object
var newGraph = new Graph();
//transfering all the new properties to the current object
for(var k in newGraph) this[k]=newGraph[k];
return this;
};
//function to get all child events of DCR Root (supports N level nesting) the Main Paper Variable is DCR
graphproto.getAllRootEvents = function (){
//filtering only Events from curEventChild
var curElEventChilds = [];
this.Events.forEach(function(el){
curElEventChilds.push(el);
});
this.Processes.forEach(function(el){
el.getChilds(false).forEach(function (elm, indx) {
curElEventChilds = $.grep(curElEventChilds, function (pEl) {
return pEl!=elm;
})
})
});
return curElEventChilds;
}
graphproto.getFragmentsList = function (){
return this.Events.filter(function (el) {
return el.isFragment()
}).map(function (el) {
return {Id: el.fragmentId,
Path: el.getPath(),
Interfaces: el.getInterfaces()}
})
}
//function to check if the nesting or the undesting is allowed because of the connections of the obj
graphproto.checkIfNestingOpAllowed = function (targetObj, isNestUnder, sourceObj) {
var graph = this;
var curObjs = [],
curProChilds = [],
curObjsExtCons = [],
curObjsIntCons = [];
//if nest under is active then
if(isNestUnder==true){
//check for csource obj and this obj
if(targetObj.baseType=='process' || targetObj.getParentProcess()!=undefined){
//if nesting inside a process or an event which is inside a process
//then check if the source has any connections
//if has any connections out of its elements then which is not spawn then return true
//else if it has no connections with others than his siblings then allow it
if(sourceObj.baseType=='process'){
//if source is process, then check if it has spawn relation or not
//if it has spawn relation then restrict nesting
var curProDirectChilds = sourceObj.getDirectChilds();
for (var i = 0; i < graph.Connections.length; i++) {
if(graph.Connections[i].to == sourceObj && checkForMatch(curProDirectChilds, "guid", graph.Connections[i].from.guid)<0){
return false;
}
};
//else continue testing on its childs
curObjs = sourceObj.getChilds();
curProChilds = sourceObj.getChilds();
}else{
if(sourceObj.getChilds(false).length>0){
curObjs = sourceObj.getChilds(false);
}
curObjs.push(sourceObj);
//if source is already inside a processes
if(sourceObj.getParentProcess()!=undefined){
return false;
}else{
curProChilds = graph.getAllRootEvents();
}
}
if(targetObj.baseType=='process'){
var curProDirectChilds = targetObj.getDirectChilds();
targProChilds = targetObj.getChilds(true);
}else{
if(targetObj.getParentProcess()!=undefined){
targProChilds = targetObj.getParentProcess().getChilds(true);
}else{
targProChilds = [];
}
}
//check the children of the processes if any of them has a connections with the outer event then it is not allowed to perform the operation
//if the source object doesn't have any connecitons then check its childs for connections
// if they have connections with other processes then restrict the source to nestunder
for (var j = 0; j < curObjs.length; j++) {
for (var i = 0; i < graph.Connections.length; i++) {
if(graph.Connections[i].from == curObjs[j]){
//if this from then check the to of this con
//if the con exists in cur ProChilds then contine else return false and break
if(checkForMatch(curProChilds, "guid", graph.Connections[i].to.guid)<0){
curObjsExtCons.push(graph.Connections[i]);
}else{
curObjsIntCons.push(graph.Connections[i]);
}
}else if(graph.Connections[i].to == curObjs[j]){
//if this to then check the from of this con
//if the con exists in cur ProChilds then contine else return false and break
if(checkForMatch(curProChilds, "guid", graph.Connections[i].from.guid)<0){
curObjsExtCons.push(graph.Connections[i]);
}else{
curObjsIntCons.push(graph.Connections[i]);
}
}
}
}
//also check the levels of source and targets
for (var i = 0; i < curObjsExtCons.length; i++) {
//check if source and target can have connections then allow nesting else reject
if(curObjsExtCons[i].to.baseType=='process' && (curObjsExtCons[i].to == targetObj && checkForMatch(curProDirectChilds, "guid", curObjsExtCons[i].from.guid)<0)){
return false;
}
}
}
return true;
}else{
//if it for unnesting then
if(targetObj.baseType=='process'){
//if source is process, then check if it has spawn relation or not
//if it has spawn relation then restrict nesting
var curProDirectChilds = targetObj.getDirectChilds();
for (var i = 0; i < graph.Connections.length; i++) {
if(graph.Connections[i].to == targetObj && checkForMatch(curProDirectChilds, "guid", graph.Connections[i].from.guid)<0){
return false;
}
};
curObjs = targetObj.getChilds();
curProChilds = targetObj.getChilds();
}else{
return true;
// if(targetObj.getChilds(false).length>0){
// curObjs = targetObj.getChilds(false);
// }
// curObjs.push(targetObj);
// if(targetObj.getParentProcess()!=undefined){
// curProChilds = targetObj.getParentProcess().getChilds(true);
// }else{
// curProChilds = graph.getAllRootEvents();
// }
}
// if(targetObj.getParentProcess()==undefined){
// return true;
// }
//check the children of the processes if any of them has a connections with the outer event then it is not allowed to perform the operation
//if the source object doesn't have any connecitons then check its childs for connections
// if they have connections with other processes then restrict the source to nestunder
for (var j = 0; j < curObjs.length; j++) {
for (var i = 0; i < graph.Connections.length; i++) {
if(graph.Connections[i].from == curObjs[j]){
//if this from then check the to of this con
//if the con exists in cur ProChilds then contine else return false and break
if(checkForMatch(curProChilds, "guid", graph.Connections[i].to.guid)<0){
curObjsExtCons.push(graph.Connections[i]);
}else{
curObjsIntCons.push(graph.Connections[i]);
}
}else if(graph.Connections[i].to == curObjs[j]){
//if this to then check the from of this con
//if the con exists in cur ProChilds then contine else return false and break
if(checkForMatch(curProChilds, "guid", graph.Connections[i].from.guid)<0){
curObjsExtCons.push(graph.Connections[i]);
}else{
curObjsIntCons.push(graph.Connections[i]);
}
}
}
}
//if this is a subprocess and any of its child has external connection then return false as process to process connections are not allowed
//unless the item to which connection is made has no parent process i-e it exists in root process
for (var i = 0; i < curObjsExtCons.length; i++) {
//check if source and target can have connections then allow nesting else reject
if(curObjsExtCons[i].from.getParentProcess()!=undefined && curObjsExtCons[i].to.getParentProcess()!=undefined && curObjsExtCons[i].to != targetObj){
return false;
}
}
return true;
}
}
//checks if the id of an event already exists or not
graphproto.checkIfIDExists = function(itemID){
var idStatus = false;
var items = this.Events.concat(this.Processes)
for (var i = 0; i < items.length; i++) {
if(items[i].id === itemID){
idStatus = true;
}
};
return idStatus;
}
// gets unique ID for the Events
graphproto.uniqueEventID = function(eventID, p){
if (this.checkIfIDExists(eventID + "_" + p))
return this.uniqueEventID(eventID, p + 1);
else
return eventID + "_" + p;
}
graphproto.getProcessCount = function () {
var count = 0;
for (var i = this.Processes.length - 1; i >= 0; i--) {
if(this.Processes[i].isRefer==false){
count++;
}
};
return count;
}
graphproto.getFormCount = function () {
return this.ReferredForms.length
}
//if item is passed then it will create copy of the itemID
graphproto.getNewID = function (isProcess, itemID, itemLabel) {
var curCount = this.eCount;
var curID = 'Activity' + curCount;
if(isProcess == true){
curCount = this.getProcessCount();
curID = 'Process' + curCount;
}else if(isProcess == false){
//this means it is a form
curCount = this.getFormCount();
curID = 'Form' + curCount;
}
// this is for copy paste function
if (itemID != undefined) {
curID = itemID + 'Copy'
var idExists = this.checkIfIDExists(curID);
if (idExists) {
curID = this.uniqueEventID(curID, 1);
}
if (itemLabel != undefined) {
if (curID.split('_').length > 1) {
itemLabel = itemLabel + 'Copy' + curID.split('_')[curID.split('_').length - 1]
} else {
itemLabel = itemLabel + 'Copy'
}
}else{
itemLabel = curID;
}
} else {
itemLabel = curID;
var idExists = this.checkIfIDExists(curID);
if (idExists) {
curID = this.uniqueEventID(curID, 1);
}
}
return {id: curID, label:itemLabel}
};
//helper functions to add resources to graph
//TODO: add validations and class for resources
graphproto.addEventType = function (el) {
if(!el){return;}
this.EventTypes.push(el);
return el;
};
//TODO: need to create new connections as these will have same object
graphproto.updateConnections = function (graph) {
if(!graph || !graph instanceof D){return;}
graph.Connections = graph.Connections;
return el;
};
//helper functions to create events/processes
/** Function to to create an Events in graph
*
* @memberof Graph
* @method createEvent
*
* @param {object} el -An object can be passed with properties assign to it, see example
*
* @example var myApp = new DCR();
* myApp.createEvent(); //this will create a simple event
* myApp.createEvent({'custom' : {'label':'An Event', 'eventDescription':'This is a test Event'}});
* //we can also provide attributes for the event object like above
* @returns {object} Returns an object of Element Class.
*/
graphproto.createEvent = function (el) {
if(!el){el = {}}
var res = new Element(this, el, false);
this.Events.push(res);
this.eCount++; //console.log(res);
return res;
};
graphproto.removeEvent = function (el) {
if(!el){return false}
};
/** Function to to create Process in graph
*
* @memberof Graph
* @method createProcess
*
* @param {object} el -An object can be passed with properties assign to it, see example
*
* @example var myApp = new DCR();
* myApp.createProcess(); //this will create a simple process
* myApp.createProcess({'custom' : {'label':'A Process', 'eventDescription':'This is a test Process'}});
* //we can also provide attributes for the process object like above, most of the properties of events and processes are same so we can use the same properties here
* @returns {object} Returns an object of Element Class.
*/
graphproto.createProcess = function (el, isRefer) {
if(this.ProsDisabled==true) return
if(!el){el = {}}
if(isRefer==true){
var res = new Element(this, el, true, false, true);
this.Parents.push(res);
this.ReferredPros.push(res);
}else{
var res = new Element(this, el, true);
}
this.Processes.push(res);
return res;
};
/** Function to to create Form in graph
*
* @memberof Graph
* @method createForm
*
* @param {object} el -An object can be passed with properties assign to it, see example
*
* @example var myApp = new DCR();
* myApp.createForm(); //this will create a simple process
* myApp.createForm({'custom' : {'label':'A Form', 'eventDescription':'This is a test Form'}});
* //we can also provide attributes for the process object like above, most of the properties of events and processes are same so we can use the same properties here
* @returns {object} Returns an object of Element Class.
*/
graphproto.createForm = function (el) {
if(this.ProsDisabled==true) return
if(!el){el = {}}
var res = new Element(this, el, false, true);
this.Events.push(res);
this.Parents.push(res);
this.ReferredForms.push(res);
return res;
};
/** Function to to create Fragment in graph
*
* @memberof Graph
* @method createFragment
*
* @param {object} el -An object can be passed with properties assign to it, see example
*
* @example var myApp = new DCR();
* myApp.createFragment(); //this will create a simple process
* myApp.createFragment({'custom' : {'label':'A Form', 'eventDescription':'This is a test Form'}});
* //we can also provide attributes for the process object like above, most of the properties of events and processes are same so we can use the same properties here
* @returns {object} Returns an object of Element Class.
*/
graphproto.createFragment = function (el, isForm) {
if(this.ProsDisabled==true) return
if(!el){el = {}}
var res = new Element(this, el, false, isForm, false, true);
this.Events.push(res);
return res;
};
/** Function to to create a Connection in graph, if connection is strong condition, create a condition and a milestone with link
*
* @memberof Graph
* @method createConnection
*
* @param {object} from - An element object is passed as source element from where connection will originate.
* @param {object} to - An element object is passed as target element to where connection will end up.
* @param {string} type - Connection type is passed as string so a connection can be identified, possible values are 'condition', 'response', 'include', 'exclude', 'milestone', 'spawn', 'update'.
* @param {string} guard - Guard for the connection as string.
* @param {number} level - filter level of the connection greater than or equal to Zero.
* @param {string} desc - Description of the connection as string.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'Enter'});
* myApp.createEvent({'id':'Exit'});
* myApp.createConnection(myApp.getById('Enter'), myApp.getById('Exit'), 'response');
* //this will create an Event Element Enter and Exit, then we can use the app to create a response connection between two by finding them by their Ids.
* @returns {object} Returns an object of Connection Class.
*/
graphproto.createConnection = function (from, to, color, type, guard, level, desc, time, groups, strong, businessRule, valueExpression) {
// if connection already exists then don't create new relation
if(to.checkIfAvailableConnection(from, type, strong)){
var res = new Connection(this, from, to, color, type, guard, level, desc, time, groups, strong, null, businessRule, valueExpression);
this.Connections.push(res);
if(type == 'condition' && strong){
// create a new milestone connection as well, as condition will be primary connection so we will only return that
this.Connections.push((new Connection(this, from, to, color, 'milestone', guard, level, desc, time, groups, false, res.link, businessRule, valueExpression)));
}else if(type == 'include' && strong){
// create a new milestone connection as well, as condition will be primary connection so we will only return that
this.Connections.push((new Connection(this, from, to, color, 'exclude', guard, level, desc, time, groups, false, res.link, businessRule, valueExpression)));
}
return res;
}
return null
};
/** Function to get the number of Nesting levels currently available, this number is generated on the basis of nesting in the graph to find out to which extant nesting is occuring. Base starts from 0 to N level of nesting
*
* @memberof Graph
* @method getMaxNLevel
*
* @returns {Number} Returns the maximum number of the nesting level.
*/
graphproto.getMaxNLevel = function(){
var currentMaxLevel = 0;
for(var i = 0; i<this.Events.length; i++){
//console.log('level is '+ arrAllEvents[i].data('NLevel'));
if(this.Events[i].nLevel>currentMaxLevel){
currentMaxLevel = this.Events[i].nLevel;
}
}
return currentMaxLevel;
}
/** Function to get the number of Sequence number currently available. Base starts from 0
*
* @memberof Graph
* @method getMaxSequence
*
* @returns {Number} Returns the maximum number of the sequence number.
*/
graphproto.getMaxSequence = function(){
var currentMaxSequence = 0;
for(var i = 0; i<this.Events.length; i++){
if(this.Events[i].custom.sequence>currentMaxSequence){
currentMaxSequence = this.Events[i].custom.sequence;
}
}
return currentMaxSequence;
}
/** Function to get all the Filter levels currently available.
*
* @memberof Graph
* @method getAllLevels
*
* @example var myApp = new DCR();
* myApp.createEvent({'custom' : {'label':'An Event', 'level':2}});
* myApp.createEvent({'custom' : {'label':'Another Event', 'level':5}});
* myApp.getAllLevels();
* //returns [2, 5]
* @returns {array} Returns an array of numbers containing the filter levels available in graph.
*/
graphproto.getAllLevels = function(){
var allLevels = [];
for (var i = 0; i < this.Events.length; i++) {
if($.inArray(this.Events[i].custom.level, allLevels)===-1){
allLevels.push(this.Events[i].custom.level);
}
};
return allLevels;
}
/** Function to get all the events by a specific role.
*
* @memberof Graph
* @method getEventsByRole
*
* @param {string} sRole - Name of the role for which to search Events against.
*
* @example var myApp = new DCR();
* myApp.createEvent({'custom' : {'label':'An Event', 'roles':['test']}});
* myApp.createEvent({'custom' : {'label':'Another Event','roles':['test']}});
* myApp.getEventsByRole('test');
* //returns [Element, Element]
* @returns {array} Returns an array of Element Class object which have the specified role assigned to them in graph.
*/
graphproto.getEventsByRole = function (sRole) {
var res = $.grep(this.Events, function (el) {
if($.inArray(sRole, el.custom.roles)>=0){
return el;
}
})
return res;
}
/** Function to get all the events by a specific level
*
* @memberof Graph
* @method getEventsByLevel
*
* @param {number} sLevel - Value of the level of which events are to be find.
* @param {boolean} proToo - Requires True to find Processes too.
*
* @example var myApp = new DCR();
* myApp.createEvent({'custom' : {'label':'An Event', 'level':2}});
* myApp.createEvent({'custom' : {'label':'Another Event','level':5}});
* myApp.getEventsByLevel(5);
* //returns [Element]
* @returns {array} Returns an array of Element Class object which have the specified level assigned to them in graph.
*/
graphproto.getEventsByLevel = function (sLevel, proToo) {
var res = $.grep(this.Events, function (el) {
return el.custom.level == parseInt(sLevel);
});
var resP = [];
if(proToo==true){
resP = $.grep(this.Processes, function (el) {
return el.custom.level == parseInt(sLevel);
});
}
return res.concat(resP);
}
/** Function to get all interface Events
*
* @memberof Graph
* @method getInterfaceEvents
*
* @example var myApp = new DCR();
* myApp.getInterfaceEvents({'interfaceEvent': true, 'custom' : {'label':'An Event', 'level':2}});
* myApp.createEvent({'custom' : {'label':'Another Event','level':5}});
* myApp.getInterfaceEvents();
* //returns [Element]
* @returns {array} Returns an array of Element Class object which are interface Events in graph.
*/
graphproto.getInterfaceEvents = function (parentEl, type) {
if(!type) {type = "outer"}; //interfaceFromOuter
return this.Events.filter(function (el) {
if(parentEl){
var curParent = parentEl.isForm()? el.getParentForm(): el.getParentProcess(false, true);
return el.interfaceType == type && curParent? curParent.id == parentEl.id: false;
}else{
return el.interfaceType == type;
}
});
}
/** Function to search events in a graph
*
* @memberof Graph
* @method findActivity
*
* @param {string} id - Id of the object to be found, value is case sensitive.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'An Event', 'custom' : {'label': 'test item'}});
* myApp.findActivity('An Event');
* //returns Element
* @returns {object} Returns the first item found as an object of Element class or returns null.
*/
graphproto.findActivity = function (id) {
if(id==undefined || id == null){
return [];
}
//get the event from this.Events and match its ID
var res = this.Events.filter(function (item) {
return id.split('_').every(function (el) {
return item.id.indexOf(el) > -1;
});
});
return res.length>0? res[0]: null;
};
/** Function to search events/processes in a graph
*
* @memberof Graph
* @method getById
*
* @param {string} id - Id of the object to be found, value is case sensitive.
*
* @example var myApp = new DCR();
* myApp.createEvent({'id':'An Event', 'custom' : {'label': 'test item'}});
* myApp.getById('An Event');
* //returns Element
* @returns {object} Returns the first item found as an object of Element class.
*/
graphproto.getById = function (id) {
if(id==undefined || id == null){
return undefined;
}
var path = id.split('.'), res = [], items = this.Events.concat(this.Processes, this.ReferredForms, this.Childs), self = this;
//! as same activity id can be in fragments or referred graphs so need to find the exact event
return findItem(path, items);
};
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
var len = o.length >>> 0;
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var thisArg = arguments[1];
var k = 0;
while (k < len) {
var kValue = o[k];
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
k++;
}
return undefined;
},
configurable: true,
writable: true
});
}
function findItem(path, items) {
var curId = path.shift()
var res = items.find(function (el, i) {
return el.id == curId;
})
if(res && path.length>0){
//get child of the item and search for the item under it.
var childItems = res.getChilds();
if(childItems.length>0){
return findItem(path, childItems)
}else if(path.length>0){ //if path is still continuing and no more branch to traverse then item is not found
return undefined;
}
}
return res;
}
graphproto.getByUId = function (uId) {
if(uId==undefined || uId == null){
return undefined;
}
var itemsToSearch = [];
itemsToSearch = this.Events.concat(this.Processes)
//get the event from this.Events and match its ID
var res = $.grep(itemsToSearch, function (el) {
return el.guid == uId;
});
if(res.length>0){
return res[0];
}else{
//search for Resources
itemsToSearch = []
itemsToSearch = this.Roles.concat(this.Groups).concat(this.EventTypes).concat(this.Parameters).concat(this.Phases).concat(this.Connections);
var prop;
res = $.grep(itemsToSearch, function (el) {
if (el.hasOwnProperty('id')) {
prop = el.id;
} else if (el.hasOwnProperty('guid')) {
prop = el.guid;
}
return prop === uId;
});
if(res.length>0){
return res[0];
}
}
return undefined;
};
graphproto.getEventsByGroup = function (group) {
var items = []
for (var i = 0; i < this.Events.length; i++) {
if(checkForMatch(this.Events[i].custom.groups, 'title', group.title)>=0){
items.push(this.Events[i])
}
};
return items;
}
//set the unique ids for all the events of current graph
function setupEventGUIDs (events) {
for (var i = 0; i < events.length; i++) {
events[i].setAttribute('guid', GUID());
if($(events[i]).find('event').length>0){
setupEventGUIDs ($(events[i]).find('event'))
}
};
return events;
}
/*graph loading related global functions*/
//function to get all events from xml for graph except the ones which are referred
function getEventLocalGraph(events, curEl) {
//console.log(events);
for (var i = 0; i < events.length; i++) {
if(events[i].getAttribute("type")=="form" && events[i].getAttribute("referId")!=undefined){
if(events[i].getAttribute("id")!=undefined && events[i].id==curEl.id && events[i].getAttribute("referId")==curEl.referId){
return events[i].getElementsByTagName("dcrgraph")[0]
}
}else if((events[i].getAttribute("type")=="form" || events[i].getAttribute("type")=="subprocess") && events[i].getAttribute("fragmentId")!=undefined){
if(events[i].getAttribute("id")!=undefined && events[i].id==curEl.id && events[i].getAttribute("fragmentId")==curEl.fragmentId){
return events[i].getElementsByTagName("dcrgraph")[0]
}
}
}
return null
}
// get the DMN XML from the main graph XML based on index provided from expressions list
function getExpressionDecisionXMLString($ServiceData, mainResourcesLoc, index) {
var curExpression = $ServiceData.find('expressions')[mainResourcesLoc].getElementsByTagName("expression")[index]
if(curExpression){
var curDecision = curExpression.getElementsByTagName('definitions');
if(curDecision.length>0){
return '<?xml version="1.0" encoding="UTF-8"?>' + XMLtoString(curDecision[0])
}
}
return ''
}
// TODO: add default layer for graph if none
// function to load the xml file provided it
function populateGraph (graph, serviceData, isRoot, curPro, options){
if(serviceData.getElementsByTagName("specification")[0]==undefined){
noty({ text: translations.error_graphxml, layout: 'top', timeout: 3500, type: 'warning' });
return;
}
var $ServiceData = $(serviceData);
var mainResourcesLoc = parseInt($ServiceData.find('specification').length -1);
var arrLabelMapping = [], arrExpressions = [], arrConditions = [], arrResponse = [], arrCoResponse = [], arrInclude = [], arrExclude = [], arrMilestone = [], arrUpdate = [], arrSpawns = [], arrParameters = [], arrRoles = [], arrGroups = [],arrPhases = [], arrEventTypes = [], arrParameters = [], arrGlobalStore = [], arrEventParameters = [];
if($ServiceData.find('labelMappings')[mainResourcesLoc] != undefined){
arrLabelMapping = xmlToJsonArray($ServiceData.find('labelMappings')[mainResourcesLoc].getElementsByTagName("labelMapping"));
}
if($ServiceData.find('expressions')[mainResourcesLoc] != undefined){
arrExpressions = xmlToJsonArray($ServiceData.find('expressions')[mainResourcesLoc].getElementsByTagName("expression"));
}
if($ServiceData.find('conditions')[mainResourcesLoc] != undefined){
arrConditions = xmlToJsonArray($ServiceData.find('conditions')[mainResourcesLoc].getElementsByTagName("condition"));
}
if($ServiceData.find('responses')[mainResourcesLoc] != undefined){
arrResponse = xmlToJsonArray($ServiceData.find('responses')[mainResourcesLoc].getElementsByTagName("response"));
}
if($ServiceData.find('coresponses')[mainResourcesLoc] != undefined){
arrCoResponse = xmlToJsonArray($ServiceData.find('coresponses')[mainResourcesLoc].getElementsByTagName("coresponse"));
}
if($ServiceData.find('includes')[mainResourcesLoc] != undefined){
arrInclude = xmlToJsonArray($ServiceData.find('includes')[mainResourcesLoc].getElementsByTagName("include"));
}
if($ServiceData.find('excludes')[mainResourcesLoc] != undefined){
arrExclude = xmlToJsonArray($ServiceData.find('excludes')[mainResourcesLoc].getElementsByTagName("exclude"));
}
if($ServiceData.find('milestones')[mainResourcesLoc] != undefined){
arrMilestone = xmlToJsonArray($ServiceData.find('milestones')[mainResourcesLoc].getElementsByTagName("milestone"));
}
if($ServiceData.find('updates')[mainResourcesLoc] != undefined){
arrUpdate = xmlToJsonArray($ServiceData.find('updates')[mainResourcesLoc].getElementsByTagName("update"));
}
if($ServiceData.find('spawns').length>0 && $ServiceData.find('spawns')[mainResourcesLoc] != undefined){
arrSpawns = xmlToJsonArray($ServiceData.find('spawns')[mainResourcesLoc].getElementsByTagName("spawn"));
}
//var globalMarkings = $ServiceData.find('globalMarking')[0];
var graphMarkings = $ServiceData.find('runtime')[mainResourcesLoc].getElementsByTagName('marking')[0];
// if(globalMarkings){
// var arrIncludedEvents = xmlToJsonArray(globalMarkings.getElementsByTagName("included")[0].getElementsByTagName("event")),
// arrPendingEvents = xmlToJsonArray(globalMarkings.getElementsByTagName("pending")[0].getElementsByTagName("event")),
// arrExecutedEvents = xmlToJsonArray(globalMarkings.getElementsByTagName("executed")[0].getElementsByTagName("event"));
// }else{
var arrIncludedEvents = xmlToJsonArray(graphMarkings.getElementsByTagName("included")[0].getElementsByTagName("event")),
arrPendingEvents = xmlToJsonArray(graphMarkings.getElementsByTagName("pendingResponses")[0].getElementsByTagName("event")),
arrExecutedEvents = xmlToJsonArray(graphMarkings.getElementsByTagName("executed")[0].getElementsByTagName("event"));
//}
if($ServiceData.find('variables')[mainResourcesLoc] != undefined){
arrParameters = $ServiceData.find('variables')[mainResourcesLoc].getElementsByTagName("variable");
}
if(graphMarkings != undefined){
var globalStoreItems = graphMarkings.getElementsByTagName("globalStore")[0].getElementsByTagName("variable");
//[].forEach.call(globalStoreItems, el=> {console.log(el, el.type)})
arrGlobalStore = xmlToJsonArray(globalStoreItems)
}
//check if the import roles is checked on importing the file or not
// if importing roles is checked or if file is loaded normally then load roles too
if(options.importRoles==true || options.isImported==false ){
//$('#pageOverlay').html('Reading Roles');
if($ServiceData.find('roles').length>0){
arrRoles = $ServiceData.find('roles')[$ServiceData.find('roles').length - 1].getElementsByTagName("role");
}
var arrRolesFiltered = [];
$.each(arrRoles, function(i, el){
if($(el).text().trim().length > 0 ){
$(el).text($(el).text());
arrRolesFiltered.push(el);
}
});
arrRoles = xmlToJsonArray(arrRolesFiltered)
// filtering the arrRoles to remove duplicate values
$.each(arrRoles, function(i, el){
if(el.text != undefined){
if(el.description==undefined){
el.description = '';
}
if(el.specification==undefined){
el.specification = '';
}
el.text = fixSlashes(el.text)
if($ServiceData.attr('isRefer') == 'true'){
if(checkForMatch(graph.ReferredRoles, "title", el.text) === -1) graph.ReferredRoles.push({'id': GUID(),'title':el.text, 'description' : fixSlashes(el.description), 'specification': fixSlashes(el.specification)});
}else{
if(checkForMatch(graph.Roles, "title", el.text) === -1) graph.Roles.push({'id': GUID(),'title':el.text, 'description' : fixSlashes(el.description), 'specification': fixSlashes(el.specification)});
}
}
});
graph.Roles = graph.Roles.sort(function (a, b) {
return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
});
//$('#pageOverlay').html('Initializing Roles List');
}
if(options.importGroups==true || options.isImported==false ){
//$('#pageOverlay').html('Reading Groups');
if($ServiceData.find('groups').length>0){
arrGroups = $ServiceData.find('groups')[$ServiceData.find('groups').length - 1].getElementsByTagName("group");
}
var arrGroupsFiltered = [];
$.each(arrGroups, function(i, el){
if($(el).text().trim().length > 0 ){
$(el).text($(el).text());
arrGroupsFiltered.push(el);
}
});
arrGroups = xmlToJsonArray(arrGroupsFiltered)
try {
arrGroups = sortArray("sequence", arrGroups, true);
} catch (ex) {
console.log("Exception while sorting groups : " + ex);
}
// filtering the arrRoles to remove duplicate values
$.each(arrGroups, function(i, el){
if(el.text != undefined){
if(el.sequence == undefined){
el.sequence = 0;
}
if(el.description==undefined){
el.description = '';
}
el.text = fixSlashes(el.text)
//pushing the items to array
if($ServiceData.attr('isRefer') == 'true'){
if(checkForMatch(graph.ReferredGroups, "title", el.text) === -1) graph.ReferredGroups.push({'id': GUID(),'title':el.text, 'sequence': el.sequence, 'description' : fixSlashes(el.description)});
}else{
if(checkForMatch(graph.Groups, "title", el.text) === -1) graph.Groups.push({'id': GUID(), 'title':el.text, 'sequence': el.sequence, 'description' : fixSlashes(el.description)});
}
}
});
graph.Groups = graph.Groups.sort(function (a, b) {
return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
});
}
if(options.importPhases==true || options.isImported==false ){
//$('#pageOverlay').html('Reading Groups');
if($ServiceData.find('phases').length>0){
arrPhases = $ServiceData.find('phases')[$ServiceData.find('phases').length - 1].getElementsByTagName("phase");
}
var arrPhasesFiltered = [];
$.each(arrPhases, function(i, el){
if($(el).text().trim().length > 0 ){
$(el).text($(el).text());
arrPhasesFiltered.push(el);
}
});
arrPhases = xmlToJsonArray(arrPhasesFiltered)
try {
arrPhases = sortArray("sequence", arrPhases, true);
} catch (ex) {
console.log("Exception while sorting phases : " + ex);
}
// filtering the arrRoles to remove duplicate values
$.each(arrPhases, function(i, el){
if(el.text != undefined){
if(el.sequence == undefined){
el.sequence = 0;
}
if(el.description==undefined){
el.description = '';
}
el.text = fixSlashes(el.text)
//pushing the items to array
if($ServiceData.attr('isRefer') == 'true'){
if(checkForMatch(graph.ReferredPhases, "title", el.text) === -1) graph.ReferredPhases.push({'id': GUID(),'title':el.text, 'sequence': el.sequence, 'description' : fixSlashes(el.description)});
}else{
if(checkForMatch(graph.Phases, "title", el.text) === -1) graph.Phases.push({'id': GUID(), 'title':el.text, 'sequence': el.sequence, 'description' : fixSlashes(el.description)});
}
}
});
graph.Phases = graph.Phases.sort(function (a, b) {
return a.sequence - b.sequence
});
}
if((options.importTypes==true && options.isReferred==false) || options.isImported==false ){
//$('#pageOverlay').html('Reading Event Types');
if($ServiceData.find('eventTypes')[mainResourcesLoc] != undefined){
arrEventTypes = $ServiceData.find('eventTypes')[mainResourcesLoc].getElementsByTagName("eventType");
}
if($ServiceData.find('eventParameters')[mainResourcesLoc] != undefined){
arrEventParameters = $ServiceData.find('eventParameters')[mainResourcesLoc].getElementsByTagName("parameter");
}
var arrEventTypesFiltered = [];
$.each(arrEventTypes, function(i, el){
if($(el).text().trim().length > 0 ){
$(el).text($(el).text());
arrEventTypesFiltered.push(el);
}
});
arrEventTypes = xmlToJsonArray(arrEventTypesFiltered)
// filtering the arrRoles to remove duplicate values
$.each(arrEventTypes, function(i, el){
if(el.text != undefined){
el.text = fixSlashes(el.text)
//pushing the items to array
if($ServiceData.attr('isRefer') == 'true'){
if(checkForMatch(graph.ReferredEventTypes, "title", el.text) === -1) graph.ReferredEventTypes.push({'id': GUID(),'title':el.text, 'description' : fixSlashes(el.description), parameters : []});
}else{
if(checkForMatch(graph.EventTypes, "title", el.text) === -1) graph.EventTypes.push({'id': GUID(), 'title':el.text, 'description' : fixSlashes(el.description), parameters : []});
}
}
});
graph.EventTypes = graph.EventTypes.sort(function (a, b) {
return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
});
//get event type parameters and set them to their types
arrEventParameters = xmlToJsonArray(arrEventParameters);
for (var i = 0; i < arrEventParameters.length; i++) {
var curParam = arrEventParameters[i];
var index = checkForMatch(graph.EventTypes, "title", fixSlashes(curParam.eventtypeid))
if(index>=0 && checkForMatch(graph.EventTypes[index].parameters, "title", fixSlashes(curParam.title)) === -1){
graph.EventTypes[index].parameters.push({title: fixSlashes(curParam.title), value: fixSlashes(curParam.value), required: curParam.required})
}
};
}
if((options.importParams==true && options.isReferred==false) || options.isImported==false ){
//$('#pageOverlay').html('Reading Parameters');
//filtering the variables in DCR file
var arrParametersFiltered = [];
$.each(arrParameters, function(i, el){
if($(el).attr('id') != '' && $(el).attr('id').trim().length >0 ){
$(el).attr('value', $(el).attr('value'));
arrParametersFiltered.push(el);
}
});
arrParameters = xmlToJsonArray(arrParametersFiltered);
//svalue is for sim value
for (var i = 0; i < arrParameters.length; i++) {
if(checkForMatch(graph.Parameters, "title", fixSlashes(arrParameters[i].id)) === -1) {
graph.Parameters.push({'id': GUID(), 'title':fixSlashes(arrParameters[i].id), 'value' : fixSlashes(arrParameters[i].value), 'type' : arrParameters[i].type, 'svalue' : fixSlashes(arrParameters[i].value)});
}
};
}
if(isRoot){
graph.GlobalStore = arrGlobalStore;
}
//$('#pageOverlay').html('Initializing Events');
// rendering the events from dataservice (events, roles , labels, runtime and initial locations)
var events = serviceData.getElementsByTagName("specification")[0].getElementsByTagName("events")[0].getElementsByTagName("event");
events = setupEventGUIDs(events);
var arrDCREvents = parseXML(events);
//getting all the external events and removing them from the arr events, so they are not drawn on canvas
if($ServiceData.find('externalEvents').length>0){
var externalEvents = $ServiceData.find('externalEvents')[mainResourcesLoc].getElementsByTagName("externalEvent");
}else{
var externalEvents = [];
}
var arrCurExternalEvents = [];
$.each(externalEvents, function(i, el){
arrCurExternalEvents.push(el.attributes.localId.value);
graph.ExternalEvents.push(el);
});
var arrEvents = [];
$.each(arrDCREvents, function(i, el){
if(el != undefined){
if($.inArray(el.id, arrCurExternalEvents) === -1) arrEvents.push(el);
}
});
//$('#pageOverlay').html('Drawing Events');
for(i=0;i<arrEvents.length;i++){
//for refer graphs, ignore events which are private
if(options.isReferred == true && arrEvents[i].scope=="private" || (arrEvents[i].custom==undefined)){
continue;
}
var label = $.grep(arrLabelMapping, function(obj){
return obj.eventId == arrEvents[i].id;
});
var eventLabel = '';
if(label.length>0){
eventLabel = fixSlashes(label[0].labelId);
}
//setting up event label found in the xml
arrEvents[i].custom.label = eventLabel;
//setting up the runtimes of the event
arrEvents[i].runtimes = {};
var curEventId = arrEvents[i].id;
if(options.isSimulation==true && options.simPaused!=true && curPro!=undefined && options.isReferred==true){
//in simulation we have may have more than one event with same id for the referred graphs so we need this for now
curEventId = arrEvents[i].oriId;
}
if(arrIncludedEvents != undefined){
var dataIncEvnt = $.grep(arrIncludedEvents, function(obj){
return obj.id == curEventId;
});
if(dataIncEvnt.length>0){
arrEvents[i].runtimes.included = true;
}else{
arrEvents[i].runtimes.included = false;
}
}
if(arrPendingEvents != undefined){
var dataPenEvnt = $.grep(arrPendingEvents, function(obj){
return obj.id == curEventId;
});
//pending events will have deadline attribute too add it too
if(dataPenEvnt.length>0){
arrEvents[i].runtimes.pending = true;
if(dataPenEvnt[0].deadline!=undefined){
var curTime = fixSlashes(dataPenEvnt[0].deadline);
var curTimeAsDate = new Date(curTime);
arrEvents[i].deadline = isDate(curTimeAsDate)? curTimeAsDate.toISOString(): curTime;
}
}else{
arrEvents[i].runtimes.pending = false;
}
}
if(arrExecutedEvents != undefined){
var dataExeEvnt = $.grep(arrExecutedEvents, function(obj){
return obj.id == curEventId;
});
//executed events will have time attribute add it too
if(dataExeEvnt.length>0){
arrEvents[i].runtimes.executed = true;
if(dataExeEvnt[0].time!=undefined){
var curTime = fixSlashes(dataExeEvnt[0].time);
var curTimeAsDate = new Date(curTime);
arrEvents[i].time = isDate(curTimeAsDate)? curTimeAsDate.toISOString(): curTime;
}
}else{
arrEvents[i].runtimes.executed = false;
}
}
//console.log(arrEvents[i]);
arrEvents[i].parentPro = curPro;
if(arrEvents[i].computation!=undefined){
var expressionIndex = checkForMatch(arrExpressions, 'id', arrEvents[i].computation)
if(expressionIndex>=0){
var expression = arrExpressions[expressionIndex]
if(expression.type && expression.type=='DMN'){
arrEvents[i].dmnXML = getExpressionDecisionXMLString($ServiceData, mainResourcesLoc, expressionIndex)
}
arrEvents[i].computation = expression.value || '';
}
}
// TODO: need to check if we are setting null true for any value, if so we need to update this check accordingly
var eventDefaultValue = arrGlobalStore.find(function (item) {
return item.id === arrEvents[i].id && item.isNull == "false"
});
if(eventDefaultValue){
arrEvents[i].defaultValue = eventDefaultValue;
}
// set precondition value, get it from expressions if any found
if(arrEvents[i].precondition!=undefined && typeof arrEvents[i].precondition.id !='undefined' && arrEvents[i].precondition.id!= null){
var expressionIndex = checkForMatch(arrExpressions, 'id', arrEvents[i].precondition.id)
if(expressionIndex>=0){
arrEvents[i].precondition.value = arrExpressions[expressionIndex].value
}
}
if(arrEvents[i].type == "form" && arrEvents[i].referId!=undefined && isInteger(parseInt(arrEvents[i].referId))==true){
console.log("DCRForm");
arrEvents[i].graphID = arrEvents[i].referId
arrEvents[i].graphXML = getEventLocalGraph(events, arrEvents[i])
var curEl = graph.createForm(arrEvents[i]);
}else if(arrEvents[i].fragmentId!=undefined && isInteger(parseInt(arrEvents[i].fragmentId)) && (arrEvents[i].dcrgraph!=undefined)){
//! if fragment has dcgraph then load it in main graph
console.log("DCR Fragment");
arrEvents[i].dcrgraph = getEventLocalGraph(events, arrEvents[i])
var curEl = graph.createFragment(arrEvents[i]);
}else{
var curEl = graph.createEvent(arrEvents[i]);
}
if(curPro!=undefined && curPro.isInstance==true){
curEl.isInstance = true;
}
}
/*events drawing loop ends here*/
//getting all the parents of childs
var allParentIDs = [], uniqueParentIDs = [];
for(var i=0; i<graph.Childs.length; i++){
if(graph.Childs[i].parentGUID!=undefined && graph.Childs[i].parentGUID!=null){
allParentIDs.push(graph.Childs[i].parentGUID);
}
}
// filtering the Parent IDS to remove duplicate values
$.each(allParentIDs, function(i, el){
if($.inArray(el, uniqueParentIDs) === -1) {uniqueParentIDs.push(el);}
});
//adding the Parents to arrParents
$.each(uniqueParentIDs, function(i, el){
//pEl = graph.getEventById(el).e;
var pEl = graph.getByUId(el);
if(pEl!= undefined && $.inArray(pEl, graph.Parents) === -1){
graph.Parents.push(pEl);
}
});
//get the classes for childs based on their parents (N level)
for(var j=0; j<graph.Childs.length; j++){
graph.Childs[j].nLevel = parseInt(graph.Childs[j].parentObj.nLevel + 1);
}
//pushing the connections to all connections for initializing later when the graph is fully loaded and
//TODO::
/*
lets setup the connections arrays here and update the from and to with the elements found here if possible
*/
//all events are created
if(arrConditions.length>0){
for (var i = 0; i < arrConditions.length; i++) {
arrConditions[i].curPro = curPro;
};
graph.AllConnections.push([arrConditions, "#FFA500", arrExpressions]);
}
if(arrResponse.length>0){
for (var i = 0; i < arrResponse.length; i++) {
arrResponse[i].curPro = curPro;
};
graph.AllConnections.push([arrResponse, "#1E90FF", arrExpressions]);
}
if(arrCoResponse.length>0){
for (var i = 0; i < arrCoResponse.length; i++) {
arrCoResponse[i].curPro = curPro;
};
graph.AllConnections.push([arrCoResponse, "#795548", arrExpressions]);
}
if(arrInclude.length>0){
for (var i = 0; i < arrInclude.length; i++) {
arrInclude[i].curPro = curPro;
};
graph.AllConnections.push([arrInclude, "#29A81A", arrExpressions]);
}
if(arrExclude.length>0){
for (var i = 0; i < arrExclude.length; i++) {
arrExclude[i].curPro = curPro;
};
graph.AllConnections.push([arrExclude, "red", arrExpressions]);
}
if(arrMilestone.length>0){
for (var i = 0; i < arrMilestone.length; i++) {
arrMilestone[i].curPro = curPro;
};
graph.AllConnections.push([arrMilestone, "#BC1AF2", arrExpressions]);
}
if(arrUpdate.length>0){
for (var i = 0; i < arrUpdate.length; i++) {
arrUpdate[i].curPro = curPro;
};
graph.AllConnections.push([arrUpdate, "#b0bfc3", arrExpressions]);
}
if(arrSpawns.length>0){
for (var i = 0; i < arrSpawns.length; i++) {
arrSpawns[i].curPro = curPro;
};
graph.AllConnections.push([arrSpawns, "#334960", arrExpressions]);
}
//$('#pageOverlay').html('Reading Graph Details');
//var graphDetails = $ServiceData.find('resources')[mainResourcesLoc].getElementsByTagName("graphDetails");
var graphDetails = $ServiceData.find('graphDetails')[mainResourcesLoc]
if(graphDetails!=undefined){
if((options.isImported == true && options.importDocDesc==true && options.isReferred==false) || options.isImported==false){
graph.Description = fixSlashes($(graphDetails).text());
}
}
var graphDocumentation = $ServiceData.find('graphDocumentation')[mainResourcesLoc]
if(graphDocumentation!=undefined && ((options.isImported == true && options.isReferred==false) || options.isImported==false)){
graph.Documentation = fixSlashes($(graphDocumentation).text());
}
var graphLanguage = $ServiceData.find('graphLanguage')[mainResourcesLoc]
if(graphLanguage!=undefined){
if((options.isImported == true && options.importDocLanguage==true && options.isReferred==false) || options.isImported==false){
graph.Language = fixSlashes($(graphLanguage).text());
}
}
var graphDomain = $ServiceData.find('graphDomain')[mainResourcesLoc]
if(graphDomain!=undefined){
if((options.isImported == true && options.importDocDomain==true && options.isReferred==false) || options.isImported==false){
graph.Domain = fixSlashes($(graphDomain).text());
}
}
//load the root graph resources and assign them to the graph properties
if(isRoot && $ServiceData.attr('isRefer') != 'true'){
var graphType = $ServiceData.attr('graphType');
if(graphType!=undefined && graphType!=null && !isNaN(graphType)){
graph.Type = parseInt(graphType);
}else{
graph.Type = 0;
}
var docdataTypesStatus = $ServiceData.attr('dataTypesStatus');
if(docdataTypesStatus!=undefined && docdataTypesStatus=='show' && $ServiceData.find('subProcess').length<1){
graph.DataTypesStatus = 'show';
}else{
graph.DataTypesStatus = 'hide';
}
//$('#pageOverlay').html('Reading Graph Title');
// setting up the window title based on the values in graph XML
var docTitle = $ServiceData.attr('title');
if((options.isImported == true && options.importDocTitle==true) || options.isImported==false ){
if(docTitle!=undefined){
if(docTitle.length<1 || docTitle.replace(/ /g,'').length<1){
graph.Title = 'DCR';
}else{
graph.Title = docTitle;
}
}else{
graph.Title = 'DCR';
}
//console.log(docTitle);
}
var docTime = $ServiceData.find('runtime')[mainResourcesLoc].getElementsByTagName("time");
if(docTime[0]!=undefined){
var docCurrentTime = docTime[0].attributes.current.value
if(isDate(docCurrentTime)==true){
graph.Time = new Date(docCurrentTime).toISOString();
}else{
graph.Time = null;
}
var docDeadLine = docTime[0].attributes.deadline.value
if(isDate(docDeadLine)==true){
graph.DeadLine = new Date(docDeadLine).toISOString();
}else{
graph.DeadLine = null;
}
var docDelay = docTime[0].attributes.delay.value
if(isDate(docDelay)==true){
graph.Delay = new Date(docDelay).toISOString();
}else{
graph.Delay = null;
}
}else{
graph.Time = null;
graph.DeadLine = null;
graph.Delay = null;
}
var docKeywords = $ServiceData.find('keywords')[mainResourcesLoc];
if((options.isImported == true && options.importKeywords==true) || options.isImported==false ){
if(docKeywords!=undefined && $(docKeywords).text().trim().length>0){
graph.Keywords = $(docKeywords).text().split(',').map(function(e) {
return addSlashes(htmlEncode(e));
});
console.log($(docKeywords).text());
}
}
var currentMarkup = null;
if($ServiceData.find('hightlighterMarkup').length>0 && $ServiceData.find('hightlighterMarkup')[mainResourcesLoc] != undefined){
currentMarkup = fixSlashes(htmlDecode($ServiceData.find('hightlighterMarkup')[mainResourcesLoc].innerHTML))
}
//on graph load check if this is old markup then transform it to new format
if(currentMarkup!=null){
// this means that it has some old markup, update it new format
currentMarkup = upgradeHighlighterMarkup(graph, currentMarkup)
}
//if it finds new markup then it will update the markup
if($ServiceData.find('highlighterMarkup').length>0 && $ServiceData.find('highlighterMarkup')[mainResourcesLoc] != undefined){
currentMarkup = $.xml2json($ServiceData.find('highlighterMarkup')[mainResourcesLoc])
}
graph.highlighterMarkup = loadHighlighterMarkup(graph, currentMarkup);
//$('#pageOverlay').html('Setting Up Graph Filter Level');
//if graph is being imported then check if resources are being imported or not
if($ServiceData.find('graphFilters').length>0){
//check if roles are being imported or not?
if((options.isImported == true && options.importRoles==true) || options.isImported==false ){
var arrFilteredGroups = $ServiceData.find('graphFilters')[mainResourcesLoc].getElementsByTagName("filteredGroups");
}
//check if groups are being imported or not?
if((options.isImported == true && options.importGroups==true) || options.isImported==false ){
var arrFilteredRoles = $ServiceData.find('graphFilters')[mainResourcesLoc].getElementsByTagName("filteredRoles");
}
//check if groups are being imported or not?
if((options.isImported == true && options.importPhases==true) || options.isImported==false ){
var arrFilteredPhases = $ServiceData.find('graphFilters')[mainResourcesLoc].getElementsByTagName("filteredPhases");
}
}else{
var arrFilteredGroups = "";
var arrFilteredRoles = "";
var arrFilteredPhases = "";
}
if($(arrFilteredGroups).text()!=""){
if((options.isImported == true && options.importDocFilters==true) || options.isImported==false ){
graph.FilterGroups = $(arrFilteredGroups).text().split(',');
}
}
if($(arrFilteredRoles).text()!=""){
if((options.isImported == true && options.importDocFilters==true) || options.isImported==false ){
graph.FilterRoles = $(arrFilteredRoles).text().split(',');
}
}
if($(arrFilteredPhases).text()!=""){
if((options.isImported == true && options.importDocFilters==true) || options.isImported==false ){
graph.FilterPhases = $(arrFilteredPhases).text().split(',');
}
}
// setting up the graph filter on loading the xml
var docFilterLevel = $ServiceData.attr('filterLevel');
var docInsightFilter = $ServiceData.attr('insightFilter');
if((options.isImported == true && options.importDocFilters==true) || options.isImported==false ){
if(docFilterLevel!=undefined && docFilterLevel.trim().length>=0 && parseInt(docFilterLevel)!=NaN){
graph.FilterLevel = parseInt(docFilterLevel);
}else{
graph.FilterLevel = -1;
}
graph.InsightFilter = (docInsightFilter!=undefined && docInsightFilter=="true")
}
//$('#pageOverlay').html('Setting Up Graph Zoom Level');
// setting up the graph zoom on loading the xml
var docZoomLevel = $ServiceData.attr('zoomLevel');
if((options.isImported == true && options.importDocZoom==true) || options.isImported==false ){
if(docZoomLevel!=undefined /*&& options.isSimulation==false*/){
if(docZoomLevel.trim().length<1 || docZoomLevel==0){
graph.ZoomLevel = 0;
}else{
graph.ZoomLevel = parseInt(docZoomLevel);
}
}else{
graph.ZoomLevel = 0;
}
}
//graph background
if((options.isImported == true && options.importDocBG==true) || options.isImported==false){
var graphBG = $ServiceData.attr('graphBG');
if(graphBG!=undefined && graphBG!=null){
graph.BG = graphBG;
}else{
graph.BG = '#EBEBEB';
}
}
// graph Exercise: this.Exercise = false
var isExercise = $ServiceData.attr('exercise');
graph.Exercise = isExercise!=undefined && isExercise!=null && isExercise=='true' ? true: false;
var sendText = $ServiceData.attr('sendText');
graph.SendText = sendText!=undefined && sendText!=null ? fixSlashes(sendText) : '';
var cancelText = $ServiceData.attr('cancelText');
graph.CancelText = cancelText!=undefined && cancelText!=null ? fixSlashes(cancelText) : '';
var hideCancel = $ServiceData.attr('hideCancel');
graph.HideCancel = hideCancel!=undefined && hideCancel!=null && hideCancel=='true' ? true: false;
var formShowInitialPhase = $ServiceData.attr('formShowInitialPhase');
graph.FormShowInitialPhase = formShowInitialPhase!=undefined && formShowInitialPhase!=null && formShowInitialPhase=='2' ? 2: 1;
// setting up the graph group style on loading the xml
var docFormGroupStyle = $ServiceData.attr('formGroupStyle');
if(docFormGroupStyle!=undefined){
graph.FormGroupStyle = docFormGroupStyle;
}else{
graph.FormGroupStyle = "Normal";
}
// setting up the form layout style on loading the xml
var docFormLayoutStyle = $ServiceData.attr('formLayoutStyle');
if (docFormLayoutStyle != undefined) {
graph.FormLayoutStyle = docFormLayoutStyle;
} else {
graph.FormLayoutStyle = "Horizontal";
}
}
}
graphproto.graphFrom = function (item, includeEl){
var self = this, path = item!=undefined && item!=null? item.id: null;
var graph = new DCR();
//self.importTo(graph, undefined, {isFormImport}) //this fails as it has the same instances for graph items, which affect main graph
//create a new graph based on current graph and delete the rest of items
graph.loadXMLString(self.writeXML(true));
//find the item in graph and let it be and remove all other items from graph
if(item!=null){
var curItem = graph.getById(path);
var childItems = curItem.getChilds();
var itemsToRemove = graph.Events.filter(function (el) {
return childItems.indexOf(el) < 0
})
//if we want to include the main item
if(includeEl==true){
itemsToRemove = itemsToRemove.filter(function (el) {
return el!=curItem
})
}
console.log(itemsToRemove)
//remove the items now
itemsToRemove.map(function (el) {
el.remove();
})
}
return graph;
}
//TODO: function to import a graph in current Graph using merge sementics
function importFrom (path, includePath){
}
//function to get GUID //http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript/21963136#21963136
/*GUID = function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}*/
//function to get UUID //http://jcward.com/UUID.js
GUID = function() {
var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
var d0 = Math.random()*0xffffffff|0;
var d1 = Math.random()*0xffffffff|0;
var d2 = Math.random()*0xffffffff|0;
var d3 = Math.random()*0xffffffff|0;
return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}
//this is the recursive function to load the main graph and all of its sub processes
function getGraphResouces (graph, graphXML, isRoot, options) {
//console.log(graphXML)
var mainResourcesLoc = parseInt($(graphXML).find('specification').length -1);
var graphSubProcesses = $(graphXML).find("resources:first-child subProcess");
if(isRoot){
populateGraph(graph, graphXML, true, 'root', options);
}
//adding this check so that if any dynamic graph loaded has any sub process we ignore them
if(graph.dataTypesStatus=="show"){
return;
}
//this means this graph has subprocessess
if(graphSubProcesses.length>0){
graphSubProcesses.each(function (index, el) {
var curID = el.attributes.id.value;
var curGUID = GUID();
el.setAttribute('guid', curGUID);
if($.inArray(curGUID, graph.ProcessedPs) === -1 ){
graph.ProcessedPs.push(curGUID);
//console.log(el);
var parentProcessNode = el.parentNode.parentNode.parentNode.parentNode.parentNode;
var parentProcess = parentProcessNode.nodeName;
var parentProcessId = '';
var parentProcessGUID = '';
if(parentProcess=='subProcess'){
parentProcessId = parentProcessNode.attributes.id.value;
parentProcessGUID = parentProcessNode.attributes.guid.value;
}
if(parentProcessId!=''){
el.setAttribute('parentId', parentProcessId);
}
if(parentProcessGUID!=''){
el.setAttribute('parentGUID', parentProcessGUID);
var curParent = graph.getByUId(parentProcessGUID)
}
var processesObj = $.xml2json(el);
if(options.isSimulation==true){
processesObj.dcrgraph = $(stringToXML($(el).find('dcrgraph').outerHTML())).find('dcrgraph')[0]
}else{
processesObj.dcrgraph = $(el).find('dcrgraph')[0];
}
if(processesObj.custom==undefined){
processesObj.custom = {}
}
processesObj.custom.label = el.attributes.name.value;
var curPro = graph.createProcess(processesObj);
curPro.parentObj = curParent;
//if no template or instance id is found then draw the process, if found then it means it is an instance we don't need to draw it
if(((el.attributes.instanceId != undefined && el.attributes.instanceId.value != "") && (el.attributes.template != undefined && el.attributes.template.value != "")) || (el.attributes.isInstance != undefined && el.attributes.isInstance.value != "")){
// if an instance then do something
//return true; // skip next things and move to next subprocess
curPro.isInstance = true;
//find all subProcesses and mark them as instance as they are not original now
$(el).find('subProcess').each(function (i, eel) {
eel.setAttribute('isInstance', true);
});
}
var mainResourcesLoc = parseInt($(el).find('specification').length -1);
//while reading the xml from DB on load, check if the process added was a reference then continue and add this to arrReferredPros
if($(el).attr('isRefer')=="true" && options.isSimulation==false){
graph.ReferredPros.push(curPro);
graph.Parents.push(curPro)
return; //returning here for lazy loading on the graphs later on
}
//updating the subprocess xml before populating it to nest them in this process
//$(el).find('events event').each(function (i, eel) {
$($(el).find('events:first-child')[0]).children().each(function (i, eel) {
eel.setAttribute('parentId', curID);
eel.setAttribute('parentGUID', curGUID);
eel.setAttribute('isRefer', curPro.isRefer);
if((curPro.instanceId != undefined && curPro.instanceId != "") && (curPro.template != undefined && curPro.template != "")){
//eel.setAttribute('isInstance', true);
eel.setAttribute('instanceId', curPro.instanceId);
eel.setAttribute('template', curPro.template);
}
});
populateGraph(graph, el, false, curPro, options);
var subGraph = $(el).find('dcrgraph')[0];
getGraphResouces(graph, subGraph, false, options);
}else{
console.log('already pro p')
}
});
}
var graphResources = '';
}
function getLocalEvent (graph, sharedEventId) {
var localEvent = undefined;
for (var i = 0; i < graph.ExternalEvents.length; i++) {
if(graph.ExternalEvents[i].attributes.localId.value == sharedEventId){
// find the item in the process location defined
var curPro = graph.getById(graph.ExternalEvents[i].attributes.externalLocation.value);
var localID = graph.ExternalEvents[i].attributes.externalId.value
localEvent = findElement (curPro, localID)
}
};
return localEvent;
}
function findElement (curPro, itemID) {
var item = undefined
if(curPro==undefined) return
curPro.getChilds(true).forEach(function (el, indx) {
if (el.oriId == itemID) {
item = el
}
})
var parentPro = curPro.getParentProcess()
if (item == undefined && parentPro!=undefined) {
item = findElement(parentPro, itemID)
}else if(item == undefined && parentPro==undefined){
curPro.graph.getAllRootEvents().forEach(function (el, indx) {
if (el.oriId == itemID) {
item = el
}
})
}
return item;
}
function getExpressionValue(expressionId, arrExpress) {
let guardVal = '';
if(expressionId!=undefined){
guardVal = arrExpress.find(function (el) {
return expressionId==el.id;
})
if(guardVal){
guardVal = fixSlashes(guardVal.value)
}
}
return guardVal
}
function setupConnections(graph, arr, color, arrExpress){
for(var i = 0; i<arr.length; i++){
var curPro = arr[i].curPro;
// if connections are inside the refer processes
if ((curPro != undefined && curPro.isRefer == true) || (curPro != undefined && curPro.instanceId != undefined && curPro.instanceId.length > 0) || (curPro != undefined && curPro.isInstance != undefined && curPro.isInstance == true)) {
// find the source and target
// if both source and target are found then make connections else return
source = findElement (curPro, arr[i].sourceId);
target = findElement (curPro, arr[i].targetId);
if (source == undefined || target == undefined) {
continue
}
} else {
source = graph.getById(arr[i].sourceId);
target = graph.getById(arr[i].targetId);
}
//search for the source or the target in the shared events and if the event is found then use its id to create the connection
if(source==undefined){
source = getLocalEvent(graph, arr[i].sourceId);
}
if(target==undefined){
target = getLocalEvent(graph, arr[i].targetId);
}
//still incase if the connections are between the distributed events
if(source==undefined || target==undefined){
graph.ReferredCons.push([[arr[i]], color, arrExpress]);
}else{
// if this is milestone for strong connections then ignore that as that will be created automatically when we create, as properties are shared we don't have to worry about that
if(arr[i].type=='milestone' && typeof arr[i].link !== 'undefined' && arr[i].link.trim().length>0){
continue;
}
if(arr[i].type=='exclude' && typeof arr[i].link !== 'undefined' && arr[i].link.trim().length>0){
continue;
}
// if condition and has link then treat as strong relation
const guard = getExpressionValue(arr[i].expressionId, arrExpress);
const valueExpression = getExpressionValue(arr[i].valueExpressionId, arrExpress);
graph.createConnection(source, target, color, arr[i].type, guard , arr[i].filterLevel, arr[i].description, arr[i].time, arr[i].groups, (typeof arr[i].link !== 'undefined'? true: false), (arr[i].businessRule == 'true'? true: false), valueExpression)
//checking if the relation has a guard or not
}
}
}
/** Main funtion to get a DCR graph based on the id of the graph. The function will make an ajax request to dcrgraphs.net api to get the graph details and if successful it will load the xml of graph
*
* @memberof Graph
* @method loadGraph
*
* @param {number} graphId -Requires DCR Graph id from dcrgraphs.net to load the graph XML
* @param {function} callback -This function will be executed when the Graph will be successfully loaded after ajax request
*
* @example var myApp = new DCR();
* myApp.loadGraph(2275);
* //if the request is made successful then this will load the graph ready to be used
* @returns {object} Returns an object of Graph Class.
*/
graphproto.loadGraph = function (graphId, callback, initialize) {
var graph = this;
var serviceUrl = "/api/dl_dcrgraph/"+parseInt(graphId);
if (initialize==true) {
serviceUrl = "/api/dl_dcrgraph/" + parseInt(graphId) + "?initialize=true";
}
if(graphId==getQueryVariable('id') || graphId == parseInt(graphId)){
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
url: serviceUrl,
dataType: "json",
beforeSend: function () {
console.log("sending graph loading request");
}
}).done(function (restResponse) {
if((restResponse!=null || restResponse != undefined) && restResponse.Id === parseInt(restResponse.Id)){
graph.loadXMLString(restResponse.GraphXml);
if(typeof(callback) === "function"){
callback(graph, restResponse);
}
return graph;
}
}).fail(function (err) {
console.log("some error occured on getting graph details.");
if(typeof(callback) === "function"){
callback(undefined, err);
}
}).always(function () {
console.log("completed getting graph details");
});
}
//return graph;
}
/** The function will make an ajax request to dcrgraphs.net api to initialize the graph and if successful it will callback the function provided
*
* @memberof Graph
* @method intializeGraphXml
*
* @param {string} graphXml -Requires DCR Graph id from dcrgraphs.net to load the graph XML
* @param {function} callback -This function will be executed when the Graph will be successfully loaded after ajax request
*
* @example var myApp = new DCR();
* myApp.intializeGraphXml(xml);
* //if the request is made successful then this will load the graph ready to be used
* @returns {object} Returns an object of Graph Class.
*/
graphproto.intializeGraphXml = function (graphXml, callback) {
var graph = this;
var serviceUrl = "/api/dl_dcrgraph/initialize";
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: serviceUrl,
dataType: "json",
data: JSON.stringify({GraphXml: graphXml }),
beforeSend: function () {
console.log("sending graph loading request");
}
}).done(function (restResponse) {
if ((restResponse != null || restResponse != undefined)) {
graph.loadXMLString(restResponse);
if (typeof (callback) === "function") {
callback(restResponse);
}
return graph;
}
}).fail(function () {
console.log("some error occured while initializing graph.");
events.fire("error", {
message: "some error occured while initializing graph.",
module: "DCR",
functionName: "intializeGraphXml",
parameters: JSON.stringify({graphXml: graphXml})
})
}).always(function () {
console.log("completed getting graph details");
});
}
/** Main funtion to load the DCR XML and populate the graph with XML data. The standard DCR Graph XML structure will be needed to properly load the Graph
*
* @memberof Graph
* @method loadXML
*
* @param {string} graphXML -Requires DCR Graph XML in XML format to load the graph
*
* @returns {object} Returns an object of Graph Class.
*/
graphproto.loadXML = function (graphXML) {
this.loadXMLString(XMLtoString(graphXML));
return this;
}
function updateItemsResource (arrItems, rType, resource) {
for (var i = 0; i < arrItems.length; i++) {
var resources = arrItems[i].custom[rType];
if(resources!=undefined && resources.length>0){
for (var j = 0; j < resources.length; j++) {
if(resources[j].title.toLowerCase() === resource.title.toLowerCase()){
arrItems[i].custom[rType][j] = resource
}
};
}
};
}
graphproto.importTo = function (graph, parentPro, options) {
//check for the resources of the elements if they exist in the graph then don't push them there
//also check if any of the imported graph items is using resources and they already exist in the graph then update the items with those resources
for (var i = 0; i < this.Groups.length; i++) {
//if this is simple import or import as simple process then add/update the items of the parent graph
//else if the import is as referred graph then add the items to referred resources
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
var index = checkForMatch(graph.Groups, 'title', this.Groups[i].title);
if(index>=0){
console.log(this.Groups[i].title + ' Group exists in graph');
updateItemsResource (this.Events, 'groups', graph.Groups[index]);
}else{
graph.Groups.push(this.Groups[i]);
}
}else if(parentPro!=undefined && parentPro.isRefer==true){
graph.ReferredGroups.push(this.Groups[i]);
}
};
for (var i = 0; i < this.Phases.length; i++) {
//if this is simple import or import as simple process then add/update the items of the parent graph
//else if the import is as referred graph then add the items to referred resources
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
var index = checkForMatch(graph.Phases, 'title', this.Phases[i].title);
if(index>=0){
console.log(this.Phases[i].title + ' Phase exists in graph');
updateItemsResource (this.Events, 'phases', graph.Phases[index]);
}else{
graph.Phases.push(this.Phases[i]);
}
}else if(parentPro!=undefined && parentPro.isRefer==true){
graph.ReferredPhases.push(this.Phases[i]);
}
};
for (var i = 0; i < this.Roles.length; i++) {
//if this is simple import or import as simple process then add/update the items of the parent graph
//else if the import is as referred graph then add the items to referred resources
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
var index = checkForMatch(graph.Roles, 'title', this.Roles[i].title);
if(index>=0){
console.log(this.Roles[i].title + ' Role exists in graph');
updateItemsResource (this.Events, 'roles', graph.Roles[index]);
}else{
graph.Roles.push(this.Roles[i]);
}
}else if(parentPro!=undefined && parentPro.isRefer==true){
graph.ReferredRoles.push(this.Roles[i]);
}
};
for (var i = 0; i < this.Keywords.length; i++) {
//if this is simple import or import as simple process then add/update the items of the parent graph
//else if the import is as referred graph then add the items to referred resources
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
if($.inArray(this.Keywords[i], graph.Keywords) === -1){
graph.Keywords.push(this.Keywords[i]);
}
}
};
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
for (var i = 0; i < this.EventTypes.length; i++) {
var index = checkForMatch(graph.EventTypes, 'title', this.EventTypes[i].title);
if(index>=0){
console.log(this.EventTypes[i].title + ' EventType exists in graph');
updateItemsResource (this.Events, 'eventType', graph.EventTypes[index]);
}else{
graph.EventTypes.push(this.EventTypes[i]);
}
};
for (var i = 0; i < this.Parameters.length; i++) {
var index = checkForMatch(graph.Parameters, 'title', this.Parameters[i].title);
if(index>=0){
console.log(this.Parameters[i].title + ' Parameter exists in graph');
//updateItemsResource (this.Events, 'groups', graph.Parameters[index]);
}else{
graph.Parameters.push(this.Parameters[i]);
}
};
}
//push all the events/connections process in the graph
for (var i = 0; i < this.Events.length; i++) {
var curItem = this.Events[i];
// console.log(curItem);
// console.log(checkForMatch(graph.Events, 'id', curItem.id));
//check if the Event with same name exists in the graph to be imported then update its id so no confusion occurs
//if the parentPro is undefined this means it is import as subprocess
//if parentPro is defined this means that it is to be nested under referred pro
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
if(graph.checkIfIDExists(curItem.id)==true){
var newID = graph.uniqueEventID(curItem.id, 1);
curItem.getChilds().forEach(function (el) {
if(el.parentId===curItem.id){
el.parentId = newID;
}
})
curItem.id = newID;
}
}
if(checkForMatch(graph.Events, 'guid', curItem.guid)<0){
this.Events[i].graph = graph;
if(options.isProcessImport==true || options.isFormImport==true){
this.Events[i].nLevel = this.Events[i].nLevel + 1;
if(parentPro!=undefined){
this.Events[i].isRefer = parentPro.isRefer;
}
}
graph.Events.push(this.Events[i]);
}
};
//push all the events/connections process in the graph
for (var i = 0; i < this.Processes.length; i++) {
var curItem = this.Processes[i];
console.log(curItem);
console.log(checkForMatch(graph.Processes, 'id', curItem.id));
//check if the Event with same name exists in the graph to be imported then update its id so no confusion occurs
//if the parentPro is undefined this means it is import as subprocess
//if parentPro is defined this means that it is to be nested under referred pro
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
if(graph.checkIfIDExists(curItem.id)==true){
var newID = graph.uniqueEventID(curItem.id, 1);
curItem.getChilds().forEach(function (el) {
if(el.parentId===curItem.id){
el.parentId = newID;
}
})
curItem.id = newID;
}
}
if(checkForMatch(graph.Processes, 'guid', curItem.guid)<0){
this.Processes[i].graph = graph;
if(options.isProcessImport==true || options.isFormImport==true){
this.Processes[i].nLevel = this.Processes[i].nLevel + 1;
if(parentPro!=undefined && this.Processes[i].parentPro==="root"){
this.Processes[i].parentGUID = parentPro.guid;
this.Processes[i].parentObj = parentPro;
this.Processes[i].parentId = parentPro.id;
this.Processes[i].parentPro = parentPro;
graph.Childs.push(this.Processes[i]);
}
}
graph.Processes.push(this.Processes[i]);
}
};
//push all the events/connections process in the graph
for (var i = 0; i < this.Connections.length; i++) {
console.log(this.Connections[i]);
this.Connections[i].graph = graph;
graph.Connections.push(this.Connections[i]);
};
if(options.importDocTitle==true){
graph.Title = this.Title;
}
if(options.importKeywords==true){
graph.Keywords = this.Keywords;
}
if(options.importDocDesc==true){
graph.Description = this.Description;
}
graph.Documentation = this.Documentation
if(options.importDocLanguage==true){
graph.Language = this.Language;
}
if(options.importDocDomain==true){
graph.Domain = this.Domain;
}
if(options.importDocZoom==true){
graph.ZoomLevel = this.ZoomLevel
events.fire('zoomLevelUpdated', graph);
}
if(options.importDocBG==true){
graph.BG = this.BG;
events.fire('graphBGUpdated', graph);
}
graph.Type = this.Type;
//push all the events/connections process in the graph
/*for (var i = 0; i < this.ExternalEvents.length; i++) {
console.log(this.ExternalEvents[i]);
graph.ExternalEvents.push(this.ExternalEvents[i]);
};
//push all the events/connections process in the graph
for (var i = 0; i < this.AllSharedEvents.length; i++) {
console.log(this.AllSharedEvents[i]);
graph.AllSharedEvents.push(this.AllSharedEvents[i]);
};*/
//push all the events/connections process in the graph
if(options.isProcessImport==true || options.isFormImport==true){
//create a process first and then add these items in it
if(parentPro==undefined){
parentPro = graph.createProcess();
graph.Parents.push(parentPro);
}
for (var i = 0; i < this.Parents.length; i++) {
//if any parent has parentObj as null and parentPro as root
//then set this new Pro as a parent for that
if((this.Parents[i].parentObj==null || this.Parents[i].parentObj==undefined) && this.Parents[i].parentPro=='root'){
if(checkForMatch(graph.Parents, 'guid', this.Parents[i].guid)<0){
this.Parents[i].parentGUID = parentPro.guid;
this.Parents[i].parentObj = parentPro;
this.Parents[i].parentId = parentPro.id;
this.Parents[i].parentPro = parentPro;
graph.Parents.push(this.Parents[i]);
}
//now as this parent is child of parentPro so add this in the childs array
if(checkForMatch(graph.Childs, 'guid', this.Parents[i].guid)<0){
graph.Childs.push(this.Parents[i]);
}
}else{
//push this item to graph parents
if(checkForMatch(graph.Parents, 'guid', this.Parents[i].guid)<0){
graph.Parents.push(this.Parents[i]);
}
}
};
//get all the root events and set them as childs for this new pro
this.getAllRootEvents().forEach(function (el) {
//if any parent has parentObj as null and parentPro as root
//or the item is not already there then push it to childs
if((el.parentObj==null || el.parentObj==undefined) && el.parentPro=='root' && checkForMatch(graph.Childs, 'guid', el.guid)<0){
el.parentGUID = parentPro.guid;
el.parentObj = parentPro;
el.parentId = parentPro.id;
el.parentPro = parentPro;
graph.Childs.push(el);
}
});
//now move all the childs of this to graph
for (var i = 0; i < this.Childs.length; i++) {
if(checkForMatch(graph.Childs, 'guid', this.Childs[i].guid)<0){
if(parentPro!=undefined && this.Childs[i].parentPro == "root"){
this.Childs[i].parentPro = parentPro;
}
graph.Childs.push(this.Childs[i]);
}
console.log(this.Childs[i]);
};
//parentPro.dcrgraph = stringToXML(this.writeXML(true).XML);
}else{
for (var i = 0; i < this.Parents.length; i++) {
console.log(this.Parents[i]);
graph.Parents.push(this.Parents[i]);
};
//push all the events/connections process in the graph
for (var i = 0; i < this.Childs.length; i++) {
console.log(this.Childs[i]);
graph.Childs.push(this.Childs[i]);
};
}
if(options.isFormImport==true){
//- we will use eventType=DCRForm to identify forms in "open" - if "new group /subprocess is enabled - then show "Open"
var item = graph.EventTypes.filter(function(el){ return el.title =="DCRForm"})
var curEventType = null;
if(item.length>0){
curEventType = item[0];
//check if id parameter exists or not
var idParam = curEventType.parameters.filter(function(el){ return el.title =="id"})
if(idParam.length>0){
idParam[0].required = "true";
}else{
curEventType.parameters.push({title: "id", value: "0", required: "true"})
}
}else{
curEventType = graph.addEventType({id: GUID(), title: "DCRForm", description: "", parameters: [{title: "id", value: "0", required: "true"}]})
}
if(parentPro!=undefined){
parentPro.custom.eventType = curEventType
parentPro.custom.eventTypeData = [{title: "id", value: parentPro.graphID, required: "true"}]
}
}
//for now just adding importing layers and highlights as it is
//TODO: need to optimize the import for layers as there can be many now with default being true
for (var i = 0; i < this.HighlightLayers.length; i++) {
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
graph.HighlightLayers.push(this.HighlightLayers[i]);
}
};
for (var i = 0; i < this.Highlights.length; i++) {
if((options.isFormImport!=true && options.isProcessImport!=true) || (options.isProcessImport==true && parentPro==undefined) || (parentPro!=undefined && parentPro.isRefer==false)){
graph.Highlights.push(this.Highlights[i]);
}
};
}
/** Main function to import DCR XML and populate the graph with XML data.
*
* @memberof Graph
* @method import
*
* @param {string} graphXML -XML of graph to be imported
* @param {number} graphID -Used for creating forms/processes
* @param {object} configs -Main configs
* @param {object} options -Import options
* @param {function} callback -Function to be called after import is done
*
* @returns {object} Returns an object of Graph Class.
*/
graphproto.import = function (graphXML, graphID, configs, options, callback) {
var graph = this;
var importedGraph = new DCR();
if(typeof(graphXML)=='object'){
graphXML = XMLtoString(graphXML)
}else if(typeof(graphXML)=='string'){
}
//if graph is imported as refer graph then use its id to store in the graph
//Always Enable try catch for the production
try{
importedGraph.loadXMLString(graphXML, configs, options);
if(options.isReferred==true && options.isFormImport == true){
console.log('referred form loading');
var parentPro = this.createForm({graphID: graphID, graphXML: stringToXML(graphXML).getElementsByTagName('dcrgraph')[0]});
}else if(options.isReferred==true && options.isProcessImport==true){
console.log('referred graph loading');
var parentPro = this.createProcess({graphID: graphID, graphXML: stringToXML(graphXML).getElementsByTagName('dcrgraph')[0]}, true);
}else if(options.isReferred==false && options.isProcessImport==true){
//just importing in to a sub process
var parentPro = this.createProcess();
this.Parents.push(parentPro);
}
importedGraph.importTo(graph, parentPro, options);
if(typeof(callback) === "function"){
callback(importedGraph, graph, parentPro, options);
}
return this;
}catch(ex){
console.error('error occured during file import '+ex)
events.fire("exception", {message: "graph import failed", details: ex, module: "DCR", functionName: "import"})
}
return this;
}
/** Main funtion to export DCR XML.
*
* @memberof Graph
* @method export
*
* @returns {object} Returns XML as string of Graph.
*/
graphproto.export = function () {
return this.writeXML(true);
}
graphproto.exportGlobalStore = function () {
return '<globalStore>'+
variablesToXML (this, true)+
'</globalStore>';
}
graphproto.isEmpty = function () {
if(this.Events.length>0){
return false
}else if(this.EventTypes.length>0){
return false;
}else if(this.Roles.length>0){
return false;
}else if(this.Groups.length>0){
return false;
}else if(this.Phases.length>0){
return false;
}else if(this.Parameters.length>0){
return false;
}else if(this.Processes.length>0){
return false;
}
return true;
}
/** Main funtion to load the DCR XML and populate the graph with XML data. The standard DCR Graph XML structure will be needed to properly load the Graph
*
* @memberof Graph
* @method loadXMLString
*
* @param {string} graphXML -Requires DCR Graph XML in string format to load the graph
*
* @example
* <!doctype html>
* <html>
* <head>
* <meta charset="utf-8">
* <title>Demo App</title>
* </head>
* <body>
* <script src="jquery.js"></script>
* <script src="xml2json.js"></script>
* <script src="DCR.js"></script>
* <script>
* $(document).ready(function () {
* var myApp = new DCR();
*
* $('#loadGraph').on('click', function () {
* var fileXML = $('#xmlInput').val();
* if(fileXML==" " || fileXML == ""){
* alert('Invalid Input');
* return;
* }
* myApp.loadXMLString(fileXML);
* //graph will reset first by default by this function just in case user clicks several time
* //so each time new graph is to be laoded properly
* });
* })
*
* </script>
* <textarea id="xmlInput"></textarea>
* <input type="submit" id="loadGraph" value="Load DCR Graph" />
* </body>
* </html>
* @returns {object} Returns an object of Graph Class.
*///loadDCRXML
graphproto.loadXMLString = function (graphXML, configs, options, reloadRefers) {
//this.reset();
//console.profile('loading graph XML');
console.time('loading graph XML')
//console.dirxml('loading XML' + graphXML);
if(graphXML==null || graphXML == undefined || graphXML.length==0){
return;
}
//Always Enable try catch for the production
try{
this.XML = graphXML;
var parsedGraphXML = stringToXML(graphXML);
this.ProcessedPs = [];
//using this as this scope changes inside the functions
var graph = this;
var dOptions = {
isImported : false,
isReferred : false,
importRoles : true,
importGroups : true,
importTypes : true,
importParams : true,
importPhases : true,
importDataTypes : true,
importDocDesc : true,
importDocLanguage : true,
importDocDomain : true,
importDocTitle : true,
importKeywords : true,
importDocFilters : true,
importDocZoom : true,
importDocBG : true,
isSimulation : false
}
if(options!=undefined){
for(var k in options) dOptions[k]=options[k];
}
/*if(referProId==null || referProId=="" || referProId==" " ){
referProId = undefined;
}
if(referID==null || referID=="" || referID==" " ){
referID = undefined;
}*/
//getting the graph and its subgraphs
getGraphResouces(this, $(parsedGraphXML).find('dcrgraph')[0], true, dOptions);
// creating connections
for (var i = 0; i < this.AllConnections.length; i++) {
setupConnections(this, this.AllConnections[i][0], this.AllConnections[i][1], this.AllConnections[i][2]);
};
this.AllConnections = [];
this.Highlights = this.Highlights.map(function(el){
el.items = el.items.map(function (item) {
//find the item in graph and add it to highlight
return findHighlightItem(graph, el.type, item)
})
return el;
})
//add global marking of the xml if any exists
var globalMarkings = $(parsedGraphXML).find('globalMarking');
this.GlobalMarking = $.xml2json(globalMarkings[globalMarkings.length-1] || null);
}catch(ex){
console.error('Exception' + ex);
events.fire("exception",
{
message: "graph xml loading failed",
module: "DCR",
fileName: "DCR.js",
functionName: "loadXMLString",
logLevel: "Error",
severity: "High",
parameters: JSON.stringify({graphXML:graphXML, configs:configs, options:options, reloadRefers:reloadRefers}),
details: ex
})
}
// this.loadFragments() // ! not needed now, may be required if we need to load fragments on graph load
this.loadReferGraphs(reloadRefers, configs)
console.timeEnd('loading graph XML')
//console.profileEnd('loading graph XML');
return this;
};
//function to get the graphs of the user, have to pass referProId to get it later for usage
function getGraph (graph, graphId, referPro, configs, callback, callbackGet) {
$.ajax({
type: configs.api.graph.get.type,
contentType: "application/json; charset=utf-8",
url: configs.api.graph.get.url +parseInt(graphId),
dataType: "json",
beforeSend : function(){
console.log('fetching graph')
}
}).done(function(restResponse){
if(restResponse!=null || restResponse!= undefined){
if(typeof(callbackGet) === "function"){
//if process id is passed then it can be used for storing the graph to
callbackGet(graph, restResponse, referPro, callback);
}
return restResponse;
}
}).fail(function(){
console.log("some error occured in getting graph.");
events.fire('loader', 'hide');
events.fire('showMessage', { msg: (translations? translations.something_went_wrong: "some error occured in getting graph."), type: 'error', timeout: 2000 })
events.fire("getGraphFailed", {graph: graph, id: graphId, referPro: referPro})
events.fire("error", {message: "some error occured in getting graph.", module: "DCR", functionName: "getGraph", parameters: JSON.stringify({graphId: graphId})
})
}).always(function(){
console.log("completed getting graph");
events.fire('loader', 'hide');
});
}
//function to check if a process is dynmaic form or not?
function isDynamicForm (curPro) {
if(curPro!=null && curPro!=undefined && curPro.dcrgraph !=undefined && curPro.dcrgraph !=null){
if(curPro.dcrgraph.attributes.dataTypesStatus!=undefined && curPro.dcrgraph.attributes.dataTypesStatus.value == "show"){
return true;
}
}
return false;
}
function populateReferGraph(graph, restResponse, referPro, reloadRefers, callback, isFormImport) {
if(reloadRefers==true){
referPro.dcrgraph = stringToXML(restResponse.GraphXml).getElementsByTagName('dcrgraph')[0];
}else{
//get the refer pro xml from the current xml
}
if(isFormImport==true){
var options = {isFormImport:true}
}else{
var options = {isProcessImport:true}
}
// if any refer graph with dcrgraph then load it and import into graph
var curGraph = new DCR();
if( referPro.dcrgraph){
curGraph.loadXML(referPro.dcrgraph);
curGraph.importTo(graph, referPro, options);
console.log('referred graph loading');
}
referPro.isDynamicForm = isDynamicForm(referPro);
if(graph.ReferredCons.length>0){
for (var i = 0; i < graph.ReferredCons.length; i++) {
setupConnections(graph, graph.ReferredCons[i][0], graph.ReferredCons[i][1], graph.ReferredCons[i][2]);
};
}
events.fire('referredProcessLoaded', {graph : graph, referGraph: curGraph, referPro: referPro});
if(typeof(callback)=='function'){
callback(curGraph, referPro)
}
}
function loadReferGraphs(graph, items, reloadRefers, configs, callback, isFormImport) {
for (var i = 0; i < items.length; i++) {
var curGraphId = parseInt(items[i].referId);
//var referProId = items[i].id;
if ($.inArray(items[i], graph.ProcessedReferGraphs) === -1) {
graph.ProcessedReferGraphs.push(items[i]);
if (reloadRefers == true) {
//let create a new graph for the curProcess and have this graph xml updated with the XML from refer graph DB XML
getGraph(graph, curGraphId, items[i], configs, callback, function (graph, restResponse, referPro, callback) {
populateReferGraph(graph, restResponse, referPro, reloadRefers, callback, isFormImport)
})
} else {
populateReferGraph(graph, null, items[i], reloadRefers, callback, isFormImport)
}
}
}
}
//main function to be used for loading referGraphs in the DCR
graphproto.loadReferGraphs = function (reloadRefers, configs, callback){
if(this.ReferredPros.length<1 && this.ReferredForms.length<1){
return this;
}
this.ProcessedReferGraphs = [];
var graph = this;
//get the graph info for all the sub processes and populate it on the graph so that it can be shown to user
loadReferGraphs(graph, this.ReferredPros, reloadRefers, configs, callback)
loadReferGraphs(graph, this.ReferredForms, reloadRefers, configs, callback, true)
}
function loadFragment(graph, container) {
if(container.dcrgraph===null){
return; //if no graph xml
}
var options = {isProcessImport:true}
var curGraph = new DCR();
curGraph.loadXML(container.dcrgraph);
curGraph.importTo(graph, container, options);
//import any referred connections from fragment to main
//TODO: need to verify this with different graphs as it may have issues if ids for items are same from main/fragment graph
for (var i = 0; i < curGraph.ReferredCons.length; i++) {
setupConnections(graph, curGraph.ReferredCons[i][0], curGraph.ReferredCons[i][1], curGraph.ReferredCons[i][2]);
};
}
//main function to be used for loading fragments in the DCR
graphproto.loadFragments = function (callback){
if(this.Fragments.length<1){
return this;
}
var graph = this;
//populate fragments in the graph
graph.Fragments.map(function (el) {
return loadFragment(graph, el)
})
if(typeof(callback)=='function'){
callback(graph)
}
}
//creating runtimes xml markup
function runtimesToXML (eRuntime, xEvents){
var runtimeTag =eRuntime;
if (eRuntime == 'pending') {
runtimeTag = 'pendingResponses';
}
var runtimeOutput = '<'+runtimeTag+'>';
xEvents.forEach(function(el){
if(el.getRuntime(eRuntime)!=undefined && el.getRuntime(eRuntime)==true){
var extraAttr = '';
if (eRuntime == 'pending' && el.deadline!=undefined) {
extraAttr = 'deadline="'+replaceSlashes(htmlEncode(el.deadline))+'"';
}else if(eRuntime == 'executed' && el.time!=undefined){
extraAttr = 'time="'+replaceSlashes(htmlEncode(el.time))+'"';
}
runtimeOutput += '<event id="'+el.id+'" '+extraAttr+'/>'
}
});
runtimeOutput += '</'+runtimeTag+'>';
return runtimeOutput;
}
function storeConnection (el, curProcess, gEvents, subProcesses, gMultiInsProcesses) {
var makeXML = false //, useFromExtID = false, useToExtID = false;
var fromParPro = el.from.getParentProcess();
var toParPro = el.to.getParentProcess();
//compare the parents of the from and to elements
if(fromParPro==toParPro && toParPro ==curProcess){
//if both lie in the same parent then use local ids and xml
//useLocal = true;
makeXML = true;
}else {
// if it is from and it exists in this processes then check the to
if($.inArray(el.from, gEvents)>=0){
if(fromParPro==undefined && toParPro!=undefined){
//if from is in parent and to is not in parent but another process then it is already stored in that process
makeXML = false;
}else if(el.from.nLevel>=el.to.nLevel){
makeXML = true;
}
}else if($.inArray(el.to, gEvents)>=0 || $.inArray(el.to, subProcesses)>=0 || $.inArray(el.to, gMultiInsProcesses)>=0){
if(toParPro==undefined && fromParPro!=undefined){
//if to is in parent and from is not in parent but another process then it is already stored in that process
makeXML = false;
}else if(el.to.nLevel>=el.from.nLevel){
makeXML = true;
}
}
}
return makeXML;
}
//creating constraints xml markup
function constraintsToXML (graph, curProcess, eConstraint, gEvents, gSubProcesses, gMultiInsProcesses){
var constraintTag = eConstraint+'s';
var constraintOutput = '<'+constraintTag+'>';
graph.Connections.forEach(function(el){
if(el.type==eConstraint){
//get source parent pro and target parent pro
//if which ever has higher nLevel store XML in that
if($.inArray(el.from, gEvents)>=0 || $.inArray(el.to, gEvents)>=0 || ($.inArray(el.from, gEvents)>=0 && gSubProcesses!=undefined && $.inArray(el.to, gSubProcesses)>=0) || ($.inArray(el.from, gEvents)>=0 && $.inArray(el.to, gMultiInsProcesses)>=0)){
//check if the source or the target is an event inside a process and is shared or not and use its credentials to make the connections
var fromID = el.from.id;
var toID = el.to.id;
if(storeConnection(el, curProcess, gEvents, gSubProcesses, gMultiInsProcesses) == true){
constraintOutput += '<'+eConstraint+' sourceId="'+fromID+'" targetId="'+toID+'"';
constraintOutput += ' filterLevel="'+htmlEncode(parseInt(el.level))+'" ';
constraintOutput += ' description="'+htmlEncode(el.description)+'" ';
constraintOutput += ' time="'+htmlEncode(el.time)+'" ';
constraintOutput += ' groups="'+htmlEncode(el.groups.map(function (x) { return x.title; }).join(','))+'" ';
if(el.guard.length>0){
constraintOutput += ' expressionId="'+el.from.id + "-path-"+el.to.id+'--'+el.type+'" ';
}
if(el.valueExpression.length>0){
constraintOutput += ' valueExpressionId="'+el.from.id + "-path-"+el.to.id+'--'+el.type+'--value" ';
}
if(el.link.length>0){
constraintOutput += ' link="'+el.link+'" ';
}
if(el.businessRule){
constraintOutput += ' businessRule="'+el.businessRule+'" ';
}
constraintOutput += ' />';
}
}else{
//there are no connections for gevents so check for shared events if there any connections is between them
}
}
});
constraintOutput += '</'+constraintTag+'>';
return constraintOutput;
}
//creating label mapping xml markup
function labelMappingToXML (elabelMap, xEvents){
var mappingTag = elabelMap+'s';
var mappingOutput = '<'+mappingTag+'>';
xEvents.forEach(function(el){
if(el.custom.label.length>0 && el.custom.label !=' '){
var objId = el.id;
mappingOutput += '<'+elabelMap+' eventId="'+objId+'" labelId="'+replaceSlashes(htmlEncode(el.custom.label))+'"/>';
}
});
mappingOutput += '</'+mappingTag+'>';
return mappingOutput;
}
//creating events xml markup on base of object and its properties
function eventsToXml (graph, xEvents, generateChilds, isProcess, isShared, includeRefer){
xEvents.forEach(function(el){
if($.inArray(el, graph.ProcessedEvents) === -1 ){
if(el.baseType=='process' && isProcess==false){
return;
}
var objId = el.id,
eventScope = el.scope,
objBox = el.getBBox(),
xLoc = parseInt(objBox.x),
yLoc = parseInt(objBox.y),
objId = objId,
eventSequence = 0;
if(isProcess==true){
if(el.instanceId==undefined){
el.instanceId = ''
}
if(el.template==undefined){
el.template = ''
}
eventXMLStr += '<subProcess id="'+objId+'" isRefer="'+el.isRefer+'" multiInstance="'+el.multiInstance+'" name="'+htmlEncode(el.custom.label)+'" instanceId="'+el.instanceId+'" template="'+el.template+'" >';
}else{
var attrs = {id: objId}
//<computation id="B-computation-expression"></computation>
if(el.computation.length>0 || el.dmnXML.length>0){
attrs.computation = el.id+'-computation'
}
if(el.interfaceType && (el.interfaceType=="inner" || el.interfaceType=="outer")){
attrs.interfaceType = el.interfaceType;
}
if(el.commonId && el.commonId==true){
attrs.commonId = el.commonId;
}
if(el.type == 'form'){
attrs.cancelText = replaceSlashes(htmlEncode(el.cancelText));
attrs.sendText = replaceSlashes(htmlEncode(el.sendText));
attrs.hideCancel = el.hideCancel;
attrs.formShowInitialPhase = parseInt(el.formShowInitialPhase);
}
// if any datatype other than default, add it to xml as type=<type>, i-e: (el.type =="subprocess" || el.type=="nesting" || el.type=="form" )
if(typeof el.type != 'undefined' && el.type!=undefined && el.type !="default"){
attrs.type = el.type;
if(el.isFragment() == true){
attrs.fragmentId = el.fragmentId;
attrs.serialise = "inline";
}
}
eventXMLStr += '<event '
for(attr in attrs){
eventXMLStr += attr+'="'+attrs[attr]+'" ';
}
eventXMLStr += '>';
}
var elExpressionMsg = el.getResourceExpression('message');
var elExpressionAttrs = {
message: (elExpressionMsg != null? replaceSlashes(htmlEncode(elExpressionMsg)):'')
}
var rExpression = el.getResourceExpression('value');
if(rExpression!=null && rExpression.trim().length>0){
elExpressionAttrs.id = el.id+'-resource'
}
eventXMLStr += '<precondition ';
for(attr in elExpressionAttrs){
eventXMLStr += attr+'="'+elExpressionAttrs[attr]+'" ';
}
eventXMLStr += ' />';
eventXMLStr += '<custom>'+
'<visualization>'+
'<location xLoc="'+xLoc+'" yLoc="'+yLoc+'" />'+
'<colors bg="'+el.custom.visualization.colors.bg+'" textStroke="'+el.custom.visualization.colors.textStroke+'" stroke="'+el.custom.visualization.colors.stroke+'" />'+
'</visualization>'+
'<roles>';
//prepare roles of event
if(el.custom.roles.length>0){
el.custom.roles.forEach(function(eRole){
eventXMLStr += '<role>'+replaceSlashes(htmlEncode(eRole.title))+'</role>'
})
}else{
eventXMLStr += '<role />';
}
eventXMLStr += '</roles>'+
'<groups>';
//prepare groups of event
if(el.custom.groups.length>0){
el.custom.groups.forEach(function(eGroup){
eventXMLStr += '<group>'+replaceSlashes(htmlEncode(eGroup.title))+'</group>'
})
}else{
eventXMLStr += '<group />';
}
eventXMLStr += '</groups>'+
'<phases>';
//prepare groups of event
if(el.custom.phases.length>0){
el.custom.phases.forEach(function(ePhase){
eventXMLStr += '<phase>'+replaceSlashes(htmlEncode(ePhase.title))+'</phase>'
})
}else{
eventXMLStr += '<phase />';
}
eventXMLStr += '</phases>'+
'<eventType>';
//setting Event Type
if(el.custom.eventType!= undefined && el.custom.eventType.title!= 'none'){
eventXMLStr += replaceSlashes(htmlEncode(el.custom.eventType.title));
}
eventXMLStr += '</eventType>'+
'<eventScope>'+ eventScope + '</eventScope>';
if(el.custom.eventTypeData!=undefined){
eventXMLStr += '<eventTypeData>';
for (var i = 0; i < el.custom.eventTypeData.length; i++) {
var curParam = el.custom.eventTypeData[i];
var attrs = 'title="'+replaceSlashes(htmlEncode(curParam.title))+'" value="'+replaceSlashes(htmlEncode(curParam.value))+'" required="'+curParam.required+'"';
eventXMLStr += '<parameter '+attrs+' />';
//<parameter title="untitled" required="true" value="asd"></parameter>
};
eventXMLStr += '</eventTypeData>';
}
eventXMLStr += '<eventDescription>';
if(el.custom.eventDescription!=undefined && htmlEncode(el.custom.eventDescription).length>0){
eventXMLStr += replaceSlashes(htmlEncode(el.custom.eventDescription));
}
eventXMLStr += '</eventDescription>';
eventXMLStr += '<purpose>';
if(el.custom.purpose!=undefined && htmlEncode(el.custom.purpose).length>0){
eventXMLStr += replaceSlashes(htmlEncode(el.custom.purpose));
}
eventXMLStr += '</purpose>';
eventXMLStr += '<guide>';
if(el.custom.guide!=undefined && htmlEncode(el.custom.guide).length>0){
eventXMLStr += replaceSlashes(htmlEncode(el.custom.guide));
}
eventXMLStr += '</guide>';
eventXMLStr += '<insight use="'+ (el.custom.insight!=undefined && el.custom.insight.use!=undefined? el.custom.insight.use: false )+'">';
if(el.custom.insight!=undefined && el.custom.insight.deadlineExpression!=undefined && el.custom.insight.deadlineExpression.trim().length>0){
eventXMLStr += '<deadlineExpression>';
eventXMLStr += replaceSlashes(htmlEncode(el.custom.insight.deadlineExpression));
eventXMLStr += '</deadlineExpression>';
}
eventXMLStr += '</insight>';
//level of the event
eventXMLStr += '<level>'+htmlEncode(el.custom.level)+'</level>';
if(el.custom.sequence!=undefined && parseInt(el.custom.sequence) != NaN){
eventSequence = parseInt(el.custom.sequence);
eventXMLStr += '<sequence>'+eventSequence+'</sequence>';
}
eventXMLStr += '<costs>'+replaceSlashes(htmlEncode(toNumber(el.custom.costs)))+'</costs>';
// //<computation id="B-computation-expression"></computation>
// if(el.custom.computation.length>0){
// eventXMLStr += '<computation id="'+el.id+'-computation"></computation>';
// }
if(isProcess==true){
eventXMLStr += '<label>'+replaceSlashes(htmlEncode(el.custom.label))+'</label>'+
'</custom>';
}else{
//event data
eventXMLStr += '<eventData>';
//typeof(el.custom.eventData)==="object" && el.custom.eventData.dataType.type != undefined
if(el.getDataType()!=undefined && el.custom.eventData.dataType.type != undefined){
var dataType = el.custom.eventData.dataType;
dataType.type = dataType.type.toLowerCase()
var eventSequence = 0;
if(dataType.sequence!=undefined && parseInt(dataType.sequence) != NaN){
eventSequence = parseInt(dataType.sequence);
}
var displayWidth ="medium";
if(dataType.width !=undefined){
displayWidth = dataType.width
}
switch(dataType.type){
case 'int':
case 'money':
case 'float':
case 'text':
case 'date':
case 'datetime':
case 'password':
if (dataType.type != 'date' && dataType.type != 'datetime') {
if (dataType.type == 'float') {
dataType.min = parseFloat(dataType.min)
dataType.max = parseFloat(dataType.max)
}
else {
dataType.min = parseInt(dataType.min)
dataType.max = parseInt(dataType.max)
}
}
eventXMLStr += '<dataType sequence="'+eventSequence+'" width="'+displayWidth+'" min="'+dataType.min+'" max="'+dataType.max+'" placeholder="'+htmlEncode(dataType.placeholder)+'" hinttext="'+htmlEncode(dataType.hinttext)+'">'+dataType.type+'</dataType>';
break;
case 'bool':
case 'label':
eventXMLStr += '<dataType sequence="'+eventSequence+'" width="'+displayWidth+'" default="'+dataType.default+'" placeholder="'+htmlEncode(dataType.placeholder)+'" hinttext="'+htmlEncode(dataType.hinttext)+'">'+dataType.type+'</dataType>';
break;
case 'table':
eventXMLStr += '<dataType sequence="'+eventSequence+'" width="'+displayWidth+'" placeholder="'+htmlEncode(dataType.placeholder)+'" hinttext="'+htmlEncode(dataType.hinttext)+'">'+dataType.type+'</dataType>';
var curChoices = el.custom.eventData.dictionary.item;
if(curChoices.length>0){
eventXMLStr += '<dictionary>';
for (var i = 0; i < curChoices.length; i++) {
eventXMLStr += '<item key="'+htmlEncode(curChoices[i].key)+'" value="'+htmlEncode(curChoices[i].value)+'"></item>'
};
eventXMLStr += '</dictionary>';
}
break;
case 'slider':
eventXMLStr += '<dataType sequence="' + eventSequence + '" width="' + displayWidth + '" placeholder="' + htmlEncode(dataType.placeholder) + '" hinttext="' + htmlEncode(dataType.hinttext) + '">' + dataType.type + '</dataType>';
var curChoices = el.custom.eventData.dictionary.item;
if (curChoices.length > 0) {
eventXMLStr += '<dictionary>';
for (var i = 0; i < curChoices.length; i++) {
eventXMLStr += '<item key="' + htmlEncode(curChoices[i].key) + '" value="' + htmlEncode(curChoices[i].value) + '"></item>'
};
eventXMLStr += '</dictionary>';
}
break;
case 'choice':
eventXMLStr += '<dataType sequence="' + eventSequence + '" width="' + displayWidth + '" placeholder="' + htmlEncode(dataType.placeholder) + '" hinttext="' + htmlEncode(dataType.hinttext) + '" dataSetList="' + htmlEncode(dataType.dataSetList) + '" multiple="'+ (typeof dataType.multiple=="undefined" || dataType.multiple=="undefined"? false: dataType.multiple) +'" format="'+(typeof dataType.format=="undefined" || dataType.format=="undefined"? false: dataType.format)+'">' + dataType.type + '</dataType>';
var curChoices = [];
if(el.custom.eventData.dictionary!=undefined && el.custom.eventData.dictionary.item!=undefined){
curChoices = el.custom.eventData.dictionary.item;
}
eventXMLStr += '<dictionary>';
for (var i = 0; i < curChoices.length; i++) {
eventXMLStr += '<item label="'+htmlEncode(curChoices[i].key || curChoices[i].label)+'" value="'+htmlEncode(curChoices[i].value)+'"></item>'
};
eventXMLStr += '</dictionary>';
break;
default:
eventXMLStr += '<dataType sequence="'+eventSequence+'" width="'+displayWidth+'" min="" max="" placeholder="'+htmlEncode(dataType.placeholder)+'" hinttext="'+htmlEncode(dataType.hinttext)+'">'+dataType.type+'</dataType>';
break;
}
eventXMLStr += '<validationRules>'
if(dataType.rules!=undefined){
for (var i = 0; i < dataType.rules.length; i++) {
var curRule = dataType.rules[i];
// if rule has details, which are when we assign rule, then use it's title
var curRuleDetails = curRule['details'];
// if details are not there which are when we load the xml, as we don't put the updated rules in xml
var curRuleTitle = typeof curRuleDetails !=='undefined'? curRuleDetails.Title:
curRule['title']? curRule['title']: ''
eventXMLStr += '<rule id="'+replaceSlashes(htmlEncode(curRule.id))+'" title="'+replaceSlashes(htmlEncode(curRuleTitle))+'" >'+
'<customMessage>'+replaceSlashes(htmlEncode(curRule.customMessage))+'</customMessage>'+
'<parameters>';
for (var j = 0; j < curRule.parameters.length; j++) {
var currentParam = curRule.parameters[j]
eventXMLStr += '<parameter id="'+replaceSlashes(htmlEncode(currentParam.id))+'" required="'+currentParam.required+'" value="'+replaceSlashes(htmlEncode(currentParam.value))+'" title="'+replaceSlashes(htmlEncode(currentParam.title))+'" type="'+currentParam.type+'"></parameter>'
};
eventXMLStr += '</parameters>'+
'</rule>';
};
}
eventXMLStr += '</validationRules>'
}
eventXMLStr += '</eventData>'+
'<interfaces>';
if(el.custom.interfaces.length>0){
el.custom.interfaces.map(function(curItem){
eventXMLStr += '<interface from="' + curItem.from + '" to="' + curItem.to + '" />';
return curItem;
})
}
eventXMLStr += '</interfaces>';
eventXMLStr += '</custom>';
graph.ProcessedEvents.push(el);
if(generateChilds==true){
//check if this event has any childs then add them here and remove them from
el.getDirectChilds().forEach(function (elx) {
//TODO: don't create child nodes for referred events as form
//if(elx.parentPro=="root" || (elx.parentPro!=undefined && elx.parentPro!=null && elx.parentPro.isForm()!=true)){
if(elx.parentPro!="root" && elx.parentPro!=undefined && elx.parentPro!=null && elx.parentPro.isReferForm()==true){
return;
}
eventsToXml(graph, [elx], true, false, false, includeRefer);
//}
})
}
if((el.isRefer == true && includeRefer== true && el.isReferForm()==true)){
//have to do this to load xml and then write it to make sure clean XML is passed
var curGraph = new DCR();
curGraph.loadXML(el.dcrgraph);
eventXMLStr += curGraph.writeXML(true);
}
eventXMLStr += '</event>';
}
}else{ /*console.log(el.id+' is Already Processed');*/}
})
//return eventXMLStr;
}
function expressionsToXML (graph, curProcess, gEvents, subProcesses, gMultiInsProcesses) {
var expressionsXML = '';
for(var i = 0; i<graph.Connections.length; i++){
if(storeConnection(graph.Connections[i], curProcess, gEvents, subProcesses, gMultiInsProcesses) == true){
if(graph.Connections[i].guard!=undefined && graph.Connections[i].guard.length>0){
expressionsXML += '<expression id="'+graph.Connections[i].from.id+'-path-'+graph.Connections[i].to.id+'--'+graph.Connections[i].type+'" value="'+replaceSlashes(htmlEncode(graph.Connections[i].guard))+'" />'
}
if(graph.Connections[i].type=='update' && graph.Connections[i].valueExpression!=undefined && graph.Connections[i].valueExpression.length>0){
expressionsXML += '<expression id="'+graph.Connections[i].from.id+'-path-'+graph.Connections[i].to.id+'--'+graph.Connections[i].type+'--value" value="'+replaceSlashes(htmlEncode(graph.Connections[i].valueExpression))+'" />'
}
}
}
for (var i = 0; i < graph.Events.length; i++) {
if(graph.Events[i].dmnXML!=undefined && graph.Events[i].dmnXML.trim().length>0){
expressionsXML += '<expression id="'+graph.Events[i].id+'-computation" value="'+replaceSlashes(htmlEncode(graph.Events[i].computation))+'" type="DMN">';
expressionsXML += graph.Events[i].prepareDMNXML(graph.Events[i].dmnXML)
expressionsXML += '</expression>';
}else if(graph.Events[i].computation!=undefined && graph.Events[i].computation.trim().length>0){
expressionsXML += '<expression id="'+graph.Events[i].id+'-computation" value="'+replaceSlashes(htmlEncode(graph.Events[i].computation))+'" />'
}
var rExpression = graph.Events[i].getResourceExpression('value');
if(rExpression !=null && rExpression.trim().length>0){
expressionsXML += '<expression id="'+graph.Events[i].id+'-resource" value="'+replaceSlashes(htmlEncode(rExpression))+'" />'
}
}
return expressionsXML;
}
function variablesToXML (graph, isSimVars) {
var variablesXML = '';
var itemsToWrite = [];
for(var i = 0; i<graph.Parameters.length; i++){
itemsToWrite.push(JSON.parse(JSON.stringify(graph.Parameters[i])))
}
if(isSimVars==true){
for (var i = 0; i < graph.GlobalStore.length; i++) {
var index = checkForMatch(itemsToWrite, 'title', graph.GlobalStore[i].id);
// check/update only parameters value
if(index>=0 && itemsToWrite[index].type == graph.GlobalStore[i].type){
itemsToWrite[index].value = replaceSlashes(htmlEncode(graph.GlobalStore[i].value))
}
};
/* add event Default Values to GlobalStore
add the type of item as type of activity datatype sent by globalstore
only have dafault value for datatype events */
var eventsWithDefaults = graph.Events.filter(function (activity) {
return typeof activity.defaultValue == 'object' && activity.defaultValue !=null && activity.defaultValue.value.trim().length> 0 && activity.canHaveDefaultValue()
}).map(function (activity) {
return { title: activity.id, value: replaceSlashes(htmlEncode(activity.defaultValue.value)), type: activity.defaultValue.type, isNull: activity.defaultValue.isNull }
})
itemsToWrite = itemsToWrite.concat(eventsWithDefaults)
}
for (var i = 0; i < itemsToWrite.length; i++) {
var id = itemsToWrite[i].title, value = itemsToWrite[i].value, isNull = '';
if(itemsToWrite[i].isNull==undefined){ //they are parameters
id = replaceSlashes(htmlEncode(itemsToWrite[i].title));
if(isSimVars==true){
value = replaceSlashes(htmlEncode(itemsToWrite[i].svalue))
}else{
value = replaceSlashes(htmlEncode(itemsToWrite[i].value))
}
}else{
isNull = 'isNull="'+itemsToWrite[i].isNull+'" type="'+itemsToWrite[i].type+'"';
}
variablesXML += '<variable id="'+id+'" value="'+value+'" '+isNull+'/>'
};
return variablesXML;
}
function getMakringItemXML(runtime, events) {
var xml = '<'+runtime+'>';
for (var i = 0; i < events.length; i++) {
var attrs = ''
for(attr in events[i]){
//attrs += attr + '="' + events[i][attr] + '" '
attrs += attr + '="' + replaceSlashes(htmlEncode(events[i][attr])) + '" '
}
xml += '<event '+attrs+' />'
}
return xml += '</'+runtime+'>';
}
function generateGlobalMarkings(graph) {
var xml = '<globalMarking>',
markings = graph.GlobalMarking;
var items = ['enabled', 'enabledSubprocesses', 'executed', 'pending', 'included']
for (var i = 0; i < items.length; i++) {
if(markings[items[i]] && markings[items[i]].event){
if(markings[items[i]].event.constructor === Object){
xml += getMakringItemXML(items[i], [markings[items[i]].event])
}else{
xml += getMakringItemXML(items[i], markings[items[i]].event)
}
}
}
return xml += '</globalMarking>';
}
//function to write the appropriate xml markup of the objects available on canvas
function generateXML (graph, gEvents, curProcess, subProcesses, multiInsProcesses, includeRefer){
console.info('generating XML')
var graphAttrs = '';
var attrs = {
title : htmlEncode(graph.Title),
dataTypesStatus : graph.DataTypesStatus,
filterLevel : graph.FilterLevel,
insightFilter : graph.InsightFilter,
zoomLevel : graph.ZoomLevel,
formGroupStyle : graph.FormGroupStyle,
formLayoutStyle : graph.FormLayoutStyle,
graphBG : graph.BG,
graphType : graph.Type,
exercise : graph.Exercise
}
if(graph.Type == '1'){
// if it is a form only then add these properties
attrs.sendText = replaceSlashes(htmlEncode(graph.SendText)),
attrs.cancelText = replaceSlashes(htmlEncode(graph.CancelText)),
attrs.hideCancel = graph.HideCancel
attrs.formShowInitialPhase = parseInt(graph.FormShowInitialPhase)
}
for(attr in attrs){
graphAttrs += attr+'="'+attrs[attr]+'" ';
}
//xml main container start //DCRTitle
var outputXML = '<dcrgraph '+ graphAttrs +'>'+
'<specification>'+
'<resources>'+
'<events>';
//prepare events
//resetting the event xml string and proccessed event array;
eventXMLStr ='', graph.ProcessedEvents = [];
var rootEvents = [];
if(curProcess==undefined){
gEvents.forEach(function(el){
if(el.currentLevel()==0){
rootEvents.push(el)
}
});
eventsToXml (graph, rootEvents, true, false, false, includeRefer);
}else{
gEvents.forEach(function(el){
var elParentLevel = -5;
//if parent element exists then
if(el.getParentProcess()!=undefined){
elParentLevel = el.getParentProcess().currentLevel()
}
//if this event is in subprocess and the its parents nLevel + 1 is = to this nLevel this means this is root element to subProcess
if(el.currentLevel()==parseInt(elParentLevel+1)){
rootEvents.push(el)
}
});
eventsToXml (graph, rootEvents, true, false, false, includeRefer);
}
outputXML += eventXMLStr;
outputXML += '</events>'+
'<subProcesses>';
//check if this graph has any sub process then add them here and remove them from
if(subProcesses.length>0){
for(var i=0; i<subProcesses.length; i++){
if($.inArray(subProcesses[i], graph.ProcessedProcesses) === -1){
//get all the events of the graph neglecting sub process and events inside that process
//getting all the child of the this process
var arrCurProChilds = [];
var arrCurProSubPros = [];
subProcesses[i].getChilds(true, true).forEach(function (el) {
if(el.baseType=='process'){
arrCurProSubPros.push(el);
}else{
arrCurProChilds.push(el);
}
});
graph.ProcessedProcesses.push(subProcesses[i]);
//outputXML += '<subProcess id="'+subProcesses[i].data('eventid')+'">';
eventXMLStr ='';
eventsToXml(graph, [subProcesses[i]],false, true, false, includeRefer);
outputXML += eventXMLStr;
//if we want to export referred graph xml then need its childs
//or if if is not referred and referred XML is not required then just make XML
if((subProcesses[i].isRefer == true && includeRefer== true)){
//have to do this to load xml and then write it to make sure clean XML is passed
var curGraph = new DCR();
curGraph.loadXML(subProcesses[i].dcrgraph);
outputXML += curGraph.writeXML(true);
}else if(subProcesses[i].isRefer != true){
outputXML += generateXML(graph, arrCurProChilds, subProcesses[i], arrCurProSubPros, multiInsProcesses, includeRefer);
}else{
//if process is refer but we don't want its XML then we just send empty
outputXML += '';
}
outputXML += '</subProcess>';
}
}
}
outputXML += '</subProcesses>';
//creating the distribution xml
outputXML += '<distribution></distribution>';
outputXML += '<labels>';
//prepare labels
gEvents.forEach(function(el){
var labelId = el.custom.label;
if(labelId.trim().length>0){
outputXML += '<label id="'+replaceSlashes(htmlEncode(labelId))+'" />';
}
});
outputXML += '</labels>';
//setting up label mappings
outputXML += labelMappingToXML('labelMapping', gEvents);
// prepare strong constraints to be processed
graph.Connections.forEach(function(el){
// for each strong condition create a linked milestone
// remove any relations which are siblings and don't have leader ( strong relation )
if(el.isOrphanSibling()) el.remove();
if(el.isSibling()){ // if is sibling, get the values from strong and apply those here
var strong = el.hasSibling(true);
if(strong){ // if has strong
var props = ['guard', 'time', 'level', 'description', 'groups'];
props.forEach(function(prop){
el[prop] = strong[prop]
if(el.type=='exclude'){
el['guard'] = strong['guard'].length>0 ? 'not('+ strong['guard'] +')' : 'not(true)';
}
})
}
}
})
outputXML += '<expressions>'+
//expressions in the DCR
expressionsToXML (graph, curProcess, gEvents, subProcesses, multiInsProcesses);
outputXML += '</expressions>'+
'<variables>'+
variablesToXML (graph, false);
outputXML += '</variables>'+
'<variableAccesses>'+
'<writeAccesses />'+
'</variableAccesses>'+
'<custom>';
outputXML += '<keywords>'+
graph.Keywords.join(',')
outputXML += '</keywords>'+
'<roles>';
//prepare roles in the DCR
graph.Roles.forEach(function(el){
outputXML += '<role description="'+replaceSlashes(htmlEncode(el.description))+'" specification="'+replaceSlashes(htmlEncode(el.specification))+'">'+replaceSlashes(htmlEncode(el.title))+'</role>';
});
outputXML += '</roles>'+
'<groups>';
//prepare Groups in the DCR
graph.Groups.forEach(function(el){
outputXML += '<group sequence="'+el.sequence+'" description="'+replaceSlashes(htmlEncode(el.description))+'">'+replaceSlashes(htmlEncode(el.title))+'</group>';
});
outputXML += '</groups>'+
'<phases>';
//prepare Groups in the DCR
graph.Phases.forEach(function(el){
outputXML += '<phase sequence="'+el.sequence+'" description="'+replaceSlashes(htmlEncode(el.description))+'">'+replaceSlashes(htmlEncode(el.title))+'</phase>';
});
outputXML += '</phases>'+
'<eventTypes>';
var eventTypesData = []
//prepare Groups in the DCR
graph.EventTypes.forEach(function(el){
outputXML += '<eventType description="'+replaceSlashes(htmlEncode(el.description))+'">'+replaceSlashes(htmlEncode(el.title))+'</eventType>';
if(el.parameters!=undefined){
eventTypesData.push({id: el.title, parameters: el.parameters})
}
});
outputXML += '</eventTypes>'+
'<eventParameters>';
for (var j = 0; j < eventTypesData.length; j++) {
var el = eventTypesData[j]
for (var i = 0; i < el.parameters.length; i++) {
var curParam = el.parameters[i];
var attrs = 'eventtypeid="'+replaceSlashes(htmlEncode(el.id))+'" title="'+replaceSlashes(htmlEncode(curParam.title))+'" value="'+replaceSlashes(htmlEncode(curParam.value))+'" required="'+curParam.required+'"';
outputXML += '<parameter '+attrs+' />';
//<parameter title="untitled" required="true" value="asd"></parameter>
};
};
outputXML += '</eventParameters>'+
'<graphDetails>';
var docDescription = replaceSlashes(htmlEncode(graph.Description));
if(docDescription.length>0){
outputXML += docDescription;
}
outputXML += '</graphDetails>'+
'<graphDocumentation>';
var docDocumentation = replaceSlashes(htmlEncode(graph.Documentation));
if(docDocumentation.length>0){
outputXML += docDocumentation;
}
outputXML += '</graphDocumentation>'+
'<graphLanguage>';
outputXML += replaceSlashes(htmlEncode(graph.Language));
outputXML += '</graphLanguage>'+
'<graphDomain>';
outputXML += replaceSlashes(htmlEncode(graph.Domain));
outputXML += '</graphDomain>'+
'<graphFilters>'+
'<filteredGroups>';
if(graph.FilterGroups.length>0){
outputXML += graph.FilterGroups.join(',');
}
outputXML += '</filteredGroups>'+
'<filteredRoles>';
if(graph.FilterRoles.length>0){
outputXML += graph.FilterRoles.join(',');
}
outputXML += '</filteredRoles>'+
'<filteredPhases>';
if(graph.FilterPhases.length>0){
outputXML += graph.FilterPhases.join(',');
}
outputXML += '</filteredPhases>'+
'</graphFilters>';
outputXML += '<hightlighterMarkup id="HLM">';
if(graph.highlighterMarkup){
outputXML += replaceSlashes(htmlEncode(graph.highlighterMarkup)); //add your code in this section i-e your highligter html/css whatever for now
}
outputXML += '</hightlighterMarkup>';
outputXML += '<highlighterMarkup>';
outputXML += graph.generateHighlightXML() //proper highlighter markup with items to be parsed/generated by DCR library
outputXML += '</highlighterMarkup>'+
'</custom>'+
'</resources>'+
'<constraints>';
//prepare events executed runtime
outputXML += constraintsToXML(graph, curProcess, 'condition', gEvents);
//prepare events executed runtime
outputXML += constraintsToXML(graph, curProcess, 'response', gEvents);
outputXML += constraintsToXML(graph, curProcess, 'coresponse', gEvents);
//prepare events executed runtime
outputXML += constraintsToXML(graph, curProcess, 'exclude', gEvents);
//prepare events executed runtime
outputXML += constraintsToXML(graph, curProcess, 'include', gEvents);
//prepare events executed runtime
outputXML += constraintsToXML(graph, curProcess, 'milestone', gEvents);
outputXML += constraintsToXML(graph, curProcess, 'update', gEvents);
//writing the multi instance connections on XML
outputXML += constraintsToXML(graph, curProcess, 'spawn', gEvents, subProcesses, multiInsProcesses);
outputXML += '</constraints>'+
'</specification>'+
'<runtime>'+
'<custom>'+
generateGlobalMarkings(graph);
if(graph.Time!=null){
outputXML += '<time current="'+graph.Time+'" deadline="'+graph.DeadLine+'" delay="'+graph.Delay+'" />';
}
outputXML += '</custom>'+
'<marking>'+
'<globalStore>'+
variablesToXML (graph, true)+
'</globalStore>';
//prepare events executed runtime
outputXML += runtimesToXML('executed', gEvents);
//prepare events included runtime
outputXML += runtimesToXML('included', gEvents);
//prepare events included runtime
outputXML += runtimesToXML('pending', gEvents);
outputXML += '</marking>'+
'</runtime>'+
'</dcrgraph>';
//console.log(outputXML);
//console.log('data converted');
return outputXML;
}
/** Main function to generate the graph XML based on the data in the graph properties, as this function is called the XML of graph is also updated to latest changes in the graph data
*
* @memberof Graph
* @method writeXML
*
* @example var myApp = new DCR();
* myApp.loadGraph(2275);
* myApp.createEvent({'custom' : {'label':'An Event'}});
* myApp.writeXML();
* //we can get the latest XML of the graph through the above command
* @returns {object} Returns an object of Graph Class.
*/
graphproto.writeXML = function (includeRefer) {
//console.log(this.XML);
this.AllSharedEvents = [];
this.ProcessedProcesses = [];
var subProcesses = [];
this.Processes.forEach(function (el) {
if(el.currentLevel()==0){
subProcesses.push(el);
};
});
//all sub process childs array
var arrAllProcessesChilds = [];
this.Processes.forEach(function (el) {
if($.inArray(el, arrAllProcessesChilds) === -1){
el.getChilds(false).forEach(function (elm, indx) {
arrAllProcessesChilds.push(elm);
});
}
});
var arrAllFormEvents = [];
this.Events.forEach(function (el) {
if($.inArray(el, arrAllFormEvents) === -1 && el.isRefer==true && el.referId==undefined){
arrAllFormEvents.push(el);
}
});
//getting all the events of the main graph but not the subProcesses and forms
var mainGraphEvents = [];
this.Events.forEach(function(el){
if($.inArray(el, mainGraphEvents) === -1 && $.inArray(el, arrAllProcessesChilds) === -1 && $.inArray(el, arrAllFormEvents) === -1 ){
mainGraphEvents.push(el);
}
});
var multiInsProcesses = [];
for (var i = 0; i < this.Processes.length; i++) {
if(this.Processes[i].multiInstance == true){
multiInsProcesses.push(this.Processes[i]);
}
};
var DCRGraphXML = generateXML(this, mainGraphEvents, undefined, subProcesses, multiInsProcesses, includeRefer);
//setting up the computed xml as this graphs final xml
// this.XML = DCRGraphXML;
// return this;
return DCRGraphXML;
};
// TODO: update events to support updateMultiLocated if we need it on write XML
graphproto.updateMultiLocatedEvents = function (prime) {
var self = this;
if(!prime) return
var propsToCopy = ['baseType', 'commonId', 'computation', 'defaultValue', 'precondition', 'runtimes', 'scope', 'type'];
self.Events.filter(function (elx) {
return prime.id === elx.id;
}).map(function (elx) {
var currentvisualization = JSON.parse(JSON.stringify(elx.custom.visualization))
console.log('888888888', elx , currentvisualization)
// copy custom as it is
Object.assign(elx.custom, prime.custom)
// copy visualizations
elx.custom.visualization = currentvisualization
// copy other required properties
propsToCopy.map(function (prop) {
elx[prop] = prime[prop]
})
})
}
/** Fucntion to print current XML of graph on the console
*
* @memberof Graph
* @method printXML
*
* @example var myApp = new DCR();
* myApp.loadGraph(2275);
* myApp.printXML();
* //we can get the latest XML of the graph through the above command
* @returns {object} Returns an object of Graph Class.
*/
graphproto.printXML = function () {
console.log(this.XML);
return this;
};
/* Highlighter related Functions here */
//add highlight for a layer in the graph
function addHighlight(graph, el){
var res = new Highlight(graph, el);
graph.Highlights.push(res);
return res;
}
/** Add Highlight to graph.
*
* @memberof Graph
* @method addHighlight
*
* @returns {Highlight} - An object of `Highlight` class.
*
*/
graphproto.addHighlight = function (el, item) {
if(!el){el = {}}
if(item!=undefined) el.items = [item]
return addHighlight(this, el)
}
/** Add Highlight to an Element of graph, e-g: activity, process, form.
*
* @memberof Element
* @method addHighlight
*
* @returns {Highlight} - An object of `Highlight` class.
*
*/
elproto.addHighlight = function (el) {
if(!el){el = {}}
if(!el.items) el.items = [this]
return addHighlight(this.graph, el)
}
/** Add Highlight to Connection of graph.
*
* @memberof Connection
* @method addHighlight
*
* @returns {Highlight} - An object of `Highlight` class.
*
*/
conproto.addHighlight = function (el) {
if(!el){el = {}}
if(!el.items) el.items = [this]
return addHighlight(this.graph, el)
}
/** Add Highlight layer to graph.
*
* @memberof Graph
* @method addHighlightLayer
*
* @returns {HighlightLayer} - An object of `HighlightLayer` class.
*
*/
graphproto.addHighlightLayer = function (el) {
if(!el){el = {}}
var res = new HighlightLayer(this, el);
this.HighlightLayers.push(res);
return res;
}
/** Get default Highlight layer of graph, it will add a new layer if graph doesn't have any layer.
*
* @memberof Graph
* @method getDefaultHighlightLayer
*
* @returns {HighlightLayer} - An object of `HighlightLayer` class or return undefined.
*
*/
graphproto.getDefaultHighlightLayer = function () {
var res = undefined
if(this.HighlightLayers.length<1){
res = this.addHighlightLayer({default: true, text: this.Description});
}else{
res = this.HighlightLayers.find(function (el) {
return el.default== true;
})
}
return res;
}
/** Get Highlights related to a Element of graph, e-g: activity, process, form.
*
* @memberof Element
* @method getHighlights
*
* @returns {array} - An array of graph Highlights.
*
*/
elproto.getHighlights = function(){
var self = this;
return this.graph.Highlights.filter(function (el) {
var hasItem = [];
if(el.type == "activity"){
hasItem = el.items.filter(function (elx) {
return elx!=undefined && elx.getPath() == self.getPath();
})
}
return hasItem.length>0
})
}
/** Get Highlights related to a connection of graph.
*
* @memberof Connection
* @method getHighlights
*
* @returns {array} - An array of graph Highlights.
*
*/
conproto.getHighlights = function(){
var self = this;
return this.graph.Highlights.filter(function (el) {
var hasItem = [];
if(el.type == "relation"){
hasItem = el.items.filter(function (elx) {
return elx!=undefined && elx.getId() == self.getId();
})
}
return hasItem.length>0
})
}
/** Generates Highlight XML.
*
* @param {Highlight} highlight - An object of `Highlight` class.
*
* @returns {string} -
<highlight type="activity">
<layers>
<layer name="description">
<ranges>
<range start="119" end="130">understand</range>
</ranges>
</layer>
</layers>
<items>
<item id="Activity1" />
</items>
</highlight>
*/
function prepareHighlightXML(highlight) {
return '<highlight type="'+highlight.type+'">'
+'<layers>'
+ (highlight.layers.map(function (item) {
return '<layer name="'+ replaceSlashes(htmlEncode(item.layer.name)) +'">'
+'<ranges>'
+ (item.ranges.map(function (range) {
return '<range start="'+range.start+'" end="'+range.end+'">'+ replaceSlashes(htmlEncode(range.text)) +'</range>'
}).join(''))
+'</ranges>'
+'</layer>'
}).join(''))
+'</layers>'
+'<items>'
+ (highlight.items.map(function (item) {
return '<item id="'+highlight.getItemId(item)+'" />'
}).join(''))
+'</items>'
+'</highlight>';
}
function prepareHighlightLayerXML(layer) {
return '<highlightLayer default="'+layer.default+'" name="'+replaceSlashes(htmlEncode(layer.name))+'">'+ (layer.text!=null?replaceSlashes(htmlEncode(layer.text)): '') +'</highlightLayer>';
}
/** Generates Highlights XML markup for highlights
*
* @memberof Graph
* @method generateHighlightXML
*
* @returns {string} - Highlight markup for DCR containing layers and highlights within it.
*
* @example <highlighterMarkup>
<highlightLayers>
<highlightLayer default="true" name="description">... highlight text here ...</highlightLayer>
</highlightLayers>
<highlights>
... highlight xml here ...
</highlights>
</highlighterMarkup>
*/
graphproto.generateHighlightXML = function () {
var markup = '<highlightLayers>';
//get all layers and then add highlights based on layers
markup += this.HighlightLayers.map(function (el) {
return prepareHighlightLayerXML(el)
}).join('');
markup += '</highlightLayers>'
+'<highlights>';
markup += this.Highlights.map(function (el) {
return prepareHighlightXML(el)
}).join('');
markup += '</highlights>';
return markup;
}
/** Removes the item from Highlights, if there are no more Items in Highlight then Highlight will also be removed
*
* @memberof Graph
* @method removeHighlightsOf
*
* @param {object} item - An object either of Class `Element`, `Connection` or Roles of graph is to be passed.
*
*/
graphproto.removeHighlightsOf = function(item){
removeHighlights(this, item)
}
/** Get a Highlight in graph by using its GUID.
*
* @memberof Graph
* @method getHighlightByGUID
*
* @param {string} id - GUID of the highlight to get.
*
* @returns {object} - Returns Highlight Object, or undefined if no item found.
*/
graphproto.getHighlightByGUID = function(id){
return this.Highlights.find(function (el) {
return el.guid == id;
})
}
//TODO: if any layer is passed then return the text of that layer
/** Get text of Highlight layer, for now it will return the text of ist default layer found, or graph description if none found.
*
* @memberof Graph
* @method getHighlighterText
*
* @param {object} layer - HighlightLayer object to get text of, //optional for now as it is to be implemented later with layer full support
*
* @returns {string} - Returns text of default HighlightLayer or Graph Description
*/
graphproto.getHighlighterText = function (layer) {
var res = this.HighlightLayers.find(function (el) {
return el.default == true;
})
return res!=undefined && res.text!=undefined? res.text: this.Description;
}
/** Get escaped text of Highlight layer to be used as base text to be embeded in DOM
*
* @memberof Graph
* @method getEscapedHighlighterText
*
*
* @returns {string} - Returns text of default HighlightLayer or Graph Description
*/
graphproto.getEscapedHighlighterText = function() {
var highlighterText = this.getHighlighterText()
return typeof highlighterText != 'undefined'? highlighterText.replace(/(?:\r\n|\r|\n)/g, "<br />"): "";
}
/** Default markup function, createe Highlither HTML Node
*
* @param {Highlight} highlight
* @param {Object} item // An object either of Class `Element`, `Connection` or Roles of graph is to be passed.
* @param {HighlightRange} range
* @param {DOMElement} textContainer
* @param {Window} curWindow
* @param {Document} curDocument
*/
function createHighlighterItem(highlight, item, range, textContainer, curWindow, curDocument) {
console.log(textContainer);
if(!textContainer) return
if(curWindow==undefined){ curWindow = window}
if(curDocument==undefined){ curDocument = document}
restoreSelection(textContainer, {start: range.start, end: range.end}, curWindow, curDocument);
console.log(curWindow)
try {
var sel = curWindow.getSelection()
var r = sel.getRangeAt(0)
var sc = r.extractContents()
var span = curDocument.createElement("span");
span.className = "selected-text-"+ highlight.type;
span.appendChild(sc);
r.insertNode(span);
if(item){
span.setAttribute("data-item-id", highlight.getItemId(item));
}
span.setAttribute("id", range.guid);
span.setAttribute("data-highlight-id", highlight.guid);
highlight.attrs.map(function (attr) {
span.setAttribute(attr.name, attr.value);
})
range.hElement = span;
clearSelection(curWindow, curDocument);
} catch (error) {
events.fire("error", {
message: "Something went wrong with creating Highlighter Item",
module: "DCR",
fileName: "DCR.js",
functionName: "createHighlighterItem",
details: error,
parameters: JSON.stringify({highlight: highlight, item: highlight.getItemId(item), range: {start: range.start, end: range.end}})
})
}
}
// gets ranges from layer of highlight and creats html nodes
function createHighlighterRangeItems(layer, el, elx, textContainer, curWindow, curDocument) {
if(el.layers && el.layers.length>0){
// check if item range belongs to current layer
var curLayer = getLayerInHighlight(el, layer)
if(curLayer!=undefined){
// find the layer in el layers and select its ranges only, else skip creating item
curLayer.ranges.map(function (range) {
createHighlighterItem(el, elx, range, textContainer, curWindow, curDocument)
})
}
}else{
throw new Error('no highlight layer found')
}
}
// TODO: need to handle layers here so only default/selected layer items are marked/created
/** Prepares the Html required for Highlighter, this HTML can be used anywhere
*
* @param {Graph} graph
* @param {DOMElement} textContainer
* @param {Window} curWindow
* @param {Document} curDocument
*/
function prepareMarkupHtml(graph, textContainer, curWindow, curDocument) {
var layer = graph.getDefaultHighlightLayer();
if(layer!=undefined){
graph.Highlights.map(function (el) {
//TODO: update function to support both items and ranges
// el.items.length>0?
// el.items.map(function (elx) {
// createHighlighterRangeItems(layer, el, elx, textContainer, curWindow, curDocument)
// }) :
createHighlighterRangeItems(layer, el, null, textContainer, curWindow, curDocument);
})
}else{
throw new Error('no default highlight layer found')
}
}
function getNode(id, document) {
return document.getElementById(id)
}
/** update markup position as well as the layer text
*
* @param {Graph} graph
* @param {DOMElement} textContainer
*/
function updateMarkup(graph, textContainer) {
var defaultLayer = graph.getDefaultHighlightLayer();
if(defaultLayer!=undefined){
var oldMarkup = textContainer.innerHTML;
var a = prepareMarkup(oldMarkup);
var b = generateLayerTextFromMarkup(oldMarkup);
$(document.body).append([a,b])
defaultLayer.text = htmlEncode(b[0].textContent)
/* only update highlights of current default layer, don't update any other highlights as those nodes will not be available.
1. get highlights of current layer
2. get ranges from those highlights for current layer
3. flat the array and update positions for only those ranges in current layer
*/
defaultLayer.getHighlights()
.map(function (item) {
return item.layers.find(function (layerItem) {
return layerItem.layer == defaultLayer
}).ranges;
})
.flat()
.map(function (range) {
console.log(range)
range.updatePosition(getUpdatedPosition(getNode(range.guid, document), a[0], window, document))
clearSelection(window, document);
})
a.remove();
b.remove();
}else{
console.error("no highlight layer found")
}
}
/** Creates the markup required for current app to work with in HTML format, so that app still works as it is
* @memberof Graph
* @method setupHighlighterMarkup
*
* @param {string} textContainerId - Id of the element in which HTML markup is to be generated.
*/
graphproto.setupHighlighterMarkup = function (textContainerNode, curWindow, curDocument) {
prepareMarkupHtml(this, textContainerNode, curWindow, curDocument)
}
/** Updates the range postions if the HTML is edited in Highlighter app.
* @memberof Graph
* @method updateMarkup
*
* @param {string} textContainerId - Id of the element in which HTML markup is to be generated.
*/
graphproto.updateMarkup = function(textContainerNode) {
try{
updateMarkup(this, textContainerNode)
}catch(ex){
console.error('Exception' + ex);
events.fire("exception",
{
message: "update Markup for Highlighter failed",
module: "DCR",
fileName: "DCR.js",
functionName: "updateMarkup",
logLevel: "Error",
parameters: '',
details: ex,
severity: "High",
})
}
}
/** Default function load Highlights of Graph From Markup
*
* @param {Graph} graph
* @param {Object} data
*/
function loadHighlighterMarkup(graph, data) {
if(data==null) data = ""
var layers = data.highlightLayers;
var highlights = data.highlights;
//setup layers in graph
if(layers!=undefined && layers.highlightLayer!=undefined){
if(!Array.isArray(layers.highlightLayer)){
//if it is not an array then make it an array
layers.highlightLayer = [layers.highlightLayer];
}
//add layers in graph
layers.highlightLayer.map(function (el) {
graph.addHighlightLayer({
name: el.name,
default: el.default && el.default === "true"? true: false,
text: el.text
})
})
}else{
// if no layers, then add a default layer for graph
graph.getDefaultHighlightLayer()
}
//setup highlights
if(highlights!=undefined && highlights.highlight!=undefined){
if(!Array.isArray(highlights.highlight)){
//if it is not an array then make it an array
highlights.highlight = [highlights.highlight];
}
//add highlights in graph
highlights.highlight.map(function (el) {
graph.addHighlight(el)
})
}
}
function findHighlightItem(graph, type, id) {
var item = undefined;
switch (type) {
case 'activity':
item = graph.getById(id);
break;
case 'role':
item = graph.Roles.find(function (el) {
return el.title === fixSlashes(htmlDecode(id));
})
break;
case 'relation':
item = graph.Connections.find(function (el) {
return el.getId() === id;
})
break;
default:
break;
}
if(item==undefined){
console.warn(id, type + "_-_ITEM_NOT_FOUND")
}
return item;
}
//https://stackoverflow.com/questions/13949059/persisting-the-changes-of-range-objects-after-selection-in-html/13950376#13950376
//http://jsfiddle.net/WeWy7/3/
// var saveSelection, restoreSelection, clearSelection;
if (window.getSelection && document.createRange) {
/** Getting start and end position of a selection from a node with in which selection is being made
* @function saveSelection
*
* @param {object} containerEl - HTML node of the element with in which selection is being made
*
* @returns {object} - Returns object with start and end postion of selection
*/
saveSelection = function(containerEl, curWindow) {
if(curWindow==undefined){ curWindow = window}
console.log('SAVE SELECTION')
var start = '', range = '';
try {
range = curWindow.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(containerEl);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
start = preSelectionRange.toString().length;
} catch (error) {
events.fire("error", {
message: "Something went wrong with saveSelection",
module: "DCR",
fileName: "DCR.js",
functionName: "saveSelection",
details: error
})
}
return {
start: start,
end: start + range.toString().length
}
};
/** Creates a selection based on start, end postions provided with in a text container
* @function restoreSelection
*
* @param {object} containerEl - HTML node of the element with in which selection is being made
* @param {object} savedSel - An object having start, end postions, e-g:{start: null ,end: null}. `HighlightRange` object can also be used.
*
*/
restoreSelection = function(containerEl, savedSel, curWindow, curDocument) {
console.log('RESTORE SELECTION')
if(curWindow==undefined){ curWindow = window}
if(curDocument==undefined){ curDocument = document}
try {
var charIndex = 0, range = curDocument.createRange();
range.setStart(containerEl, 0);
range.collapse(true);
var nodeStack = [containerEl], node, foundStart = false, stop = false;
while (!stop && (node = nodeStack.pop())) {
if (node.nodeType == 3) {
var nextCharIndex = charIndex + node.length;
if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
range.setStart(node, savedSel.start - charIndex);
foundStart = true;
}
if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
range.setEnd(node, savedSel.end - charIndex);
stop = true;
}
charIndex = nextCharIndex;
} else {
var i = node.childNodes.length;
while (i--) {
nodeStack.push(node.childNodes[i]);
}
}
}
var sel = curWindow.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} catch (error) {
events.fire("error", {
message: "Something went wrong while restoring selections",
module: "DCR",
functionName: "restoreSelection",
parameters: JSON.stringify({savedSel: savedSel}),
details: error
})
}
}
/** Clears any selection made in the document.
* @function clearSelection
*/
clearSelection = function(curWindow, curDocument) {
if(curWindow==undefined){ curWindow = window}
if(curDocument==undefined){ curDocument = document}
console.log('CLEAR SELECTION')
try {
if (curWindow.getSelection) {
if (curWindow.getSelection().empty) { // Chrome
curWindow.getSelection().empty();
} else if (curWindow.getSelection().removeAllRanges) { // Firefox
curWindow.getSelection().removeAllRanges();
}
} else if (curDocument.selection) { // IE?
curDocument.selection.empty();
}
} catch (error) {
events.fire("error", {
message: "Something went wrong while clearing selections",
module: "DCR",
functionName: "clearSelection",
details: error
})
}
}
} else if (document.selection && document.body.createTextRange) {
saveSelection = function(containerEl, curWindow, curDocument) {
if(curWindow==undefined){ curWindow = window}
if(curDocument==undefined){ curDocument = document}
var selectedTextRange = curDocument.selection.createRange();
var preSelectionTextRange = curDocument.body.createTextRange();
preSelectionTextRange.moveToElementText(containerEl);
preSelectionTextRange.setEndPoint("EndToStart", selectedTextRange);
var start = preSelectionTextRange.text.length;
return {
start: start,
end: start + selectedTextRange.text.length
}
};
restoreSelection = function(containerEl, savedSel, curWindow, curDocument) {
if(curWindow==undefined){ curWindow = window}
if(curDocument==undefined){ curDocument = document}
var textRange = curDocument.body.createTextRange();
textRange.moveToElementText(containerEl);
textRange.collapse(true);
textRange.moveEnd("character", savedSel.end);
textRange.moveStart("character", savedSel.start);
textRange.select();
};
}
/** Provides the position of Highlight Node in container
* @function getUpdatedPosition
*
* @param {DOMElement} anode - HTML node of the element with in which selection is being made
* @param {DOMElement} textContainer
* @param {Window} curWindow
* @param {Document} curDocument
*/
getUpdatedPosition = function(anode, textContainer, curWindow, curDocument) {
if(curDocument==undefined){ curDocument = document}
try {
if(anode){
var range = new Range();
range.selectNodeContents(anode); // or selectNode(p) to select the <p> tag too
curDocument.getSelection().removeAllRanges(); // clear existing selection if any
curDocument.getSelection().addRange(range);
}else{
console.error('Node not found to get range', anode);
events.fire("error", {message: "Node not found to get range", module: "DCR", functionName: "getUpdatedPosition", parameters: JSON.stringify({anode: anode, textContainer:textContainer})
})
}
} catch (error) {
events.fire("error", {
message: "Something went wrong with getUpdatedPosition",
module: "DCR",
functionName: "getUpdatedPosition",
parameters: JSON.stringify({anode: anode, textContainer:textContainer}),
details: error
})
}
return saveSelection(textContainer, curWindow)
}
function getItemType(el) {
// item type are: selected-text-[role, activity, relation]
// or later introduced for some duration were: marked-[role, relation, activity]
// also data-item-type="[relation, role, activity]" was introduced at somepoint
// it seems some of the graphs still have experimental attributes so need to handle according to that.
/**
* get class list of el, find item type from class list, if still not found try data-item-type
* if still no sucess return undefined
* types: 'activity', 'role', 'relation', 'suggestion', 'alias', 'comment', 'note'
* prefixs = ['selected-text', 'marked']
*/
var types = ['activity', 'role', 'relation', 'alias', 'comment'];
var itemTypeFromAttr = el.getAttribute('data-item-type');
var itemType = el.classList.value.split(" ").map(function(cItem){
var splitted = cItem.split("-")
return splitted[splitted.length-1]
}).filter(function (item) {
// filter through all classes and find which has types in it and use that class to determine type by comparing list with types list
return types.find(function(cItem){
return cItem == item
})
});
itemType = itemType.length>0? itemType[0]: null;
if(itemType==undefined || itemType=="undefined" || typeof itemType == "undefined"){
console.log("itemType", itemType, " FOR ", el)
}
// if still no item type based on class, then try attribute
if(el.getAttribute('data-item-type')!==null && itemType==null){
itemType = types.find(function(cItem){
return cItem == itemTypeFromAttr
})
}
//if all fails try to analyze the id and if id is base on relation pattern then setType as relation, try to split it
var elId = el.getAttribute('data-item-id');
if(elId!=null && (itemType==null || typeof itemType == "undefined") && elId.split("--").length>2){
itemType = "relation"
console.log("settingType", itemType, " FOR ", elId)
}
return typeof itemType == "undefined"? null: itemType;
}
/** Converts Old HTML Element for Highlights to New Highlighter Item
*
* @param {DOMElement} el
* @param {DOMElement} textContainer
*/
function getOldHighlightDetails(el, textContainer){
var positions = getUpdatedPosition(el, textContainer);
if(el.classList.length>0){
var itemType = getItemType(el);
return {
type: itemType!=null && itemType!=""? itemType: 'none',
items: {
item: [{id: el.getAttribute("data-item-id")}]
},
layers:{
layer: [{
name: "description",
ranges: {
range: [{
start: positions.start,
end: positions.end,
text: el.textContent
}]
}
}]
}
}
}
return null
}
/** Main function to upgrade the Highlighter Old markup to new structure
*
* innerText resolves the new lines but messes up the ranges so we do following:
*
* we use textContent, ist we replace any line break still missing br and add br for it and store it in a div from where we shall get the ranges
* oldMarkup.replace(/(?:\r\n|\r|\n)/g, "<br />") //to replace any new lines in to <br /> as current markup is inconsistent and maynot have have <br /> on each line break;
*
* in ist part if we replace the br with \n then we loose the linebreaks in textContent so we ist replace the br with \n and then add a new div with \n text to get the actual text with linebreaks,
* baseText.replace(/<br\s*[\/]?>/g, "\n")
*
* we pass the textContent to layer and find ranges on baseText
* why we use textContent: https://stackoverflow.com/questions/35213147/difference-between-textcontent-vs-innertext
*/
function upgradeHighlighterMarkup(graph, oldMarkup) {
var a = prepareMarkup(oldMarkup);
var b = generateLayerTextFromMarkup(oldMarkup);
$(document.body).append([a,b])
//lets create a new layer ist,
var data = {
highlightLayers : {
highlightLayer: [{name:"description", default: "true", text: b[0].textContent}]
},
highlights: {
highlight: []
}
}
//find the possible items and prepare their data
$(a).find('span').each(function(i, el){
var item = getOldHighlightDetails(el, a[0]);
if(item!=null){
data.highlights.highlight.push(item)
}
console.log("getOldHighlightDetails:", item)
})
//remove the extra div created
a.remove();
b.remove();
return data;
}
function prepareMarkup(oldMarkup) {
var baseText = oldMarkup.replace(/(?:\r\n|\r|\n)/g, "<br>");
//we place the html with br to get the indexes
return $('<div id="tempUpgradeHolder"/>').html(baseText).css({'opacity': 0})
}
function generateLayerTextFromMarkup(oldMarkup) {
var baseText = oldMarkup.replace(/(?:\r\n|\r|\n)/g, "<br>");
//we place the html in another div to get the textContent with linebreaks
return $('<div id="tempUpgradeHolderTextOnly"/>').html(baseText.replace(/<br\s*[\/]?>/g, "\n")).css({'opacity': 0})
}
if(typeof String.prototype.trim !== 'function') {
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
}
}
$.fn.outerHTML = function(){
// IE, Chrome & Safari will comply with the non-standard outerHTML, all others (FF) will have a fall-back for cloning
return (!this.length) ? this : (this[0].outerHTML || (
function(el){
var div = document.createElement('div');
div.appendChild(el.cloneNode(true));
var contents = div.innerHTML;
div = null;
return contents;
})(this[0]));
}
//setting DCR as global to be used
window.DCR = D;
return D;
}
));