You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
24510 lines
720 KiB
24510 lines
720 KiB
/** |
|
* @namespace Oryx name space for different utility methods |
|
* @name ORYX.Utils |
|
*/ |
|
|
|
if(!ORYX) var ORYX = {}; |
|
|
|
ORYX.Utils = { |
|
/** |
|
* General helper method for parsing a param out of current location url |
|
* @example |
|
* // Current url in Browser => "http://oryx.org?param=value" |
|
* ORYX.Utils.getParamFromUrl("param") // => "value" |
|
* @param {Object} name |
|
*/ |
|
getParamFromUrl: function(name){ |
|
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); |
|
var regexS = "[\\?&]" + name + "=([^&#]*)"; |
|
var regex = new RegExp(regexS); |
|
var results = regex.exec(window.location.href); |
|
if (results == null) { |
|
return null; |
|
} |
|
else { |
|
return results[1]; |
|
} |
|
}, |
|
|
|
adjustLightness: function(){ |
|
return arguments[0]; |
|
}, |
|
|
|
adjustGradient: function(gradient, reference){ |
|
|
|
if (ORYX.CONFIG.DISABLE_GRADIENT && gradient){ |
|
|
|
var col = reference.getAttributeNS(null, "stop-color") || "#ffffff"; |
|
|
|
$A(gradient.getElementsByTagName("stop")).each(function(stop){ |
|
if (stop == reference){ return; } |
|
stop.setAttributeNS(null, "stop-color", col); |
|
}); |
|
} |
|
} |
|
} |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
XMLNS = { |
|
ATOM: "http://www.w3.org/2005/Atom", |
|
XHTML: "http://www.w3.org/1999/xhtml", |
|
ERDF: "http://purl.org/NET/erdf/profile", |
|
RDFS: "http://www.w3.org/2000/01/rdf-schema#", |
|
RDF: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", |
|
RAZIEL: "http://b3mn.org/Raziel", |
|
|
|
SCHEMA: "" |
|
}; |
|
|
|
//TODO kann kickstart sich vielleicht auch um die erzeugung von paketen/ |
|
// namespaces k???mmern? z.b. requireNamespace("ORYX.Core.SVG"); |
|
var Kickstart = { |
|
started: false, |
|
callbacks: [], |
|
alreadyLoaded: [], |
|
PATH: '', |
|
|
|
load: function() { Kickstart.kick(); }, |
|
|
|
kick: function() { |
|
//console.profile("loading"); |
|
if(!Kickstart.started) { |
|
Kickstart.started = true; |
|
Kickstart.callbacks.each(function(callback){ |
|
// call the registered callback asynchronously. |
|
window.setTimeout(callback, 1); |
|
}); |
|
} |
|
}, |
|
|
|
register: function(callback) { |
|
//TODO Add some mutual exclusion between kick and register calls. |
|
with(Kickstart) { |
|
if(started) window.setTimeout(callback, 1); |
|
else Kickstart.callbacks.push(callback) |
|
} |
|
}, |
|
|
|
/** |
|
* Loads a js, assuring that it has only been downloaded once. |
|
* @param {String} url the script to load. |
|
*/ |
|
require: function(url) { |
|
// if not already loaded, include it. |
|
if(Kickstart.alreadyLoaded.member(url)) |
|
return false; |
|
return Kickstart.include(url); |
|
}, |
|
|
|
/** |
|
* Loads a js, regardless of whether it has only been already downloaded. |
|
* @param {String} url the script to load. |
|
*/ |
|
include: function(url) { |
|
|
|
// prepare a script tag and place it in html head. |
|
var head = document.getElementsByTagNameNS(XMLNS.XHTML, 'head')[0]; |
|
var s = document.createElementNS(XMLNS.XHTML, "script"); |
|
s.setAttributeNS(XMLNS.XHTML, 'type', 'text/javascript'); |
|
s.src = Kickstart.PATH + url; |
|
|
|
//TODO macht es sinn, dass neue skript als letztes kind in den head |
|
// einzubinden (stichwort reihenfolge der skript tags)? |
|
head.appendChild(s); |
|
|
|
// remember this url. |
|
Kickstart.alreadyLoaded.push(url); |
|
|
|
return true; |
|
} |
|
} |
|
|
|
// register kickstart as the new onload event listener on current window. |
|
// previous listener(s) are triggered to launch with kickstart. |
|
Event.observe(window, 'load', Kickstart.load);/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
var ERDF = { |
|
|
|
LITERAL: 0x01, |
|
RESOURCE: 0x02, |
|
DELIMITERS: ['.', '-'], |
|
HASH: '#', |
|
HYPHEN: "-", |
|
|
|
schemas: [], |
|
callback: undefined, |
|
log: undefined, |
|
|
|
init: function(callback) { |
|
|
|
// init logging. |
|
//ERDF.log = Log4js.getLogger("oryx"); |
|
//ERDF.log.setLevel(Log4js.Level.ALL); |
|
//ERDF.log.addAppender(new ConsoleAppender(ERDF.log, false)); |
|
|
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace("ERDF Parser is initialized."); |
|
|
|
// register callbacks and default schemas. |
|
ERDF.callback = callback; |
|
ERDF.registerSchema('schema', XMLNS.SCHEMA); |
|
ERDF.registerSchema('rdfs', XMLNS.RDFS); |
|
}, |
|
|
|
run: function() { |
|
|
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace("ERDF Parser is running."); |
|
|
|
// do the work. |
|
return ERDF._checkProfile() && ERDF.parse(); |
|
}, |
|
|
|
parse: function() { |
|
|
|
//(ERDF.log.isDebugEnabled()) |
|
// ERDF.log.debug("Begin parsing document metadata."); |
|
|
|
// time measuring |
|
ERDF.__startTime = new Date(); |
|
|
|
var bodies = document.getElementsByTagNameNS(XMLNS.XHTML, 'body'); |
|
var subject = {type: ERDF.RESOURCE, value: ''}; |
|
|
|
var result = ERDF._parseDocumentMetadata() && |
|
ERDF._parseFromTag(bodies[0], subject); |
|
|
|
// time measuring |
|
ERDF.__stopTime = new Date(); |
|
|
|
var duration = (ERDF.__stopTime - ERDF.__startTime)/1000.; |
|
//alert('ERDF parsing took ' + duration + ' s.'); |
|
|
|
return result; |
|
}, |
|
|
|
_parseDocumentMetadata: function() { |
|
|
|
// get links from head element. |
|
var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head'); |
|
var links = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'link'); |
|
var metas = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'meta'); |
|
|
|
// process links first, since they could contain schema definitions. |
|
$A(links).each(function(link) { |
|
var properties = link.getAttribute('rel'); |
|
var reversedProperties = link.getAttribute('rev'); |
|
var value = link.getAttribute('href'); |
|
|
|
ERDF._parseTriplesFrom( |
|
ERDF.RESOURCE, '', |
|
properties, |
|
ERDF.RESOURCE, value); |
|
|
|
ERDF._parseTriplesFrom( |
|
ERDF.RESOURCE, value, |
|
reversedProperties, |
|
ERDF.RESOURCE, ''); |
|
}); |
|
|
|
// continue with metas. |
|
$A(metas).each(function(meta) { |
|
var property = meta.getAttribute('name'); |
|
var value = meta.getAttribute('content'); |
|
|
|
ERDF._parseTriplesFrom( |
|
ERDF.RESOURCE, '', |
|
property, |
|
ERDF.LITERAL, value); |
|
}); |
|
|
|
return true; |
|
}, |
|
|
|
_parseFromTag: function(node, subject, depth) { |
|
|
|
// avoid parsing non-xhtml content. |
|
if(!node || !node.namespaceURI || node.namespaceURI != XMLNS.XHTML) { return; } |
|
|
|
// housekeeping. |
|
if(!depth) depth=0; |
|
var id = node.getAttribute('id'); |
|
|
|
// some logging. |
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace(">".times(depth) + " Parsing " + node.nodeName + " ("+node.nodeType+") for data on " + |
|
// ((subject.type == ERDF.RESOURCE) ? ('<' + subject.value + '>') : '') + |
|
// ((subject.type == ERDF.LITERAL) ? '"' + subject.value + '"' : '')); |
|
|
|
/* triple finding! */ |
|
|
|
// in a-tags... |
|
if(node.nodeName.endsWith(':a') || node.nodeName == 'a') { |
|
var properties = node.getAttribute('rel'); |
|
var reversedProperties = node.getAttribute('rev'); |
|
var value = node.getAttribute('href'); |
|
var title = node.getAttribute('title'); |
|
var content = node.textContent; |
|
|
|
// rel triples |
|
ERDF._parseTriplesFrom( |
|
subject.type, subject.value, |
|
properties, |
|
ERDF.RESOURCE, value, |
|
function(triple) { |
|
var label = title? title : content; |
|
|
|
// label triples |
|
ERDF._parseTriplesFrom( |
|
triple.object.type, triple.object.value, |
|
'rdfs.label', |
|
ERDF.LITERAL, label); |
|
}); |
|
|
|
// rev triples |
|
ERDF._parseTriplesFrom( |
|
subject.type, subject.value, |
|
reversedProperties, |
|
ERDF.RESOURCE, ''); |
|
|
|
// type triples |
|
ERDF._parseTypeTriplesFrom( |
|
subject.type, subject.value, |
|
properties); |
|
|
|
// in img-tags... |
|
} else if(node.nodeName.endsWith(':img') || node.nodeName == 'img') { |
|
var properties = node.getAttribute('class'); |
|
var value = node.getAttribute('src'); |
|
var alt = node.getAttribute('alt'); |
|
|
|
ERDF._parseTriplesFrom( |
|
subject.type, subject.value, |
|
properties, |
|
ERDF.RESOURCE, value, |
|
function(triple) { |
|
var label = alt; |
|
|
|
// label triples |
|
ERDF._parseTriplesFrom( |
|
triple.object.type, triple.object.value, |
|
'rdfs.label', |
|
ERDF.LITERAL, label); |
|
}); |
|
|
|
} |
|
|
|
// in every tag |
|
var properties = node.getAttribute('class'); |
|
var title = node.getAttribute('title'); |
|
var content = node.textContent; |
|
var label = title ? title : content; |
|
|
|
// regular triples |
|
ERDF._parseTriplesFrom( |
|
subject.type, subject.value, |
|
properties, |
|
ERDF.LITERAL, label); |
|
|
|
if(id) subject = {type: ERDF.RESOURCE, value: ERDF.HASH+id}; |
|
|
|
// type triples |
|
ERDF._parseTypeTriplesFrom( |
|
subject.type, subject.value, |
|
properties); |
|
|
|
// parse all children that are element nodes. |
|
var children = node.childNodes; |
|
if(children) $A(children).each(function(_node) { |
|
if(_node.nodeType == _node.ELEMENT_NODE) |
|
ERDF._parseFromTag(_node, subject, depth+1); }); |
|
}, |
|
|
|
_parseTriplesFrom: function(subjectType, subject, properties, |
|
objectType, object, callback) { |
|
|
|
if(!properties) return; |
|
properties.toLowerCase().split(' ').each( function(property) { |
|
|
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace("Going for property " + property); |
|
|
|
var schema = ERDF.schemas.find( function(schema) { |
|
return false || ERDF.DELIMITERS.find( function(delimiter) { |
|
return property.startsWith(schema.prefix + delimiter); |
|
}); |
|
}); |
|
|
|
if(schema && object) { |
|
property = property.substring( |
|
schema.prefix.length+1, property.length); |
|
var triple = ERDF.registerTriple( |
|
new ERDF.Resource(subject), |
|
{prefix: schema.prefix, name: property}, |
|
(objectType == ERDF.RESOURCE) ? |
|
new ERDF.Resource(object) : |
|
new ERDF.Literal(object)); |
|
|
|
if(callback) callback(triple); |
|
} |
|
}); |
|
}, |
|
|
|
_parseTypeTriplesFrom: function(subjectType, subject, properties, callback) { |
|
|
|
if(!properties) return; |
|
properties.toLowerCase().split(' ').each( function(property) { |
|
|
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace("Going for property " + property); |
|
|
|
var schema = ERDF.schemas.find( function(schema) { |
|
return false || ERDF.DELIMITERS.find( function(delimiter) { |
|
return property.startsWith(ERDF.HYPHEN + schema.prefix + delimiter); |
|
}); |
|
}); |
|
|
|
if(schema && subject) { |
|
property = property.substring(schema.prefix.length+2, property.length); |
|
var triple = ERDF.registerTriple( |
|
(subjectType == ERDF.RESOURCE) ? |
|
new ERDF.Resource(subject) : |
|
new ERDF.Literal(subject), |
|
{prefix: 'rdf', name: 'type'}, |
|
new ERDF.Resource(schema.namespace+property)); |
|
if(callback) callback(triple); |
|
} |
|
}); |
|
}, |
|
|
|
/** |
|
* Checks for ERDF profile declaration in head of document. |
|
*/ |
|
_checkProfile: function() { |
|
|
|
// get profiles from head element. |
|
var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head'); |
|
var profiles = heads[0].getAttribute("profile"); |
|
var found = false; |
|
|
|
// if erdf profile is contained. |
|
if(profiles && profiles.split(" ").member(XMLNS.ERDF)) { |
|
|
|
// pass check. |
|
//if(ERDF.log.isTraceEnabled()) |
|
// ERDF.log.trace("Found ERDF profile " + XMLNS.ERDF); |
|
return true; |
|
|
|
} else { |
|
|
|
// otherwise fail check. |
|
//if(ERDF.log.isFatalEnabled()) |
|
// ERDF.log.fatal("No ERDF profile found."); |
|
return false; |
|
} |
|
}, |
|
|
|
__stripHashes: function(s) { |
|
return (s && (typeof s.substring == 'function') && s.substring(0, 1)=='#') ? s.substring(1, s.length) : s; |
|
}, |
|
|
|
registerSchema: function(prefix, namespace) { |
|
|
|
// TODO check whether already registered, if so, complain. |
|
ERDF.schemas.push({ |
|
prefix: prefix, |
|
namespace: namespace |
|
}); |
|
|
|
//if(ERDF.log.isDebugEnabled()) |
|
// ERDF.log.debug("Prefix '"+prefix+"' for '"+namespace+"' registered."); |
|
}, |
|
|
|
registerTriple: function(subject, predicate, object) { |
|
|
|
// if prefix is schema, this is a schema definition. |
|
if(predicate.prefix.toLowerCase() == 'schema') |
|
this.registerSchema(predicate.name, object.value); |
|
|
|
var triple = new ERDF.Triple(subject, predicate, object); |
|
ERDF.callback(triple); |
|
|
|
//if(ERDF.log.isInfoEnabled()) |
|
// ERDF.log.info(triple) |
|
|
|
// return the registered triple. |
|
return triple; |
|
}, |
|
|
|
__enhanceObject: function() { |
|
|
|
/* Resource state querying methods */ |
|
this.isResource = function() { |
|
return this.type == ERDF.RESOURCE }; |
|
this.isLocal = function() { |
|
return this.isResource() && this.value.startsWith('#') }; |
|
this.isCurrentDocument = function() { |
|
return this.isResource() && (this.value == '') }; |
|
|
|
/* Resource getter methods.*/ |
|
this.getId = function() { |
|
return this.isLocal() ? ERDF.__stripHashes(this.value) : false; }; |
|
|
|
/* Liiteral state querying methods */ |
|
this.isLiteral = function() { |
|
return this.type == ERDF.LIITERAL }; |
|
}, |
|
|
|
serialize: function(literal) { |
|
|
|
if(!literal){ |
|
return ""; |
|
}else if(literal.constructor == String) { |
|
return literal; |
|
} else if(literal.constructor == Boolean) { |
|
return literal? 'true':'false'; |
|
} else { |
|
return literal.toString(); |
|
} |
|
} |
|
}; |
|
|
|
|
|
ERDF.Triple = function(subject, predicate, object) { |
|
|
|
this.subject = subject; |
|
this.predicate = predicate; |
|
this.object = object; |
|
|
|
this.toString = function() { |
|
|
|
return "[ERDF.Triple] " + |
|
this.subject.toString() + ' ' + |
|
this.predicate.prefix + ':' + this.predicate.name + ' ' + |
|
this.object.toString(); |
|
}; |
|
}; |
|
|
|
ERDF.Resource = function(uri) { |
|
|
|
this.type = ERDF.RESOURCE; |
|
this.value = uri; |
|
ERDF.__enhanceObject.apply(this); |
|
|
|
this.toString = function() { |
|
return '<' + this.value + '>'; |
|
} |
|
|
|
}; |
|
|
|
ERDF.Literal = function(literal) { |
|
|
|
this.type = ERDF.LITERAL; |
|
this.value = ERDF.serialize(literal); |
|
ERDF.__enhanceObject.apply(this); |
|
|
|
this.toString = function() { |
|
return '"' + this.value + '"'; |
|
} |
|
};/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/* |
|
* Save and triple generation behaviour. Use this area to configure |
|
* data management to your needs. |
|
*/ |
|
var USE_ASYNCHRONOUS_REQUESTS = true; |
|
var DISCARD_UNUSED_TRIPLES = true; |
|
var PREFER_SPANS_OVER_DIVS = true; |
|
var PREFER_TITLE_OVER_TEXTNODE = false; |
|
var RESOURCE_ID_PREFIX = 'resource'; |
|
|
|
var SHOW_DEBUG_ALERTS_WHEN_SAVING = false; |
|
var SHOW_EXTENDED_DEBUG_INFORMATION = false; |
|
|
|
/* |
|
* Back end specific workarounds. |
|
*/ |
|
|
|
var USE_ARESS_WORKAROUNDS = true; |
|
|
|
/* |
|
* Data management constants. Do not change these, as they are used |
|
* both internally and externally to communicate on events and to identify |
|
* command object actions in triple production and embedding rules. |
|
*/ |
|
|
|
// Resource constants |
|
var RESOURCE_CREATED = 0x01; |
|
var RESOURCE_REMOVED = 0x02; |
|
var RESOURCE_SAVED = 0x04; |
|
var RESOURCE_RELOADED = 0x08; |
|
var RESOURCE_SYNCHRONIZED = 0x10; |
|
|
|
// Triple constants |
|
var TRIPLE_REMOVE = 0x01; |
|
var TRIPLE_ADD = 0x02; |
|
var TRIPLE_RELOAD = 0x04; |
|
var TRIPLE_SAVE = 0x08; |
|
|
|
var PROCESSDATA_REF = 'processdata'; |
|
|
|
// HTTP status code constants |
|
// |
|
//// 2xx |
|
//const 200_OK = 'Ok'; |
|
//const 201_CREATED = 'Created'; |
|
//const 202_ACCEPTED = 'Accepted'; |
|
//const 204_NO_CONTENT = 'No Content'; |
|
// |
|
//// 3xx |
|
//const 301_MOVED_PERMANENTLY = 'Moved Permanently'; |
|
//const 302_MOVED_TEMPORARILY = 'Moved Temporarily'; |
|
//const 304_NOT_MODIFIED = 'Not Modified'; |
|
// |
|
//// 4xx |
|
//const 400_BAD_REQUEST = 'Bad Request'; |
|
//const 401_UNAUTHORIZED = 'Unauthorized'; |
|
//const 403_FORBIDDEN = 'Forbidden'; |
|
//const 404_NOT_FOUND = 'Not Found'; |
|
//const 409_CONFLICT = 'Conflict'; |
|
// |
|
//// 5xx |
|
//const 500_INTERNAL_SERVER_ERROR = 'Internal Server Error'; |
|
//const 501_NOT_IMPLEMENTED = 'Not Implemented'; |
|
//const 502_BAD_GATEWAY = 'Bad Gateway'; |
|
//const 503_SERVICE_UNAVAILABLE = 'Service Unavailable'; |
|
// |
|
/** |
|
* The Data Management object. Use this one when interacting with page internal |
|
* data. Initialize data management by DataManager.init(); |
|
* @class DataManager |
|
*/ |
|
var DataManager = { |
|
|
|
/** |
|
* The init method should be called once in the DataManagers lifetime. |
|
* It causes the DataManager to initialize itself, the erdf parser, do all |
|
* neccessary registrations and configurations, to run the parser and |
|
* from then on deliver all resulting triples. |
|
* No parameters needed are needed in a call to this method. |
|
*/ |
|
init: function() { |
|
ERDF.init(DataManager._registerTriple); |
|
DataManager.__synclocal(); |
|
}, |
|
|
|
/** |
|
* This triple array is meant to be the whole knowledge of the DataManager. |
|
*/ |
|
_triples: [], |
|
|
|
/** |
|
* This method is meant for callback from erdf parsing. It is not to be |
|
* used in another way than to add triples to the triple store. |
|
* @param {Object} triple the triple to add to the triple store. |
|
*/ |
|
_registerTriple: function(triple) { |
|
DataManager._triples.push(triple) |
|
}, |
|
|
|
/** |
|
* The __synclocal method is for internal usage only. |
|
* It performs synchronization with the local document, that is, the triple |
|
* store is adjustet to the content of the document, which could have been |
|
* changed by any other applications running on the same page. |
|
*/ |
|
__synclocal: function() { |
|
DataManager._triples = []; |
|
ERDF.run(); |
|
}, |
|
|
|
/** |
|
* Makes the shape passed into this method synchronize itself with the DOM. |
|
* This method returns the shapes resource object for further manipulation. |
|
* @param {Object} shape |
|
*/ |
|
__synchronizeShape: function(shape) { |
|
|
|
var r = ResourceManager.getResource(shape.resourceId); |
|
var serialize = shape.serialize(); |
|
|
|
// store all serialize values |
|
serialize.each( function(ser) { |
|
|
|
var resource = (ser.type == 'resource'); |
|
var _triple = new ERDF.Triple( |
|
new ERDF.Resource(shape.resourceId), |
|
{prefix: ser.prefix, name: ser.name}, |
|
resource ? |
|
new ERDF.Resource(ser.value) : |
|
new ERDF.Literal(ser.value) |
|
); |
|
DataManager.setObject(_triple); |
|
}); |
|
|
|
return r; |
|
}, |
|
|
|
__storeShape: function(shape) { |
|
|
|
// first synchronize the shape, |
|
var resource = DataManager.__synchronizeShape(shape); |
|
|
|
// then save the synchronized dom. |
|
resource.save(); |
|
}, |
|
|
|
__forceExistance: function(shape) { |
|
|
|
if(!$(shape.resourceId)) { |
|
|
|
if(!$$('.' + PROCESSDATA_REF)[0]) |
|
DataManager.graft(XMLNS.XHTML, |
|
document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), ['div', {'class': PROCESSDATA_REF, 'style':'display:none;'}]); |
|
|
|
// object is literal |
|
DataManager.graft(XMLNS.XHTML, |
|
$$('.' + PROCESSDATA_REF)[0], [ |
|
|
|
'div', { |
|
'id': shape.resourceId, |
|
//This should be done in a more dynamic way!!!!! |
|
'class': (shape instanceof ORYX.Core.Canvas) ? "-oryx-canvas" : undefined |
|
} |
|
]); |
|
|
|
} else { |
|
var resource = $(shape.resourceId) |
|
var children = $A(resource.childNodes) |
|
children.each( function(child) { |
|
resource.removeChild(child); |
|
}); |
|
}; |
|
}, |
|
|
|
__persistShape: function(shape) { |
|
|
|
// a shape serialization. |
|
var shapeData = shape.serialize(); |
|
|
|
// initialize a triple array and construct a shape resource |
|
// to be used in triple generation. |
|
var triplesArray = []; |
|
var shapeResource = new ERDF.Resource(shape.resourceId); |
|
|
|
// remove all triples for this particular shape's resource |
|
DataManager.removeTriples( DataManager.query( |
|
shapeResource, undefined, undefined)); |
|
|
|
// for each data set in the shape's serialization |
|
shapeData.each( function(data) { |
|
|
|
// construct a triple's value |
|
var value = (data.type == 'resource') ? |
|
new ERDF.Resource(data.value) : |
|
new ERDF.Literal(data.value); |
|
|
|
// construct triple and add it to the DOM. |
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: data.prefix, name: data.name}, |
|
value |
|
)); |
|
}); |
|
}, |
|
|
|
__persistDOM: function(facade) { |
|
|
|
// getChildShapes gets all shapes (nodes AND edges), deep flag |
|
// makes it return a flattened child hierarchy. |
|
|
|
var canvas = facade.getCanvas(); |
|
var shapes = canvas.getChildShapes(true); |
|
var result = ''; |
|
|
|
// persist all shapes. |
|
shapes.each( function(shape) { |
|
DataManager.__forceExistance(shape); |
|
}); |
|
//DataManager.__synclocal(); |
|
|
|
DataManager.__renderCanvas(facade); |
|
result += DataManager.serialize( |
|
$(ERDF.__stripHashes(facade.getCanvas().resourceId)), true); |
|
|
|
shapes.each( function(shape) { |
|
|
|
DataManager.__persistShape(shape); |
|
result += DataManager.serialize( |
|
$(ERDF.__stripHashes(shape.resourceId)), true); |
|
}); |
|
|
|
//result += DataManager.__renderCanvas(facade); |
|
|
|
return result; |
|
}, |
|
|
|
__renderCanvas: function(facade) { |
|
|
|
var canvas = facade.getCanvas(); |
|
var stencilSets = facade.getStencilSets(); |
|
var shapes = canvas.getChildShapes(true); |
|
|
|
DataManager.__forceExistance(canvas); |
|
|
|
DataManager.__persistShape(canvas); |
|
|
|
var shapeResource = new ERDF.Resource(canvas.resourceId); |
|
|
|
// remove all triples for this particular shape's resource |
|
DataManager.removeTriples( DataManager.query( |
|
shapeResource, undefined, undefined)); |
|
|
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "mode"}, |
|
new ERDF.Literal("writable") |
|
)); |
|
|
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "mode"}, |
|
new ERDF.Literal("fullscreen") |
|
)); |
|
|
|
stencilSets.values().each(function(stencilset) { |
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "stencilset"}, |
|
new ERDF.Resource(stencilset.source().replace(/&/g, "%26")) |
|
)); |
|
|
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "ssnamespace"}, |
|
new ERDF.Resource(stencilset.namespace()) |
|
)); |
|
|
|
stencilset.extensions().keys().each(function(extension) { |
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "ssextension"}, |
|
new ERDF.Literal(extension) |
|
)); |
|
}); |
|
}); |
|
|
|
shapes.each(function(shape) { |
|
DataManager.addTriple( new ERDF.Triple( |
|
shapeResource, |
|
{prefix: "oryx", name: "render"}, |
|
new ERDF.Resource("#" + shape.resourceId) |
|
)); |
|
}); |
|
}, |
|
|
|
__counter: 0, |
|
__provideId: function() { |
|
|
|
while($(RESOURCE_ID_PREFIX+DataManager.__counter)) |
|
DataManager.__counter++; |
|
|
|
return RESOURCE_ID_PREFIX+DataManager.__counter; |
|
}, |
|
|
|
serializeDOM: function(facade) { |
|
|
|
return DataManager.__persistDOM(facade); |
|
}, |
|
|
|
syncGlobal: function(facade) { |
|
|
|
return DataManager.__syncglobal(facade); |
|
}, |
|
|
|
/** |
|
* This method is used to synchronize local DOM with remote resources. |
|
* Local changes are commited to the server, and remote changes are |
|
* performed to the local document. |
|
* @param {Object} facade The facade of the editor that holds certain |
|
* resource representations as shapes. |
|
*/ |
|
__syncglobal: function(facade) { |
|
|
|
// getChildShapes gets all shapes (nodes AND edges), deep flag |
|
// makes it return a flattened child hierarchy. |
|
|
|
var canvas = facade.getCanvas(); |
|
var shapes = canvas.getChildShapes(true); |
|
|
|
// create dummy resource representations in the dom |
|
// for all shapes that were newly created. |
|
|
|
shapes.select( function(shape) { |
|
|
|
// select shapes without resource id. |
|
|
|
return !($(shape.resourceId)); |
|
|
|
}).each( function(shape) { |
|
|
|
// create new resources for them. |
|
if(USE_ARESS_WORKAROUNDS) { |
|
|
|
/* |
|
* This is a workaround due to a bug in aress. Resources are |
|
* ignoring changes to raziel:type property once they are |
|
* created. As long as this is not fixed, the resource is now |
|
* being created using a randomly guessed id, this temporary id |
|
* is then used in references and the appropriate div is being |
|
* populated with properties. |
|
* |
|
* AFTER THIS PHASE THE DATA IS INCONSISTENT AS REFERENCES POINT |
|
* TO IDS THAT ARE UNKNOWN TO THE BACK END. |
|
* |
|
* After the resource is actually created in aress, it gets an id |
|
* that is persistent. All shapes are then being populated with the |
|
* correct id references and stored on the server. |
|
* |
|
* AFTER THE SAVE PROCESS HAS RETURNED, THE DATA IS CONSISTENT |
|
* REGARDING THE ID REFERENCES AGAIN. |
|
*/ |
|
|
|
var razielType = shape.properties['raziel-type']; |
|
|
|
var div = '<div xmlns="http://www.w3.org/1999/xhtml">' + |
|
'<span class="raziel-type">'+razielType+'</span></div>'; |
|
|
|
var r = ResourceManager.__createResource(div); |
|
shape.resourceId = r.id(); |
|
|
|
} else { |
|
|
|
var r = ResourceManager.__createResource(); |
|
shape.resourceId = r.id(); |
|
} |
|
|
|
}); |
|
|
|
shapes.each( function(shape) { |
|
|
|
// store all shapes. |
|
DataManager.__storeShape(shape); |
|
}); |
|
}, |
|
|
|
/** |
|
* This method serializes a single div into a string that satisfies the |
|
* client/server communication protocol. It ingnores all elements that have |
|
* an attribute named class that includes 'transient'. |
|
* @param {Object} node the element to serialize. |
|
* @param {Object} preserveNamespace whether to preserve the parent's |
|
* namespace. If you are not sure about namespaces, provide |
|
* just the element to be serialized. |
|
*/ |
|
serialize: function(node, preserveNamespace) { |
|
|
|
if (node.nodeType == node.ELEMENT_NODE) { |
|
// serialize an element node. |
|
|
|
var children = $A(node.childNodes); |
|
var attributes = $A(node.attributes); |
|
var clazz = new String(node.getAttribute('class')); |
|
var ignore = clazz.split(' ').member('transient'); |
|
|
|
// ignore transients. |
|
|
|
if(ignore) |
|
return ''; |
|
|
|
// start serialization. |
|
|
|
var result = '<' + node.nodeName; |
|
|
|
// preserve namespace? |
|
if(!preserveNamespace) |
|
result += ' xmlns="' + (node.namespaceURI ? node.namespaceURI : XMLNS.XHTML) + '" xmlns:oryx="http://oryx-editor.org"'; |
|
|
|
// add all attributes. |
|
|
|
attributes.each(function(attribute) { |
|
result += ' ' + attribute.nodeName + '="' + |
|
attribute.nodeValue + '"';}); |
|
|
|
// close if no children. |
|
|
|
if(children.length == 0) |
|
result += '/>'; |
|
|
|
else { |
|
|
|
// serialize all children. |
|
|
|
result += '>'; |
|
children.each(function(_node) { |
|
result += DataManager.serialize(_node, true)}); |
|
result += '</' + node.nodeName + '>' |
|
} |
|
|
|
return result; |
|
|
|
} else if (node.nodeType == node.TEXT_NODE) { |
|
|
|
// serialize a text node. |
|
return node.nodeValue; |
|
} |
|
|
|
//TODO serialize cdata areas also. |
|
//TODO work on namespace awareness. |
|
}, |
|
|
|
addTriple: function(triple) { |
|
|
|
// assert the subject is a resource |
|
|
|
if(!triple.subject.type == ERDF.LITERAL) |
|
throw 'Cannot add the triple ' + triple.toString() + |
|
' because the subject is not a resource.' |
|
|
|
// get the element which represents this triple's subject. |
|
var elementId = ERDF.__stripHashes(triple.subject.value); |
|
var element = $(elementId); |
|
|
|
// assert the subject is inside this document. |
|
if(!element) |
|
throw 'Cannot add the triple ' + triple.toString() + |
|
' because the subject "'+elementId+'" is not in the document.'; |
|
|
|
if(triple.object.type == ERDF.LITERAL) |
|
|
|
// object is literal |
|
DataManager.graft(XMLNS.XHTML, element, [ |
|
'span', {'class': (triple.predicate.prefix + "-" + |
|
triple.predicate.name)}, triple.object.value.escapeHTML() |
|
]); |
|
|
|
else { |
|
|
|
// object is resource |
|
DataManager.graft(XMLNS.XHTML, element, [ |
|
'a', {'rel': (triple.predicate.prefix + "-" + |
|
triple.predicate.name), 'href': triple.object.value} |
|
]); |
|
|
|
} |
|
|
|
return true; |
|
}, |
|
|
|
removeTriples: function(triples) { |
|
|
|
// alert('Removing ' +triples.length+' triples.'); |
|
|
|
// from all the triples select those ... |
|
var removed = triples.select( |
|
|
|
function(triple) { |
|
|
|
// TODO remove also from triple store. |
|
// ... that were actually removed. |
|
return DataManager.__removeTriple(triple); |
|
}); |
|
|
|
// sync and return removed triples. |
|
// DataManager.__synclocal(); |
|
return removed; |
|
}, |
|
|
|
removeTriple: function(triple) { |
|
|
|
// remember whether the triple was actually removed. |
|
var result = DataManager.__removeTriple(triple); |
|
|
|
// sync and return removed triples. |
|
// DataManager.__synclocal(); |
|
return result; |
|
}, |
|
|
|
__removeTriple: function(triple) { |
|
|
|
// assert the subject is a resource |
|
if(!triple.subject.type == ERDF.LITERAL) |
|
|
|
throw 'Cannot remove the triple ' + triple.toString() + |
|
' because the subject is not a resource.'; |
|
|
|
// get the element which represents this triple's subject. |
|
var elementId = ERDF.__stripHashes(triple.subject.value); |
|
var element = $(elementId); |
|
|
|
// assert the subject is inside this document. |
|
if(!element) |
|
|
|
throw 'Cannot remove the triple ' + triple.toString() + |
|
' because the subject is not in the document.'; |
|
|
|
if(triple.object.type == ERDF.LITERAL) { |
|
|
|
// continue searching actively for the triple. |
|
var result = DataManager.__removeTripleRecursively(triple, element); |
|
return result; |
|
} |
|
}, |
|
|
|
__removeTripleRecursively: function(triple, continueFrom) { |
|
|
|
// return when this node is not an element node. |
|
if(continueFrom.nodeType != continueFrom.ELEMENT_NODE) |
|
return false; |
|
|
|
var classes = new String(continueFrom.getAttribute('class')); |
|
var children = $A(continueFrom.childNodes); |
|
|
|
if(classes.include(triple.predicate.prefix + '-' + triple.predicate.name)) { |
|
|
|
var content = continueFrom.textContent; |
|
if( (triple.object.type == ERDF.LITERAL) && |
|
(triple.object.value == content)) |
|
|
|
continueFrom.parentNode.removeChild(continueFrom); |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
children.each(function(_node) { |
|
DataManager.__removeTripleRecursively(triple, _node)}); |
|
return false; |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* graft() function |
|
* Originally by Sean M. Burke from interglacial.com, altered for usage with |
|
* SVG and namespace (xmlns) support. Be sure you understand xmlns before |
|
* using this funtion, as it creates all grafted elements in the xmlns |
|
* provided by you and all element's attribures in default xmlns. If you |
|
* need to graft elements in a certain xmlns and wish to assign attributes |
|
* in both that and another xmlns, you will need to do stepwise grafting, |
|
* adding non-default attributes yourself or you'll have to enhance this |
|
* function. Latter, I would appreciate: martin???apfelfabrik.de |
|
* @param {Object} namespace The namespace in which |
|
* elements should be grafted. |
|
* @param {Object} parent The element that should contain the grafted |
|
* structure after the function returned. |
|
* @param {Object} t the crafting structure. |
|
* @param {Object} doc the document in which grafting is performed. |
|
*/ |
|
graft: function(namespace, parent, t, doc) { |
|
|
|
doc = (doc || (parent && parent.ownerDocument) || document); |
|
var e; |
|
if(t === undefined) { |
|
echo( "Can't graft an undefined value"); |
|
} else if(t.constructor == String) { |
|
e = doc.createTextNode( t ); |
|
} else { |
|
for(var i = 0; i < t.length; i++) { |
|
if( i === 0 && t[i].constructor == String ) { |
|
var snared = t[i].match( /^([a-z][a-z0-9]*)\.([^\s\.]+)$/i ); |
|
if( snared ) { |
|
e = doc.createElementNS(namespace, snared[1]); |
|
e.setAttributeNS(null, 'class', snared[2] ); |
|
continue; |
|
} |
|
snared = t[i].match( /^([a-z][a-z0-9]*)$/i ); |
|
if( snared ) { |
|
e = doc.createElementNS(namespace, snared[1]); // but no class |
|
continue; |
|
} |
|
|
|
// Otherwise: |
|
e = doc.createElementNS(namespace, "span"); |
|
e.setAttribute(null, "class", "namelessFromLOL" ); |
|
} |
|
|
|
if( t[i] === undefined ) { |
|
echo("Can't graft an undefined value in a list!"); |
|
} else if( t[i].constructor == String || t[i].constructor == Array) { |
|
this.graft(namespace, e, t[i], doc ); |
|
} else if( t[i].constructor == Number ) { |
|
this.graft(namespace, e, t[i].toString(), doc ); |
|
} else if( t[i].constructor == Object ) { |
|
// hash's properties => element's attributes |
|
for(var k in t[i]) { e.setAttributeNS(null, k, t[i][k] ); } |
|
} else if( t[i].constructor == Boolean ) { |
|
this.graft(namespace, e, t[i] ? 'true' : 'false', doc ); |
|
} else |
|
throw "Object " + t[i] + " is inscrutable as an graft arglet."; |
|
} |
|
} |
|
|
|
if(parent) parent.appendChild(e); |
|
|
|
return Element.extend(e); // return the topmost created node |
|
}, |
|
|
|
setObject: function(triple) { |
|
|
|
/** |
|
* Erwartungen von Arvid an diese Funktion: |
|
* - Es existiert genau ein triple mit dem Subjekt und Praedikat, |
|
* das uebergeben wurde, und dieses haelt uebergebenes Objekt. |
|
*/ |
|
|
|
var triples = DataManager.query( |
|
triple.subject, |
|
triple.predicate, |
|
undefined |
|
); |
|
|
|
DataManager.removeTriples(triples); |
|
|
|
DataManager.addTriple(triple); |
|
|
|
return true; |
|
}, |
|
|
|
query: function(subject, predicate, object) { |
|
|
|
/* |
|
* Typical triple. |
|
* {value: subject, type: subjectType}, |
|
* {prefix: schema.prefix, name: property}, |
|
* {value: object, type: objectType}); |
|
*/ |
|
|
|
return DataManager._triples.select(function(triple) { |
|
|
|
var select = ((subject) ? |
|
(triple.subject.type == subject.type) && |
|
(triple.subject.value == subject.value) : true); |
|
if(predicate) { |
|
select = select && ((predicate.prefix) ? |
|
(triple.predicate.prefix == predicate.prefix) : true); |
|
select = select && ((predicate.name) ? |
|
(triple.predicate.name == predicate.name) : true); |
|
} |
|
select = select && ((object) ? |
|
(triple.object.type == object.type) && |
|
(triple.object.value == object.value) : true); |
|
return select; |
|
}); |
|
} |
|
} |
|
|
|
Kickstart.register(DataManager.init); |
|
|
|
function assert(expr, m) { if(!expr) throw m; }; |
|
|
|
function DMCommand(action, triple) { |
|
|
|
// store action and triple. |
|
this.action = action; |
|
this.triple = triple; |
|
|
|
this.toString = function() { |
|
return 'Command('+action+', '+triple+')'; |
|
}; |
|
} |
|
|
|
function DMCommandHandler(nextHandler) { |
|
|
|
/** |
|
* Private method to set the next handler in the Chain of Responsibility |
|
* (see http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern for |
|
* details). |
|
* @param {DMCommandHandler} handler The handler that is next in the chain. |
|
*/ |
|
this.__setNext = function(handler) { |
|
var _next = this.__next; |
|
this.__next = nextHandler; |
|
return _next ? _next : true; |
|
}; |
|
this.__setNext(nextHandler); |
|
|
|
/** |
|
* Invokes the next handler. If there is no next handler, this method |
|
* returns false, otherwise it forwards the result of the handling. |
|
* @param {Object} command The command object to be processed. |
|
*/ |
|
this.__invokeNext = function(command) { |
|
return this.__next ? this.__next.handle(command) : false; |
|
}; |
|
|
|
/** |
|
* Handles a command. The abstract method process() is called with the |
|
* command object that has been passed. If the process method catches the |
|
* command (returns true on completion), the handle() method returns true. |
|
* If the process() method doesn't catch the command, the next handler will |
|
* be invoked. |
|
* @param {Object} command The command object to be processed. |
|
*/ |
|
this.handle = function(command) { |
|
return this.process(command) ? true : this.__invokeNext(command); |
|
} |
|
|
|
/** |
|
* Empty process() method returning false. If javascript knew abstract |
|
* class members, this would be one. |
|
* @param {Object} command The command object to process. |
|
*/ |
|
this.process = function(command) { return false; }; |
|
}; |
|
|
|
/** |
|
* This Handler manages the addition and the removal of meta elements in the |
|
* head of the document. |
|
* @param {DMCommandHandler} next The handler that is next in the chain. |
|
*/ |
|
function MetaTagHandler(next) { |
|
|
|
DMCommandHandler.apply(this, [next]); |
|
this.process = function(command) { |
|
|
|
with(command.triple) { |
|
|
|
/* assert prerequisites */ |
|
if( !( |
|
(subject instanceof ERDF.Resource) && |
|
(subject.isCurrentDocument()) && |
|
(object instanceof ERDF.Literal) |
|
)) return false; |
|
} |
|
|
|
}; |
|
}; |
|
|
|
var chain = new MetaTagHandler(); |
|
var command = new DMCommand(TRIPLE_ADD, new ERDF.Triple( |
|
new ERDF.Resource(''), |
|
'rdf:tool', |
|
new ERDF.Literal('') |
|
)); |
|
|
|
/* |
|
if(chain.handle(command)) |
|
alert('Handled!'); |
|
*/ |
|
|
|
ResourceManager = { |
|
|
|
__corrupt: false, |
|
__latelyCreatedResource: undefined, |
|
__listeners: $H(), |
|
__token: 1, |
|
|
|
addListener: function(listener, mask) { |
|
|
|
if(!(listener instanceof Function)) |
|
throw 'Resource event listener is not a function!'; |
|
if(!(mask)) |
|
throw 'Invalid mask for resource event listener registration.'; |
|
|
|
// construct controller and token. |
|
var controller = {listener: listener, mask: mask}; |
|
var token = ResourceManager.__token++; |
|
|
|
// add new listener. |
|
ResourceManager.__listeners[token] = controller; |
|
|
|
// return the token generated. |
|
return token; |
|
}, |
|
|
|
removeListener: function(token) { |
|
|
|
// remove the listener with the token and return it. |
|
return ResourceManager.__listners.remove(token); |
|
}, |
|
|
|
__Event: function(action, resourceId) { |
|
this.action = action; |
|
this.resourceId = resourceId; |
|
}, |
|
|
|
__dispatchEvent: function(event) { |
|
|
|
// get all listeners. for each listener, ... |
|
ResourceManager.__listeners.values().each(function(controller) { |
|
|
|
// .. if listener subscribed to this type of event ... |
|
if(event.action & controller.mask) |
|
return controller.listener(event); |
|
}); |
|
}, |
|
|
|
getResource: function(id) { |
|
|
|
// get all possible resources for this. |
|
id = ERDF.__stripHashes(id); |
|
var resources = DataManager.query( |
|
new ERDF.Resource('#'+id), |
|
{prefix: 'raziel', name: 'entry'}, |
|
undefined |
|
); |
|
|
|
// check for consistency. |
|
if((resources.length == 1) && (resources[0].object.isResource())) { |
|
var entryUrl = resources[0].object.value; |
|
return new ResourceManager.__Resource(id, entryUrl); |
|
} |
|
|
|
// else throw an error message. |
|
throw ('Resource with id ' +id+ ' not recognized as such. ' + |
|
((resources.length > 1) ? |
|
' There is more than one raziel:entry URL.' : |
|
' There is no raziel:entry URL.')); |
|
|
|
return false; |
|
}, |
|
|
|
__createResource: function(alternativeDiv) { |
|
|
|
var collectionUrls = DataManager.query( |
|
new ERDF.Resource(''), |
|
// TODO This will become raziel:collection in near future. |
|
{prefix: 'raziel', name: 'collection'}, |
|
undefined |
|
); |
|
|
|
// check for consistency. |
|
|
|
if( (collectionUrls.length == 1) && |
|
(collectionUrls[0].object.isResource())) { |
|
|
|
// get the collection url. |
|
|
|
var collectionUrl = collectionUrls[0].object.value; |
|
var resource = undefined; |
|
|
|
// if there is an old id, serialize the dummy div from there, |
|
// otherwise create a dummy div on the fly. |
|
|
|
var serialization = alternativeDiv? alternativeDiv : |
|
'<div xmlns="http://www.w3.org/1999/xhtml"></div>'; |
|
|
|
ResourceManager.__request( |
|
'POST', collectionUrl, serialization, |
|
|
|
// on success |
|
function() { |
|
|
|
// get div and id that have been generated by the server. |
|
|
|
var response = (this.responseXML); |
|
var div = response.childNodes[0]; |
|
var id = div.getAttribute('id'); |
|
|
|
// store div in DOM |
|
if(!$$('.' + PROCESSDATA_REF)[0]) |
|
DataManager.graft(XMLNS.XHTML, |
|
document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), ['div', {'class': PROCESSDATA_REF, 'style':'display:none;'}]); |
|
|
|
$$('.' + PROCESSDATA_REF)[0].appendChild(div.cloneNode(true)); |
|
|
|
// parse local erdf data once more. |
|
|
|
DataManager.__synclocal(); |
|
|
|
// get new resource object. |
|
|
|
resource = new ResourceManager.getResource(id); |
|
|
|
// set up an action informing of the creation. |
|
|
|
ResourceManager.__resourceActionSucceeded( |
|
this, RESOURCE_CREATED, undefined); |
|
}, |
|
|
|
function() { ResourceManager.__resourceActionFailed( |
|
this, RESOURCE_CREATED, undefined);}, |
|
false |
|
); |
|
|
|
return resource; |
|
} |
|
|
|
// else |
|
throw 'Could not create resource! raziel:collection URL is missing!'; |
|
return false; |
|
|
|
}, |
|
|
|
__Resource: function(id, url) { |
|
|
|
this.__id = id; |
|
this.__url = url; |
|
|
|
/* |
|
* Process URL is no longer needed to refer to the shape element on the |
|
* canvas. AReSS uses the id's to gather information on fireing |
|
* behaviour now. |
|
*/ |
|
|
|
// // find the process url. |
|
// var processUrl = undefined; |
|
// |
|
// var urls = DataManager.query( |
|
// new ERDF.Resource('#'+this.__id), |
|
// {prefix: 'raziel', name: 'process'}, |
|
// undefined |
|
// ); |
|
// |
|
// if(urls.length == 0) { throw 'The resource with the id ' +id+ ' has no process url.'}; |
|
// |
|
// urls.each( function(triple) { |
|
// |
|
// // if there are more urls, use the last one. |
|
// processUrl = triple.object.value; |
|
// }); |
|
// |
|
// this.__processUrl = processUrl; |
|
// |
|
// // convenience function for getting the process url. |
|
// this.processUrl = function() { |
|
// return this.__processUrl; |
|
// } |
|
|
|
|
|
// convenience finction for getting the id. |
|
this.id = function() { |
|
return this.__id; |
|
} |
|
|
|
// convenience finction for getting the entry url. |
|
this.url = function() { |
|
return this.__url; |
|
} |
|
|
|
this.reload = function() { |
|
var _url = this.__url; |
|
var _id = this.__id; |
|
ResourceManager.__request( |
|
'GET', _url, null, |
|
function() { ResourceManager.__resourceActionSucceeded( |
|
this, RESOURCE_RELOADED, _id); }, |
|
function() { ResourceManager.__resourceActionFailed( |
|
this, RESURCE_RELOADED, _id); }, |
|
USE_ASYNCHRONOUS_REQUESTS |
|
); |
|
}; |
|
|
|
this.save = function(synchronize) { |
|
var _url = this.__url; |
|
var _id = this.__id; |
|
data = DataManager.serialize($(_id)); |
|
ResourceManager.__request( |
|
'PUT', _url, data, |
|
function() { ResourceManager.__resourceActionSucceeded( |
|
this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE_SAVED, _id); }, |
|
function() { ResourceManager.__resourceActionFailed( |
|
this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE.SAVED, _id); }, |
|
USE_ASYNCHRONOUS_REQUESTS |
|
); |
|
}; |
|
|
|
this.remove = function() { |
|
var _url = this.__url; |
|
var _id = this.__id; |
|
ResourceManager.__request( |
|
'DELETE', _url, null, |
|
function() { ResourceManager.__resourceActionSucceeded( |
|
this, RESOURCE_REMOVED, _id); }, |
|
function() { ResourceManager.__resourceActionFailed( |
|
this, RESOURCE_REMOVED, _id);}, |
|
USE_ASYNCHRONOUS_REQUESTS |
|
); |
|
}; |
|
}, |
|
|
|
request: function(url, requestOptions) { |
|
|
|
var options = { |
|
method: 'get', |
|
asynchronous: true, |
|
parameters: {} |
|
}; |
|
|
|
Object.extend(options, requestOptions || {}); |
|
|
|
var params = Hash.toQueryString(options.parameters); |
|
if (params) |
|
url += (url.include('?') ? '&' : '?') + params; |
|
|
|
return ResourceManager.__request( |
|
options.method, |
|
url, |
|
options.data, |
|
(options.onSuccess instanceof Function ? function() { options.onSuccess(this); } : undefined ), |
|
(options.onFailure instanceof Function ? function() { options.onFailure(this); } : undefined ), |
|
options.asynchronous && USE_ASYNCHRONOUS_REQUESTS, |
|
options.headers); |
|
}, |
|
|
|
__request: function(method, url, data, success, error, async, headers) { |
|
|
|
// get a request object |
|
var httpRequest = Try.these( |
|
|
|
/* do the Mozilla/Safari/Opera stuff */ |
|
function() { return new XMLHttpRequest(); }, |
|
|
|
/* do the IE stuff */ |
|
function() { return new ActiveXObject("Msxml2.XMLHTTP"); }, |
|
function() { return new ActiveXObject("Microsoft.XMLHTTP") } |
|
); |
|
|
|
// if there is no request object ... |
|
if (!httpRequest) { |
|
if(!this.__corrupt) |
|
throw 'This browser does not provide any AJAX functionality. You will not be able to use the software provided with the page you are viewing. Please consider installing appropriate extensions.'; |
|
this.__corrupt = true; |
|
return false; |
|
} |
|
|
|
if(success instanceof Function) |
|
httpRequest.onload = success; |
|
if(error instanceof Function) { |
|
httpRequest.onerror = error; |
|
} |
|
|
|
var h = $H(headers) |
|
h.keys().each(function(key) { |
|
|
|
httpRequest.setRequestHeader(key, h[key]); |
|
}); |
|
|
|
try { |
|
|
|
if(SHOW_DEBUG_ALERTS_WHEN_SAVING) |
|
|
|
alert(method + ' ' + url + '\n' + |
|
SHOW_EXTENDED_DEBUG_INFORMATION ? data : ''); |
|
|
|
// TODO Remove synchronous calls to the server as soon as xenodot |
|
// handles asynchronous requests without failure. |
|
httpRequest.open(method, url, !async?false:true); |
|
httpRequest.send(data); |
|
|
|
} catch(e) { |
|
return false; |
|
} |
|
return true; |
|
}, |
|
|
|
__resourceActionSucceeded: function(transport, action, id) { |
|
|
|
var status = transport.status; |
|
var response = transport.responseText; |
|
|
|
if(SHOW_DEBUG_ALERTS_WHEN_SAVING) |
|
|
|
alert(status + ' ' + url + '\n' + |
|
SHOW_EXTENDED_DEBUG_INFORMATION ? data : ''); |
|
|
|
// if the status code is not in 2xx, throw an error. |
|
if(status >= 300) |
|
throw 'The server responded with an error: ' + status + '\n' + (SHOW_EXTENDED_DEBUG_INFORMATION ? + data : 'If you need additional information here, including the data sent by the server, consider setting SHOW_EXTENDED_DEBUG_INFORMATION to true.'); |
|
|
|
switch(action) { |
|
|
|
case RESOURCE_REMOVED: |
|
|
|
// get div and id |
|
var response = (transport.responseXML); |
|
var div = response.childNodes[0]; |
|
var id = div.getAttribute('id'); |
|
|
|
// remove the resource from DOM |
|
var localDiv = document.getElementById(id); |
|
localDiv.parentNode.removeChild(localDiv); |
|
break; |
|
|
|
case RESOURCE_CREATED: |
|
|
|
// nothing remains to be done. |
|
break; |
|
|
|
case RESOURCE_SAVED | RESOURCE_SYNCHRONIZED: |
|
|
|
DataManager.__synclocal(); |
|
|
|
case RESOURCE_SAVED: |
|
|
|
// nothing remains to be done. |
|
break; |
|
|
|
case RESOURCE_RELOADED: |
|
|
|
// get div and id |
|
var response = (transport.responseXML); |
|
var div = response.childNodes[0]; |
|
var id = div.getAttribute('id'); |
|
|
|
// remove the local resource representation from DOM |
|
var localDiv = document.getElementById(id) |
|
localDiv.parentNode.removeChild(localDiv); |
|
|
|
// store div in DOM |
|
if(!$$(PROCESSDATA_REF)[0]) |
|
DataManager.graft(XMLNS.XHTML, |
|
document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), ['div', {'class': PROCESSDATA_REF, 'style':'display:none;'}]); |
|
|
|
$$(PROCESSDATA_REF)[0].appendChild(div.cloneNode(true)); |
|
DataManager.__synclocal(); |
|
break; |
|
|
|
default: |
|
DataManager.__synclocal(); |
|
|
|
} |
|
|
|
// dispatch to all listeners ... |
|
ResourceManager.__dispatchEvent( |
|
|
|
// ... an event describing the change that happened here. |
|
new ResourceManager.__Event(action, id) |
|
); |
|
}, |
|
|
|
__resourceActionFailed: function(transport, action, id) { |
|
throw "Fatal: Resource action failed. There is something horribly " + |
|
"wrong with either the server, the transport protocol or your " + |
|
"online status. Sure you're online?"; |
|
} |
|
}/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* The super class for all classes in ORYX. Adds some OOP feeling to javascript. |
|
* See article "Object Oriented Super Class Method Calling with JavaScript" on |
|
* http://truecode.blogspot.com/2006/08/object-oriented-super-class-method.html |
|
* for a documentation on this. Fairly good article that points out errors in |
|
* Douglas Crockford's inheritance and super method calling approach. |
|
* Worth reading. |
|
* @class Clazz |
|
*/ |
|
var Clazz = function() {}; |
|
|
|
/** |
|
* Empty constructor. |
|
* @methodOf Clazz.prototype |
|
*/ |
|
Clazz.prototype.construct = function() {}; |
|
|
|
/** |
|
* Can be used to build up inheritances of classes. |
|
* @example |
|
* var MyClass = Clazz.extend({ |
|
* construct: function(myParam){ |
|
* // Do sth. |
|
* } |
|
* }); |
|
* var MySubClass = MyClass.extend({ |
|
* construct: function(myParam){ |
|
* // Use this to call constructor of super class |
|
* arguments.callee.$.construct.apply(this, arguments); |
|
* // Do sth. |
|
* } |
|
* }); |
|
* @param {Object} def The definition of the new class. |
|
*/ |
|
Clazz.extend = function(def) { |
|
var classDef = function() { |
|
if (arguments[0] !== Clazz) { this.construct.apply(this, arguments); } |
|
}; |
|
|
|
var proto = new this(Clazz); |
|
var superClass = this.prototype; |
|
|
|
for (var n in def) { |
|
var item = def[n]; |
|
if (item instanceof Function) item.$ = superClass; |
|
proto[n] = item; |
|
} |
|
|
|
classDef.prototype = proto; |
|
|
|
//Give this new class the same static extend method |
|
classDef.extend = this.extend; |
|
return classDef; |
|
};/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX) var ORYX = {}; |
|
|
|
if(!ORYX.CONFIG) ORYX.CONFIG = {}; |
|
|
|
/** |
|
* This file contains URI constants that may be used for XMLHTTPRequests. |
|
*/ |
|
|
|
ORYX.CONFIG.ROOT_PATH = "editor/"; //TODO: Remove last slash!! |
|
ORYX.CONFIG.EXPLORER_PATH = "explorer"; |
|
ORYX.CONFIG.LIBS_PATH = "libs"; |
|
|
|
/** |
|
* Regular Config |
|
*/ |
|
ORYX.CONFIG.SERVER_HANDLER_ROOT = "service"; |
|
ORYX.CONFIG.SERVER_EDITOR_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/editor"; |
|
ORYX.CONFIG.SERVER_MODEL_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/model"; |
|
ORYX.CONFIG.STENCILSET_HANDLER = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/editor_stencilset?embedsvg=true&url=true&namespace="; |
|
ORYX.CONFIG.STENCIL_SETS_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/editor_stencilset"; |
|
|
|
ORYX.CONFIG.PLUGINS_CONFIG = "editor-app/plugins.xml"; |
|
ORYX.CONFIG.SYNTAXCHECKER_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/syntaxchecker"; |
|
ORYX.CONFIG.DEPLOY_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/model/deploy"; |
|
ORYX.CONFIG.MODEL_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/models"; |
|
ORYX.CONFIG.FORM_FLOW_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/formflows"; |
|
ORYX.CONFIG.FORM_FLOW_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/formflow"; |
|
ORYX.CONFIG.FORM_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/forms"; |
|
ORYX.CONFIG.FORM_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/form"; |
|
ORYX.CONFIG.SUB_PROCESS_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/subprocesses"; |
|
ORYX.CONFIG.SUB_PROCESS_IMAGE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/subprocess"; |
|
ORYX.CONFIG.TEST_SERVICE_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/service/"; |
|
|
|
ORYX.CONFIG.SERVICE_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/services"; |
|
ORYX.CONFIG.CONDITION_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/conditionelements"; |
|
ORYX.CONFIG.VARIABLEDEF_ELEMENT_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/variabledefinitionelements"; |
|
ORYX.CONFIG.VALIDATOR_LIST_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/validators"; |
|
|
|
ORYX.CONFIG.SS_EXTENSIONS_FOLDER = ORYX.CONFIG.ROOT_PATH + "stencilsets/extensions/"; |
|
ORYX.CONFIG.SS_EXTENSIONS_CONFIG = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/editor_ssextensions"; |
|
ORYX.CONFIG.ORYX_NEW_URL = "/new"; |
|
ORYX.CONFIG.BPMN_LAYOUTER = ORYX.CONFIG.ROOT_PATH + "bpmnlayouter"; |
|
|
|
ORYX.CONFIG.EXPRESSION_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/expression-metadata"; |
|
ORYX.CONFIG.DATASOURCE_METADATA_URL = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/datasource-metadata";/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX) var ORYX = {}; |
|
|
|
if(!ORYX.CONFIG) ORYX.CONFIG = {}; |
|
|
|
/** |
|
* Signavio specific variables |
|
*/ |
|
ORYX.CONFIG.BACKEND_SWITCH = true; |
|
ORYX.CONFIG.PANEL_LEFT_WIDTH = 250; |
|
ORYX.CONFIG.PANEL_RIGHT_COLLAPSED = true; |
|
ORYX.CONFIG.PANEL_RIGHT_WIDTH = 300; |
|
ORYX.CONFIG.APPNAME = 'KISBPM'; |
|
ORYX.CONFIG.WEB_URL = "."; |
|
|
|
ORYX.CONFIG.BLANK_IMAGE = ORYX.CONFIG.LIBS_PATH + '/ext-2.0.2/resources/images/default/s.gif'; |
|
|
|
/* Specify offset of header */ |
|
ORYX.CONFIG.OFFSET_HEADER = 61; |
|
|
|
/* Show grid line while dragging */ |
|
ORYX.CONFIG.SHOW_GRIDLINE = true; |
|
|
|
/* Editor-Mode */ |
|
ORYX.CONFIG.MODE_READONLY = "readonly"; |
|
ORYX.CONFIG.MODE_FULLSCREEN = "fullscreen"; |
|
ORYX.CONFIG.WINDOW_HEIGHT = 800; |
|
ORYX.CONFIG.PREVENT_LOADINGMASK_AT_READY = false; |
|
|
|
/* Plugins */ |
|
ORYX.CONFIG.PLUGINS_ENABLED = true; |
|
ORYX.CONFIG.PLUGINS_FOLDER = "Plugins/"; |
|
|
|
ORYX.CONFIG.BPMN20_SCHEMA_VALIDATION_ON = true; |
|
|
|
/* Namespaces */ |
|
ORYX.CONFIG.NAMESPACE_ORYX = "http://www.b3mn.org/oryx"; |
|
ORYX.CONFIG.NAMESPACE_SVG = "http://www.w3.org/2000/svg"; |
|
|
|
/* UI */ |
|
ORYX.CONFIG.CANVAS_WIDTH = 1200; |
|
ORYX.CONFIG.CANVAS_HEIGHT = 1050; |
|
ORYX.CONFIG.CANVAS_RESIZE_INTERVAL = 100; |
|
ORYX.CONFIG.CANVAS_MIN_WIDTH = 800; |
|
ORYX.CONFIG.CANVAS_MIN_HEIGHT = 300; |
|
ORYX.CONFIG.SELECTED_AREA_PADDING = 4; |
|
ORYX.CONFIG.CANVAS_BACKGROUND_COLOR = "none"; |
|
ORYX.CONFIG.GRID_DISTANCE = 30; |
|
ORYX.CONFIG.GRID_ENABLED = true; |
|
ORYX.CONFIG.ZOOM_OFFSET = 0.1; |
|
ORYX.CONFIG.DEFAULT_SHAPE_MARGIN = 60; |
|
ORYX.CONFIG.SCALERS_SIZE = 7; |
|
ORYX.CONFIG.MINIMUM_SIZE = 20; |
|
ORYX.CONFIG.MAXIMUM_SIZE = 10000; |
|
ORYX.CONFIG.OFFSET_MAGNET = 15; |
|
ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP = 8; |
|
ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM = 8; |
|
ORYX.CONFIG.OFFSET_EDGE_BOUNDS = 5; |
|
ORYX.CONFIG.COPY_MOVE_OFFSET = 30; |
|
|
|
ORYX.CONFIG.BORDER_OFFSET = 14; |
|
|
|
ORYX.CONFIG.MAX_NUM_SHAPES_NO_GROUP = 20; // Updated so the form editor shows all elements at once |
|
|
|
ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER = 30; |
|
ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET = 45; |
|
|
|
/* Shape-Menu Align */ |
|
ORYX.CONFIG.SHAPEMENU_RIGHT = "Oryx_Right"; |
|
ORYX.CONFIG.SHAPEMENU_BOTTOM = "Oryx_Bottom"; |
|
ORYX.CONFIG.SHAPEMENU_LEFT = "Oryx_Left"; |
|
ORYX.CONFIG.SHAPEMENU_TOP = "Oryx_Top"; |
|
|
|
|
|
/* Morph-Menu Item */ |
|
ORYX.CONFIG.MORPHITEM_DISABLED = "Oryx_MorphItem_disabled"; |
|
|
|
/* Property type names */ |
|
ORYX.CONFIG.TYPE_STRING = "string"; |
|
ORYX.CONFIG.TYPE_BOOLEAN = "boolean"; |
|
ORYX.CONFIG.TYPE_INTEGER = "integer"; |
|
ORYX.CONFIG.TYPE_FLOAT = "float"; |
|
ORYX.CONFIG.TYPE_COLOR = "color"; |
|
ORYX.CONFIG.TYPE_DATE = "date"; |
|
ORYX.CONFIG.TYPE_CHOICE = "choice"; |
|
ORYX.CONFIG.TYPE_URL = "url"; |
|
ORYX.CONFIG.TYPE_DIAGRAM_LINK = "diagramlink"; |
|
ORYX.CONFIG.TYPE_COMPLEX = "complex"; |
|
ORYX.CONFIG.TYPE_MULTIPLECOMPLEX = "multiplecomplex"; |
|
ORYX.CONFIG.TYPE_TEXT = "text"; |
|
ORYX.CONFIG.TYPE_KISBPM_MULTIINSTANCE = "kisbpm-multiinstance"; |
|
ORYX.CONFIG.TYPE_MODEL_LINK = "modellink"; |
|
ORYX.CONFIG.TYPE_FORM_FLOW_LINK = "formflowlink"; |
|
ORYX.CONFIG.TYPE_FORM_LINK = "formlink"; |
|
ORYX.CONFIG.TYPE_SUB_PROCESS_LINK = "subprocesslink"; |
|
ORYX.CONFIG.TYPE_SERVICE_LINK = "servicelink"; |
|
ORYX.CONFIG.TYPE_CONDITIONS = "conditions"; |
|
ORYX.CONFIG.TYPE_VARIABLES = "variables"; |
|
ORYX.CONFIG.TYPE_LISTENER = "listener"; |
|
ORYX.CONFIG.TYPE_EPC_FREQ = "epcfrequency"; |
|
ORYX.CONFIG.TYPE_GLOSSARY_LINK = "glossarylink"; |
|
ORYX.CONFIG.TYPE_EXPRESSION = "expression"; |
|
ORYX.CONFIG.TYPE_DATASOURCE = "datasource"; |
|
ORYX.CONFIG.TYPE_DATASOURCE_MINIMAL = "datasource-minimal"; |
|
ORYX.CONFIG.TYPE_VALIDATORS = "validators"; |
|
|
|
|
|
/* Vertical line distance of multiline labels */ |
|
ORYX.CONFIG.LABEL_LINE_DISTANCE = 2; |
|
ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT = 12; |
|
|
|
/* Open Morph Menu with Hover */ |
|
ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER = false; |
|
|
|
|
|
/* Editor constants come here */ |
|
ORYX.CONFIG.EDITOR_ALIGN_BOTTOM = 0x01; |
|
ORYX.CONFIG.EDITOR_ALIGN_MIDDLE = 0x02; |
|
ORYX.CONFIG.EDITOR_ALIGN_TOP = 0x04; |
|
ORYX.CONFIG.EDITOR_ALIGN_LEFT = 0x08; |
|
ORYX.CONFIG.EDITOR_ALIGN_CENTER = 0x10; |
|
ORYX.CONFIG.EDITOR_ALIGN_RIGHT = 0x20; |
|
ORYX.CONFIG.EDITOR_ALIGN_SIZE = 0x30; |
|
|
|
/* Event types */ |
|
ORYX.CONFIG.EVENT_MOUSEDOWN = "mousedown"; |
|
ORYX.CONFIG.EVENT_MOUSEUP = "mouseup"; |
|
ORYX.CONFIG.EVENT_MOUSEOVER = "mouseover"; |
|
ORYX.CONFIG.EVENT_MOUSEOUT = "mouseout"; |
|
ORYX.CONFIG.EVENT_MOUSEMOVE = "mousemove"; |
|
ORYX.CONFIG.EVENT_DBLCLICK = "dblclick"; |
|
ORYX.CONFIG.EVENT_KEYDOWN = "keydown"; |
|
ORYX.CONFIG.EVENT_KEYUP = "keyup"; |
|
|
|
ORYX.CONFIG.EVENT_LOADED = "editorloaded"; |
|
ORYX.CONFIG.EVENT_SAVED = "editorSaved"; |
|
|
|
ORYX.CONFIG.EVENT_EXECUTE_COMMANDS = "executeCommands"; |
|
ORYX.CONFIG.EVENT_STENCIL_SET_LOADED = "stencilSetLoaded"; |
|
ORYX.CONFIG.EVENT_SELECTION_CHANGED = "selectionchanged"; |
|
ORYX.CONFIG.EVENT_SHAPEADDED = "shapeadded"; |
|
ORYX.CONFIG.EVENT_SHAPEREMOVED = "shaperemoved"; |
|
ORYX.CONFIG.EVENT_PROPERTY_CHANGED = "propertyChanged"; |
|
ORYX.CONFIG.EVENT_DRAGDROP_START = "dragdrop.start"; |
|
ORYX.CONFIG.EVENT_SHAPE_MENU_CLOSE = "shape.menu.close"; |
|
ORYX.CONFIG.EVENT_DRAGDROP_END = "dragdrop.end"; |
|
ORYX.CONFIG.EVENT_RESIZE_START = "resize.start"; |
|
ORYX.CONFIG.EVENT_RESIZE_END = "resize.end"; |
|
ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED = "dragDocker.docked"; |
|
ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW = "highlight.showHighlight"; |
|
ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE = "highlight.hideHighlight"; |
|
ORYX.CONFIG.EVENT_LOADING_ENABLE = "loading.enable"; |
|
ORYX.CONFIG.EVENT_LOADING_DISABLE = "loading.disable"; |
|
ORYX.CONFIG.EVENT_LOADING_STATUS = "loading.status"; |
|
ORYX.CONFIG.EVENT_OVERLAY_SHOW = "overlay.show"; |
|
ORYX.CONFIG.EVENT_OVERLAY_HIDE = "overlay.hide"; |
|
ORYX.CONFIG.EVENT_ARRANGEMENT_TOP = "arrangement.setToTop"; |
|
ORYX.CONFIG.EVENT_ARRANGEMENT_BACK = "arrangement.setToBack"; |
|
ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD = "arrangement.setForward"; |
|
ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD = "arrangement.setBackward"; |
|
ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED = "propertyWindow.propertyChanged"; |
|
ORYX.CONFIG.EVENT_LAYOUT_ROWS = "layout.rows"; |
|
ORYX.CONFIG.EVENT_LAYOUT_BPEL = "layout.BPEL"; |
|
ORYX.CONFIG.EVENT_LAYOUT_BPEL_VERTICAL = "layout.BPEL.vertical"; |
|
ORYX.CONFIG.EVENT_LAYOUT_BPEL_HORIZONTAL = "layout.BPEL.horizontal"; |
|
ORYX.CONFIG.EVENT_LAYOUT_BPEL_SINGLECHILD = "layout.BPEL.singlechild"; |
|
ORYX.CONFIG.EVENT_LAYOUT_BPEL_AUTORESIZE = "layout.BPEL.autoresize"; |
|
ORYX.CONFIG.EVENT_AUTOLAYOUT_LAYOUT = "autolayout.layout"; |
|
ORYX.CONFIG.EVENT_UNDO_EXECUTE = "undo.execute"; |
|
ORYX.CONFIG.EVENT_UNDO_ROLLBACK = "undo.rollback"; |
|
ORYX.CONFIG.EVENT_BUTTON_UPDATE = "toolbar.button.update"; |
|
ORYX.CONFIG.EVENT_LAYOUT = "layout.dolayout"; |
|
ORYX.CONFIG.EVENT_GLOSSARY_LINK_EDIT = "glossary.link.edit"; |
|
ORYX.CONFIG.EVENT_GLOSSARY_SHOW = "glossary.show.info"; |
|
ORYX.CONFIG.EVENT_GLOSSARY_NEW = "glossary.show.new"; |
|
ORYX.CONFIG.EVENT_DOCKERDRAG = "dragTheDocker"; |
|
ORYX.CONFIG.EVENT_CANVAS_SCROLL = "canvas.scroll"; |
|
|
|
ORYX.CONFIG.EVENT_SHOW_PROPERTYWINDOW = "propertywindow.show"; |
|
ORYX.CONFIG.EVENT_ABOUT_TO_SAVE = "file.aboutToSave"; |
|
|
|
/* Selection Shapes Highlights */ |
|
ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE = 5; |
|
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR = "#4444FF"; |
|
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR2 = "#9999FF"; |
|
|
|
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_CORNER = "corner"; |
|
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE = "rectangle"; |
|
|
|
ORYX.CONFIG.SELECTION_VALID_COLOR = "#00FF00"; |
|
ORYX.CONFIG.SELECTION_INVALID_COLOR = "#FF0000"; |
|
|
|
|
|
ORYX.CONFIG.DOCKER_DOCKED_COLOR = "#00FF00"; |
|
ORYX.CONFIG.DOCKER_UNDOCKED_COLOR = "#FF0000"; |
|
ORYX.CONFIG.DOCKER_SNAP_OFFSET = 10; |
|
|
|
/* Copy & Paste */ |
|
ORYX.CONFIG.EDIT_OFFSET_PASTE = 10; |
|
|
|
/* Key-Codes */ |
|
ORYX.CONFIG.KEY_CODE_X = 88; |
|
ORYX.CONFIG.KEY_CODE_C = 67; |
|
ORYX.CONFIG.KEY_CODE_V = 86; |
|
ORYX.CONFIG.KEY_CODE_DELETE = 46; |
|
ORYX.CONFIG.KEY_CODE_META = 224; |
|
ORYX.CONFIG.KEY_CODE_BACKSPACE = 8; |
|
ORYX.CONFIG.KEY_CODE_LEFT = 37; |
|
ORYX.CONFIG.KEY_CODE_RIGHT = 39; |
|
ORYX.CONFIG.KEY_CODE_UP = 38; |
|
ORYX.CONFIG.KEY_CODE_DOWN = 40; |
|
|
|
// TODO Determine where the lowercase constants are still used and remove them from here. |
|
ORYX.CONFIG.KEY_Code_enter = 12; |
|
ORYX.CONFIG.KEY_Code_left = 37; |
|
ORYX.CONFIG.KEY_Code_right = 39; |
|
ORYX.CONFIG.KEY_Code_top = 38; |
|
ORYX.CONFIG.KEY_Code_bottom = 40; |
|
|
|
/* Supported Meta Keys */ |
|
|
|
ORYX.CONFIG.META_KEY_META_CTRL = "metactrl"; |
|
ORYX.CONFIG.META_KEY_ALT = "alt"; |
|
ORYX.CONFIG.META_KEY_SHIFT = "shift"; |
|
|
|
/* Key Actions */ |
|
|
|
ORYX.CONFIG.KEY_ACTION_DOWN = "down"; |
|
ORYX.CONFIG.KEY_ACTION_UP = "up"; |
|
|
|
|
|
/* Form Rowlayouting */ |
|
ORYX.CONFIG.FORM_ROW_WIDTH = 350; |
|
ORYX.CONFIG.FORM_GROUP_MARGIN = 5; |
|
ORYX.CONFIG.FORM_GROUP_EMPTY_HEIGHT = 100; |
|
|
|
/* Form element types */ |
|
ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX = 'http://b3mn.org/stencilset/xforms'; |
|
ORYX.CONFIG.FORM_ELEMENT_TYPE_ROOT = 'http://b3mn.org/stencilset/xforms#XForm'; |
|
ORYX.CONFIG.FORM_ELEMENT_TYPE_GROUP = 'http://b3mn.org/stencilset/xforms#Group'; |
|
ORYX.CONFIG.FORM_ELEMENT_TYPE_REPEATING_GROUP = 'http://b3mn.org/stencilset/xforms#RepeatingGroup'; |
|
ORYX.CONFIG.FORM_ELEMENT_TYPE_LABEL_FIELD = 'http://b3mn.org/stencilset/xforms#LabelField'; |
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
function printf() { |
|
|
|
var result = arguments[0]; |
|
for (var i=1; i<arguments.length; i++) |
|
result = result.replace('%' + (i-1), arguments[i]); |
|
return result; |
|
} |
|
|
|
// oryx constants. |
|
var ORYX_LOGLEVEL_TRACE = 5; |
|
var ORYX_LOGLEVEL_DEBUG = 4; |
|
var ORYX_LOGLEVEL_INFO = 3; |
|
var ORYX_LOGLEVEL_WARN = 2; |
|
var ORYX_LOGLEVEL_ERROR = 1; |
|
var ORYX_LOGLEVEL_FATAL = 0; |
|
var ORYX_LOGLEVEL = 3; |
|
var ORYX_CONFIGURATION_DELAY = 100; |
|
var ORYX_CONFIGURATION_WAIT_ATTEMPTS = 10; |
|
|
|
if(!ORYX) var ORYX = {}; |
|
|
|
ORYX = Object.extend(ORYX, { |
|
|
|
//set the path in the config.js file!!!! |
|
PATH: ORYX.CONFIG.ROOT_PATH, |
|
//CONFIGURATION: "config.js", |
|
|
|
URLS: [], |
|
|
|
alreadyLoaded: [], |
|
|
|
configrationRetries: 0, |
|
|
|
Version: '0.1.1', |
|
|
|
availablePlugins: [], |
|
|
|
/** |
|
* The ORYX.Log logger. |
|
*/ |
|
Log: { |
|
|
|
__appenders: [ |
|
{ |
|
append: function(message) { |
|
if(typeof(console) !== "undefined" && console.log !== undefined) { |
|
console.log(message); |
|
} |
|
} |
|
} |
|
], |
|
|
|
trace: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_TRACE) |
|
ORYX.Log.__log('TRACE', arguments); }, |
|
debug: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_DEBUG) |
|
ORYX.Log.__log('DEBUG', arguments); }, |
|
info: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_INFO) |
|
ORYX.Log.__log('INFO', arguments); }, |
|
warn: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_WARN) |
|
ORYX.Log.__log('WARN', arguments); }, |
|
error: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_ERROR) |
|
ORYX.Log.__log('ERROR', arguments); }, |
|
fatal: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_FATAL) |
|
ORYX.Log.__log('FATAL', arguments); }, |
|
|
|
__log: function(prefix, messageParts) { |
|
|
|
messageParts[0] = (new Date()).getTime() + " " |
|
+ prefix + " " + messageParts[0]; |
|
var message = printf.apply(null, messageParts); |
|
|
|
ORYX.Log.__appenders.each(function(appender) { |
|
appender.append(message); |
|
}); |
|
}, |
|
|
|
addAppender: function(appender) { |
|
ORYX.Log.__appenders.push(appender); |
|
} |
|
}, |
|
|
|
/** |
|
* First bootstrapping layer. The Oryx loading procedure begins. In this |
|
* step, all preliminaries that are not in the responsibility of Oryx to be |
|
* met have to be checked here, such as the existance of the prototpe |
|
* library in the current execution environment. After that, the second |
|
* bootstrapping layer is being invoked. Failing to ensure that any |
|
* preliminary condition is not met has to fail with an error. |
|
*/ |
|
load: function() { |
|
|
|
ORYX.Log.debug("Oryx begins loading procedure."); |
|
|
|
// check for prototype |
|
if( (typeof Prototype=='undefined') || |
|
(typeof Element == 'undefined') || |
|
(typeof Element.Methods=='undefined') || |
|
parseFloat(Prototype.Version.split(".")[0] + "." + |
|
Prototype.Version.split(".")[1]) < 1.5) |
|
|
|
throw("Application requires the Prototype JavaScript framework >= 1.5.3"); |
|
|
|
ORYX.Log.debug("Prototype > 1.5 found."); |
|
|
|
// continue loading. |
|
ORYX._load(); |
|
}, |
|
|
|
/** |
|
* Second bootstrapping layer. The oryx configuration is checked. When not |
|
* yet loaded, config.js is being requested from the server. A repeated |
|
* error in retrieving the configuration will result in an error to be |
|
* thrown after a certain time of retries. Once the configuration is there, |
|
* all urls that are registered with oryx loading are being requested from |
|
* the server. Once everything is loaded, the third layer is being invoked. |
|
*/ |
|
_load: function() { |
|
/* |
|
// if configuration not there already, |
|
if(!(ORYX.CONFIG)) { |
|
|
|
// if this is the first attempt... |
|
if(ORYX.configrationRetries == 0) { |
|
|
|
// get the path and filename. |
|
var configuration = ORYX.PATH + ORYX.CONFIGURATION; |
|
|
|
ORYX.Log.debug("Configuration not found, loading from '%0'.", |
|
configuration); |
|
|
|
// require configuration file. |
|
Kickstart.require(configuration); |
|
|
|
// else if attempts exceeded ... |
|
} else if(ORYX.configrationRetries >= ORYX_CONFIGURATION_WAIT_ATTEMPTS) { |
|
|
|
throw "Tried to get configuration" + |
|
ORYX_CONFIGURATION_WAIT_ATTEMPTS + |
|
" times from '" + configuration + "'. Giving up." |
|
|
|
} else if(ORYX.configrationRetries > 0){ |
|
|
|
// point out how many attempts are left... |
|
ORYX.Log.debug("Waiting once more (%0 attempts left)", |
|
(ORYX_CONFIGURATION_WAIT_ATTEMPTS - |
|
ORYX.configrationRetries)); |
|
|
|
} |
|
|
|
// any case: continue in a moment with increased retry count. |
|
ORYX.configrationRetries++; |
|
window.setTimeout(ORYX._load, ORYX_CONFIGURATION_DELAY); |
|
return; |
|
} |
|
|
|
ORYX.Log.info("Configuration loaded."); |
|
|
|
// load necessary scripts. |
|
ORYX.URLS.each(function(url) { |
|
ORYX.Log.debug("Requireing '%0'", url); |
|
Kickstart.require(ORYX.PATH + url) }); |
|
*/ |
|
// configurate logging and load plugins. |
|
ORYX.loadPlugins(); |
|
}, |
|
|
|
/** |
|
* Third bootstrapping layer. This is where first the plugin coniguration |
|
* file is loaded into oryx, analyzed, and where all plugins are being |
|
* requested by the server. Afterwards, all editor instances will be |
|
* initialized. |
|
*/ |
|
loadPlugins: function() { |
|
|
|
// load plugins if enabled. |
|
if(ORYX.CONFIG.PLUGINS_ENABLED) |
|
ORYX._loadPlugins() |
|
else |
|
ORYX.Log.warn("Ignoring plugins, loading Core only."); |
|
|
|
// init the editor instances. |
|
init(); |
|
}, |
|
|
|
_loadPlugins: function() { |
|
|
|
// load plugin configuration file. |
|
var source = ORYX.CONFIG.PLUGINS_CONFIG; |
|
|
|
ORYX.Log.debug("Loading plugin configuration from '%0'.", source); |
|
|
|
new Ajax.Request(source, { |
|
asynchronous: false, |
|
method: 'get', |
|
onSuccess: function(result) { |
|
|
|
/* |
|
* This is the method that is being called when the plugin |
|
* configuration was successfully loaded from the server. The |
|
* file has to be processed and the contents need to be |
|
* considered for further plugin requireation. |
|
*/ |
|
|
|
ORYX.Log.info("Plugin configuration file loaded."); |
|
|
|
// get plugins.xml content |
|
var resultXml = result.responseXML; |
|
|
|
// TODO: Describe how properties are handled. |
|
// Get the globale Properties |
|
var globalProperties = []; |
|
var preferences = $A(resultXml.getElementsByTagName("properties")); |
|
preferences.each( function(p) { |
|
|
|
var props = $A(p.childNodes); |
|
props.each( function(prop) { |
|
var property = new Hash(); |
|
|
|
// get all attributes from the node and set to global properties |
|
var attributes = $A(prop.attributes) |
|
attributes.each(function(attr){property[attr.nodeName] = attr.nodeValue}); |
|
if(attributes.length > 0) { globalProperties.push(property) }; |
|
}); |
|
}); |
|
|
|
|
|
// TODO Why are we using XML if we don't respect structure anyway? |
|
// for each plugin element in the configuration.. |
|
var plugin = resultXml.getElementsByTagName("plugin"); |
|
$A(plugin).each( function(node) { |
|
|
|
// get all element's attributes. |
|
// TODO: What about: var pluginData = $H(node.attributes) !? |
|
var pluginData = new Hash(); |
|
$A(node.attributes).each( function(attr){ |
|
pluginData[attr.nodeName] = attr.nodeValue}); |
|
|
|
// ensure there's a name attribute. |
|
if(!pluginData['name']) { |
|
ORYX.Log.error("A plugin is not providing a name. Ingnoring this plugin."); |
|
return; |
|
} |
|
|
|
// ensure there's a source attribute. |
|
if(!pluginData['source']) { |
|
ORYX.Log.error("Plugin with name '%0' doesn't provide a source attribute.", pluginData['name']); |
|
return; |
|
} |
|
|
|
// Get all private Properties |
|
var propertyNodes = node.getElementsByTagName("property"); |
|
var properties = []; |
|
$A(propertyNodes).each(function(prop) { |
|
var property = new Hash(); |
|
|
|
// Get all Attributes from the Node |
|
var attributes = $A(prop.attributes) |
|
attributes.each(function(attr){property[attr.nodeName] = attr.nodeValue}); |
|
if(attributes.length > 0) { properties.push(property) }; |
|
|
|
}); |
|
|
|
// Set all Global-Properties to the Properties |
|
properties = properties.concat(globalProperties); |
|
|
|
// Set Properties to Plugin-Data |
|
pluginData['properties'] = properties; |
|
|
|
// Get the RequieredNodes |
|
var requireNodes = node.getElementsByTagName("requires"); |
|
var requires; |
|
$A(requireNodes).each(function(req) { |
|
var namespace = $A(req.attributes).find(function(attr){ return attr.name == "namespace"}) |
|
if( namespace && namespace.nodeValue ){ |
|
if( !requires ){ |
|
requires = {namespaces:[]} |
|
} |
|
|
|
requires.namespaces.push(namespace.nodeValue) |
|
} |
|
}); |
|
|
|
// Set Requires to the Plugin-Data, if there is one |
|
if( requires ){ |
|
pluginData['requires'] = requires; |
|
} |
|
|
|
|
|
// Get the RequieredNodes |
|
var notUsesInNodes = node.getElementsByTagName("notUsesIn"); |
|
var notUsesIn; |
|
$A(notUsesInNodes).each(function(not) { |
|
var namespace = $A(not.attributes).find(function(attr){ return attr.name == "namespace"}) |
|
if( namespace && namespace.nodeValue ){ |
|
if( !notUsesIn ){ |
|
notUsesIn = {namespaces:[]} |
|
} |
|
|
|
notUsesIn.namespaces.push(namespace.nodeValue) |
|
} |
|
}); |
|
|
|
// Set Requires to the Plugin-Data, if there is one |
|
if( notUsesIn ){ |
|
pluginData['notUsesIn'] = notUsesIn; |
|
} |
|
|
|
|
|
var url = ORYX.PATH + ORYX.CONFIG.PLUGINS_FOLDER + pluginData['source']; |
|
|
|
ORYX.Log.debug("Requireing '%0'", url); |
|
|
|
// Add the Script-Tag to the Site |
|
//Kickstart.require(url); |
|
|
|
ORYX.Log.info("Plugin '%0' successfully loaded.", pluginData['name']); |
|
|
|
// Add the Plugin-Data to all available Plugins |
|
ORYX.availablePlugins.push(pluginData); |
|
|
|
}); |
|
|
|
}, |
|
onFailure:this._loadPluginsOnFails |
|
}); |
|
|
|
}, |
|
|
|
_loadPluginsOnFails: function(result) { |
|
|
|
ORYX.Log.error("Plugin configuration file not available."); |
|
} |
|
}); |
|
|
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
|
|
/** |
|
* EditPathHandler |
|
* |
|
* Edit SVG paths' coordinates according to specified from-to movement and |
|
* horizontal and vertical scaling factors. |
|
* The resulting path's d attribute is stored in instance variable d. |
|
* |
|
* @constructor |
|
*/ |
|
ORYX.Core.SVG.EditPathHandler = Clazz.extend({ |
|
|
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.x = 0; |
|
this.y = 0; |
|
this.oldX = 0; |
|
this.oldY = 0; |
|
this.deltaWidth = 1; |
|
this.deltaHeight = 1; |
|
|
|
this.d = ""; |
|
}, |
|
|
|
/** |
|
* init |
|
* |
|
* @param {float} x Target point's x-coordinate |
|
* @param {float} y Target point's y-coordinate |
|
* @param {float} oldX Reference point's x-coordinate |
|
* @param {float} oldY Reference point's y-coordinate |
|
* @param {float} deltaWidth Horizontal scaling factor |
|
* @param {float} deltaHeight Vertical scaling factor |
|
*/ |
|
init: function(x, y, oldX, oldY, deltaWidth, deltaHeight) { |
|
this.x = x; |
|
this.y = y; |
|
this.oldX = oldX; |
|
this.oldY = oldY; |
|
this.deltaWidth = deltaWidth; |
|
this.deltaHeight = deltaHeight; |
|
|
|
this.d = ""; |
|
}, |
|
|
|
/** |
|
* editPointsAbs |
|
* |
|
* @param {Array} points Array of absolutePoints |
|
*/ |
|
editPointsAbs: function(points) { |
|
if(points instanceof Array) { |
|
var newPoints = []; |
|
var x, y; |
|
for(var i = 0; i < points.length; i++) { |
|
x = (parseFloat(points[i]) - this.oldX)*this.deltaWidth + this.x; |
|
i++; |
|
y = (parseFloat(points[i]) - this.oldY)*this.deltaHeight + this.y; |
|
newPoints.push(x); |
|
newPoints.push(y); |
|
} |
|
|
|
return newPoints; |
|
} else { |
|
//TODO error |
|
} |
|
}, |
|
|
|
/** |
|
* editPointsRel |
|
* |
|
* @param {Array} points Array of absolutePoints |
|
*/ |
|
editPointsRel: function(points) { |
|
if(points instanceof Array) { |
|
var newPoints = []; |
|
var x, y; |
|
for(var i = 0; i < points.length; i++) { |
|
x = parseFloat(points[i])*this.deltaWidth; |
|
i++; |
|
y = parseFloat(points[i])*this.deltaHeight; |
|
newPoints.push(x); |
|
newPoints.push(y); |
|
} |
|
|
|
return newPoints; |
|
} else { |
|
//TODO error |
|
} |
|
}, |
|
|
|
/** |
|
* arcAbs - A |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
var pointsAbs = this.editPointsAbs([x, y]); |
|
var pointsRel = this.editPointsRel([rx, ry]); |
|
|
|
this.d = this.d.concat(" A" + pointsRel[0] + " " + pointsRel[1] + |
|
" " + xAxisRotation + " " + largeArcFlag + |
|
" " + sweepFlag + " " + pointsAbs[0] + " " + |
|
pointsAbs[1] + " "); |
|
}, |
|
|
|
/** |
|
* arcRel - a |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
var pointsRel = this.editPointsRel([rx, ry, x, y]); |
|
|
|
this.d = this.d.concat(" a" + pointsRel[0] + " " + pointsRel[1] + |
|
" " + xAxisRotation + " " + largeArcFlag + |
|
" " + sweepFlag + " " + pointsRel[2] + " " + |
|
pointsRel[3] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoCubicAbs - C |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicAbs: function(x1, y1, x2, y2, x, y) { |
|
var pointsAbs = this.editPointsAbs([x1, y1, x2, y2, x, y]); |
|
|
|
this.d = this.d.concat(" C" + pointsAbs[0] + " " + pointsAbs[1] + |
|
" " + pointsAbs[2] + " " + pointsAbs[3] + |
|
" " + pointsAbs[4] + " " + pointsAbs[5] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoCubicRel - c |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicRel: function(x1, y1, x2, y2, x, y) { |
|
var pointsRel = this.editPointsRel([x1, y1, x2, y2, x, y]); |
|
|
|
this.d = this.d.concat(" c" + pointsRel[0] + " " + pointsRel[1] + |
|
" " + pointsRel[2] + " " + pointsRel[3] + |
|
" " + pointsRel[4] + " " + pointsRel[5] + " "); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalAbs - H |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalAbs: function(x) { |
|
var pointsAbs = this.editPointsAbs([x, 0]); |
|
|
|
this.d = this.d.concat(" H" + pointsAbs[0] + " "); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalRel - h |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalRel: function(x) { |
|
var pointsRel = this.editPointsRel([x, 0]); |
|
|
|
this.d = this.d.concat(" h" + pointsRel[0] + " "); |
|
}, |
|
|
|
/** |
|
* linetoAbs - L |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoAbs: function(x, y) { |
|
var pointsAbs = this.editPointsAbs([x, y]); |
|
|
|
this.d = this.d.concat(" L" + pointsAbs[0] + " " + pointsAbs[1] + " "); |
|
}, |
|
|
|
/** |
|
* linetoRel - l |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoRel: function(x, y) { |
|
var pointsRel = this.editPointsRel([x, y]); |
|
|
|
this.d = this.d.concat(" l" + pointsRel[0] + " " + pointsRel[1] + " "); |
|
}, |
|
|
|
/** |
|
* movetoAbs - M |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoAbs: function(x, y) { |
|
var pointsAbs = this.editPointsAbs([x, y]); |
|
|
|
this.d = this.d.concat(" M" + pointsAbs[0] + " " + pointsAbs[1] + " "); |
|
}, |
|
|
|
/** |
|
* movetoRel - m |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoRel: function(x, y) { |
|
var pointsRel; |
|
if(this.d === "") { |
|
pointsRel = this.editPointsAbs([x, y]); |
|
} else { |
|
pointsRel = this.editPointsRel([x, y]); |
|
} |
|
|
|
this.d = this.d.concat(" m" + pointsRel[0] + " " + pointsRel[1] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticAbs - Q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticAbs: function(x1, y1, x, y) { |
|
var pointsAbs = this.editPointsAbs([x1, y1, x, y]); |
|
|
|
this.d = this.d.concat(" Q" + pointsAbs[0] + " " + pointsAbs[1] + " " + |
|
pointsAbs[2] + " " + pointsAbs[3] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticRel - q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticRel: function(x1, y1, x, y) { |
|
var pointsRel = this.editPointsRel([x1, y1, x, y]); |
|
|
|
this.d = this.d.concat(" q" + pointsRel[0] + " " + pointsRel[1] + " " + |
|
pointsRel[2] + " " + pointsRel[3] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothAbs - S |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothAbs: function(x2, y2, x, y) { |
|
var pointsAbs = this.editPointsAbs([x2, y2, x, y]); |
|
|
|
this.d = this.d.concat(" S" + pointsAbs[0] + " " + pointsAbs[1] + " " + |
|
pointsAbs[2] + " " + pointsAbs[3] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothRel - s |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothRel: function(x2, y2, x, y) { |
|
var pointsRel = this.editPointsRel([x2, y2, x, y]); |
|
|
|
this.d = this.d.concat(" s" + pointsRel[0] + " " + pointsRel[1] + " " + |
|
pointsRel[2] + " " + pointsRel[3] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothAbs - T |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothAbs: function(x, y) { |
|
var pointsAbs = this.editPointsAbs([x, y]); |
|
|
|
this.d = this.d.concat(" T" + pointsAbs[0] + " " + pointsAbs[1] + " "); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothRel - t |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothRel: function(x, y) { |
|
var pointsRel = this.editPointsRel([x, y]); |
|
|
|
this.d = this.d.concat(" t" + pointsRel[0] + " " + pointsRel[1] + " "); |
|
}, |
|
|
|
/** |
|
* linetoVerticalAbs - V |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalAbs: function(y) { |
|
var pointsAbs = this.editPointsAbs([0, y]); |
|
|
|
this.d = this.d.concat(" V" + pointsAbs[1] + " "); |
|
}, |
|
|
|
/** |
|
* linetoVerticalRel - v |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalRel: function(y) { |
|
var pointsRel = this.editPointsRel([0, y]); |
|
|
|
this.d = this.d.concat(" v" + pointsRel[1] + " "); |
|
}, |
|
|
|
/** |
|
* closePath - z or Z |
|
*/ |
|
closePath: function() { |
|
this.d = this.d.concat(" z"); |
|
} |
|
|
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
|
|
/** |
|
* MinMaxPathHandler |
|
* |
|
* Determine the minimum and maximum of a SVG path's absolute coordinates. |
|
* For relative coordinates the absolute value is computed for consideration. |
|
* The values are stored in attributes minX, minY, maxX, and maxY. |
|
* |
|
* @constructor |
|
*/ |
|
ORYX.Core.SVG.MinMaxPathHandler = Clazz.extend({ |
|
|
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.minX = undefined; |
|
this.minY = undefined; |
|
this.maxX = undefined; |
|
this.maxY = undefined; |
|
|
|
this._lastAbsX = undefined; |
|
this._lastAbsY = undefined; |
|
}, |
|
|
|
/** |
|
* Store minimal and maximal coordinates of passed points to attributes minX, maxX, minY, maxY |
|
* |
|
* @param {Array} points Array of absolutePoints |
|
*/ |
|
calculateMinMax: function(points) { |
|
if(points instanceof Array) { |
|
var x, y; |
|
for(var i = 0; i < points.length; i++) { |
|
x = parseFloat(points[i]); |
|
i++; |
|
y = parseFloat(points[i]); |
|
|
|
this.minX = (this.minX !== undefined) ? Math.min(this.minX, x) : x; |
|
this.maxX = (this.maxX !== undefined) ? Math.max(this.maxX, x) : x; |
|
this.minY = (this.minY !== undefined) ? Math.min(this.minY, y) : y; |
|
this.maxY = (this.maxY !== undefined) ? Math.max(this.maxY, y) : y; |
|
|
|
this._lastAbsX = x; |
|
this._lastAbsY = y; |
|
} |
|
} else { |
|
//TODO error |
|
} |
|
}, |
|
|
|
/** |
|
* arcAbs - A |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
this.calculateMinMax([x, y]); |
|
}, |
|
|
|
/** |
|
* arcRel - a |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicAbs - C |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicAbs: function(x1, y1, x2, y2, x, y) { |
|
this.calculateMinMax([x1, y1, x2, y2, x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicRel - c |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicRel: function(x1, y1, x2, y2, x, y) { |
|
this.calculateMinMax([this._lastAbsX + x1, this._lastAbsY + y1, |
|
this._lastAbsX + x2, this._lastAbsY + y2, |
|
this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalAbs - H |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalAbs: function(x) { |
|
this.calculateMinMax([x, this._lastAbsY]); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalRel - h |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalRel: function(x) { |
|
this.calculateMinMax([this._lastAbsX + x, this._lastAbsY]); |
|
}, |
|
|
|
/** |
|
* linetoAbs - L |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoAbs: function(x, y) { |
|
this.calculateMinMax([x, y]); |
|
}, |
|
|
|
/** |
|
* linetoRel - l |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoRel: function(x, y) { |
|
this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* movetoAbs - M |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoAbs: function(x, y) { |
|
this.calculateMinMax([x, y]); |
|
}, |
|
|
|
/** |
|
* movetoRel - m |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoRel: function(x, y) { |
|
if(this._lastAbsX && this._lastAbsY) { |
|
this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]); |
|
} else { |
|
this.calculateMinMax([x, y]); |
|
} |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticAbs - Q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticAbs: function(x1, y1, x, y) { |
|
this.calculateMinMax([x1, y1, x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticRel - q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticRel: function(x1, y1, x, y) { |
|
this.calculateMinMax([this._lastAbsX + x1, this._lastAbsY + y1, this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothAbs - S |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothAbs: function(x2, y2, x, y) { |
|
this.calculateMinMax([x2, y2, x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothRel - s |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothRel: function(x2, y2, x, y) { |
|
this.calculateMinMax([this._lastAbsX + x2, this._lastAbsY + y2, this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothAbs - T |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothAbs: function(x, y) { |
|
this.calculateMinMax([x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothRel - t |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothRel: function(x, y) { |
|
this.calculateMinMax([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* linetoVerticalAbs - V |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalAbs: function(y) { |
|
this.calculateMinMax([this._lastAbsX, y]); |
|
}, |
|
|
|
/** |
|
* linetoVerticalRel - v |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalRel: function(y) { |
|
this.calculateMinMax([this._lastAbsX, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* closePath - z or Z |
|
*/ |
|
closePath: function() { |
|
return;// do nothing |
|
} |
|
|
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
|
|
/** |
|
* PathHandler |
|
* |
|
* Determine absolute points of a SVG path. The coordinates are stored |
|
* sequentially in the attribute points (x-coordinates at even indices, |
|
* y-coordinates at odd indices). |
|
* |
|
* @constructor |
|
*/ |
|
ORYX.Core.SVG.PointsPathHandler = Clazz.extend({ |
|
|
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.points = []; |
|
|
|
this._lastAbsX = undefined; |
|
this._lastAbsY = undefined; |
|
}, |
|
|
|
/** |
|
* addPoints |
|
* |
|
* @param {Array} points Array of absolutePoints |
|
*/ |
|
addPoints: function(points) { |
|
if(points instanceof Array) { |
|
var x, y; |
|
for(var i = 0; i < points.length; i++) { |
|
x = parseFloat(points[i]); |
|
i++; |
|
y = parseFloat(points[i]); |
|
|
|
this.points.push(x); |
|
this.points.push(y); |
|
//this.points.push({x:x, y:y}); |
|
|
|
this._lastAbsX = x; |
|
this._lastAbsY = y; |
|
} |
|
} else { |
|
//TODO error |
|
} |
|
}, |
|
|
|
/** |
|
* arcAbs - A |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcAbs: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* arcRel - a |
|
* |
|
* @param {Number} rx |
|
* @param {Number} ry |
|
* @param {Number} xAxisRotation |
|
* @param {Boolean} largeArcFlag |
|
* @param {Boolean} sweepFlag |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
arcRel: function(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicAbs - C |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicAbs: function(x1, y1, x2, y2, x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicRel - c |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicRel: function(x1, y1, x2, y2, x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalAbs - H |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalAbs: function(x) { |
|
this.addPoints([x, this._lastAbsY]); |
|
}, |
|
|
|
/** |
|
* linetoHorizontalRel - h |
|
* |
|
* @param {Number} x |
|
*/ |
|
linetoHorizontalRel: function(x) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY]); |
|
}, |
|
|
|
/** |
|
* linetoAbs - L |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoAbs: function(x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* linetoRel - l |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
linetoRel: function(x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* movetoAbs - M |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoAbs: function(x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* movetoRel - m |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
movetoRel: function(x, y) { |
|
if(this._lastAbsX && this._lastAbsY) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
} else { |
|
this.addPoints([x, y]); |
|
} |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticAbs - Q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticAbs: function(x1, y1, x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticRel - q |
|
* |
|
* @param {Number} x1 |
|
* @param {Number} y1 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticRel: function(x1, y1, x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothAbs - S |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothAbs: function(x2, y2, x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoCubicSmoothRel - s |
|
* |
|
* @param {Number} x2 |
|
* @param {Number} y2 |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoCubicSmoothRel: function(x2, y2, x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothAbs - T |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothAbs: function(x, y) { |
|
this.addPoints([x, y]); |
|
}, |
|
|
|
/** |
|
* curvetoQuadraticSmoothRel - t |
|
* |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
curvetoQuadraticSmoothRel: function(x, y) { |
|
this.addPoints([this._lastAbsX + x, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* linetoVerticalAbs - V |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalAbs: function(y) { |
|
this.addPoints([this._lastAbsX, y]); |
|
}, |
|
|
|
/** |
|
* linetoVerticalRel - v |
|
* |
|
* @param {Number} y |
|
*/ |
|
linetoVerticalRel: function(y) { |
|
this.addPoints([this._lastAbsX, this._lastAbsY + y]); |
|
}, |
|
|
|
/** |
|
* closePath - z or Z |
|
*/ |
|
closePath: function() { |
|
return;// do nothing |
|
} |
|
|
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* |
|
* Config variables |
|
*/ |
|
NAMESPACE_ORYX = "http://www.b3mn.org/oryx"; |
|
NAMESPACE_SVG = "http://www.w3.org/2000/svg/"; |
|
|
|
/** |
|
* @classDescription This class wraps the manipulation of a SVG marker. |
|
* @namespace ORYX.Core.SVG |
|
* uses Inheritance (Clazz) |
|
* uses Prototype 1.5.0 |
|
* |
|
*/ |
|
|
|
/** |
|
* Init package |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
ORYX.Core.SVG.SVGMarker = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param markerElement {SVGMarkerElement} |
|
*/ |
|
construct: function(markerElement) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.id = undefined; |
|
this.element = markerElement; |
|
this.refX = undefined; |
|
this.refY = undefined; |
|
this.markerWidth = undefined; |
|
this.markerHeight = undefined; |
|
this.oldRefX = undefined; |
|
this.oldRefY = undefined; |
|
this.oldMarkerWidth = undefined; |
|
this.oldMarkerHeight = undefined; |
|
this.optional = false; |
|
this.enabled = true; |
|
this.minimumLength = undefined; |
|
this.resize = false; |
|
|
|
this.svgShapes = []; |
|
|
|
this._init(); //initialisation of all the properties declared above. |
|
}, |
|
|
|
/** |
|
* Initializes the values that are defined in the constructor. |
|
*/ |
|
_init: function() { |
|
//check if this.element is a SVGMarkerElement |
|
if(!( this.element == "[object SVGMarkerElement]")) { |
|
throw "SVGMarker: Argument is not an instance of SVGMarkerElement."; |
|
} |
|
|
|
this.id = this.element.getAttributeNS(null, "id"); |
|
|
|
//init svg marker attributes |
|
var refXValue = this.element.getAttributeNS(null, "refX"); |
|
if(refXValue) { |
|
this.refX = parseFloat(refXValue); |
|
} else { |
|
this.refX = 0; |
|
} |
|
var refYValue = this.element.getAttributeNS(null, "refY"); |
|
if(refYValue) { |
|
this.refY = parseFloat(refYValue); |
|
} else { |
|
this.refY = 0; |
|
} |
|
var markerWidthValue = this.element.getAttributeNS(null, "markerWidth"); |
|
if(markerWidthValue) { |
|
this.markerWidth = parseFloat(markerWidthValue); |
|
} else { |
|
this.markerWidth = 3; |
|
} |
|
var markerHeightValue = this.element.getAttributeNS(null, "markerHeight"); |
|
if(markerHeightValue) { |
|
this.markerHeight = parseFloat(markerHeightValue); |
|
} else { |
|
this.markerHeight = 3; |
|
} |
|
|
|
this.oldRefX = this.refX; |
|
this.oldRefY = this.refY; |
|
this.oldMarkerWidth = this.markerWidth; |
|
this.oldMarkerHeight = this.markerHeight; |
|
|
|
//init oryx attributes |
|
var optionalAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "optional"); |
|
if(optionalAttr) { |
|
optionalAttr = optionalAttr.strip(); |
|
this.optional = (optionalAttr.toLowerCase() === "yes"); |
|
} else { |
|
this.optional = false; |
|
} |
|
|
|
var enabledAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "enabled"); |
|
if(enabledAttr) { |
|
enabledAttr = enabledAttr.strip(); |
|
this.enabled = !(enabledAttr.toLowerCase() === "no"); |
|
} else { |
|
this.enabled = true; |
|
} |
|
|
|
var minLengthAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "minimumLength"); |
|
if(minLengthAttr) { |
|
this.minimumLength = parseFloat(minLengthAttr); |
|
} |
|
|
|
var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "resize"); |
|
if(resizeAttr) { |
|
resizeAttr = resizeAttr.strip(); |
|
this.resize = (resizeAttr.toLowerCase() === "yes"); |
|
} else { |
|
this.resize = false; |
|
} |
|
|
|
//init SVGShape objects |
|
//this.svgShapes = this._getSVGShapes(this.element); |
|
}, |
|
|
|
/** |
|
* |
|
*/ |
|
_getSVGShapes: function(svgElement) { |
|
if(svgElement.hasChildNodes) { |
|
var svgShapes = []; |
|
var me = this; |
|
$A(svgElement.childNodes).each(function(svgChild) { |
|
try { |
|
var svgShape = new ORYX.Core.SVG.SVGShape(svgChild); |
|
svgShapes.push(svgShape); |
|
} catch (e) { |
|
svgShapes = svgShapes.concat(me._getSVGShapes(svgChild)); |
|
} |
|
}); |
|
return svgShapes; |
|
} |
|
}, |
|
|
|
/** |
|
* Writes the changed values into the SVG marker. |
|
*/ |
|
update: function() { |
|
//TODO mache marker resizebar!!! aber erst wenn der rest der connectingshape funzt! |
|
|
|
// //update marker attributes |
|
// if(this.refX != this.oldRefX) { |
|
// this.element.setAttributeNS(null, "refX", this.refX); |
|
// } |
|
// if(this.refY != this.oldRefY) { |
|
// this.element.setAttributeNS(null, "refY", this.refY); |
|
// } |
|
// if(this.markerWidth != this.oldMarkerWidth) { |
|
// this.element.setAttributeNS(null, "markerWidth", this.markerWidth); |
|
// } |
|
// if(this.markerHeight != this.oldMarkerHeight) { |
|
// this.element.setAttributeNS(null, "markerHeight", this.markerHeight); |
|
// } |
|
// |
|
// //update SVGShape objects |
|
// var widthDelta = this.markerWidth / this.oldMarkerWidth; |
|
// var heightDelta = this.markerHeight / this.oldMarkerHeight; |
|
// if(widthDelta != 1 && heightDelta != 1) { |
|
// this.svgShapes.each(function(svgShape) { |
|
// |
|
// }); |
|
// } |
|
|
|
//update old values to prepare the next update |
|
this.oldRefX = this.refX; |
|
this.oldRefY = this.refY; |
|
this.oldMarkerWidth = this.markerWidth; |
|
this.oldMarkerHeight = this.markerHeight; |
|
}, |
|
|
|
toString: function() { return (this.element) ? "SVGMarker " + this.element.id : "SVGMarker " + this.element;} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* |
|
* Config variables |
|
*/ |
|
NAMESPACE_ORYX = "http://www.b3mn.org/oryx"; |
|
NAMESPACE_SVG = "http://www.w3.org/2000/svg/"; |
|
|
|
/** |
|
* @classDescription This class wraps the manipulation of a SVG basic shape or a path. |
|
* @namespace ORYX.Core.SVG |
|
* uses Inheritance (Clazz) |
|
* uses Prototype 1.5.0 |
|
* uses PathParser by Kevin Lindsey (http://kevlindev.com/) |
|
* uses MinMaxPathHandler |
|
* uses EditPathHandler |
|
* |
|
*/ |
|
|
|
//init package |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
ORYX.Core.SVG.SVGShape = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param svgElem {SVGElement} An SVGElement that is a basic shape or a path. |
|
*/ |
|
construct: function(svgElem) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.type; |
|
this.element = svgElem; |
|
this.x = undefined; |
|
this.y = undefined; |
|
this.width = undefined; |
|
this.height = undefined; |
|
this.oldX = undefined; |
|
this.oldY = undefined; |
|
this.oldWidth = undefined; |
|
this.oldHeight = undefined; |
|
this.radiusX = undefined; |
|
this.radiusY = undefined; |
|
this.isHorizontallyResizable = false; |
|
this.isVerticallyResizable = false; |
|
//this.anchors = []; |
|
this.anchorLeft = false; |
|
this.anchorRight = false; |
|
this.anchorTop = false; |
|
this.anchorBottom = false; |
|
|
|
//attributes of path elements of edge objects |
|
this.allowDockers = true; |
|
this.resizeMarkerMid = false; |
|
|
|
this.editPathParser; |
|
this.editPathHandler; |
|
|
|
this.init(); //initialisation of all the properties declared above. |
|
}, |
|
|
|
/** |
|
* Initializes the values that are defined in the constructor. |
|
*/ |
|
init: function() { |
|
|
|
/**initialize position and size*/ |
|
if(ORYX.Editor.checkClassType(this.element, SVGRectElement) || ORYX.Editor.checkClassType(this.element, SVGImageElement)) { |
|
this.type = "Rect"; |
|
|
|
var xAttr = this.element.getAttributeNS(null, "x"); |
|
if(xAttr) { |
|
this.oldX = parseFloat(xAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var yAttr = this.element.getAttributeNS(null, "y"); |
|
if(yAttr) { |
|
this.oldY = parseFloat(yAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var widthAttr = this.element.getAttributeNS(null, "width"); |
|
if(widthAttr) { |
|
this.oldWidth = parseFloat(widthAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var heightAttr = this.element.getAttributeNS(null, "height"); |
|
if(heightAttr) { |
|
this.oldHeight = parseFloat(heightAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
|
|
} else if(ORYX.Editor.checkClassType(this.element, SVGCircleElement)) { |
|
this.type = "Circle"; |
|
|
|
var cx = undefined; |
|
var cy = undefined; |
|
//var r = undefined; |
|
|
|
var cxAttr = this.element.getAttributeNS(null, "cx"); |
|
if(cxAttr) { |
|
cx = parseFloat(cxAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var cyAttr = this.element.getAttributeNS(null, "cy"); |
|
if(cyAttr) { |
|
cy = parseFloat(cyAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var rAttr = this.element.getAttributeNS(null, "r"); |
|
if(rAttr) { |
|
//r = parseFloat(rAttr); |
|
this.radiusX = parseFloat(rAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
this.oldX = cx - this.radiusX; |
|
this.oldY = cy - this.radiusX; |
|
this.oldWidth = 2*this.radiusX; |
|
this.oldHeight = 2*this.radiusX; |
|
|
|
} else if(ORYX.Editor.checkClassType(this.element, SVGEllipseElement)) { |
|
this.type = "Ellipse"; |
|
|
|
var cx = undefined; |
|
var cy = undefined; |
|
//var rx = undefined; |
|
//var ry = undefined; |
|
var cxAttr = this.element.getAttributeNS(null, "cx"); |
|
if(cxAttr) { |
|
cx = parseFloat(cxAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var cyAttr = this.element.getAttributeNS(null, "cy"); |
|
if(cyAttr) { |
|
cy = parseFloat(cyAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var rxAttr = this.element.getAttributeNS(null, "rx"); |
|
if(rxAttr) { |
|
this.radiusX = parseFloat(rxAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var ryAttr = this.element.getAttributeNS(null, "ry"); |
|
if(ryAttr) { |
|
this.radiusY = parseFloat(ryAttr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
this.oldX = cx - this.radiusX; |
|
this.oldY = cy - this.radiusY; |
|
this.oldWidth = 2*this.radiusX; |
|
this.oldHeight = 2*this.radiusY; |
|
|
|
} else if(ORYX.Editor.checkClassType(this.element, SVGLineElement)) { |
|
this.type = "Line"; |
|
|
|
var x1 = undefined; |
|
var y1 = undefined; |
|
var x2 = undefined; |
|
var y2 = undefined; |
|
var x1Attr = this.element.getAttributeNS(null, "x1"); |
|
if(x1Attr) { |
|
x1 = parseFloat(x1Attr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var y1Attr = this.element.getAttributeNS(null, "y1"); |
|
if(y1Attr) { |
|
y1 = parseFloat(y1Attr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var x2Attr = this.element.getAttributeNS(null, "x2"); |
|
if(x2Attr) { |
|
x2 = parseFloat(x2Attr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
var y2Attr = this.element.getAttributeNS(null, "y2"); |
|
if(y2Attr) { |
|
y2 = parseFloat(y2Attr); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
this.oldX = Math.min(x1,x2); |
|
this.oldY = Math.min(y1,y2); |
|
this.oldWidth = Math.abs(x1-x2); |
|
this.oldHeight = Math.abs(y1-y2); |
|
|
|
} else if(ORYX.Editor.checkClassType(this.element, SVGPolylineElement) || ORYX.Editor.checkClassType(this.element, SVGPolygonElement)) { |
|
this.type = "Polyline"; |
|
|
|
var pointsArray = []; |
|
if (this.element.points&&this.element.points.numberOfItems){ |
|
for(var i=0, size=this.element.points.numberOfItems; i<size; i++){ |
|
pointsArray.push(this.element.points.getItem(i).x) |
|
pointsArray.push(this.element.points.getItem(i).y) |
|
} |
|
} else { |
|
var points = this.element.getAttributeNS(null, "points"); |
|
if(points) { |
|
points = points.replace(/,/g , " "); |
|
pointsArray = points.split(" "); |
|
pointsArray = pointsArray.without(""); |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
} |
|
|
|
|
|
if(pointsArray && pointsArray.length && pointsArray.length > 1) { |
|
var minX = parseFloat(pointsArray[0]); |
|
var minY = parseFloat(pointsArray[1]); |
|
var maxX = parseFloat(pointsArray[0]); |
|
var maxY = parseFloat(pointsArray[1]); |
|
|
|
for(var i = 0; i < pointsArray.length; i++) { |
|
minX = Math.min(minX, parseFloat(pointsArray[i])); |
|
maxX = Math.max(maxX, parseFloat(pointsArray[i])); |
|
i++; |
|
minY = Math.min(minY, parseFloat(pointsArray[i])); |
|
maxY = Math.max(maxY, parseFloat(pointsArray[i])); |
|
} |
|
|
|
this.oldX = minX; |
|
this.oldY = minY; |
|
this.oldWidth = maxX-minX; |
|
this.oldHeight = maxY-minY; |
|
} else { |
|
throw "Missing attribute in element " + this.element; |
|
} |
|
|
|
} else if(ORYX.Editor.checkClassType(this.element, SVGPathElement)) { |
|
this.type = "Path"; |
|
|
|
this.editPathParser = new PathParser(); |
|
this.editPathHandler = new ORYX.Core.SVG.EditPathHandler(); |
|
this.editPathParser.setHandler(this.editPathHandler); |
|
|
|
var parser = new PathParser(); |
|
var handler = new ORYX.Core.SVG.MinMaxPathHandler(); |
|
parser.setHandler(handler); |
|
parser.parsePath(this.element); |
|
|
|
this.oldX = handler.minX; |
|
this.oldY = handler.minY; |
|
this.oldWidth = handler.maxX - handler.minX; |
|
this.oldHeight = handler.maxY - handler.minY; |
|
|
|
delete parser; |
|
delete handler; |
|
} else { |
|
throw "Element is not a shape."; |
|
} |
|
|
|
/** initialize attributes of oryx namespace */ |
|
//resize |
|
var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "resize"); |
|
if(resizeAttr) { |
|
resizeAttr = resizeAttr.toLowerCase(); |
|
if(resizeAttr.match(/horizontal/)) { |
|
this.isHorizontallyResizable = true; |
|
} else { |
|
this.isHorizontallyResizable = false; |
|
} |
|
if(resizeAttr.match(/vertical/)) { |
|
this.isVerticallyResizable = true; |
|
} else { |
|
this.isVerticallyResizable = false; |
|
} |
|
} else { |
|
this.isHorizontallyResizable = false; |
|
this.isVerticallyResizable = false; |
|
} |
|
|
|
//anchors |
|
var anchorAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "anchors"); |
|
if(anchorAttr) { |
|
anchorAttr = anchorAttr.replace("/,/g", " "); |
|
var anchors = anchorAttr.split(" ").without(""); |
|
|
|
for(var i = 0; i < anchors.length; i++) { |
|
switch(anchors[i].toLowerCase()) { |
|
case "left": |
|
this.anchorLeft = true; |
|
break; |
|
case "right": |
|
this.anchorRight = true; |
|
break; |
|
case "top": |
|
this.anchorTop = true; |
|
break; |
|
case "bottom": |
|
this.anchorBottom = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//allowDockers and resizeMarkerMid |
|
if(ORYX.Editor.checkClassType(this.element, SVGPathElement)) { |
|
var allowDockersAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "allowDockers"); |
|
if(allowDockersAttr) { |
|
if(allowDockersAttr.toLowerCase() === "no") { |
|
this.allowDockers = false; |
|
} else { |
|
this.allowDockers = true; |
|
} |
|
} |
|
|
|
var resizeMarkerMidAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "resizeMarker-mid"); |
|
if(resizeMarkerMidAttr) { |
|
if(resizeMarkerMidAttr.toLowerCase() === "yes") { |
|
this.resizeMarkerMid = true; |
|
} else { |
|
this.resizeMarkerMid = false; |
|
} |
|
} |
|
} |
|
|
|
this.x = this.oldX; |
|
this.y = this.oldY; |
|
this.width = this.oldWidth; |
|
this.height = this.oldHeight; |
|
}, |
|
|
|
/** |
|
* Writes the changed values into the SVG element. |
|
*/ |
|
update: function() { |
|
|
|
if(this.x !== this.oldX || this.y !== this.oldY || this.width !== this.oldWidth || this.height !== this.oldHeight) { |
|
switch(this.type) { |
|
case "Rect": |
|
if(this.x !== this.oldX) this.element.setAttributeNS(null, "x", this.x); |
|
if(this.y !== this.oldY) this.element.setAttributeNS(null, "y", this.y); |
|
if(this.width !== this.oldWidth) this.element.setAttributeNS(null, "width", this.width); |
|
if(this.height !== this.oldHeight) this.element.setAttributeNS(null, "height", this.height); |
|
break; |
|
case "Circle": |
|
//calculate the radius |
|
//var r; |
|
// if(this.width/this.oldWidth <= this.height/this.oldHeight) { |
|
// this.radiusX = ((this.width > this.height) ? this.width : this.height)/2.0; |
|
// } else { |
|
this.radiusX = ((this.width < this.height) ? this.width : this.height)/2.0; |
|
//} |
|
|
|
this.element.setAttributeNS(null, "cx", this.x + this.width/2.0); |
|
this.element.setAttributeNS(null, "cy", this.y + this.height/2.0); |
|
this.element.setAttributeNS(null, "r", this.radiusX); |
|
break; |
|
case "Ellipse": |
|
this.radiusX = this.width/2; |
|
this.radiusY = this.height/2; |
|
|
|
this.element.setAttributeNS(null, "cx", this.x + this.radiusX); |
|
this.element.setAttributeNS(null, "cy", this.y + this.radiusY); |
|
this.element.setAttributeNS(null, "rx", this.radiusX); |
|
this.element.setAttributeNS(null, "ry", this.radiusY); |
|
break; |
|
case "Line": |
|
if(this.x !== this.oldX) |
|
this.element.setAttributeNS(null, "x1", this.x); |
|
|
|
if(this.y !== this.oldY) |
|
this.element.setAttributeNS(null, "y1", this.y); |
|
|
|
if(this.x !== this.oldX || this.width !== this.oldWidth) |
|
this.element.setAttributeNS(null, "x2", this.x + this.width); |
|
|
|
if(this.y !== this.oldY || this.height !== this.oldHeight) |
|
this.element.setAttributeNS(null, "y2", this.y + this.height); |
|
break; |
|
case "Polyline": |
|
var points = this.element.getAttributeNS(null, "points"); |
|
if(points) { |
|
points = points.replace(/,/g, " ").split(" ").without(""); |
|
|
|
if(points && points.length && points.length > 1) { |
|
|
|
//TODO what if oldWidth == 0? |
|
var widthDelta = (this.oldWidth === 0) ? 0 : this.width / this.oldWidth; |
|
var heightDelta = (this.oldHeight === 0) ? 0 : this.height / this.oldHeight; |
|
|
|
var updatedPoints = ""; |
|
for(var i = 0; i < points.length; i++) { |
|
var x = (parseFloat(points[i])-this.oldX)*widthDelta + this.x; |
|
i++; |
|
var y = (parseFloat(points[i])-this.oldY)*heightDelta + this.y; |
|
updatedPoints += x + " " + y + " "; |
|
} |
|
this.element.setAttributeNS(null, "points", updatedPoints); |
|
} else { |
|
//TODO error |
|
} |
|
} else { |
|
//TODO error |
|
} |
|
break; |
|
case "Path": |
|
//calculate scaling delta |
|
//TODO what if oldWidth == 0? |
|
var widthDelta = (this.oldWidth === 0) ? 0 : this.width / this.oldWidth; |
|
var heightDelta = (this.oldHeight === 0) ? 0 : this.height / this.oldHeight; |
|
|
|
//use path parser to edit each point of the path |
|
this.editPathHandler.init(this.x, this.y, this.oldX, this.oldY, widthDelta, heightDelta); |
|
this.editPathParser.parsePath(this.element); |
|
|
|
//change d attribute of path |
|
this.element.setAttributeNS(null, "d", this.editPathHandler.d); |
|
break; |
|
} |
|
|
|
this.oldX = this.x; |
|
this.oldY = this.y; |
|
this.oldWidth = this.width; |
|
this.oldHeight = this.height; |
|
} |
|
|
|
// Remove cached variables |
|
delete this.visible; |
|
delete this.handler; |
|
}, |
|
|
|
isPointIncluded: function(pointX, pointY) { |
|
|
|
// Check if there are the right arguments and if the node is visible |
|
if(!pointX || !pointY || !this.isVisible()) { |
|
return false; |
|
} |
|
|
|
switch(this.type) { |
|
case "Rect": |
|
return (pointX >= this.x && pointX <= this.x + this.width && |
|
pointY >= this.y && pointY <= this.y+this.height); |
|
break; |
|
case "Circle": |
|
//calculate the radius |
|
// var r; |
|
// if(this.width/this.oldWidth <= this.height/this.oldHeight) { |
|
// r = ((this.width > this.height) ? this.width : this.height)/2.0; |
|
// } else { |
|
// r = ((this.width < this.height) ? this.width : this.height)/2.0; |
|
// } |
|
return ORYX.Core.Math.isPointInEllipse(pointX, pointY, this.x + this.width/2.0, this.y + this.height/2.0, this.radiusX, this.radiusX); |
|
break; |
|
case "Ellipse": |
|
return ORYX.Core.Math.isPointInEllipse(pointX, pointY, this.x + this.radiusX, this.y + this.radiusY, this.radiusX, this.radiusY); |
|
break; |
|
case "Line": |
|
return ORYX.Core.Math.isPointInLine(pointX, pointY, this.x, this.y, this.x + this.width, this.y + this.height); |
|
break; |
|
case "Polyline": |
|
var points = this.element.getAttributeNS(null, "points"); |
|
|
|
if(points) { |
|
points = points.replace(/,/g , " ").split(" ").without(""); |
|
|
|
points = points.collect(function(n) { |
|
return parseFloat(n); |
|
}); |
|
|
|
return ORYX.Core.Math.isPointInPolygone(pointX, pointY, points); |
|
} else { |
|
return false; |
|
} |
|
break; |
|
case "Path": |
|
|
|
// Cache Path handler |
|
if (!this.handler) { |
|
var parser = new PathParser(); |
|
this.handler = new ORYX.Core.SVG.PointsPathHandler(); |
|
parser.setHandler(this.handler); |
|
parser.parsePath(this.element); |
|
} |
|
|
|
return ORYX.Core.Math.isPointInPolygone(pointX, pointY, this.handler.points); |
|
|
|
break; |
|
default: |
|
return false; |
|
} |
|
}, |
|
|
|
/** |
|
* Returns true if the element is visible |
|
* @param {SVGElement} elem |
|
* @return boolean |
|
*/ |
|
isVisible: function(elem) { |
|
|
|
if (this.visible !== undefined){ |
|
return this.visible; |
|
} |
|
|
|
if (!elem) { |
|
elem = this.element; |
|
} |
|
|
|
var hasOwnerSVG = false; |
|
try { |
|
hasOwnerSVG = !!elem.ownerSVGElement; |
|
} catch(e){} |
|
|
|
// Is SVG context |
|
if ( hasOwnerSVG ) { |
|
// IF G-Element |
|
if (ORYX.Editor.checkClassType(elem, SVGGElement)) { |
|
if (elem.className && elem.className.baseVal == "me") { |
|
this.visible = true; |
|
return this.visible; |
|
} |
|
} |
|
|
|
// Check if fill or stroke is set |
|
var fill = elem.getAttributeNS(null, "fill"); |
|
var stroke = elem.getAttributeNS(null, "stroke"); |
|
if (fill && fill == "none" && stroke && stroke == "none") { |
|
this.visible = false; |
|
} else { |
|
// Check if displayed |
|
var attr = elem.getAttributeNS(null, "display"); |
|
if(!attr) |
|
this.visible = this.isVisible(elem.parentNode); |
|
else if (attr == "none") |
|
this.visible = false; |
|
else |
|
this.visible = true; |
|
} |
|
} else { |
|
this.visible = true; |
|
} |
|
|
|
return this.visible; |
|
}, |
|
|
|
toString: function() { return (this.element) ? "SVGShape " + this.element.id : "SVGShape " + this.element;} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.SVG) {ORYX.Core.SVG = {};} |
|
|
|
/** |
|
* @classDescription Class for adding text to a shape. |
|
* |
|
*/ |
|
ORYX.Core.SVG.Label = Clazz.extend({ |
|
|
|
_characterSets:[ |
|
"%W", |
|
"@", |
|
"m", |
|
"wDGMOQ?????#+=<>~^", |
|
"ABCHKNRSUVXZ??????????&", |
|
"bdghnopqux???????????ETY1234567890?????_????${}*????`???????????", |
|
"aeksvyz?????FLP????????????????", |
|
"c-", |
|
"rtJ\"/()[]:;!|\\", |
|
"fjI., ", |
|
"'", |
|
"il" |
|
], |
|
_characterSetValues:[15,14,13,11,10,9,8,7,6,5,4,3], |
|
|
|
/** |
|
* Constructor |
|
* @param options {Object} : |
|
* textElement |
|
* |
|
*/ |
|
construct: function(options) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
if(!options.textElement) { |
|
throw "Label: No parameter textElement." |
|
} else if (!ORYX.Editor.checkClassType( options.textElement, SVGTextElement ) ) { |
|
throw "Label: Parameter textElement is not an SVGTextElement." |
|
} |
|
|
|
this.invisibleRenderPoint = -5000; |
|
|
|
this.node = options.textElement; |
|
|
|
|
|
this.node.setAttributeNS(null, 'stroke-width', '0pt'); |
|
this.node.setAttributeNS(null, 'letter-spacing', '-0.01px'); |
|
|
|
this.shapeId = options.shapeId; |
|
|
|
this.id; |
|
|
|
this.fitToElemId; |
|
|
|
this.edgePosition; |
|
|
|
this.x; |
|
this.y; |
|
this.oldX; |
|
this.oldY; |
|
|
|
this.isVisible = true; |
|
|
|
this._text; |
|
this._verticalAlign; |
|
this._horizontalAlign; |
|
this._rotate; |
|
this._rotationPoint; |
|
|
|
//this.anchors = []; |
|
this.anchorLeft; |
|
this.anchorRight; |
|
this.anchorTop; |
|
this.anchorBottom; |
|
|
|
this._isChanged = true; |
|
|
|
//if the text element already has an id, don't change it. |
|
var _id = this.node.getAttributeNS(null, 'id'); |
|
if(_id) { |
|
this.id = _id; |
|
} |
|
|
|
//initialization |
|
|
|
//set referenced element the text is fit to |
|
this.fitToElemId = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'fittoelem'); |
|
if(this.fitToElemId) |
|
this.fitToElemId = this.shapeId + this.fitToElemId; |
|
|
|
//set alignment |
|
var alignValues = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align'); |
|
if(alignValues) { |
|
alignValues = alignValues.replace(/,/g, " "); |
|
alignValues = alignValues.split(" "); |
|
alignValues = alignValues.without(""); |
|
|
|
alignValues.each((function(alignValue) { |
|
switch (alignValue) { |
|
case 'top': |
|
case 'middle': |
|
case 'bottom': |
|
if(!this._verticalAlign){this._originVerticalAlign = this._verticalAlign = alignValue;} |
|
break; |
|
case 'left': |
|
case 'center': |
|
case 'right': |
|
if(!this._horizontalAlign){this._originHorizontalAlign = this._horizontalAlign = alignValue;} |
|
break; |
|
} |
|
}).bind(this)); |
|
} |
|
|
|
//set edge position (only in case the label belongs to an edge) |
|
this.edgePosition = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'edgePosition'); |
|
if(this.edgePosition) { |
|
this.originEdgePosition = this.edgePosition = this.edgePosition.toLowerCase(); |
|
} |
|
|
|
|
|
//get offset top |
|
this.offsetTop = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetTop') || ORYX.CONFIG.OFFSET_EDGE_LABEL_TOP; |
|
if(this.offsetTop) { |
|
this.offsetTop = parseInt(this.offsetTop); |
|
} |
|
|
|
//get offset top |
|
this.offsetBottom = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'offsetBottom') || ORYX.CONFIG.OFFSET_EDGE_LABEL_BOTTOM; |
|
if(this.offsetBottom) { |
|
this.offsetBottom = parseInt(this.offsetBottom); |
|
} |
|
|
|
|
|
//set rotation |
|
var rotateValue = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'rotate'); |
|
if(rotateValue) { |
|
try { |
|
this._rotate = parseFloat(rotateValue); |
|
} catch (e) { |
|
this._rotate = 0; |
|
} |
|
} else { |
|
this._rotate = 0; |
|
} |
|
|
|
//anchors |
|
var anchorAttr = this.node.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "anchors"); |
|
if(anchorAttr) { |
|
anchorAttr = anchorAttr.replace("/,/g", " "); |
|
var anchors = anchorAttr.split(" ").without(""); |
|
|
|
for(var i = 0; i < anchors.length; i++) { |
|
switch(anchors[i].toLowerCase()) { |
|
case "left": |
|
this.originAnchorLeft = this.anchorLeft = true; |
|
break; |
|
case "right": |
|
this.originAnchorRight = this.anchorRight = true; |
|
break; |
|
case "top": |
|
this.originAnchorTop = this.anchorTop = true; |
|
break; |
|
case "bottom": |
|
this.originAnchorBottom = this.anchorBottom = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
//if no alignment defined, set default alignment |
|
if(!this._verticalAlign) { this._verticalAlign = 'bottom'; } |
|
if(!this._horizontalAlign) { this._horizontalAlign = 'left'; } |
|
|
|
var xValue = this.node.getAttributeNS(null, 'x'); |
|
if(xValue) { |
|
this.oldX = this.x = parseFloat(xValue); |
|
} else { |
|
//TODO error |
|
} |
|
|
|
var yValue = this.node.getAttributeNS(null, 'y'); |
|
if(yValue) { |
|
this.oldY = this.y = parseFloat(yValue); |
|
} else { |
|
//TODO error |
|
} |
|
|
|
//set initial text |
|
this.text(this.node.textContent); |
|
}, |
|
|
|
/** |
|
* Reset the anchor position to the original value |
|
* which was specified in the stencil set |
|
* |
|
*/ |
|
resetAnchorPosition: function(){ |
|
this.anchorLeft = this.originAnchorLeft || false; |
|
this.anchorRight = this.originAnchorRight || false; |
|
this.anchorTop = this.originAnchorTop || false; |
|
this.anchorBottom = this.originAnchorBottom || false; |
|
}, |
|
|
|
isOriginAnchorLeft: function(){ return this.originAnchorLeft || false; }, |
|
isOriginAnchorRight: function(){ return this.originAnchorRight || false; }, |
|
isOriginAnchorTop: function(){ return this.originAnchorTop || false; }, |
|
isOriginAnchorBottom: function(){ return this.originAnchorBottom || false; }, |
|
|
|
|
|
isAnchorLeft: function(){ return this.anchorLeft || false; }, |
|
isAnchorRight: function(){ return this.anchorRight || false; }, |
|
isAnchorTop: function(){ return this.anchorTop || false; }, |
|
isAnchorBottom: function(){ return this.anchorBottom || false; }, |
|
|
|
/** |
|
* Returns the x coordinate |
|
* @return {number} |
|
*/ |
|
getX: function(){ |
|
try { |
|
var x = this.node.x.baseVal.getItem(0).value; |
|
switch(this.horizontalAlign()){ |
|
case "left": return x; |
|
case "center": return x - (this.getWidth()/2); |
|
case "right": return x - this.getWidth(); |
|
} |
|
return this.node.getBBox().x; |
|
} catch(e){ |
|
return this.x; |
|
} |
|
}, |
|
|
|
setX: function(x){ |
|
if (this.position) |
|
this.position.x = x; |
|
else |
|
this.setOriginX(x); |
|
}, |
|
|
|
|
|
/** |
|
* Returns the y coordinate |
|
* @return {number} |
|
*/ |
|
getY: function(){ |
|
try { |
|
return this.node.getBBox().y; |
|
} catch(e){ |
|
return this.y; |
|
} |
|
}, |
|
|
|
setY: function(y){ |
|
if (this.position) |
|
this.position.y = y; |
|
else |
|
this.setOriginY(y); |
|
}, |
|
|
|
setOriginX: function(x){ |
|
this.x = x; |
|
}, |
|
|
|
setOriginY: function(y){ |
|
this.y = y; |
|
}, |
|
|
|
|
|
/** |
|
* Returns the width of the label |
|
* @return {number} |
|
*/ |
|
getWidth: function(){ |
|
try { |
|
try { |
|
var width, cn = this.node.childNodes; |
|
if (cn.length == 0) { |
|
width = this.node.getBBox().width; |
|
} else { |
|
for (var i = 0, size = cn.length; i < size; ++i) { |
|
var w = cn[i].getComputedTextLength(); |
|
if ("undefined" == typeof width || width < w) { |
|
width = w; |
|
} |
|
} |
|
} |
|
return width+(width%2==0?0:1); |
|
} catch (ee) { |
|
return this.node.getBBox().width; |
|
} |
|
} catch(e){ |
|
return 0; |
|
} |
|
}, |
|
|
|
getOriginUpperLeft: function(){ |
|
var x = this.x, y = this.y; |
|
switch (this._horizontalAlign){ |
|
case 'center' : |
|
x -= this.getWidth()/2; |
|
break; |
|
case 'right' : |
|
x -= this.getWidth(); |
|
break; |
|
} |
|
switch (this._verticalAlign){ |
|
case 'middle' : |
|
y -= this.getHeight()/2; |
|
break; |
|
case 'bottom' : |
|
y -= this.getHeight(); |
|
break; |
|
} |
|
return {x:x, y:y}; |
|
}, |
|
|
|
/** |
|
* Returns the height of the label |
|
* @return {number} |
|
*/ |
|
getHeight: function(){ |
|
try { |
|
return this.node.getBBox().height; |
|
} catch(e){ |
|
return 0; |
|
} |
|
}, |
|
|
|
/** |
|
* Returns the relative center position of the label |
|
* to its parent shape. |
|
* @return {Object} |
|
*/ |
|
getCenter: function(){ |
|
var up = {x: this.getX(), y: this.getY()}; |
|
up.x += this.getWidth()/2; |
|
up.y += this.getHeight()/2; |
|
return up; |
|
}, |
|
|
|
/** |
|
* Sets the position of a label relative to the parent. |
|
* @param {Object} position |
|
*/ |
|
setPosition: function(position){ |
|
if (!position || position.x === undefined || position.y === undefined) { |
|
delete this.position; |
|
} else { |
|
this.position = position; |
|
} |
|
|
|
if (this.position){ |
|
delete this._referencePoint; |
|
delete this.edgePosition; |
|
} |
|
|
|
this._isChanged = true; |
|
this.update(); |
|
}, |
|
|
|
/** |
|
* Return the position |
|
*/ |
|
getPosition: function(){ |
|
return this.position; |
|
}, |
|
|
|
setReferencePoint: function(ref){ |
|
if (ref) { |
|
this._referencePoint = ref; |
|
} else { |
|
delete this._referencePoint; |
|
} |
|
if (this._referencePoint){ |
|
delete this.position; |
|
} |
|
}, |
|
|
|
getReferencePoint: function(){ |
|
return this._referencePoint || undefined; |
|
}, |
|
|
|
changed: function() { |
|
this._isChanged = true; |
|
}, |
|
|
|
/** |
|
* Register a callback which will be called if the label |
|
* was rendered. |
|
* @param {Object} fn |
|
*/ |
|
registerOnChange: function(fn){ |
|
if (!this.changeCallbacks){ |
|
this.changeCallbacks = []; |
|
} |
|
if (fn instanceof Function && !this.changeCallbacks.include(fn)){ |
|
this.changeCallbacks.push(fn); |
|
} |
|
}, |
|
|
|
/** |
|
* Unregister the callback for changes. |
|
* @param {Object} fn |
|
*/ |
|
unregisterOnChange: function(fn){ |
|
if (this.changeCallbacks && fn instanceof Function && this.changeCallbacks.include(fn)){ |
|
this.changeCallbacks = this.changeCallbacks.without(fn); |
|
} |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the labe is currently in |
|
* the update mechanism. |
|
* @return {Boolean} |
|
*/ |
|
isUpdating: function(){ |
|
return !!this._isUpdating; |
|
}, |
|
|
|
|
|
getOriginEdgePosition: function(){ |
|
return this.originEdgePosition; |
|
}, |
|
|
|
/** |
|
* Returns the edgeposition. |
|
* |
|
* @return {String} "starttop", "startmiddle", "startbottom", |
|
* "midtop", "midbottom", "endtop", "endbottom" or null |
|
*/ |
|
getEdgePosition: function(){ |
|
return this.edgePosition || null; |
|
}, |
|
|
|
/** |
|
* Set the edge position, must be one of the valid |
|
* edge positions (see getEdgePosition). |
|
* Removes the reference point and the absolute position as well. |
|
* |
|
* @param {Object} position |
|
*/ |
|
setEdgePosition: function(position){ |
|
if (["starttop", "startmiddle", "startbottom", |
|
"midtop", "midbottom", "endtop", "endbottom"].include(position)){ |
|
this.edgePosition = position; |
|
delete this.position; |
|
delete this._referencePoint; |
|
} else { |
|
delete this.edgePosition; |
|
} |
|
}, |
|
|
|
/** |
|
* Update the SVG text element. |
|
*/ |
|
update: function(force) { |
|
|
|
var x = this.x, y = this.y; |
|
if (this.position){ |
|
x = this.position.x; |
|
y = this.position.y; |
|
} |
|
x = Math.floor(x); y = Math.floor(y); |
|
|
|
if(this._isChanged || x !== this.oldX || y !== this.oldY || force === true) { |
|
if (this.isVisible) { |
|
this._isChanged = false; |
|
this._isUpdating = true; |
|
|
|
this.node.setAttributeNS(null, 'x', x); |
|
this.node.setAttributeNS(null, 'y', y); |
|
this.node.removeAttributeNS(null, "fill-opacity"); |
|
|
|
//this.node.setAttributeNS(null, 'font-size', this._fontSize); |
|
//this.node.setAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'align', this._horizontalAlign + " " + this._verticalAlign); |
|
|
|
this.oldX = x; |
|
this.oldY = y; |
|
|
|
//set rotation |
|
if (!this.position && !this.getReferencePoint()) { |
|
if (this._rotate !== undefined) { |
|
if (this._rotationPoint) |
|
this.node.setAttributeNS(null, 'transform', 'rotate(' + this._rotate + ' ' + Math.floor(this._rotationPoint.x) + ' ' + Math.floor(this._rotationPoint.y) + ')'); |
|
else |
|
this.node.setAttributeNS(null, 'transform', 'rotate(' + this._rotate + ' ' + Math.floor(x) + ' ' + Math.floor(y) + ')'); |
|
} |
|
} else { |
|
this.node.removeAttributeNS(null, 'transform'); |
|
} |
|
|
|
var textLines = this._text.split("\n"); |
|
while (textLines.last() == "") |
|
textLines.pop(); |
|
|
|
|
|
if (this.node.ownerDocument) { |
|
// Only reset the tspans if the text |
|
// has changed or has to be wrapped |
|
if (this.fitToElemId || this._textHasChanged){ |
|
this.node.textContent = ""; // Remove content |
|
textLines.each((function(textLine, index){ |
|
var tspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan'); |
|
tspan.textContent = textLine.trim(); |
|
if (this.fitToElemId) { |
|
tspan.setAttributeNS(null, 'x', this.invisibleRenderPoint); |
|
tspan.setAttributeNS(null, 'y', this.invisibleRenderPoint); |
|
} |
|
|
|
/* |
|
* Chrome's getBBox() method fails, if a text node contains an empty tspan element. |
|
* So, we add a whitespace to such a tspan element. |
|
*/ |
|
if(tspan.textContent === "") { |
|
tspan.textContent = " "; |
|
} |
|
|
|
//append tspan to text node |
|
this.node.appendChild(tspan); |
|
}).bind(this)); |
|
delete this._textHasChanged; |
|
delete this.indices; |
|
} |
|
|
|
//Work around for Mozilla bug 293581 |
|
if (this.isVisible && this.fitToElemId) { |
|
this.node.setAttributeNS(null, 'visibility', 'hidden'); |
|
} |
|
|
|
if (this.fitToElemId) { |
|
window.setTimeout(this._checkFittingToReferencedElem.bind(this), 0); |
|
//this._checkFittingToReferencedElem(); |
|
} else { |
|
window.setTimeout(this._positionText.bind(this), 0); |
|
//this._positionText(); |
|
} |
|
} |
|
} else { |
|
this.node.textContent = ""; |
|
//this.node.setAttributeNS(null, "fill-opacity", "0.2"); |
|
} |
|
} |
|
}, |
|
|
|
_checkFittingToReferencedElem: function() { |
|
try { |
|
var tspans = $A(this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')); |
|
|
|
//only do this in firefox 3. all other browsers do not support word wrapping!!!!! |
|
//if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1)>=3) { |
|
var newtspans = []; |
|
|
|
var refNode = this.node.ownerDocument.getElementById(this.fitToElemId); |
|
|
|
if (refNode) { |
|
var refbb = refNode.getBBox(); |
|
|
|
var fontSize = this.getFontSize(); |
|
|
|
for (var j = 0; j < tspans.length; j++) { |
|
var tspan = tspans[j]; |
|
|
|
var textLength = this._getRenderedTextLength(tspan, undefined, undefined, fontSize); |
|
|
|
var refBoxLength = (this._rotate != 0 |
|
&& this._rotate % 180 != 0 |
|
&& this._rotate % 90 == 0 ? |
|
refbb.height : refbb.width); |
|
|
|
if (textLength > refBoxLength) { |
|
|
|
var startIndex = 0; |
|
var lastSeperatorIndex = 0; |
|
|
|
var numOfChars = this.getTrimmedTextLength(tspan.textContent); |
|
for (var i = 0; i < numOfChars; i++) { |
|
var sslength = this._getRenderedTextLength(tspan, startIndex, i-startIndex, fontSize); |
|
|
|
if (sslength > refBoxLength - 2) { |
|
var newtspan = this.node.ownerDocument.createElementNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan'); |
|
if (lastSeperatorIndex <= startIndex) { |
|
lastSeperatorIndex = (i == 0) ? i : i-1; |
|
newtspan.textContent = tspan.textContent.slice(startIndex, lastSeperatorIndex).trim(); |
|
//lastSeperatorIndex = i; |
|
} |
|
else { |
|
newtspan.textContent = tspan.textContent.slice(startIndex, ++lastSeperatorIndex).trim(); |
|
} |
|
|
|
newtspan.setAttributeNS(null, 'x', this.invisibleRenderPoint); |
|
newtspan.setAttributeNS(null, 'y', this.invisibleRenderPoint); |
|
|
|
//insert tspan to text node |
|
//this.node.insertBefore(newtspan, tspan); |
|
newtspans.push(newtspan); |
|
|
|
startIndex = lastSeperatorIndex; |
|
} |
|
else { |
|
var curChar = tspan.textContent.charAt(i); |
|
if (curChar == ' ' || |
|
curChar == '-' || |
|
curChar == "." || |
|
curChar == "," || |
|
curChar == ";" || |
|
curChar == ":") { |
|
lastSeperatorIndex = i; |
|
} |
|
} |
|
} |
|
|
|
tspan.textContent = tspan.textContent.slice(startIndex).trim(); |
|
} |
|
|
|
newtspans.push(tspan); |
|
} |
|
|
|
while (this.node.hasChildNodes()) |
|
this.node.removeChild(this.node.childNodes[0]); |
|
|
|
while (newtspans.length > 0) { |
|
this.node.appendChild(newtspans.shift()); |
|
} |
|
} |
|
//} |
|
} catch (e) { |
|
ORYX.Log.fatal("Error " + e); |
|
} |
|
window.setTimeout(this._positionText.bind(this), 0); |
|
//this._positionText(); |
|
}, |
|
|
|
/** |
|
* This is a work around method for Mozilla bug 293581. |
|
* Before the method getComputedTextLength works, the text has to be rendered. |
|
*/ |
|
_positionText: function() { |
|
try { |
|
|
|
var tspans = this.node.childNodes; |
|
|
|
var fontSize = this.getFontSize(this.node); |
|
|
|
var invalidTSpans = []; |
|
|
|
var x = this.x, y = this.y; |
|
if (this.position){ |
|
x = this.position.x; |
|
y = this.position.y; |
|
} |
|
x = Math.floor(x); y = Math.floor(y); |
|
|
|
var i = 0, indic = []; // Cache indices if the _positionText is called again, before update is called |
|
var is =(this.indices || $R(0,tspans.length-1).toArray()); |
|
var length = is.length; |
|
is.each((function(index){ |
|
if ("undefined" == typeof index){ |
|
return; |
|
} |
|
|
|
var tspan = tspans[i++]; |
|
|
|
if(tspan.textContent.trim() === "") { |
|
invalidTSpans.push(tspan); |
|
} else { |
|
//set vertical position |
|
var dy = 0; |
|
switch (this._verticalAlign) { |
|
case 'bottom': |
|
dy = -(length - index - 1) * (fontSize); |
|
break; |
|
case 'middle': |
|
dy = -(length / 2.0 - index - 1) * (fontSize); |
|
dy -= ORYX.CONFIG.LABEL_LINE_DISTANCE / 2; |
|
break; |
|
case 'top': |
|
dy = index * (fontSize); |
|
dy += fontSize; |
|
break; |
|
} |
|
tspan.setAttributeNS(null, 'dy', Math.floor(dy)); |
|
|
|
tspan.setAttributeNS(null, 'x', x); |
|
tspan.setAttributeNS(null, 'y', y); |
|
indic.push(index); |
|
} |
|
|
|
}).bind(this)); |
|
|
|
indic.length = tspans.length; |
|
this.indices = this.indices || indic; |
|
|
|
invalidTSpans.each(function(tspan) { |
|
this.node.removeChild(tspan) |
|
}.bind(this)); |
|
|
|
//set horizontal alignment |
|
switch (this._horizontalAlign) { |
|
case 'left': |
|
this.node.setAttributeNS(null, 'text-anchor', 'start'); |
|
break; |
|
case 'center': |
|
this.node.setAttributeNS(null, 'text-anchor', 'middle'); |
|
break; |
|
case 'right': |
|
this.node.setAttributeNS(null, 'text-anchor', 'end'); |
|
break; |
|
} |
|
|
|
} catch(e) { |
|
//console.log(e); |
|
this._isChanged = true; |
|
} |
|
|
|
|
|
if(this.isVisible) { |
|
this.node.removeAttributeNS(null, 'visibility'); |
|
} |
|
|
|
|
|
// Finished |
|
delete this._isUpdating; |
|
|
|
// Raise change event |
|
(this.changeCallbacks||[]).each(function(fn){ |
|
fn.apply(fn); |
|
}) |
|
|
|
}, |
|
|
|
/** |
|
* Returns the text length of the text content of an SVG tspan element. |
|
* For all browsers but Firefox 3 the values are estimated. |
|
* @param {TSpanSVGElement} tspan |
|
* @param {int} startIndex Optional, for sub strings |
|
* @param {int} endIndex Optional, for sub strings |
|
*/ |
|
_getRenderedTextLength: function(tspan, startIndex, endIndex, fontSize) { |
|
//if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) { |
|
if(startIndex === undefined) { |
|
//test string: abcdefghijklmnopqrstuvwxyz????????????????,.-#+ 1234567890?????ABCDEFGHIJKLMNOPQRSTUVWXYZ;:_'*???????????????!"????$%&/()=?[]{}|<>'~????`\^?????????@????????????????? |
|
// for(var i = 0; i < tspan.textContent.length; i++) { |
|
// console.log(tspan.textContent.charAt(i), tspan.getSubStringLength(i,1), this._estimateCharacterWidth(tspan.textContent.charAt(i))*(fontSize/14.0)); |
|
// } |
|
return tspan.getComputedTextLength(); |
|
} else { |
|
return tspan.getSubStringLength(startIndex, endIndex); |
|
} |
|
/*} else { |
|
if(startIndex === undefined) { |
|
return this._estimateTextWidth(tspan.textContent, fontSize); |
|
} else { |
|
return this._estimateTextWidth(tspan.textContent.substr(startIndex, endIndex).trim(), fontSize); |
|
} |
|
}*/ |
|
}, |
|
|
|
/** |
|
* Estimates the text width for a string. |
|
* Used for word wrapping in all browser but FF3. |
|
* @param {Object} text |
|
*/ |
|
_estimateTextWidth: function(text, fontSize) { |
|
var sum = 0.0; |
|
for(var i = 0; i < text.length; i++) { |
|
sum += this._estimateCharacterWidth(text.charAt(i)); |
|
} |
|
|
|
return sum*(fontSize/14.0); |
|
}, |
|
|
|
/** |
|
* Estimates the width of a single character for font size 14. |
|
* Used for word wrapping in all browser but FF3. |
|
* @param {Object} character |
|
*/ |
|
_estimateCharacterWidth: function(character) { |
|
for(var i = 0; i < this._characterSets.length; i++) { |
|
if(this._characterSets[i].indexOf(character) >= 0) { |
|
return this._characterSetValues[i]; |
|
} |
|
} |
|
return 9; |
|
}, |
|
|
|
getReferencedElementWidth: function() { |
|
var refNode = this.node.ownerDocument.getElementById(this.fitToElemId); |
|
|
|
if(refNode) { |
|
var refbb = refNode.getBBox(); |
|
|
|
if(refbb) { |
|
return (this._rotate != 0 |
|
&& this._rotate % 180 != 0 |
|
&& this._rotate % 90 == 0 ? |
|
refbb.height : refbb.width); |
|
} |
|
} |
|
|
|
return undefined; |
|
}, |
|
|
|
/** |
|
* If no parameter is provided, this method returns the current text. |
|
* @param text {String} Optional. Replaces the old text with this one. |
|
*/ |
|
text: function() { |
|
switch (arguments.length) { |
|
case 0: |
|
return this._text |
|
break; |
|
|
|
case 1: |
|
var oldText = this._text; |
|
if(arguments[0]) { |
|
// Filter out multiple spaces to fix issue in chrome for line-wrapping |
|
this._text = arguments[0].toString(); |
|
if(this._text != null && this._text != undefined) { |
|
this._text = this._text.replace(/ {2,}/g,' '); |
|
} |
|
} else { |
|
this._text = ""; |
|
} |
|
if(oldText !== this._text) { |
|
this._isChanged = true; |
|
this._textHasChanged = true; |
|
} |
|
break; |
|
|
|
default: |
|
//TODO error |
|
break; |
|
} |
|
}, |
|
|
|
getOriginVerticalAlign: function(){ |
|
return this._originVerticalAlign; |
|
}, |
|
|
|
verticalAlign: function() { |
|
switch(arguments.length) { |
|
case 0: |
|
return this._verticalAlign; |
|
case 1: |
|
if(['top', 'middle', 'bottom'].member(arguments[0])) { |
|
var oldValue = this._verticalAlign; |
|
this._verticalAlign = arguments[0]; |
|
if(this._verticalAlign !== oldValue) { |
|
this._isChanged = true; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
//TODO error |
|
break; |
|
} |
|
}, |
|
|
|
getOriginHorizontalAlign: function(){ |
|
return this._originHorizontalAlign; |
|
}, |
|
|
|
horizontalAlign: function() { |
|
switch(arguments.length) { |
|
case 0: |
|
return this._horizontalAlign; |
|
case 1: |
|
if(['left', 'center', 'right'].member(arguments[0])) { |
|
var oldValue = this._horizontalAlign; |
|
this._horizontalAlign = arguments[0]; |
|
if(this._horizontalAlign !== oldValue) { |
|
this._isChanged = true; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
//TODO error |
|
break; |
|
} |
|
}, |
|
|
|
rotate: function() { |
|
switch(arguments.length) { |
|
case 0: |
|
return this._rotate; |
|
case 1: |
|
if (this._rotate != arguments[0]) { |
|
this._rotate = arguments[0]; |
|
this._rotationPoint = undefined; |
|
this._isChanged = true; |
|
} |
|
case 2: |
|
if(this._rotate != arguments[0] || |
|
!this._rotationPoint || |
|
this._rotationPoint.x != arguments[1].x || |
|
this._rotationPoint.y != arguments[1].y) { |
|
this._rotate = arguments[0]; |
|
this._rotationPoint = arguments[1]; |
|
this._isChanged = true; |
|
} |
|
|
|
} |
|
}, |
|
|
|
hide: function() { |
|
if(this.isVisible) { |
|
this.isVisible = false; |
|
this._isChanged = true; |
|
} |
|
}, |
|
|
|
show: function() { |
|
if(!this.isVisible) { |
|
this.isVisible = true; |
|
this._isChanged = true; |
|
|
|
// Since text is removed from the tspan when "hidden", mark |
|
// the text as changed to get it redrawn |
|
this._textHasChanged = true; |
|
} |
|
}, |
|
|
|
/** |
|
* iterates parent nodes till it finds a SVG font-size |
|
* attribute. |
|
* @param {SVGElement} node |
|
*/ |
|
getInheritedFontSize: function(node) { |
|
if(!node || !node.getAttributeNS) |
|
return; |
|
|
|
var attr = node.getAttributeNS(null, "font-size"); |
|
if(attr) { |
|
return parseFloat(attr); |
|
} else if(!ORYX.Editor.checkClassType(node, SVGSVGElement)) { |
|
return this.getInheritedFontSize(node.parentNode); |
|
} |
|
}, |
|
|
|
getFontSize: function(node) { |
|
var tspans = this.node.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan'); |
|
|
|
//trying to get an inherited font-size attribute |
|
//NO CSS CONSIDERED! |
|
var fontSize = this.getInheritedFontSize(this.node); |
|
|
|
if (!fontSize) { |
|
//because this only works in firefox 3, all other browser use the default line height |
|
if (tspans[0] && /Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) { |
|
fontSize = tspans[0].getExtentOfChar(0).height; |
|
} |
|
else { |
|
fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT; |
|
} |
|
|
|
//handling of unsupported method in webkit |
|
if (fontSize <= 0) { |
|
fontSize = ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT; |
|
} |
|
} |
|
|
|
if(fontSize) |
|
this.node.setAttribute("oryx:fontSize", fontSize); |
|
|
|
return fontSize; |
|
}, |
|
|
|
/** |
|
* Get trimmed text length for use with |
|
* getExtentOfChar and getSubStringLength. |
|
* @param {String} text |
|
*/ |
|
getTrimmedTextLength: function(text) { |
|
text = text.strip().gsub(' ', ' '); |
|
|
|
var oldLength; |
|
do { |
|
oldLength = text.length; |
|
text = text.gsub(' ', ' '); |
|
} while (oldLength > text.length); |
|
|
|
return text.length; |
|
}, |
|
|
|
/** |
|
* Returns the offset from |
|
* edge to the label which is |
|
* positioned under the edge |
|
* @return {int} |
|
*/ |
|
getOffsetBottom: function(){ |
|
return this.offsetBottom; |
|
}, |
|
|
|
|
|
/** |
|
* Returns the offset from |
|
* edge to the label which is |
|
* positioned over the edge |
|
* @return {int} |
|
*/ |
|
getOffsetTop: function(){ |
|
return this.offsetTop; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} obj |
|
*/ |
|
deserialize: function(obj, shape){ |
|
if (obj && "undefined" != typeof obj.x && "undefined" != typeof obj.y){ |
|
this.setPosition({x:obj.x, y:obj.y}); |
|
|
|
if ("undefined" != typeof obj.distance){ |
|
var from = shape.dockers[obj.from]; |
|
var to = shape.dockers[obj.to]; |
|
if (from && to){ |
|
this.setReferencePoint({ |
|
dirty : true, |
|
distance : obj.distance, |
|
intersection : {x: obj.x, y: obj.y}, |
|
orientation : obj.orientation, |
|
segment: { |
|
from: from, |
|
fromIndex: obj.from, |
|
fromPosition: from.bounds.center(), |
|
to: to, |
|
toIndex: obj.to, |
|
toPosition: to.bounds.center() |
|
} |
|
}) |
|
} |
|
} |
|
|
|
if (obj.left) this.anchorLeft = true; |
|
if (obj.right) this.anchorRight = true; |
|
if (obj.top) this.anchorTop = true; |
|
if (obj.bottom) this.anchorBottom = true; |
|
if (obj.valign) this.verticalAlign(obj.valign); |
|
if (obj.align) this.horizontalAlign(obj.align); |
|
|
|
} else if (obj && "undefined" != typeof obj.edge){ |
|
this.setEdgePosition(obj.edge); |
|
} |
|
}, |
|
|
|
/** |
|
* |
|
* @return {Object} |
|
*/ |
|
serialize: function(){ |
|
|
|
// On edge position |
|
if (this.getEdgePosition()){ |
|
if (this.getOriginEdgePosition() !== this.getEdgePosition()){ |
|
return {edge: this.getEdgePosition()}; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
// On self defined position |
|
if (this.position){ |
|
var pos = {x: this.position.x, y: this.position.y}; |
|
if (this.isAnchorLeft() && this.isAnchorLeft() !== this.isOriginAnchorLeft()){ |
|
pos.left = true; |
|
} |
|
if (this.isAnchorRight() && this.isAnchorRight() !== this.isOriginAnchorRight()){ |
|
pos.right = true; |
|
} |
|
if (this.isAnchorTop() && this.isAnchorTop() !== this.isOriginAnchorTop()){ |
|
pos.top = true; |
|
} |
|
if (this.isAnchorBottom() && this.isAnchorBottom() !== this.isOriginAnchorBottom()){ |
|
pos.bottom = true; |
|
} |
|
|
|
if (this.getOriginVerticalAlign() !== this.verticalAlign()){ |
|
pos.valign = this.verticalAlign(); |
|
} |
|
if (this.getOriginHorizontalAlign() !== this.horizontalAlign()){ |
|
pos.align = this.horizontalAlign(); |
|
} |
|
|
|
return pos; |
|
} |
|
|
|
// On reference point which is interesting for edges |
|
if (this.getReferencePoint()){ |
|
var ref = this.getReferencePoint(); |
|
return { |
|
distance : ref.distance, |
|
x : ref.intersection.x, |
|
y : ref.intersection.y, |
|
from : ref.segment.fromIndex, |
|
to : ref.segment.toIndex, |
|
orientation : ref.orientation, |
|
valign : this.verticalAlign(), |
|
align : this.horizontalAlign() |
|
} |
|
} |
|
return null; |
|
}, |
|
|
|
toString: function() { return "Label " + this.id } |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.Math) {ORYX.Core.Math = {};} |
|
|
|
/** |
|
* Calculate the middle point between two given points |
|
* @param {x:double, y:double} point1 |
|
* @param {x:double, y:double} point2 |
|
* @return the middle point |
|
*/ |
|
ORYX.Core.Math.midPoint = function(point1, point2) { |
|
return { |
|
x: (point1.x + point2.x) / 2.0, |
|
y: (point1.y + point2.y) / 2.0 |
|
} |
|
} |
|
|
|
/** |
|
* Returns a TRUE if the point is over a line (defined by |
|
* point1 and point 2). In Addition a threshold can be set, |
|
* which defines the weight of those line. |
|
* |
|
* @param {int} pointX - Point X |
|
* @param {int} pointY - Point Y |
|
* @param {int} lPoint1X - Line first Point X |
|
* @param {int} lPoint1Y - Line first Point Y |
|
* @param {int} lPoint2X - Line second Point X |
|
* @param {int} lPoint2Y - Line second Point y |
|
* @param {int} offset {optional} - maximal distance to line |
|
* @class ORYX.Core.Math.prototype |
|
*/ |
|
ORYX.Core.Math.isPointInLine = function (pointX, pointY, lPoint1X, lPoint1Y, lPoint2X, lPoint2Y, offset) { |
|
|
|
offset = offset ? Math.abs(offset) : 1; |
|
|
|
// Check if the edge is vertical |
|
if(Math.abs(lPoint1X-lPoint2X)<=offset && Math.abs(pointX-lPoint1X)<=offset && pointY-Math.max(lPoint1Y, lPoint2Y)<=offset && Math.min(lPoint1Y, lPoint2Y)-pointY<=offset) { |
|
return true |
|
} |
|
|
|
// Check if the edge is horizontal |
|
if(Math.abs(lPoint1Y-lPoint2Y)<=offset && Math.abs(pointY-lPoint1Y)<=offset && pointX-Math.max(lPoint1X, lPoint2X)<=offset && Math.min(lPoint1X, lPoint2X)-pointX<=offset) { |
|
return true |
|
} |
|
|
|
if(pointX > Math.max(lPoint1X, lPoint2X) || pointX < Math.min(lPoint1X, lPoint2X)) { |
|
return false |
|
} |
|
|
|
if(pointY > Math.max(lPoint1Y, lPoint2Y) || pointY < Math.min(lPoint1Y, lPoint2Y)) { |
|
return false |
|
} |
|
|
|
var s = (lPoint1Y - lPoint2Y) / (lPoint1X - lPoint2X); |
|
|
|
return Math.abs(pointY - ((s * pointX) + lPoint1Y - s * lPoint1X)) < offset |
|
} |
|
|
|
/** |
|
* Get a boolean if the point is in the polygone |
|
* |
|
*/ |
|
ORYX.Core.Math.isPointInEllipse = function (pointX, pointY, cx, cy, rx, ry) { |
|
|
|
if(cx === undefined || cy === undefined || rx === undefined || ry === undefined) { |
|
throw "ORYX.Core.Math.isPointInEllipse needs a ellipse with these properties: x, y, radiusX, radiusY" |
|
} |
|
|
|
var tx = (pointX - cx) / rx; |
|
var ty = (pointY - cy) / ry; |
|
|
|
return tx * tx + ty * ty < 1.0; |
|
} |
|
|
|
/** |
|
* Get a boolean if the point is in the polygone |
|
* @param {int} pointX |
|
* @param {int} pointY |
|
* @param {[int]} Cornerpoints of the Polygone (x,y,x,y,...) |
|
*/ |
|
ORYX.Core.Math.isPointInPolygone = function(pointX, pointY, polygone){ |
|
|
|
if (arguments.length < 3) { |
|
throw "ORYX.Core.Math.isPointInPolygone needs two arguments" |
|
} |
|
|
|
var lastIndex = polygone.length-1; |
|
|
|
if (polygone[0] !== polygone[lastIndex - 1] || polygone[1] !== polygone[lastIndex]) { |
|
polygone.push(polygone[0]); |
|
polygone.push(polygone[1]); |
|
} |
|
|
|
var crossings = 0; |
|
|
|
var x1, y1, x2, y2, d; |
|
|
|
for (var i = 0; i < polygone.length - 3; ) { |
|
x1=polygone[i]; |
|
y1=polygone[++i]; |
|
x2=polygone[++i]; |
|
y2=polygone[i+1]; |
|
d=(pointY - y1) * (x2 - x1) - (pointX - x1) * (y2 - y1); |
|
|
|
if ((y1 >= pointY) != (y2 >= pointY)) { |
|
crossings += y2 - y1 >= 0 ? d >= 0 : d <= 0; |
|
} |
|
if (!d && Math.min(x1,x2) <= pointX && pointX <= Math.max(x1,x2) |
|
&& Math.min(y1,y2) <= pointY && pointY <= Math.max(y1,y2)) { |
|
return true; |
|
} |
|
} |
|
return (crossings%2)?true:false; |
|
} |
|
|
|
/** |
|
* Calculates the distance between a point and a line. It is also testable, if |
|
* the distance orthogonal to the line, matches the segment of the line. |
|
* |
|
* @param {float} lineP1 |
|
* The starting point of the line segment |
|
* @param {float} lineP2 |
|
* The end point of the line segment |
|
* @param {Point} point |
|
* The point to calculate the distance to. |
|
* @param {boolean} toSegmentOnly |
|
* Flag to signal if only the segment of the line shell be evaluated. |
|
*/ |
|
ORYX.Core.Math.distancePointLinie = function( |
|
lineP1, |
|
lineP2, |
|
point, |
|
toSegmentOnly) { |
|
|
|
var intersectionPoint = |
|
ORYX.Core.Math.getPointOfIntersectionPointLine(lineP1, |
|
lineP2, |
|
point, |
|
toSegmentOnly); |
|
|
|
if(!intersectionPoint) { |
|
return null; |
|
} |
|
|
|
return ORYX.Core.Math.getDistancePointToPoint(point, intersectionPoint); |
|
}; |
|
|
|
/** |
|
* Calculates the distance between two points. |
|
* |
|
* @param {point} point1 |
|
* @param {point} point2 |
|
*/ |
|
ORYX.Core.Math.getDistancePointToPoint = function(point1, point2) { |
|
return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)); |
|
}; |
|
|
|
/** |
|
* Calculates the relative distance of a point which is between two other points. |
|
* |
|
* @param {point} between1 |
|
* @param {point} between2 |
|
* @param {point} point |
|
*/ |
|
ORYX.Core.Math.getDistanceBetweenTwoPoints = function(between1, between2, point) { |
|
return ORYX.Core.Math.getDistancePointToPoint(point, between1) / |
|
ORYX.Core.Math.getDistancePointToPoint(between1, between2); |
|
}; |
|
|
|
|
|
/** |
|
* Returns true, if the point is of the left hand |
|
* side of the regarding the line. |
|
* |
|
* @param {point} lineP1 Line first point |
|
* @param {point} lineP2 Line second point |
|
* @param {point} point |
|
*/ |
|
ORYX.Core.Math.pointIsLeftOfLine = function(lineP1, lineP2, point){ |
|
|
|
var vec1 = ORYX.Core.Math.getVector(lineP1, lineP2); |
|
var vec2 = ORYX.Core.Math.getVector(lineP1, point); |
|
// if the cross produkt is more than 0 |
|
return ((vec1.x*vec2.y) - (vec2.x*vec1.y)) > 0 |
|
}; |
|
|
|
/** |
|
* Calculates the a point which is relatively between two other points. |
|
* |
|
* @param {point} point1 |
|
* @param {point} point2 |
|
* @param {number} relative Relative which is between 0 and 1 |
|
*/ |
|
ORYX.Core.Math.getPointBetweenTwoPoints = function(point1, point2, relative) { |
|
relative = Math.max(Math.min(relative || 0, 1), 0); |
|
|
|
if (relative === 0){ |
|
return point1; |
|
} else if (relative === 1){ |
|
return point2; |
|
} |
|
|
|
return { |
|
x: point1.x + ((point2.x - point1.x) * relative), |
|
y: point1.y + ((point2.y - point1.y) * relative) |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* Returns the vector of the both points |
|
* |
|
* @param {point} point1 |
|
* @param {point} point2 |
|
*/ |
|
ORYX.Core.Math.getVector = function(point1, point2){ |
|
return { |
|
x: point2.x - point1.x, |
|
y: point2.y - point1.y |
|
} |
|
} |
|
|
|
/** |
|
* Returns the an identity vector of the given vector, |
|
* which has the length ot one. |
|
* |
|
* @param {point} vector |
|
* or |
|
* @param {point} point1 |
|
* @param {point} point2 |
|
*/ |
|
ORYX.Core.Math.getIdentityVector = function(vector){ |
|
|
|
if (arguments.length == 2){ |
|
vector = ORYX.Core.Math.getVector(arguments[0], arguments[1]); |
|
} |
|
|
|
var length = Math.sqrt((vector.x*vector.x)+(vector.y*vector.y)) |
|
return { |
|
x: vector.x / (length || 1), |
|
y: vector.y / (length || 1) |
|
} |
|
} |
|
|
|
|
|
ORYX.Core.Math.getOrthogonalIdentityVector = function(point1, point2){ |
|
var vec = arguments.length == 1 ? point1 : ORYX.Core.Math.getIdentityVector(point1, point2); |
|
return { |
|
x: vec.y, |
|
y: -vec.x |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Returns the intersection point of a line and a point that defines a line |
|
* orthogonal to the given line. |
|
* |
|
* @param {float} lineP1 |
|
* The starting point of the line segment |
|
* @param {float} lineP2 |
|
* The end point of the line segment |
|
* @param {Point} point |
|
* The point to calculate the distance to. |
|
* @param {boolean} onSegmentOnly |
|
* Flag to signal if only the segment of the line shell be evaluated. |
|
*/ |
|
ORYX.Core.Math.getPointOfIntersectionPointLine = function( |
|
lineP1, |
|
lineP2, |
|
point, |
|
onSegmentOnly) { |
|
|
|
/* |
|
* [P3 - P1 - u(P2 - P1)] dot (P2 - P1) = 0 |
|
* u =((x3-x1)(x2-x1)+(y3-y1)(y2-y1))/(p2-p1)?? |
|
*/ |
|
var denominator = Math.pow(lineP2.x - lineP1.x, 2) |
|
+ Math.pow(lineP2.y - lineP1.y, 2); |
|
if(denominator == 0) { |
|
return undefined; |
|
} |
|
|
|
var u = ((point.x - lineP1.x) * (lineP2.x - lineP1.x) |
|
+ (point.y - lineP1.y) * (lineP2.y - lineP1.y)) |
|
/ denominator; |
|
|
|
if(onSegmentOnly) { |
|
if (!(0 <= u && u <= 1)) { |
|
return undefined; |
|
} |
|
} |
|
|
|
pointOfIntersection = new Object(); |
|
pointOfIntersection.x = lineP1.x + u * (lineP2.x - lineP1.x); |
|
pointOfIntersection.y = lineP1.y + u * (lineP2.y - lineP1.y); |
|
|
|
return pointOfIntersection; |
|
}; |
|
|
|
/** |
|
* Translated the point with the given matrix. |
|
* @param {Point} point |
|
* @param {Matrix} matrix |
|
* @return {Object} Includes x, y |
|
*/ |
|
ORYX.Core.Math.getTranslatedPoint = function(point, matrix){ |
|
var x = matrix.a*point.x+matrix.c*point.y+matrix.e*1; |
|
var y = matrix.b*point.x+matrix.d*point.y+matrix.f*1; |
|
return {x:x, y:y} |
|
} |
|
|
|
|
|
/** |
|
* Returns the inverse matrix of the given SVG transformation matrix |
|
* @param {SVGTransformationMatrix} matrix |
|
* @return {Matrix} |
|
*/ |
|
ORYX.Core.Math.getInverseMatrix = function(matrix){ |
|
|
|
var det = ORYX.Core.Math.getDeterminant(matrix), m = matrix; |
|
// +- -+ |
|
// | a c e | |
|
// | b d f | |
|
// | 0 0 1 | |
|
// +- -+ |
|
return { |
|
a: det * ((m.d*1)-(m.f*0)), |
|
b: det * ((m.f*0)-(m.b*1)), |
|
c: det * ((m.e*0)-(m.c*1)), |
|
d: det * ((m.a*1)-(m.e*0)), |
|
e: det * ((m.c*m.f)-(m.e*m.d)), |
|
f: det * ((m.e*m.b)-(m.a*m.f)) |
|
} |
|
} |
|
|
|
/** |
|
* Returns the determinant of the svg transformation matrix |
|
* @param {SVGTranformationMatrix} matrix |
|
* @return {Number} |
|
* |
|
*/ |
|
ORYX.Core.Math.getDeterminant = function(m){ |
|
// a11a22a33+a12a23a31+a13a21a32-a13a22a31-a12a21a33-a11a23a32 |
|
return (m.a*m.d*1)+(m.c*m.f*0)+(m.e*m.b*0)-(m.e*m.d*0)-(m.c*m.b*1)-(m.a*m.f*0); |
|
} |
|
|
|
/** |
|
* Returns the bounding box of the given node. Translates the |
|
* origin bounding box with the tranlation matrix. |
|
* @param {SVGElement} node |
|
* @return {Object} Includes x, y, width, height |
|
*/ |
|
ORYX.Core.Math.getTranslatedBoundingBox = function(node){ |
|
var matrix = node.getCTM(); |
|
var bb = node.getBBox(); |
|
var ul = ORYX.Core.Math.getTranslatedPoint({x:bb.x, y:bb.y}, matrix); |
|
var ll = ORYX.Core.Math.getTranslatedPoint({x:bb.x, y:bb.y+bb.height}, matrix); |
|
var ur = ORYX.Core.Math.getTranslatedPoint({x:bb.x+bb.width, y:bb.y}, matrix); |
|
var lr = ORYX.Core.Math.getTranslatedPoint({x:bb.x+bb.width, y:bb.y+bb.height}, matrix); |
|
|
|
var minPoint = { |
|
x: Math.min(ul.x, ll.x, ur.x, lr.x), |
|
y: Math.min(ul.y, ll.y, ur.y, lr.y) |
|
} |
|
var maxPoint = { |
|
x: Math.max(ul.x, ll.x, ur.x, lr.x), |
|
y: Math.max(ul.y, ll.y, ur.y, lr.y) |
|
} |
|
return { |
|
x: minPoint.x, |
|
y: minPoint.y, |
|
width: maxPoint.x - minPoint.x, |
|
height: maxPoint.y - minPoint.y |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* Returns the angle of the given line, which is representated by the two points |
|
* @param {Point} p1 |
|
* @param {Point} p2 |
|
* @return {Number} 0 <= x <= 359.99999 |
|
*/ |
|
ORYX.Core.Math.getAngle = function(p1, p2){ |
|
if(p1.x == p2.x && p1.y == p2.y) |
|
return 0; |
|
|
|
var angle = Math.asin(Math.sqrt(Math.pow(p1.y-p2.y, 2)) |
|
/(Math.sqrt(Math.pow(p2.x-p1.x, 2)+Math.pow(p1.y-p2.y, 2)))) |
|
*180/Math.PI; |
|
|
|
if(p2.x >= p1.x && p2.y <= p1.y) |
|
return angle; |
|
else if(p2.x < p1.x && p2.y <= p1.y) |
|
return 180 - angle; |
|
else if(p2.x < p1.x && p2.y > p1.y) |
|
return 180 + angle; |
|
else |
|
return 360 - angle; |
|
}; |
|
|
|
|
|
/** |
|
* Implementation of the cohen-sutherland algorithm |
|
*/ |
|
new function(){ |
|
|
|
var RIGHT = 2, TOP = 8, BOTTOM = 4, LEFT = 1; |
|
|
|
function computeOutCode (x, y, xmin, ymin, xmax, ymax) { |
|
var code = 0; |
|
if (y > ymax) |
|
code |= TOP; |
|
else if (y < ymin) |
|
code |= BOTTOM; |
|
if (x > xmax) |
|
code |= RIGHT; |
|
else if (x < xmin) |
|
code |= LEFT; |
|
return code; |
|
} |
|
|
|
/** |
|
* Returns TRUE if the rectangle is over the edge and has intersection points or includes it |
|
* @param {Object} x1 Point A of the line |
|
* @param {Object} y1 |
|
* @param {Object} x2 Point B of the line |
|
* @param {Object} y2 |
|
* @param {Object} xmin Point A of the rectangle |
|
* @param {Object} ymin |
|
* @param {Object} xmax Point B of the rectangle |
|
* @param {Object} ymax |
|
*/ |
|
ORYX.Core.Math.isRectOverLine = function(x1, y1, x2, y2, xmin, ymin, xmax, ymax){ |
|
return !!ORYX.Core.Math.clipLineOnRect.apply(ORYX.Core.Math, arguments); |
|
} |
|
|
|
/** |
|
* Returns the clipped line on the given rectangle. If there is |
|
* no intersection, it will return NULL. |
|
* |
|
* @param {Object} x1 Point A of the line |
|
* @param {Object} y1 |
|
* @param {Object} x2 Point B of the line |
|
* @param {Object} y2 |
|
* @param {Object} xmin Point A of the rectangle |
|
* @param {Object} ymin |
|
* @param {Object} xmax Point B of the rectangle |
|
* @param {Object} ymax |
|
*/ |
|
ORYX.Core.Math.clipLineOnRect = function(x1, y1, x2, y2, xmin, ymin, xmax, ymax){ |
|
//Outcodes for P0, P1, and whatever point lies outside the clip rectangle |
|
var outcode0, outcode1, outcodeOut, hhh = 0; |
|
var accept = false, done = false; |
|
|
|
//compute outcodes |
|
outcode0 = computeOutCode(x1, y1, xmin, ymin, xmax, ymax); |
|
outcode1 = computeOutCode(x2, y2, xmin, ymin, xmax, ymax); |
|
|
|
do { |
|
if ((outcode0 | outcode1) == 0 ){ |
|
accept = true; |
|
done = true; |
|
} else if ( (outcode0 & outcode1) > 0 ) { |
|
done = true; |
|
} else { |
|
//failed both tests, so calculate the line segment to clip |
|
//from an outside point to an intersection with clip edge |
|
var x = 0, y = 0; |
|
//At least one endpoint is outside the clip rectangle; pick it. |
|
outcodeOut = outcode0 != 0 ? outcode0: outcode1; |
|
//Now find the intersection point; |
|
//use formulas y = y0 + slope * (x - x0), x = x0 + (1/slope)* (y - y0) |
|
if ( (outcodeOut & TOP) > 0 ) { |
|
x = x1 + (x2 - x1) * (ymax - y1)/(y2 - y1); |
|
y = ymax; |
|
} else if ((outcodeOut & BOTTOM) > 0 ) { |
|
x = x1 + (x2 - x1) * (ymin - y1)/(y2 - y1); |
|
y = ymin; |
|
} else if ((outcodeOut & RIGHT)> 0) { |
|
y = y1 + (y2 - y1) * (xmax - x1)/(x2 - x1); |
|
x = xmax; |
|
} else if ((outcodeOut & LEFT) > 0) { |
|
y = y1 + (y2 - y1) * (xmin - x1)/(x2 - x1); |
|
x = xmin; |
|
} |
|
|
|
//Now we move outside point to intersection point to clip |
|
//and get ready for next pass. |
|
if (outcodeOut == outcode0) { |
|
x1 = x; |
|
y1 = y; |
|
outcode0 = computeOutCode (x1, y1, xmin, ymin, xmax, ymax); |
|
} else { |
|
x2 = x; |
|
y2 = y; |
|
outcode1 = computeOutCode (x2, y2, xmin, ymin, xmax, ymax); |
|
} |
|
} |
|
hhh ++; |
|
} while (done != true && hhh < 5000); |
|
|
|
if(accept) { |
|
return {a:{x:x1, y:y1}, b:{x:x2, y:y2}}; |
|
} |
|
return null; |
|
} |
|
}(); |
|
|
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespace |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.StencilSet) {ORYX.Core.StencilSet = {};} |
|
|
|
/** |
|
* Class Stencil |
|
* uses Prototpye 1.5.0 |
|
* uses Inheritance |
|
* |
|
* This class represents one stencil of a stencil set. |
|
*/ |
|
ORYX.Core.StencilSet.Stencil = { |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(jsonStencil, namespace, source, stencilSet, propertyPackages, defaultPosition) { |
|
arguments.callee.$.construct.apply(this, arguments); // super(); |
|
|
|
// check arguments and set defaults. |
|
if(!jsonStencil) throw "Stencilset seems corrupt."; |
|
if(!namespace) throw "Stencil does not provide namespace."; |
|
if(!source) throw "Stencil does not provide SVG source."; |
|
if(!stencilSet) throw "Fatal internal error loading stencilset."; |
|
//if(!propertyPackages) throw "Fatal internal error loading stencilset."; |
|
|
|
this._source = source; |
|
this._jsonStencil = jsonStencil; |
|
this._stencilSet = stencilSet; |
|
this._namespace = namespace; |
|
this._propertyPackages = propertyPackages; |
|
|
|
if(defaultPosition && !this._jsonStencil.position) |
|
this._jsonStencil.position = defaultPosition; |
|
|
|
this._view; |
|
this._properties = new Hash(); |
|
|
|
// check stencil consistency and set defaults. |
|
/*with(this._jsonStencil) { |
|
|
|
if(!type) throw "Stencil does not provide type."; |
|
if((type != "edge") && (type != "node")) |
|
throw "Stencil type must be 'edge' or 'node'."; |
|
if(!id || id == "") throw "Stencil does not provide valid id."; |
|
if(!title || title == "") |
|
throw "Stencil does not provide title"; |
|
if(!description) { description = ""; }; |
|
if(!groups) { groups = []; } |
|
if(!roles) { roles = []; } |
|
|
|
// add id of stencil to its roles |
|
roles.push(id); |
|
}*/ |
|
|
|
//init all JSON values |
|
if(!this._jsonStencil.type || !(this._jsonStencil.type === "edge" || this._jsonStencil.type === "node")) { |
|
throw "ORYX.Core.StencilSet.Stencil(construct): Type is not defined."; |
|
} |
|
if(!this._jsonStencil.id || this._jsonStencil.id === "") { |
|
throw "ORYX.Core.StencilSet.Stencil(construct): Id is not defined."; |
|
} |
|
if(!this._jsonStencil.title || this._jsonStencil.title === "") { |
|
throw "ORYX.Core.StencilSet.Stencil(construct): Title is not defined."; |
|
} |
|
|
|
if(!this._jsonStencil.description) { this._jsonStencil.description = ""; }; |
|
if(!this._jsonStencil.groups) { this._jsonStencil.groups = []; } |
|
if(!this._jsonStencil.roles) { this._jsonStencil.roles = []; } |
|
|
|
//add id of stencil to its roles |
|
this._jsonStencil.roles.push(this._jsonStencil.id); |
|
|
|
//prepend namespace to each role |
|
this._jsonStencil.roles.each((function(role, index) { |
|
this._jsonStencil.roles[index] = namespace + role; |
|
}).bind(this)); |
|
|
|
//delete duplicate roles |
|
this._jsonStencil.roles = this._jsonStencil.roles.uniq(); |
|
|
|
//make id unique by prepending namespace of stencil set |
|
this._jsonStencil.id = namespace + this._jsonStencil.id; |
|
|
|
this.postProcessProperties(); |
|
|
|
// init serialize callback |
|
if(!this._jsonStencil.serialize) { |
|
this._jsonStencil.serialize = {}; |
|
//this._jsonStencil.serialize = function(shape, data) { return data;}; |
|
} |
|
|
|
// init deserialize callback |
|
if(!this._jsonStencil.deserialize) { |
|
this._jsonStencil.deserialize = {}; |
|
//this._jsonStencil.deserialize = function(shape, data) { return data;}; |
|
} |
|
|
|
// init layout callback |
|
if(!this._jsonStencil.layout) { |
|
this._jsonStencil.layout = [] |
|
//this._jsonStencil.layout = function() {return true;} |
|
} |
|
|
|
//TODO does not work correctly, if the url does not exist |
|
//How to guarantee that the view is loaded correctly before leaving the constructor??? |
|
var url = source + "view/" + jsonStencil.view; |
|
// override content type when this is webkit. |
|
|
|
if(this._jsonStencil.view.trim().match(/</)) { |
|
var parser = new DOMParser(); |
|
var xml = parser.parseFromString( this._jsonStencil.view ,"text/xml"); |
|
|
|
//check if result is a SVG document |
|
if( ORYX.Editor.checkClassType( xml.documentElement, SVGSVGElement )) { |
|
|
|
this._view = xml.documentElement; |
|
|
|
} else { |
|
throw "ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document." |
|
} |
|
} else { |
|
new Ajax.Request( |
|
url, { |
|
asynchronous:false, method:'get', |
|
onSuccess:this._loadSVGOnSuccess.bind(this), |
|
onFailure:this._loadSVGOnFailure.bind(this) |
|
}); |
|
} |
|
}, |
|
|
|
postProcessProperties: function() { |
|
|
|
// init property packages |
|
if(this._jsonStencil.propertyPackages && this._jsonStencil.propertyPackages instanceof Array) { |
|
|
|
this._jsonStencil.propertyPackages.each((function(ppId) { |
|
var pp = this._propertyPackages[ppId]; |
|
|
|
if(pp) { |
|
pp.each((function(prop){ |
|
var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this); |
|
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp; |
|
|
|
}).bind(this)); |
|
} |
|
}).bind(this)); |
|
} |
|
|
|
// init properties |
|
if(this._jsonStencil.properties && this._jsonStencil.properties instanceof Array) { |
|
this._jsonStencil.properties.each((function(prop) { |
|
var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this); |
|
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp; |
|
}).bind(this)); |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* @param {ORYX.Core.StencilSet.Stencil} stencil |
|
* @return {Boolean} True, if stencil has the same namespace and type. |
|
*/ |
|
equals: function(stencil) { |
|
return (this.id() === stencil.id()); |
|
}, |
|
|
|
stencilSet: function() { |
|
return this._stencilSet; |
|
}, |
|
|
|
type: function() { |
|
return this._jsonStencil.type; |
|
}, |
|
|
|
namespace: function() { |
|
return this._namespace; |
|
}, |
|
|
|
id: function() { |
|
return this._jsonStencil.id; |
|
}, |
|
|
|
idWithoutNs: function(){ |
|
return this.id().replace(this.namespace(),""); |
|
}, |
|
|
|
title: function() { |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "title"); |
|
}, |
|
|
|
description: function() { |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "description"); |
|
}, |
|
|
|
groups: function() { |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "groups"); |
|
}, |
|
|
|
position: function() { |
|
return (isNaN(this._jsonStencil.position) ? 0 : this._jsonStencil.position); |
|
}, |
|
|
|
view: function() { |
|
return this._view.cloneNode(true) || this._view; |
|
}, |
|
|
|
icon: function() { |
|
return this._jsonStencil.icon; |
|
}, |
|
|
|
fixedAspectRatio: function() { |
|
return this._jsonStencil.fixedAspectRatio === true; |
|
}, |
|
|
|
hasMultipleRepositoryEntries: function() { |
|
return (this.getRepositoryEntries().length > 0); |
|
}, |
|
|
|
getRepositoryEntries: function() { |
|
return (this._jsonStencil.repositoryEntries) ? |
|
$A(this._jsonStencil.repositoryEntries) : $A([]); |
|
}, |
|
|
|
properties: function() { |
|
return this._properties.values(); |
|
}, |
|
|
|
property: function(id) { |
|
return this._properties[id]; |
|
}, |
|
|
|
roles: function() { |
|
return this._jsonStencil.roles; |
|
}, |
|
|
|
defaultAlign: function() { |
|
if(!this._jsonStencil.defaultAlign) |
|
return "east"; |
|
return this._jsonStencil.defaultAlign; |
|
}, |
|
|
|
serialize: function(shape, data) { |
|
return this._jsonStencil.serialize; |
|
//return this._jsonStencil.serialize(shape, data); |
|
}, |
|
|
|
deserialize: function(shape, data) { |
|
return this._jsonStencil.deserialize; |
|
//return this._jsonStencil.deserialize(shape, data); |
|
}, |
|
|
|
// in which case is targetShape used? |
|
// layout: function(shape, targetShape) { |
|
// return this._jsonStencil.layout(shape, targetShape); |
|
// }, |
|
// layout property to store events for layouting in plugins |
|
layout: function(shape) { |
|
return this._jsonStencil.layout |
|
}, |
|
|
|
addProperty: function(property, namespace) { |
|
if(property && namespace) { |
|
var oProp = new ORYX.Core.StencilSet.Property(property, namespace, this); |
|
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp; |
|
} |
|
}, |
|
|
|
removeProperty: function(propertyId) { |
|
if(propertyId) { |
|
var oProp = this._properties.values().find(function(prop) { |
|
return (propertyId == prop.id()); |
|
}); |
|
if(oProp) |
|
delete this._properties[oProp.prefix() + "-" + oProp.id()]; |
|
} |
|
}, |
|
|
|
_loadSVGOnSuccess: function(result) { |
|
|
|
var xml = null; |
|
|
|
/* |
|
* We want to get a dom object for the requested file. Unfortunately, |
|
* safari has some issues here. this is meant as a fallback for all |
|
* browsers that don't recognize the svg mimetype as XML but support |
|
* data: urls on Ajax calls. |
|
*/ |
|
|
|
// responseXML != undefined. |
|
// if(!(result.responseXML)) |
|
|
|
// get the dom by data: url. |
|
// xml = _evenMoreEvilHack(result.responseText, 'text/xml'); |
|
|
|
// else |
|
|
|
// get it the usual way. |
|
xml = result.responseXML; |
|
|
|
//check if result is a SVG document |
|
if( ORYX.Editor.checkClassType( xml.documentElement, SVGSVGElement )) { |
|
|
|
this._view = xml.documentElement; |
|
|
|
} else { |
|
throw "ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document." |
|
} |
|
}, |
|
|
|
_loadSVGOnFailure: function(result) { |
|
throw "ORYX.Core.StencilSet.Stencil(_loadSVGOnFailure): Loading SVG document failed." |
|
}, |
|
|
|
toString: function() { return "Stencil " + this.title() + " (" + this.id() + ")"; } |
|
}; |
|
|
|
ORYX.Core.StencilSet.Stencil = Clazz.extend(ORYX.Core.StencilSet.Stencil); |
|
|
|
/** |
|
* Transform a string into an xml document, the Safari way, as long as |
|
* the nightlies are broken. Even more evil version. |
|
* @param {Object} str |
|
* @param {Object} contentType |
|
*/ |
|
function _evenMoreEvilHack(str, contentType) { |
|
|
|
/* |
|
* This even more evil hack was taken from |
|
* http://web-graphics.com/mtarchive/001606.php#chatty004999 |
|
*/ |
|
|
|
if (window.ActiveXObject) { |
|
var d = new ActiveXObject("MSXML.DomDocument"); |
|
d.loadXML(str); |
|
return d; |
|
} else if (window.XMLHttpRequest) { |
|
var req = new XMLHttpRequest; |
|
req.open("GET", "data:" + (contentType || "application/xml") + |
|
";charset=utf-8," + encodeURIComponent(str), false); |
|
if (req.overrideMimeType) { |
|
req.overrideMimeType(contentType); |
|
} |
|
req.send(null); |
|
return req.responseXML; |
|
} |
|
} |
|
|
|
/** |
|
* Transform a string into an xml document, the Safari way, as long as |
|
* the nightlies are broken. |
|
* @param {Object} result the xml document object. |
|
*/ |
|
function _evilSafariHack(serializedXML) { |
|
|
|
/* |
|
* The Dave way. Taken from: |
|
* http://web-graphics.com/mtarchive/001606.php |
|
* |
|
* There is another possibility to parse XML in Safari, by implementing |
|
* the DOMParser in javascript. However, in the latest nightlies of |
|
* WebKit, DOMParser is already available, but still buggy. So, this is |
|
* the best compromise for the time being. |
|
*/ |
|
|
|
var xml = serializedXML; |
|
var url = "data:text/xml;charset=utf-8," + encodeURIComponent(xml); |
|
var dom = null; |
|
|
|
// your standard AJAX stuff |
|
var req = new XMLHttpRequest(); |
|
req.open("GET", url); |
|
req.onload = function() { dom = req.responseXML; } |
|
req.send(null); |
|
|
|
return dom; |
|
} |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespace |
|
*/ |
|
if (!ORYX) { |
|
var ORYX = {}; |
|
} |
|
if (!ORYX.Core) { |
|
ORYX.Core = {}; |
|
} |
|
if (!ORYX.Core.StencilSet) { |
|
ORYX.Core.StencilSet = {}; |
|
} |
|
|
|
/** |
|
* Class Property |
|
* uses Prototpye 1.5.0 |
|
* uses Inheritance |
|
*/ |
|
ORYX.Core.StencilSet.Property = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(jsonProp, namespace, stencil){ |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this._jsonProp = jsonProp || ORYX.Log.error("Parameter jsonProp is not defined."); |
|
this._namespace = namespace || ORYX.Log.error("Parameter namespace is not defined."); |
|
this._stencil = stencil || ORYX.Log.error("Parameter stencil is not defined."); |
|
|
|
this._items = {}; |
|
this._complexItems = {}; |
|
|
|
// Flag to indicate whether or not the property should be hidden |
|
// This can be for example when the stencil set is upgraded, but the model |
|
// has a value for that specific property filled in which we still want to show. |
|
// If the value is missing, the property can simply be not shown. |
|
this._hidden = false; |
|
|
|
jsonProp.id = jsonProp.id || ORYX.Log.error("ORYX.Core.StencilSet.Property(construct): Id is not defined."); |
|
jsonProp.id = jsonProp.id.toLowerCase(); |
|
|
|
if (!jsonProp.type) { |
|
ORYX.Log.info("Type is not defined for stencil '%0', id '%1'. Falling back to 'String'.", stencil, jsonProp.id); |
|
jsonProp.type = "string"; |
|
} |
|
else { |
|
jsonProp.type = jsonProp.type.toLowerCase(); |
|
} |
|
|
|
jsonProp.prefix = jsonProp.prefix || "oryx"; |
|
jsonProp.title = jsonProp.title || ""; |
|
jsonProp.value = jsonProp.value || ""; |
|
jsonProp.description = jsonProp.description || ""; |
|
jsonProp.readonly = jsonProp.readonly || false; |
|
jsonProp.optional = jsonProp.optional !== false; |
|
|
|
//init refToView |
|
if (this._jsonProp.refToView) { |
|
if (!(this._jsonProp.refToView instanceof Array)) { |
|
this._jsonProp.refToView = [this._jsonProp.refToView]; |
|
} |
|
} |
|
else { |
|
this._jsonProp.refToView = []; |
|
} |
|
|
|
var globalMin = this.getMinForType(jsonProp.type); |
|
if (jsonProp.min === undefined || jsonProp.min === null) { |
|
jsonProp.min =globalMin; |
|
} else if (jsonProp.min < globalMin) { |
|
jsonProp.min = globalMin; |
|
} |
|
|
|
var globalMax = this.getMaxForType(jsonProp.type); |
|
if (jsonProp.max === undefined || jsonProp.max === null) { |
|
jsonProp.max = globalMax; |
|
} else if (jsonProp.max > globalMax) { |
|
jsonProp.min = globalMax; |
|
} |
|
|
|
if (!jsonProp.fillOpacity) { |
|
jsonProp.fillOpacity = false; |
|
} |
|
|
|
if ("number" != typeof jsonProp.lightness) { |
|
jsonProp.lightness = 1; |
|
} else { |
|
jsonProp.lightness = Math.max(0, Math.min(1, jsonProp.lightness)); |
|
} |
|
|
|
if (!jsonProp.strokeOpacity) { |
|
jsonProp.strokeOpacity = false; |
|
} |
|
|
|
if (jsonProp.length === undefined || jsonProp.length === null) { |
|
jsonProp.length = Number.MAX_VALUE; |
|
} |
|
|
|
if (!jsonProp.wrapLines) { |
|
jsonProp.wrapLines = false; |
|
} |
|
|
|
if (!jsonProp.dateFormat) { |
|
jsonProp.dateFormat = ORYX.I18N.PropertyWindow.dateFormat || "m/d/y"; |
|
} |
|
|
|
if (!jsonProp.fill) { |
|
jsonProp.fill = false; |
|
} |
|
|
|
if (!jsonProp.stroke) { |
|
jsonProp.stroke = false; |
|
} |
|
|
|
if(!jsonProp.inverseBoolean) { |
|
jsonProp.inverseBoolean = false; |
|
} |
|
|
|
if(!jsonProp.directlyEditable && jsonProp.directlyEditable != false) { |
|
jsonProp.directlyEditable = true; |
|
} |
|
|
|
if(jsonProp.visible !== false) { |
|
jsonProp.visible = true; |
|
} |
|
|
|
if(jsonProp.isList !== true) { |
|
jsonProp.isList = false; |
|
|
|
if(!jsonProp.list || !(jsonProp.list instanceof Array)) { |
|
jsonProp.list = []; |
|
} |
|
} |
|
|
|
if(!jsonProp.category) { |
|
if (jsonProp.popular) { |
|
jsonProp.category = "popular"; |
|
} else { |
|
jsonProp.category = "others"; |
|
} |
|
} |
|
|
|
if(!jsonProp.alwaysAppearInMultiselect) { |
|
jsonProp.alwaysAppearInMultiselect = false; |
|
} |
|
|
|
if (jsonProp.type === ORYX.CONFIG.TYPE_CHOICE) { |
|
if (jsonProp.items && jsonProp.items instanceof Array) { |
|
jsonProp.items.each((function(jsonItem){ |
|
// why is the item's value used as the key??? |
|
this._items[jsonItem.value.toLowerCase()] = new ORYX.Core.StencilSet.PropertyItem(jsonItem, namespace, this); |
|
}).bind(this)); |
|
} |
|
else { |
|
throw "ORYX.Core.StencilSet.Property(construct): No property items defined." |
|
} |
|
// extended by Kerstin (start) |
|
} |
|
else |
|
if (jsonProp.type === ORYX.CONFIG.TYPE_COMPLEX || jsonProp.type == ORYX.CONFIG.TYPE_MULTIPLECOMPLEX) { |
|
if (jsonProp.complexItems && jsonProp.complexItems instanceof Array) { |
|
jsonProp.complexItems.each((function(jsonComplexItem){ |
|
this._complexItems[jsonComplexItem.id.toLowerCase()] = new ORYX.Core.StencilSet.ComplexPropertyItem(jsonComplexItem, namespace, this); |
|
}).bind(this)); |
|
} |
|
} |
|
// extended by Kerstin (end) |
|
}, |
|
|
|
getMinForType : function(type) { |
|
if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) { |
|
return -Math.pow(2,31) |
|
} else { |
|
return -Number.MAX_VALUE+1; |
|
} |
|
}, |
|
getMaxForType : function(type) { |
|
if (type.toLowerCase() == ORYX.CONFIG.TYPE_INTEGER) { |
|
return Math.pow(2,31)-1 |
|
} else { |
|
return Number.MAX_VALUE; |
|
} |
|
}, |
|
|
|
/** |
|
* @param {ORYX.Core.StencilSet.Property} property |
|
* @return {Boolean} True, if property has the same namespace and id. |
|
*/ |
|
equals: function(property){ |
|
return (this._namespace === property.namespace() && |
|
this.id() === property.id()) ? true : false; |
|
}, |
|
|
|
namespace: function(){ |
|
return this._namespace; |
|
}, |
|
|
|
stencil: function(){ |
|
return this._stencil; |
|
}, |
|
|
|
id: function(){ |
|
return this._jsonProp.id; |
|
}, |
|
|
|
prefix: function(){ |
|
return this._jsonProp.prefix; |
|
}, |
|
|
|
type: function(){ |
|
return this._jsonProp.type; |
|
}, |
|
|
|
inverseBoolean: function() { |
|
return this._jsonProp.inverseBoolean; |
|
}, |
|
|
|
category: function() { |
|
return this._jsonProp.category; |
|
}, |
|
|
|
setCategory: function(value) { |
|
this._jsonProp.category = value; |
|
}, |
|
|
|
directlyEditable: function() { |
|
return this._jsonProp.directlyEditable; |
|
}, |
|
|
|
visible: function() { |
|
return this._jsonProp.visible; |
|
}, |
|
|
|
title: function(){ |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "title"); |
|
}, |
|
|
|
value: function(){ |
|
return this._jsonProp.value; |
|
}, |
|
|
|
readonly: function(){ |
|
return this._jsonProp.readonly; |
|
}, |
|
|
|
optional: function(){ |
|
return this._jsonProp.optional; |
|
}, |
|
|
|
description: function(){ |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonProp, "description"); |
|
}, |
|
|
|
/** |
|
* An optional link to a SVG element so that the property affects the |
|
* graphical representation of the stencil. |
|
*/ |
|
refToView: function(){ |
|
return this._jsonProp.refToView; |
|
}, |
|
|
|
/** |
|
* If type is integer or float, min is the lower bounds of value. |
|
*/ |
|
min: function(){ |
|
return this._jsonProp.min; |
|
}, |
|
|
|
/** |
|
* If type ist integer or float, max is the upper bounds of value. |
|
*/ |
|
max: function(){ |
|
return this._jsonProp.max; |
|
}, |
|
|
|
/** |
|
* If type is float, this method returns if the fill-opacity property should |
|
* be set. |
|
* @return {Boolean} |
|
*/ |
|
fillOpacity: function(){ |
|
return this._jsonProp.fillOpacity; |
|
}, |
|
|
|
/** |
|
* If type is float, this method returns if the stroke-opacity property should |
|
* be set. |
|
* @return {Boolean} |
|
*/ |
|
strokeOpacity: function(){ |
|
return this._jsonProp.strokeOpacity; |
|
}, |
|
|
|
/** |
|
* If type is string or richtext, length is the maximum length of the text. |
|
* TODO how long can a string be. |
|
*/ |
|
length: function(){ |
|
return this._jsonProp.length ? this._jsonProp.length : Number.MAX_VALUE; |
|
}, |
|
|
|
wrapLines: function(){ |
|
return this._jsonProp.wrapLines; |
|
}, |
|
|
|
/** |
|
* If type is date, dateFormat specifies the format of the date. The format |
|
* specification of the ext library is used: |
|
* |
|
* Format Output Description |
|
* ------ ---------- -------------------------------------------------------------- |
|
* d 10 Day of the month, 2 digits with leading zeros |
|
* D Wed A textual representation of a day, three letters |
|
* j 10 Day of the month without leading zeros |
|
* l Wednesday A full textual representation of the day of the week |
|
* S th English ordinal day of month suffix, 2 chars (use with j) |
|
* w 3 Numeric representation of the day of the week |
|
* z 9 The julian date, or day of the year (0-365) |
|
* W 01 ISO-8601 2-digit week number of year, weeks starting on Monday (00-52) |
|
* F January A full textual representation of the month |
|
* m 01 Numeric representation of a month, with leading zeros |
|
* M Jan Month name abbreviation, three letters |
|
* n 1 Numeric representation of a month, without leading zeros |
|
* t 31 Number of days in the given month |
|
* L 0 Whether its a leap year (1 if it is a leap year, else 0) |
|
* Y 2007 A full numeric representation of a year, 4 digits |
|
* y 07 A two digit representation of a year |
|
* a pm Lowercase Ante meridiem and Post meridiem |
|
* A PM Uppercase Ante meridiem and Post meridiem |
|
* g 3 12-hour format of an hour without leading zeros |
|
* G 15 24-hour format of an hour without leading zeros |
|
* h 03 12-hour format of an hour with leading zeros |
|
* H 15 24-hour format of an hour with leading zeros |
|
* i 05 Minutes with leading zeros |
|
* s 01 Seconds, with leading zeros |
|
* O -0600 Difference to Greenwich time (GMT) in hours |
|
* T CST Timezone setting of the machine running the code |
|
* Z -21600 Timezone offset in seconds (negative if west of UTC, positive if east) |
|
* |
|
* Example: |
|
* F j, Y, g:i a -> January 10, 2007, 3:05 pm |
|
*/ |
|
dateFormat: function(){ |
|
return this._jsonProp.dateFormat; |
|
}, |
|
|
|
/** |
|
* If type is color, this method returns if the fill property should |
|
* be set. |
|
* @return {Boolean} |
|
*/ |
|
fill: function(){ |
|
return this._jsonProp.fill; |
|
}, |
|
|
|
/** |
|
* Lightness defines the satiation of the color |
|
* 0 is the pure color |
|
* 1 is white |
|
* @return {Integer} lightness |
|
*/ |
|
lightness: function(){ |
|
return this._jsonProp.lightness; |
|
}, |
|
|
|
/** |
|
* If type is color, this method returns if the stroke property should |
|
* be set. |
|
* @return {Boolean} |
|
*/ |
|
stroke: function(){ |
|
return this._jsonProp.stroke; |
|
}, |
|
|
|
/** |
|
* If type is choice, items is a hash map with all alternative values |
|
* (PropertyItem objects) with id as keys. |
|
*/ |
|
items: function(){ |
|
return $H(this._items).values(); |
|
}, |
|
|
|
item: function(value){ |
|
if (value) { |
|
return this._items[value.toLowerCase()]; |
|
} else { |
|
return null; |
|
} |
|
}, |
|
|
|
toString: function(){ |
|
return "Property " + this.title() + " (" + this.id() + ")"; |
|
}, |
|
|
|
complexItems: function(){ |
|
return $H(this._complexItems).values(); |
|
}, |
|
|
|
complexItem: function(id){ |
|
if(id) { |
|
return this._complexItems[id.toLowerCase()]; |
|
} else { |
|
return null; |
|
} |
|
|
|
}, |
|
|
|
complexAttributeToView: function(){ |
|
return this._jsonProp.complexAttributeToView || ""; |
|
}, |
|
|
|
isList: function() { |
|
return !!this._jsonProp.isList; |
|
}, |
|
|
|
getListItems: function() { |
|
return this._jsonProp.list; |
|
}, |
|
|
|
/** |
|
* If type is glossary link, the |
|
* type of category can be defined where |
|
* the link only can go to. |
|
* @return {String} The glossary category id |
|
*/ |
|
linkableType: function(){ |
|
return this._jsonProp.linkableType || ""; |
|
}, |
|
|
|
alwaysAppearInMultiselect : function() { |
|
return this._jsonProp.alwaysAppearInMultiselect; |
|
}, |
|
|
|
popular: function() { |
|
return this._jsonProp.popular || false; |
|
}, |
|
|
|
setPopular: function() { |
|
this._jsonProp.popular = true; |
|
}, |
|
|
|
hide: function() { |
|
this._hidden = true; |
|
}, |
|
|
|
isHidden: function() { |
|
return this._hidden; |
|
} |
|
|
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespace |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.StencilSet) {ORYX.Core.StencilSet = {};} |
|
|
|
/** |
|
* Class Stencil |
|
* uses Prototpye 1.5.0 |
|
* uses Inheritance |
|
*/ |
|
ORYX.Core.StencilSet.PropertyItem = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(jsonItem, namespace, property) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
if(!jsonItem) { |
|
throw "ORYX.Core.StencilSet.PropertyItem(construct): Parameter jsonItem is not defined."; |
|
} |
|
if(!namespace) { |
|
throw "ORYX.Core.StencilSet.PropertyItem(construct): Parameter namespace is not defined."; |
|
} |
|
if(!property) { |
|
throw "ORYX.Core.StencilSet.PropertyItem(construct): Parameter property is not defined."; |
|
} |
|
|
|
this._jsonItem = jsonItem; |
|
this._namespace = namespace; |
|
this._property = property; |
|
|
|
//init all values |
|
if(!jsonItem.value) { |
|
throw "ORYX.Core.StencilSet.PropertyItem(construct): Value is not defined."; |
|
} |
|
|
|
if(this._jsonItem.refToView) { |
|
if(!(this._jsonItem.refToView instanceof Array)) { |
|
this._jsonItem.refToView = [this._jsonItem.refToView]; |
|
} |
|
} else { |
|
this._jsonItem.refToView = []; |
|
} |
|
}, |
|
|
|
/** |
|
* @param {ORYX.Core.StencilSet.PropertyItem} item |
|
* @return {Boolean} True, if item has the same namespace and id. |
|
*/ |
|
equals: function(item) { |
|
return (this.property().equals(item.property()) && |
|
this.value() === item.value()); |
|
}, |
|
|
|
namespace: function() { |
|
return this._namespace; |
|
}, |
|
|
|
property: function() { |
|
return this._property; |
|
}, |
|
|
|
value: function() { |
|
return this._jsonItem.value; |
|
}, |
|
|
|
title: function() { |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonItem, "title"); |
|
}, |
|
|
|
refToView: function() { |
|
return this._jsonItem.refToView; |
|
}, |
|
|
|
icon: function() { |
|
return (this._jsonItem.icon) ? this.property().stencil()._source + "icons/" + this._jsonItem.icon : ""; |
|
}, |
|
|
|
toString: function() { return "PropertyItem " + this.property() + " (" + this.value() + ")"; } |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.StencilSet) {ORYX.Core.StencilSet = {};} |
|
|
|
/** |
|
* Class Stencil |
|
* uses Prototpye 1.5.0 |
|
* uses Inheritance |
|
*/ |
|
ORYX.Core.StencilSet.ComplexPropertyItem = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(jsonItem, namespace, property) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
if(!jsonItem) { |
|
throw "ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter jsonItem is not defined."; |
|
} |
|
if(!namespace) { |
|
throw "ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter namespace is not defined."; |
|
} |
|
if(!property) { |
|
throw "ORYX.Core.StencilSet.ComplexPropertyItem(construct): Parameter property is not defined."; |
|
} |
|
|
|
this._jsonItem = jsonItem; |
|
this._namespace = namespace; |
|
this._property = property; |
|
this._items = new Hash(); |
|
this._complexItems = new Hash(); |
|
|
|
//init all values |
|
if(!jsonItem.name) { |
|
throw "ORYX.Core.StencilSet.ComplexPropertyItem(construct): Name is not defined."; |
|
} |
|
|
|
if(!jsonItem.type) { |
|
throw "ORYX.Core.StencilSet.ComplexPropertyItem(construct): Type is not defined."; |
|
} else { |
|
jsonItem.type = jsonItem.type.toLowerCase(); |
|
} |
|
|
|
if(jsonItem.type === ORYX.CONFIG.TYPE_CHOICE) { |
|
if(jsonItem.items && jsonItem.items instanceof Array) { |
|
jsonItem.items.each((function(item) { |
|
this._items[item.value] = new ORYX.Core.StencilSet.PropertyItem(item, namespace, this); |
|
}).bind(this)); |
|
} else { |
|
throw "ORYX.Core.StencilSet.Property(construct): No property items defined." |
|
} |
|
} else if(jsonItem.type === ORYX.CONFIG.TYPE_COMPLEX) { |
|
if(jsonItem.complexItems && jsonItem.complexItems instanceof Array) { |
|
jsonItem.complexItems.each((function(complexItem) { |
|
this._complexItems[complexItem.id] = new ORYX.Core.StencilSet.ComplexPropertyItem(complexItem, namespace, this); |
|
}).bind(this)); |
|
} else { |
|
throw "ORYX.Core.StencilSet.Property(construct): No property items defined." |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* @param {ORYX.Core.StencilSet.PropertyItem} item |
|
* @return {Boolean} True, if item has the same namespace and id. |
|
*/ |
|
equals: function(item) { |
|
return (this.property().equals(item.property()) && |
|
this.name() === item.name()); |
|
}, |
|
|
|
namespace: function() { |
|
return this._namespace; |
|
}, |
|
|
|
property: function() { |
|
return this._property; |
|
}, |
|
|
|
name: function() { |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonItem, "name"); |
|
}, |
|
|
|
id: function() { |
|
return this._jsonItem.id; |
|
}, |
|
|
|
type: function() { |
|
return this._jsonItem.type; |
|
}, |
|
|
|
optional: function() { |
|
return this._jsonItem.optional; |
|
}, |
|
|
|
width: function() { |
|
return this._jsonItem.width; |
|
}, |
|
|
|
value: function() { |
|
return this._jsonItem.value; |
|
}, |
|
|
|
items: function() { |
|
return this._items.values(); |
|
}, |
|
|
|
complexItems: function() { |
|
return this._complexItems.values(); |
|
}, |
|
|
|
disable: function() { |
|
return this._jsonItem.disable; |
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.StencilSet) {ORYX.Core.StencilSet = {};} |
|
|
|
/** |
|
* Class Rules uses Prototpye 1.5.0 uses Inheritance |
|
* |
|
* This class implements the API to check the stencil sets' rules. |
|
*/ |
|
ORYX.Core.StencilSet.Rules = { |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this._stencilSets = []; |
|
this._stencils = []; |
|
this._containerStencils = []; |
|
|
|
this._cachedConnectSET = new Hash(); |
|
this._cachedConnectSE = new Hash(); |
|
this._cachedConnectTE = new Hash(); |
|
this._cachedCardSE = new Hash(); |
|
this._cachedCardTE = new Hash(); |
|
this._cachedContainPC = new Hash(); |
|
this._cachedMorphRS = new Hash(); |
|
|
|
this._connectionRules = new Hash(); |
|
this._cardinalityRules = new Hash(); |
|
this._containmentRules = new Hash(); |
|
this._morphingRules = new Hash(); |
|
this._layoutRules = new Hash(); |
|
}, |
|
|
|
/** |
|
* Call this method to initialize the rules for a stencil set and all of its |
|
* active extensions. |
|
* |
|
* @param {Object} |
|
* stencilSet |
|
*/ |
|
initializeRules: function(stencilSet) { |
|
|
|
var existingSS = this._stencilSets.find(function(ss) { |
|
return (ss.namespace() == stencilSet.namespace()); |
|
}); |
|
if (existingSS) { |
|
// reinitialize all rules |
|
var stencilsets = this._stencilSets.clone(); |
|
stencilsets = stencilsets.without(existingSS); |
|
stencilsets.push(stencilSet); |
|
|
|
this._stencilSets = []; |
|
this._stencils = []; |
|
this._containerStencils = []; |
|
|
|
this._cachedConnectSET = new Hash(); |
|
this._cachedConnectSE = new Hash(); |
|
this._cachedConnectTE = new Hash(); |
|
this._cachedCardSE = new Hash(); |
|
this._cachedCardTE = new Hash(); |
|
this._cachedContainPC = new Hash(); |
|
this._cachedMorphRS = new Hash(); |
|
|
|
this._connectionRules = new Hash(); |
|
this._cardinalityRules = new Hash(); |
|
this._containmentRules = new Hash(); |
|
this._morphingRules = new Hash(); |
|
this._layoutRules = new Hash(); |
|
|
|
stencilsets.each(function(ss){ |
|
this.initializeRules(ss); |
|
}.bind(this)); |
|
return; |
|
} |
|
else { |
|
this._stencilSets.push(stencilSet); |
|
|
|
var jsonRules = new Hash(stencilSet.jsonRules()); |
|
var namespace = stencilSet.namespace(); |
|
var stencils = stencilSet.stencils(); |
|
|
|
stencilSet.extensions().values().each(function(extension) { |
|
if(extension.rules) { |
|
if(extension.rules.connectionRules) |
|
jsonRules.connectionRules = jsonRules.connectionRules.concat(extension.rules.connectionRules); |
|
if(extension.rules.cardinalityRules) |
|
jsonRules.cardinalityRules = jsonRules.cardinalityRules.concat(extension.rules.cardinalityRules); |
|
if(extension.rules.containmentRules) |
|
jsonRules.containmentRules = jsonRules.containmentRules.concat(extension.rules.containmentRules); |
|
if(extension.rules.morphingRules) |
|
jsonRules.morphingRules = jsonRules.morphingRules.concat(extension.rules.morphingRules); |
|
} |
|
if(extension.stencils) |
|
stencils = stencils.concat(extension.stencils); |
|
}); |
|
|
|
this._stencils = this._stencils.concat(stencilSet.stencils()); |
|
|
|
// init connection rules |
|
var cr = this._connectionRules; |
|
if (jsonRules.connectionRules) { |
|
jsonRules.connectionRules.each((function(rules){ |
|
if (this._isRoleOfOtherNamespace(rules.role)) { |
|
if (!cr[rules.role]) { |
|
cr[rules.role] = new Hash(); |
|
} |
|
} |
|
else { |
|
if (!cr[namespace + rules.role]) |
|
cr[namespace + rules.role] = new Hash(); |
|
} |
|
|
|
rules.connects.each((function(connect){ |
|
var toRoles = []; |
|
if (connect.to) { |
|
if (!(connect.to instanceof Array)) { |
|
connect.to = [connect.to]; |
|
} |
|
connect.to.each((function(to){ |
|
if (this._isRoleOfOtherNamespace(to)) { |
|
toRoles.push(to); |
|
} |
|
else { |
|
toRoles.push(namespace + to); |
|
} |
|
}).bind(this)); |
|
} |
|
|
|
var role, from; |
|
if (this._isRoleOfOtherNamespace(rules.role)) |
|
role = rules.role; |
|
else |
|
role = namespace + rules.role; |
|
|
|
if (this._isRoleOfOtherNamespace(connect.from)) |
|
from = connect.from; |
|
else |
|
from = namespace + connect.from; |
|
|
|
if (!cr[role][from]) |
|
cr[role][from] = toRoles; |
|
else |
|
cr[role][from] = cr[role][from].concat(toRoles); |
|
|
|
}).bind(this)); |
|
}).bind(this)); |
|
} |
|
|
|
// init cardinality rules |
|
var cardr = this._cardinalityRules; |
|
if (jsonRules.cardinalityRules) { |
|
jsonRules.cardinalityRules.each((function(rules){ |
|
var cardrKey; |
|
if (this._isRoleOfOtherNamespace(rules.role)) { |
|
cardrKey = rules.role; |
|
} |
|
else { |
|
cardrKey = namespace + rules.role; |
|
} |
|
|
|
if (!cardr[cardrKey]) { |
|
cardr[cardrKey] = {}; |
|
for (i in rules) { |
|
cardr[cardrKey][i] = rules[i]; |
|
} |
|
} |
|
|
|
var oe = new Hash(); |
|
if (rules.outgoingEdges) { |
|
rules.outgoingEdges.each((function(rule){ |
|
if (this._isRoleOfOtherNamespace(rule.role)) { |
|
oe[rule.role] = rule; |
|
} |
|
else { |
|
oe[namespace + rule.role] = rule; |
|
} |
|
}).bind(this)); |
|
} |
|
cardr[cardrKey].outgoingEdges = oe; |
|
var ie = new Hash(); |
|
if (rules.incomingEdges) { |
|
rules.incomingEdges.each((function(rule){ |
|
if (this._isRoleOfOtherNamespace(rule.role)) { |
|
ie[rule.role] = rule; |
|
} |
|
else { |
|
ie[namespace + rule.role] = rule; |
|
} |
|
}).bind(this)); |
|
} |
|
cardr[cardrKey].incomingEdges = ie; |
|
}).bind(this)); |
|
} |
|
|
|
// init containment rules |
|
var conr = this._containmentRules; |
|
if (jsonRules.containmentRules) { |
|
jsonRules.containmentRules.each((function(rules){ |
|
var conrKey; |
|
if (this._isRoleOfOtherNamespace(rules.role)) { |
|
conrKey = rules.role; |
|
} |
|
else { |
|
this._containerStencils.push(namespace + rules.role); |
|
conrKey = namespace + rules.role; |
|
} |
|
if (!conr[conrKey]) { |
|
conr[conrKey] = []; |
|
} |
|
(rules.contains||[]).each((function(containRole){ |
|
if (this._isRoleOfOtherNamespace(containRole)) { |
|
conr[conrKey].push(containRole); |
|
} |
|
else { |
|
conr[conrKey].push(namespace + containRole); |
|
} |
|
}).bind(this)); |
|
}).bind(this)); |
|
} |
|
|
|
// init morphing rules |
|
var morphr = this._morphingRules; |
|
if (jsonRules.morphingRules) { |
|
jsonRules.morphingRules.each((function(rules){ |
|
var morphrKey; |
|
if (this._isRoleOfOtherNamespace(rules.role)) { |
|
morphrKey = rules.role; |
|
} |
|
else { |
|
morphrKey = namespace + rules.role; |
|
} |
|
if (!morphr[morphrKey]) { |
|
morphr[morphrKey] = []; |
|
} |
|
if(!rules.preserveBounds) { |
|
rules.preserveBounds = false; |
|
} |
|
rules.baseMorphs.each((function(baseMorphStencilId){ |
|
var morphStencil = this._getStencilById(namespace + baseMorphStencilId); |
|
if(morphStencil) { |
|
morphr[morphrKey].push(morphStencil); |
|
} |
|
}).bind(this)); |
|
}).bind(this)); |
|
} |
|
|
|
// init layouting rules |
|
var layoutRules = this._layoutRules; |
|
if (jsonRules.layoutRules) { |
|
|
|
var getDirections = function(o){ |
|
return { |
|
"edgeRole":o.edgeRole||undefined, |
|
"t": o["t"]||1, |
|
"r": o["r"]||1, |
|
"b": o["b"]||1, |
|
"l": o["l"]||1 |
|
} |
|
} |
|
|
|
jsonRules.layoutRules.each(function(rules){ |
|
var layoutKey; |
|
if (this._isRoleOfOtherNamespace(rules.role)) { |
|
layoutKey = rules.role; |
|
} |
|
else { |
|
layoutKey = namespace + rules.role; |
|
} |
|
if (!layoutRules[layoutKey]) { |
|
layoutRules[layoutKey] = {}; |
|
} |
|
if (rules["in"]){ |
|
layoutRules[layoutKey]["in"] = getDirections(rules["in"]); |
|
} |
|
if (rules["ins"]){ |
|
layoutRules[layoutKey]["ins"] = (rules["ins"]||[]).map(function(e){ return getDirections(e) }) |
|
} |
|
if (rules["out"]) { |
|
layoutRules[layoutKey]["out"] = getDirections(rules["out"]); |
|
} |
|
if (rules["outs"]){ |
|
layoutRules[layoutKey]["outs"] = (rules["outs"]||[]).map(function(e){ return getDirections(e) }) |
|
} |
|
}.bind(this)); |
|
} |
|
} |
|
}, |
|
|
|
_getStencilById: function(id) { |
|
return this._stencils.find(function(stencil) { |
|
return stencil.id()==id; |
|
}); |
|
}, |
|
|
|
_cacheConnect: function(args) { |
|
result = this._canConnect(args); |
|
|
|
if (args.sourceStencil && args.targetStencil) { |
|
var source = this._cachedConnectSET[args.sourceStencil.id()]; |
|
|
|
if(!source) { |
|
source = new Hash(); |
|
this._cachedConnectSET[args.sourceStencil.id()] = source; |
|
} |
|
|
|
var edge = source[args.edgeStencil.id()]; |
|
|
|
if(!edge) { |
|
edge = new Hash(); |
|
source[args.edgeStencil.id()] = edge; |
|
} |
|
|
|
edge[args.targetStencil.id()] = result; |
|
|
|
} else if (args.sourceStencil) { |
|
var source = this._cachedConnectSE[args.sourceStencil.id()]; |
|
|
|
if(!source) { |
|
source = new Hash(); |
|
this._cachedConnectSE[args.sourceStencil.id()] = source; |
|
} |
|
|
|
source[args.edgeStencil.id()] = result; |
|
|
|
} else { |
|
var target = this._cachedConnectTE[args.targetStencil.id()]; |
|
|
|
if(!target) { |
|
target = new Hash(); |
|
this._cachedConnectTE[args.targetStencil.id()] = target; |
|
} |
|
|
|
target[args.edgeStencil.id()] = result; |
|
} |
|
|
|
return result; |
|
}, |
|
|
|
_cacheCard: function(args) { |
|
|
|
if(args.sourceStencil) { |
|
var source = this._cachedCardSE[args.sourceStencil.id()] |
|
|
|
if(!source) { |
|
source = new Hash(); |
|
this._cachedCardSE[args.sourceStencil.id()] = source; |
|
} |
|
|
|
var max = this._getMaximumNumberOfOutgoingEdge(args); |
|
if(max == undefined) |
|
max = -1; |
|
|
|
source[args.edgeStencil.id()] = max; |
|
} |
|
|
|
if(args.targetStencil) { |
|
var target = this._cachedCardTE[args.targetStencil.id()] |
|
|
|
if(!target) { |
|
target = new Hash(); |
|
this._cachedCardTE[args.targetStencil.id()] = target; |
|
} |
|
|
|
var max = this._getMaximumNumberOfIncomingEdge(args); |
|
if(max == undefined) |
|
max = -1; |
|
|
|
target[args.edgeStencil.id()] = max; |
|
} |
|
}, |
|
|
|
_cacheContain: function(args) { |
|
|
|
var result = [this._canContain(args), |
|
this._getMaximumOccurrence(args.containingStencil, args.containedStencil)] |
|
|
|
if(result[1] == undefined) |
|
result[1] = -1; |
|
|
|
var children = this._cachedContainPC[args.containingStencil.id()]; |
|
|
|
if(!children) { |
|
children = new Hash(); |
|
this._cachedContainPC[args.containingStencil.id()] = children; |
|
} |
|
|
|
children[args.containedStencil.id()] = result; |
|
|
|
return result; |
|
}, |
|
|
|
/** |
|
* Returns all stencils belonging to a morph group. (calculation result is |
|
* cached) |
|
*/ |
|
_cacheMorph: function(role) { |
|
|
|
var morphs = this._cachedMorphRS[role]; |
|
|
|
if(!morphs) { |
|
morphs = []; |
|
|
|
if(this._morphingRules.keys().include(role)) { |
|
morphs = this._stencils.select(function(stencil) { |
|
return stencil.roles().include(role); |
|
}); |
|
} |
|
|
|
this._cachedMorphRS[role] = morphs; |
|
} |
|
return morphs; |
|
}, |
|
|
|
/** Begin connection rules' methods */ |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args sourceStencil: ORYX.Core.StencilSet.Stencil | undefined |
|
* sourceShape: ORYX.Core.Shape | undefined |
|
* |
|
* At least sourceStencil or sourceShape has to be specified |
|
* |
|
* @return {Array} Array of stencils of edges that can be outgoing edges of |
|
* the source. |
|
*/ |
|
outgoingEdgeStencils: function(args) { |
|
// check arguments |
|
if(!args.sourceShape && !args.sourceStencil) { |
|
return []; |
|
} |
|
|
|
// init arguments |
|
if(args.sourceShape) { |
|
args.sourceStencil = args.sourceShape.getStencil(); |
|
} |
|
|
|
var _edges = []; |
|
|
|
// test each edge, if it can connect to source |
|
this._stencils.each((function(stencil) { |
|
if(stencil.type() === "edge") { |
|
var newArgs = Object.clone(args); |
|
newArgs.edgeStencil = stencil; |
|
if(this.canConnect(newArgs)) { |
|
_edges.push(stencil); |
|
} |
|
} |
|
}).bind(this)); |
|
|
|
return _edges; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args targetStencil: ORYX.Core.StencilSet.Stencil | undefined |
|
* targetShape: ORYX.Core.Shape | undefined |
|
* |
|
* At least targetStencil or targetShape has to be specified |
|
* |
|
* @return {Array} Array of stencils of edges that can be incoming edges of |
|
* the target. |
|
*/ |
|
incomingEdgeStencils: function(args) { |
|
// check arguments |
|
if(!args.targetShape && !args.targetStencil) { |
|
return []; |
|
} |
|
|
|
// init arguments |
|
if(args.targetShape) { |
|
args.targetStencil = args.targetShape.getStencil(); |
|
} |
|
|
|
var _edges = []; |
|
|
|
// test each edge, if it can connect to source |
|
this._stencils.each((function(stencil) { |
|
if(stencil.type() === "edge") { |
|
var newArgs = Object.clone(args); |
|
newArgs.edgeStencil = stencil; |
|
if(this.canConnect(newArgs)) { |
|
_edges.push(stencil); |
|
} |
|
} |
|
}).bind(this)); |
|
|
|
return _edges; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined |
|
* edgeShape: ORYX.Core.Edge | undefined targetStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined targetShape: |
|
* ORYX.Core.Node | undefined |
|
* |
|
* At least edgeStencil or edgeShape has to be specified!!! |
|
* |
|
* @return {Array} Returns an array of stencils that can be source of the |
|
* specified edge. |
|
*/ |
|
sourceStencils: function(args) { |
|
// check arguments |
|
if(!args || |
|
!args.edgeShape && !args.edgeStencil) { |
|
return []; |
|
} |
|
|
|
// init arguments |
|
if(args.targetShape) { |
|
args.targetStencil = args.targetShape.getStencil(); |
|
} |
|
|
|
if(args.edgeShape) { |
|
args.edgeStencil = args.edgeShape.getStencil(); |
|
} |
|
|
|
var _sources = []; |
|
|
|
// check each stencil, if it can be a source |
|
this._stencils.each((function(stencil) { |
|
var newArgs = Object.clone(args); |
|
newArgs.sourceStencil = stencil; |
|
if(this.canConnect(newArgs)) { |
|
_sources.push(stencil); |
|
} |
|
}).bind(this)); |
|
|
|
return _sources; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args edgeStencil: ORYX.Core.StencilSet.Stencil | undefined |
|
* edgeShape: ORYX.Core.Edge | undefined sourceStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined sourceShape: |
|
* ORYX.Core.Node | undefined |
|
* |
|
* At least edgeStencil or edgeShape has to be specified!!! |
|
* |
|
* @return {Array} Returns an array of stencils that can be target of the |
|
* specified edge. |
|
*/ |
|
targetStencils: function(args) { |
|
// check arguments |
|
if(!args || |
|
!args.edgeShape && !args.edgeStencil) { |
|
return []; |
|
} |
|
|
|
// init arguments |
|
if(args.sourceShape) { |
|
args.sourceStencil = args.sourceShape.getStencil(); |
|
} |
|
|
|
if(args.edgeShape) { |
|
args.edgeStencil = args.edgeShape.getStencil(); |
|
} |
|
|
|
var _targets = []; |
|
|
|
// check stencil, if it can be a target |
|
this._stencils.each((function(stencil) { |
|
var newArgs = Object.clone(args); |
|
newArgs.targetStencil = stencil; |
|
if(this.canConnect(newArgs)) { |
|
_targets.push(stencil); |
|
} |
|
}).bind(this)); |
|
|
|
return _targets; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape: |
|
* ORYX.Core.Edge |undefined sourceStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined sourceShape: |
|
* ORYX.Core.Node |undefined targetStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined targetShape: |
|
* ORYX.Core.Node |undefined |
|
* |
|
* At least source or target has to be specified!!! |
|
* |
|
* @return {Boolean} Returns, if the edge can connect source and target. |
|
*/ |
|
canConnect: function(args) { |
|
// check arguments |
|
if(!args || |
|
(!args.sourceShape && !args.sourceStencil && |
|
!args.targetShape && !args.targetStencil) || |
|
!args.edgeShape && !args.edgeStencil) { |
|
return false; |
|
} |
|
|
|
// init arguments |
|
if(args.sourceShape) { |
|
args.sourceStencil = args.sourceShape.getStencil(); |
|
} |
|
if(args.targetShape) { |
|
args.targetStencil = args.targetShape.getStencil(); |
|
} |
|
if(args.edgeShape) { |
|
args.edgeStencil = args.edgeShape.getStencil(); |
|
} |
|
|
|
var result; |
|
|
|
if(args.sourceStencil && args.targetStencil) { |
|
var source = this._cachedConnectSET[args.sourceStencil.id()]; |
|
|
|
if(!source) |
|
result = this._cacheConnect(args); |
|
else { |
|
var edge = source[args.edgeStencil.id()]; |
|
|
|
if(!edge) |
|
result = this._cacheConnect(args); |
|
else { |
|
var target = edge[args.targetStencil.id()]; |
|
|
|
if(target == undefined) |
|
result = this._cacheConnect(args); |
|
else |
|
result = target; |
|
} |
|
} |
|
} else if (args.sourceStencil) { |
|
var source = this._cachedConnectSE[args.sourceStencil.id()]; |
|
|
|
if(!source) |
|
result = this._cacheConnect(args); |
|
else { |
|
var edge = source[args.edgeStencil.id()]; |
|
|
|
if(edge == undefined) |
|
result = this._cacheConnect(args); |
|
else |
|
result = edge; |
|
} |
|
} else { // args.targetStencil |
|
var target = this._cachedConnectTE[args.targetStencil.id()]; |
|
|
|
if(!target) |
|
result = this._cacheConnect(args); |
|
else { |
|
var edge = target[args.edgeStencil.id()]; |
|
|
|
if(edge == undefined) |
|
result = this._cacheConnect(args); |
|
else |
|
result = edge; |
|
} |
|
} |
|
|
|
// check cardinality |
|
if (result) { |
|
if(args.sourceShape) { |
|
var source = this._cachedCardSE[args.sourceStencil.id()]; |
|
|
|
if(!source) { |
|
this._cacheCard(args); |
|
source = this._cachedCardSE[args.sourceStencil.id()]; |
|
} |
|
|
|
var max = source[args.edgeStencil.id()]; |
|
|
|
if(max == undefined) { |
|
this._cacheCard(args); |
|
} |
|
|
|
max = source[args.edgeStencil.id()]; |
|
|
|
if(max != -1) { |
|
result = args.sourceShape.getOutgoingShapes().all(function(cs) { |
|
if((cs.getStencil().id() === args.edgeStencil.id()) && |
|
((args.edgeShape) ? cs !== args.edgeShape : true)) { |
|
max--; |
|
return (max > 0) ? true : false; |
|
} else { |
|
return true; |
|
} |
|
}); |
|
} |
|
} |
|
|
|
if (args.targetShape) { |
|
var target = this._cachedCardTE[args.targetStencil.id()]; |
|
|
|
if(!target) { |
|
this._cacheCard(args); |
|
target = this._cachedCardTE[args.targetStencil.id()]; |
|
} |
|
|
|
var max = target[args.edgeStencil.id()]; |
|
|
|
if(max == undefined) { |
|
this._cacheCard(args); |
|
} |
|
|
|
max = target[args.edgeStencil.id()]; |
|
|
|
if(max != -1) { |
|
result = args.targetShape.getIncomingShapes().all(function(cs){ |
|
if ((cs.getStencil().id() === args.edgeStencil.id()) && |
|
((args.edgeShape) ? cs !== args.edgeShape : true)) { |
|
max--; |
|
return (max > 0) ? true : false; |
|
} |
|
else { |
|
return true; |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
return result; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args edgeStencil: ORYX.Core.StencilSet.Stencil edgeShape: |
|
* ORYX.Core.Edge |undefined sourceStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined sourceShape: |
|
* ORYX.Core.Node |undefined targetStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined targetShape: |
|
* ORYX.Core.Node |undefined |
|
* |
|
* At least source or target has to be specified!!! |
|
* |
|
* @return {Boolean} Returns, if the edge can connect source and target. |
|
*/ |
|
_canConnect: function(args) { |
|
// check arguments |
|
if(!args || |
|
(!args.sourceShape && !args.sourceStencil && |
|
!args.targetShape && !args.targetStencil) || |
|
!args.edgeShape && !args.edgeStencil) { |
|
return false; |
|
} |
|
|
|
// init arguments |
|
if(args.sourceShape) { |
|
args.sourceStencil = args.sourceShape.getStencil(); |
|
} |
|
if(args.targetShape) { |
|
args.targetStencil = args.targetShape.getStencil(); |
|
} |
|
if(args.edgeShape) { |
|
args.edgeStencil = args.edgeShape.getStencil(); |
|
} |
|
|
|
// 1. check connection rules |
|
var resultCR; |
|
|
|
// get all connection rules for this edge |
|
var edgeRules = this._getConnectionRulesOfEdgeStencil(args.edgeStencil); |
|
|
|
// check connection rules, if the source can be connected to the target |
|
// with the specified edge. |
|
if(edgeRules.keys().length === 0) { |
|
resultCR = false; |
|
} else { |
|
if(args.sourceStencil) { |
|
resultCR = args.sourceStencil.roles().any(function(sourceRole) { |
|
var targetRoles = edgeRules[sourceRole]; |
|
|
|
if(!targetRoles) {return false;} |
|
|
|
if(args.targetStencil) { |
|
return (targetRoles.any(function(targetRole) { |
|
return args.targetStencil.roles().member(targetRole); |
|
})); |
|
} else { |
|
return true; |
|
} |
|
}); |
|
} else { // !args.sourceStencil -> there is args.targetStencil |
|
resultCR = edgeRules.values().any(function(targetRoles) { |
|
return args.targetStencil.roles().any(function(targetRole) { |
|
return targetRoles.member(targetRole); |
|
}); |
|
}); |
|
} |
|
} |
|
|
|
return resultCR; |
|
}, |
|
|
|
/** End connection rules' methods */ |
|
|
|
|
|
/** Begin containment rules' methods */ |
|
|
|
isContainer: function(shape) { |
|
return this._containerStencils.member(shape.getStencil().id()); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args containingStencil: ORYX.Core.StencilSet.Stencil |
|
* containingShape: ORYX.Core.AbstractShape containedStencil: |
|
* ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape |
|
*/ |
|
canContain: function(args) { |
|
if(!args || |
|
!args.containingStencil && !args.containingShape || |
|
!args.containedStencil && !args.containedShape) { |
|
return false; |
|
} |
|
|
|
// init arguments |
|
if(args.containedShape) { |
|
args.containedStencil = args.containedShape.getStencil(); |
|
} |
|
|
|
if(args.containingShape) { |
|
args.containingStencil = args.containingShape.getStencil(); |
|
} |
|
|
|
//if(args.containingStencil.type() == 'edge' || args.containedStencil.type() == 'edge') |
|
// return false; |
|
if(args.containedStencil.type() == 'edge') |
|
return false; |
|
|
|
var childValues; |
|
|
|
var parent = this._cachedContainPC[args.containingStencil.id()]; |
|
|
|
if(!parent) |
|
childValues = this._cacheContain(args); |
|
else { |
|
childValues = parent[args.containedStencil.id()]; |
|
|
|
if(!childValues) |
|
childValues = this._cacheContain(args); |
|
} |
|
|
|
if(!childValues[0]) |
|
return false; |
|
else if (childValues[1] == -1) |
|
return true; |
|
else { |
|
if(args.containingShape) { |
|
var max = childValues[1]; |
|
return args.containingShape.getChildShapes(false).all(function(as) { |
|
if(as.getStencil().id() === args.containedStencil.id()) { |
|
max--; |
|
return (max > 0) ? true : false; |
|
} else { |
|
return true; |
|
} |
|
}); |
|
} else { |
|
return true; |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args containingStencil: ORYX.Core.StencilSet.Stencil |
|
* containingShape: ORYX.Core.AbstractShape containedStencil: |
|
* ORYX.Core.StencilSet.Stencil containedShape: ORYX.Core.Shape |
|
*/ |
|
_canContain: function(args) { |
|
if(!args || |
|
!args.containingStencil && !args.containingShape || |
|
!args.containedStencil && !args.containedShape) { |
|
return false; |
|
} |
|
|
|
// init arguments |
|
if(args.containedShape) { |
|
args.containedStencil = args.containedShape.getStencil(); |
|
} |
|
|
|
if(args.containingShape) { |
|
args.containingStencil = args.containingShape.getStencil(); |
|
} |
|
|
|
// if(args.containingShape) { |
|
// if(args.containingShape instanceof ORYX.Core.Edge) { |
|
// // edges cannot contain other shapes |
|
// return false; |
|
// } |
|
// } |
|
|
|
|
|
var result; |
|
|
|
// check containment rules |
|
result = args.containingStencil.roles().any((function(role) { |
|
var roles = this._containmentRules[role]; |
|
if(roles) { |
|
return roles.any(function(role) { |
|
return args.containedStencil.roles().member(role); |
|
}); |
|
} else { |
|
return false; |
|
} |
|
}).bind(this)); |
|
|
|
return result; |
|
}, |
|
|
|
/** End containment rules' methods */ |
|
|
|
|
|
/** Begin morphing rules' methods */ |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args |
|
* stencil: ORYX.Core.StencilSet.Stencil | undefined |
|
* shape: ORYX.Core.Shape | undefined |
|
* |
|
* At least stencil or shape has to be specified |
|
* |
|
* @return {Array} Array of stencils that the passed stencil/shape can be |
|
* transformed to (including the current stencil itself) |
|
*/ |
|
morphStencils: function(args) { |
|
// check arguments |
|
if(!args.stencil && !args.shape) { |
|
return []; |
|
} |
|
|
|
// init arguments |
|
if(args.shape) { |
|
args.stencil = args.shape.getStencil(); |
|
} |
|
|
|
var _morphStencils = []; |
|
args.stencil.roles().each(function(role) { |
|
this._cacheMorph(role).each(function(stencil) { |
|
_morphStencils.push(stencil); |
|
}) |
|
}.bind(this)); |
|
|
|
|
|
var baseMorphs = this.baseMorphs(); |
|
// BaseMorphs should be in the front of the array |
|
_morphStencils = _morphStencils.uniq().sort(function(a,b){ return baseMorphs.include(a)&&!baseMorphs.include(b) ? -1 : (baseMorphs.include(b)&&!baseMorphs.include(a) ? 1 : 0)}) |
|
return _morphStencils; |
|
}, |
|
|
|
/** |
|
* @return {Array} An array of all base morph stencils |
|
*/ |
|
baseMorphs: function() { |
|
var _baseMorphs = []; |
|
this._morphingRules.each(function(pair) { |
|
pair.value.each(function(baseMorph) { |
|
_baseMorphs.push(baseMorph); |
|
}); |
|
}); |
|
return _baseMorphs; |
|
}, |
|
|
|
/** |
|
* Returns true if there are morphing rules defines |
|
* @return {boolean} |
|
*/ |
|
containsMorphingRules: function(){ |
|
return this._stencilSets.any(function(ss){ return !!ss.jsonRules().morphingRules}); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args |
|
* sourceStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined |
|
* sourceShape: |
|
* ORYX.Core.Node |undefined |
|
* targetStencil: |
|
* ORYX.Core.StencilSet.Stencil | undefined |
|
* targetShape: |
|
* ORYX.Core.Node |undefined |
|
* |
|
* |
|
* @return {Stencil} Returns, the stencil for the connecting edge |
|
* or null if connection is not possible |
|
*/ |
|
connectMorph: function(args) { |
|
// check arguments |
|
if(!args || |
|
(!args.sourceShape && !args.sourceStencil && |
|
!args.targetShape && !args.targetStencil)) { |
|
return false; |
|
} |
|
|
|
// init arguments |
|
if(args.sourceShape) { |
|
args.sourceStencil = args.sourceShape.getStencil(); |
|
} |
|
if(args.targetShape) { |
|
args.targetStencil = args.targetShape.getStencil(); |
|
} |
|
|
|
var incoming = this.incomingEdgeStencils(args); |
|
var outgoing = this.outgoingEdgeStencils(args); |
|
|
|
var edgeStencils = incoming.select(function(e) { return outgoing.member(e); }); // intersection of sets |
|
var baseEdgeStencils = this.baseMorphs().select(function(e) { return edgeStencils.member(e); }); // again: intersection of sets |
|
|
|
if(baseEdgeStencils.size()>0) |
|
return baseEdgeStencils[0]; // return any of the possible base morphs |
|
else if(edgeStencils.size()>0) |
|
return edgeStencils[0]; // return any of the possible stencils |
|
|
|
return null; //connection not possible |
|
}, |
|
|
|
/** |
|
* Return true if the stencil should be located in the shape menu |
|
* @param {ORYX.Core.StencilSet.Stencil} morph |
|
* @return {Boolean} Returns true if the morphs in the morph group of the |
|
* specified morph shall be displayed in the shape menu |
|
*/ |
|
showInShapeMenu: function(stencil) { |
|
return this._stencilSets.any(function(ss){ |
|
return ss.jsonRules().morphingRules |
|
.any(function(r){ |
|
return stencil.roles().include(ss.namespace() + r.role) |
|
&& r.showInShapeMenu !== false; |
|
}) |
|
}); |
|
}, |
|
|
|
preserveBounds: function(stencil) { |
|
return this._stencilSets.any(function(ss) { |
|
return ss.jsonRules().morphingRules.any(function(r) { |
|
|
|
|
|
return stencil.roles().include(ss.namespace() + r.role) |
|
&& r.preserveBounds; |
|
}) |
|
}) |
|
}, |
|
|
|
/** End morphing rules' methods */ |
|
|
|
|
|
/** Begin layouting rules' methods */ |
|
|
|
/** |
|
* Returns a set on "in" and "out" layouting rules for a given shape |
|
* @param {Object} shape |
|
* @param {Object} edgeShape (Optional) |
|
* @return {Object} "in" and "out" with a default value of {"t":1, "r":1, "b":1, "r":1} if not specified in the json |
|
*/ |
|
getLayoutingRules : function(shape, edgeShape){ |
|
|
|
if (!shape||!(shape instanceof ORYX.Core.Shape)){ return } |
|
|
|
var layout = {"in":{},"out":{}}; |
|
|
|
var parseValues = function(o, v){ |
|
if (o && o[v]){ |
|
["t","r","b","l"].each(function(d){ |
|
layout[v][d]=Math.max(o[v][d],layout[v][d]||0); |
|
}); |
|
} |
|
if (o && o[v+"s"] instanceof Array){ |
|
["t","r","b","l"].each(function(d){ |
|
var defaultRule = o[v+"s"].find(function(e){ return !e.edgeRole }); |
|
var edgeRule; |
|
if (edgeShape instanceof ORYX.Core.Edge) { |
|
edgeRule = o[v + "s"].find(function(e){return this._hasRole(edgeShape, e.edgeRole) }.bind(this)); |
|
} |
|
layout[v][d]=Math.max(edgeRule?edgeRule[d]:defaultRule[d],layout[v][d]||0); |
|
}.bind(this)); |
|
} |
|
}.bind(this) |
|
|
|
// For each role |
|
shape.getStencil().roles().each(function(role) { |
|
// check if there are layout information |
|
if (this._layoutRules[role]){ |
|
// if so, parse those information to the 'layout' variable |
|
parseValues(this._layoutRules[role], "in"); |
|
parseValues(this._layoutRules[role], "out"); |
|
} |
|
}.bind(this)); |
|
|
|
// Make sure, that every attribute has an value, |
|
// otherwise set 1 |
|
["in","out"].each(function(v){ |
|
["t","r","b","l"].each(function(d){ |
|
layout[v][d]=layout[v][d]!==undefined?layout[v][d]:1; |
|
}); |
|
}) |
|
|
|
return layout; |
|
}, |
|
|
|
/** End layouting rules' methods */ |
|
|
|
/** Helper methods */ |
|
|
|
/** |
|
* Checks wether a shape contains the given role or the role is equal the stencil id |
|
* @param {ORYX.Core.Shape} shape |
|
* @param {String} role |
|
*/ |
|
_hasRole: function(shape, role){ |
|
if (!(shape instanceof ORYX.Core.Shape)||!role){ return } |
|
var isRole = shape.getStencil().roles().any(function(r){ return r == role}); |
|
|
|
return isRole || shape.getStencil().id() == (shape.getStencil().namespace()+role); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {String} |
|
* role |
|
* |
|
* @return {Array} Returns an array of stencils that can act as role. |
|
*/ |
|
_stencilsWithRole: function(role) { |
|
return this._stencils.findAll(function(stencil) { |
|
return (stencil.roles().member(role)) ? true : false; |
|
}); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {String} |
|
* role |
|
* |
|
* @return {Array} Returns an array of stencils that can act as role and |
|
* have the type 'edge'. |
|
*/ |
|
_edgesWithRole: function(role) { |
|
return this._stencils.findAll(function(stencil) { |
|
return (stencil.roles().member(role) && stencil.type() === "edge") ? true : false; |
|
}); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {String} |
|
* role |
|
* |
|
* @return {Array} Returns an array of stencils that can act as role and |
|
* have the type 'node'. |
|
*/ |
|
_nodesWithRole: function(role) { |
|
return this._stencils.findAll(function(stencil) { |
|
return (stencil.roles().member(role) && stencil.type() === "node") ? true : false; |
|
}); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {ORYX.Core.StencilSet.Stencil} |
|
* parent |
|
* @param {ORYX.Core.StencilSet.Stencil} |
|
* child |
|
* |
|
* @returns {Boolean} Returns the maximum occurrence of shapes of the |
|
* stencil's type inside the parent. |
|
*/ |
|
_getMaximumOccurrence: function(parent, child) { |
|
var max; |
|
child.roles().each((function(role) { |
|
var cardRule = this._cardinalityRules[role]; |
|
if(cardRule && cardRule.maximumOccurrence) { |
|
if(max) { |
|
max = Math.min(max, cardRule.maximumOccurrence); |
|
} else { |
|
max = cardRule.maximumOccurrence; |
|
} |
|
} |
|
}).bind(this)); |
|
|
|
return max; |
|
}, |
|
|
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args sourceStencil: ORYX.Core.Node edgeStencil: |
|
* ORYX.Core.StencilSet.Stencil |
|
* |
|
* @return {Boolean} Returns, the maximum number of outgoing edges of the |
|
* type specified by edgeStencil of the sourceShape. |
|
*/ |
|
_getMaximumNumberOfOutgoingEdge: function(args) { |
|
if(!args || |
|
!args.sourceStencil || |
|
!args.edgeStencil) { |
|
return false; |
|
} |
|
|
|
var max; |
|
args.sourceStencil.roles().each((function(role) { |
|
var cardRule = this._cardinalityRules[role]; |
|
|
|
if(cardRule && cardRule.outgoingEdges) { |
|
args.edgeStencil.roles().each(function(edgeRole) { |
|
var oe = cardRule.outgoingEdges[edgeRole]; |
|
|
|
if(oe && oe.maximum) { |
|
if(max) { |
|
max = Math.min(max, oe.maximum); |
|
} else { |
|
max = oe.maximum; |
|
} |
|
} |
|
}); |
|
} |
|
}).bind(this)); |
|
|
|
return max; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} |
|
* args targetStencil: ORYX.Core.StencilSet.Stencil edgeStencil: |
|
* ORYX.Core.StencilSet.Stencil |
|
* |
|
* @return {Boolean} Returns the maximum number of incoming edges of the |
|
* type specified by edgeStencil of the targetShape. |
|
*/ |
|
_getMaximumNumberOfIncomingEdge: function(args) { |
|
if(!args || |
|
!args.targetStencil || |
|
!args.edgeStencil) { |
|
return false; |
|
} |
|
|
|
var max; |
|
args.targetStencil.roles().each((function(role) { |
|
var cardRule = this._cardinalityRules[role]; |
|
if(cardRule && cardRule.incomingEdges) { |
|
args.edgeStencil.roles().each(function(edgeRole) { |
|
var ie = cardRule.incomingEdges[edgeRole]; |
|
if(ie && ie.maximum) { |
|
if(max) { |
|
max = Math.min(max, ie.maximum); |
|
} else { |
|
max = ie.maximum; |
|
} |
|
} |
|
}); |
|
} |
|
}).bind(this)); |
|
|
|
return max; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {ORYX.Core.StencilSet.Stencil} |
|
* edgeStencil |
|
* |
|
* @return {Hash} Returns a hash map of all connection rules for |
|
* edgeStencil. |
|
*/ |
|
_getConnectionRulesOfEdgeStencil: function(edgeStencil) { |
|
var edgeRules = new Hash(); |
|
edgeStencil.roles().each((function(role) { |
|
if(this._connectionRules[role]) { |
|
this._connectionRules[role].each(function(cr) { |
|
if(edgeRules[cr.key]) { |
|
edgeRules[cr.key] = edgeRules[cr.key].concat(cr.value); |
|
} else { |
|
edgeRules[cr.key] = cr.value; |
|
} |
|
}); |
|
} |
|
}).bind(this)); |
|
|
|
return edgeRules; |
|
}, |
|
|
|
_isRoleOfOtherNamespace: function(role) { |
|
return (role.indexOf("#") > 0); |
|
}, |
|
|
|
toString: function() { return "Rules"; } |
|
} |
|
ORYX.Core.StencilSet.Rules = Clazz.extend(ORYX.Core.StencilSet.Rules); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespace |
|
*/ |
|
if (!ORYX) { |
|
var ORYX = {}; |
|
} |
|
if (!ORYX.Core) { |
|
ORYX.Core = {}; |
|
} |
|
if (!ORYX.Core.StencilSet) { |
|
ORYX.Core.StencilSet = {}; |
|
} |
|
|
|
/** |
|
* This class represents a stencil set. It offers methods for accessing |
|
* the attributes of the stencil set description JSON file and the stencil set's |
|
* stencils. |
|
*/ |
|
ORYX.Core.StencilSet.StencilSet = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param source {URL} A reference to the stencil set specification. |
|
* |
|
*/ |
|
construct: function(source, modelMetaData, editorId){ |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
if (!source) { |
|
throw "ORYX.Core.StencilSet.StencilSet(construct): Parameter 'source' is not defined."; |
|
} |
|
|
|
if (source.endsWith("/")) { |
|
source = source.substr(0, source.length - 1); |
|
} |
|
|
|
this._extensions = new Hash(); |
|
|
|
this._source = source; |
|
this._baseUrl = source.substring(0, source.lastIndexOf("/") + 1); |
|
|
|
this._jsonObject = {}; |
|
|
|
this._stencils = new Hash(); |
|
this._availableStencils = new Hash(); |
|
|
|
if(ORYX.CONFIG.BACKEND_SWITCH) { |
|
this._baseUrl = "editor/stencilsets/bpmn2.0/"; |
|
this._source = "stencilsets/bpmn2.0/bpmn2.0.json"; |
|
console.log(ACTIVITI.CONFIG.contextRoot + '/editor/stencilset') |
|
new Ajax.Request(ACTIVITI.CONFIG.contextRoot + '/editor/stencilset?version=' + Date.now(), { |
|
asynchronous: false, |
|
method: 'get', |
|
onSuccess: this._init.bind(this), |
|
onFailure: this._cancelInit.bind(this) |
|
}); |
|
|
|
} else { |
|
new Ajax.Request(source, { |
|
asynchronous: false, |
|
method: 'get', |
|
onSuccess: this._init.bind(this), |
|
onFailure: this._cancelInit.bind(this) |
|
}); |
|
} |
|
|
|
if (this.errornous) |
|
throw "Loading stencil set " + source + " failed."; |
|
}, |
|
|
|
/** |
|
* Finds a root stencil in this stencil set. There may be many of these. If |
|
* there are, the first one found will be used. In Firefox, this is the |
|
* topmost definition in the stencil set description file. |
|
*/ |
|
findRootStencilName: function(){ |
|
|
|
// find any stencil that may be root. |
|
var rootStencil = this._stencils.values().find(function(stencil){ |
|
return stencil._jsonStencil.mayBeRoot |
|
}); |
|
|
|
// if there is none, just guess the first. |
|
if (!rootStencil) { |
|
ORYX.Log.warn("Did not find any stencil that may be root. Taking a guess."); |
|
rootStencil = this._stencils.values()[0]; |
|
} |
|
|
|
// return its id. |
|
return rootStencil.id(); |
|
}, |
|
|
|
/** |
|
* @param {ORYX.Core.StencilSet.StencilSet} stencilSet |
|
* @return {Boolean} True, if stencil set has the same namespace. |
|
*/ |
|
equals: function(stencilSet){ |
|
return (this.namespace() === stencilSet.namespace()); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Oryx.Core.StencilSet.Stencil} rootStencil If rootStencil is defined, it only returns stencils |
|
* that could be (in)direct child of that stencil. |
|
*/ |
|
stencils: function(rootStencil, rules, sortByGroup){ |
|
if(rootStencil && rules) { |
|
var stencils = this._availableStencils.values(); |
|
var containers = [rootStencil]; |
|
var checkedContainers = []; |
|
|
|
var result = []; |
|
|
|
while (containers.size() > 0) { |
|
var container = containers.pop(); |
|
checkedContainers.push(container); |
|
var children = stencils.findAll(function(stencil){ |
|
var args = { |
|
containingStencil: container, |
|
containedStencil: stencil |
|
}; |
|
return rules.canContain(args); |
|
}); |
|
for(var i = 0; i < children.size(); i++) { |
|
if (!checkedContainers.member(children[i])) { |
|
containers.push(children[i]); |
|
} |
|
} |
|
result = result.concat(children).uniq(); |
|
} |
|
|
|
// Sort the result to the origin order |
|
result = result.sortBy(function(stencil) { |
|
return stencils.indexOf(stencil); |
|
}); |
|
|
|
|
|
if(sortByGroup) { |
|
result = result.sortBy(function(stencil) { |
|
return stencil.groups().first(); |
|
}); |
|
} |
|
|
|
var edges = stencils.findAll(function(stencil) { |
|
return stencil.type() == "edge"; |
|
}); |
|
result = result.concat(edges); |
|
|
|
return result; |
|
|
|
} else { |
|
if(sortByGroup) { |
|
return this._availableStencils.values().sortBy(function(stencil) { |
|
return stencil.groups().first(); |
|
}); |
|
} else { |
|
return this._availableStencils.values(); |
|
} |
|
} |
|
}, |
|
|
|
nodes: function(){ |
|
return this._availableStencils.values().findAll(function(stencil){ |
|
return (stencil.type() === 'node') |
|
}); |
|
}, |
|
|
|
edges: function(){ |
|
return this._availableStencils.values().findAll(function(stencil){ |
|
return (stencil.type() === 'edge') |
|
}); |
|
}, |
|
|
|
stencil: function(id){ |
|
return this._stencils[id]; |
|
}, |
|
|
|
title: function(){ |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonObject, "title"); |
|
}, |
|
|
|
description: function(){ |
|
return ORYX.Core.StencilSet.getTranslation(this._jsonObject, "description"); |
|
}, |
|
|
|
namespace: function(){ |
|
return this._jsonObject ? this._jsonObject.namespace : null; |
|
}, |
|
|
|
jsonRules: function(){ |
|
return this._jsonObject ? this._jsonObject.rules : null; |
|
}, |
|
|
|
source: function(){ |
|
return this._source; |
|
}, |
|
|
|
extensions: function() { |
|
return this._extensions; |
|
}, |
|
|
|
addExtension: function(url) { |
|
|
|
new Ajax.Request(url, { |
|
method: 'GET', |
|
asynchronous: false, |
|
onSuccess: (function(transport) { |
|
this.addExtensionDirectly(transport.responseText); |
|
}).bind(this), |
|
onFailure: (function(transport) { |
|
ORYX.Log.debug("Loading stencil set extension file failed. The request returned an error." + transport); |
|
}).bind(this), |
|
onException: (function(transport) { |
|
ORYX.Log.debug("Loading stencil set extension file failed. The request returned an error." + transport); |
|
}).bind(this) |
|
|
|
}); |
|
}, |
|
|
|
addExtensionDirectly: function(str){ |
|
|
|
try { |
|
eval("var jsonExtension = " + str); |
|
|
|
if(!(jsonExtension["extends"].endsWith("#"))) |
|
jsonExtension["extends"] += "#"; |
|
|
|
if(jsonExtension["extends"] == this.namespace()) { |
|
this._extensions[jsonExtension.namespace] = jsonExtension; |
|
|
|
var defaultPosition = this._stencils.keys().size(); |
|
//load new stencils |
|
if(jsonExtension.stencils) { |
|
$A(jsonExtension.stencils).each(function(stencil) { |
|
defaultPosition++; |
|
var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this, undefined, defaultPosition); |
|
this._stencils[oStencil.id()] = oStencil; |
|
this._availableStencils[oStencil.id()] = oStencil; |
|
}.bind(this)); |
|
} |
|
|
|
//load additional properties |
|
if (jsonExtension.properties) { |
|
var stencils = this._stencils.values(); |
|
|
|
stencils.each(function(stencil){ |
|
var roles = stencil.roles(); |
|
|
|
jsonExtension.properties.each(function(prop){ |
|
prop.roles.any(function(role){ |
|
role = jsonExtension["extends"] + role; |
|
if (roles.member(role)) { |
|
prop.properties.each(function(property){ |
|
stencil.addProperty(property, jsonExtension.namespace); |
|
}); |
|
|
|
return true; |
|
} |
|
else |
|
return false; |
|
}) |
|
}) |
|
}.bind(this)); |
|
} |
|
|
|
//remove stencil properties |
|
if(jsonExtension.removeproperties) { |
|
jsonExtension.removeproperties.each(function(remprop) { |
|
var stencil = this.stencil(jsonExtension["extends"] + remprop.stencil); |
|
if(stencil) { |
|
remprop.properties.each(function(propId) { |
|
stencil.removeProperty(propId); |
|
}); |
|
} |
|
}.bind(this)); |
|
} |
|
|
|
//remove stencils |
|
if(jsonExtension.removestencils) { |
|
$A(jsonExtension.removestencils).each(function(remstencil) { |
|
delete this._availableStencils[jsonExtension["extends"] + remstencil]; |
|
}.bind(this)); |
|
} |
|
} |
|
} catch (e) { |
|
ORYX.Log.debug("StencilSet.addExtension: Something went wrong when initialising the stencil set extension. " + e); |
|
} |
|
}, |
|
|
|
removeExtension: function(namespace) { |
|
var jsonExtension = this._extensions[namespace]; |
|
if(jsonExtension) { |
|
|
|
//unload extension's stencils |
|
if(jsonExtension.stencils) { |
|
$A(jsonExtension.stencils).each(function(stencil) { |
|
var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this); |
|
delete this._stencils[oStencil.id()]; // maybe not ?? |
|
delete this._availableStencils[oStencil.id()]; |
|
}.bind(this)); |
|
} |
|
|
|
//unload extension's properties |
|
if (jsonExtension.properties) { |
|
var stencils = this._stencils.values(); |
|
|
|
stencils.each(function(stencil){ |
|
var roles = stencil.roles(); |
|
|
|
jsonExtension.properties.each(function(prop){ |
|
prop.roles.any(function(role){ |
|
role = jsonExtension["extends"] + role; |
|
if (roles.member(role)) { |
|
prop.properties.each(function(property){ |
|
stencil.removeProperty(property.id); |
|
}); |
|
|
|
return true; |
|
} |
|
else |
|
return false; |
|
}) |
|
}) |
|
}.bind(this)); |
|
} |
|
|
|
//restore removed stencil properties |
|
if(jsonExtension.removeproperties) { |
|
jsonExtension.removeproperties.each(function(remprop) { |
|
var stencil = this.stencil(jsonExtension["extends"] + remprop.stencil); |
|
if(stencil) { |
|
var stencilJson = $A(this._jsonObject.stencils).find(function(s) { return s.id == stencil.id() }); |
|
remprop.properties.each(function(propId) { |
|
var propertyJson = $A(stencilJson.properties).find(function(p) { return p.id == propId }); |
|
stencil.addProperty(propertyJson, this.namespace()); |
|
}.bind(this)); |
|
} |
|
}.bind(this)); |
|
} |
|
|
|
//restore removed stencils |
|
if(jsonExtension.removestencils) { |
|
$A(jsonExtension.removestencils).each(function(remstencil) { |
|
var sId = jsonExtension["extends"] + remstencil; |
|
this._availableStencils[sId] = this._stencils[sId]; |
|
}.bind(this)); |
|
} |
|
} |
|
delete this._extensions[namespace]; |
|
}, |
|
|
|
__handleStencilset: function(response){ |
|
|
|
try { |
|
// using eval instead of prototype's parsing, |
|
// since there are functions in this JSON. |
|
eval("this._jsonObject =" + response.responseText); |
|
} |
|
catch (e) { |
|
throw "Stenciset corrupt: " + e; |
|
} |
|
|
|
// assert it was parsed. |
|
if (!this._jsonObject) { |
|
throw "Error evaluating stencilset. It may be corrupt."; |
|
} |
|
|
|
with (this._jsonObject) { |
|
|
|
// assert there is a namespace. |
|
if (!namespace || namespace === "") |
|
throw "Namespace definition missing in stencilset."; |
|
|
|
if (!(stencils instanceof Array)) |
|
throw "Stencilset corrupt."; |
|
|
|
// assert namespace ends with '#'. |
|
if (!namespace.endsWith("#")) |
|
namespace = namespace + "#"; |
|
|
|
// assert title and description are strings. |
|
if (!title) |
|
title = ""; |
|
if (!description) |
|
description = ""; |
|
} |
|
}, |
|
|
|
/** |
|
* This method is called when the HTTP request to get the requested stencil |
|
* set succeeds. The response is supposed to be a JSON representation |
|
* according to the stencil set specification. |
|
* @param {Object} response The JSON representation according to the |
|
* stencil set specification. |
|
*/ |
|
_init: function(response){ |
|
|
|
// init and check consistency. |
|
this.__handleStencilset(response); |
|
|
|
var pps = new Hash(); |
|
|
|
// init property packages |
|
if(this._jsonObject.propertyPackages) { |
|
$A(this._jsonObject.propertyPackages).each((function(pp) { |
|
pps[pp.name] = pp.properties; |
|
}).bind(this)); |
|
} |
|
|
|
var defaultPosition = 0; |
|
|
|
// init each stencil |
|
$A(this._jsonObject.stencils).each((function(stencil){ |
|
defaultPosition++; |
|
|
|
// instantiate normally. |
|
var oStencil = new ORYX.Core.StencilSet.Stencil(stencil, this.namespace(), this._baseUrl, this, pps, defaultPosition); |
|
this._stencils[oStencil.id()] = oStencil; |
|
this._availableStencils[oStencil.id()] = oStencil; |
|
|
|
}).bind(this)); |
|
}, |
|
|
|
_cancelInit: function(response){ |
|
this.errornous = true; |
|
}, |
|
|
|
toString: function(){ |
|
return "StencilSet " + this.title() + " (" + this.namespace() + ")"; |
|
} |
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespace |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.StencilSet) {ORYX.Core.StencilSet = {};} |
|
|
|
/** |
|
* Class StencilSets |
|
* uses Prototpye 1.5.0 |
|
* uses Inheritance |
|
* |
|
* Singleton |
|
*/ |
|
//storage for loaded stencil sets by namespace |
|
ORYX.Core.StencilSet._stencilSetsByNamespace = new Hash(); |
|
|
|
//storage for stencil sets by url |
|
ORYX.Core.StencilSet._stencilSetsByUrl = new Hash(); |
|
|
|
//storage for stencil set namespaces by editor instances |
|
ORYX.Core.StencilSet._StencilSetNSByEditorInstance = new Hash(); |
|
|
|
//storage for rules by editor instances |
|
ORYX.Core.StencilSet._rulesByEditorInstance = new Hash(); |
|
|
|
/** |
|
* |
|
* @param {String} editorId |
|
* |
|
* @return {Hash} Returns a hash map with all stencil sets that are loaded by |
|
* the editor with the editorId. |
|
*/ |
|
ORYX.Core.StencilSet.stencilSets = function(editorId) { |
|
var stencilSetNSs = ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId]; |
|
var stencilSets = new Hash(); |
|
if(stencilSetNSs) { |
|
stencilSetNSs.each(function(stencilSetNS) { |
|
var stencilSet = ORYX.Core.StencilSet.stencilSet(stencilSetNS) |
|
stencilSets[stencilSet.namespace()] = stencilSet; |
|
}); |
|
} |
|
return stencilSets; |
|
}; |
|
|
|
/** |
|
* |
|
* @param {String} namespace |
|
* |
|
* @return {ORYX.Core.StencilSet.StencilSet} Returns the stencil set with the specified |
|
* namespace. |
|
* |
|
* The method can handle namespace strings like |
|
* http://www.example.org/stencilset |
|
* http://www.example.org/stencilset# |
|
* http://www.example.org/stencilset#ANode |
|
*/ |
|
ORYX.Core.StencilSet.stencilSet = function(namespace) { |
|
ORYX.Log.trace("Getting stencil set %0", namespace); |
|
var splitted = namespace.split("#", 1); |
|
if(splitted.length === 1) { |
|
ORYX.Log.trace("Getting stencil set %0", splitted[0]); |
|
return ORYX.Core.StencilSet._stencilSetsByNamespace[splitted[0] + "#"]; |
|
} else { |
|
return undefined; |
|
} |
|
}; |
|
|
|
/** |
|
* |
|
* @param {String} id |
|
* |
|
* @return {ORYX.Core.StencilSet.Stencil} Returns the stencil specified by the id. |
|
* |
|
* The id must be unique and contains the namespace of the stencil's stencil set. |
|
* e.g. http://www.example.org/stencilset#ANode |
|
*/ |
|
ORYX.Core.StencilSet.stencil = function(id) { |
|
ORYX.Log.trace("Getting stencil for %0", id); |
|
var ss = ORYX.Core.StencilSet.stencilSet(id); |
|
if(ss) { |
|
return ss.stencil(id); |
|
} else { |
|
|
|
ORYX.Log.trace("Cannot fild stencil for %0", id); |
|
return undefined; |
|
} |
|
}; |
|
|
|
/** |
|
* |
|
* @param {String} editorId |
|
* |
|
* @return {ORYX.Core.StencilSet.Rules} Returns the rules object for the editor |
|
* specified by its editor id. |
|
*/ |
|
ORYX.Core.StencilSet.rules = function(editorId) { |
|
if(!ORYX.Core.StencilSet._rulesByEditorInstance[editorId]) { |
|
ORYX.Core.StencilSet._rulesByEditorInstance[editorId] = new ORYX.Core.StencilSet.Rules(); |
|
} |
|
return ORYX.Core.StencilSet._rulesByEditorInstance[editorId]; |
|
}; |
|
|
|
/** |
|
* |
|
* @param {String} url |
|
* @param {String} editorId |
|
* |
|
* Loads a stencil set from url, if it is not already loaded. |
|
* It also stores which editor instance loads the stencil set and |
|
* initializes the Rules object for the editor instance. |
|
*/ |
|
ORYX.Core.StencilSet.loadStencilSet = function(url, modelMetaData, editorId) { |
|
|
|
// Alfresco: disable cache, because stencil sets are now flexible |
|
|
|
//var stencilSet = ORYX.Core.StencilSet._stencilSetsByUrl[url]; |
|
|
|
//if(!stencilSet) { |
|
//load stencil set |
|
stencilSet = new ORYX.Core.StencilSet.StencilSet(url, modelMetaData, editorId); |
|
|
|
//store stencil set |
|
ORYX.Core.StencilSet._stencilSetsByNamespace[stencilSet.namespace()] = stencilSet; |
|
|
|
//store stencil set by url |
|
ORYX.Core.StencilSet._stencilSetsByUrl[url] = stencilSet; |
|
//} |
|
|
|
var namespace = stencilSet.namespace(); |
|
|
|
//store which editorInstance loads the stencil set |
|
if(ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId]) { |
|
ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId].push(namespace); |
|
} else { |
|
ORYX.Core.StencilSet._StencilSetNSByEditorInstance[editorId] = [namespace]; |
|
} |
|
|
|
//store the rules for the editor instance |
|
if(ORYX.Core.StencilSet._rulesByEditorInstance[editorId]) { |
|
ORYX.Core.StencilSet._rulesByEditorInstance[editorId].initializeRules(stencilSet); |
|
} else { |
|
var rules = new ORYX.Core.StencilSet.Rules(); |
|
rules.initializeRules(stencilSet); |
|
ORYX.Core.StencilSet._rulesByEditorInstance[editorId] = rules; |
|
} |
|
}; |
|
|
|
/** |
|
* Returns the translation of an attribute in jsonObject specified by its name |
|
* according to navigator.language |
|
*/ |
|
ORYX.Core.StencilSet.getTranslation = function(jsonObject, name) { |
|
var lang = ORYX.I18N.Language.toLowerCase(); |
|
|
|
var result = jsonObject[name + "_" + lang]; |
|
|
|
if(result) |
|
return result; |
|
|
|
result = jsonObject[name + "_" + lang.substr(0, 2)]; |
|
|
|
if(result) |
|
return result; |
|
|
|
return jsonObject[name]; |
|
}; |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
|
|
/** |
|
* @classDescription With Bounds you can set and get position and size of UIObjects. |
|
*/ |
|
ORYX.Core.Command = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function() { |
|
|
|
}, |
|
|
|
execute: function(){ |
|
throw "Command.execute() has to be implemented!" |
|
}, |
|
|
|
rollback: function(){ |
|
throw "Command.rollback() has to be implemented!" |
|
} |
|
|
|
|
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
|
|
/** |
|
* @classDescription With Bounds you can set and get position and size of UIObjects. |
|
*/ |
|
ORYX.Core.Bounds = { |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function() { |
|
this._changedCallbacks = []; //register a callback with changedCallacks.push(this.method.bind(this)); |
|
this.a = {}; |
|
this.b = {}; |
|
this.set.apply(this, arguments); |
|
this.suspendChange = false; |
|
this.changedWhileSuspend = false; |
|
}, |
|
|
|
/** |
|
* Calls all registered callbacks. |
|
*/ |
|
_changed: function(sizeChanged) { |
|
if(!this.suspendChange) { |
|
this._changedCallbacks.each(function(callback) { |
|
callback(this, sizeChanged); |
|
}.bind(this)); |
|
this.changedWhileSuspend = false; |
|
} else |
|
this.changedWhileSuspend = true; |
|
}, |
|
|
|
/** |
|
* Registers a callback that is called, if the bounds changes. |
|
* @param callback {Function} The callback function. |
|
*/ |
|
registerCallback: function(callback) { |
|
if(!this._changedCallbacks.member(callback)) { |
|
this._changedCallbacks.push(callback); |
|
} |
|
}, |
|
|
|
/** |
|
* Unregisters a callback. |
|
* @param callback {Function} The callback function. |
|
*/ |
|
unregisterCallback: function(callback) { |
|
this._changedCallbacks = this._changedCallbacks.without(callback); |
|
}, |
|
|
|
/** |
|
* Sets position and size of the shape dependent of four coordinates |
|
* (set(ax, ay, bx, by);), two points (set({x: ax, y: ay}, {x: bx, y: by});) |
|
* or one bound (set({a: {x: ax, y: ay}, b: {x: bx, y: by}});). |
|
*/ |
|
set: function() { |
|
|
|
var changed = false; |
|
|
|
switch (arguments.length) { |
|
|
|
case 1: |
|
if(this.a.x !== arguments[0].a.x) { |
|
changed = true; |
|
this.a.x = arguments[0].a.x; |
|
} |
|
if(this.a.y !== arguments[0].a.y) { |
|
changed = true; |
|
this.a.y = arguments[0].a.y; |
|
} |
|
if(this.b.x !== arguments[0].b.x) { |
|
changed = true; |
|
this.b.x = arguments[0].b.x; |
|
} |
|
if(this.b.y !== arguments[0].b.y) { |
|
changed = true; |
|
this.b.y = arguments[0].b.y; |
|
} |
|
break; |
|
|
|
case 2: |
|
var ax = Math.min(arguments[0].x, arguments[1].x); |
|
var ay = Math.min(arguments[0].y, arguments[1].y); |
|
var bx = Math.max(arguments[0].x, arguments[1].x); |
|
var by = Math.max(arguments[0].y, arguments[1].y); |
|
if(this.a.x !== ax) { |
|
changed = true; |
|
this.a.x = ax; |
|
} |
|
if(this.a.y !== ay) { |
|
changed = true; |
|
this.a.y = ay; |
|
} |
|
if(this.b.x !== bx) { |
|
changed = true; |
|
this.b.x = bx; |
|
} |
|
if(this.b.y !== by) { |
|
changed = true; |
|
this.b.y = by; |
|
} |
|
break; |
|
|
|
case 4: |
|
var ax = Math.min(arguments[0], arguments[2]); |
|
var ay = Math.min(arguments[1], arguments[3]); |
|
var bx = Math.max(arguments[0], arguments[2]); |
|
var by = Math.max(arguments[1], arguments[3]); |
|
if(this.a.x !== ax) { |
|
changed = true; |
|
this.a.x = ax; |
|
} |
|
if(this.a.y !== ay) { |
|
changed = true; |
|
this.a.y = ay; |
|
} |
|
if(this.b.x !== bx) { |
|
changed = true; |
|
this.b.x = bx; |
|
} |
|
if(this.b.y !== by) { |
|
changed = true; |
|
this.b.y = by; |
|
} |
|
break; |
|
} |
|
|
|
if(changed) { |
|
this._changed(true); |
|
} |
|
}, |
|
|
|
/** |
|
* Moves the bounds so that the point p will be the new upper left corner. |
|
* @param {Point} p |
|
* or |
|
* @param {Number} x |
|
* @param {Number} y |
|
*/ |
|
moveTo: function() { |
|
|
|
var currentPosition = this.upperLeft(); |
|
switch (arguments.length) { |
|
case 1: |
|
this.moveBy({ |
|
x: arguments[0].x - currentPosition.x, |
|
y: arguments[0].y - currentPosition.y |
|
}); |
|
break; |
|
case 2: |
|
this.moveBy({ |
|
x: arguments[0] - currentPosition.x, |
|
y: arguments[1] - currentPosition.y |
|
}); |
|
break; |
|
default: |
|
//TODO error |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Moves the bounds relatively by p. |
|
* @param {Point} p |
|
* or |
|
* @param {Number} x |
|
* @param {Number} y |
|
* |
|
*/ |
|
moveBy: function() { |
|
var changed = false; |
|
|
|
switch (arguments.length) { |
|
case 1: |
|
var p = arguments[0]; |
|
if(p.x !== 0 || p.y !== 0) { |
|
changed = true; |
|
this.a.x += p.x; |
|
this.b.x += p.x; |
|
this.a.y += p.y; |
|
this.b.y += p.y; |
|
} |
|
break; |
|
case 2: |
|
var x = arguments[0]; |
|
var y = arguments[1]; |
|
if(x !== 0 || y !== 0) { |
|
changed = true; |
|
this.a.x += x; |
|
this.b.x += x; |
|
this.a.y += y; |
|
this.b.y += y; |
|
} |
|
break; |
|
default: |
|
//TODO error |
|
} |
|
|
|
if(changed) { |
|
this._changed(); |
|
} |
|
}, |
|
|
|
/*** |
|
* Includes the bounds b into the current bounds. |
|
* @param {Bounds} b |
|
*/ |
|
include: function(b) { |
|
|
|
if( (this.a.x === undefined) && (this.a.y === undefined) && |
|
(this.b.x === undefined) && (this.b.y === undefined)) { |
|
return b; |
|
}; |
|
|
|
var cx = Math.min(this.a.x,b.a.x); |
|
var cy = Math.min(this.a.y,b.a.y); |
|
|
|
var dx = Math.max(this.b.x,b.b.x); |
|
var dy = Math.max(this.b.y,b.b.y); |
|
|
|
|
|
this.set(cx, cy, dx, dy); |
|
}, |
|
|
|
/** |
|
* Relatively extends the bounds by p. |
|
* @param {Point} p |
|
*/ |
|
extend: function(p) { |
|
|
|
if(p.x !== 0 || p.y !== 0) { |
|
// this is over cross for the case that a and b have same coordinates. |
|
//((this.a.x > this.b.x) ? this.a : this.b).x += p.x; |
|
//((this.b.y > this.a.y) ? this.b : this.a).y += p.y; |
|
this.b.x += p.x; |
|
this.b.y += p.y; |
|
|
|
this._changed(true); |
|
} |
|
}, |
|
|
|
/** |
|
* Widens the scope of the bounds by x. |
|
* @param {Number} x |
|
*/ |
|
widen: function(x) { |
|
if (x !== 0) { |
|
this.suspendChange = true; |
|
this.moveBy({x: -x, y: -x}); |
|
this.extend({x: 2*x, y: 2*x}); |
|
this.suspendChange = false; |
|
if(this.changedWhileSuspend) { |
|
this._changed(true); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Returns the upper left corner's point regardless of the |
|
* bound delimiter points. |
|
*/ |
|
upperLeft: function() { |
|
var result = {}; |
|
result.x = this.a.x; |
|
result.y = this.a.y; |
|
return result; |
|
}, |
|
|
|
/** |
|
* Returns the lower Right left corner's point regardless of the |
|
* bound delimiter points. |
|
*/ |
|
lowerRight: function() { |
|
var result = {}; |
|
result.x = this.b.x; |
|
result.y = this.b.y; |
|
return result; |
|
}, |
|
|
|
/** |
|
* @return {Number} Width of bounds. |
|
*/ |
|
width: function() { |
|
return this.b.x - this.a.x; |
|
}, |
|
|
|
/** |
|
* @return {Number} Height of bounds. |
|
*/ |
|
height: function() { |
|
return this.b.y - this.a.y; |
|
}, |
|
|
|
/** |
|
* @return {Point} The center point of this bounds. |
|
*/ |
|
center: function() { |
|
var center = {}; |
|
center.x =(this.a.x + this.b.x)/2.0; |
|
center.y =(this.a.y + this.b.y)/2.0; |
|
return center; |
|
}, |
|
|
|
|
|
/** |
|
* @return {Point} The center point of this bounds relative to upperLeft. |
|
*/ |
|
midPoint: function() { |
|
|
|
var midpoint = {}; |
|
midpoint.x = (this.b.x - this.a.x)/2.0; |
|
midpoint.y = (this.b.y - this.a.y)/2.0; |
|
return midpoint; |
|
}, |
|
|
|
/** |
|
* Moves the center point of this bounds to the new position. |
|
* @param p {Point} |
|
* or |
|
* @param x {Number} |
|
* @param y {Number} |
|
*/ |
|
centerMoveTo: function() { |
|
var currentPosition = this.center(); |
|
|
|
switch (arguments.length) { |
|
|
|
case 1: |
|
this.moveBy(arguments[0].x - currentPosition.x, |
|
arguments[0].y - currentPosition.y); |
|
break; |
|
|
|
case 2: |
|
this.moveBy(arguments[0] - currentPosition.x, |
|
arguments[1] - currentPosition.y); |
|
break; |
|
} |
|
}, |
|
|
|
isIncluded: function(point, offset) { |
|
|
|
var pointX, pointY, offset; |
|
|
|
// Get the the two Points |
|
switch(arguments.length) { |
|
case 1: |
|
pointX = arguments[0].x; |
|
pointY = arguments[0].y; |
|
offset = 0; |
|
|
|
break; |
|
case 2: |
|
if(arguments[0].x && arguments[0].y) { |
|
pointX = arguments[0].x; |
|
pointY = arguments[0].y; |
|
offset = Math.abs(arguments[1]); |
|
} else { |
|
pointX = arguments[0]; |
|
pointY = arguments[1]; |
|
offset = 0; |
|
} |
|
break; |
|
case 3: |
|
pointX = arguments[0]; |
|
pointY = arguments[1]; |
|
offset = Math.abs(arguments[2]); |
|
break; |
|
default: |
|
throw "isIncluded needs one, two or three arguments"; |
|
} |
|
|
|
var ul = this.upperLeft(); |
|
var lr = this.lowerRight(); |
|
|
|
if(pointX >= ul.x - offset |
|
&& pointX <= lr.x + offset && pointY >= ul.y - offset |
|
&& pointY <= lr.y + offset) |
|
return true; |
|
else |
|
return false; |
|
}, |
|
|
|
/** |
|
* @return {Bounds} A copy of this bounds. |
|
*/ |
|
clone: function() { |
|
|
|
//Returns a new bounds object without the callback |
|
// references of the original bounds |
|
return new ORYX.Core.Bounds(this); |
|
}, |
|
|
|
toString: function() { |
|
|
|
return "( "+this.a.x+" | "+this.a.y+" )/( "+this.b.x+" | "+this.b.y+" )"; |
|
}, |
|
|
|
serializeForERDF: function() { |
|
|
|
return this.a.x+","+this.a.y+","+this.b.x+","+this.b.y; |
|
} |
|
}; |
|
|
|
ORYX.Core.Bounds = Clazz.extend(ORYX.Core.Bounds);/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
|
|
/** |
|
* @classDescription Abstract base class for all objects that have a graphical representation |
|
* within the editor. |
|
* @extends Clazz |
|
*/ |
|
ORYX.Core.UIObject = { |
|
/** |
|
* Constructor of the UIObject class. |
|
*/ |
|
construct: function(options) { |
|
|
|
this.isChanged = true; //Flag, if UIObject has been changed since last update. |
|
this.isResized = true; |
|
this.isVisible = true; //Flag, if UIObject's display attribute is set to 'inherit' or 'none' |
|
this.isSelectable = false; //Flag, if UIObject is selectable. |
|
this.isResizable = false; //Flag, if UIObject is resizable. |
|
this.isMovable = false; //Flag, if UIObject is movable. |
|
|
|
this.id = ORYX.Editor.provideId(); //get unique id |
|
this.parent = undefined; //parent is defined, if this object is added to another uiObject. |
|
this.node = undefined; //this is a reference to the SVG representation, either locally or in DOM. |
|
this.children = []; //array for all add uiObjects |
|
|
|
this.bounds = new ORYX.Core.Bounds(); //bounds with undefined values |
|
|
|
this._changedCallback = this._changed.bind(this); //callback reference for calling _changed |
|
this.bounds.registerCallback(this._changedCallback); //set callback in bounds |
|
|
|
if(options && options.eventHandlerCallback) { |
|
this.eventHandlerCallback = options.eventHandlerCallback; |
|
} |
|
}, |
|
|
|
/** |
|
* Sets isChanged flag to true. Callback for the bounds object. |
|
*/ |
|
_changed: function(bounds, isResized) { |
|
this.isChanged = true; |
|
if(this.bounds == bounds) |
|
this.isResized = isResized || this.isResized; |
|
}, |
|
|
|
/** |
|
* If something changed, this method calls the refresh method that must be implemented by subclasses. |
|
*/ |
|
update: function() { |
|
if(this.isChanged) { |
|
this.refresh(); |
|
this.isChanged = false; |
|
|
|
//call update of all children |
|
this.children.each(function(value) { |
|
value.update(); |
|
}); |
|
} |
|
}, |
|
|
|
/** |
|
* Is called in update method, if isChanged is set to true. Sub classes should call the super class method. |
|
*/ |
|
refresh: function() { |
|
|
|
}, |
|
|
|
/** |
|
* @return {Array} Array of all child UIObjects. |
|
*/ |
|
getChildren: function() { |
|
return this.children.clone(); |
|
}, |
|
|
|
/** |
|
* @return {Array} Array of all parent UIObjects. |
|
*/ |
|
getParents: function(){ |
|
var parents = []; |
|
var parent = this.parent; |
|
while(parent){ |
|
parents.push(parent); |
|
parent = parent.parent; |
|
} |
|
return parents; |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the given parent is one of the UIObjects parents or the UIObject themselves, otherwise FALSE. |
|
* @param {UIObject} parent |
|
* @return {Boolean} |
|
*/ |
|
isParent: function(parent){ |
|
var cparent = this; |
|
while(cparent){ |
|
if (cparent === parent){ |
|
return true; |
|
} |
|
cparent = cparent.parent; |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* @return {String} Id of this UIObject |
|
*/ |
|
getId: function() { |
|
return this.id; |
|
}, |
|
|
|
/** |
|
* Method for accessing child uiObjects by id. |
|
* @param {String} id |
|
* @param {Boolean} deep |
|
* |
|
* @return {UIObject} If found, it returns the UIObject with id. |
|
*/ |
|
getChildById: function(id, deep) { |
|
return this.children.find(function(uiObj) { |
|
if(uiObj.getId() === id) { |
|
return uiObj; |
|
} else { |
|
if(deep) { |
|
var obj = uiObj.getChildById(id, deep); |
|
if(obj) { |
|
return obj; |
|
} |
|
} |
|
} |
|
}); |
|
}, |
|
|
|
/** |
|
* Adds an UIObject to this UIObject and sets the parent of the |
|
* added UIObject. It is also added to the SVG representation of this |
|
* UIObject. |
|
* @param {UIObject} uiObject |
|
*/ |
|
add: function(uiObject) { |
|
//add uiObject, if it is not already a child of this object |
|
if (!(this.children.member(uiObject))) { |
|
//if uiObject is child of another parent, remove it from that parent. |
|
if(uiObject.parent) { |
|
uiObject.remove(uiObject); |
|
} |
|
|
|
//add uiObject to children |
|
this.children.push(uiObject); |
|
|
|
//set parent reference |
|
uiObject.parent = this; |
|
|
|
//add uiObject.node to this.node |
|
uiObject.node = this.node.appendChild(uiObject.node); |
|
|
|
//register callback to get informed, if child is changed |
|
uiObject.bounds.registerCallback(this._changedCallback); |
|
|
|
//uiObject.update(); |
|
} else { |
|
ORYX.Log.info("add: ORYX.Core.UIObject is already a child of this object."); |
|
} |
|
}, |
|
|
|
/** |
|
* Removes UIObject from this UIObject. The SVG representation will also |
|
* be removed from this UIObject's SVG representation. |
|
* @param {UIObject} uiObject |
|
*/ |
|
remove: function(uiObject) { |
|
//if uiObject is a child of this object, remove it. |
|
if (this.children.member(uiObject)) { |
|
//remove uiObject from children |
|
this.children = this._uiObjects.without(uiObject); |
|
|
|
//delete parent reference of uiObject |
|
uiObject.parent = undefined; |
|
|
|
//delete uiObject.node from this.node |
|
uiObject.node = this.node.removeChild(uiObject.node); |
|
|
|
//unregister callback to get informed, if child is changed |
|
uiObject.bounds.unregisterCallback(this._changedCallback); |
|
} else { |
|
ORYX.Log.info("remove: ORYX.Core.UIObject is not a child of this object."); |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Calculates absolute bounds of this UIObject. |
|
*/ |
|
absoluteBounds: function() { |
|
if(this.parent) { |
|
var absUL = this.absoluteXY(); |
|
return new ORYX.Core.Bounds(absUL.x, absUL.y, |
|
absUL.x + this.bounds.width(), |
|
absUL.y + this.bounds.height()); |
|
} else { |
|
return this.bounds.clone(); |
|
} |
|
}, |
|
|
|
/** |
|
* @return {Point} The absolute position of this UIObject. |
|
*/ |
|
absoluteXY: function() { |
|
if(this.parent) { |
|
var pXY = this.parent.absoluteXY(); |
|
var result = {}; |
|
result.x = pXY.x + this.bounds.upperLeft().x; |
|
result.y = pXY.y + this.bounds.upperLeft().y; |
|
return result; |
|
} else { |
|
var result = {}; |
|
result.x = this.bounds.upperLeft().x; |
|
result.y = this.bounds.upperLeft().y; |
|
return result; |
|
} |
|
}, |
|
|
|
/** |
|
* @return {Point} The absolute position from the Center of this UIObject. |
|
*/ |
|
absoluteCenterXY: function() { |
|
if(this.parent) { |
|
var pXY = this.parent.absoluteXY(); |
|
var result = {}; |
|
result.x = pXY.x + this.bounds.center().x; |
|
result.y = pXY.y + this.bounds.center().y; |
|
return result; |
|
|
|
} else { |
|
var result = {}; |
|
result.x = this.bounds.center().x; |
|
result.y = this.bounds.center().y; |
|
return result; |
|
} |
|
}, |
|
|
|
/** |
|
* Hides this UIObject and all its children. |
|
*/ |
|
hide: function() { |
|
this.node.setAttributeNS(null, 'display', 'none'); |
|
this.isVisible = false; |
|
this.children.each(function(uiObj) { |
|
uiObj.hide(); |
|
}); |
|
}, |
|
|
|
/** |
|
* Enables visibility of this UIObject and all its children. |
|
*/ |
|
show: function() { |
|
this.node.setAttributeNS(null, 'display', 'inherit'); |
|
this.isVisible = true; |
|
this.children.each(function(uiObj) { |
|
uiObj.show(); |
|
}); |
|
}, |
|
|
|
addEventHandlers: function(node) { |
|
|
|
node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this._delegateEvent.bind(this), false); |
|
node.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this._delegateEvent.bind(this), false); |
|
node.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this._delegateEvent.bind(this), false); |
|
node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOVER, this._delegateEvent.bind(this), false); |
|
node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOUT, this._delegateEvent.bind(this), false); |
|
node.addEventListener('click', this._delegateEvent.bind(this), false); |
|
node.addEventListener(ORYX.CONFIG.EVENT_DBLCLICK, this._delegateEvent.bind(this), false); |
|
|
|
}, |
|
|
|
_delegateEvent: function(event) { |
|
if(this.eventHandlerCallback) { |
|
this.eventHandlerCallback(event, this); |
|
} |
|
}, |
|
|
|
toString: function() { return "UIObject " + this.id } |
|
}; |
|
ORYX.Core.UIObject = Clazz.extend(ORYX.Core.UIObject);/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
/** |
|
* Top Level uiobject. |
|
* @class ORYX.Core.AbstractShape |
|
* @extends ORYX.Core.UIObject |
|
*/ |
|
ORYX.Core.AbstractShape = ORYX.Core.UIObject.extend( |
|
/** @lends ORYX.Core.AbstractShape.prototype */ |
|
{ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(options, stencil, facade) { |
|
|
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.resourceId = ORYX.Editor.provideId(); //Id of resource in DOM |
|
|
|
// stencil reference |
|
this._stencil = stencil; |
|
// if the stencil defines a super stencil that should be used for its instances, set it. |
|
if (this._stencil._jsonStencil.superId){ |
|
stencilId = this._stencil.id() |
|
superStencilId = stencilId.substring(0, stencilId.indexOf("#") + 1) + stencil._jsonStencil.superId; |
|
stencilSet = this._stencil.stencilSet(); |
|
this._stencil = stencilSet.stencil(superStencilId); |
|
} |
|
|
|
//Hash map for all properties. Only stores the values of the properties. |
|
this.properties = new Hash(); |
|
this.propertiesChanged = new Hash(); |
|
|
|
// List of properties which are not included in the stencilset, |
|
// but which gets (de)serialized |
|
this.hiddenProperties = new Hash(); |
|
|
|
|
|
//Initialization of property map and initial value. |
|
this._stencil.properties().each((function(property) { |
|
var key = property.prefix() + "-" + property.id(); |
|
this.properties[key] = property.value(); |
|
this.propertiesChanged[key] = true; |
|
}).bind(this)); |
|
|
|
// if super stencil was defined, also regard stencil's properties: |
|
if (stencil._jsonStencil.superId) { |
|
stencil.properties().each((function(property) { |
|
var key = property.prefix() + "-" + property.id(); |
|
var value = property.value(); |
|
var oldValue = this.properties[key]; |
|
this.properties[key] = value; |
|
this.propertiesChanged[key] = true; |
|
|
|
// Raise an event, to show that the property has changed |
|
// required for plugins like processLink.js |
|
//window.setTimeout( function(){ |
|
|
|
this._delegateEvent({ |
|
type : ORYX.CONFIG.EVENT_PROPERTY_CHANGED, |
|
name : key, |
|
value : value, |
|
oldValue: oldValue |
|
}); |
|
|
|
//}.bind(this), 10) |
|
|
|
}).bind(this)); |
|
} |
|
|
|
}, |
|
|
|
layout: function() { |
|
|
|
}, |
|
|
|
/** |
|
* Returns the stencil object specifiing the type of the shape. |
|
*/ |
|
getStencil: function() { |
|
return this._stencil; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} resourceId |
|
*/ |
|
getChildShapeByResourceId: function(resourceId) { |
|
|
|
resourceId = ERDF.__stripHashes(resourceId); |
|
|
|
return this.getChildShapes(true).find(function(shape) { |
|
return shape.resourceId == resourceId |
|
}); |
|
}, |
|
/** |
|
* |
|
* @param {Object} deep |
|
* @param {Object} iterator |
|
*/ |
|
getChildShapes: function(deep, iterator) { |
|
var result = []; |
|
|
|
this.children.each(function(uiObject) { |
|
if(uiObject instanceof ORYX.Core.Shape && uiObject.isVisible ) { |
|
if(iterator) { |
|
iterator(uiObject); |
|
} |
|
result.push(uiObject); |
|
if(deep) { |
|
result = result.concat(uiObject.getChildShapes(deep, iterator)); |
|
} |
|
} |
|
}); |
|
|
|
return result; |
|
}, |
|
|
|
/** |
|
* @param {Object} shape |
|
* @return {boolean} true if any of shape's childs is given shape |
|
*/ |
|
hasChildShape: function(shape){ |
|
return this.getChildShapes().any(function(child){ |
|
return (child === shape) || child.hasChildShape(shape); |
|
}); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} deep |
|
* @param {Object} iterator |
|
*/ |
|
getChildNodes: function(deep, iterator) { |
|
var result = []; |
|
|
|
this.children.each(function(uiObject) { |
|
if(uiObject instanceof ORYX.Core.Node && uiObject.isVisible) { |
|
if(iterator) { |
|
iterator(uiObject); |
|
} |
|
result.push(uiObject); |
|
} |
|
if(uiObject instanceof ORYX.Core.Shape) { |
|
if(deep) { |
|
result = result.concat(uiObject.getChildNodes(deep, iterator)); |
|
} |
|
} |
|
}); |
|
|
|
return result; |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} deep |
|
* @param {Object} iterator |
|
*/ |
|
getChildEdges: function(deep, iterator) { |
|
var result = []; |
|
|
|
this.children.each(function(uiObject) { |
|
if(uiObject instanceof ORYX.Core.Edge && uiObject.isVisible) { |
|
if(iterator) { |
|
iterator(uiObject); |
|
} |
|
result.push(uiObject); |
|
} |
|
if(uiObject instanceof ORYX.Core.Shape) { |
|
if(deep) { |
|
result = result.concat(uiObject.getChildEdges(deep, iterator)); |
|
} |
|
} |
|
}); |
|
|
|
return result; |
|
}, |
|
|
|
/** |
|
* Returns a sorted array of ORYX.Core.Node objects. |
|
* Ordered in z Order, the last object has the highest z Order. |
|
*/ |
|
//TODO deep iterator |
|
getAbstractShapesAtPosition: function() { |
|
var x, y; |
|
switch (arguments.length) { |
|
case 1: |
|
x = arguments[0].x; |
|
y = arguments[0].y; |
|
break; |
|
case 2: //two or more arguments |
|
x = arguments[0]; |
|
y = arguments[1]; |
|
break; |
|
default: |
|
throw "getAbstractShapesAtPosition needs 1 or 2 arguments!" |
|
} |
|
|
|
if(this.isPointIncluded(x, y)) { |
|
|
|
var result = []; |
|
result.push(this); |
|
|
|
//check, if one child is at that position |
|
|
|
|
|
var childNodes = this.getChildNodes(); |
|
var childEdges = this.getChildEdges(); |
|
|
|
[childNodes, childEdges].each(function(ne){ |
|
var nodesAtPosition = new Hash(); |
|
|
|
ne.each(function(node) { |
|
if(!node.isVisible){ return } |
|
var candidates = node.getAbstractShapesAtPosition( x , y ); |
|
if(candidates.length > 0) { |
|
var nodesInZOrder = $A(node.node.parentNode.childNodes); |
|
var zOrderIndex = nodesInZOrder.indexOf(node.node); |
|
nodesAtPosition[zOrderIndex] = candidates; |
|
} |
|
}); |
|
|
|
nodesAtPosition.keys().sort().each(function(key) { |
|
result = result.concat(nodesAtPosition[key]); |
|
}); |
|
}); |
|
|
|
return result; |
|
|
|
} else { |
|
return []; |
|
} |
|
}, |
|
|
|
/** |
|
* |
|
* @param key {String} Must be 'prefix-id' of property |
|
* @param value {Object} Can be of type String or Number according to property type. |
|
*/ |
|
setProperty: function(key, value, force) { |
|
var oldValue = this.properties[key]; |
|
if(oldValue !== value || force === true) { |
|
this.properties[key] = value; |
|
this.propertiesChanged[key] = true; |
|
this._changed(); |
|
|
|
// Raise an event, to show that the property has changed |
|
//window.setTimeout( function(){ |
|
|
|
if (!this._isInSetProperty) { |
|
this._isInSetProperty = true; |
|
|
|
this._delegateEvent({ |
|
type : ORYX.CONFIG.EVENT_PROPERTY_CHANGED, |
|
elements : [this], |
|
name : key, |
|
value : value, |
|
oldValue: oldValue |
|
}); |
|
|
|
delete this._isInSetProperty; |
|
} |
|
//}.bind(this), 10) |
|
} |
|
}, |
|
|
|
/** |
|
* Returns TRUE if one of the properties is flagged as dirty |
|
* @return {boolean} |
|
*/ |
|
isPropertyChanged: function(){ |
|
return this.propertiesChanged.any(function(property){ return property.value }); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {String} Must be 'prefix-id' of property |
|
* @param {Object} Can be of type String or Number according to property type. |
|
*/ |
|
setHiddenProperty: function(key, value) { |
|
// IF undefined, Delete |
|
if (value === undefined) { |
|
delete this.hiddenProperties[key]; |
|
return; |
|
} |
|
var oldValue = this.hiddenProperties[key]; |
|
if (oldValue !== value) { |
|
this.hiddenProperties[key] = value; |
|
} |
|
}, |
|
/** |
|
* Calculate if the point is inside the Shape |
|
* @param {Point} |
|
*/ |
|
isPointIncluded: function(pointX, pointY, absoluteBounds) { |
|
var absBounds = absoluteBounds ? absoluteBounds : this.absoluteBounds(); |
|
return absBounds.isIncluded(pointX, pointY); |
|
|
|
}, |
|
|
|
/** |
|
* Get the serialized object |
|
* return Array with hash-entrees (prefix, name, value) |
|
* Following values will given: |
|
* Type |
|
* Properties |
|
*/ |
|
serialize: function() { |
|
var serializedObject = []; |
|
|
|
// Add the type |
|
serializedObject.push({name: 'type', prefix:'oryx', value: this.getStencil().id(), type: 'literal'}); |
|
|
|
// Add hidden properties |
|
this.hiddenProperties.each(function(prop){ |
|
serializedObject.push({name: prop.key.replace("oryx-", ""), prefix: "oryx", value: prop.value, type: 'literal'}); |
|
}.bind(this)); |
|
|
|
// Add all properties |
|
this.getStencil().properties().each((function(property){ |
|
|
|
var prefix = property.prefix(); // Get prefix |
|
var name = property.id(); // Get name |
|
|
|
//if(typeof this.properties[prefix+'-'+name] == 'boolean' || this.properties[prefix+'-'+name] != "") |
|
serializedObject.push({name: name, prefix: prefix, value: this.properties[prefix+'-'+name], type: 'literal'}); |
|
|
|
}).bind(this)); |
|
|
|
return serializedObject; |
|
}, |
|
|
|
|
|
deserialize: function(serialize){ |
|
// Search in Serialize |
|
var initializedDocker = 0; |
|
|
|
// Sort properties so that the hidden properties are first in the list |
|
serialize = serialize.sort(function(a,b){ a = Number(this.properties.keys().member(a.prefix+"-"+a.name)); b = Number(this.properties.keys().member(b.prefix+"-"+b.name)); return a > b ? 1 : (a < b ? -1 : 0) }.bind(this)); |
|
|
|
serialize.each((function(obj){ |
|
|
|
var name = obj.name; |
|
var prefix = obj.prefix; |
|
var value = obj.value; |
|
|
|
// Complex properties can be real json objects, encode them to a string |
|
if (Object.prototype.toString.call(value) === "Object") value = JSON.stringify(value); |
|
|
|
switch(prefix + "-" + name){ |
|
case 'raziel-parent': |
|
// Set parent |
|
if(!this.parent) {break}; |
|
|
|
// Set outgoing Shape |
|
var parent = this.getCanvas().getChildShapeByResourceId(value); |
|
if(parent) { |
|
parent.add(this); |
|
} |
|
|
|
break; |
|
default: |
|
// If list, eval as an array |
|
var prop = this.getStencil().property(prefix+"-"+name); |
|
if (prop && prop.isList() && typeof value === "string"){ |
|
if ((value||"").strip()&&!value.startsWith("[")&&!value.startsWith("]")) |
|
value = "[\""+value.strip()+"\"]"; |
|
value = ((value||"").strip()||"[]").evalJSON(); |
|
} |
|
|
|
// Set property |
|
if(this.properties.keys().member(prefix+"-"+name)) { |
|
this.setProperty(prefix+"-"+name, value); |
|
} else if(!(name === "bounds"||name === "parent"||name === "target"||name === "dockers"||name === "docker"||name === "outgoing"||name === "incoming")) { |
|
this.setHiddenProperty(prefix+"-"+name, value); |
|
} |
|
|
|
} |
|
}).bind(this)); |
|
}, |
|
|
|
toString: function() { return "ORYX.Core.AbstractShape " + this.id }, |
|
|
|
/** |
|
* Converts the shape to a JSON representation. |
|
* @return {Object} A JSON object with included ORYX.Core.AbstractShape.JSONHelper and getShape() method. |
|
*/ |
|
toJSON: function(){ |
|
var json = { |
|
resourceId: this.resourceId, |
|
properties: jQuery.extend({}, this.properties, this.hiddenProperties).inject({}, function(props, prop){ |
|
var key = prop[0]; |
|
var value = prop[1]; |
|
|
|
//If complex property, value should be a json object |
|
if ( this.getStencil().property(key) |
|
&& this.getStencil().property(key).type() === ORYX.CONFIG.TYPE_COMPLEX |
|
&& Object.prototype.toString.call(value) === "String"){ |
|
|
|
try {value = JSON.parse(value);} catch(error){} |
|
//try {value = JSON.parse(value);} catch(error){} |
|
|
|
// Parse date |
|
} else if (value instanceof Date&&this.getStencil().property(key)){ |
|
try { |
|
value = value.format(this.getStencil().property(key).dateFormat()); |
|
} catch(e){} |
|
} |
|
|
|
//Takes "my_property" instead of "oryx-my_property" as key |
|
key = key.replace(/^[\w_]+-/, ""); |
|
props[key] = value; |
|
|
|
return props; |
|
}.bind(this)), |
|
stencil: { |
|
id: this.getStencil().idWithoutNs() |
|
}, |
|
childShapes: this.getChildShapes().map(function(shape){ |
|
return shape.toJSON(); |
|
}) |
|
}; |
|
|
|
if(this.getOutgoingShapes){ |
|
json.outgoing = this.getOutgoingShapes().map(function(shape){ |
|
return { |
|
resourceId: shape.resourceId |
|
}; |
|
}); |
|
} |
|
|
|
if(this.bounds){ |
|
json.bounds = { |
|
lowerRight: this.bounds.lowerRight(), |
|
upperLeft: this.bounds.upperLeft() |
|
}; |
|
} |
|
|
|
if(this.dockers){ |
|
json.dockers = this.dockers.map(function(docker){ |
|
var d = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center(); |
|
d.getDocker = function(){return docker;}; |
|
return d; |
|
}); |
|
} |
|
|
|
jQuery.extend(json, ORYX.Core.AbstractShape.JSONHelper); |
|
|
|
// do not pollute the json attributes (for serialization), so put the corresponding |
|
// shape is encapsulated in a method |
|
json.getShape = function(){ |
|
return this; |
|
}.bind(this); |
|
|
|
return json; |
|
} |
|
}); |
|
|
|
/** |
|
* @namespace Collection of methods which can be used on a shape json object (ORYX.Core.AbstractShape#toJSON()). |
|
* @example |
|
* jQuery.extend(shapeAsJson, ORYX.Core.AbstractShape.JSONHelper); |
|
*/ |
|
ORYX.Core.AbstractShape.JSONHelper = { |
|
/** |
|
* Iterates over each child shape. |
|
* @param {Object} iterator Iterator function getting a child shape and his parent as arguments. |
|
* @param {boolean} [deep=false] Iterate recursively (childShapes of childShapes) |
|
* @param {boolean} [modify=false] If true, the result of the iterator function is taken as new shape, return false to delete it. This enables modifying the object while iterating through the child shapes. |
|
* @example |
|
* // Increases the lowerRight x value of each direct child shape by one. |
|
* myShapeAsJson.eachChild(function(shape, parentShape){ |
|
* shape.bounds.lowerRight.x = shape.bounds.lowerRight.x + 1; |
|
* return shape; |
|
* }, false, true); |
|
*/ |
|
eachChild: function(iterator, deep, modify){ |
|
if(!this.childShapes) return; |
|
|
|
var newChildShapes = []; //needed if modify = true |
|
|
|
this.childShapes.each(function(shape){ |
|
if (!(shape.eachChild instanceof Function)){ |
|
jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper); |
|
} |
|
var res = iterator(shape, this); |
|
if(res) newChildShapes.push(res); //if false is returned, and modify = true, current shape is deleted. |
|
|
|
if(deep) shape.eachChild(iterator, deep, modify); |
|
}.bind(this)); |
|
|
|
if(modify) this.childShapes = newChildShapes; |
|
}, |
|
|
|
getShape: function(){ |
|
return null; |
|
}, |
|
getChildShapes: function(deep){ |
|
var allShapes = this.childShapes; |
|
|
|
if(deep){ |
|
this.eachChild(function(shape){ |
|
if (!(shape.getChildShapes instanceof Function)){ |
|
jQuery.extend(shape, ORYX.Core.AbstractShape.JSONHelper); |
|
} |
|
allShapes = allShapes.concat(shape.getChildShapes(deep)); |
|
}, true); |
|
} |
|
|
|
return allShapes; |
|
}, |
|
|
|
/** |
|
* @return {String} Serialized JSON object |
|
*/ |
|
serialize: function(){ |
|
return JSON.stringify(this); |
|
} |
|
} |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
|
|
/** |
|
@namespace Namespace for the Oryx core elements. |
|
@name ORYX.Core |
|
*/ |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
/** |
|
* @class Oryx canvas. |
|
* @extends ORYX.Core.AbstractShape |
|
* |
|
*/ |
|
ORYX.Core.Canvas = ORYX.Core.AbstractShape.extend({ |
|
/** @lends ORYX.Core.Canvas.prototype */ |
|
|
|
/** |
|
* Defines the current zoom level |
|
*/ |
|
zoomLevel:1, |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(options, stencil, facade) { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
if(!(options && options.width && options.height)) { |
|
|
|
ORYX.Log.fatal("Canvas is missing mandatory parameters options.width and options.height."); |
|
return; |
|
} |
|
this.facade = facade; |
|
//TODO: set document resource id |
|
this.resourceId = options.id; |
|
|
|
this.nodes = []; |
|
|
|
this.edges = []; |
|
|
|
// Row highlighting states |
|
this.colHighlightState = 0; |
|
|
|
this.colHighlightEnabled = false; |
|
|
|
//init svg document |
|
this.rootNode = ORYX.Editor.graft("http://www.w3.org/2000/svg", options.parentNode, |
|
['svg', {id: this.id, width: options.width, height: options.height}, |
|
['defs', {}] |
|
]); |
|
|
|
this.rootNode.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); |
|
this.rootNode.setAttribute("xmlns:svg", "http://www.w3.org/2000/svg"); |
|
|
|
this._htmlContainer = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", options.parentNode, |
|
['div', {id: "oryx_canvas_htmlContainer", style:"position:absolute; top:5px"}]); |
|
|
|
// Additional SVG-node BELOW the stencils to allow underlays (if that is even a word) by plugins |
|
this.underlayNode = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.rootNode, |
|
['svg', {id: "underlay-container"}]); |
|
|
|
// Create 2 svg-elements in the svg-container |
|
this.columnHightlight1 = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.underlayNode, |
|
['rect', {x: 0, width: ORYX.CONFIG.FORM_ROW_WIDTH + 35, height: "100%", style: "fill: #fff6d5", visibility: "hidden"}]); |
|
|
|
this.columnHightlight2 = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.underlayNode, |
|
['rect', {x: ORYX.CONFIG.FORM_ROW_WIDTH + 35, width: ORYX.CONFIG.FORM_ROW_WIDTH + 25, height: "100%", style: "fill: #fff6d5", visibility: "hidden"}]); |
|
|
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.rootNode, |
|
['g', {}, |
|
['g', {"class": "stencils"}, |
|
['g', {"class": "me"}], |
|
['g', {"class": "children"}], |
|
['g', {"class": "edge"}] |
|
], |
|
['g', {"class":"svgcontainer"}] |
|
]); |
|
|
|
/* |
|
var off = 2 * ORYX.CONFIG.GRID_DISTANCE; |
|
var size = 3; |
|
var d = ""; |
|
for(var i = 0; i <= options.width; i += off) |
|
for(var j = 0; j <= options.height; j += off) |
|
d = d + "M" + (i - size) + " " + j + " l" + (2*size) + " 0 m" + (-size) + " " + (-size) + " l0 " + (2*size) + " m0" + (-size) + " "; |
|
|
|
ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node.firstChild.firstChild, |
|
['path', {d:d , stroke:'#000000', 'stroke-width':'0.15px'},]); |
|
*/ |
|
|
|
//Global definition of default font for shapes |
|
//Definitions in the SVG definition of a stencil will overwrite these settings for |
|
// that stencil. |
|
/*if(navigator.platform.indexOf("Mac") > -1) { |
|
this.node.setAttributeNS(null, 'stroke', 'black'); |
|
this.node.setAttributeNS(null, 'stroke-width', '0.5px'); |
|
this.node.setAttributeNS(null, 'font-family', 'Skia'); |
|
//this.node.setAttributeNS(null, 'letter-spacing', '2px'); |
|
this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT); |
|
} else { |
|
this.node.setAttributeNS(null, 'stroke', 'none'); |
|
this.node.setAttributeNS(null, 'font-family', 'Verdana'); |
|
this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT); |
|
}*/ |
|
|
|
this.node.setAttributeNS(null, 'stroke', 'none'); |
|
this.node.setAttributeNS(null, 'font-family', 'Verdana, sans-serif'); |
|
this.node.setAttributeNS(null, 'font-size-adjust', 'none'); |
|
this.node.setAttributeNS(null, 'font-style', 'normal'); |
|
this.node.setAttributeNS(null, 'font-variant', 'normal'); |
|
this.node.setAttributeNS(null, 'font-weight', 'normal'); |
|
this.node.setAttributeNS(null, 'line-heigth', 'normal'); |
|
|
|
this.node.setAttributeNS(null, 'font-size', ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT); |
|
|
|
this.bounds.set(0,0,options.width, options.height); |
|
|
|
this.addEventHandlers(this.rootNode.parentNode); |
|
|
|
//disable context menu |
|
this.rootNode.oncontextmenu = function() {return false;}; |
|
}, |
|
|
|
focus: function(){ |
|
|
|
try { |
|
// Get a href |
|
if (!this.focusEl) |
|
{ |
|
this.focusEl = jQuery('body').append(jQuery('<a href="#" class="x-grid3-focus x-grid3-focus-canvas"/>')); |
|
this.focusEl.swallowEvent("click", true); |
|
} |
|
|
|
// Focus it |
|
this.focusEl.focus.defer(1, this.focusEl); |
|
this.focusEl.blur.defer(3, this.focusEl); |
|
|
|
} catch(e){} |
|
}, |
|
|
|
setHightlightState: function(state) { |
|
if(this.colHighlightEnabled && this.colHighlightState != state) { |
|
if(state == 0) { |
|
this.columnHightlight1.setAttribute("visibility", "hidden"); |
|
this.columnHightlight2.setAttribute("visibility", "hidden"); |
|
} else if(state == 1) { |
|
this.columnHightlight1.setAttribute("visibility", "visible"); |
|
this.columnHightlight2.setAttribute("visibility", "hidden"); |
|
} else if(state == 2) { |
|
this.columnHightlight1.setAttribute("visibility", "hidden"); |
|
this.columnHightlight2.setAttribute("visibility", "visible"); |
|
} else if(state == 3) { |
|
this.columnHightlight1.setAttribute("visibility", "visible"); |
|
this.columnHightlight2.setAttribute("visibility", "visible"); |
|
} |
|
this.colHighlightState = state; |
|
} |
|
}, |
|
|
|
setHightlightStateBasedOnX : function(x) { |
|
if(x > ORYX.CONFIG.FORM_ROW_WIDTH + 30) { |
|
this.setHightlightState(2); |
|
} else { |
|
this.setHightlightState(1); |
|
} |
|
}, |
|
|
|
update: function() { |
|
|
|
this.nodes.each(function(node) { |
|
this._traverseForUpdate(node); |
|
}.bind(this)); |
|
|
|
// call stencil's layout callback |
|
// (needed for row layouting of xforms) |
|
//this.getStencil().layout(this); |
|
|
|
var layoutEvents = this.getStencil().layout(); |
|
|
|
if(layoutEvents) { |
|
layoutEvents.each(function(event) { |
|
|
|
// setup additional attributes |
|
event.shape = this; |
|
event.forceExecution = true; |
|
event.target = this.rootNode; |
|
|
|
// do layouting |
|
|
|
this._delegateEvent(event); |
|
}.bind(this)) |
|
} |
|
|
|
this.nodes.invoke("_update"); |
|
|
|
this.edges.invoke("_update", true); |
|
|
|
/*this.children.each(function(child) { |
|
child._update(); |
|
});*/ |
|
}, |
|
|
|
_traverseForUpdate: function(shape) { |
|
var childRet = shape.isChanged; |
|
shape.getChildNodes(false, function(child) { |
|
if(this._traverseForUpdate(child)) { |
|
childRet = true; |
|
} |
|
}.bind(this)); |
|
|
|
if(childRet) { |
|
shape.layout(); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
}, |
|
|
|
layout: function() { |
|
|
|
|
|
|
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} deep |
|
* @param {Object} iterator |
|
*/ |
|
getChildNodes: function(deep, iterator) { |
|
if(!deep && !iterator) { |
|
return this.nodes.clone(); |
|
} else { |
|
var result = []; |
|
this.nodes.each(function(uiObject) { |
|
if(iterator) { |
|
iterator(uiObject); |
|
} |
|
result.push(uiObject); |
|
|
|
if(deep && uiObject instanceof ORYX.Core.Shape) { |
|
result = result.concat(uiObject.getChildNodes(deep, iterator)); |
|
} |
|
}); |
|
|
|
return result; |
|
} |
|
}, |
|
|
|
/** |
|
* buggy crap! use base class impl instead! |
|
* @param {Object} iterator |
|
*/ |
|
/* getChildEdges: function(iterator) { |
|
if(iterator) { |
|
this.edges.each(function(edge) { |
|
iterator(edge); |
|
}); |
|
} |
|
|
|
return this.edges.clone(); |
|
}, |
|
*/ |
|
/** |
|
* Overrides the UIObject.add method. Adds uiObject to the correct sub node. |
|
* @param {UIObject} uiObject |
|
*/ |
|
add: function(uiObject, index, silent) { |
|
//if uiObject is child of another UIObject, remove it. |
|
if(uiObject instanceof ORYX.Core.UIObject) { |
|
if (!(this.children.member(uiObject))) { |
|
//if uiObject is child of another parent, remove it from that parent. |
|
if(uiObject.parent) { |
|
uiObject.parent.remove(uiObject, true); |
|
} |
|
|
|
//add uiObject to the Canvas |
|
//add uiObject to this Shape |
|
if(index != undefined) |
|
this.children.splice(index, 0, uiObject); |
|
else |
|
this.children.push(uiObject); |
|
|
|
//set parent reference |
|
uiObject.parent = this; |
|
|
|
//add uiObject.node to this.node depending on the type of uiObject |
|
if(uiObject instanceof ORYX.Core.Shape) { |
|
if(uiObject instanceof ORYX.Core.Edge) { |
|
uiObject.addMarkers(this.rootNode.getElementsByTagNameNS(NAMESPACE_SVG, "defs")[0]); |
|
uiObject.node = this.node.childNodes[0].childNodes[2].appendChild(uiObject.node); |
|
this.edges.push(uiObject); |
|
} else { |
|
uiObject.node = this.node.childNodes[0].childNodes[1].appendChild(uiObject.node); |
|
this.nodes.push(uiObject); |
|
} |
|
} else { //UIObject |
|
uiObject.node = this.node.appendChild(uiObject.node); |
|
} |
|
|
|
uiObject.bounds.registerCallback(this._changedCallback); |
|
|
|
if(this.eventHandlerCallback && silent !== true) |
|
this.eventHandlerCallback({type:ORYX.CONFIG.EVENT_SHAPEADDED,shape:uiObject}) |
|
} else { |
|
|
|
ORYX.Log.warn("add: ORYX.Core.UIObject is already a child of this object."); |
|
} |
|
} else { |
|
|
|
ORYX.Log.fatal("add: Parameter is not of type ORYX.Core.UIObject."); |
|
} |
|
}, |
|
|
|
/** |
|
* Overrides the UIObject.remove method. Removes uiObject. |
|
* @param {UIObject} uiObject |
|
*/ |
|
remove: function(uiObject, silent) { |
|
//if uiObject is a child of this object, remove it. |
|
if (this.children.member(uiObject)) { |
|
//remove uiObject from children |
|
var parent = uiObject.parent; |
|
|
|
this.children = this.children.without(uiObject); |
|
|
|
//delete parent reference of uiObject |
|
uiObject.parent = undefined; |
|
|
|
//delete uiObject.node from this.node |
|
if(uiObject instanceof ORYX.Core.Shape) { |
|
if(uiObject instanceof ORYX.Core.Edge) { |
|
uiObject.removeMarkers(); |
|
uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node); |
|
this.edges = this.edges.without(uiObject); |
|
} else { |
|
uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node); |
|
this.nodes = this.nodes.without(uiObject); |
|
} |
|
} else { //UIObject |
|
uiObject.node = this.node.removeChild(uiObject.node); |
|
} |
|
|
|
if(this.eventHandlerCallback && silent !== true) |
|
this.eventHandlerCallback({type:ORYX.CONFIG.EVENT_SHAPEREMOVED,shape:uiObject, parent:parent}); |
|
|
|
uiObject.bounds.unregisterCallback(this._changedCallback); |
|
} else { |
|
|
|
ORYX.Log.warn("remove: ORYX.Core.UIObject is not a child of this object."); |
|
} |
|
}, |
|
|
|
/** |
|
* Creates shapes out of the given collection of shape objects and adds them to the canvas. |
|
* @example |
|
* canvas.addShapeObjects({ |
|
bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } }, |
|
resourceId:"oryx_F0715955-50F2-403D-9851-C08CFE70F8BD", |
|
childShapes:[], |
|
properties:{}, |
|
stencil:{ |
|
id:"Subprocess" |
|
}, |
|
outgoing:[{resourceId: 'aShape'}], |
|
target: {resourceId: 'aShape'} |
|
}); |
|
* @param {Object} shapeObjects |
|
* @param {Function} [eventHandler] An event handler passed to each newly created shape (as eventHandlerCallback) |
|
* @return {Array} A collection of ORYX.Core.Shape |
|
* @methodOf ORYX.Core.Canvas.prototype |
|
*/ |
|
addShapeObjects: function(shapeObjects, eventHandler){ |
|
if(!shapeObjects) return; |
|
|
|
this.initializingShapes = true; |
|
|
|
/*FIXME This implementation is very evil! At first, all shapes are created on |
|
canvas. In a second step, the attributes are applied. There must be a distinction |
|
between the configuration phase (where the outgoings, for example, are just named), |
|
and the creation phase (where the outgoings are evaluated). This must be reflected |
|
in code to provide a nicer API/ implementation!!! */ |
|
|
|
var addShape = function(shape, parent){ |
|
// Create a new Stencil |
|
var stencil = ORYX.Core.StencilSet.stencil(this.getStencil().namespace() + shape.stencil.id ); |
|
|
|
// Create a new Shape |
|
var ShapeClass = (stencil.type() == "node") ? ORYX.Core.Node : ORYX.Core.Edge; |
|
var newShape = new ShapeClass( |
|
{'eventHandlerCallback': eventHandler}, |
|
stencil, this.facade); |
|
|
|
// Set the resource id |
|
newShape.resourceId = shape.resourceId; |
|
newShape.node.id = "svg-" + shape.resourceId; |
|
|
|
// Set parent to json object to be used later |
|
// Due to the nested json structure, normally shape.parent is not set/ must not be set. |
|
// In special cases, it can be easier to set this directly instead of a nested structure. |
|
shape.parent = "#" + ((shape.parent && shape.parent.resourceId) || parent.resourceId); |
|
|
|
// Add the shape to the canvas |
|
this.add( newShape ); |
|
|
|
return { |
|
json: shape, |
|
object: newShape |
|
}; |
|
}.bind(this); |
|
|
|
/** Builds up recursively a flatted array of shapes, including a javascript object and json representation |
|
* @param {Object} shape Any object that has Object#childShapes |
|
*/ |
|
var addChildShapesRecursively = function(shape){ |
|
var addedShapes = []; |
|
|
|
if (shape.childShapes && shape.childShapes.constructor == String) |
|
{ |
|
shape.childShapes = JSON.parse(shape.childShapes); |
|
} |
|
|
|
shape.childShapes.each(function(childShape){ |
|
addedShapes.push(addShape(childShape, shape)); |
|
addedShapes = addedShapes.concat(addChildShapesRecursively(childShape)); |
|
}); |
|
|
|
return addedShapes; |
|
}.bind(this); |
|
|
|
var shapes = addChildShapesRecursively({ |
|
childShapes: shapeObjects, |
|
resourceId: this.resourceId |
|
}); |
|
|
|
|
|
// prepare deserialisation parameter |
|
shapes.each( |
|
function(shape){ |
|
var properties = []; |
|
for(field in shape.json.properties){ |
|
properties.push({ |
|
prefix: 'oryx', |
|
name: field, |
|
value: shape.json.properties[field] |
|
}); |
|
} |
|
|
|
// Outgoings |
|
shape.json.outgoing.each(function(out){ |
|
properties.push({ |
|
prefix: 'raziel', |
|
name: 'outgoing', |
|
value: "#"+out.resourceId |
|
}); |
|
}); |
|
|
|
// Target |
|
// (because of a bug, the first outgoing is taken when there is no target, |
|
// can be removed after some time) |
|
if(shape.object instanceof ORYX.Core.Edge) { |
|
var target = shape.json.target || shape.json.outgoing[0]; |
|
if(target){ |
|
properties.push({ |
|
prefix: 'raziel', |
|
name: 'target', |
|
value: "#"+target.resourceId |
|
}); |
|
} |
|
} |
|
|
|
// Bounds |
|
if (shape.json.bounds) { |
|
properties.push({ |
|
prefix: 'oryx', |
|
name: 'bounds', |
|
value: shape.json.bounds.upperLeft.x + "," + shape.json.bounds.upperLeft.y + "," + shape.json.bounds.lowerRight.x + "," + shape.json.bounds.lowerRight.y |
|
}); |
|
} |
|
|
|
//Dockers [{x:40, y:50}, {x:30, y:60}] => "40 50 30 60 #" |
|
if(shape.json.dockers){ |
|
properties.push({ |
|
prefix: 'oryx', |
|
name: 'dockers', |
|
value: shape.json.dockers.inject("", function(dockersStr, docker){ |
|
return dockersStr + docker.x + " " + docker.y + " "; |
|
}) + " #" |
|
}); |
|
} |
|
|
|
//Parent |
|
properties.push({ |
|
prefix: 'raziel', |
|
name: 'parent', |
|
value: shape.json.parent |
|
}); |
|
|
|
shape.__properties = properties; |
|
}.bind(this) |
|
); |
|
|
|
// Deserialize the properties from the shapes |
|
// This can't be done earlier because Shape#deserialize expects that all referenced nodes are already there |
|
|
|
// first, deserialize all nodes |
|
shapes.each(function(shape) { |
|
if(shape.object instanceof ORYX.Core.Node) { |
|
shape.object.deserialize(shape.__properties, shape.json); |
|
} |
|
}); |
|
|
|
// second, deserialize all edges |
|
shapes.each(function(shape) { |
|
if(shape.object instanceof ORYX.Core.Edge) { |
|
shape.object.deserialize(shape.__properties, shape.json); |
|
shape.object._oldBounds = shape.object.bounds.clone(); |
|
shape.object._update(); |
|
} |
|
}); |
|
|
|
delete this.initializingShapes; |
|
return shapes.pluck("object"); |
|
}, |
|
|
|
/** |
|
* Updates the size of the canvas, regarding to the containg shapes. |
|
*/ |
|
updateSize: function(){ |
|
// Check the size for the canvas |
|
var maxWidth = 0; |
|
var maxHeight = 0; |
|
var offset = 100; |
|
this.getChildShapes(true, function(shape){ |
|
var b = shape.bounds; |
|
maxWidth = Math.max( maxWidth, b.lowerRight().x + offset) |
|
maxHeight = Math.max( maxHeight, b.lowerRight().y + offset) |
|
}); |
|
|
|
if( this.bounds.width() < maxWidth || this.bounds.height() < maxHeight ){ |
|
this.setSize({width: Math.max(this.bounds.width(), maxWidth), height: Math.max(this.bounds.height(), maxHeight)}) |
|
} |
|
}, |
|
|
|
getRootNode: function() { |
|
return this.rootNode; |
|
}, |
|
|
|
getUnderlayNode: function() { |
|
return this.underlayNode; |
|
}, |
|
|
|
getSvgContainer: function() { |
|
return this.node.childNodes[1]; |
|
}, |
|
|
|
getHTMLContainer: function() { |
|
return this._htmlContainer; |
|
}, |
|
|
|
/** |
|
* Return all elements of the same highest level |
|
* @param {Object} elements |
|
*/ |
|
getShapesWithSharedParent: function(elements) { |
|
|
|
// If there is no elements, return [] |
|
if(!elements || elements.length < 1) { return []; } |
|
// If there is one element, return this element |
|
if(elements.length == 1) { return elements;} |
|
|
|
return elements.findAll(function(value){ |
|
var parentShape = value.parent; |
|
while(parentShape){ |
|
if(elements.member(parentShape)) return false; |
|
parentShape = parentShape.parent; |
|
} |
|
return true; |
|
}); |
|
|
|
}, |
|
|
|
setSize: function(size, dontSetBounds) { |
|
if(!size || !size.width || !size.height){return;}; |
|
|
|
if(this.rootNode.parentNode){ |
|
this.rootNode.parentNode.style.width = size.width + 'px'; |
|
this.rootNode.parentNode.style.height = size.height + 'px'; |
|
} |
|
|
|
this.rootNode.setAttributeNS(null, 'width', size.width); |
|
this.rootNode.setAttributeNS(null, 'height', size.height); |
|
|
|
//this._htmlContainer.style.top = "-" + (size.height + 4) + 'px'; |
|
if( !dontSetBounds ){ |
|
this.bounds.set({a:{x:0,y:0},b:{x:size.width/this.zoomLevel,y:size.height/this.zoomLevel}}); |
|
} |
|
}, |
|
|
|
/** |
|
* Returns an SVG document of the current process. |
|
* @param {Boolean} escapeText Use true, if you want to parse it with an XmlParser, |
|
* false, if you want to use the SVG document in browser on client side. |
|
*/ |
|
getSVGRepresentation: function(escapeText) { |
|
// Get the serialized svg image source |
|
var svgClone = this.getRootNode().cloneNode(true); |
|
|
|
this._removeInvisibleElements(svgClone); |
|
|
|
var x1, y1, x2, y2; |
|
this.getChildShapes(true).each(function(shape) { |
|
var absBounds = shape.absoluteBounds(); |
|
var ul = absBounds.upperLeft(); |
|
var lr = absBounds.lowerRight(); |
|
if(x1 == undefined) { |
|
x1 = ul.x; |
|
y1 = ul.y; |
|
x2 = lr.x; |
|
y2 = lr.y; |
|
} else { |
|
x1 = Math.min(x1, ul.x); |
|
y1 = Math.min(y1, ul.y); |
|
x2 = Math.max(x2, lr.x); |
|
y2 = Math.max(y2, lr.y); |
|
} |
|
}); |
|
|
|
var margin = 50; |
|
var width, height, tx, ty; |
|
if(x1 == undefined) { |
|
width = 0; |
|
height = 0; |
|
tx = 0; |
|
ty = 0; |
|
} else { |
|
width = x2; |
|
height = y2; |
|
tx = -x1+margin/2; |
|
ty = -y1+margin/2; |
|
} |
|
|
|
// Set the width and height |
|
svgClone.setAttributeNS(null, 'width', width + margin); |
|
svgClone.setAttributeNS(null, 'height', height + margin); |
|
|
|
//remove scale factor |
|
svgClone.childNodes[1].removeAttributeNS(null, 'transform'); |
|
|
|
try{ |
|
var svgCont = svgClone.childNodes[1].childNodes[1]; |
|
svgCont.parentNode.removeChild(svgCont); |
|
} catch(e) {} |
|
|
|
if(escapeText) { |
|
$A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan')).each(function(elem) { |
|
elem.textContent = elem.textContent.escapeHTML(); |
|
}); |
|
|
|
$A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text')).each(function(elem) { |
|
if(elem.childNodes.length == 0) |
|
elem.textContent = elem.textContent.escapeHTML(); |
|
}); |
|
} |
|
|
|
// generating absolute urls for the pdf-exporter |
|
$A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'image')).each(function(elem) { |
|
var href = elem.getAttributeNS("http://www.w3.org/1999/xlink","href"); |
|
|
|
if(!href.match("^(http|https)://")) { |
|
href = window.location.protocol + "//" + window.location.host + href; |
|
elem.setAttributeNS("http://www.w3.org/1999/xlink", "href", href); |
|
} |
|
}); |
|
|
|
|
|
// escape all links |
|
$A(svgClone.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'a')).each(function(elem) { |
|
elem.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", (elem.getAttributeNS("http://www.w3.org/1999/xlink","href")||"").escapeHTML()); |
|
}); |
|
|
|
return svgClone; |
|
}, |
|
|
|
/** |
|
* Removes all nodes (and its children) that has the |
|
* attribute visibility set to "hidden" |
|
*/ |
|
_removeInvisibleElements: function(element) { |
|
var index = 0; |
|
while(index < element.childNodes.length) { |
|
var child = element.childNodes[index]; |
|
if(child.getAttributeNS && |
|
child.getAttributeNS(null, "visibility") === "hidden") { |
|
element.removeChild(child); |
|
} else { |
|
this._removeInvisibleElements(child); |
|
index++; |
|
} |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* This method checks all shapes on the canvas and removes all shapes that |
|
* contain invalid bounds values or dockers values(NaN) |
|
*/ |
|
/*cleanUp: function(parent) { |
|
if (!parent) { |
|
parent = this; |
|
} |
|
parent.getChildShapes().each(function(shape){ |
|
var a = shape.bounds.a; |
|
var b = shape.bounds.b; |
|
if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) { |
|
parent.remove(shape); |
|
} |
|
else { |
|
shape.getDockers().any(function(docker) { |
|
a = docker.bounds.a; |
|
b = docker.bounds.b; |
|
if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) { |
|
parent.remove(shape); |
|
return true; |
|
} |
|
return false; |
|
}); |
|
shape.getMagnets().any(function(magnet) { |
|
a = magnet.bounds.a; |
|
b = magnet.bounds.b; |
|
if (isNaN(a.x) || isNaN(a.y) || isNaN(b.x) || isNaN(b.y)) { |
|
parent.remove(shape); |
|
return true; |
|
} |
|
return false; |
|
}); |
|
this.cleanUp(shape); |
|
} |
|
}.bind(this)); |
|
},*/ |
|
|
|
_delegateEvent: function(event) { |
|
if(this.eventHandlerCallback && ( event.target == this.rootNode || event.target == this.rootNode.parentNode )) { |
|
this.eventHandlerCallback(event, this); |
|
} |
|
}, |
|
|
|
toString: function() { return "Canvas " + this.id }, |
|
|
|
/** |
|
* Calls {@link ORYX.Core.AbstractShape#toJSON} and adds some stencil set information. |
|
*/ |
|
toJSON: function() { |
|
var json = arguments.callee.$.toJSON.apply(this, arguments); |
|
|
|
// if(ORYX.CONFIG.STENCILSET_HANDLER.length > 0) { |
|
// json.stencilset = { |
|
// url: this.getStencil().stencilSet().namespace() |
|
// }; |
|
// } else { |
|
json.stencilset = { |
|
url: this.getStencil().stencilSet().source(), |
|
namespace: this.getStencil().stencilSet().namespace() |
|
}; |
|
// } |
|
|
|
|
|
return json; |
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
var idCounter = 0; |
|
var ID_PREFIX = "resource"; |
|
|
|
/** |
|
* Main initialization method. To be called when loading |
|
* of the document, including all scripts, is completed. |
|
*/ |
|
function init() { |
|
|
|
ORYX.Log.debug("Querying editor instances"); |
|
|
|
// Hack for WebKit to set the SVGElement-Classes |
|
ORYX.Editor.setMissingClasses(); |
|
|
|
// If someone wants to create the editor instance himself |
|
if (window.onOryxResourcesLoaded) { |
|
window.onOryxResourcesLoaded(); |
|
} |
|
// Else fetch the model from server and display editor |
|
else { |
|
var modelId = window.location.search.substring(4); |
|
var modelUrl = "./service/model/" + modelId + "/json"; |
|
|
|
ORYX.Editor.createByUrl(modelUrl); |
|
} |
|
} |
|
|
|
/** |
|
@namespace Global Oryx name space |
|
@name ORYX |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
|
|
/** |
|
* The Editor class. |
|
* @class ORYX.Editor |
|
* @extends Clazz |
|
* @param {Object} config An editor object, passed to {@link ORYX.Editor#loadSerialized} |
|
* @param {String} config.id Any ID that can be used inside the editor. If fullscreen=false, any HTML node with this id must be present to render the editor to this node. |
|
* @param {boolean} [config.fullscreen=true] Render editor in fullscreen mode or not. |
|
* @param {String} config.stencilset.url Stencil set URL. |
|
* @param {String} [config.stencil.id] Stencil type used for creating the canvas. |
|
* @param {Object} config.properties Any properties applied to the canvas. |
|
*/ |
|
ORYX.Editor = { |
|
/** @lends ORYX.Editor.prototype */ |
|
// Defines the global dom event listener |
|
DOMEventListeners: new Hash(), |
|
|
|
// Defines the selection |
|
selection: [], |
|
|
|
// Defines the current zoom level |
|
zoomLevel:1.0, |
|
|
|
construct: function(config) { |
|
|
|
// initialization. |
|
this._eventsQueue = []; |
|
this.loadedPlugins = []; |
|
this.pluginsData = []; |
|
|
|
|
|
//meta data about the model for the signavio warehouse |
|
//directory, new, name, description, revision, model (the model data) |
|
|
|
this.modelMetaData = config; |
|
|
|
var model = config; |
|
|
|
this.id = model.modelId; |
|
|
|
if(config.model) { |
|
model = config.model; |
|
} |
|
|
|
if(!this.id) { |
|
this.id = model.id; |
|
if(!this.id) { |
|
this.id = ORYX.Editor.provideId(); |
|
} |
|
} |
|
|
|
// Defines if the editor should be fullscreen or not |
|
this.fullscreen = config.fullscreen !== false; |
|
|
|
// Initialize the eventlistener |
|
this._initEventListener(); |
|
|
|
// Load particular stencilset |
|
if(ORYX.CONFIG.BACKEND_SWITCH) { |
|
var ssUrl = (model.stencilset.namespace||model.stencilset.url).replace("#", "%23"); |
|
ORYX.Core.StencilSet.loadStencilSet(ssUrl, this.modelMetaData, this.id); |
|
} else { |
|
var ssUrl = model.stencilset.url; |
|
ORYX.Core.StencilSet.loadStencilSet(ssUrl, this.modelMetaData, this.id); |
|
} |
|
|
|
// CREATES the canvas |
|
this._createCanvas(model.stencil ? model.stencil.id : null, model.properties); |
|
|
|
// GENERATES the whole EXT.VIEWPORT |
|
this._generateGUI(); |
|
|
|
// Initializing of a callback to check loading ends |
|
var loadPluginFinished = false; |
|
var loadContentFinished = false; |
|
var initFinished = function(){ |
|
if( !loadPluginFinished || !loadContentFinished ){ return } |
|
this._finishedLoading(); |
|
}.bind(this) |
|
|
|
// LOAD the plugins |
|
window.setTimeout(function(){ |
|
this.loadPlugins(); |
|
loadPluginFinished = true; |
|
initFinished(); |
|
}.bind(this), 100); |
|
|
|
// LOAD the content of the current editor instance |
|
window.setTimeout(function(){ |
|
this.loadSerialized(model, true); // Request the meta data as well |
|
this.getCanvas().update(); |
|
loadContentFinished = true; |
|
initFinished(); |
|
}.bind(this), 200); |
|
}, |
|
|
|
_finishedLoading: function() { |
|
// Raise Loaded Event |
|
this.handleEvents( {type:ORYX.CONFIG.EVENT_LOADED} ) |
|
}, |
|
|
|
_initEventListener: function(){ |
|
|
|
// Register on Events |
|
|
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYDOWN, this.catchKeyDownEvents.bind(this), false); |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_KEYUP, this.catchKeyUpEvents.bind(this), false); |
|
|
|
// Enable Key up and down Event |
|
this._keydownEnabled = true; |
|
this._keyupEnabled = true; |
|
|
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEDOWN] = []; |
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEUP] = []; |
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOVER] = []; |
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEOUT] = []; |
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_SELECTION_CHANGED] = []; |
|
this.DOMEventListeners[ORYX.CONFIG.EVENT_MOUSEMOVE] = []; |
|
|
|
}, |
|
|
|
/** |
|
* Generate the whole viewport of the |
|
* Editor and initialized the Ext-Framework |
|
* |
|
*/ |
|
_generateGUI: function() { |
|
|
|
// Defines the layout height if it's NOT fullscreen |
|
var layoutHeight = ORYX.CONFIG.WINDOW_HEIGHT; |
|
var canvasParent = this.getCanvas().rootNode.parentNode; |
|
|
|
jQuery("#canvasSection").append(canvasParent); |
|
|
|
// Set the editor to the center, and refresh the size |
|
canvasParent.parentNode.setAttributeNS(null, 'align', 'center'); |
|
canvasParent.setAttributeNS(null, 'align', 'left'); |
|
this.getCanvas().setSize({ |
|
width : ORYX.CONFIG.CANVAS_WIDTH, |
|
height : ORYX.CONFIG.CANVAS_HEIGHT |
|
}); |
|
|
|
}, |
|
|
|
getAvailablePlugins: function(){ |
|
var curAvailablePlugins=ORYX.availablePlugins.clone(); |
|
curAvailablePlugins.each(function(plugin){ |
|
if(this.loadedPlugins.find(function(loadedPlugin){ |
|
return loadedPlugin.type==this.name; |
|
}.bind(plugin))){ |
|
plugin.engaged=true; |
|
}else{ |
|
plugin.engaged=false; |
|
} |
|
}.bind(this)); |
|
return curAvailablePlugins; |
|
}, |
|
|
|
loadScript: function (url, callback){ |
|
var script = document.createElement("script") |
|
script.type = "text/javascript"; |
|
if (script.readyState){ //IE |
|
script.onreadystatechange = function(){ |
|
if (script.readyState == "loaded" || script.readyState == "complete"){ |
|
script.onreadystatechange = null; |
|
callback(); |
|
} |
|
}; |
|
} else { //Others |
|
script.onload = function(){ |
|
callback(); |
|
}; |
|
} |
|
script.src = url; |
|
document.getElementsByTagName("head")[0].appendChild(script); |
|
}, |
|
/** |
|
* activate Plugin |
|
* |
|
* @param {String} name |
|
* @param {Function} callback |
|
* callback(sucess, [errorCode]) |
|
* errorCodes: NOTUSEINSTENCILSET, REQUIRESTENCILSET, NOTFOUND, YETACTIVATED |
|
*/ |
|
activatePluginByName: function(name, callback, loadTry){ |
|
|
|
var match=this.getAvailablePlugins().find(function(value){return value.name==name}); |
|
if(match && (!match.engaged || (match.engaged==='false'))){ |
|
var loadedStencilSetsNamespaces = this.getStencilSets().keys(); |
|
var facade = this._getPluginFacade(); |
|
var newPlugin; |
|
var me=this; |
|
ORYX.Log.debug("Initializing plugin '%0'", match.name); |
|
|
|
if (!match.requires || !match.requires.namespaces || match.requires.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) ){ |
|
if(!match.notUsesIn || !match.notUsesIn.namespaces || !match.notUsesIn.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 })){ |
|
|
|
try { |
|
|
|
var className = eval(match.name); |
|
var newPlugin = new className(facade, match); |
|
newPlugin.type = match.name; |
|
|
|
// If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data |
|
if (newPlugin.registryChanged) |
|
newPlugin.registryChanged(me.pluginsData); |
|
|
|
// If there have an onSelection-Method it will pushed to the Editor Event-Handler |
|
if (newPlugin.onSelectionChanged) |
|
me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, newPlugin.onSelectionChanged.bind(newPlugin)); |
|
this.loadedPlugins.push(newPlugin); |
|
this.loadedPlugins.each(function(loaded){ |
|
if(loaded.registryChanged) |
|
loaded.registryChanged(this.pluginsData); |
|
}.bind(me)); |
|
callback(true); |
|
|
|
} catch(e) { |
|
ORYX.Log.warn("Plugin %0 is not available", match.name); |
|
if(!!loadTry){ |
|
callback(false,"INITFAILED"); |
|
return; |
|
} |
|
this.loadScript("plugins/scripts/"+match.source, this.activatePluginByName.bind(this,match.name,callback,true)); |
|
} |
|
}else{ |
|
callback(false,"NOTUSEINSTENCILSET"); |
|
ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name); |
|
} |
|
|
|
} else { |
|
callback(false,"REQUIRESTENCILSET"); |
|
ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name); |
|
} |
|
|
|
|
|
}else{ |
|
callback(false, match?"NOTFOUND":"YETACTIVATED"); |
|
//TODO error handling |
|
} |
|
}, |
|
|
|
/** |
|
* Laden der Plugins |
|
*/ |
|
loadPlugins: function() { |
|
|
|
// if there should be plugins but still are none, try again. |
|
// TODO this should wait for every plugin respectively. |
|
/*if (!ORYX.Plugins && ORYX.availablePlugins.length > 0) { |
|
window.setTimeout(this.loadPlugins.bind(this), 100); |
|
return; |
|
}*/ |
|
|
|
var me = this; |
|
var newPlugins = []; |
|
|
|
|
|
var loadedStencilSetsNamespaces = this.getStencilSets().keys(); |
|
|
|
// Available Plugins will be initalize |
|
var facade = this._getPluginFacade(); |
|
|
|
// If there is an Array where all plugins are described, than only take those |
|
// (that comes from the usage of oryx with a mashup api) |
|
if( ORYX.MashupAPI && ORYX.MashupAPI.loadablePlugins && ORYX.MashupAPI.loadablePlugins instanceof Array ){ |
|
|
|
// Get the plugins from the available plugins (those who are in the plugins.xml) |
|
ORYX.availablePlugins = $A(ORYX.availablePlugins).findAll(function(value){ |
|
return ORYX.MashupAPI.loadablePlugins.include( value.name ) |
|
}) |
|
|
|
// Add those plugins to the list, which are only in the loadablePlugins list |
|
ORYX.MashupAPI.loadablePlugins.each(function( className ){ |
|
if( !(ORYX.availablePlugins.find(function(val){ return val.name == className }))){ |
|
ORYX.availablePlugins.push( {name: className } ); |
|
} |
|
}) |
|
} |
|
|
|
|
|
ORYX.availablePlugins.each(function(value) { |
|
ORYX.Log.debug("Initializing plugin '%0'", value.name); |
|
if( (!value.requires || !value.requires.namespaces || value.requires.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) ) && |
|
(!value.notUsesIn || !value.notUsesIn.namespaces || !value.notUsesIn.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) )&& |
|
/*only load activated plugins or undefined */ |
|
(value.engaged || (value.engaged===undefined)) ){ |
|
|
|
try { |
|
var className = eval(value.name); |
|
if( className ){ |
|
var plugin = new className(facade, value); |
|
plugin.type = value.name; |
|
newPlugins.push( plugin ); |
|
plugin.engaged=true; |
|
} |
|
} catch(e) { |
|
ORYX.Log.warn("Plugin %0 is not available %1", value.name, e); |
|
} |
|
|
|
} else { |
|
ORYX.Log.info("Plugin need a stencilset which is not loaded'", value.name); |
|
} |
|
|
|
}); |
|
|
|
newPlugins.each(function(value) { |
|
// If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data |
|
if(value.registryChanged) |
|
value.registryChanged(me.pluginsData); |
|
|
|
// If there have an onSelection-Method it will pushed to the Editor Event-Handler |
|
if(value.onSelectionChanged) |
|
me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, value.onSelectionChanged.bind(value)); |
|
}); |
|
|
|
this.loadedPlugins = newPlugins; |
|
|
|
this.registerPluginsOnKeyEvents(); |
|
|
|
this.setSelection(); |
|
|
|
}, |
|
|
|
/** |
|
* Creates the Canvas |
|
* @param {String} [stencilType] The stencil type used for creating the canvas. If not given, a stencil with myBeRoot = true from current stencil set is taken. |
|
* @param {Object} [canvasConfig] Any canvas properties (like language). |
|
*/ |
|
_createCanvas: function(stencilType, canvasConfig) { |
|
if (stencilType) { |
|
// Add namespace to stencilType |
|
if (stencilType.search(/^http/) === -1) { |
|
stencilType = this.getStencilSets().values()[0].namespace() + stencilType; |
|
} |
|
} |
|
else { |
|
// Get any root stencil type |
|
stencilType = this.getStencilSets().values()[0].findRootStencilName(); |
|
} |
|
|
|
// get the stencil associated with the type |
|
var canvasStencil = ORYX.Core.StencilSet.stencil(stencilType); |
|
|
|
if (!canvasStencil) |
|
ORYX.Log.fatal("Initialisation failed, because the stencil with the type %0 is not part of one of the loaded stencil sets.", stencilType); |
|
|
|
// create all dom |
|
// TODO fix border, so the visible canvas has a double border and some spacing to the scrollbars |
|
var div = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", null, ['div']); |
|
// set class for custom styling |
|
div.addClassName("ORYX_Editor"); |
|
|
|
// create the canvas |
|
this._canvas = new ORYX.Core.Canvas({ |
|
width : ORYX.CONFIG.CANVAS_WIDTH, |
|
height : ORYX.CONFIG.CANVAS_HEIGHT, |
|
'eventHandlerCallback' : this.handleEvents.bind(this), |
|
id : this.id, |
|
parentNode : div |
|
}, canvasStencil, this._getPluginFacade()); |
|
|
|
if (canvasConfig) { |
|
// Migrate canvasConfig to an RDF-like structure |
|
//FIXME this isn't nice at all because we don't want rdf any longer |
|
var properties = []; |
|
for(field in canvasConfig){ |
|
properties.push({ |
|
prefix: 'oryx', |
|
name: field, |
|
value: canvasConfig[field] |
|
}); |
|
} |
|
|
|
this._canvas.deserialize(properties); |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Returns a per-editor singleton plugin facade. |
|
* To be used in plugin initialization. |
|
*/ |
|
_getPluginFacade: function() { |
|
|
|
// if there is no pluginfacade already created: |
|
if(!(this._pluginFacade)) |
|
|
|
// create it. |
|
this._pluginFacade = { |
|
|
|
activatePluginByName: this.activatePluginByName.bind(this), |
|
//deactivatePluginByName: this.deactivatePluginByName.bind(this), |
|
getAvailablePlugins: this.getAvailablePlugins.bind(this), |
|
offer: this.offer.bind(this), |
|
getStencilSets: this.getStencilSets.bind(this), |
|
getStencilSetExtensionDefinition:function(){ return Object.clone(this.ss_extensions_def||{})}.bind(this), |
|
getRules: this.getRules.bind(this), |
|
loadStencilSet: this.loadStencilSet.bind(this), |
|
createShape: this.createShape.bind(this), |
|
deleteShape: this.deleteShape.bind(this), |
|
getSelection: this.getSelection.bind(this), |
|
setSelection: this.setSelection.bind(this), |
|
updateSelection: this.updateSelection.bind(this), |
|
getCanvas: this.getCanvas.bind(this), |
|
|
|
importJSON: this.importJSON.bind(this), |
|
getJSON: this.getJSON.bind(this), |
|
getSerializedJSON: this.getSerializedJSON.bind(this), |
|
|
|
executeCommands: this.executeCommands.bind(this), |
|
isExecutingCommands: this.isExecutingCommands.bind(this), |
|
|
|
registerOnEvent: this.registerOnEvent.bind(this), |
|
unregisterOnEvent: this.unregisterOnEvent.bind(this), |
|
raiseEvent: this.handleEvents.bind(this), |
|
enableEvent: this.enableEvent.bind(this), |
|
disableEvent: this.disableEvent.bind(this), |
|
|
|
eventCoordinates: this.eventCoordinates.bind(this), |
|
eventCoordinatesXY: this.eventCoordinatesXY.bind(this), |
|
|
|
getModelMetaData: this.getModelMetaData.bind(this) |
|
}; |
|
|
|
// return it. |
|
return this._pluginFacade; |
|
}, |
|
|
|
isExecutingCommands: function(){ |
|
return !!this.commandExecuting; |
|
}, |
|
|
|
/** |
|
* Implementes the command pattern |
|
* (The real usage of the command pattern |
|
* is implemented and shown in the Plugins/undo.js) |
|
* |
|
* @param <Oryx.Core.Command>[] Array of commands |
|
*/ |
|
executeCommands: function(commands){ |
|
|
|
if (!this.commandStack){ |
|
this.commandStack = []; |
|
} |
|
if (!this.commandStackExecuted){ |
|
this.commandStackExecuted = []; |
|
} |
|
|
|
|
|
this.commandStack = [].concat(this.commandStack) |
|
.concat(commands); |
|
|
|
// Check if already executes |
|
if (this.commandExecuting){ return; } |
|
|
|
// Start execution |
|
this.commandExecuting = true; |
|
|
|
// Iterate over all commands |
|
while(this.commandStack.length > 0){ |
|
var command = this.commandStack.shift(); |
|
// and execute it |
|
command.execute(); |
|
this.commandStackExecuted.push(command); |
|
} |
|
|
|
// Raise event for executing commands |
|
this.handleEvents({ |
|
type : ORYX.CONFIG.EVENT_EXECUTE_COMMANDS, |
|
commands : this.commandStackExecuted |
|
}); |
|
|
|
// Remove temporary vars |
|
delete this.commandStack; |
|
delete this.commandStackExecuted; |
|
delete this.commandExecuting; |
|
|
|
|
|
this.updateSelection(); |
|
|
|
}, |
|
|
|
/** |
|
* Returns JSON of underlying canvas (calls ORYX.Canvas#toJSON()). |
|
* @return {Object} Returns JSON representation as JSON object. |
|
*/ |
|
getJSON: function(){ |
|
delete Array.prototype.toJSON; |
|
var canvasJSON = this.getCanvas().toJSON(); |
|
canvasJSON.ssextensions = this.getStencilSets().values()[0].extensions().keys().findAll(function(sse){ return !sse.endsWith('/meta#') }); |
|
return canvasJSON; |
|
}, |
|
|
|
/** |
|
* Serializes a call to toJSON(). |
|
* @return {String} Returns JSON representation as string. |
|
*/ |
|
getSerializedJSON: function(){ |
|
return JSON.stringify(this.getJSON()); |
|
}, |
|
|
|
/** |
|
* Imports shapes in JSON as expected by {@link ORYX.Editor#loadSerialized} |
|
* @param {Object|String} jsonObject The (serialized) json object to be imported |
|
* @param {boolean } [noSelectionAfterImport=false] Set to true if no shapes should be selected after import |
|
* @throws {SyntaxError} If the serialized json object contains syntax errors |
|
*/ |
|
importJSON: function(jsonObject, noSelectionAfterImport) { |
|
|
|
try { |
|
jsonObject = this.renewResourceIds(jsonObject); |
|
} catch(error){ |
|
throw error; |
|
} |
|
//check, if the imported json model can be loaded in this editor |
|
// (stencil set has to fit) |
|
if(jsonObject.stencilset.namespace && jsonObject.stencilset.namespace !== this.getCanvas().getStencil().stencilSet().namespace()) { |
|
alert(String.format(ORYX.I18N.JSONImport.wrongSS, jsonObject.stencilset.namespace, this.getCanvas().getStencil().stencilSet().namespace())); |
|
return null; |
|
} else { |
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(jsonObject, loadSerializedCB, noSelectionAfterImport, facade){ |
|
this.jsonObject = jsonObject; |
|
this.noSelection = noSelectionAfterImport; |
|
this.facade = facade; |
|
this.shapes; |
|
this.connections = []; |
|
this.parents = new Hash(); |
|
this.selection = this.facade.getSelection(); |
|
this.loadSerialized = loadSerializedCB; |
|
}, |
|
execute: function(){ |
|
|
|
if (!this.shapes) { |
|
// Import the shapes out of the serialization |
|
this.shapes = this.loadSerialized( this.jsonObject ); |
|
|
|
//store all connections |
|
this.shapes.each(function(shape) { |
|
|
|
if (shape.getDockers) { |
|
var dockers = shape.getDockers(); |
|
if (dockers) { |
|
if (dockers.length > 0) { |
|
this.connections.push([dockers.first(), dockers.first().getDockedShape(), dockers.first().referencePoint]); |
|
} |
|
if (dockers.length > 1) { |
|
this.connections.push([dockers.last(), dockers.last().getDockedShape(), dockers.last().referencePoint]); |
|
} |
|
} |
|
} |
|
|
|
//store parents |
|
this.parents[shape.id] = shape.parent; |
|
}.bind(this)); |
|
} else { |
|
this.shapes.each(function(shape) { |
|
this.parents[shape.id].add(shape); |
|
}.bind(this)); |
|
|
|
this.connections.each(function(con) { |
|
con[0].setDockedShape(con[1]); |
|
con[0].setReferencePoint(con[2]); |
|
con[0].update(); |
|
}); |
|
} |
|
|
|
//this.parents.values().uniq().invoke("update"); |
|
this.facade.getCanvas().update(); |
|
|
|
if(!this.noSelection) |
|
this.facade.setSelection(this.shapes); |
|
else |
|
this.facade.updateSelection(); |
|
|
|
// call updateSize again, because during loadSerialized the edges' bounds |
|
// are not yet initialized properly |
|
this.facade.getCanvas().updateSize(); |
|
|
|
}, |
|
rollback: function(){ |
|
var selection = this.facade.getSelection(); |
|
|
|
this.shapes.each(function(shape) { |
|
selection = selection.without(shape); |
|
this.facade.deleteShape(shape); |
|
}.bind(this)); |
|
|
|
/*this.parents.values().uniq().each(function(parent) { |
|
if(!this.shapes.member(parent)) |
|
parent.update(); |
|
}.bind(this));*/ |
|
|
|
this.facade.getCanvas().update(); |
|
|
|
this.facade.setSelection(selection); |
|
} |
|
}) |
|
|
|
var command = new commandClass(jsonObject, |
|
this.loadSerialized.bind(this), |
|
noSelectionAfterImport, |
|
this._getPluginFacade()); |
|
|
|
this.executeCommands([command]); |
|
|
|
return command.shapes.clone(); |
|
} |
|
}, |
|
|
|
/** |
|
* This method renew all resource Ids and according references. |
|
* Warning: The implementation performs a substitution on the serialized object for |
|
* easier implementation. This results in a low performance which is acceptable if this |
|
* is only used when importing models. |
|
* @param {Object|String} jsonObject |
|
* @throws {SyntaxError} If the serialized json object contains syntax errors. |
|
* @return {Object} The jsonObject with renewed ids. |
|
* @private |
|
*/ |
|
renewResourceIds: function(jsonObject){ |
|
// For renewing resource ids, a serialized and object version is needed |
|
if(Object.prototype.toString.call(jsonObject) === "String"){ |
|
try { |
|
var serJsonObject = jsonObject; |
|
jsonObject = JSON.parse(jsonObject); |
|
} catch(error){ |
|
throw new SyntaxError(error.message); |
|
} |
|
} else { |
|
var serJsonObject = JSON.stringify(jsonObject); |
|
} |
|
|
|
// collect all resourceIds recursively |
|
var collectResourceIds = function(shapes){ |
|
if(!shapes) return []; |
|
|
|
return shapes.map(function(shape){ |
|
return collectResourceIds(shape.childShapes).concat(shape.resourceId); |
|
}).flatten(); |
|
} |
|
var resourceIds = collectResourceIds(jsonObject.childShapes); |
|
|
|
// Replace each resource id by a new one |
|
resourceIds.each(function(oldResourceId){ |
|
var newResourceId = ORYX.Editor.provideId(); |
|
serJsonObject = serJsonObject.replace(new RegExp(oldResourceId, 'g'), newResourceId); |
|
}); |
|
|
|
return JSON.parse(serJsonObject); |
|
}, |
|
|
|
/** |
|
* Loads serialized model to the oryx. |
|
* @example |
|
* editor.loadSerialized({ |
|
* resourceId: "mymodel1", |
|
* childShapes: [ |
|
* { |
|
* stencil:{ id:"Subprocess" }, |
|
* outgoing:[{resourceId: 'aShape'}], |
|
* target: {resourceId: 'aShape'}, |
|
* bounds:{ lowerRight:{ y:510, x:633 }, upperLeft:{ y:146, x:210 } }, |
|
* resourceId: "myshape1", |
|
* childShapes:[], |
|
* properties:{}, |
|
* } |
|
* ], |
|
* properties:{ |
|
* language: "English" |
|
* }, |
|
* stencilset:{ |
|
* url:"http://localhost:8080/oryx/stencilsets/bpmn1.1/bpmn1.1.json" |
|
* }, |
|
* stencil:{ |
|
* id:"BPMNDiagram" |
|
* } |
|
* }); |
|
* @param {Object} model Description of the model to load. |
|
* @param {Array} [model.ssextensions] List of stenctil set extensions. |
|
* @param {String} model.stencilset.url |
|
* @param {String} model.stencil.id |
|
* @param {Array} model.childShapes |
|
* @param {Array} [model.properties] |
|
* @param {String} model.resourceId |
|
* @return {ORYX.Core.Shape[]} List of created shapes |
|
* @methodOf ORYX.Editor.prototype |
|
*/ |
|
loadSerialized: function(model, requestMeta){ |
|
var canvas = this.getCanvas(); |
|
|
|
// Bugfix (cf. http://code.google.com/p/oryx-editor/issues/detail?id=240) |
|
// Deserialize the canvas' stencil set extensions properties first! |
|
this.loadSSExtensions(model.ssextensions); |
|
|
|
// Load Meta Data Extension if available |
|
// #Signavio |
|
if (requestMeta === true) { |
|
var metaDataExtension = this.getExtensionForMetaData(); |
|
if (metaDataExtension) { |
|
this.loadSSExtension(metaDataExtension); |
|
} |
|
} |
|
|
|
var shapes = this.getCanvas().addShapeObjects(model.childShapes, this.handleEvents.bind(this)); |
|
|
|
if(model.properties) { |
|
for(key in model.properties) { |
|
var value = model.properties[key]; |
|
var prop = this.getCanvas().getStencil().property("oryx-"+key); |
|
if (!(typeof value === "string") && (!prop || !prop.isList())) { |
|
value = JSON.stringify(value); |
|
} |
|
this.getCanvas().setProperty("oryx-" + key, value); |
|
} |
|
} |
|
|
|
|
|
this.getCanvas().updateSize(); |
|
|
|
// Force to update the selection |
|
this.selection = [null]; |
|
this.setSelection([]); |
|
|
|
return shapes; |
|
}, |
|
|
|
/** |
|
* Return the namespace of the extension which |
|
* provided all the self defined meta data |
|
* @return {String} Returns null if no extension is defined, otherwise the namespace |
|
* |
|
*/ |
|
getExtensionForMetaData: function(){ |
|
if (!this.ss_extensions_def||!(this.ss_extensions_def.extensions instanceof Array)){ |
|
return null; |
|
} |
|
|
|
var stencilsets = this.getStencilSets(); |
|
var extension = this.ss_extensions_def.extensions.find(function(ex){ |
|
return !!stencilsets[ex["extends"]] && ex.namespace.endsWith("/meta#"); |
|
}); |
|
|
|
return extension ? extension.namespace || null : null; |
|
}, |
|
|
|
/** |
|
* Calls ORYX.Editor.prototype.ss_extension_namespace for each element |
|
* @param {Array} ss_extension_namespaces An array of stencil set extension namespaces. |
|
*/ |
|
loadSSExtensions: function(ss_extension_namespaces){ |
|
if(!ss_extension_namespaces) return; |
|
|
|
ss_extension_namespaces.each(function(ss_extension_namespace){ |
|
this.loadSSExtension(ss_extension_namespace); |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Loads a stencil set extension. |
|
* The stencil set extensions definiton file must already |
|
* be loaded when the editor is initialized. |
|
*/ |
|
loadSSExtension: function(ss_extension_namespace) { |
|
|
|
if (this.ss_extensions_def) { |
|
var extension = this.ss_extensions_def.extensions.find(function(ex){ |
|
return (ex.namespace == ss_extension_namespace); |
|
}); |
|
|
|
if (!extension) { |
|
return; |
|
} |
|
|
|
var stencilset = this.getStencilSets()[extension["extends"]]; |
|
|
|
if (!stencilset) { |
|
return; |
|
} |
|
|
|
// Check if absolute or relative url |
|
if ((extension["definition"]||"").startsWith("/")){ |
|
stencilset.addExtension(extension["definition"]) |
|
} else { |
|
stencilset.addExtension(ORYX.CONFIG.SS_EXTENSIONS_FOLDER + extension["definition"]) |
|
} |
|
|
|
//stencilset.addExtension("/oryx/build/stencilsets/extensions/" + extension["definition"]) |
|
this.getRules().initializeRules(stencilset); |
|
|
|
this._getPluginFacade().raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED |
|
}); |
|
} |
|
|
|
}, |
|
|
|
disableEvent: function(eventType){ |
|
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) { |
|
this._keydownEnabled = false; |
|
} |
|
if(eventType == ORYX.CONFIG.EVENT_KEYUP) { |
|
this._keyupEnabled = false; |
|
} |
|
if(this.DOMEventListeners.keys().member(eventType)) { |
|
var value = this.DOMEventListeners.remove(eventType); |
|
this.DOMEventListeners['disable_' + eventType] = value; |
|
} |
|
}, |
|
|
|
enableEvent: function(eventType){ |
|
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) { |
|
this._keydownEnabled = true; |
|
} |
|
|
|
if(eventType == ORYX.CONFIG.EVENT_KEYUP) { |
|
this._keyupEnabled = true; |
|
} |
|
|
|
if(this.DOMEventListeners.keys().member("disable_" + eventType)) { |
|
var value = this.DOMEventListeners.remove("disable_" + eventType); |
|
this.DOMEventListeners[eventType] = value; |
|
} |
|
}, |
|
|
|
/** |
|
* Methods for the PluginFacade |
|
*/ |
|
registerOnEvent: function(eventType, callback) { |
|
if(!(this.DOMEventListeners.keys().member(eventType))) { |
|
this.DOMEventListeners[eventType] = []; |
|
} |
|
|
|
this.DOMEventListeners[eventType].push(callback); |
|
}, |
|
|
|
unregisterOnEvent: function(eventType, callback) { |
|
if(this.DOMEventListeners.keys().member(eventType)) { |
|
this.DOMEventListeners[eventType] = this.DOMEventListeners[eventType].without(callback); |
|
} else { |
|
// Event is not supported |
|
// TODO: Error Handling |
|
} |
|
}, |
|
|
|
getSelection: function() { |
|
return this.selection || []; |
|
}, |
|
|
|
getStencilSets: function() { |
|
return ORYX.Core.StencilSet.stencilSets(this.id); |
|
}, |
|
|
|
getRules: function() { |
|
return ORYX.Core.StencilSet.rules(this.id); |
|
}, |
|
|
|
loadStencilSet: function(source) { |
|
try { |
|
ORYX.Core.StencilSet.loadStencilSet(source, this.modelMetaData, this.id); |
|
this.handleEvents({type:ORYX.CONFIG.EVENT_STENCIL_SET_LOADED}); |
|
} catch (e) { |
|
ORYX.Log.warn("Requesting stencil set file failed. (" + e + ")"); |
|
} |
|
}, |
|
|
|
offer: function(pluginData) { |
|
if(!this.pluginsData.member(pluginData)){ |
|
this.pluginsData.push(pluginData); |
|
} |
|
}, |
|
|
|
/** |
|
* It creates an new event or adds the callback, if already existing, |
|
* for the key combination that the plugin passes in keyCodes attribute |
|
* of the offer method. |
|
* |
|
* The new key down event fits the schema: |
|
* key.event[.metactrl][.alt][.shift].'thekeyCode' |
|
*/ |
|
registerPluginsOnKeyEvents: function() { |
|
this.pluginsData.each(function(pluginData) { |
|
|
|
if(pluginData.keyCodes) { |
|
|
|
pluginData.keyCodes.each(function(keyComb) { |
|
var eventName = "key.event"; |
|
|
|
/* Include key action */ |
|
eventName += '.' + keyComb.keyAction; |
|
|
|
if(keyComb.metaKeys) { |
|
/* Register on ctrl or apple meta key as meta key */ |
|
if(keyComb.metaKeys. |
|
indexOf(ORYX.CONFIG.META_KEY_META_CTRL) > -1) { |
|
eventName += "." + ORYX.CONFIG.META_KEY_META_CTRL; |
|
} |
|
|
|
/* Register on alt key as meta key */ |
|
if(keyComb.metaKeys. |
|
indexOf(ORYX.CONFIG.META_KEY_ALT) > -1) { |
|
eventName += '.' + ORYX.CONFIG.META_KEY_ALT; |
|
} |
|
|
|
/* Register on shift key as meta key */ |
|
if(keyComb.metaKeys. |
|
indexOf(ORYX.CONFIG.META_KEY_SHIFT) > -1) { |
|
eventName += '.' + ORYX.CONFIG.META_KEY_SHIFT; |
|
} |
|
} |
|
|
|
/* Register on the actual key */ |
|
if(keyComb.keyCode) { |
|
eventName += '.' + keyComb.keyCode; |
|
} |
|
|
|
/* Register the event */ |
|
ORYX.Log.debug("Register Plugin on Key Event: %0", eventName); |
|
if (pluginData.toggle === true && pluginData.buttonInstance) { |
|
this.registerOnEvent(eventName, function(){ |
|
pluginData.buttonInstance.toggle(!pluginData.buttonInstance.pressed); // Toggle |
|
pluginData.functionality.call(pluginData, pluginData.buttonInstance, pluginData.buttonInstance.pressed); // Call function |
|
}); |
|
} else { |
|
this.registerOnEvent(eventName, pluginData.functionality) |
|
} |
|
|
|
}.bind(this)); |
|
} |
|
}.bind(this)); |
|
}, |
|
|
|
isEqual: function(a,b){ |
|
return a === b || (a.length === b.length && a.all(function(r){ return b.include(r) })) |
|
}, |
|
|
|
isDirty: function(a){ |
|
return a.any(function(shape){ return shape.isPropertyChanged() }) |
|
}, |
|
|
|
setSelection: function(elements, subSelectionElement, force) { |
|
|
|
if (!elements) { elements = []; } |
|
if (!(elements instanceof Array)) { elements = [elements]; } |
|
|
|
elements = elements.findAll(function(n){ return n && n instanceof ORYX.Core.Shape }); |
|
|
|
if (elements[0] instanceof ORYX.Core.Canvas) { |
|
elements = []; |
|
} |
|
|
|
if (!force && this.isEqual(this.selection, elements) && !this.isDirty(elements)){ |
|
return; |
|
} |
|
|
|
this.selection = elements; |
|
this._subSelection = subSelectionElement; |
|
|
|
this.handleEvents({type:ORYX.CONFIG.EVENT_SELECTION_CHANGED, elements:elements, subSelection: subSelectionElement, force: !!force}) |
|
}, |
|
|
|
updateSelection: function() { |
|
this.setSelection(this.selection, this._subSelection, true); |
|
/*var s = this.selection; |
|
this.setSelection(); |
|
this.setSelection(s);*/ |
|
}, |
|
|
|
getCanvas: function() { |
|
return this._canvas; |
|
}, |
|
|
|
|
|
/** |
|
* option = { |
|
* type: string, |
|
* position: {x:int, y:int}, |
|
* connectingType: uiObj-Class |
|
* connectedShape: uiObj |
|
* draggin: bool |
|
* namespace: url |
|
* parent: ORYX.Core.AbstractShape |
|
* template: a template shape that the newly created inherits properties from. |
|
* } |
|
*/ |
|
createShape: function(option) { |
|
|
|
if(option && option.serialize && option.serialize instanceof Array){ |
|
|
|
var type = option.serialize.find(function(obj){return (obj.prefix+"-"+obj.name) == "oryx-type"}); |
|
var stencil = ORYX.Core.StencilSet.stencil(type.value); |
|
|
|
if(stencil.type() == 'node'){ |
|
var newShapeObject = new ORYX.Core.Node({'eventHandlerCallback':this.handleEvents.bind(this)}, stencil, this._getPluginFacade()); |
|
} else { |
|
var newShapeObject = new ORYX.Core.Edge({'eventHandlerCallback':this.handleEvents.bind(this)}, stencil, this._getPluginFacade()); |
|
} |
|
|
|
this.getCanvas().add(newShapeObject); |
|
newShapeObject.deserialize(option.serialize); |
|
|
|
return newShapeObject; |
|
} |
|
|
|
// If there is no argument, throw an exception |
|
if(!option || !option.type || !option.namespace) { throw "To create a new shape you have to give an argument with type and namespace";} |
|
|
|
var canvas = this.getCanvas(); |
|
var newShapeObject; |
|
|
|
// Get the shape type |
|
var shapetype = option.type; |
|
|
|
// Get the stencil set |
|
var sset = ORYX.Core.StencilSet.stencilSet(option.namespace); |
|
// Create an New Shape, dependents on an Edge or a Node |
|
if(sset.stencil(shapetype).type() == "node") { |
|
newShapeObject = new ORYX.Core.Node({'eventHandlerCallback':this.handleEvents.bind(this)}, sset.stencil(shapetype), this._getPluginFacade()) |
|
} else { |
|
newShapeObject = new ORYX.Core.Edge({'eventHandlerCallback':this.handleEvents.bind(this)}, sset.stencil(shapetype), this._getPluginFacade()) |
|
} |
|
|
|
// when there is a template, inherit the properties. |
|
if(option.template) { |
|
|
|
newShapeObject._jsonStencil.properties = option.template._jsonStencil.properties; |
|
newShapeObject.postProcessProperties(); |
|
} |
|
|
|
// Add to the canvas |
|
if(option.parent && newShapeObject instanceof ORYX.Core.Node) { |
|
option.parent.add(newShapeObject); |
|
} else { |
|
canvas.add(newShapeObject); |
|
} |
|
|
|
|
|
// Set the position |
|
var point = option.position ? option.position : {x:100, y:200}; |
|
|
|
|
|
var con; |
|
// If there is create a shape and in the argument there is given an ConnectingType and is instance of an edge |
|
if(option.connectingType && option.connectedShape && !(newShapeObject instanceof ORYX.Core.Edge)) { |
|
|
|
// there will be create a new Edge |
|
con = new ORYX.Core.Edge({'eventHandlerCallback':this.handleEvents.bind(this)}, sset.stencil(option.connectingType)); |
|
|
|
// And both endings dockers will be referenced to the both shapes |
|
con.dockers.first().setDockedShape(option.connectedShape); |
|
|
|
var magnet = option.connectedShape.getDefaultMagnet() |
|
var cPoint = magnet ? magnet.bounds.center() : option.connectedShape.bounds.midPoint(); |
|
con.dockers.first().setReferencePoint( cPoint ); |
|
con.dockers.last().setDockedShape(newShapeObject); |
|
con.dockers.last().setReferencePoint(newShapeObject.getDefaultMagnet().bounds.center()); |
|
|
|
// The Edge will be added to the canvas and be updated |
|
canvas.add(con); |
|
//con.update(); |
|
|
|
} |
|
|
|
// Move the new Shape to the position |
|
if(newShapeObject instanceof ORYX.Core.Edge && option.connectedShape) { |
|
|
|
newShapeObject.dockers.first().setDockedShape(option.connectedShape); |
|
|
|
if( option.connectedShape instanceof ORYX.Core.Node ){ |
|
newShapeObject.dockers.first().setReferencePoint(option.connectedShape.getDefaultMagnet().bounds.center()); |
|
newShapeObject.dockers.last().bounds.centerMoveTo(point); |
|
} else { |
|
newShapeObject.dockers.first().setReferencePoint(option.connectedShape.bounds.midPoint()); |
|
} |
|
|
|
var start = newShapeObject.dockers.first(); |
|
var end = newShapeObject.dockers.last(); |
|
|
|
if(start.getDockedShape() && end.getDockedShape()) { |
|
var startPoint = start.getAbsoluteReferencePoint(); |
|
var endPoint = end.getAbsoluteReferencePoint(); |
|
|
|
var docker = newShapeObject.createDocker(); |
|
docker.bounds.centerMoveTo({ |
|
x: startPoint.x + (endPont.x - startPoint.x) / 2, |
|
y: startPoint.y + (endPont.y - startPoint.y) / 2 |
|
}); |
|
} |
|
|
|
} else { |
|
|
|
var b = newShapeObject.bounds |
|
if( newShapeObject instanceof ORYX.Core.Node && newShapeObject.dockers.length == 1){ |
|
b = newShapeObject.dockers.first().bounds |
|
} |
|
|
|
b.centerMoveTo(point); |
|
|
|
var upL = b.upperLeft(); |
|
b.moveBy( -Math.min(upL.x, 0) , -Math.min(upL.y, 0) ) |
|
|
|
var lwR = b.lowerRight(); |
|
b.moveBy( -Math.max(lwR.x-canvas.bounds.width(), 0) , -Math.max(lwR.y-canvas.bounds.height(), 0) ) |
|
|
|
} |
|
|
|
// Update the shape |
|
if (newShapeObject instanceof ORYX.Core.Edge) { |
|
newShapeObject._update(false); |
|
} |
|
|
|
// And refresh the selection |
|
if(!(newShapeObject instanceof ORYX.Core.Edge)&&!(option.dontUpdateSelection)) { |
|
this.setSelection([newShapeObject]); |
|
} |
|
|
|
if(con && con.alignDockers) { |
|
//con.alignDockers(); |
|
} |
|
if(newShapeObject.alignDockers) { |
|
newShapeObject.alignDockers(); |
|
} |
|
|
|
return newShapeObject; |
|
}, |
|
|
|
deleteShape: function(shape) { |
|
|
|
if (!shape || !shape.parent){ return } |
|
|
|
//remove shape from parent |
|
// this also removes it from DOM |
|
shape.parent.remove(shape); |
|
|
|
//delete references to outgoing edges |
|
shape.getOutgoingShapes().each(function(os) { |
|
var docker = os.getDockers().first(); |
|
if(docker && docker.getDockedShape() == shape) { |
|
docker.setDockedShape(undefined); |
|
} |
|
}); |
|
|
|
//delete references to incoming edges |
|
shape.getIncomingShapes().each(function(is) { |
|
var docker = is.getDockers().last(); |
|
if(docker && docker.getDockedShape() == shape) { |
|
docker.setDockedShape(undefined); |
|
} |
|
}); |
|
|
|
//delete references of the shape's dockers |
|
shape.getDockers().each(function(docker) { |
|
docker.setDockedShape(undefined); |
|
}); |
|
}, |
|
|
|
/** |
|
* Returns an object with meta data about the model. |
|
* Like name, description, ... |
|
* |
|
* Empty object with the current backend. |
|
* |
|
* @return {Object} Meta data about the model |
|
*/ |
|
getModelMetaData: function() { |
|
return this.modelMetaData; |
|
}, |
|
|
|
/* Event-Handler Methods */ |
|
|
|
/** |
|
* Helper method to execute an event immediately. The event is not |
|
* scheduled in the _eventsQueue. Needed to handle Layout-Callbacks. |
|
*/ |
|
_executeEventImmediately: function(eventObj) { |
|
if(this.DOMEventListeners.keys().member(eventObj.event.type)) { |
|
this.DOMEventListeners[eventObj.event.type].each((function(value) { |
|
value(eventObj.event, eventObj.arg); |
|
}).bind(this)); |
|
} |
|
}, |
|
|
|
_executeEvents: function() { |
|
this._queueRunning = true; |
|
while(this._eventsQueue.length > 0) { |
|
var val = this._eventsQueue.shift(); |
|
this._executeEventImmediately(val); |
|
} |
|
this._queueRunning = false; |
|
}, |
|
|
|
/** |
|
* Leitet die Events an die Editor-Spezifischen Event-Methoden weiter |
|
* @param {Object} event Event , welches gefeuert wurde |
|
* @param {Object} uiObj Target-UiObj |
|
*/ |
|
handleEvents: function(event, uiObj) { |
|
|
|
ORYX.Log.trace("Dispatching event type %0 on %1", event.type, uiObj); |
|
|
|
switch(event.type) { |
|
case ORYX.CONFIG.EVENT_MOUSEDOWN: |
|
this._handleMouseDown(event, uiObj); |
|
break; |
|
case ORYX.CONFIG.EVENT_MOUSEMOVE: |
|
this._handleMouseMove(event, uiObj); |
|
break; |
|
case ORYX.CONFIG.EVENT_MOUSEUP: |
|
this._handleMouseUp(event, uiObj); |
|
break; |
|
case ORYX.CONFIG.EVENT_MOUSEOVER: |
|
this._handleMouseHover(event, uiObj); |
|
break; |
|
case ORYX.CONFIG.EVENT_MOUSEOUT: |
|
this._handleMouseOut(event, uiObj); |
|
break; |
|
} |
|
/* Force execution if necessary. Used while handle Layout-Callbacks. */ |
|
if(event.forceExecution) { |
|
this._executeEventImmediately({event: event, arg: uiObj}); |
|
} else { |
|
this._eventsQueue.push({event: event, arg: uiObj}); |
|
} |
|
|
|
if(!this._queueRunning) { |
|
this._executeEvents(); |
|
} |
|
|
|
// TODO: Make this return whether no listener returned false. |
|
// So that, when one considers bubbling undesireable, it won't happen. |
|
return false; |
|
}, |
|
|
|
isValidEvent: function(e){ |
|
try { |
|
var isInput = ["INPUT", "TEXTAREA"].include(e.target.tagName.toUpperCase()); |
|
var gridHasFocus = e.target.className.include("x-grid3-focus") && !e.target.className.include("x-grid3-focus-canvas"); |
|
return !isInput && !gridHasFocus; |
|
} catch(e){ |
|
return false; |
|
} |
|
}, |
|
|
|
catchKeyUpEvents: function(event) { |
|
if(!this._keyupEnabled) { |
|
return; |
|
} |
|
/* assure we have the current event. */ |
|
if (!event) |
|
event = window.event; |
|
|
|
// Checks if the event comes from some input field |
|
if (!this.isValidEvent(event)){ |
|
return; |
|
} |
|
|
|
/* Create key up event type */ |
|
var keyUpEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_UP); |
|
|
|
ORYX.Log.debug("Key Event to handle: %0", keyUpEvent); |
|
|
|
/* forward to dispatching. */ |
|
this.handleEvents({type: keyUpEvent, event:event}); |
|
}, |
|
|
|
/** |
|
* Catches all key down events and forward the appropriated event to |
|
* dispatching concerning to the pressed keys. |
|
* |
|
* @param {Event} |
|
* The key down event to handle |
|
*/ |
|
catchKeyDownEvents: function(event) { |
|
if(!this._keydownEnabled) { |
|
return; |
|
} |
|
/* Assure we have the current event. */ |
|
if (!event) |
|
event = window.event; |
|
|
|
/* Fixed in FF3 */ |
|
// This is a mac-specific fix. The mozilla event object has no knowledge |
|
// of meta key modifier on osx, however, it is needed for certain |
|
// shortcuts. This fix adds the metaKey field to the event object, so |
|
// that all listeners that registered per Oryx plugin facade profit from |
|
// this. The original bug is filed in |
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=418334 |
|
//if (this.__currentKey == ORYX.CONFIG.KEY_CODE_META) { |
|
// event.appleMetaKey = true; |
|
//} |
|
//this.__currentKey = pressedKey; |
|
|
|
// Checks if the event comes from some input field |
|
if (!this.isValidEvent(event)){ |
|
return; |
|
} |
|
|
|
/* Create key up event type */ |
|
var keyDownEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_DOWN); |
|
|
|
ORYX.Log.debug("Key Event to handle: %0", keyDownEvent); |
|
|
|
/* Forward to dispatching. */ |
|
this.handleEvents({type: keyDownEvent,event: event}); |
|
}, |
|
|
|
/** |
|
* Creates the event type name concerning to the pressed keys. |
|
* |
|
* @param {Event} keyDownEvent |
|
* The source keyDownEvent to build up the event name |
|
*/ |
|
createKeyCombEvent: function(keyEvent, keyAction) { |
|
|
|
/* Get the currently pressed key code. */ |
|
var pressedKey = keyEvent.which || keyEvent.keyCode; |
|
//this.__currentKey = pressedKey; |
|
|
|
/* Event name */ |
|
var eventName = "key.event"; |
|
|
|
/* Key action */ |
|
if(keyAction) { |
|
eventName += "." + keyAction; |
|
} |
|
|
|
/* Ctrl or apple meta key is pressed */ |
|
if(keyEvent.ctrlKey || keyEvent.metaKey) { |
|
eventName += "." + ORYX.CONFIG.META_KEY_META_CTRL; |
|
} |
|
|
|
/* Alt key is pressed */ |
|
if(keyEvent.altKey) { |
|
eventName += "." + ORYX.CONFIG.META_KEY_ALT; |
|
} |
|
|
|
/* Alt key is pressed */ |
|
if(keyEvent.shiftKey) { |
|
eventName += "." + ORYX.CONFIG.META_KEY_SHIFT; |
|
} |
|
|
|
/* Return the composed event name */ |
|
return eventName + "." + pressedKey; |
|
}, |
|
|
|
_handleMouseDown: function(event, uiObj) { |
|
|
|
// get canvas. |
|
var canvas = this.getCanvas(); |
|
// Try to get the focus |
|
canvas.focus() |
|
|
|
// find the shape that is responsible for this element's id. |
|
var element = event.currentTarget; |
|
var elementController = uiObj; |
|
|
|
// gather information on selection. |
|
var currentIsSelectable = (elementController !== null) && |
|
(elementController !== undefined) && (elementController.isSelectable); |
|
var currentIsMovable = (elementController !== null) && |
|
(elementController !== undefined) && (elementController.isMovable); |
|
var modifierKeyPressed = event.shiftKey || event.ctrlKey; |
|
var noObjectsSelected = this.selection.length === 0; |
|
var currentIsSelected = this.selection.member(elementController); |
|
|
|
|
|
// Rule #1: When there is nothing selected, select the clicked object. |
|
if(currentIsSelectable && noObjectsSelected) { |
|
|
|
this.setSelection([elementController]); |
|
|
|
ORYX.Log.trace("Rule #1 applied for mouse down on %0", element.id); |
|
|
|
// Rule #3: When at least one element is selected, and there is no |
|
// control key pressed, and the clicked object is not selected, select |
|
// the clicked object. |
|
} else if(currentIsSelectable && !noObjectsSelected && |
|
!modifierKeyPressed && !currentIsSelected) { |
|
|
|
this.setSelection([elementController]); |
|
|
|
//var objectType = elementController.readAttributes(); |
|
//alert(objectType[0] + ": " + objectType[1]); |
|
|
|
ORYX.Log.trace("Rule #3 applied for mouse down on %0", element.id); |
|
|
|
// Rule #4: When the control key is pressed, and the current object is |
|
// not selected, add it to the selection. |
|
} else if(currentIsSelectable && modifierKeyPressed |
|
&& !currentIsSelected) { |
|
|
|
var newSelection = this.selection.clone(); |
|
newSelection.push(elementController) |
|
this.setSelection(newSelection) |
|
|
|
ORYX.Log.trace("Rule #4 applied for mouse down on %0", element.id); |
|
|
|
// Rule #6 |
|
} else if(currentIsSelectable && currentIsSelected && |
|
modifierKeyPressed) { |
|
|
|
var newSelection = this.selection.clone(); |
|
this.setSelection(newSelection.without(elementController)) |
|
|
|
ORYX.Log.trace("Rule #6 applied for mouse down on %0", elementController.id); |
|
|
|
// Rule #5: When there is at least one object selected and no control |
|
// key pressed, we're dragging. |
|
/*} else if(currentIsSelectable && !noObjectsSelected |
|
&& !modifierKeyPressed) { |
|
|
|
if(this.log.isTraceEnabled()) |
|
this.log.trace("Rule #5 applied for mouse down on "+element.id); |
|
*/ |
|
// Rule #2: When clicked on something that is neither |
|
// selectable nor movable, clear the selection, and return. |
|
} else if (!currentIsSelectable && !currentIsMovable) { |
|
|
|
this.setSelection([]); |
|
|
|
ORYX.Log.trace("Rule #2 applied for mouse down on %0", element.id); |
|
|
|
return; |
|
|
|
// Rule #7: When the current object is not selectable but movable, |
|
// it is probably a control. Leave the selection unchanged but set |
|
// the movedObject to the current one and enable Drag. Dockers will |
|
// be processed in the dragDocker plugin. |
|
} else if(!currentIsSelectable && currentIsMovable && !(elementController instanceof ORYX.Core.Controls.Docker)) { |
|
|
|
// TODO: If there is any moveable elements, do this in a plugin |
|
//ORYX.Core.UIEnableDrag(event, elementController); |
|
|
|
ORYX.Log.trace("Rule #7 applied for mouse down on %0", element.id); |
|
|
|
// Rule #8: When the element is selectable and is currently selected and no |
|
// modifier key is pressed |
|
} else if(currentIsSelectable && currentIsSelected && |
|
!modifierKeyPressed) { |
|
|
|
this._subSelection = this._subSelection != elementController ? elementController : undefined; |
|
|
|
this.setSelection(this.selection, this._subSelection); |
|
|
|
ORYX.Log.trace("Rule #8 applied for mouse down on %0", element.id); |
|
} |
|
|
|
|
|
// prevent event from bubbling, return. |
|
//Event.stop(event); |
|
return; |
|
}, |
|
|
|
_handleMouseMove: function(event, uiObj) { |
|
return; |
|
}, |
|
|
|
_handleMouseUp: function(event, uiObj) { |
|
// get canvas. |
|
var canvas = this.getCanvas(); |
|
|
|
// find the shape that is responsible for this elemement's id. |
|
var elementController = uiObj; |
|
|
|
//get event position |
|
var evPos = this.eventCoordinates(event); |
|
|
|
//Event.stop(event); |
|
}, |
|
|
|
_handleMouseHover: function(event, uiObj) { |
|
return; |
|
}, |
|
|
|
_handleMouseOut: function(event, uiObj) { |
|
return; |
|
}, |
|
|
|
/** |
|
* Calculates the event coordinates to SVG document coordinates. |
|
* @param {Event} event |
|
* @return {SVGPoint} The event coordinates in the SVG document |
|
*/ |
|
eventCoordinates: function(event) { |
|
|
|
var canvas = this.getCanvas(); |
|
|
|
var svgPoint = canvas.node.ownerSVGElement.createSVGPoint(); |
|
svgPoint.x = event.clientX; |
|
svgPoint.y = event.clientY; |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
svgPoint.x = svgPoint.x * additionalIEZoom; |
|
svgPoint.y = svgPoint.y * additionalIEZoom; |
|
} |
|
|
|
var matrix = canvas.node.getScreenCTM(); |
|
return svgPoint.matrixTransform(matrix.inverse()); |
|
}, |
|
|
|
eventCoordinatesXY: function(x, y) { |
|
|
|
var canvas = this.getCanvas(); |
|
|
|
var svgPoint = canvas.node.ownerSVGElement.createSVGPoint(); |
|
svgPoint.x = x; |
|
svgPoint.y = y; |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
svgPoint.x = svgPoint.x * additionalIEZoom; |
|
svgPoint.y = svgPoint.y * additionalIEZoom; |
|
} |
|
|
|
var matrix = canvas.node.getScreenCTM(); |
|
return svgPoint.matrixTransform(matrix.inverse()); |
|
} |
|
}; |
|
ORYX.Editor = Clazz.extend(ORYX.Editor); |
|
|
|
/** |
|
* Creates a new ORYX.Editor instance by fetching a model from given url and passing it to the constructur |
|
* @param {String} modelUrl The JSON URL of a model. |
|
* @param {Object} config Editor config passed to the constructur, merged with the response of the request to modelUrl |
|
*/ |
|
ORYX.Editor.createByUrl = function(modelUrl){ |
|
new Ajax.Request(modelUrl, { |
|
method: 'GET', |
|
onSuccess: function(transport) { |
|
var editorConfig = JSON.parse(transport.responseText); |
|
new ORYX.Editor(editorConfig); |
|
}.bind(this) |
|
}); |
|
} |
|
|
|
// TODO Implement namespace awareness on attribute level. |
|
/** |
|
* graft() function |
|
* Originally by Sean M. Burke from interglacial.com, altered for usage with |
|
* SVG and namespace (xmlns) support. Be sure you understand xmlns before |
|
* using this funtion, as it creates all grafted elements in the xmlns |
|
* provided by you and all element's attribures in default xmlns. If you |
|
* need to graft elements in a certain xmlns and wish to assign attributes |
|
* in both that and another xmlns, you will need to do stepwise grafting, |
|
* adding non-default attributes yourself or you'll have to enhance this |
|
* function. Latter, I would appreciate: martin???apfelfabrik.de |
|
* @param {Object} namespace The namespace in which |
|
* elements should be grafted. |
|
* @param {Object} parent The element that should contain the grafted |
|
* structure after the function returned. |
|
* @param {Object} t the crafting structure. |
|
* @param {Object} doc the document in which grafting is performed. |
|
*/ |
|
ORYX.Editor.graft = function(namespace, parent, t, doc) { |
|
|
|
doc = (doc || (parent && parent.ownerDocument) || document); |
|
var e; |
|
if(t === undefined) { |
|
throw "Can't graft an undefined value"; |
|
} else if(t.constructor == String) { |
|
e = doc.createTextNode( t ); |
|
} else { |
|
for(var i = 0; i < t.length; i++) { |
|
if( i === 0 && t[i].constructor == String ) { |
|
var snared; |
|
snared = t[i].match( /^([a-z][a-z0-9]*)\.([^\s\.]+)$/i ); |
|
if( snared ) { |
|
e = doc.createElementNS(namespace, snared[1] ); |
|
e.setAttributeNS(null, 'class', snared[2] ); |
|
continue; |
|
} |
|
snared = t[i].match( /^([a-z][a-z0-9]*)$/i ); |
|
if( snared ) { |
|
e = doc.createElementNS(namespace, snared[1] ); // but no class |
|
continue; |
|
} |
|
|
|
// Otherwise: |
|
e = doc.createElementNS(namespace, "span" ); |
|
e.setAttribute(null, "class", "namelessFromLOL" ); |
|
} |
|
|
|
if( t[i] === undefined ) { |
|
throw "Can't graft an undefined value in a list!"; |
|
} else if( t[i].constructor == String || t[i].constructor == Array ) { |
|
this.graft(namespace, e, t[i], doc ); |
|
} else if( t[i].constructor == Number ) { |
|
this.graft(namespace, e, t[i].toString(), doc ); |
|
} else if( t[i].constructor == Object ) { |
|
// hash's properties => element's attributes |
|
for(var k in t[i]) { e.setAttributeNS(null, k, t[i][k] ); } |
|
} else { |
|
|
|
} |
|
} |
|
} |
|
if(parent && parent.appendChild) { |
|
parent.appendChild( e ); |
|
} else { |
|
|
|
} |
|
return e; // return the topmost created node |
|
}; |
|
|
|
ORYX.Editor.provideId = function() { |
|
var res = [], hex = '0123456789ABCDEF'; |
|
|
|
for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random()*0x10); |
|
|
|
res[14] = 4; |
|
res[19] = (res[19] & 0x3) | 0x8; |
|
|
|
for (var i = 0; i < 36; i++) res[i] = hex[res[i]]; |
|
|
|
res[8] = res[13] = res[18] = res[23] = '-'; |
|
|
|
return "oryx_" + res.join(''); |
|
}; |
|
|
|
/** |
|
* When working with Ext, conditionally the window needs to be resized. To do |
|
* so, use this class method. Resize is deferred until 100ms, and all subsequent |
|
* resizeBugFix calls are ignored until the initially requested resize is |
|
* performed. |
|
*/ |
|
ORYX.Editor.resizeFix = function() { |
|
if (!ORYX.Editor._resizeFixTimeout) { |
|
ORYX.Editor._resizeFixTimeout = window.setTimeout(function() { |
|
window.resizeBy(1,1); |
|
window.resizeBy(-1,-1); |
|
ORYX.Editor._resizefixTimeout = null; |
|
}, 100); |
|
} |
|
}; |
|
|
|
ORYX.Editor.Cookie = { |
|
|
|
callbacks:[], |
|
|
|
onChange: function( callback, interval ){ |
|
|
|
this.callbacks.push(callback); |
|
this.start( interval ) |
|
|
|
}, |
|
|
|
start: function( interval ){ |
|
|
|
if( this.pe ){ |
|
return; |
|
} |
|
|
|
var currentString = document.cookie; |
|
|
|
this.pe = new PeriodicalExecuter( function(){ |
|
|
|
if( currentString != document.cookie ){ |
|
currentString = document.cookie; |
|
this.callbacks.each(function(callback){ callback(this.getParams()) }.bind(this)); |
|
} |
|
|
|
}.bind(this), ( interval || 10000 ) / 1000); |
|
}, |
|
|
|
stop: function(){ |
|
|
|
if( this.pe ){ |
|
this.pe.stop(); |
|
this.pe = null; |
|
} |
|
}, |
|
|
|
getParams: function(){ |
|
var res = {}; |
|
|
|
var p = document.cookie; |
|
p.split("; ").each(function(param){ res[param.split("=")[0]] = param.split("=")[1];}); |
|
|
|
return res; |
|
}, |
|
|
|
toString: function(){ |
|
return document.cookie; |
|
} |
|
}; |
|
|
|
/** |
|
* Workaround for SAFARI/Webkit, because |
|
* when trying to check SVGSVGElement of instanceof there is |
|
* raising an error |
|
* |
|
*/ |
|
ORYX.Editor.SVGClassElementsAreAvailable = true; |
|
ORYX.Editor.setMissingClasses = function() { |
|
|
|
try { |
|
SVGElement; |
|
} catch(e) { |
|
ORYX.Editor.SVGClassElementsAreAvailable = false; |
|
SVGSVGElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg').toString(); |
|
SVGGElement = document.createElementNS('http://www.w3.org/2000/svg', 'g').toString(); |
|
SVGPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path').toString(); |
|
SVGTextElement = document.createElementNS('http://www.w3.org/2000/svg', 'text').toString(); |
|
//SVGMarkerElement = document.createElementNS('http://www.w3.org/2000/svg', 'marker').toString(); |
|
SVGRectElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect').toString(); |
|
SVGImageElement = document.createElementNS('http://www.w3.org/2000/svg', 'image').toString(); |
|
SVGCircleElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle').toString(); |
|
SVGEllipseElement = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse').toString(); |
|
SVGLineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line').toString(); |
|
SVGPolylineElement = document.createElementNS('http://www.w3.org/2000/svg', 'polyline').toString(); |
|
SVGPolygonElement = document.createElementNS('http://www.w3.org/2000/svg', 'polygon').toString(); |
|
|
|
} |
|
|
|
} |
|
ORYX.Editor.checkClassType = function( classInst, classType ) { |
|
|
|
if( ORYX.Editor.SVGClassElementsAreAvailable ){ |
|
return classInst instanceof classType |
|
} else { |
|
return classInst == classType |
|
} |
|
}; |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
|
|
new function(){ |
|
|
|
ORYX.Core.UIEnableDrag = function(event, uiObj, option) { |
|
|
|
this.uiObj = uiObj; |
|
var upL = uiObj.bounds.upperLeft(); |
|
|
|
var a = uiObj.node.getScreenCTM(); |
|
this.faktorXY= {x: a.a, y: a.d}; |
|
|
|
this.scrollNode = uiObj.node.ownerSVGElement.parentNode.parentNode; |
|
|
|
this.offSetPosition = { |
|
x: Event.pointerX(event) - (upL.x * this.faktorXY.x), |
|
y: Event.pointerY(event) - (upL.y * this.faktorXY.y)}; |
|
|
|
this.offsetScroll = {x:this.scrollNode.scrollLeft,y:this.scrollNode.scrollTop}; |
|
|
|
this.dragCallback = ORYX.Core.UIDragCallback.bind(this); |
|
this.disableCallback = ORYX.Core.UIDisableDrag.bind(this); |
|
|
|
this.movedCallback = option ? option.movedCallback : undefined; |
|
this.upCallback = option ? option.upCallback : undefined; |
|
|
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true); |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback , false); |
|
|
|
}; |
|
|
|
ORYX.Core.UIDragCallback = function(event) { |
|
|
|
var position = { |
|
x: Event.pointerX(event) - this.offSetPosition.x, |
|
y: Event.pointerY(event) - this.offSetPosition.y} |
|
|
|
position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft; |
|
position.y -= this.offsetScroll.y - this.scrollNode.scrollTop; |
|
|
|
position.x /= this.faktorXY.x; |
|
position.y /= this.faktorXY.y; |
|
|
|
this.uiObj.bounds.moveTo(position); |
|
//this.uiObj.update(); |
|
|
|
if(this.movedCallback) |
|
this.movedCallback(event); |
|
|
|
//Event.stop(event); |
|
|
|
}; |
|
|
|
ORYX.Core.UIDisableDrag = function(event) { |
|
document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.dragCallback, false); |
|
document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.disableCallback, true); |
|
|
|
if(this.upCallback) |
|
this.upCallback(event); |
|
|
|
this.upCallback = undefined; |
|
this.movedCallback = undefined; |
|
|
|
Event.stop(event); |
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
* Implements a command to move docker by an offset. |
|
* |
|
* @class ORYX.Core.MoveDockersCommand |
|
* @param {Object} object An object with the docker id as key and docker and offset as object value |
|
* |
|
*/ |
|
ORYX.Core.MoveDockersCommand = ORYX.Core.Command.extend({ |
|
construct: function(dockers){ |
|
this.dockers = $H(dockers); |
|
this.edges = $H({}); |
|
}, |
|
execute: function(){ |
|
if (this.changes) { |
|
this.executeAgain(); |
|
return; |
|
} else { |
|
this.changes = $H({}); |
|
} |
|
|
|
this.dockers.values().each(function(docker){ |
|
var edge = docker.docker.parent; |
|
if (!edge){ return } |
|
|
|
if (!this.changes[edge.getId()]) { |
|
this.changes[edge.getId()] = { |
|
edge : edge, |
|
oldDockerPositions : edge.dockers.map(function(r){ return r.bounds.center() }) |
|
} |
|
} |
|
docker.docker.bounds.moveBy(docker.offset); |
|
this.edges[edge.getId()] = edge; |
|
docker.docker.update(); |
|
}.bind(this)); |
|
this.edges.each(function(edge){ |
|
this.updateEdge(edge.value); |
|
if (this.changes[edge.value.getId()]) |
|
this.changes[edge.value.getId()].dockerPositions = edge.value.dockers.map(function(r){ return r.bounds.center() }) |
|
}.bind(this)); |
|
}, |
|
updateEdge: function(edge){ |
|
edge._update(true); |
|
[edge.getOutgoingShapes(), edge.getIncomingShapes()].flatten().invoke("_update", [true]) |
|
}, |
|
executeAgain: function(){ |
|
this.changes.values().each(function(change){ |
|
// Reset the dockers |
|
this.removeAllDocker(change.edge); |
|
change.dockerPositions.each(function(pos, i){ |
|
if (i==0||i==change.dockerPositions.length-1){ return } |
|
var docker = change.edge.createDocker(undefined, pos); |
|
docker.bounds.centerMoveTo(pos); |
|
docker.update(); |
|
}.bind(this)); |
|
this.updateEdge(change.edge); |
|
}.bind(this)); |
|
}, |
|
rollback: function(){ |
|
this.changes.values().each(function(change){ |
|
// Reset the dockers |
|
this.removeAllDocker(change.edge); |
|
change.oldDockerPositions.each(function(pos, i){ |
|
if (i==0||i==change.oldDockerPositions.length-1){ return } |
|
var docker = change.edge.createDocker(undefined, pos); |
|
docker.bounds.centerMoveTo(pos); |
|
docker.update(); |
|
}.bind(this)); |
|
this.updateEdge(change.edge); |
|
}.bind(this)); |
|
}, |
|
removeAllDocker: function(edge){ |
|
edge.dockers.slice(1, edge.dockers.length-1).each(function(docker){ |
|
edge.removeDocker(docker); |
|
}) |
|
} |
|
}); |
|
|
|
}(); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
|
|
/** |
|
* @classDescription Base class for Shapes. |
|
* @extends ORYX.Core.AbstractShape |
|
*/ |
|
ORYX.Core.Shape = { |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function(options, stencil, facade) { |
|
// call base class constructor |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.facade = facade; |
|
this.dockers = []; |
|
this.magnets = []; |
|
|
|
this._defaultMagnet; |
|
|
|
this.incoming = []; |
|
this.outgoing = []; |
|
|
|
this.nodes = []; |
|
|
|
this._dockerChangedCallback = this._dockerChanged.bind(this); |
|
|
|
//Hash map for all labels. Labels are not treated as children of shapes. |
|
this._labels = new Hash(); |
|
|
|
// create SVG node |
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", |
|
null, |
|
['g', {id:"svg-" + this.resourceId}, |
|
['g', {"class": "stencils"}, |
|
['g', {"class": "me"}], |
|
['g', {"class": "children", style:"overflow:hidden"}], |
|
['g', {"class": "edge"}] |
|
], |
|
['g', {"class": "controls"}, |
|
['g', {"class": "dockers"}], |
|
['g', {"class": "magnets"}] |
|
] |
|
]); |
|
}, |
|
|
|
/** |
|
* If changed flag is set, refresh method is called. |
|
*/ |
|
update: function() { |
|
//if(this.isChanged) { |
|
//this.layout(); |
|
//} |
|
}, |
|
|
|
/** |
|
* !!!Not called from any sub class!!! |
|
*/ |
|
_update: function() { |
|
|
|
}, |
|
|
|
/** |
|
* Calls the super class refresh method |
|
* and updates the svg elements that are referenced by a property. |
|
*/ |
|
refresh: function() { |
|
//call base class refresh method |
|
arguments.callee.$.refresh.apply(this, arguments); |
|
|
|
if(this.node.ownerDocument) { |
|
//adjust SVG to properties' values |
|
var me = this; |
|
this.propertiesChanged.each((function(propChanged) { |
|
if(propChanged.value) { |
|
var prop = this.properties[propChanged.key]; |
|
var property = this.getStencil().property(propChanged.key); |
|
if (property != undefined) { |
|
this.propertiesChanged[propChanged.key] = false; |
|
|
|
//handle choice properties |
|
if(property.type() == ORYX.CONFIG.TYPE_CHOICE) { |
|
//iterate all references to SVG elements |
|
property.refToView().each((function(ref) { |
|
//if property is referencing a label, update the label |
|
if(ref !== "") { |
|
var label = this._labels[this.id + ref]; |
|
if (label && property.item(prop)) { |
|
label.text(property.item(prop).title()); |
|
} |
|
} |
|
}).bind(this)); |
|
|
|
//if the choice's items are referencing SVG elements |
|
// show the selected and hide all other referenced SVG |
|
// elements |
|
var refreshedSvgElements = new Hash(); |
|
property.items().each((function(item) { |
|
item.refToView().each((function(itemRef) { |
|
if(itemRef == "") { return; } |
|
|
|
var svgElem = this.node.ownerDocument.getElementById(this.id + itemRef); |
|
|
|
if(!svgElem) { return; } |
|
|
|
|
|
/* Do not refresh the same svg element multiple times */ |
|
if(!refreshedSvgElements[svgElem.id] || prop == item.value()) { |
|
svgElem.setAttributeNS(null, 'display', ((prop == item.value()) ? 'inherit' : 'none')); |
|
refreshedSvgElements[svgElem.id] = svgElem; |
|
} |
|
|
|
// Reload the href if there is an image-tag |
|
if(ORYX.Editor.checkClassType(svgElem, SVGImageElement)) { |
|
svgElem.setAttributeNS('http://www.w3.org/1999/xlink', 'href', svgElem.getAttributeNS('http://www.w3.org/1999/xlink', 'href')); |
|
} |
|
}).bind(this)); |
|
}).bind(this)); |
|
|
|
} else { //handle properties that are not of type choice |
|
//iterate all references to SVG elements |
|
property.refToView().each((function(ref) { |
|
//if the property does not reference an SVG element, |
|
// do nothing |
|
|
|
if(ref === "") { return; } |
|
|
|
var refId = this.id + ref; |
|
|
|
if (property.type() === ORYX.CONFIG.TYPE_KISBPM_MULTIINSTANCE) |
|
{ |
|
if (ref === "multiinstance") { |
|
|
|
var svgElemParallel = this.node.ownerDocument.getElementById(this.id + 'parallel'); |
|
if(svgElemParallel) |
|
{ |
|
if (prop === 'Parallel') |
|
{ |
|
svgElemParallel.setAttributeNS(null, 'display', 'inherit'); |
|
} |
|
else |
|
{ |
|
svgElemParallel.setAttributeNS(null, 'display', 'none'); |
|
} |
|
} |
|
|
|
var svgElemSequential = this.node.ownerDocument.getElementById(this.id + 'sequential'); |
|
|
|
if(svgElemSequential) |
|
{ |
|
if (prop === 'Sequential') |
|
{ |
|
svgElemSequential.setAttributeNS(null, 'display', 'inherit'); |
|
} |
|
else |
|
{ |
|
svgElemSequential.setAttributeNS(null, 'display', 'none'); |
|
} |
|
} |
|
} |
|
return; |
|
|
|
} |
|
else if (property.type() === "cancelactivity") |
|
{ |
|
var svgElemFrame = this.node.ownerDocument.getElementById(this.id + 'frame'); |
|
var svgElemFrame2 = this.node.ownerDocument.getElementById(this.id + 'frame2'); |
|
|
|
if (prop === 'true') |
|
{ |
|
svgElemFrame.setAttributeNS(null, 'display', 'inherit'); |
|
svgElemFrame2.setAttributeNS(null, 'display', 'inherit'); |
|
} |
|
else |
|
{ |
|
svgElemFrame.setAttributeNS(null, 'display', 'none'); |
|
svgElemFrame2.setAttributeNS(null, 'display', 'none'); |
|
} |
|
} |
|
|
|
//get the SVG element |
|
var svgElem = this.node.ownerDocument.getElementById(refId); |
|
|
|
//if the SVG element can not be found |
|
if(!svgElem || !(svgElem.ownerSVGElement)) { |
|
//if the referenced SVG element is a SVGAElement, it cannot |
|
// be found with getElementById (Firefox bug). |
|
// this is a work around |
|
if(property.type() === ORYX.CONFIG.TYPE_URL || property.type() === ORYX.CONFIG.TYPE_DIAGRAM_LINK) { |
|
var svgElems = this.node.ownerDocument.getElementsByTagNameNS('http://www.w3.org/2000/svg', 'a'); |
|
|
|
svgElem = $A(svgElems).find(function(elem) { |
|
return elem.getAttributeNS(null, 'id') === refId; |
|
}); |
|
|
|
if(!svgElem) { return; } |
|
} else { |
|
//this.propertiesChanged[propChanged.key] = true; |
|
return; |
|
} |
|
} |
|
|
|
if (property.complexAttributeToView()) { |
|
var label = this._labels[refId]; |
|
if (label) { |
|
try { |
|
propJson = prop.evalJSON(); |
|
var value = propJson[property.complexAttributeToView()] |
|
label.text(value ? value : prop); |
|
} catch (e) { |
|
label.text(prop); |
|
} |
|
} |
|
|
|
} else { |
|
switch (property.type()) { |
|
case ORYX.CONFIG.TYPE_BOOLEAN: |
|
if (typeof prop == "string") |
|
prop = prop === "true" |
|
|
|
svgElem.setAttributeNS(null, 'display', (!(prop === property.inverseBoolean())) ? 'inherit' : 'none'); |
|
|
|
break; |
|
case ORYX.CONFIG.TYPE_COLOR: |
|
if(property.fill()) { |
|
if (svgElem.tagName.toLowerCase() === "stop"){ |
|
if (prop){ |
|
|
|
if (property.lightness() && property.lightness() !== 1){ |
|
prop = ORYX.Utils.adjustLightness(prop, property.lightness()); |
|
} |
|
|
|
svgElem.setAttributeNS(null, "stop-color", prop); |
|
|
|
// Adjust stop color of the others |
|
if (svgElem.parentNode.tagName.toLowerCase() === "radialgradient"){ |
|
ORYX.Utils.adjustGradient(svgElem.parentNode, svgElem); |
|
} |
|
} |
|
|
|
// If there is no value, set opaque |
|
if (svgElem.parentNode.tagName.toLowerCase() === "radialgradient"){ |
|
$A(svgElem.parentNode.getElementsByTagName('stop')).each(function(stop){ |
|
stop.setAttributeNS(null, "stop-opacity", prop ? stop.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, 'default-stop-opacity') || 1 : 0); |
|
}.bind(this)) |
|
} |
|
} else { |
|
svgElem.setAttributeNS(null, 'fill', prop); |
|
} |
|
} |
|
if(property.stroke()) { |
|
svgElem.setAttributeNS(null, 'stroke', prop); |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_STRING: |
|
var label = this._labels[refId]; |
|
if (label) { |
|
label.text(prop); |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_EXPRESSION: |
|
var label = this._labels[refId]; |
|
if (label) { |
|
label.text(prop); |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_DATASOURCE: |
|
var label = this._labels[refId]; |
|
if (label) { |
|
label.text(prop); |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_INTEGER: |
|
var label = this._labels[refId]; |
|
if (label) { |
|
label.text(prop); |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_FLOAT: |
|
if(property.fillOpacity()) { |
|
svgElem.setAttributeNS(null, 'fill-opacity', prop); |
|
} |
|
if(property.strokeOpacity()) { |
|
svgElem.setAttributeNS(null, 'stroke-opacity', prop); |
|
} |
|
if(!property.fillOpacity() && !property.strokeOpacity()) { |
|
var label = this._labels[refId]; |
|
if (label) { |
|
label.text(prop); |
|
} |
|
} |
|
break; |
|
|
|
case ORYX.CONFIG.TYPE_FORM_LINK: |
|
if (ref == "pimg") { |
|
var onclickAttr = svgElem.getAttributeNodeNS('', 'onclick'); |
|
if(onclickAttr) { |
|
if(prop && ("" + prop).length > 0) { |
|
onclickAttr.textContent = "window.location = '../service/editor?id=" + prop + "_form'"; |
|
} else { |
|
newFormFacade = this.facade; |
|
onclickAttr.textContent = "displayNewFormDialog('" + this.resourceId + "');"; |
|
} |
|
} |
|
} else if (ref == "linkIndicator") { |
|
if (prop && prop.length > 0) { |
|
svgElem.setAttributeNS(null, 'display', 'inherit'); |
|
} else { |
|
svgElem.setAttributeNS(null, 'display', 'none'); |
|
} |
|
} |
|
break; |
|
case ORYX.CONFIG.TYPE_URL: |
|
case ORYX.CONFIG.TYPE_DIAGRAM_LINK: |
|
//TODO what is the dafault path? |
|
var hrefAttr = svgElem.getAttributeNodeNS('http://www.w3.org/1999/xlink', 'xlink:href'); |
|
if(hrefAttr) { |
|
hrefAttr.textContent = prop; |
|
} else { |
|
svgElem.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', prop); |
|
} |
|
break; |
|
|
|
} |
|
} |
|
}).bind(this)); |
|
|
|
|
|
} |
|
} |
|
|
|
} |
|
}).bind(this)); |
|
|
|
//update labels |
|
this._labels.values().each(function(label) { |
|
label.update(); |
|
}); |
|
} |
|
}, |
|
|
|
layout: function() { |
|
//this.getStencil().layout(this) |
|
var layoutEvents = this.getStencil().layout() |
|
if (layoutEvents) { |
|
layoutEvents.each(function(event) { |
|
|
|
// setup additional attributes |
|
event.shape = this; |
|
event.forceExecution = true; |
|
|
|
// do layouting |
|
this._delegateEvent(event); |
|
}.bind(this)) |
|
|
|
} |
|
}, |
|
|
|
/** |
|
* Returns an array of Label objects. |
|
*/ |
|
getLabels: function() { |
|
return this._labels.values(); |
|
}, |
|
|
|
/** |
|
* Returns the label for a given ref |
|
* @return {ORYX.Core.Label} Returns null if there is no label |
|
*/ |
|
getLabel: function(ref){ |
|
if (!ref){ |
|
return null; |
|
} |
|
return (this._labels.find(function(o){ |
|
return o.key.endsWith(ref); |
|
})||{}).value || null; |
|
}, |
|
|
|
/** |
|
* Hides all related labels |
|
* |
|
*/ |
|
hideLabels: function(){ |
|
this.getLabels().invoke("hide"); |
|
}, |
|
|
|
/** |
|
* Shows all related labels |
|
* |
|
*/ |
|
showLabels: function(){ |
|
var labels = this.getLabels(); |
|
labels.invoke("show"); |
|
labels.each(function(label) { |
|
label.update(); |
|
}); |
|
}, |
|
|
|
setOpacity: function(value, animate){ |
|
|
|
value = Math.max(Math.min((typeof value == "number" ? value : 1.0), 1.0), 0.0); |
|
|
|
if (value !== 1.0){ |
|
value = String(value); |
|
this.node.setAttributeNS(null, "fill-opacity", value) |
|
this.node.setAttributeNS(null, "stroke-opacity", value) |
|
} else { |
|
this.node.removeAttributeNS(null, "fill-opacity"); |
|
this.node.removeAttributeNS(null, "stroke-opacity"); |
|
} |
|
}, |
|
|
|
/** |
|
* Returns an array of dockers of this object. |
|
*/ |
|
getDockers: function() { |
|
return this.dockers; |
|
}, |
|
|
|
getMagnets: function() { |
|
return this.magnets; |
|
}, |
|
|
|
getDefaultMagnet: function() { |
|
if(this._defaultMagnet) { |
|
return this._defaultMagnet; |
|
} else if (this.magnets.length > 0) { |
|
return this.magnets[0]; |
|
} else { |
|
return undefined; |
|
} |
|
}, |
|
|
|
getParentShape: function() { |
|
return this.parent; |
|
}, |
|
|
|
getIncomingShapes: function(iterator) { |
|
if(iterator) { |
|
this.incoming.each(iterator); |
|
} |
|
return this.incoming; |
|
}, |
|
|
|
getIncomingNodes: function(iterator) { |
|
return this.incoming.select(function(incoming){ |
|
var isNode = (incoming instanceof ORYX.Core.Node); |
|
if(isNode && iterator) iterator(incoming); |
|
return isNode; |
|
}); |
|
}, |
|
|
|
|
|
getOutgoingShapes: function(iterator) { |
|
if(iterator) { |
|
this.outgoing.each(iterator); |
|
} |
|
return this.outgoing; |
|
}, |
|
|
|
getOutgoingNodes: function(iterator) { |
|
return this.outgoing.select(function(out){ |
|
var isNode = (out instanceof ORYX.Core.Node); |
|
if(isNode && iterator) iterator(out); |
|
return isNode; |
|
}); |
|
}, |
|
|
|
getAllDockedShapes: function(iterator) { |
|
var result = this.incoming.concat(this.outgoing); |
|
if(iterator) { |
|
result.each(iterator); |
|
} |
|
return result |
|
}, |
|
|
|
getCanvas: function() { |
|
if(this.parent instanceof ORYX.Core.Canvas) { |
|
return this.parent; |
|
} else if(this.parent instanceof ORYX.Core.Shape) { |
|
return this.parent.getCanvas(); |
|
} else { |
|
return undefined; |
|
} |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} deep |
|
* @param {Object} iterator |
|
*/ |
|
getChildNodes: function(deep, iterator) { |
|
if(!deep && !iterator) { |
|
return this.nodes.clone(); |
|
} else { |
|
var result = []; |
|
this.nodes.each(function(uiObject) { |
|
if(!uiObject.isVisible){return} |
|
if(iterator) { |
|
iterator(uiObject); |
|
} |
|
result.push(uiObject); |
|
|
|
if(deep && uiObject instanceof ORYX.Core.Shape) { |
|
result = result.concat(uiObject.getChildNodes(deep, iterator)); |
|
} |
|
}); |
|
|
|
return result; |
|
} |
|
}, |
|
|
|
/** |
|
* Overrides the UIObject.add method. Adds uiObject to the correct sub node. |
|
* @param {UIObject} uiObject |
|
* @param {Number} index |
|
*/ |
|
add: function(uiObject, index, silent) { |
|
//parameter has to be an UIObject, but |
|
// must not be an Edge. |
|
if(uiObject instanceof ORYX.Core.UIObject |
|
&& !(uiObject instanceof ORYX.Core.Edge)) { |
|
|
|
if (!(this.children.member(uiObject))) { |
|
//if uiObject is child of another parent, remove it from that parent. |
|
if(uiObject.parent) { |
|
uiObject.parent.remove(uiObject, true); |
|
} |
|
|
|
//add uiObject to this Shape |
|
if(index != undefined) |
|
this.children.splice(index, 0, uiObject); |
|
else |
|
this.children.push(uiObject); |
|
|
|
//set parent reference |
|
uiObject.parent = this; |
|
|
|
//add uiObject.node to this.node depending on the type of uiObject |
|
var parent; |
|
if(uiObject instanceof ORYX.Core.Node) { |
|
parent = this.node.childNodes[0].childNodes[1]; |
|
this.nodes.push(uiObject); |
|
} else if(uiObject instanceof ORYX.Core.Controls.Control) { |
|
var ctrls = this.node.childNodes[1]; |
|
if(uiObject instanceof ORYX.Core.Controls.Docker) { |
|
parent = ctrls.childNodes[0]; |
|
if (this.dockers.length >= 2){ |
|
this.dockers.splice(index!==undefined?Math.min(index, this.dockers.length-1):this.dockers.length-1, 0, uiObject); |
|
} else { |
|
this.dockers.push(uiObject); |
|
} |
|
} else if(uiObject instanceof ORYX.Core.Controls.Magnet) { |
|
parent = ctrls.childNodes[1]; |
|
this.magnets.push(uiObject); |
|
} else { |
|
parent = ctrls; |
|
} |
|
} else { //UIObject |
|
parent = this.node; |
|
} |
|
|
|
if(index != undefined && index < parent.childNodes.length) |
|
uiObject.node = parent.insertBefore(uiObject.node, parent.childNodes[index]); |
|
else |
|
uiObject.node = parent.appendChild(uiObject.node); |
|
|
|
this._changed(); |
|
//uiObject.bounds.registerCallback(this._changedCallback); |
|
|
|
|
|
if(this.eventHandlerCallback && silent !== true) |
|
this.eventHandlerCallback({type:ORYX.CONFIG.EVENT_SHAPEADDED,shape:uiObject}) |
|
|
|
} else { |
|
|
|
ORYX.Log.warn("add: ORYX.Core.UIObject is already a child of this object."); |
|
} |
|
} else { |
|
|
|
ORYX.Log.warn("add: Parameter is not of type ORYX.Core.UIObject."); |
|
} |
|
}, |
|
|
|
/** |
|
* Overrides the UIObject.remove method. Removes uiObject. |
|
* @param {UIObject} uiObject |
|
*/ |
|
remove: function(uiObject, silent) { |
|
//if uiObject is a child of this object, remove it. |
|
if (this.children.member(uiObject)) { |
|
//remove uiObject from children |
|
var parent = uiObject.parent; |
|
|
|
this.children = this.children.without(uiObject); |
|
|
|
//delete parent reference of uiObject |
|
uiObject.parent = undefined; |
|
|
|
//delete uiObject.node from this.node |
|
if(uiObject instanceof ORYX.Core.Shape) { |
|
if(uiObject instanceof ORYX.Core.Edge) { |
|
uiObject.removeMarkers(); |
|
uiObject.node = this.node.childNodes[0].childNodes[2].removeChild(uiObject.node); |
|
} else { |
|
uiObject.node = this.node.childNodes[0].childNodes[1].removeChild(uiObject.node); |
|
this.nodes = this.nodes.without(uiObject); |
|
} |
|
} else if(uiObject instanceof ORYX.Core.Controls.Control) { |
|
if (uiObject instanceof ORYX.Core.Controls.Docker) { |
|
uiObject.node = this.node.childNodes[1].childNodes[0].removeChild(uiObject.node); |
|
this.dockers = this.dockers.without(uiObject); |
|
} else if (uiObject instanceof ORYX.Core.Controls.Magnet) { |
|
uiObject.node = this.node.childNodes[1].childNodes[1].removeChild(uiObject.node); |
|
this.magnets = this.magnets.without(uiObject); |
|
} else { |
|
uiObject.node = this.node.childNodes[1].removeChild(uiObject.node); |
|
} |
|
} |
|
|
|
if(this.eventHandlerCallback && silent !== true) |
|
this.eventHandlerCallback({type: ORYX.CONFIG.EVENT_SHAPEREMOVED, shape: uiObject, parent: parent}); |
|
|
|
this._changed(); |
|
//uiObject.bounds.unregisterCallback(this._changedCallback); |
|
} else { |
|
|
|
ORYX.Log.warn("remove: ORYX.Core.UIObject is not a child of this object."); |
|
} |
|
}, |
|
|
|
/** |
|
* Calculate the Border Intersection Point between two points |
|
* @param {PointA} |
|
* @param {PointB} |
|
*/ |
|
getIntersectionPoint: function() { |
|
|
|
var pointAX, pointAY, pointBX, pointBY; |
|
|
|
// Get the the two Points |
|
switch(arguments.length) { |
|
case 2: |
|
pointAX = arguments[0].x; |
|
pointAY = arguments[0].y; |
|
pointBX = arguments[1].x; |
|
pointBY = arguments[1].y; |
|
break; |
|
case 4: |
|
pointAX = arguments[0]; |
|
pointAY = arguments[1]; |
|
pointBX = arguments[2]; |
|
pointBY = arguments[3]; |
|
break; |
|
default: |
|
throw "getIntersectionPoints needs two or four arguments"; |
|
} |
|
|
|
|
|
|
|
// Defined an include and exclude point |
|
var includePointX, includePointY, excludePointX, excludePointY; |
|
|
|
var bounds = this.absoluteBounds(); |
|
|
|
if(this.isPointIncluded(pointAX, pointAY, bounds)){ |
|
includePointX = pointAX; |
|
includePointY = pointAY; |
|
} else { |
|
excludePointX = pointAX; |
|
excludePointY = pointAY; |
|
} |
|
|
|
if(this.isPointIncluded(pointBX, pointBY, bounds)){ |
|
includePointX = pointBX; |
|
includePointY = pointBY; |
|
} else { |
|
excludePointX = pointBX; |
|
excludePointY = pointBY; |
|
} |
|
|
|
// If there is no inclue or exclude Shape, than return |
|
if(!includePointX || !includePointY || !excludePointX || !excludePointY) { |
|
return undefined; |
|
} |
|
|
|
var midPointX = 0; |
|
var midPointY = 0; |
|
|
|
var refPointX, refPointY; |
|
|
|
var minDifferent = 1; |
|
// Get the UpperLeft and LowerRight |
|
//var ul = bounds.upperLeft(); |
|
//var lr = bounds.lowerRight(); |
|
|
|
var i = 0; |
|
|
|
while(true) { |
|
// Calculate the midpoint of the current to points |
|
var midPointX = Math.min(includePointX, excludePointX) + ((Math.max(includePointX, excludePointX) - Math.min(includePointX, excludePointX)) / 2.0); |
|
var midPointY = Math.min(includePointY, excludePointY) + ((Math.max(includePointY, excludePointY) - Math.min(includePointY, excludePointY)) / 2.0); |
|
|
|
|
|
// Set the new midpoint by the means of the include of the bounds |
|
if(this.isPointIncluded(midPointX, midPointY, bounds)){ |
|
includePointX = midPointX; |
|
includePointY = midPointY; |
|
} else { |
|
excludePointX = midPointX; |
|
excludePointY = midPointY; |
|
} |
|
|
|
// Calc the length of the line |
|
var length = Math.sqrt(Math.pow(includePointX - excludePointX, 2) + Math.pow(includePointY - excludePointY, 2)) |
|
// Calc a point one step from the include point |
|
refPointX = includePointX + ((excludePointX - includePointX) / length), |
|
refPointY = includePointY + ((excludePointY - includePointY) / length) |
|
|
|
|
|
// If the reference point not in the bounds, break |
|
if(!this.isPointIncluded(refPointX, refPointY, bounds)) { |
|
break |
|
} |
|
|
|
|
|
} |
|
|
|
// Return the last includepoint |
|
return {x:refPointX , y:refPointY}; |
|
}, |
|
|
|
|
|
|
|
/** |
|
* Calculate if the point is inside the Shape |
|
* @param {PointX} |
|
* @param {PointY} |
|
*/ |
|
isPointIncluded: function(){ |
|
return false |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the given node |
|
* is a child node of the shapes node |
|
* @param {Element} node |
|
* @return {Boolean} |
|
* |
|
*/ |
|
containsNode: function(node){ |
|
var me = this.node.firstChild.firstChild; |
|
while(node){ |
|
if (node == me){ |
|
return true; |
|
} |
|
node = node.parentNode; |
|
} |
|
return false |
|
}, |
|
|
|
/** |
|
* Calculate if the point is over an special offset area |
|
* @param {Point} |
|
*/ |
|
isPointOverOffset: function(){ |
|
return this.isPointIncluded.apply( this , arguments ) |
|
}, |
|
|
|
_dockerChanged: function() { |
|
|
|
}, |
|
|
|
/** |
|
* Create a Docker for this Edge |
|
* |
|
*/ |
|
createDocker: function(index, position) { |
|
var docker = new ORYX.Core.Controls.Docker({eventHandlerCallback: this.eventHandlerCallback}); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
if (position) { |
|
docker.bounds.centerMoveTo(position); |
|
} |
|
this.add(docker, index); |
|
|
|
return docker |
|
}, |
|
|
|
/** |
|
* Get the serialized object |
|
* return Array with hash-entrees (prefix, name, value) |
|
* Following values will given: |
|
* Bounds |
|
* Outgoing Shapes |
|
* Parent |
|
*/ |
|
serialize: function() { |
|
var serializedObject = arguments.callee.$.serialize.apply(this); |
|
|
|
// Add the bounds |
|
serializedObject.push({name: 'bounds', prefix:'oryx', value: this.bounds.serializeForERDF(), type: 'literal'}); |
|
|
|
// Add the outgoing shapes |
|
this.getOutgoingShapes().each((function(followingShape){ |
|
serializedObject.push({name: 'outgoing', prefix:'raziel', value: '#'+ERDF.__stripHashes(followingShape.resourceId), type: 'resource'}); |
|
}).bind(this)); |
|
|
|
// Add the parent shape, if the parent not the canvas |
|
//if(this.parent instanceof ORYX.Core.Shape){ |
|
serializedObject.push({name: 'parent', prefix:'raziel', value: '#'+ERDF.__stripHashes(this.parent.resourceId), type: 'resource'}); |
|
//} |
|
|
|
return serializedObject; |
|
}, |
|
|
|
|
|
deserialize: function(serialize, json){ |
|
arguments.callee.$.deserialize.apply(this, arguments); |
|
|
|
// Set the Bounds |
|
var bounds = serialize.find(function(ser){ return 'oryx-bounds' === (ser.prefix+"-"+ser.name) }); |
|
if (bounds) { |
|
var b = bounds.value.replace(/,/g, " ").split(" ").without(""); |
|
if (this instanceof ORYX.Core.Edge) { |
|
if (!this.dockers.first().isChanged) |
|
this.dockers.first().bounds.centerMoveTo(parseFloat(b[0]), parseFloat(b[1])); |
|
if (!this.dockers.last().isChanged) |
|
this.dockers.last().bounds.centerMoveTo(parseFloat(b[2]), parseFloat(b[3])); |
|
} else { |
|
this.bounds.set(parseFloat(b[0]), parseFloat(b[1]), parseFloat(b[2]), parseFloat(b[3])); |
|
} |
|
} |
|
|
|
if (json && json.labels instanceof Array){ |
|
json.labels.each(function(slabel){ |
|
var label = this.getLabel(slabel.ref); |
|
if (label){ |
|
label.deserialize(slabel, this); |
|
} |
|
}.bind(this)) |
|
} |
|
}, |
|
|
|
toJSON: function(){ |
|
var json = arguments.callee.$.toJSON.apply(this, arguments); |
|
|
|
var labels = [], id = this.id; |
|
this._labels.each(function(obj){ |
|
var slabel = obj.value.serialize(); |
|
if (slabel){ |
|
slabel.ref = obj.key.replace(id, ''); |
|
labels.push(slabel); |
|
} |
|
}); |
|
|
|
if (labels.length > 0){ |
|
json.labels = labels; |
|
} |
|
return json; |
|
}, |
|
|
|
|
|
/** |
|
* Private methods. |
|
*/ |
|
|
|
/** |
|
* Child classes have to overwrite this method for initializing a loaded |
|
* SVG representation. |
|
* @param {SVGDocument} svgDocument |
|
*/ |
|
_init: function(svgDocument) { |
|
//adjust ids |
|
this._adjustIds(svgDocument, 0); |
|
}, |
|
|
|
_adjustIds: function(element, idIndex) { |
|
if(element instanceof Element) { |
|
var eid = element.getAttributeNS(null, 'id'); |
|
if(eid && eid !== "") { |
|
element.setAttributeNS(null, 'id', this.id + eid); |
|
} else { |
|
element.setAttributeNS(null, 'id', this.id + "_" + this.id + "_" + idIndex); |
|
idIndex++; |
|
} |
|
|
|
// Replace URL in fill attribute |
|
var fill = element.getAttributeNS(null, 'fill'); |
|
if (fill&&fill.include("url(#")){ |
|
fill = fill.replace(/url\(#/g, 'url(#'+this.id); |
|
element.setAttributeNS(null, 'fill', fill); |
|
} |
|
|
|
if(element.hasChildNodes()) { |
|
for(var i = 0; i < element.childNodes.length; i++) { |
|
idIndex = this._adjustIds(element.childNodes[i], idIndex); |
|
} |
|
} |
|
} |
|
return idIndex; |
|
}, |
|
|
|
toString: function() { return "ORYX.Core.Shape " + this.getId() } |
|
}; |
|
ORYX.Core.Shape = ORYX.Core.AbstractShape.extend(ORYX.Core.Shape);/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.Controls) {ORYX.Core.Controls = {};} |
|
|
|
|
|
/** |
|
* @classDescription Abstract base class for all Controls. |
|
*/ |
|
ORYX.Core.Controls.Control = ORYX.Core.UIObject.extend({ |
|
|
|
toString: function() { return "Control " + this.id; } |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.Controls) {ORYX.Core.Controls = {};} |
|
|
|
|
|
/** |
|
* @classDescription Represents a movable docker that can be bound to a shape. Dockers are used |
|
* for positioning shape objects. |
|
* @extends {Control} |
|
* |
|
* TODO absoluteXY und absoluteCenterXY von einem Docker liefern falsche Werte!!! |
|
*/ |
|
ORYX.Core.Controls.Docker = ORYX.Core.Controls.Control.extend({ |
|
/** |
|
* Constructor |
|
*/ |
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.isMovable = true; // Enables movability |
|
this.bounds.set(0, 0, 16, 16); // Set the bounds |
|
this.referencePoint = undefined; // Refrenzpoint |
|
this._dockedShapeBounds = undefined; |
|
this._dockedShape = undefined; |
|
this._oldRefPoint1 = undefined; |
|
this._oldRefPoint2 = undefined; |
|
|
|
//this.anchors = []; |
|
this.anchorLeft; |
|
this.anchorRight; |
|
this.anchorTop; |
|
this.anchorBottom; |
|
|
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", |
|
null, |
|
['g']); |
|
|
|
// The DockerNode reprasentation |
|
this._dockerNode = ORYX.Editor.graft("http://www.w3.org/2000/svg", |
|
this.node, |
|
['g', {"pointer-events":"all"}, |
|
['circle', {cx:"8", cy:"8", r:"8", stroke:"none", fill:"none"}], |
|
['circle', {cx:"8", cy:"8", r:"3", stroke:"black", fill:"red", "stroke-width":"1"}] |
|
]); |
|
|
|
// The ReferenzNode reprasentation |
|
this._referencePointNode = ORYX.Editor.graft("http://www.w3.org/2000/svg", |
|
this.node, |
|
['g', {"pointer-events":"none"}, |
|
['circle', {cx: this.bounds.upperLeft().x, cy: this.bounds.upperLeft().y, r: 3, fill:"red", "fill-opacity":0.4}]]); |
|
|
|
// Hide the Docker |
|
this.hide(); |
|
|
|
//Add to the EventHandler |
|
this.addEventHandlers(this._dockerNode); |
|
|
|
// Buffer the Update Callback for un-/register on Event-Handler |
|
this._updateCallback = this._changed.bind(this); |
|
}, |
|
|
|
update: function() { |
|
// If there have an DockedShape |
|
if(this._dockedShape) { |
|
if(this._dockedShapeBounds && this._dockedShape instanceof ORYX.Core.Node) { |
|
// Calc the delta of width and height of the lastBounds and the current Bounds |
|
var dswidth = this._dockedShapeBounds.width(); |
|
var dsheight = this._dockedShapeBounds.height(); |
|
if(!dswidth) |
|
dswidth = 1; |
|
if(!dsheight) |
|
dsheight = 1; |
|
var widthDelta = this._dockedShape.bounds.width() / dswidth; |
|
var heightDelta = this._dockedShape.bounds.height() / dsheight; |
|
|
|
// If there is an different |
|
if(widthDelta !== 1.0 || heightDelta !== 1.0) { |
|
// Set the delta |
|
this.referencePoint.x *= widthDelta; |
|
this.referencePoint.y *= heightDelta; |
|
} |
|
|
|
// Clone these bounds |
|
this._dockedShapeBounds = this._dockedShape.bounds.clone(); |
|
} |
|
|
|
// Get the first and the last Docker of the parent Shape |
|
var dockerIndex = this.parent.dockers.indexOf(this) |
|
var dock1 = this; |
|
var dock2 = this.parent.dockers.length > 1 ? |
|
(dockerIndex === 0? // If there is the first element |
|
this.parent.dockers[dockerIndex + 1]: // then take the next docker |
|
this.parent.dockers[dockerIndex - 1]): // if not, then take the docker before |
|
undefined; |
|
|
|
// Calculate the first absolute Refenzpoint |
|
var absoluteReferenzPoint1 = dock1.getDockedShape() ? |
|
dock1.getAbsoluteReferencePoint() : |
|
dock1.bounds.center(); |
|
|
|
// Calculate the last absolute Refenzpoint |
|
var absoluteReferenzPoint2 = dock2 && dock2.getDockedShape() ? |
|
dock2.getAbsoluteReferencePoint() : |
|
dock2 ? |
|
dock2.bounds.center() : |
|
undefined; |
|
|
|
// If there is no last absolute Referenzpoint |
|
if(!absoluteReferenzPoint2) { |
|
// Calculate from the middle of the DockedShape |
|
var center = this._dockedShape.absoluteCenterXY(); |
|
var minDimension = this._dockedShape.bounds.width() * this._dockedShape.bounds.height(); |
|
absoluteReferenzPoint2 = { |
|
x: absoluteReferenzPoint1.x + (center.x - absoluteReferenzPoint1.x) * -minDimension, |
|
y: absoluteReferenzPoint1.y + (center.y - absoluteReferenzPoint1.y) * -minDimension |
|
} |
|
} |
|
|
|
var newPoint = undefined; |
|
|
|
/*if (!this._oldRefPoint1 || !this._oldRefPoint2 || |
|
absoluteReferenzPoint1.x !== this._oldRefPoint1.x || |
|
absoluteReferenzPoint1.y !== this._oldRefPoint1.y || |
|
absoluteReferenzPoint2.x !== this._oldRefPoint2.x || |
|
absoluteReferenzPoint2.y !== this._oldRefPoint2.y) {*/ |
|
|
|
// Get the new point for the Docker, calucalted by the intersection point of the Shape and the two points |
|
newPoint = this._dockedShape.getIntersectionPoint(absoluteReferenzPoint1, absoluteReferenzPoint2); |
|
|
|
// If there is new point, take the referencepoint as the new point |
|
if(!newPoint) { |
|
newPoint = this.getAbsoluteReferencePoint(); |
|
} |
|
|
|
if(this.parent && this.parent.parent) { |
|
var grandParentPos = this.parent.parent.absoluteXY(); |
|
newPoint.x -= grandParentPos.x; |
|
newPoint.y -= grandParentPos.y; |
|
} |
|
|
|
// Set the bounds to the new point |
|
this.bounds.centerMoveTo(newPoint) |
|
|
|
this._oldRefPoint1 = absoluteReferenzPoint1; |
|
this._oldRefPoint2 = absoluteReferenzPoint2; |
|
} |
|
/*else { |
|
newPoint = this.bounds.center(); |
|
}*/ |
|
|
|
|
|
// } |
|
|
|
// Call the super class |
|
arguments.callee.$.update.apply(this, arguments); |
|
}, |
|
|
|
/** |
|
* Calls the super class refresh method and updates the view of the docker. |
|
*/ |
|
refresh: function() { |
|
arguments.callee.$.refresh.apply(this, arguments); |
|
|
|
// Refresh the dockers node |
|
var p = this.bounds.upperLeft(); |
|
this._dockerNode.setAttributeNS(null, 'transform','translate(' + p.x + ', ' + p.y + ')'); |
|
|
|
// Refresh the referencepoints node |
|
p = Object.clone(this.referencePoint); |
|
|
|
if(p && this._dockedShape){ |
|
var upL |
|
if(this.parent instanceof ORYX.Core.Edge) { |
|
upL = this._dockedShape.absoluteXY(); |
|
} else { |
|
upL = this._dockedShape.bounds.upperLeft(); |
|
} |
|
p.x += upL.x; |
|
p.y += upL.y; |
|
} else { |
|
p = this.bounds.center(); |
|
} |
|
|
|
this._referencePointNode.setAttributeNS(null, 'transform','translate(' + p.x + ', ' + p.y + ')'); |
|
}, |
|
|
|
/** |
|
* Set the reference point |
|
* @param {Object} point |
|
*/ |
|
setReferencePoint: function(point) { |
|
// Set the referencepoint |
|
if(this.referencePoint !== point && |
|
(!this.referencePoint || |
|
!point || |
|
this.referencePoint.x !== point.x || |
|
this.referencePoint.y !== point.y)) { |
|
|
|
this.referencePoint = point; |
|
this._changed(); |
|
} |
|
|
|
|
|
// Update directly, because the referencepoint has no influence of the bounds |
|
//this.refresh(); |
|
}, |
|
|
|
/** |
|
* Get the absolute referencepoint |
|
*/ |
|
getAbsoluteReferencePoint: function() { |
|
if(!this.referencePoint || !this._dockedShape) { |
|
return undefined; |
|
} else { |
|
var absUL = this._dockedShape.absoluteXY(); |
|
return { |
|
x: this.referencePoint.x + absUL.x, |
|
y: this.referencePoint.y + absUL.y |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Set the docked Shape from the docker |
|
* @param {Object} shape |
|
*/ |
|
setDockedShape: function(shape) { |
|
|
|
// If there is an old docked Shape |
|
if(this._dockedShape) { |
|
this._dockedShape.bounds.unregisterCallback(this._updateCallback) |
|
|
|
// Delete the Shapes from the incoming and outgoing array |
|
// If this Docker the incoming of the Shape |
|
if(this === this.parent.dockers.first()) { |
|
|
|
this.parent.incoming = this.parent.incoming.without(this._dockedShape); |
|
this._dockedShape.outgoing = this._dockedShape.outgoing.without(this.parent); |
|
|
|
// If this Docker the outgoing of the Shape |
|
} else if (this === this.parent.dockers.last()){ |
|
|
|
this.parent.outgoing = this.parent.outgoing.without(this._dockedShape); |
|
this._dockedShape.incoming = this._dockedShape.incoming.without(this.parent); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
// Set the new Shape |
|
this._dockedShape = shape; |
|
this._dockedShapeBounds = undefined; |
|
var referencePoint = undefined; |
|
|
|
// If there is an Shape, register the updateCallback if there are changes in the shape bounds |
|
if(this._dockedShape) { |
|
|
|
// Add the Shapes to the incoming and outgoing array |
|
// If this Docker the incoming of the Shape |
|
if(this === this.parent.dockers.first()) { |
|
|
|
this.parent.incoming.push(shape); |
|
shape.outgoing.push(this.parent); |
|
|
|
// If this Docker the outgoing of the Shape |
|
} else if (this === this.parent.dockers.last()){ |
|
|
|
this.parent.outgoing.push(shape); |
|
shape.incoming.push(this.parent); |
|
|
|
} |
|
|
|
// Get the bounds and set the new referencepoint |
|
var bounds = this.bounds; |
|
var absUL = shape.absoluteXY(); |
|
|
|
/*if(shape.parent){ |
|
var b = shape.parent.bounds.upperLeft(); |
|
absUL.x -= b.x; |
|
absUL.y -= b.y; |
|
}*/ |
|
|
|
referencePoint = { |
|
x: bounds.center().x - absUL.x, |
|
y: bounds.center().y - absUL.y |
|
} |
|
|
|
this._dockedShapeBounds = this._dockedShape.bounds.clone(); |
|
|
|
this._dockedShape.bounds.registerCallback(this._updateCallback); |
|
|
|
// Set the color of the docker as docked |
|
this.setDockerColor(ORYX.CONFIG.DOCKER_DOCKED_COLOR); |
|
} else { |
|
// Set the color of the docker as undocked |
|
this.setDockerColor(ORYX.CONFIG.DOCKER_UNDOCKED_COLOR); |
|
} |
|
|
|
// Set the referencepoint |
|
this.setReferencePoint(referencePoint); |
|
this._changed(); |
|
//this.update(); |
|
}, |
|
|
|
/** |
|
* Get the docked Shape |
|
*/ |
|
getDockedShape: function() { |
|
return this._dockedShape; |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the docker has a docked shape |
|
*/ |
|
isDocked: function() { |
|
return !!this._dockedShape; |
|
}, |
|
|
|
/** |
|
* Set the Color of the Docker |
|
* @param {Object} color |
|
*/ |
|
setDockerColor: function(color) { |
|
this._dockerNode.lastChild.setAttributeNS(null, "fill", color); |
|
}, |
|
|
|
preventHiding: function(prevent){ |
|
this._preventHiding = Math.max(0, (this._preventHiding||0) + (prevent ? 1 : -1)); |
|
}, |
|
|
|
/** |
|
* Hides this UIObject and all its children. |
|
*/ |
|
hide: function() { |
|
if (this._preventHiding){ |
|
return false; |
|
} |
|
|
|
// Hide docker and reference point |
|
this.node.setAttributeNS(null, 'visibility', 'hidden'); |
|
this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden'); |
|
|
|
this.children.each(function(uiObj) { |
|
uiObj.hide(); |
|
}); |
|
}, |
|
|
|
/** |
|
* Enables visibility of this UIObject and all its children. |
|
*/ |
|
show: function() { |
|
// Show docker |
|
this.node.setAttributeNS(null, 'visibility', 'visible'); |
|
|
|
// Hide reference point if the connected shape is an edge |
|
if (this.getDockedShape() instanceof ORYX.Core.Edge){ |
|
this._referencePointNode.setAttributeNS(null, 'visibility', 'hidden'); |
|
} else { |
|
this._referencePointNode.setAttributeNS(null, 'visibility', 'visible'); |
|
} |
|
|
|
this.children.each(function(uiObj) { |
|
uiObj.show(); |
|
}); |
|
}, |
|
|
|
toString: function() { return "Docker " + this.id } |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if(!ORYX) {var ORYX = {};} |
|
if(!ORYX.Core) {ORYX.Core = {};} |
|
if(!ORYX.Core.Controls) {ORYX.Core.Controls = {};} |
|
|
|
|
|
/** |
|
* @classDescription Represents a magnet that is part of another shape and can |
|
* be attached to dockers. Magnets are used for linking edge objects |
|
* to other Shape objects. |
|
* @extends {Control} |
|
*/ |
|
ORYX.Core.Controls.Magnet = ORYX.Core.Controls.Control.extend({ |
|
|
|
/** |
|
* Constructor |
|
*/ |
|
construct: function() { |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
//this.anchors = []; |
|
this.anchorLeft; |
|
this.anchorRight; |
|
this.anchorTop; |
|
this.anchorBottom; |
|
|
|
this.bounds.set(0, 0, 16, 16); |
|
|
|
//graft magnet's root node into owner's control group. |
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", |
|
null, |
|
['g', {"pointer-events":"all"}, |
|
['circle', {cx:"8", cy:"8", r:"4", stroke:"none", fill:"red", "fill-opacity":"0.3"}], |
|
]); |
|
|
|
this.hide(); |
|
}, |
|
|
|
update: function() { |
|
arguments.callee.$.update.apply(this, arguments); |
|
|
|
//this.isChanged = true; |
|
}, |
|
|
|
_update: function() { |
|
arguments.callee.$.update.apply(this, arguments); |
|
|
|
//this.isChanged = true; |
|
}, |
|
|
|
refresh: function() { |
|
arguments.callee.$.refresh.apply(this, arguments); |
|
|
|
var p = this.bounds.upperLeft(); |
|
/*if(this.parent) { |
|
var parentPos = this.parent.bounds.upperLeft(); |
|
p.x += parentPos.x; |
|
p.y += parentPos.y; |
|
}*/ |
|
|
|
this.node.setAttributeNS(null, 'transform','translate(' + p.x + ', ' + p.y + ')'); |
|
}, |
|
|
|
show: function() { |
|
//this.refresh(); |
|
arguments.callee.$.show.apply(this, arguments); |
|
}, |
|
|
|
toString: function() { |
|
return "Magnet " + this.id; |
|
} |
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if (!ORYX) { |
|
var ORYX = {}; |
|
} |
|
if (!ORYX.Core) { |
|
ORYX.Core = {}; |
|
} |
|
|
|
/** |
|
* @classDescription Abstract base class for all Nodes. |
|
* @extends ORYX.Core.Shape |
|
*/ |
|
ORYX.Core.Node = { |
|
|
|
/** |
|
* Constructor |
|
* @param options {Object} A container for arguments. |
|
* @param stencil {Stencil} |
|
*/ |
|
construct: function(options, stencil, facade){ |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.isSelectable = true; |
|
this.isMovable = true; |
|
this._dockerUpdated = false; |
|
this.facade = facade; |
|
|
|
this._oldBounds = new ORYX.Core.Bounds(); //init bounds with undefined values |
|
this._svgShapes = []; //array of all SVGShape objects of |
|
// SVG representation |
|
|
|
//TODO vielleicht in shape verschieben? |
|
this.minimumSize = undefined; // {width:..., height:...} |
|
this.maximumSize = undefined; |
|
|
|
//TODO vielleicht in shape oder uiobject verschieben? |
|
// vielleicht sogar isResizable ersetzen? |
|
this.isHorizontallyResizable = false; |
|
this.isVerticallyResizable = false; |
|
|
|
this.dataId = undefined; |
|
|
|
this._init(this._stencil.view()); |
|
this.forcedHeight = -1; |
|
}, |
|
|
|
/** |
|
* This method checks whether the shape is resized correctly and calls the |
|
* super class update method. |
|
*/ |
|
_update: function(){ |
|
|
|
this.dockers.invoke("update"); |
|
if (this.isChanged) { |
|
|
|
var bounds = this.bounds; |
|
var oldBounds = this._oldBounds; |
|
|
|
if (this.isResized) { |
|
|
|
var widthDelta = bounds.width() / oldBounds.width(); |
|
var heightDelta = bounds.height() / oldBounds.height(); |
|
|
|
//iterate over all relevant svg elements and resize them |
|
this._svgShapes.each(function(svgShape){ |
|
//adjust width |
|
if (svgShape.isHorizontallyResizable) { |
|
svgShape.width = svgShape.oldWidth * widthDelta; |
|
} |
|
//adjust height |
|
if (svgShape.isVerticallyResizable) { |
|
svgShape.height = svgShape.oldHeight * heightDelta; |
|
} |
|
|
|
//check, if anchors are set |
|
var anchorOffset; |
|
var leftIncluded = svgShape.anchorLeft; |
|
var rightIncluded = svgShape.anchorRight; |
|
|
|
if (rightIncluded) { |
|
anchorOffset = oldBounds.width() - (svgShape.oldX + svgShape.oldWidth); |
|
if (leftIncluded) { |
|
svgShape.width = bounds.width() - svgShape.x - anchorOffset; |
|
} |
|
else { |
|
svgShape.x = bounds.width() - (anchorOffset + svgShape.width); |
|
} |
|
} |
|
else |
|
if (!leftIncluded) { |
|
svgShape.x = widthDelta * svgShape.oldX; |
|
if (!svgShape.isHorizontallyResizable) { |
|
svgShape.x = svgShape.x + svgShape.width * widthDelta / 2 - svgShape.width / 2; |
|
} |
|
} |
|
|
|
var topIncluded = svgShape.anchorTop; |
|
var bottomIncluded = svgShape.anchorBottom; |
|
|
|
if (bottomIncluded) { |
|
anchorOffset = oldBounds.height() - (svgShape.oldY + svgShape.oldHeight); |
|
if (topIncluded) { |
|
svgShape.height = bounds.height() - svgShape.y - anchorOffset; |
|
} |
|
else { |
|
// Hack for choreography task layouting |
|
if (!svgShape._isYLocked) { |
|
svgShape.y = bounds.height() - (anchorOffset + svgShape.height); |
|
} |
|
} |
|
} |
|
else |
|
if (!topIncluded) { |
|
svgShape.y = heightDelta * svgShape.oldY; |
|
if (!svgShape.isVerticallyResizable) { |
|
svgShape.y = svgShape.y + svgShape.height * heightDelta / 2 - svgShape.height / 2; |
|
} |
|
} |
|
}); |
|
|
|
//check, if the current bounds is unallowed horizontally or vertically resized |
|
var p = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
if (!this.isHorizontallyResizable && bounds.width() !== oldBounds.width()) { |
|
p.x = oldBounds.width() - bounds.width(); |
|
} |
|
if (!this.isVerticallyResizable && bounds.height() !== oldBounds.height()) { |
|
p.y = oldBounds.height() - bounds.height(); |
|
} |
|
if (p.x !== 0 || p.y !== 0) { |
|
bounds.extend(p); |
|
} |
|
|
|
//check, if the current bounds are between maximum and minimum bounds |
|
p = { |
|
x: 0, |
|
y: 0 |
|
}; |
|
var widthDifference, heightDifference; |
|
if (this.minimumSize) { |
|
|
|
ORYX.Log.debug("Shape (%0)'s min size: (%1x%2)", this, this.minimumSize.width, this.minimumSize.height); |
|
widthDifference = this.minimumSize.width - bounds.width(); |
|
if (widthDifference > 0) { |
|
p.x += widthDifference; |
|
} |
|
heightDifference = this.minimumSize.height - bounds.height(); |
|
if (heightDifference > 0) { |
|
p.y += heightDifference; |
|
} |
|
} |
|
if (this.maximumSize) { |
|
|
|
ORYX.Log.debug("Shape (%0)'s max size: (%1x%2)", this, this.maximumSize.width, this.maximumSize.height); |
|
widthDifference = bounds.width() - this.maximumSize.width; |
|
if (widthDifference > 0) { |
|
p.x -= widthDifference; |
|
} |
|
heightDifference = bounds.height() - this.maximumSize.height; |
|
if (heightDifference > 0) { |
|
p.y -= heightDifference; |
|
} |
|
} |
|
if (p.x !== 0 || p.y !== 0) { |
|
bounds.extend(p); |
|
} |
|
|
|
//update magnets |
|
|
|
var widthDelta = bounds.width() / oldBounds.width(); |
|
var heightDelta = bounds.height() / oldBounds.height(); |
|
|
|
var leftIncluded, rightIncluded, topIncluded, bottomIncluded, center, newX, newY; |
|
|
|
this.magnets.each(function(magnet){ |
|
leftIncluded = magnet.anchorLeft; |
|
rightIncluded = magnet.anchorRight; |
|
topIncluded = magnet.anchorTop; |
|
bottomIncluded = magnet.anchorBottom; |
|
|
|
center = magnet.bounds.center(); |
|
|
|
if (leftIncluded) { |
|
newX = center.x; |
|
} |
|
else |
|
if (rightIncluded) { |
|
newX = bounds.width() - (oldBounds.width() - center.x) |
|
} |
|
else { |
|
newX = center.x * widthDelta; |
|
} |
|
|
|
if (topIncluded) { |
|
newY = center.y; |
|
} |
|
else |
|
if (bottomIncluded) { |
|
newY = bounds.height() - (oldBounds.height() - center.y); |
|
} |
|
else { |
|
newY = center.y * heightDelta; |
|
} |
|
|
|
if (center.x !== newX || center.y !== newY) { |
|
magnet.bounds.centerMoveTo(newX, newY); |
|
} |
|
}); |
|
|
|
//set new position of labels |
|
this.getLabels().each(function(label){ |
|
// Set the position dependings on it anchor |
|
if (!label.isAnchorLeft()) { |
|
if (label.isAnchorRight()) { |
|
label.setX(bounds.width() - (oldBounds.width() - label.oldX)) |
|
} else { |
|
label.setX((label.position?label.position.x:label.x) * widthDelta); |
|
} |
|
} |
|
if (!label.isAnchorTop()) { |
|
if (label.isAnchorBottom()) { |
|
label.setY(bounds.height() - (oldBounds.height() - label.oldY)); |
|
} else { |
|
label.setY((label.position?label.position.y:label.y) * heightDelta); |
|
} |
|
} |
|
|
|
// If there is an position, |
|
// set the origin position as well |
|
if (label.position){ |
|
if (!label.isOriginAnchorLeft()) { |
|
if (label.isOriginAnchorRight()) { |
|
label.setOriginX(bounds.width() - (oldBounds.width() - label.oldX)) |
|
} else { |
|
label.setOriginX(label.x * widthDelta); |
|
} |
|
} |
|
if (!label.isOriginAnchorTop()) { |
|
if (label.isOriginAnchorBottom()) { |
|
label.setOriginY(bounds.height() - (oldBounds.height() - label.oldY)); |
|
} else { |
|
label.setOriginY(label.y * heightDelta); |
|
} |
|
} |
|
} |
|
}); |
|
|
|
//update docker |
|
var docker = this.dockers[0]; |
|
if (docker) { |
|
docker.bounds.unregisterCallback(this._dockerChangedCallback); |
|
if (!this._dockerUpdated) { |
|
docker.bounds.centerMoveTo(this.bounds.center()); |
|
this._dockerUpdated = false; |
|
} |
|
|
|
docker.update(); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
} |
|
this.isResized = false; |
|
} |
|
|
|
this.refresh(); |
|
|
|
this.isChanged = false; |
|
|
|
this._oldBounds = this.bounds.clone(); |
|
} |
|
|
|
this.children.each(function(value) { |
|
if(!(value instanceof ORYX.Core.Controls.Docker)) { |
|
value._update(); |
|
} |
|
}); |
|
|
|
if (this.dockers.length > 0&&!this.dockers.first().getDockedShape()) { |
|
this.dockers.each(function(docker){ |
|
docker.bounds.centerMoveTo(this.bounds.center()) |
|
}.bind(this)) |
|
} |
|
|
|
/*this.incoming.each((function(edge) { |
|
if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node)) |
|
edge._update(true); |
|
}).bind(this)); |
|
|
|
this.outgoing.each((function(edge) { |
|
if(!(this.dockers[0] && this.dockers[0].getDockedShape() instanceof ORYX.Core.Node)) |
|
edge._update(true); |
|
}).bind(this)); */ |
|
}, |
|
|
|
/** |
|
* This method repositions and resizes the SVG representation |
|
* of the shape. |
|
*/ |
|
refresh: function(){ |
|
arguments.callee.$.refresh.apply(this, arguments); |
|
|
|
/** Movement */ |
|
var x = this.bounds.upperLeft().x; |
|
var y = this.bounds.upperLeft().y; |
|
|
|
// Move owner element |
|
this.node.firstChild.setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")"); |
|
// Move magnets |
|
this.node.childNodes[1].childNodes[1].setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")"); |
|
|
|
/** Resize */ |
|
|
|
//iterate over all relevant svg elements and update them |
|
this._svgShapes.each(function(svgShape){ |
|
svgShape.update(); |
|
}); |
|
}, |
|
|
|
_dockerChanged: function(){ |
|
var docker = this.dockers[0]; |
|
|
|
//set the bounds of the the association |
|
this.bounds.centerMoveTo(docker.bounds.center()); |
|
|
|
this._dockerUpdated = true; |
|
//this._update(true); |
|
}, |
|
|
|
/** |
|
* This method traverses a tree of SVGElements and returns |
|
* all SVGShape objects. For each basic shape or path element |
|
* a SVGShape object is initialized. |
|
* |
|
* @param svgNode {SVGElement} |
|
* @return {Array} Array of SVGShape objects |
|
*/ |
|
_initSVGShapes: function(svgNode){ |
|
var svgShapes = []; |
|
try { |
|
var svgShape = new ORYX.Core.SVG.SVGShape(svgNode); |
|
svgShapes.push(svgShape); |
|
} |
|
catch (e) { |
|
//do nothing |
|
} |
|
|
|
if (svgNode.hasChildNodes()) { |
|
for (var i = 0; i < svgNode.childNodes.length; i++) { |
|
svgShapes = svgShapes.concat(this._initSVGShapes(svgNode.childNodes[i])); |
|
} |
|
} |
|
|
|
return svgShapes; |
|
}, |
|
|
|
/** |
|
* Calculate if the point is inside the Shape |
|
* @param {PointX} |
|
* @param {PointY} |
|
* @param {absoluteBounds} optional: for performance |
|
*/ |
|
isPointIncluded: function(pointX, pointY, absoluteBounds){ |
|
// If there is an arguments with the absoluteBounds |
|
var absBounds = absoluteBounds && absoluteBounds instanceof ORYX.Core.Bounds ? absoluteBounds : this.absoluteBounds(); |
|
|
|
if (!absBounds.isIncluded(pointX, pointY)) { |
|
return false; |
|
} else { |
|
|
|
} |
|
|
|
|
|
//point = Object.clone(point); |
|
var ul = absBounds.upperLeft(); |
|
var x = pointX - ul.x; |
|
var y = pointY - ul.y; |
|
|
|
var i=0; |
|
do { |
|
var isPointIncluded = this._svgShapes[i++].isPointIncluded( x, y ); |
|
} while( !isPointIncluded && i < this._svgShapes.length) |
|
|
|
return isPointIncluded; |
|
|
|
/*return this._svgShapes.any(function(svgShape){ |
|
return svgShape.isPointIncluded(point); |
|
});*/ |
|
}, |
|
|
|
|
|
/** |
|
* Calculate if the point is over an special offset area |
|
* @param {Point} |
|
*/ |
|
isPointOverOffset: function( pointX, pointY ){ |
|
var isOverEl = arguments.callee.$.isPointOverOffset.apply( this , arguments ); |
|
|
|
if (isOverEl) { |
|
|
|
// If there is an arguments with the absoluteBounds |
|
var absBounds = this.absoluteBounds(); |
|
absBounds.widen( - ORYX.CONFIG.BORDER_OFFSET ); |
|
|
|
if ( !absBounds.isIncluded( pointX, pointY )) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
|
|
}, |
|
|
|
serialize: function(){ |
|
var result = arguments.callee.$.serialize.apply(this); |
|
|
|
// Add the docker's bounds |
|
// nodes only have at most one docker! |
|
this.dockers.each((function(docker){ |
|
if (docker.getDockedShape()) { |
|
var center = docker.referencePoint; |
|
center = center ? center : docker.bounds.center(); |
|
result.push({ |
|
name: 'docker', |
|
prefix: 'oryx', |
|
value: $H(center).values().join(','), |
|
type: 'literal' |
|
}); |
|
} |
|
}).bind(this)); |
|
|
|
// Get the spezific serialized object from the stencil |
|
try { |
|
//result = this.getStencil().serialize(this, result); |
|
|
|
var serializeEvent = this.getStencil().serialize(); |
|
|
|
/* |
|
* call serialize callback by reference, result should be found |
|
* in serializeEvent.result |
|
*/ |
|
if(serializeEvent.type) { |
|
serializeEvent.shape = this; |
|
serializeEvent.data = result; |
|
serializeEvent.result = undefined; |
|
serializeEvent.forceExecution = true; |
|
|
|
this._delegateEvent(serializeEvent); |
|
|
|
if(serializeEvent.result) { |
|
result = serializeEvent.result; |
|
} |
|
} |
|
} |
|
catch (e) { |
|
} |
|
return result; |
|
}, |
|
|
|
deserialize: function(data){ |
|
arguments.callee.$.deserialize.apply(this, arguments); |
|
|
|
try { |
|
//data = this.getStencil().deserialize(this, data); |
|
|
|
var deserializeEvent = this.getStencil().deserialize(); |
|
|
|
/* |
|
* call serialize callback by reference, result should be found |
|
* in serializeEventInfo.result |
|
*/ |
|
if(deserializeEvent.type) { |
|
deserializeEvent.shape = this; |
|
deserializeEvent.data = data; |
|
deserializeEvent.result = undefined; |
|
deserializeEvent.forceExecution = true; |
|
|
|
this._delegateEvent(deserializeEvent); |
|
if(deserializeEvent.result) { |
|
data = deserializeEvent.result; |
|
} |
|
} |
|
} |
|
catch (e) { |
|
} |
|
|
|
// Set the outgoing shapes |
|
var outgoing = data.findAll(function(ser){ return (ser.prefix+"-"+ser.name) == 'raziel-outgoing'}); |
|
outgoing.each((function(obj){ |
|
// TODO: Look at Canvas |
|
if(!this.parent) {return}; |
|
|
|
// Set outgoing Shape |
|
var next = this.getCanvas().getChildShapeByResourceId(obj.value); |
|
|
|
if(next){ |
|
if(next instanceof ORYX.Core.Edge) { |
|
//Set the first docker of the next shape |
|
next.dockers.first().setDockedShape(this); |
|
next.dockers.first().setReferencePoint(next.dockers.first().bounds.center()); |
|
} else if(next.dockers.length > 0) { //next is a node and next has a docker |
|
next.dockers.first().setDockedShape(this); |
|
//next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0}); |
|
} |
|
} |
|
|
|
}).bind(this)); |
|
|
|
if (this.dockers.length === 1) { |
|
var dockerPos; |
|
dockerPos = data.find(function(entry){ |
|
return (entry.prefix + "-" + entry.name === "oryx-dockers"); |
|
}); |
|
|
|
if (dockerPos) { |
|
var points = dockerPos.value.replace(/,/g, " ").split(" ").without("").without("#"); |
|
if (points.length === 2 && this.dockers[0].getDockedShape()) { |
|
this.dockers[0].setReferencePoint({ |
|
x: parseFloat(points[0]), |
|
y: parseFloat(points[1]) |
|
}); |
|
} |
|
else { |
|
this.dockers[0].bounds.centerMoveTo(parseFloat(points[0]), parseFloat(points[1])); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* This method excepts the SVGDoucment that is the SVG representation |
|
* of this shape. |
|
* The bounds of the shape are calculated, the SVG representation's upper left point |
|
* is moved to 0,0 and it the method sets if this shape is resizable. |
|
* |
|
* @param {SVGDocument} svgDocument |
|
*/ |
|
_init: function(svgDocument){ |
|
arguments.callee.$._init.apply(this, arguments); |
|
|
|
var svgNode = svgDocument.getElementsByTagName("g")[0]; //outer most g node |
|
// set all required attributes |
|
var attributeTitle = svgDocument.ownerDocument.createAttribute("title"); |
|
attributeTitle.nodeValue = this.getStencil().title(); |
|
svgNode.setAttributeNode(attributeTitle); |
|
|
|
var attributeId = svgDocument.ownerDocument.createAttribute("id"); |
|
attributeId.nodeValue = this.id; |
|
svgNode.setAttributeNode(attributeId); |
|
|
|
// |
|
var stencilTargetNode = this.node.childNodes[0].childNodes[0]; //<g class=me>" |
|
svgNode = stencilTargetNode.appendChild(svgNode); |
|
|
|
// Add to the EventHandler |
|
this.addEventHandlers(svgNode.parentNode); |
|
|
|
/**set minimum and maximum size*/ |
|
var minSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "minimumSize"); |
|
if (minSizeAttr) { |
|
minSizeAttr = minSizeAttr.replace("/,/g", " "); |
|
var minSizeValues = minSizeAttr.split(" "); |
|
minSizeValues = minSizeValues.without(""); |
|
|
|
if (minSizeValues.length > 1) { |
|
this.minimumSize = { |
|
width: parseFloat(minSizeValues[0]), |
|
height: parseFloat(minSizeValues[1]) |
|
}; |
|
} |
|
else { |
|
//set minimumSize to (1,1), so that width and height of the stencil can never be (0,0) |
|
this.minimumSize = { |
|
width: 1, |
|
height: 1 |
|
}; |
|
} |
|
} |
|
|
|
var maxSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "maximumSize"); |
|
if (maxSizeAttr) { |
|
maxSizeAttr = maxSizeAttr.replace("/,/g", " "); |
|
var maxSizeValues = maxSizeAttr.split(" "); |
|
maxSizeValues = maxSizeValues.without(""); |
|
|
|
if (maxSizeValues.length > 1) { |
|
this.maximumSize = { |
|
width: parseFloat(maxSizeValues[0]), |
|
height: parseFloat(maxSizeValues[1]) |
|
}; |
|
} |
|
} |
|
|
|
if (this.minimumSize && this.maximumSize && |
|
(this.minimumSize.width > this.maximumSize.width || |
|
this.minimumSize.height > this.maximumSize.height)) { |
|
|
|
//TODO wird verschluckt!!! |
|
throw this + ": Minimum Size must be greater than maxiumSize."; |
|
} |
|
|
|
/**get current bounds and adjust it to upperLeft == (0,0)*/ |
|
//initialize all SVGShape objects |
|
this._svgShapes = this._initSVGShapes(svgNode); |
|
|
|
//get upperLeft and lowerRight of stencil |
|
var upperLeft = { |
|
x: undefined, |
|
y: undefined |
|
}; |
|
var lowerRight = { |
|
x: undefined, |
|
y: undefined |
|
}; |
|
var me = this; |
|
this._svgShapes.each(function(svgShape){ |
|
upperLeft.x = (upperLeft.x !== undefined) ? Math.min(upperLeft.x, svgShape.x) : svgShape.x; |
|
upperLeft.y = (upperLeft.y !== undefined) ? Math.min(upperLeft.y, svgShape.y) : svgShape.y; |
|
lowerRight.x = (lowerRight.x !== undefined) ? Math.max(lowerRight.x, svgShape.x + svgShape.width) : svgShape.x + svgShape.width; |
|
lowerRight.y = (lowerRight.y !== undefined) ? Math.max(lowerRight.y, svgShape.y + svgShape.height) : svgShape.y + svgShape.height; |
|
|
|
/** set if resizing is enabled */ |
|
//TODO isResizable durch die beiden anderen booleans ersetzen? |
|
if (svgShape.isHorizontallyResizable) { |
|
me.isHorizontallyResizable = true; |
|
me.isResizable = true; |
|
} |
|
if (svgShape.isVerticallyResizable) { |
|
me.isVerticallyResizable = true; |
|
me.isResizable = true; |
|
} |
|
if (svgShape.anchorTop && svgShape.anchorBottom) { |
|
me.isVerticallyResizable = true; |
|
me.isResizable = true; |
|
} |
|
if (svgShape.anchorLeft && svgShape.anchorRight) { |
|
me.isHorizontallyResizable = true; |
|
me.isResizable = true; |
|
} |
|
}); |
|
|
|
//move all SVGShapes by -upperLeft |
|
this._svgShapes.each(function(svgShape){ |
|
svgShape.x -= upperLeft.x; |
|
svgShape.y -= upperLeft.y; |
|
svgShape.update(); |
|
}); |
|
|
|
//set bounds of shape |
|
//the offsets are also needed for positioning the magnets and the docker |
|
var offsetX = upperLeft.x; |
|
var offsetY = upperLeft.y; |
|
|
|
lowerRight.x -= offsetX; |
|
lowerRight.y -= offsetY; |
|
upperLeft.x = 0; |
|
upperLeft.y = 0; |
|
|
|
//prevent that width or height of initial bounds is 0 |
|
if (lowerRight.x === 0) { |
|
lowerRight.x = 1; |
|
} |
|
if (lowerRight.y === 0) { |
|
lowerRight.y = 1; |
|
} |
|
|
|
this._oldBounds.set(upperLeft, lowerRight); |
|
this.bounds.set(upperLeft, lowerRight); |
|
|
|
/**initialize magnets */ |
|
|
|
var magnets = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "magnets"); |
|
|
|
if (magnets && magnets.length > 0) { |
|
|
|
magnets = $A(magnets[0].getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "magnet")); |
|
|
|
var me = this; |
|
magnets.each(function(magnetElem){ |
|
var magnet = new ORYX.Core.Controls.Magnet({ |
|
eventHandlerCallback: me.eventHandlerCallback |
|
}); |
|
var cx = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cx")); |
|
var cy = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cy")); |
|
magnet.bounds.centerMoveTo({ |
|
x: cx - offsetX, |
|
y: cy - offsetY |
|
}); |
|
|
|
//get anchors |
|
var anchors = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "anchors"); |
|
if (anchors) { |
|
anchors = anchors.replace("/,/g", " "); |
|
anchors = anchors.split(" ").without(""); |
|
for(var i = 0; i < anchors.length; i++) { |
|
switch(anchors[i].toLowerCase()) { |
|
case "left": |
|
magnet.anchorLeft = true; |
|
break; |
|
case "right": |
|
magnet.anchorRight = true; |
|
break; |
|
case "top": |
|
magnet.anchorTop = true; |
|
break; |
|
case "bottom": |
|
magnet.anchorBottom = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
me.add(magnet); |
|
|
|
//check, if magnet is default magnet |
|
if (!this._defaultMagnet) { |
|
var defaultAttr = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "default"); |
|
if (defaultAttr && defaultAttr.toLowerCase() === "yes") { |
|
me._defaultMagnet = magnet; |
|
} |
|
} |
|
}); |
|
} |
|
else { |
|
// Add a Magnet in the Center of Shape |
|
var magnet = new ORYX.Core.Controls.Magnet(); |
|
magnet.bounds.centerMoveTo(this.bounds.width() / 2, this.bounds.height() / 2); |
|
this.add(magnet); |
|
} |
|
|
|
/**initialize docker */ |
|
var dockerElem = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "docker"); |
|
|
|
if (dockerElem && dockerElem.length > 0) { |
|
dockerElem = dockerElem[0]; |
|
var docker = this.createDocker(); |
|
var cx = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cx")); |
|
var cy = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cy")); |
|
docker.bounds.centerMoveTo({ |
|
x: cx - offsetX, |
|
y: cy - offsetY |
|
}); |
|
|
|
//get anchors |
|
var anchors = dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "anchors"); |
|
if (anchors) { |
|
anchors = anchors.replace("/,/g", " "); |
|
anchors = anchors.split(" ").without(""); |
|
|
|
for(var i = 0; i < anchors.length; i++) { |
|
switch(anchors[i].toLowerCase()) { |
|
case "left": |
|
docker.anchorLeft = true; |
|
break; |
|
case "right": |
|
docker.anchorRight = true; |
|
break; |
|
case "top": |
|
docker.anchorTop = true; |
|
break; |
|
case "bottom": |
|
docker.anchorBottom = true; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/**initialize labels*/ |
|
var textElems = svgNode.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text'); |
|
$A(textElems).each((function(textElem){ |
|
var label = new ORYX.Core.SVG.Label({ |
|
textElement: textElem, |
|
shapeId: this.id |
|
}); |
|
label.x -= offsetX; |
|
label.y -= offsetY; |
|
this._labels[label.id] = label; |
|
|
|
label.registerOnChange(this.layout.bind(this)); |
|
|
|
// Only apply fitting on form-components |
|
if(this._stencil.id().indexOf(ORYX.CONFIG.FORM_ELEMENT_ID_PREFIX) == 0) { |
|
label.registerOnChange(this.fitToLabels.bind(this)); |
|
} |
|
|
|
}).bind(this)); |
|
}, |
|
|
|
fitToLabels: function() { |
|
var y = 0; |
|
|
|
this.getLabels().each(function(label){ |
|
var lr = label.getY() + label.getHeight(); |
|
if(lr > y) { |
|
y = lr; |
|
} |
|
}); |
|
|
|
var bounds = this.bounds; |
|
var boundsChanged = false; |
|
|
|
if(this.minimumSize) { |
|
// Check if y-value exceeds the min-value. If not, stick to this value. |
|
var minHeight = this.minimumSize.height; |
|
if(y < minHeight && bounds.height() > minHeight && minHeight > this.forcedHeight) { |
|
bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + minHeight); |
|
boundsChanged = true; |
|
} else if(y > minHeight && bounds.height() != y && y > this.forcedHeight){ |
|
bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + y); |
|
boundsChanged = true; |
|
} else if(bounds.height() > this.forcedHeight && this.forcedHeight > 0) { |
|
bounds.set(bounds.upperLeft().x, bounds.upperLeft().y, bounds.lowerRight().x, bounds.upperLeft().y + this.forcedHeight); |
|
boundsChanged = true; |
|
} |
|
} |
|
|
|
if(boundsChanged) { |
|
// Force facade to re-layout since bounds are changed AFTER layout has been performed |
|
if(this.facade.getCanvas() != null) { |
|
this.facade.getCanvas().update(); |
|
} |
|
|
|
// Re-select if needed to force the select |
|
if(this.facade.getSelection().member(this)) { |
|
var selectedNow = this.facade.getSelection(); |
|
this.facade.setSelection([]); |
|
this.facade.setSelection(selectedNow); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Override the Method, that a docker is not shown |
|
* |
|
*/ |
|
createDocker: function() { |
|
var docker = new ORYX.Core.Controls.Docker({eventHandlerCallback: this.eventHandlerCallback}); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
|
|
this.dockers.push( docker ); |
|
docker.parent = this; |
|
docker.bounds.registerCallback(this._changedCallback); |
|
|
|
return docker |
|
}, |
|
|
|
toString: function(){ |
|
return this._stencil.title() + " " + this.id |
|
} |
|
}; |
|
ORYX.Core.Node = ORYX.Core.Shape.extend(ORYX.Core.Node); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
NAMESPACE_SVG = "http://www.w3.org/2000/svg"; |
|
NAMESPACE_ORYX = "http://www.b3mn.org/oryx"; |
|
|
|
|
|
/** |
|
* Init namespaces |
|
*/ |
|
if (!ORYX) { |
|
var ORYX = {}; |
|
} |
|
if (!ORYX.Core) { |
|
ORYX.Core = {}; |
|
} |
|
|
|
|
|
/** |
|
* @classDescription Abstract base class for all connections. |
|
* @extends {ORYX.Core.Shape} |
|
* @param options {Object} |
|
* |
|
* TODO da die verschiebung der Edge nicht ueber eine |
|
* translation gemacht wird, die sich auch auf alle kind UIObjects auswirkt, |
|
* muessen die kinder hier beim verschieben speziell betrachtet werden. |
|
* Das sollte ueberarbeitet werden. |
|
* |
|
*/ |
|
ORYX.Core.Edge = { |
|
/** |
|
* Constructor |
|
* @param {Object} options |
|
* @param {Stencil} stencil |
|
*/ |
|
construct: function(options, stencil, facade){ |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.isMovable = true; |
|
this.isSelectable = true; |
|
|
|
this._dockerUpdated = false; |
|
|
|
this._markers = new Hash(); //a hash map of SVGMarker objects where keys are the marker ids |
|
this._paths = []; |
|
this._interactionPaths = []; |
|
this._dockersByPath = new Hash(); |
|
this._markersByPath = new Hash(); |
|
|
|
/* Data structures to store positioning information of attached child nodes */ |
|
this.attachedNodePositionData = new Hash(); |
|
|
|
//TODO was muss hier initial erzeugt werden? |
|
var stencilNode = this.node.childNodes[0].childNodes[0]; |
|
stencilNode = ORYX.Editor.graft("http://www.w3.org/2000/svg", stencilNode, ['g', { |
|
"pointer-events": "painted" |
|
}]); |
|
|
|
//Add to the EventHandler |
|
this.addEventHandlers(stencilNode.parentNode); |
|
|
|
|
|
this._oldBounds = this.bounds.clone(); |
|
|
|
//load stencil |
|
this._init(this._stencil.view()); |
|
|
|
if (stencil instanceof Array) { |
|
this.deserialize(stencil); |
|
} |
|
|
|
}, |
|
|
|
_update: function(force){ |
|
if(this._dockerUpdated || this.isChanged || force) { |
|
|
|
this.dockers.invoke("update"); |
|
|
|
if (false && (this.bounds.width() === 0 || this.bounds.height() === 0)) { |
|
var width = this.bounds.width(); |
|
var height = this.bounds.height(); |
|
this.bounds.extend({ |
|
x: width === 0 ? 2 : 0, |
|
y: height === 0 ? 2 : 0 |
|
}); |
|
this.bounds.moveBy({ |
|
x: width === 0 ? -1 : 0, |
|
y: height === 0 ? -1 : 0 |
|
}); |
|
|
|
} |
|
|
|
// TODO: Bounds muss abhaengig des Eltern-Shapes gesetzt werden |
|
var upL = this.bounds.upperLeft(); |
|
var oldUpL = this._oldBounds.upperLeft(); |
|
var oldWidth = this._oldBounds.width() === 0 ? this.bounds.width() : this._oldBounds.width(); |
|
var oldHeight = this._oldBounds.height() === 0 ? this.bounds.height() : this._oldBounds.height(); |
|
var diffX = upL.x - oldUpL.x; |
|
var diffY = upL.y - oldUpL.y; |
|
var diffWidth = (this.bounds.width() / oldWidth) || 1; |
|
var diffHeight = (this.bounds.height() / oldHeight) || 1; |
|
|
|
this.dockers.each((function(docker){ |
|
// Unregister on BoundsChangedCallback |
|
docker.bounds.unregisterCallback(this._dockerChangedCallback); |
|
|
|
// If there is any changes at the edge and is there is not an DockersUpdate |
|
// set the new bounds to the docker |
|
if (!this._dockerUpdated) { |
|
docker.bounds.moveBy(diffX, diffY); |
|
|
|
if (diffWidth !== 1 || diffHeight !== 1) { |
|
var relX = docker.bounds.upperLeft().x - upL.x; |
|
var relY = docker.bounds.upperLeft().y - upL.y; |
|
|
|
docker.bounds.moveTo(upL.x + relX * diffWidth, upL.y + relY * diffHeight); |
|
} |
|
} |
|
// Do Docker update and register on DockersBoundChange |
|
docker.update(); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
|
|
}).bind(this)); |
|
|
|
if (this._dockerUpdated) { |
|
var a = this.dockers.first().bounds.center(); |
|
var b = this.dockers.first().bounds.center(); |
|
|
|
this.dockers.each((function(docker){ |
|
var center = docker.bounds.center(); |
|
a.x = Math.min(a.x, center.x); |
|
a.y = Math.min(a.y, center.y); |
|
b.x = Math.max(b.x, center.x); |
|
b.y = Math.max(b.y, center.y); |
|
}).bind(this)); |
|
|
|
//set the bounds of the the association |
|
this.bounds.set(Object.clone(a), Object.clone(b)); |
|
} |
|
|
|
upL = this.bounds.upperLeft(); oldUpL = this._oldBounds.upperLeft(); |
|
diffWidth = (this.bounds.width() / (oldWidth||this.bounds.width())); diffHeight = (this.bounds.height() / (oldHeight||this.bounds.height())); |
|
diffX = upL.x - oldUpL.x; diffY = upL.y - oldUpL.y; |
|
|
|
//reposition labels |
|
this.getLabels().each(function(label) { |
|
|
|
if (label.getReferencePoint()){ |
|
var ref = label.getReferencePoint(); |
|
var from = ref.segment.from, to = ref.segment.to; |
|
if (!from || !from.parent || !to || !to.parent) { |
|
return; |
|
} |
|
|
|
var fromPosition = from.bounds.center(), toPosition = to.bounds.center(); |
|
|
|
if (fromPosition.x === ref.segment.fromPosition.x && fromPosition.y === ref.segment.fromPosition.y && |
|
toPosition.x === ref.segment.toPosition.x && toPosition.y === ref.segment.toPosition.y && !ref.dirty){ |
|
return; |
|
} |
|
|
|
if (!this.parent.initializingShapes) { |
|
var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(ref.segment.fromPosition, ref.segment.toPosition, ref.intersection); |
|
var newIntersection = ORYX.Core.Math.getPointBetweenTwoPoints(fromPosition, toPosition, isNaN(oldDistance) ? 0.5 : oldDistance); |
|
|
|
/** |
|
* Set position |
|
*/ |
|
// Get the orthogonal identity vector of the current segment |
|
var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition); |
|
var isHor = Math.abs(oiv.y)===1, isVer = Math.abs(oiv.x)===1; |
|
oiv.x *= ref.distance; oiv.y *= ref.distance; // vector * distance |
|
oiv.x += newIntersection.x; oiv.y += newIntersection.y; // vector + the intersection point |
|
var mx = isHor && ref.orientation && (ref.iorientation||ref.orientation).endsWith("r") ? -label.getWidth() : 0; |
|
var my = isVer && ref.orientation && (ref.iorientation||ref.orientation).startsWith("l") ? -label.getHeight()+2 : 0; |
|
label.setX(oiv.x+mx); label.setY(oiv.y+my); |
|
|
|
// Update the reference point |
|
this.updateReferencePointOfLabel(label, newIntersection, from, to); |
|
} else { |
|
var oiv = ORYX.Core.Math.getOrthogonalIdentityVector(fromPosition, toPosition); |
|
oiv.x *= ref.distance; oiv.y *= ref.distance; // vector * distance |
|
oiv.x += ref.intersection.x; oiv.y += ref.intersection.y; // vector + the intersection point |
|
label.setX(oiv.x); label.setY(oiv.y); |
|
ref.segment.fromPosition = fromPosition; ref.segment.toPosition = toPosition; |
|
} |
|
|
|
return; |
|
} |
|
|
|
// Update label position if no reference point is set |
|
if (label.position && !this.parent.initializingShapes){ |
|
var x = label.position.x + (diffX * (diffWidth||1)); |
|
if (x > this.bounds.lowerRight().x){ |
|
x += this.bounds.width()-(this.bounds.width()/(diffWidth||1)); |
|
} |
|
|
|
var y = label.position.y + (diffY * (diffHeight||1)); |
|
if (y > this.bounds.lowerRight().y){ |
|
y += this.bounds.height()-(this.bounds.height()/(diffHeight||1)); |
|
} |
|
label.setX(x);label.setY(y); |
|
return; |
|
} |
|
|
|
switch (label.getEdgePosition()) { |
|
case "starttop": |
|
var angle = this._getAngle(this.dockers[0], this.dockers[1]); |
|
var pos = this.dockers.first().bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x + label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x - label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
|
|
break; |
|
|
|
case "startmiddle": |
|
var angle = this._getAngle(this.dockers[0], this.dockers[1]); |
|
var pos = this.dockers.first().bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x + 2; |
|
label.y = pos.y + 4; |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x + 1; |
|
label.y = pos.y + 4; |
|
label.rotate(180 - angle, pos); |
|
} |
|
|
|
break; |
|
|
|
case "startbottom": |
|
var angle = this._getAngle(this.dockers[0], this.dockers[1]); |
|
var pos = this.dockers.first().bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x + label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x - label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
|
|
break; |
|
case "midtop": |
|
var numOfDockers = this.dockers.length; |
|
if(numOfDockers%2 == 0) { |
|
var angle = this._getAngle(this.dockers[numOfDockers/2-1], this.dockers[numOfDockers/2]) |
|
var pos1 = this.dockers[numOfDockers/2-1].bounds.center(); |
|
var pos2 = this.dockers[numOfDockers/2].bounds.center(); |
|
var pos = {x:(pos1.x + pos2.x)/2.0, y:(pos1.y+pos2.y)/2.0}; |
|
|
|
label.horizontalAlign("center"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x; |
|
label.y = pos.y - label.getOffsetTop(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.rotate(180 - angle, pos); |
|
} |
|
} else { |
|
var index = parseInt(numOfDockers/2); |
|
var angle = this._getAngle(this.dockers[index], this.dockers[index+1]) |
|
var pos = this.dockers[index].bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x + label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x - label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
} |
|
|
|
break; |
|
case "midbottom": |
|
var numOfDockers = this.dockers.length; |
|
if(numOfDockers%2 == 0) { |
|
var angle = this._getAngle(this.dockers[numOfDockers/2-1], this.dockers[numOfDockers/2]) |
|
var pos1 = this.dockers[numOfDockers/2-1].bounds.center(); |
|
var pos2 = this.dockers[numOfDockers/2].bounds.center(); |
|
var pos = {x:(pos1.x + pos2.x)/2.0, y:(pos1.y+pos2.y)/2.0}; |
|
|
|
label.horizontalAlign("center"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x; |
|
label.y = pos.y + label.getOffsetTop(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.rotate(180 - angle, pos); |
|
} |
|
} else { |
|
var index = parseInt(numOfDockers/2); |
|
var angle = this._getAngle(this.dockers[index], this.dockers[index+1]) |
|
var pos = this.dockers[index].bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x + label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x - label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
} |
|
|
|
break; |
|
case "endtop": |
|
var length = this.dockers.length; |
|
var angle = this._getAngle(this.dockers[length-2], this.dockers[length-1]); |
|
var pos = this.dockers.last().bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x - label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
label.x = pos.x + label.getOffsetTop(); |
|
label.y = pos.y - label.getOffsetTop(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
|
|
break; |
|
case "endbottom": |
|
var length = this.dockers.length; |
|
var angle = this._getAngle(this.dockers[length-2], this.dockers[length-1]); |
|
var pos = this.dockers.last().bounds.center(); |
|
|
|
if (angle <= 90 || angle > 270) { |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x - label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(360 - angle, pos); |
|
} else { |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top"); |
|
label.x = pos.x + label.getOffsetBottom(); |
|
label.y = pos.y + label.getOffsetBottom(); |
|
label.rotate(180 - angle, pos); |
|
} |
|
|
|
break; |
|
} |
|
}.bind(this)); |
|
|
|
this.children.each(function(value) { |
|
if(value instanceof ORYX.Core.Node) { |
|
this.calculatePositionOfAttachedChildNode.call(this, value); |
|
} |
|
}.bind(this)); |
|
|
|
this.refreshAttachedNodes(); |
|
this.refresh(); |
|
|
|
this.isChanged = false; |
|
this._dockerUpdated = false; |
|
|
|
this._oldBounds = this.bounds.clone(); |
|
} |
|
|
|
|
|
// IE10 specific fix, start and end-markes get left behind when moving path |
|
var userAgent = navigator.userAgent; |
|
if (navigator.appVersion.indexOf("MSIE 10") !== -1 || (userAgent.indexOf('Trident') !== -1 && userAgent.indexOf('rv:11') !== -1)) |
|
{ |
|
this.node.parentNode.insertBefore(this.node, this.node); |
|
} |
|
}, |
|
|
|
/** |
|
* Moves a point to the upperLeft of a node's bounds. |
|
* |
|
* @param {point} point |
|
* The point to move |
|
* @param {ORYX.Core.Bounds} bounds |
|
* The Bounds of the related noe |
|
*/ |
|
movePointToUpperLeftOfNode: function(point, bounds) { |
|
point.x -= bounds.width()/2; |
|
point.y -= bounds.height()/2; |
|
}, |
|
|
|
/** |
|
* Refreshes the visual representation of edge's attached nodes. |
|
*/ |
|
refreshAttachedNodes: function() { |
|
this.attachedNodePositionData.values().each(function(nodeData) { |
|
var startPoint = nodeData.segment.docker1.bounds.center(); |
|
var endPoint = nodeData.segment.docker2.bounds.center(); |
|
this.relativizePoint(startPoint); |
|
this.relativizePoint(endPoint); |
|
|
|
var newNodePosition = new Object(); |
|
|
|
/* Calculate new x-coordinate */ |
|
newNodePosition.x = startPoint.x |
|
+ nodeData.relativDistanceFromDocker1 |
|
* (endPoint.x - startPoint.x); |
|
|
|
/* Calculate new y-coordinate */ |
|
newNodePosition.y = startPoint.y |
|
+ nodeData.relativDistanceFromDocker1 |
|
* (endPoint.y - startPoint.y); |
|
|
|
/* Convert new position to the upper left of the node */ |
|
this.movePointToUpperLeftOfNode(newNodePosition, nodeData.node.bounds); |
|
|
|
/* Move node to its new position */ |
|
nodeData.node.bounds.moveTo(newNodePosition); |
|
nodeData.node._update(); |
|
|
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Calculates the position of an edge's child node. The node is placed on |
|
* the path of the edge. |
|
* |
|
* @param {node} |
|
* The node to calculate the new position |
|
* @return {Point} |
|
* The calculated upper left point of the node's shape. |
|
*/ |
|
calculatePositionOfAttachedChildNode: function(node) { |
|
/* Initialize position */ |
|
var position = new Object(); |
|
position.x = 0; |
|
position.y = 0; |
|
|
|
/* Case: Node was just added */ |
|
if(!this.attachedNodePositionData[node.getId()]) { |
|
this.attachedNodePositionData[node.getId()] = new Object(); |
|
this.attachedNodePositionData[node.getId()] |
|
.relativDistanceFromDocker1 = 0; |
|
this.attachedNodePositionData[node.getId()].node = node; |
|
this.attachedNodePositionData[node.getId()].segment = new Object(); |
|
this.findEdgeSegmentForNode(node); |
|
}else if(node.isChanged) { |
|
this.findEdgeSegmentForNode(node); |
|
} |
|
|
|
|
|
|
|
}, |
|
|
|
/** |
|
* Finds the appropriate edge segement for a node. |
|
* The segment is choosen, which has the smallest distance to the node. |
|
* |
|
* @param {ORYX.Core.Node} node |
|
* The concerning node |
|
*/ |
|
findEdgeSegmentForNode: function(node) { |
|
var length = this.dockers.length; |
|
var smallestDistance = undefined; |
|
|
|
for(i=1;i<length;i++) { |
|
var lineP1 = this.dockers[i-1].bounds.center(); |
|
var lineP2 = this.dockers[i].bounds.center(); |
|
this.relativizePoint(lineP1); |
|
this.relativizePoint(lineP2); |
|
|
|
var nodeCenterPoint = node.bounds.center(); |
|
var distance = ORYX.Core.Math.distancePointLinie( |
|
lineP1, |
|
lineP2, |
|
nodeCenterPoint, |
|
true); |
|
|
|
if((distance || distance == 0) && ((!smallestDistance && smallestDistance != 0) |
|
|| distance < smallestDistance)) { |
|
|
|
smallestDistance = distance; |
|
|
|
this.attachedNodePositionData[node.getId()].segment.docker1 = |
|
this.dockers[i-1]; |
|
this.attachedNodePositionData[node.getId()].segment.docker2 = |
|
this.dockers[i]; |
|
|
|
} |
|
|
|
/* Either the distance does not match the segment or the distance |
|
* between docker1 and docker2 is 0 |
|
* |
|
* In this case choose the nearest docker as attaching point. |
|
* |
|
*/ |
|
if(!distance && !smallestDistance && smallestDistance != 0) { |
|
(ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP1) |
|
< ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP2)) ? |
|
this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 0 : |
|
this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 1; |
|
this.attachedNodePositionData[node.getId()].segment.docker1 = |
|
this.dockers[i-1]; |
|
this.attachedNodePositionData[node.getId()].segment.docker2 = |
|
this.dockers[i]; |
|
} |
|
} |
|
|
|
/* Calculate position on edge segment for the node */ |
|
if(smallestDistance || smallestDistance == 0) { |
|
this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = |
|
this.getLineParameterForPosition( |
|
this.attachedNodePositionData[node.getId()].segment.docker1, |
|
this.attachedNodePositionData[node.getId()].segment.docker2, |
|
node); |
|
} |
|
}, |
|
|
|
|
|
/** |
|
* |
|
* @param {ORYX.Core.Node|Object} node or position |
|
* @return {Object} An object with the following attribute: {ORYX.Core.Docker} fromDocker, {ORYX.Core.Docker} toDocker, {X/Y} position, {int} distance |
|
*/ |
|
findSegment: function(node){ |
|
|
|
var length = this.dockers.length; |
|
var result; |
|
|
|
var nodeCenterPoint = node instanceof ORYX.Core.UIObject ? node.bounds.center() : node; |
|
|
|
for (i = 1; i < length; i++) { |
|
var lineP1 = this.dockers[i - 1].bounds.center(); |
|
var lineP2 = this.dockers[i].bounds.center(); |
|
|
|
var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true); |
|
|
|
if (typeof distance == "number" && (result === undefined || distance < result.distance)) { |
|
result = { |
|
distance: distance, |
|
fromDocker: this.dockers[i - 1], |
|
toDocker: this.dockers[i] |
|
} |
|
|
|
} |
|
} |
|
return result; |
|
}, |
|
|
|
/** |
|
* Returns the value of the scalar to determine the position of the node on |
|
* line defined by docker1 and docker2. |
|
* |
|
* @param {point} docker1 |
|
* The docker that defines the start of the line segment |
|
* @param {point} docker2 |
|
* The docker that defines the end of the line segment |
|
* @param {ORYX.Core.Node} node |
|
* The concerning node |
|
* |
|
* @return {float} positionParameter |
|
* The scalar value to determine the position on the line |
|
*/ |
|
getLineParameterForPosition: function(docker1, docker2, node) { |
|
var dockerPoint1 = docker1.bounds.center(); |
|
var dockerPoint2 = docker2.bounds.center(); |
|
this.relativizePoint(dockerPoint1); |
|
this.relativizePoint(dockerPoint2); |
|
|
|
var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine( |
|
dockerPoint1, |
|
dockerPoint2, |
|
node.bounds.center(), true); |
|
if(!intersectionPoint) { |
|
return 0; |
|
} |
|
|
|
var relativeDistance = |
|
ORYX.Core.Math.getDistancePointToPoint(intersectionPoint, dockerPoint1) / |
|
ORYX.Core.Math.getDistancePointToPoint(dockerPoint1, dockerPoint2); |
|
|
|
return relativeDistance; |
|
}, |
|
/** |
|
* Makes point relative to the upper left of the edge's bound. |
|
* |
|
* @param {point} point |
|
* The point to relativize |
|
*/ |
|
relativizePoint: function(point) { |
|
point.x -= this.bounds.upperLeft().x; |
|
point.y -= this.bounds.upperLeft().y; |
|
}, |
|
|
|
/** |
|
* Move the first and last docker and calls the refresh method. |
|
* Attention: This does not calculates intersection point between the |
|
* edge and the bounded nodes. This only works if only the nodes are |
|
* moves. |
|
* |
|
*/ |
|
optimizedUpdate: function(){ |
|
|
|
var updateDocker = function(docker){ |
|
if (!docker._dockedShape || !docker._dockedShapeBounds) |
|
return; |
|
var off = { |
|
x: docker._dockedShape.bounds.a.x - docker._dockedShapeBounds.a.x, |
|
y: docker._dockedShape.bounds.a.y - docker._dockedShapeBounds.a.y |
|
}; |
|
docker.bounds.moveBy(off); |
|
docker._dockedShapeBounds.moveBy(off); |
|
} |
|
|
|
updateDocker(this.dockers.first()); |
|
updateDocker(this.dockers.last()); |
|
|
|
this.refresh(); |
|
}, |
|
|
|
refresh: function(){ |
|
//call base class refresh method |
|
arguments.callee.$.refresh.apply(this, arguments); |
|
|
|
//TODO consider points for marker mids |
|
var lastPoint; |
|
this._paths.each((function(path, index){ |
|
var dockers = this._dockersByPath[path.id]; |
|
var c = undefined; |
|
var d = undefined; |
|
if (lastPoint) { |
|
d = "M" + lastPoint.x + " " + lastPoint.y; |
|
} |
|
else { |
|
c = dockers[0].bounds.center(); |
|
lastPoint = c; |
|
|
|
d = "M" + c.x + " " + c.y; |
|
} |
|
|
|
for (var i = 1; i < dockers.length; i++) { |
|
// for each docker, draw a line to the center |
|
c = dockers[i].bounds.center(); |
|
d = d + "L" + c.x + " " + c.y + " "; |
|
lastPoint = c; |
|
} |
|
|
|
path.setAttributeNS(null, "d", d); |
|
this._interactionPaths[index].setAttributeNS(null, "d", d); |
|
|
|
}).bind(this)); |
|
|
|
|
|
/* move child shapes of an edge */ |
|
if(this.getChildNodes().length > 0) { |
|
var x = this.bounds.upperLeft().x; |
|
var y = this.bounds.upperLeft().y; |
|
|
|
this.node.firstChild.childNodes[1].setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")"); |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Calculate the Border Intersection Point between two points |
|
* @param {PointA} |
|
* @param {PointB} |
|
*/ |
|
getIntersectionPoint: function(){ |
|
|
|
var length = Math.floor(this.dockers.length / 2) |
|
|
|
return ORYX.Core.Math.midPoint(this.dockers[length - 1].bounds.center(), this.dockers[length].bounds.center()) |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the bounds is over the edge |
|
* @param {Bounds} |
|
* |
|
*/ |
|
isBoundsIncluded: function(bounds){ |
|
var dockers = this.dockers, size = dockers.length; |
|
return dockers.any(function(docker, i){ |
|
if (i == size-1){ return false; } |
|
var a = docker.bounds.center(); |
|
var b = dockers[i+1].bounds.center(); |
|
|
|
return ORYX.Core.Math.isRectOverLine(a.x, a.y, b.x, b.y, bounds.a.x, bounds.a.y, bounds.b.x, bounds.b.y); |
|
}); |
|
}, |
|
|
|
/** |
|
* Calculate if the point is inside the Shape |
|
* @param {PointX} |
|
* @param {PointY} |
|
*/ |
|
isPointIncluded: function(pointX, pointY){ |
|
|
|
var isbetweenAB = this.absoluteBounds().isIncluded(pointX, pointY, |
|
ORYX.CONFIG.OFFSET_EDGE_BOUNDS); |
|
|
|
var isPointIncluded = undefined; |
|
|
|
if (isbetweenAB && this.dockers.length > 0) { |
|
|
|
var i = 0; |
|
var point1, point2; |
|
|
|
|
|
do { |
|
|
|
point1 = this.dockers[i].bounds.center(); |
|
point2 = this.dockers[++i].bounds.center(); |
|
|
|
isPointIncluded = ORYX.Core.Math.isPointInLine(pointX, pointY, |
|
point1.x, point1.y, |
|
point2.x, point2.y, |
|
ORYX.CONFIG.OFFSET_EDGE_BOUNDS); |
|
|
|
} while (!isPointIncluded && i < this.dockers.length - 1) |
|
|
|
} |
|
|
|
return isPointIncluded; |
|
|
|
}, |
|
|
|
|
|
/** |
|
* Calculate if the point is over an special offset area |
|
* @param {Point} |
|
*/ |
|
isPointOverOffset: function(){ |
|
return false |
|
}, |
|
|
|
/** |
|
* Returns TRUE if the given node |
|
* is a child node of the shapes node |
|
* @param {Element} node |
|
* @return {Boolean} |
|
* |
|
*/ |
|
containsNode: function(node){ |
|
if (this._paths.include(node) || |
|
this._interactionPaths.include(node)){ |
|
return true; |
|
} |
|
return false; |
|
}, |
|
|
|
/** |
|
* Returns the angle of the line between two dockers |
|
* (0 - 359.99999999) |
|
*/ |
|
_getAngle: function(docker1, docker2) { |
|
var p1 = docker1 instanceof ORYX.Core.Controls.Docker ? docker1.absoluteCenterXY() : docker1; |
|
var p2 = docker2 instanceof ORYX.Core.Controls.Docker ? docker2.absoluteCenterXY() : docker2; |
|
|
|
return ORYX.Core.Math.getAngle(p1, p2); |
|
}, |
|
|
|
alignDockers: function(){ |
|
this._update(true); |
|
|
|
var firstPoint = this.dockers.first().bounds.center(); |
|
var lastPoint = this.dockers.last().bounds.center(); |
|
|
|
var deltaX = lastPoint.x - firstPoint.x; |
|
var deltaY = lastPoint.y - firstPoint.y; |
|
|
|
var numOfDockers = this.dockers.length - 1; |
|
|
|
this.dockers.each((function(docker, index){ |
|
var part = index / numOfDockers; |
|
docker.bounds.unregisterCallback(this._dockerChangedCallback); |
|
docker.bounds.moveTo(firstPoint.x + part * deltaX, firstPoint.y + part * deltaY); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
}).bind(this)); |
|
|
|
this._dockerChanged(); |
|
}, |
|
|
|
add: function(shape){ |
|
arguments.callee.$.add.apply(this, arguments); |
|
|
|
// If the new shape is a Docker which is not contained |
|
if (shape instanceof ORYX.Core.Controls.Docker && this.dockers.include(shape)){ |
|
// Add it to the dockers list ordered by paths |
|
var pathArray = this._dockersByPath.values()[0]; |
|
if (pathArray) { |
|
pathArray.splice(this.dockers.indexOf(shape), 0, shape); |
|
} |
|
|
|
/* Perform nessary adjustments on the edge's child shapes */ |
|
this.handleChildShapesAfterAddDocker(shape); |
|
} |
|
}, |
|
|
|
/** |
|
* Performs nessary adjustments on the edge's child shapes. |
|
* |
|
* @param {ORYX.Core.Controls.Docker} docker |
|
* The added docker |
|
*/ |
|
handleChildShapesAfterAddDocker: function(docker) { |
|
/* Ensure type of Docker */ |
|
if(!docker instanceof ORYX.Core.Controls.Docker) {return undefined;} |
|
|
|
var index = this.dockers.indexOf(docker); |
|
if(!(0 < index && index < this.dockers.length - 1)) { |
|
/* Exception: Expect added docker between first and last node of the edge */ |
|
return undefined; |
|
} |
|
|
|
/* Get child nodes concerning the segment of the new docker */ |
|
var startDocker = this.dockers[index-1]; |
|
var endDocker = this.dockers[index+1]; |
|
|
|
/* Adjust the position of edge's child nodes */ |
|
var segmentElements = |
|
this.getAttachedNodePositionDataForSegment(startDocker, endDocker); |
|
|
|
var lengthSegmentPart1 = ORYX.Core.Math.getDistancePointToPoint( |
|
startDocker.bounds.center(), |
|
docker.bounds.center()); |
|
var lengthSegmentPart2 = ORYX.Core.Math.getDistancePointToPoint( |
|
endDocker.bounds.center(), |
|
docker.bounds.center()); |
|
|
|
if(!(lengthSegmentPart1 + lengthSegmentPart2)) {return;} |
|
|
|
var relativDockerPosition = lengthSegmentPart1 / (lengthSegmentPart1 + lengthSegmentPart2); |
|
|
|
segmentElements.each(function(nodePositionData) { |
|
/* Assign child node to the new segment */ |
|
if(nodePositionData.value.relativDistanceFromDocker1 < relativDockerPosition) { |
|
/* Case: before added Docker */ |
|
nodePositionData.value.segment.docker2 = docker; |
|
nodePositionData.value.relativDistanceFromDocker1 = |
|
nodePositionData.value.relativDistanceFromDocker1 / relativDockerPosition; |
|
} else { |
|
/* Case: after added Docker */ |
|
nodePositionData.value.segment.docker1 = docker; |
|
var newFullDistance = 1 - relativDockerPosition; |
|
var relativPartOfSegment = |
|
nodePositionData.value.relativDistanceFromDocker1 |
|
- relativDockerPosition; |
|
|
|
nodePositionData.value.relativDistanceFromDocker1 = |
|
relativPartOfSegment / newFullDistance; |
|
|
|
} |
|
}) |
|
|
|
|
|
// Update all labels reference points |
|
this.getLabels().each(function(label){ |
|
|
|
var ref = label.getReferencePoint(); |
|
if (!ref) { |
|
return; |
|
} |
|
var index = this.dockers.indexOf(docker); |
|
if (index >= ref.segment.fromIndex && index <= ref.segment.toIndex){ |
|
|
|
var segment = this.findSegment(ref.intersection); |
|
if (!segment){ |
|
// Choose whether the first of the last segment |
|
segment.fromDocker = ref.segment.fromIndex >= (this.dockers.length/2) ? this.dockers[0] : this.dockers[this.dockers.length-2]; |
|
segment.toDocker = this.dockers[this.dockers.indexOf(from)+1]; // The next one if the to docker |
|
} |
|
|
|
var fromPosition = segment.fromDocker.bounds.center(), toPosition = segment.toDocker.bounds.center(); |
|
|
|
var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine( |
|
fromPosition, // P1 - Center of the first docker |
|
toPosition, // P2 - Center of the second docker |
|
ref.intersection, // P3 - Center of the label |
|
true); |
|
//var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(ref.segment.fromPosition, ref.segment.toPosition, ref.intersection); |
|
//intersection = ORYX.Core.Math.getPointBetweenTwoPoints(fromPosition, toPosition, isNaN(oldDistance) ? 0.5 : (lengthOld*oldDistance)/lengthNew); |
|
|
|
// Update the reference point |
|
this.updateReferencePointOfLabel(label, intersection, segment.fromDocker, segment.toDocker, true); |
|
} |
|
}.bind(this)); |
|
|
|
/* Update attached nodes visual representation */ |
|
this.refreshAttachedNodes(); |
|
}, |
|
|
|
/** |
|
* Returns elements from {@link attachedNodePositiondata} that match the |
|
* segement defined by startDocker and endDocker. |
|
* |
|
* @param {ORYX.Core.Controls.Docker} startDocker |
|
* The docker defining the begin of the segment. |
|
* @param {ORYX.Core.Controls.Docker} endDocker |
|
* The docker defining the begin of the segment. |
|
* |
|
* @return {Hash} attachedNodePositionData |
|
* Child elements matching the segment |
|
*/ |
|
getAttachedNodePositionDataForSegment: function(startDocker, endDocker) { |
|
/* Ensure that the segment is defined correctly */ |
|
if(!((startDocker instanceof ORYX.Core.Controls.Docker) |
|
&& (endDocker instanceof ORYX.Core.Controls.Docker))) { |
|
return []; |
|
} |
|
|
|
/* Get elements of the segment */ |
|
var elementsOfSegment = |
|
this.attachedNodePositionData.findAll(function(nodePositionData) { |
|
return nodePositionData.value.segment.docker1 === startDocker && |
|
nodePositionData.value.segment.docker2 === endDocker; |
|
}); |
|
|
|
/* Return a Hash in each case */ |
|
if(!elementsOfSegment) {return [];} |
|
|
|
return elementsOfSegment; |
|
}, |
|
|
|
/** |
|
* Removes an edge's child shape |
|
*/ |
|
remove: function(shape) { |
|
arguments.callee.$.remove.apply(this, arguments); |
|
|
|
if(this.attachedNodePositionData[shape.getId()]) { |
|
delete this.attachedNodePositionData[shape.getId()]; |
|
} |
|
|
|
/* Adjust child shapes if neccessary */ |
|
if(shape instanceof ORYX.Core.Controls.Docker) { |
|
this.handleChildShapesAfterRemoveDocker(shape); |
|
} |
|
}, |
|
|
|
updateReferencePointOfLabel: function(label, intersection, from, to, dirty){ |
|
if (!label.getReferencePoint() || !label.isVisible) { |
|
return; |
|
} |
|
|
|
var ref = label.getReferencePoint(); |
|
|
|
// |
|
if (ref.orientation && ref.orientation !== "ce"){ |
|
var angle = this._getAngle(from, to); |
|
if (ref.distance >= 0){ |
|
if(angle == 0){ |
|
label.horizontalAlign("left");//ref.orientation == "lr" ? "right" : "left"); |
|
label.verticalAlign("bottom"); |
|
} else if (angle > 0 && angle < 90){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
} else if (angle == 90){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top");//ref.orientation == "lr" ? "bottom" : "top"); |
|
} else if (angle > 90 && angle < 180){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top"); |
|
} else if (angle == 180){ |
|
label.horizontalAlign("left");//ref.orientation == "ur" ? "right" : "left"); |
|
label.verticalAlign("top"); |
|
} else if (angle > 180 && angle < 270){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top"); |
|
} else if (angle == 270){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top");//ref.orientation == "ll" ? "bottom" : "top"); |
|
} else if (angle > 270 && angle <= 360){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
} |
|
} else { |
|
if(angle == 0){ |
|
label.horizontalAlign("left");//ref.orientation == "ur" ? "right" : "left"); |
|
label.verticalAlign("top"); |
|
} else if (angle > 0 && angle < 90){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top"); |
|
} else if (angle == 90){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("top");//ref.orientation == "ll" ? "bottom" : "top"); |
|
} else if (angle > 90 && angle < 180){ |
|
label.horizontalAlign("left"); |
|
label.verticalAlign("bottom"); |
|
} else if (angle == 180){ |
|
label.horizontalAlign("left");//ref.orientation == "lr" ? "right" : "left"); |
|
label.verticalAlign("bottom"); |
|
} else if (angle > 180 && angle < 270){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("bottom"); |
|
} else if (angle == 270){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top");//ref.orientation == "lr" ? "bottom" : "top"); |
|
} else if (angle > 270 && angle <= 360){ |
|
label.horizontalAlign("right"); |
|
label.verticalAlign("top"); |
|
} |
|
} |
|
ref.iorientation = ref.iorientation || ref.orientation; |
|
ref.orientation = (label.verticalAlign()=="top"?"u":"l") + (label.horizontalAlign()=="left"?"l":"r"); |
|
} |
|
|
|
label.setReferencePoint(jQuery.extend({},{ |
|
intersection: intersection, |
|
segment: { |
|
from: from, |
|
fromIndex: this.dockers.indexOf(from), |
|
fromPosition: from.bounds.center(), |
|
to: to, |
|
toIndex: this.dockers.indexOf(to), |
|
toPosition: to.bounds.center() |
|
}, |
|
dirty: dirty || false |
|
},ref)) |
|
}, |
|
/** |
|
* Adjusts the child shapes of an edges after a docker was removed. |
|
* |
|
* @param{ORYX.Core.Controls.Docker} docker |
|
* The removed docker. |
|
*/ |
|
handleChildShapesAfterRemoveDocker: function(docker) { |
|
/* Ensure docker type */ |
|
if(!(docker instanceof ORYX.Core.Controls.Docker)) {return;} |
|
|
|
this.attachedNodePositionData.each(function(nodePositionData) { |
|
if(nodePositionData.value.segment.docker1 === docker) { |
|
/* The new start of the segment is the predecessor of docker2. */ |
|
var index = this.dockers.indexOf(nodePositionData.value.segment.docker2); |
|
if(index == -1) {return;} |
|
nodePositionData.value.segment.docker1 = this.dockers[index - 1]; |
|
} |
|
else if(nodePositionData.value.segment.docker2 === docker) { |
|
/* The new end of the segment is the successor of docker1. */ |
|
var index = this.dockers.indexOf(nodePositionData.value.segment.docker1); |
|
if(index == -1) {return;} |
|
nodePositionData.value.segment.docker2 = this.dockers[index + 1]; |
|
} |
|
}.bind(this)); |
|
|
|
// Update all labels reference points |
|
this.getLabels().each(function(label){ |
|
|
|
var ref = label.getReferencePoint(); |
|
if (!ref) { |
|
return; |
|
} |
|
var from = ref.segment.from; |
|
var to = ref.segment.to; |
|
|
|
if (from !== docker && to !== docker){ |
|
return; |
|
} |
|
|
|
var segment = this.findSegment(ref.intersection); |
|
if (!segment){ |
|
from = segment.fromDocker; |
|
to = segment.toDocker; |
|
} else { |
|
from = from === docker ? this.dockers[this.dockers.indexOf(to)-1] : from; |
|
to = this.dockers[this.dockers.indexOf(from)+1]; |
|
} |
|
|
|
var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(from.bounds.center(), to.bounds.center(), ref.intersection, true); |
|
// Update the reference point |
|
this.updateReferencePointOfLabel(label, intersection, from, to, true); |
|
}.bind(this)); |
|
|
|
/* Update attached nodes visual representation */ |
|
this.refreshAttachedNodes(); |
|
}, |
|
|
|
/** |
|
*@deprecated Use the .createDocker() Method and set the point via the bounds |
|
*/ |
|
addDocker: function(position, exDocker){ |
|
var lastDocker; |
|
var result; |
|
this._dockersByPath.any((function(pair){ |
|
return pair.value.any((function(docker, index){ |
|
if (!lastDocker) { |
|
lastDocker = docker; |
|
return false; |
|
} |
|
else { |
|
var point1 = lastDocker.bounds.center(); |
|
var point2 = docker.bounds.center(); |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
position.x = position.x / additionalIEZoom; |
|
position.y = position.y / additionalIEZoom; |
|
} |
|
|
|
if (ORYX.Core.Math.isPointInLine(position.x, position.y, point1.x, point1.y, point2.x, point2.y, 10)) { |
|
var path = this._paths.find(function(path){ |
|
return path.id === pair.key; |
|
}); |
|
if (path) { |
|
var allowAttr = path.getAttributeNS(NAMESPACE_ORYX, 'allowDockers'); |
|
if (allowAttr && allowAttr.toLowerCase() === "no") { |
|
return true; |
|
} |
|
} |
|
|
|
var newDocker = (exDocker) ? exDocker : this.createDocker(this.dockers.indexOf(lastDocker) + 1, position); |
|
newDocker.bounds.centerMoveTo(position); |
|
if(exDocker) |
|
this.add(newDocker, this.dockers.indexOf(lastDocker) + 1); |
|
result = newDocker; |
|
return true; |
|
} |
|
else { |
|
lastDocker = docker; |
|
return false; |
|
} |
|
} |
|
}).bind(this)); |
|
}).bind(this)); |
|
return result; |
|
}, |
|
|
|
removeDocker: function(docker){ |
|
if (this.dockers.length > 2 && !(this.dockers.first() === docker)) { |
|
this._dockersByPath.any((function(pair){ |
|
if (pair.value.member(docker)) { |
|
if (docker === pair.value.last()) { |
|
return true; |
|
} |
|
else { |
|
this.remove(docker); |
|
this._dockersByPath[pair.key] = pair.value.without(docker); |
|
this.isChanged = true; |
|
this._dockerChanged(); |
|
return true; |
|
} |
|
} |
|
return false; |
|
}).bind(this)); |
|
} |
|
}, |
|
|
|
/** |
|
* Removes all dockers from the edge which are on |
|
* the line between two dockers |
|
* @return {Object} Removed dockers in an indicied array |
|
* (key is the removed position of the docker, value is docker themselve) |
|
*/ |
|
removeUnusedDockers:function(){ |
|
var marked = $H({}); |
|
|
|
this.dockers.each(function(docker, i){ |
|
if (i==0||i==this.dockers.length-1){ return } |
|
var previous = this.dockers[i-1]; |
|
|
|
/* Do not consider already removed dockers */ |
|
if(marked.values().indexOf(previous) != -1 && this.dockers[i-2]) { |
|
previous = this.dockers[i-2]; |
|
} |
|
var next = this.dockers[i+1]; |
|
|
|
var cp = previous.getDockedShape() && previous.referencePoint ? previous.getAbsoluteReferencePoint() : previous.bounds.center(); |
|
var cn = next.getDockedShape() && next.referencePoint ? next.getAbsoluteReferencePoint() : next.bounds.center(); |
|
var cd = docker.bounds.center(); |
|
|
|
if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 1)){ |
|
marked[i] = docker; |
|
} |
|
}.bind(this)) |
|
|
|
marked.each(function(docker){ |
|
this.removeDocker(docker.value); |
|
}.bind(this)) |
|
|
|
if (marked.values().length > 0){ |
|
this._update(true); |
|
} |
|
|
|
return marked; |
|
}, |
|
|
|
/** |
|
* Initializes the Edge after loading the SVG representation of the edge. |
|
* @param {SVGDocument} svgDocument |
|
*/ |
|
_init: function(svgDocument){ |
|
arguments.callee.$._init.apply(this, arguments); |
|
|
|
var minPointX, minPointY, maxPointX, maxPointY; |
|
|
|
//init markers |
|
var defs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, "defs"); |
|
if (defs.length > 0) { |
|
defs = defs[0]; |
|
var markerElements = $A(defs.getElementsByTagNameNS(NAMESPACE_SVG, "marker")); |
|
var marker; |
|
var me = this; |
|
markerElements.each(function(markerElement){ |
|
try { |
|
marker = new ORYX.Core.SVG.SVGMarker(markerElement.cloneNode(true)); |
|
me._markers[marker.id] = marker; |
|
var textElements = $A(marker.element.getElementsByTagNameNS(NAMESPACE_SVG, "text")); |
|
var label; |
|
textElements.each(function(textElement){ |
|
label = new ORYX.Core.SVG.Label({ |
|
textElement: textElement, |
|
shapeId: this.id |
|
}); |
|
me._labels[label.id] = label; |
|
}); |
|
} |
|
catch (e) { |
|
} |
|
}); |
|
} |
|
|
|
|
|
var gs = svgDocument.getElementsByTagNameNS(NAMESPACE_SVG, "g"); |
|
if (gs.length <= 0) { |
|
throw "Edge: No g element found."; |
|
} |
|
var g = gs[0]; |
|
|
|
|
|
g.setAttributeNS(null, "id", null); |
|
|
|
var isFirst = true; |
|
|
|
$A(g.childNodes).each((function(path, index){ |
|
if (ORYX.Editor.checkClassType(path, SVGPathElement)) { |
|
path = path.cloneNode(false); |
|
|
|
var pathId = this.id + "_" + index; |
|
path.setAttributeNS(null, "id", pathId); |
|
this._paths.push(path); |
|
|
|
//check, if markers are set and update the id |
|
var markersByThisPath = []; |
|
var markerUrl = path.getAttributeNS(null, "marker-start"); |
|
|
|
if (markerUrl && markerUrl !== "") { |
|
markerUrl = markerUrl.strip(); |
|
markerUrl = markerUrl.replace(/^url\(#/, ''); |
|
|
|
var markerStartId = this.getValidMarkerId(markerUrl); |
|
path.setAttributeNS(null, "marker-start", "url(#" + markerStartId + ")"); |
|
|
|
markersByThisPath.push(this._markers[markerStartId]); |
|
} |
|
|
|
markerUrl = path.getAttributeNS(null, "marker-mid"); |
|
|
|
if (markerUrl && markerUrl !== "") { |
|
markerUrl = markerUrl.strip(); |
|
markerUrl = markerUrl.replace(/^url\(#/, ''); |
|
var markerMidId = this.getValidMarkerId(markerUrl); |
|
path.setAttributeNS(null, "marker-mid", "url(#" + markerMidId + ")"); |
|
|
|
markersByThisPath.push(this._markers[markerMidId]); |
|
} |
|
|
|
markerUrl = path.getAttributeNS(null, "marker-end"); |
|
|
|
if (markerUrl && markerUrl !== "") { |
|
markerUrl = markerUrl.strip(); |
|
|
|
var markerEndId = this.getValidMarkerId(markerUrl); |
|
path.setAttributeNS(null, "marker-end", "url(#" + markerEndId + ")"); |
|
|
|
markersByThisPath.push(this._markers[markerEndId]); |
|
} |
|
|
|
this._markersByPath[pathId] = markersByThisPath; |
|
|
|
//init dockers |
|
var parser = new PathParser(); |
|
var handler = new ORYX.Core.SVG.PointsPathHandler(); |
|
parser.setHandler(handler); |
|
parser.parsePath(path); |
|
|
|
if (handler.points.length < 4) { |
|
throw "Edge: Path has to have two or more points specified."; |
|
} |
|
|
|
this._dockersByPath[pathId] = []; |
|
|
|
for (var i = 0; i < handler.points.length; i += 2) { |
|
//handler.points.each((function(point, pIndex){ |
|
var x = handler.points[i]; |
|
var y = handler.points[i+1]; |
|
if (isFirst || i > 0) { |
|
var docker = new ORYX.Core.Controls.Docker({ |
|
eventHandlerCallback: this.eventHandlerCallback |
|
}); |
|
docker.bounds.centerMoveTo(x,y); |
|
docker.bounds.registerCallback(this._dockerChangedCallback); |
|
this.add(docker, this.dockers.length); |
|
|
|
//this._dockersByPath[pathId].push(docker); |
|
|
|
//calculate minPoint and maxPoint |
|
if (minPointX) { |
|
minPointX = Math.min(x, minPointX); |
|
minPointY = Math.min(y, minPointY); |
|
} |
|
else { |
|
minPointX = x; |
|
minPointY = y; |
|
} |
|
|
|
if (maxPointX) { |
|
maxPointX = Math.max(x, maxPointX); |
|
maxPointY = Math.max(y, maxPointY); |
|
} |
|
else { |
|
maxPointX = x; |
|
maxPointY = y; |
|
} |
|
} |
|
//}).bind(this)); |
|
} |
|
isFirst = false; |
|
} |
|
}).bind(this)); |
|
|
|
this.bounds.set(minPointX, minPointY, maxPointX, maxPointY); |
|
|
|
if (false&&(this.bounds.width() === 0 || this.bounds.height() === 0)) { |
|
var width = this.bounds.width(); |
|
var height = this.bounds.height(); |
|
|
|
this.bounds.extend({ |
|
x: width === 0 ? 2 : 0, |
|
y: height === 0 ? 2 : 0 |
|
}); |
|
|
|
this.bounds.moveBy({ |
|
x: width === 0 ? -1 : 0, |
|
y: height === 0 ? -1 : 0 |
|
}); |
|
|
|
} |
|
|
|
this._oldBounds = this.bounds.clone(); |
|
|
|
//add paths to this.node |
|
this._paths.reverse(); |
|
var paths = []; |
|
this._paths.each((function(path){ |
|
paths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(path)); |
|
}).bind(this)); |
|
|
|
this._paths = paths; |
|
|
|
//init interaction path |
|
this._paths.each((function(path){ |
|
var iPath = path.cloneNode(false); |
|
iPath.setAttributeNS(null, "id", undefined); |
|
iPath.setAttributeNS(null, "stroke-width", 10); |
|
iPath.setAttributeNS(null, "visibility", "hidden"); |
|
iPath.setAttributeNS(null, "stroke-dasharray", null); |
|
iPath.setAttributeNS(null, "stroke", "black"); |
|
iPath.setAttributeNS(null, "fill", "none"); |
|
iPath.setAttributeNS(null, "title", this.getStencil().title()); |
|
this._interactionPaths.push(this.node.childNodes[0].childNodes[0].childNodes[0].appendChild(iPath)); |
|
}).bind(this)); |
|
|
|
this._paths.reverse(); |
|
this._interactionPaths.reverse(); |
|
|
|
/**initialize labels*/ |
|
var textElems = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text'); |
|
|
|
$A(textElems).each((function(textElem){ |
|
var label = new ORYX.Core.SVG.Label({ |
|
textElement: textElem, |
|
shapeId: this.id |
|
}); |
|
this.node.childNodes[0].childNodes[0].appendChild(label.node); |
|
this._labels[label.id] = label; |
|
|
|
label.registerOnChange(this.layout.bind(this)); |
|
}).bind(this)); |
|
|
|
|
|
this.propertiesChanged.each(function(pair){ |
|
pair.value = true; |
|
}); |
|
|
|
|
|
//if(this.dockers.length == 2) { |
|
|
|
|
|
|
|
// } |
|
|
|
//this._update(true); |
|
}, |
|
|
|
getValidMarkerId: function(markerUrl) { |
|
if(markerUrl.indexOf("url(\"#") >= 0) { |
|
// Fix for IE9, additional quotes are added to the <id |
|
var rawId = markerUrl.replace(/^url\(\"#/, "").replace(/\"\)$/, ''); |
|
return this.id + rawId; |
|
} else { |
|
markerUrl = markerUrl.replace(/^url\(#/, ''); |
|
return this.id.concat(markerUrl.replace(/\)$/, '')); |
|
} |
|
}, |
|
|
|
/** |
|
* Adds all necessary markers of this Edge to the SVG document. |
|
* Has to be called, while this.node is part of DOM. |
|
*/ |
|
addMarkers: function(defs){ |
|
this._markers.each(function(marker){ |
|
if (!defs.ownerDocument.getElementById(marker.value.id)) { |
|
marker.value.element = defs.appendChild(marker.value.element); |
|
} |
|
}); |
|
}, |
|
|
|
/** |
|
* Removes all necessary markers of this Edge from the SVG document. |
|
* Has to be called, while this.node is part of DOM. |
|
*/ |
|
removeMarkers: function(){ |
|
var svgElement = this.node.ownerSVGElement; |
|
if (svgElement) { |
|
var defs = svgElement.getElementsByTagNameNS(NAMESPACE_SVG, "defs"); |
|
if (defs.length > 0) { |
|
defs = defs[0]; |
|
this._markers.each(function(marker){ |
|
var foundMarker = defs.ownerDocument.getElementById(marker.value.id); |
|
if (foundMarker) { |
|
marker.value.element = defs.removeChild(marker.value.element); |
|
} |
|
}); |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Calls when a docker has changed |
|
*/ |
|
_dockerChanged: function(){ |
|
|
|
//this._update(true); |
|
this._dockerUpdated = true; |
|
|
|
}, |
|
|
|
serialize: function(){ |
|
var result = arguments.callee.$.serialize.apply(this); |
|
|
|
//add dockers triple |
|
var value = ""; |
|
this._dockersByPath.each((function(pair){ |
|
pair.value.each(function(docker){ |
|
var position = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center(); |
|
value = value.concat(position.x + " " + position.y + " "); |
|
}); |
|
|
|
value += " # "; |
|
}).bind(this)); |
|
result.push({ |
|
name: 'dockers', |
|
prefix: 'oryx', |
|
value: value, |
|
type: 'literal' |
|
}); |
|
|
|
//add parent triple dependant on the dockedShapes |
|
//TODO change this when canvas becomes a resource |
|
/* var source = this.dockers.first().getDockedShape(); |
|
var target = this.dockers.last().getDockedShape(); |
|
var sharedParent; |
|
if (source && target) { |
|
//get shared parent |
|
while (source.parent) { |
|
source = source.parent; |
|
if (source instanceof ORYX.Core.Canvas) { |
|
sharedParent = source; |
|
break; |
|
} |
|
else { |
|
var targetParent = target.parent; |
|
var found; |
|
while (targetParent) { |
|
if (source === targetParent) { |
|
sharedParent = source; |
|
found = true; |
|
break; |
|
} |
|
else { |
|
targetParent = targetParent.parent; |
|
} |
|
} |
|
if (found) { |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
else |
|
if (source) { |
|
sharedParent = source.parent; |
|
} |
|
else |
|
if (target) { |
|
sharedParent = target.parent; |
|
} |
|
*/ |
|
//if (sharedParent) { |
|
/* result.push({ |
|
name: 'parent', |
|
prefix: 'raziel', |
|
//value: '#' + ERDF.__stripHashes(sharedParent.resourceId), |
|
value: '#' + ERDF.__stripHashes(this.getCanvas().resourceId), |
|
type: 'resource' |
|
});*/ |
|
//} |
|
|
|
//serialize target and source |
|
var lastDocker = this.dockers.last(); |
|
|
|
var target = lastDocker.getDockedShape(); |
|
|
|
if(target) { |
|
result.push({ |
|
name: 'target', |
|
prefix: 'raziel', |
|
value: '#' + ERDF.__stripHashes(target.resourceId), |
|
type: 'resource' |
|
}); |
|
} |
|
|
|
try { |
|
//result = this.getStencil().serialize(this, result); |
|
var serializeEvent = this.getStencil().serialize(); |
|
|
|
/* |
|
* call serialize callback by reference, result should be found |
|
* in serializeEvent.result |
|
*/ |
|
if(serializeEvent.type) { |
|
serializeEvent.shape = this; |
|
serializeEvent.data = result; |
|
serializeEvent.result = undefined; |
|
serializeEvent.forceExecution = true; |
|
|
|
this._delegateEvent(serializeEvent); |
|
|
|
if(serializeEvent.result) { |
|
result = serializeEvent.result; |
|
} |
|
} |
|
} |
|
catch (e) { |
|
} |
|
return result; |
|
}, |
|
|
|
deserialize: function(data){ |
|
try { |
|
//data = this.getStencil().deserialize(this, data); |
|
|
|
var deserializeEvent = this.getStencil().deserialize(); |
|
|
|
/* |
|
* call serialize callback by reference, result should be found |
|
* in serializeEventInfo.result |
|
*/ |
|
if(deserializeEvent.type) { |
|
deserializeEvent.shape = this; |
|
deserializeEvent.data = data; |
|
deserializeEvent.result = undefined; |
|
deserializeEvent.forceExecution = true; |
|
|
|
this._delegateEvent(deserializeEvent); |
|
if(deserializeEvent.result) { |
|
data = deserializeEvent.result; |
|
} |
|
} |
|
} |
|
catch (e) { |
|
} |
|
|
|
// Set the outgoing shapes |
|
var target = data.find(function(ser) {return (ser.prefix+"-"+ser.name) == 'raziel-target'}); |
|
var targetShape; |
|
if(target) { |
|
targetShape = this.getCanvas().getChildShapeByResourceId(target.value); |
|
} |
|
|
|
var outgoing = data.findAll(function(ser){ return (ser.prefix+"-"+ser.name) == 'raziel-outgoing'}); |
|
outgoing.each((function(obj){ |
|
// TODO: Look at Canvas |
|
if(!this.parent) {return}; |
|
|
|
// Set outgoing Shape |
|
var next = this.getCanvas().getChildShapeByResourceId(obj.value); |
|
|
|
if(next){ |
|
if(next == targetShape) { |
|
// If this is an edge, set the last docker to the next shape |
|
this.dockers.last().setDockedShape(next); |
|
this.dockers.last().setReferencePoint({x: next.bounds.width() / 2.0, y: next.bounds.height() / 2.0}); |
|
} else if(next instanceof ORYX.Core.Edge) { |
|
//Set the first docker of the next shape |
|
next.dockers.first().setDockedShape(this); |
|
//next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0}); |
|
} /*else if(next.dockers.length > 0) { //next is a node and next has a docker |
|
next.dockers.first().setDockedShape(this); |
|
next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0}); |
|
}*/ |
|
} |
|
|
|
}).bind(this)); |
|
|
|
|
|
var oryxDockers = data.find(function(obj){ |
|
return (obj.prefix === "oryx" && |
|
obj.name === "dockers"); |
|
}); |
|
|
|
if (oryxDockers) { |
|
var dataByPath = oryxDockers.value.split("#").without("").without(" "); |
|
|
|
dataByPath.each((function(data, index){ |
|
var values = data.replace(/,/g, " ").split(" ").without(""); |
|
|
|
//for each docker two values must be defined |
|
if (values.length % 2 === 0) { |
|
var path = this._paths[index]; |
|
|
|
if (path) { |
|
if (index === 0) { |
|
while (this._dockersByPath[path.id].length > 2) { |
|
this.removeDocker(this._dockersByPath[path.id][1]); |
|
} |
|
} |
|
else { |
|
while (this._dockersByPath[path.id].length > 1) { |
|
this.removeDocker(this._dockersByPath[path.id][0]); |
|
} |
|
} |
|
|
|
var dockersByPath = this._dockersByPath[path.id]; |
|
|
|
if (index === 0) { |
|
//set position of first docker |
|
var x = parseFloat(values.shift()); |
|
var y = parseFloat(values.shift()); |
|
|
|
if (dockersByPath.first().getDockedShape()) { |
|
dockersByPath.first().setReferencePoint({ |
|
x: x, |
|
y: y |
|
}); |
|
} |
|
else { |
|
dockersByPath.first().bounds.centerMoveTo(x, y); |
|
} |
|
} |
|
|
|
//set position of last docker |
|
y = parseFloat(values.pop()); |
|
x = parseFloat(values.pop()); |
|
|
|
if (dockersByPath.last().getDockedShape()) { |
|
dockersByPath.last().setReferencePoint({ |
|
x: x, |
|
y: y |
|
}); |
|
} else { |
|
dockersByPath.last().bounds.centerMoveTo(x, y); |
|
} |
|
|
|
//add additional dockers |
|
for (var i = 0; i < values.length; i++) { |
|
x = parseFloat(values[i]); |
|
y = parseFloat(values[++i]); |
|
|
|
var newDocker = this.createDocker(); |
|
newDocker.bounds.centerMoveTo(x, y); |
|
|
|
//this.dockers = this.dockers.without(newDocker); |
|
//this.dockers.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker); |
|
//dockersByPath.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker); |
|
} |
|
} |
|
} |
|
}).bind(this)); |
|
} else { |
|
this.alignDockers(); |
|
} |
|
|
|
arguments.callee.$.deserialize.apply(this, arguments); |
|
|
|
this._changed(); |
|
}, |
|
|
|
toString: function(){ |
|
return this.getStencil().title() + " " + this.id; |
|
}, |
|
|
|
/** |
|
* @return {ORYX.Core.Shape} Returns last docked shape or null. |
|
*/ |
|
getTarget: function(){ |
|
return this.dockers.last() ? this.dockers.last().getDockedShape() : null; |
|
}, |
|
|
|
/** |
|
* @return {ORYX.Core.Shape} Returns the first docked shape or null |
|
*/ |
|
getSource: function() { |
|
return this.dockers.first() ? this.dockers.first().getDockedShape() : null; |
|
}, |
|
|
|
/** |
|
* Checks whether the edge is at least docked to one shape. |
|
* |
|
* @return {boolean} True if edge is docked |
|
*/ |
|
isDocked: function() { |
|
var isDocked = false; |
|
this.dockers.each(function(docker) { |
|
if(docker.isDocked()) { |
|
isDocked = true; |
|
throw $break; |
|
} |
|
}); |
|
return isDocked; |
|
}, |
|
|
|
/** |
|
* Calls {@link ORYX.Core.AbstractShape#toJSON} and add a some stencil set information. |
|
*/ |
|
toJSON: function() { |
|
var json = arguments.callee.$.toJSON.apply(this, arguments); |
|
|
|
if(this.getTarget()) { |
|
json.target = { |
|
resourceId: this.getTarget().resourceId |
|
}; |
|
} |
|
|
|
return json; |
|
} |
|
}; |
|
ORYX.Core.Edge = ORYX.Core.Shape.extend(ORYX.Core.Edge); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX){ var ORYX = {} } |
|
if(!ORYX.Plugins){ ORYX.Plugins = {} } |
|
|
|
/** |
|
This abstract plugin class can be used to build plugins on. |
|
It provides some more basic functionality like registering events (on*-handlers)... |
|
@example |
|
ORYX.Plugins.MyPlugin = ORYX.Plugins.AbstractPlugin.extend({ |
|
construct: function() { |
|
// Call super class constructor |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
[...] |
|
}, |
|
[...] |
|
}); |
|
|
|
@class ORYX.Plugins.AbstractPlugin |
|
@constructor Creates a new instance |
|
@author Willi Tscheschner |
|
*/ |
|
ORYX.Plugins.AbstractPlugin = Clazz.extend({ |
|
/** |
|
* The facade which offer editor-specific functionality |
|
* @type Facade |
|
* @memberOf ORYX.Plugins.AbstractPlugin.prototype |
|
*/ |
|
facade: null, |
|
|
|
construct: function( facade ){ |
|
this.facade = facade; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.onLoaded.bind(this)); |
|
}, |
|
|
|
/** |
|
Overwrite to handle load event. TODO: Document params!!! |
|
@methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
*/ |
|
onLoaded: function(){}, |
|
|
|
/** |
|
Overwrite to handle selection changed event. TODO: Document params!!! |
|
@methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
*/ |
|
onSelectionChanged: function(){}, |
|
|
|
/** |
|
Show overlay on given shape. |
|
@methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
@example |
|
showOverlay( |
|
myShape, |
|
{ stroke: "green" }, |
|
ORYX.Editor.graft("http://www.w3.org/2000/svg", null, ['path', { |
|
"title": "Click the element to execute it!", |
|
"stroke-width": 2.0, |
|
"stroke": "black", |
|
"d": "M0,-5 L5,0 L0,5 Z", |
|
"line-captions": "round" |
|
}]) |
|
) |
|
@param {Oryx.XXX.Shape[]} shapes One shape or array of shapes the overlay should be put on |
|
@param {Oryx.XXX.Attributes} attributes some attributes... |
|
@param {Oryx.svg.node} svgNode The svg node which should be used as overlay |
|
@param {String} [svgNode="NW"] The svg node position where the overlay should be placed |
|
*/ |
|
showOverlay: function(shapes, attributes, svgNode, svgNodePosition ){ |
|
|
|
if( !(shapes instanceof Array) ){ |
|
shapes = [shapes] |
|
} |
|
|
|
// Define Shapes |
|
shapes = shapes.map(function(shape){ |
|
var el = shape; |
|
if( typeof shape == "string" ){ |
|
el = this.facade.getCanvas().getChildShapeByResourceId( shape ); |
|
el = el || this.facade.getCanvas().getChildById( shape, true ); |
|
} |
|
return el; |
|
}.bind(this)).compact(); |
|
|
|
// Define unified id |
|
if( !this.overlayID ){ |
|
this.overlayID = this.type + ORYX.Editor.provideId(); |
|
} |
|
|
|
this.facade.raiseEvent({ |
|
type : ORYX.CONFIG.EVENT_OVERLAY_SHOW, |
|
id : this.overlayID, |
|
shapes : shapes, |
|
attributes : attributes, |
|
node : svgNode, |
|
nodePosition: svgNodePosition || "NW" |
|
}); |
|
|
|
}, |
|
|
|
/** |
|
Hide current overlay. |
|
@methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
*/ |
|
hideOverlay: function(){ |
|
this.facade.raiseEvent({ |
|
type : ORYX.CONFIG.EVENT_OVERLAY_HIDE, |
|
id : this.overlayID |
|
}); |
|
}, |
|
|
|
/** |
|
Does a transformation with the given xslt stylesheet. |
|
@methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
@param {String} data The data (e.g. eRDF) which should be transformed |
|
@param {String} stylesheet URL of a stylesheet which should be used for transforming data. |
|
*/ |
|
doTransform: function( data, stylesheet ) { |
|
|
|
if( !stylesheet || !data ){ |
|
return "" |
|
} |
|
|
|
var parser = new DOMParser(); |
|
var parsedData = parser.parseFromString(data, "text/xml"); |
|
source=stylesheet; |
|
new Ajax.Request(source, { |
|
asynchronous: false, |
|
method: 'get', |
|
onSuccess: function(transport){ |
|
xsl = transport.responseText |
|
}.bind(this), |
|
onFailure: (function(transport){ |
|
ORYX.Log.error("XSL load failed" + transport); |
|
}).bind(this) |
|
}); |
|
var xsltProcessor = new XSLTProcessor(); |
|
var domParser = new DOMParser(); |
|
var xslObject = domParser.parseFromString(xsl, "text/xml"); |
|
xsltProcessor.importStylesheet(xslObject); |
|
|
|
try { |
|
|
|
var newData = xsltProcessor.transformToFragment(parsedData, document); |
|
var serializedData = (new XMLSerializer()).serializeToString(newData); |
|
|
|
/* Firefox 2 to 3 problem?! */ |
|
serializedData = !serializedData.startsWith("<?xml") ? "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializedData : serializedData; |
|
|
|
return serializedData; |
|
|
|
}catch (error) { |
|
return -1; |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Opens a new window that shows the given XML content. |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
* @param {Object} content The XML content to be shown. |
|
* @example |
|
* openDownloadWindow( "my.xml", "<exampleXML />" ); |
|
*/ |
|
openXMLWindow: function(content) { |
|
var win = window.open( |
|
'data:application/xml,' + encodeURIComponent( |
|
content |
|
), |
|
'_blank', "resizable=yes,width=600,height=600,toolbar=0,scrollbars=yes" |
|
); |
|
}, |
|
|
|
/** |
|
* Opens a download window for downloading the given content. |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
* @param {String} filename The content's file name |
|
* @param {String} content The content to download |
|
*/ |
|
openDownloadWindow: function(filename, content) { |
|
var win = window.open(""); |
|
if (win != null) { |
|
win.document.open(); |
|
win.document.write("<html><body>"); |
|
var submitForm = win.document.createElement("form"); |
|
win.document.body.appendChild(submitForm); |
|
|
|
var createHiddenElement = function(name, value) { |
|
var newElement = document.createElement("input"); |
|
newElement.name=name; |
|
newElement.type="hidden"; |
|
newElement.value = value; |
|
return newElement |
|
} |
|
|
|
submitForm.appendChild( createHiddenElement("download", content) ); |
|
submitForm.appendChild( createHiddenElement("file", filename) ); |
|
|
|
|
|
submitForm.method = "POST"; |
|
win.document.write("</body></html>"); |
|
win.document.close(); |
|
submitForm.action= ORYX.PATH + "/download"; |
|
submitForm.submit(); |
|
} |
|
}, |
|
|
|
/** |
|
* Serializes DOM. |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
* @type {String} Serialized DOM |
|
*/ |
|
getSerializedDOM: function(){ |
|
// Force to set all resource IDs |
|
var serializedDOM = DataManager.serializeDOM( this.facade ); |
|
|
|
//add namespaces |
|
serializedDOM = '<?xml version="1.0" encoding="utf-8"?>' + |
|
'<html xmlns="http://www.w3.org/1999/xhtml" ' + |
|
'xmlns:b3mn="http://b3mn.org/2007/b3mn" ' + |
|
'xmlns:ext="http://b3mn.org/2007/ext" ' + |
|
'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ' + |
|
'xmlns:atom="http://b3mn.org/2007/atom+xhtml">' + |
|
'<head profile="http://purl.org/NET/erdf/profile">' + |
|
'<link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />' + |
|
'<link rel="schema.dcTerms" href="http://purl.org/dc/terms/ " />' + |
|
'<link rel="schema.b3mn" href="http://b3mn.org" />' + |
|
'<link rel="schema.oryx" href="http://oryx-editor.org/" />' + |
|
'<link rel="schema.raziel" href="http://raziel.org/" />' + |
|
'<base href="' + |
|
location.href.split("?")[0] + |
|
'" />' + |
|
'</head><body>' + |
|
serializedDOM + |
|
'</body></html>'; |
|
|
|
return serializedDOM; |
|
}, |
|
|
|
/** |
|
* Sets the editor in read only mode: Edges/ dockers cannot be moved anymore, |
|
* shapes cannot be selected anymore. |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
*/ |
|
enableReadOnlyMode: function(){ |
|
//Edges cannot be moved anymore |
|
this.facade.disableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN); |
|
|
|
// Stop the user from editing the diagram while the plugin is active |
|
this._stopSelectionChange = function(){ |
|
if(this.facade.getSelection().length > 0) { |
|
this.facade.setSelection([]); |
|
} |
|
}; |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this)); |
|
}, |
|
/** |
|
* Disables read only mode, see @see |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
* @see ORYX.Plugins.AbstractPlugin.prototype.enableReadOnlyMode |
|
*/ |
|
disableReadOnlyMode: function(){ |
|
// Edges can be moved now again |
|
this.facade.enableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN); |
|
|
|
if (this._stopSelectionChange) { |
|
this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this)); |
|
this._stopSelectionChange = undefined; |
|
} |
|
}, |
|
|
|
/** |
|
* Extracts RDF from DOM. |
|
* @methodOf ORYX.Plugins.AbstractPlugin.prototype |
|
* @type {String} Extracted RFD. Null if there are transformation errors. |
|
*/ |
|
getRDFFromDOM: function(){ |
|
//convert to RDF |
|
try { |
|
var xsl = ""; |
|
source=ORYX.PATH + "lib/extract-rdf.xsl"; |
|
new Ajax.Request(source, { |
|
asynchronous: false, |
|
method: 'get', |
|
onSuccess: function(transport){ |
|
xsl = transport.responseText |
|
}.bind(this), |
|
onFailure: (function(transport){ |
|
ORYX.Log.error("XSL load failed" + transport); |
|
}).bind(this) |
|
}); |
|
|
|
var domParser = new DOMParser(); |
|
var xmlObject = domParser.parseFromString(this.getSerializedDOM(), "text/xml"); |
|
var xslObject = domParser.parseFromString(xsl, "text/xml"); |
|
var xsltProcessor = new XSLTProcessor(); |
|
xsltProcessor.importStylesheet(xslObject); |
|
var result = xsltProcessor.transformToFragment(xmlObject, document); |
|
|
|
var serializer = new XMLSerializer(); |
|
|
|
return serializer.serializeToString(result); |
|
} catch(e){ |
|
console.log("error serializing " + e); |
|
return ""; |
|
} |
|
|
|
|
|
}, |
|
|
|
/** |
|
* Checks if a certain stencil set is loaded right now. |
|
* |
|
*/ |
|
isStencilSetExtensionLoaded: function(stencilSetExtensionNamespace) { |
|
return this.facade.getStencilSets().values().any( |
|
function(ss){ |
|
return ss.extensions().keys().any( |
|
function(extensionKey) { |
|
return extensionKey == stencilSetExtensionNamespace; |
|
}.bind(this) |
|
); |
|
}.bind(this) |
|
); |
|
}, |
|
|
|
/** |
|
* Raises an event so that registered layouters does |
|
* have the posiblility to layout the given shapes |
|
* For further reading, have a look into the AbstractLayouter |
|
* class |
|
* @param {Object} shapes |
|
*/ |
|
doLayout: function(shapes){ |
|
// Raises a do layout event |
|
if (this.facade.raiseEvent) |
|
{ |
|
this.facade.raiseEvent({ |
|
type : ORYX.CONFIG.EVENT_LAYOUT, |
|
shapes : shapes |
|
}); |
|
} |
|
else |
|
{ |
|
this.facade.handleEvents({ |
|
type : ORYX.CONFIG.EVENT_LAYOUT, |
|
shapes : shapes |
|
}); |
|
} |
|
}, |
|
|
|
|
|
/** |
|
* Does a primitive layouting with the incoming/outgoing |
|
* edges (set the dockers to the right position) and if |
|
* necessary, it will be called the real layouting |
|
* @param {ORYX.Core.Node} node |
|
* @param {Array} edges |
|
*/ |
|
layoutEdges : function(node, allEdges, offset){ |
|
|
|
if (!this.facade.isExecutingCommands()){ return } |
|
|
|
var Command = ORYX.Core.Command.extend({ |
|
construct: function(edges, node, offset, plugin){ |
|
this.edges = edges; |
|
this.node = node; |
|
this.plugin = plugin; |
|
this.offset = offset; |
|
|
|
// Get the new absolute center |
|
var center = node.absoluteXY(); |
|
this.ulo = {x: center.x - offset.x, y:center.y - offset.y}; |
|
|
|
|
|
}, |
|
execute: function(){ |
|
|
|
if (this.changes){ |
|
this.executeAgain(); |
|
return; |
|
} else { |
|
this.changes = []; |
|
this.edges.each(function(edge){ |
|
this.changes.push({ |
|
edge: edge, |
|
oldDockerPositions: edge.dockers.map(function(r){ return r.bounds.center() }) |
|
}) |
|
}.bind(this)); |
|
} |
|
|
|
// Find all edges, which are related to the node and |
|
// have more than two dockers |
|
this.edges |
|
// Find all edges with more than two dockers |
|
.findAll(function(r){ return r.dockers.length > 2 }.bind(this)) |
|
// For every edge, check second and one before last docker |
|
// if there are horizontal/vertical on the same level |
|
// and if so, align the the bounds |
|
.each(function(edge){ |
|
if (edge.dockers.first().getDockedShape() === this.node){ |
|
var second = edge.dockers[1]; |
|
if (this.align(second.bounds, edge.dockers.first())){ second.update(); } |
|
} else if (edge.dockers.last().getDockedShape() === this.node) { |
|
var beforeLast = edge.dockers[edge.dockers.length-2]; |
|
if (this.align(beforeLast.bounds, edge.dockers.last())){ beforeLast.update(); } |
|
} |
|
edge._update(true); |
|
edge.removeUnusedDockers(); |
|
if (this.isBendPointIncluded(edge)){ |
|
this.plugin.doLayout(edge); |
|
return; |
|
} |
|
}.bind(this)); |
|
|
|
|
|
// Find all edges, which have only to dockers |
|
// and is located horizontal/vertical. |
|
// Do layout with those edges |
|
this.edges |
|
// Find all edges with exactly two dockers |
|
.each(function(edge){ |
|
if (edge.dockers.length == 2){ |
|
var p1 = edge.dockers.first().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center(); |
|
var p2 = edge.dockers.last().getAbsoluteReferencePoint() || edge.dockers.first().bounds.center(); |
|
// Find all horizontal/vertical edges |
|
if (Math.abs(-Math.abs(p1.x - p2.x) + Math.abs(this.offset.x)) < 2 || Math.abs(-Math.abs(p1.y - p2.y) + Math.abs(this.offset.y)) < 2){ |
|
this.plugin.doLayout(edge); |
|
} |
|
} |
|
}.bind(this)); |
|
|
|
this.edges.each(function(edge, i){ |
|
this.changes[i].dockerPositions = edge.dockers.map(function(r){ return r.bounds.center() }); |
|
}.bind(this)); |
|
|
|
}, |
|
/** |
|
* Align the bounds if the center is |
|
* the same than the old center |
|
* @params {Object} bounds |
|
* @params {Object} bounds2 |
|
*/ |
|
align: function(bounds, refDocker){ |
|
|
|
var abRef = refDocker.getAbsoluteReferencePoint() || refDocker.bounds.center(); |
|
|
|
var xdif = bounds.center().x-abRef.x; |
|
var ydif = bounds.center().y-abRef.y; |
|
if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3 && this.offset.xs === undefined){ |
|
bounds.moveBy({x:-xdif, y:0}) |
|
} |
|
if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3 && this.offset.ys === undefined){ |
|
bounds.moveBy({y:-ydif, x:0}) |
|
} |
|
|
|
if (this.offset.xs !== undefined || this.offset.ys !== undefined){ |
|
var absPXY = refDocker.getDockedShape().absoluteXY(); |
|
xdif = bounds.center().x-(absPXY.x+((abRef.x-absPXY.x)/this.offset.xs)); |
|
ydif = bounds.center().y-(absPXY.y+((abRef.y-absPXY.y)/this.offset.ys)); |
|
|
|
if (Math.abs(-Math.abs(xdif) + Math.abs(this.offset.x)) < 3){ |
|
bounds.moveBy({x:-(bounds.center().x-abRef.x), y:0}) |
|
} |
|
|
|
if (Math.abs(-Math.abs(ydif) + Math.abs(this.offset.y)) < 3){ |
|
bounds.moveBy({y:-(bounds.center().y-abRef.y), x:0}) |
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Returns a TRUE if there are bend point which overlay the shape |
|
*/ |
|
isBendPointIncluded: function(edge){ |
|
// Get absolute bounds |
|
var ab = edge.dockers.first().getDockedShape(); |
|
var bb = edge.dockers.last().getDockedShape(); |
|
|
|
if (ab) { |
|
ab = ab.absoluteBounds(); |
|
ab.widen(5); |
|
} |
|
|
|
if (bb) { |
|
bb = bb.absoluteBounds(); |
|
bb.widen(20); // Wide with 20 because of the arrow from the edge |
|
} |
|
|
|
return edge.dockers |
|
.any(function(docker, i){ |
|
var c = docker.bounds.center(); |
|
// Dont count first and last |
|
return i != 0 && i != edge.dockers.length-1 && |
|
// Check if the point is included to the absolute bounds |
|
((ab && ab.isIncluded(c)) || (bb && bb.isIncluded(c))) |
|
}) |
|
}, |
|
|
|
removeAllDocker: function(edge){ |
|
edge.dockers.slice(1, edge.dockers.length-1).each(function(docker){ |
|
edge.removeDocker(docker); |
|
}) |
|
}, |
|
executeAgain: function(){ |
|
this.changes.each(function(change){ |
|
// Reset the dockers |
|
this.removeAllDocker(change.edge); |
|
change.dockerPositions.each(function(pos, i){ |
|
if (i==0||i==change.dockerPositions.length-1){ return } |
|
var docker = change.edge.createDocker(undefined, pos); |
|
docker.bounds.centerMoveTo(pos); |
|
docker.update(); |
|
}.bind(this)); |
|
change.edge._update(true); |
|
}.bind(this)); |
|
}, |
|
rollback: function(){ |
|
this.changes.each(function(change){ |
|
// Reset the dockers |
|
this.removeAllDocker(change.edge); |
|
change.oldDockerPositions.each(function(pos, i){ |
|
if (i==0||i==change.oldDockerPositions.length-1){ return } |
|
var docker = change.edge.createDocker(undefined, pos); |
|
docker.bounds.centerMoveTo(pos); |
|
docker.update(); |
|
}.bind(this)); |
|
change.edge._update(true); |
|
}.bind(this)); |
|
} |
|
}); |
|
|
|
this.facade.executeCommands([new Command(allEdges, node, offset, this)]); |
|
|
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX){ var ORYX = {} } |
|
if(!ORYX.Plugins){ ORYX.Plugins = {} } |
|
|
|
/** |
|
This abstract plugin implements the core behaviour of layout |
|
|
|
@class ORYX.Plugins.AbstractLayouter |
|
@constructor Creates a new instance |
|
@author Willi Tscheschner |
|
*/ |
|
ORYX.Plugins.AbstractLayouter = ORYX.Plugins.AbstractPlugin.extend({ |
|
|
|
/** |
|
* 'layouted' defined all types of shapes which will be layouted. |
|
* It can be one value or an array of values. The value |
|
* can be a Stencil ID (as String) or an class type of either |
|
* a ORYX.Core.Node or ORYX.Core.Edge |
|
* @type Array|String|Object |
|
* @memberOf ORYX.Plugins.AbstractLayouter.prototype |
|
*/ |
|
layouted : [], |
|
|
|
/** |
|
* Constructor |
|
* @param {Object} facade |
|
* @memberOf ORYX.Plugins.AbstractLayouter.prototype |
|
*/ |
|
construct: function( facade ){ |
|
arguments.callee.$.construct.apply(this, arguments); |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LAYOUT, this._initLayout.bind(this)); |
|
}, |
|
|
|
/** |
|
* Proofs if this shape should be layouted or not |
|
* @param {Object} shape |
|
* @memberOf ORYX.Plugins.AbstractLayouter.prototype |
|
*/ |
|
isIncludedInLayout: function(shape){ |
|
if (!(this.layouted instanceof Array)){ |
|
this.layouted = [this.layouted].compact(); |
|
} |
|
|
|
// If there are no elements |
|
if (this.layouted.length <= 0) { |
|
// Return TRUE |
|
return true; |
|
} |
|
|
|
// Return TRUE if there is any correlation between |
|
// the 'layouted' attribute and the shape themselve. |
|
return this.layouted.any(function(s){ |
|
if (typeof s == "string") { |
|
return shape.getStencil().id().include(s); |
|
} else { |
|
return shape instanceof s; |
|
} |
|
}) |
|
}, |
|
|
|
/** |
|
* Callback to start the layouting |
|
* @param {Object} event Layout event |
|
* @param {Object} shapes Given shapes |
|
* @memberOf ORYX.Plugins.AbstractLayouter.prototype |
|
*/ |
|
_initLayout: function(event){ |
|
|
|
// Get the shapes |
|
var shapes = [event.shapes].flatten().compact(); |
|
|
|
// Find all shapes which should be layouted |
|
var toLayout = shapes.findAll(function(shape){ |
|
return this.isIncludedInLayout(shape) |
|
}.bind(this)) |
|
|
|
// If there are shapes left |
|
if (toLayout.length > 0){ |
|
// Do layout |
|
this.layout(toLayout); |
|
} |
|
}, |
|
|
|
/** |
|
* Implementation of layouting a set on shapes |
|
* @param {Object} shapes Given shapes |
|
* @memberOf ORYX.Plugins.AbstractLayouter.prototype |
|
*/ |
|
layout: function(shapes){ |
|
throw new Error("Layouter has to implement the layout function.") |
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.Edit = Clazz.extend({ |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
this.clipboard = new ORYX.Plugins.Edit.ClipBoard(); |
|
|
|
//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this)); |
|
|
|
this.facade.offer({ |
|
name: ORYX.I18N.Edit.cut, |
|
description: ORYX.I18N.Edit.cutDesc, |
|
icon: ORYX.PATH + "images/cut.png", |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: 88, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.callEdit.bind(this, this.editCut), |
|
group: ORYX.I18N.Edit.group, |
|
index: 1, |
|
minShape: 1 |
|
}); |
|
|
|
this.facade.offer({ |
|
name: ORYX.I18N.Edit.copy, |
|
description: ORYX.I18N.Edit.copyDesc, |
|
icon: ORYX.PATH + "images/page_copy.png", |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: 67, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.callEdit.bind(this, this.editCopy, [true, false]), |
|
group: ORYX.I18N.Edit.group, |
|
index: 2, |
|
minShape: 1 |
|
}); |
|
|
|
this.facade.offer({ |
|
name: ORYX.I18N.Edit.paste, |
|
description: ORYX.I18N.Edit.pasteDesc, |
|
icon: ORYX.PATH + "images/page_paste.png", |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: 86, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.callEdit.bind(this, this.editPaste), |
|
isEnabled: this.clipboard.isOccupied.bind(this.clipboard), |
|
group: ORYX.I18N.Edit.group, |
|
index: 3, |
|
minShape: 0, |
|
maxShape: 0 |
|
}); |
|
|
|
this.facade.offer({ |
|
name: ORYX.I18N.Edit.del, |
|
description: ORYX.I18N.Edit.delDesc, |
|
icon: ORYX.PATH + "images/cross.png", |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: 8, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
}, |
|
{ |
|
keyCode: 46, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.callEdit.bind(this, this.editDelete), |
|
group: ORYX.I18N.Edit.group, |
|
index: 4, |
|
minShape: 1 |
|
}); |
|
}, |
|
|
|
callEdit: function(fn, args){ |
|
window.setTimeout(function(){ |
|
fn.apply(this, (args instanceof Array ? args : [])); |
|
}.bind(this), 1); |
|
}, |
|
|
|
/** |
|
* Handles the mouse down event and starts the copy-move-paste action, if |
|
* control or meta key is pressed. |
|
*/ |
|
handleMouseDown: function(event) { |
|
if(this._controlPressed) { |
|
this._controlPressed = false; |
|
this.editCopy(); |
|
// console.log("copiedEle: %0",this.clipboard.shapesAsJson) |
|
// console.log("mousevent: %o",event) |
|
this.editPaste(); |
|
event.forceExecution = true; |
|
this.facade.raiseEvent(event, this.clipboard.shapesAsJson); |
|
|
|
} |
|
}, |
|
|
|
/** |
|
* The key handler for this plugin. Every action from the set of cut, copy, |
|
* paste and delete should be accessible trough simple keyboard shortcuts. |
|
* This method checks whether any event triggers one of those actions. |
|
* |
|
* @param {Object} event The keyboard event that should be analysed for |
|
* triggering of this plugin. |
|
*/ |
|
// keyHandler: function(event){ |
|
// //TODO document what event.which is. |
|
// |
|
// ORYX.Log.debug("edit.js handles a keyEvent."); |
|
// |
|
// // assure we have the current event. |
|
// if (!event) |
|
// event = window.event; |
|
// |
|
// |
|
// // get the currently pressed key and state of control key. |
|
// var pressedKey = event.which || event.keyCode; |
|
// var ctrlPressed = event.ctrlKey; |
|
// |
|
// // if the object is to be deleted, do so, and return immediately. |
|
// if ((pressedKey == ORYX.CONFIG.KEY_CODE_DELETE) || |
|
// ((pressedKey == ORYX.CONFIG.KEY_CODE_BACKSPACE) && |
|
// (event.metaKey || event.appleMetaKey))) { |
|
// |
|
// ORYX.Log.debug("edit.js deletes the shape."); |
|
// this.editDelete(); |
|
// return; |
|
// } |
|
// |
|
// // if control key is not pressed, we're not interested anymore. |
|
// if (!ctrlPressed) |
|
// return; |
|
// |
|
// // when ctrl is pressed, switch trough the possibilities. |
|
// switch (pressedKey) { |
|
// |
|
// // cut. |
|
// case ORYX.CONFIG.KEY_CODE_X: |
|
// this.editCut(); |
|
// break; |
|
// |
|
// // copy. |
|
// case ORYX.CONFIG.KEY_CODE_C: |
|
// this.editCopy(); |
|
// break; |
|
// |
|
// // paste. |
|
// case ORYX.CONFIG.KEY_CODE_V: |
|
// this.editPaste(); |
|
// break; |
|
// } |
|
// }, |
|
|
|
/** |
|
* Returns a list of shapes which should be considered while copying. |
|
* Besides the shapes of given ones, edges and attached nodes are added to the result set. |
|
* If one of the given shape is a child of another given shape, it is not put into the result. |
|
*/ |
|
getAllShapesToConsider: function(shapes){ |
|
var shapesToConsider = []; // only top-level shapes |
|
var childShapesToConsider = []; // all child shapes of top-level shapes |
|
|
|
shapes.each(function(shape){ |
|
//Throw away these shapes which have a parent in given shapes |
|
isChildShapeOfAnother = shapes.any(function(s2){ |
|
return s2.hasChildShape(shape); |
|
}); |
|
if(isChildShapeOfAnother) return; |
|
|
|
// This shape should be considered |
|
shapesToConsider.push(shape); |
|
// Consider attached nodes (e.g. intermediate events) |
|
if (shape instanceof ORYX.Core.Node) { |
|
var attached = shape.getOutgoingNodes(); |
|
attached = attached.findAll(function(a){ return !shapes.include(a) }); |
|
shapesToConsider = shapesToConsider.concat(attached); |
|
} |
|
|
|
childShapesToConsider = childShapesToConsider.concat(shape.getChildShapes(true)); |
|
}.bind(this)); |
|
|
|
// All edges between considered child shapes should be considered |
|
// Look for these edges having incoming and outgoing in childShapesToConsider |
|
var edgesToConsider = this.facade.getCanvas().getChildEdges().select(function(edge){ |
|
// Ignore if already added |
|
if(shapesToConsider.include(edge)) return false; |
|
// Ignore if there are no docked shapes |
|
if(edge.getAllDockedShapes().size() === 0) return false; |
|
// True if all docked shapes are in considered child shapes |
|
return edge.getAllDockedShapes().all(function(shape){ |
|
// Remember: Edges can have other edges on outgoing, that is why edges must not be included in childShapesToConsider |
|
return shape instanceof ORYX.Core.Edge || childShapesToConsider.include(shape); |
|
}); |
|
}); |
|
shapesToConsider = shapesToConsider.concat(edgesToConsider); |
|
|
|
return shapesToConsider; |
|
}, |
|
|
|
/** |
|
* Performs the cut operation by first copy-ing and then deleting the |
|
* current selection. |
|
*/ |
|
editCut: function(){ |
|
//TODO document why this returns false. |
|
//TODO document what the magic boolean parameters are supposed to do. |
|
|
|
this.editCopy(false, true); |
|
this.editDelete(true); |
|
return false; |
|
}, |
|
|
|
/** |
|
* Performs the copy operation. |
|
* @param {Object} will_not_update ?? |
|
*/ |
|
editCopy: function( will_update, useNoOffset ){ |
|
var selection = this.facade.getSelection(); |
|
|
|
//if the selection is empty, do not remove the previously copied elements |
|
if(selection.length == 0) return; |
|
|
|
this.clipboard.refresh(selection, this.getAllShapesToConsider(selection), this.facade.getCanvas().getStencil().stencilSet().namespace(), useNoOffset); |
|
|
|
if( will_update ) this.facade.updateSelection(); |
|
}, |
|
|
|
/** |
|
* Performs the paste operation. |
|
*/ |
|
editPaste: function(){ |
|
// Create a new canvas with childShapes |
|
//and stencilset namespace to be JSON Import conform |
|
var canvas = { |
|
childShapes: this.clipboard.shapesAsJson, |
|
stencilset:{ |
|
namespace:this.clipboard.SSnamespace |
|
} |
|
}; |
|
// Apply json helper to iterate over json object |
|
jQuery.extend(canvas, ORYX.Core.AbstractShape.JSONHelper); |
|
|
|
var childShapeResourceIds = canvas.getChildShapes(true).pluck("resourceId"); |
|
var outgoings = {}; |
|
// Iterate over all shapes |
|
canvas.eachChild(function(shape, parent){ |
|
// Throw away these references where referenced shape isn't copied |
|
shape.outgoing = shape.outgoing.select(function(out){ |
|
return childShapeResourceIds.include(out.resourceId); |
|
}); |
|
shape.outgoing.each(function(out){ |
|
if (!outgoings[out.resourceId]){ outgoings[out.resourceId] = []; } |
|
outgoings[out.resourceId].push(shape); |
|
}); |
|
|
|
return shape; |
|
}.bind(this), true, true); |
|
|
|
|
|
// Iterate over all shapes |
|
canvas.eachChild(function(shape, parent){ |
|
|
|
// Check if there has a valid target |
|
if(shape.target && !(childShapeResourceIds.include(shape.target.resourceId))){ |
|
shape.target = undefined; |
|
shape.targetRemoved = true; |
|
} |
|
|
|
// Check if the first docker is removed |
|
if( shape.dockers && |
|
shape.dockers.length >= 1 && |
|
shape.dockers[0].getDocker && |
|
((shape.dockers[0].getDocker().getDockedShape() && |
|
!childShapeResourceIds.include(shape.dockers[0].getDocker().getDockedShape().resourceId)) || |
|
!shape.getShape().dockers[0].getDockedShape()&&!outgoings[shape.resourceId])) { |
|
|
|
shape.sourceRemoved = true; |
|
} |
|
|
|
return shape; |
|
}.bind(this), true, true); |
|
|
|
|
|
// Iterate over top-level shapes |
|
canvas.eachChild(function(shape, parent){ |
|
// All top-level shapes should get an offset in their bounds |
|
// Move the shape occording to COPY_MOVE_OFFSET |
|
if (this.clipboard.useOffset) { |
|
shape.bounds = { |
|
lowerRight: { |
|
x: shape.bounds.lowerRight.x + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
y: shape.bounds.lowerRight.y + ORYX.CONFIG.COPY_MOVE_OFFSET |
|
}, |
|
upperLeft: { |
|
x: shape.bounds.upperLeft.x + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
y: shape.bounds.upperLeft.y + ORYX.CONFIG.COPY_MOVE_OFFSET |
|
} |
|
}; |
|
} |
|
// Only apply offset to shapes with a target |
|
if (shape.dockers){ |
|
shape.dockers = shape.dockers.map(function(docker, i){ |
|
// If shape had a target but the copied does not have anyone anymore, |
|
// migrate the relative dockers to absolute ones. |
|
if( (shape.targetRemoved === true && i == shape.dockers.length - 1&&docker.getDocker) || |
|
(shape.sourceRemoved === true && i == 0&&docker.getDocker)){ |
|
|
|
docker = docker.getDocker().bounds.center(); |
|
} |
|
|
|
// If it is the first docker and it has a docked shape, |
|
// just return the coordinates |
|
if ((i == 0 && docker.getDocker instanceof Function && |
|
shape.sourceRemoved !== true && (docker.getDocker().getDockedShape() || ((outgoings[shape.resourceId]||[]).length > 0 && (!(shape.getShape() instanceof ORYX.Core.Node) || outgoings[shape.resourceId][0].getShape() instanceof ORYX.Core.Node)))) || |
|
(i == shape.dockers.length - 1 && docker.getDocker instanceof Function && |
|
shape.targetRemoved !== true && (docker.getDocker().getDockedShape() || shape.target))){ |
|
|
|
return { |
|
x: docker.x, |
|
y: docker.y, |
|
getDocker: docker.getDocker |
|
} |
|
} else if (this.clipboard.useOffset) { |
|
return { |
|
x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
getDocker: docker.getDocker |
|
}; |
|
} else { |
|
return { |
|
x: docker.x, |
|
y: docker.y, |
|
getDocker: docker.getDocker |
|
}; |
|
} |
|
}.bind(this)); |
|
|
|
} else if (shape.getShape() instanceof ORYX.Core.Node && shape.dockers && shape.dockers.length > 0 && (!shape.dockers.first().getDocker || shape.sourceRemoved === true || !(shape.dockers.first().getDocker().getDockedShape() || outgoings[shape.resourceId]))){ |
|
|
|
shape.dockers = shape.dockers.map(function(docker, i){ |
|
|
|
if((shape.sourceRemoved === true && i == 0&&docker.getDocker)){ |
|
docker = docker.getDocker().bounds.center(); |
|
} |
|
|
|
if (this.clipboard.useOffset) { |
|
return { |
|
x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET, |
|
getDocker: docker.getDocker |
|
}; |
|
} else { |
|
return { |
|
x: docker.x, |
|
y: docker.y, |
|
getDocker: docker.getDocker |
|
}; |
|
} |
|
}.bind(this)); |
|
} |
|
|
|
return shape; |
|
}.bind(this), false, true); |
|
|
|
this.clipboard.useOffset = true; |
|
this.facade.importJSON(canvas); |
|
}, |
|
|
|
/** |
|
* Performs the delete operation. No more asking. |
|
*/ |
|
editDelete: function(){ |
|
var selection = this.facade.getSelection(); |
|
|
|
var clipboard = new ORYX.Plugins.Edit.ClipBoard(); |
|
clipboard.refresh(selection, this.getAllShapesToConsider(selection)); |
|
|
|
var command = new ORYX.Plugins.Edit.DeleteCommand(clipboard , this.facade); |
|
|
|
this.facade.executeCommands([command]); |
|
} |
|
}); |
|
|
|
ORYX.Plugins.Edit.ClipBoard = Clazz.extend({ |
|
construct: function(){ |
|
this.shapesAsJson = []; |
|
this.selection = []; |
|
this.SSnamespace=""; |
|
this.useOffset=true; |
|
}, |
|
isOccupied: function(){ |
|
return this.shapesAsJson.length > 0; |
|
}, |
|
refresh: function(selection, shapes, namespace, useNoOffset){ |
|
this.selection = selection; |
|
this.SSnamespace=namespace; |
|
// Store outgoings, targets and parents to restore them later on |
|
this.outgoings = {}; |
|
this.parents = {}; |
|
this.targets = {}; |
|
this.useOffset = useNoOffset !== true; |
|
|
|
this.shapesAsJson = shapes.map(function(shape){ |
|
var s = shape.toJSON(); |
|
s.parent = {resourceId : shape.getParentShape().resourceId}; |
|
s.parentIndex = shape.getParentShape().getChildShapes().indexOf(shape) |
|
return s; |
|
}); |
|
} |
|
}); |
|
|
|
ORYX.Plugins.Edit.DeleteCommand = ORYX.Core.Command.extend({ |
|
construct: function(clipboard, facade){ |
|
this.clipboard = clipboard; |
|
this.shapesAsJson = clipboard.shapesAsJson; |
|
this.facade = facade; |
|
|
|
// Store dockers of deleted shapes to restore connections |
|
this.dockers = this.shapesAsJson.map(function(shapeAsJson) { |
|
var shape = shapeAsJson.getShape(); |
|
var incomingDockers = shape.getIncomingShapes().map(function(s){return s.getDockers().last();}); |
|
var outgoingDockers = shape.getOutgoingShapes().map(function(s){return s.getDockers().first();}); |
|
var dockers = shape.getDockers().concat(incomingDockers, outgoingDockers).compact().map(function(docker){ |
|
return { |
|
object: docker, |
|
referencePoint: docker.referencePoint, |
|
dockedShape: docker.getDockedShape() |
|
}; |
|
}); |
|
return dockers; |
|
}).flatten(); |
|
}, |
|
execute: function(){ |
|
this.shapesAsJson.each(function(shapeAsJson){ |
|
// Delete shape |
|
this.facade.deleteShape(shapeAsJson.getShape()); |
|
}.bind(this)); |
|
|
|
this.facade.setSelection([]); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
|
|
}, |
|
rollback: function(){ |
|
this.shapesAsJson.each(function(shapeAsJson) { |
|
var shape = shapeAsJson.getShape(); |
|
var parent = this.facade.getCanvas().getChildShapeByResourceId(shapeAsJson.parent.resourceId) || this.facade.getCanvas(); |
|
parent.add(shape, shape.parentIndex); |
|
}.bind(this)); |
|
|
|
//reconnect shapes |
|
this.dockers.each(function(d) { |
|
d.object.setDockedShape(d.dockedShape); |
|
d.object.setReferencePoint(d.referencePoint); |
|
}.bind(this)); |
|
|
|
this.facade.setSelection(this.selectedShapes); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
|
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* @namespace Oryx name space for plugins |
|
* @name ORYX.Plugins |
|
*/ |
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
/** |
|
* The view plugin offers all of zooming functionality accessible over the |
|
* tool bar. This are zoom in, zoom out, zoom to standard, zoom fit to model. |
|
* |
|
* @class ORYX.Plugins.View |
|
* @extends Clazz |
|
* @param {Object} facade The editor facade for plugins. |
|
*/ |
|
ORYX.Plugins.View = { |
|
/** @lends ORYX.Plugins.View.prototype */ |
|
facade: undefined, |
|
|
|
construct: function(facade, ownPluginData) { |
|
this.facade = facade; |
|
//Standard Values |
|
this.zoomLevel = 1.0; |
|
this.maxFitToScreenLevel=1.5; |
|
this.minZoomLevel = 0.1; |
|
this.maxZoomLevel = 2.5; |
|
this.diff=5; //difference between canvas and view port, s.th. like toolbar?? |
|
|
|
//Read properties |
|
if (ownPluginData !== undefined && ownPluginData !== null) { |
|
ownPluginData.properties.each( function(property) { |
|
if (property.zoomLevel) {this.zoomLevel = Number(1.0);} |
|
if (property.maxFitToScreenLevel) {this.maxFitToScreenLevel=Number(property.maxFitToScreenLevel);} |
|
if (property.minZoomLevel) {this.minZoomLevel = Number(property.minZoomLevel);} |
|
if (property.maxZoomLevel) {this.maxZoomLevel = Number(property.maxZoomLevel);} |
|
}.bind(this)); |
|
} |
|
|
|
|
|
/* Register zoom in */ |
|
this.facade.offer({ |
|
'name':ORYX.I18N.View.zoomIn, |
|
'functionality': this.zoom.bind(this, [1.0 + ORYX.CONFIG.ZOOM_OFFSET]), |
|
'group': ORYX.I18N.View.group, |
|
'icon': ORYX.PATH + "images/magnifier_zoom_in.png", |
|
'description': ORYX.I18N.View.zoomInDesc, |
|
'index': 1, |
|
'minShape': 0, |
|
'maxShape': 0, |
|
'isEnabled': function(){return this.zoomLevel < this.maxZoomLevel }.bind(this)}); |
|
|
|
/* Register zoom out */ |
|
this.facade.offer({ |
|
'name':ORYX.I18N.View.zoomOut, |
|
'functionality': this.zoom.bind(this, [1.0 - ORYX.CONFIG.ZOOM_OFFSET]), |
|
'group': ORYX.I18N.View.group, |
|
'icon': ORYX.PATH + "images/magnifier_zoom_out.png", |
|
'description': ORYX.I18N.View.zoomOutDesc, |
|
'index': 2, |
|
'minShape': 0, |
|
'maxShape': 0, |
|
'isEnabled': function(){ return this._checkSize() }.bind(this)}); |
|
|
|
/* Register zoom standard */ |
|
this.facade.offer({ |
|
'name':ORYX.I18N.View.zoomStandard, |
|
'functionality': this.setAFixZoomLevel.bind(this, 1), |
|
'group': ORYX.I18N.View.group, |
|
'icon': ORYX.PATH + "images/zoom_standard.png", |
|
'cls' : 'icon-large', |
|
'description': ORYX.I18N.View.zoomStandardDesc, |
|
'index': 3, |
|
'minShape': 0, |
|
'maxShape': 0, |
|
'isEnabled': function(){return this.zoomLevel != 1}.bind(this) |
|
}); |
|
|
|
/* Register zoom fit to model */ |
|
this.facade.offer({ |
|
'name':ORYX.I18N.View.zoomFitToModel, |
|
'functionality': this.zoomFitToModel.bind(this), |
|
'group': ORYX.I18N.View.group, |
|
'icon': ORYX.PATH + "images/image.png", |
|
'description': ORYX.I18N.View.zoomFitToModelDesc, |
|
'index': 4, |
|
'minShape': 0, |
|
'maxShape': 0 |
|
}); |
|
}, |
|
|
|
/** |
|
* It sets the zoom level to a fix value and call the zooming function. |
|
* |
|
* @param {Number} zoomLevel |
|
* the zoom level |
|
*/ |
|
setAFixZoomLevel : function(zoomLevel) { |
|
this.zoomLevel = zoomLevel; |
|
this._checkZoomLevelRange(); |
|
this.zoom(1); |
|
}, |
|
|
|
/** |
|
* It does the actual zooming. It changes the viewable size of the canvas |
|
* and all to its child elements. |
|
* |
|
* @param {Number} factor |
|
* the factor to adjust the zoom level |
|
*/ |
|
zoom: function(factor) { |
|
// TODO: Zoomen auf allen Objekten im SVG-DOM |
|
|
|
this.zoomLevel *= factor; |
|
var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode; |
|
var canvas = this.facade.getCanvas(); |
|
var newWidth = canvas.bounds.width() * this.zoomLevel; |
|
var newHeight = canvas.bounds.height() * this.zoomLevel; |
|
|
|
/* Set new top offset */ |
|
var offsetTop = (canvas.node.parentNode.parentNode.parentNode.offsetHeight - newHeight) / 2.0; |
|
offsetTop = offsetTop > 20 ? offsetTop - 20 : 0; |
|
canvas.node.parentNode.parentNode.style.marginTop = offsetTop + "px"; |
|
offsetTop += 5; |
|
canvas.getHTMLContainer().style.top = offsetTop + "px"; |
|
|
|
/*readjust scrollbar*/ |
|
var newScrollTop= scrollNode.scrollTop - Math.round((canvas.getHTMLContainer().parentNode.getHeight()-newHeight) / 2)+this.diff; |
|
var newScrollLeft= scrollNode.scrollLeft - Math.round((canvas.getHTMLContainer().parentNode.getWidth()-newWidth) / 2)+this.diff; |
|
|
|
/* Set new Zoom-Level */ |
|
canvas.setSize({width: newWidth, height: newHeight}, true); |
|
|
|
/* Set Scale-Factor */ |
|
canvas.node.setAttributeNS(null, "transform", "scale(" +this.zoomLevel+ ")"); |
|
|
|
/* Refresh the Selection */ |
|
this.facade.updateSelection(); |
|
scrollNode.scrollTop=newScrollTop; |
|
scrollNode.scrollLeft=newScrollLeft; |
|
|
|
/* Update the zoom-level*/ |
|
canvas.zoomLevel = this.zoomLevel; |
|
}, |
|
|
|
|
|
/** |
|
* It calculates the zoom level to fit whole model into the visible area |
|
* of the canvas. Than the model gets zoomed and the position of the |
|
* scroll bars are adjusted. |
|
* |
|
*/ |
|
zoomFitToModel: function() { |
|
|
|
/* Get the size of the visible area of the canvas */ |
|
var scrollNode = this.facade.getCanvas().getHTMLContainer().parentNode.parentNode; |
|
var visibleHeight = scrollNode.getHeight() - 30; |
|
var visibleWidth = scrollNode.getWidth() - 30; |
|
|
|
var nodes = this.facade.getCanvas().getChildShapes(); |
|
|
|
if(!nodes || nodes.length < 1) { |
|
return false; |
|
} |
|
|
|
/* Calculate size of canvas to fit the model */ |
|
var bounds = nodes[0].absoluteBounds().clone(); |
|
nodes.each(function(node) { |
|
bounds.include(node.absoluteBounds().clone()); |
|
}); |
|
|
|
|
|
/* Set new Zoom Level */ |
|
var scaleFactorWidth = visibleWidth / bounds.width(); |
|
var scaleFactorHeight = visibleHeight / bounds.height(); |
|
|
|
/* Choose the smaller zoom level to fit the whole model */ |
|
var zoomFactor = scaleFactorHeight < scaleFactorWidth ? scaleFactorHeight : scaleFactorWidth; |
|
|
|
/*Test if maximum zoom is reached*/ |
|
if(zoomFactor>this.maxFitToScreenLevel){zoomFactor=this.maxFitToScreenLevel} |
|
/* Do zooming */ |
|
this.setAFixZoomLevel(zoomFactor); |
|
|
|
/* Set scroll bar position */ |
|
scrollNode.scrollTop = Math.round(bounds.upperLeft().y * this.zoomLevel) - 5; |
|
scrollNode.scrollLeft = Math.round(bounds.upperLeft().x * this.zoomLevel) - 5; |
|
|
|
}, |
|
|
|
/** |
|
* It checks if the zoom level is less or equal to the level, which is required |
|
* to schow the whole canvas. |
|
* |
|
* @private |
|
*/ |
|
_checkSize:function(){ |
|
var canvasParent=this.facade.getCanvas().getHTMLContainer().parentNode; |
|
var minForCanvas= Math.min((canvasParent.parentNode.getWidth()/canvasParent.getWidth()),(canvasParent.parentNode.getHeight()/canvasParent.getHeight())); |
|
return 1.05 > minForCanvas; |
|
|
|
}, |
|
/** |
|
* It checks if the zoom level is included in the definined zoom |
|
* level range. |
|
* |
|
* @private |
|
*/ |
|
_checkZoomLevelRange: function() { |
|
/*var canvasParent=this.facade.getCanvas().getHTMLContainer().parentNode; |
|
var maxForCanvas= Math.max((canvasParent.parentNode.getWidth()/canvasParent.getWidth()),(canvasParent.parentNode.getHeight()/canvasParent.getHeight())); |
|
if(this.zoomLevel > maxForCanvas) { |
|
this.zoomLevel = maxForCanvas; |
|
}*/ |
|
if(this.zoomLevel < this.minZoomLevel) { |
|
this.zoomLevel = this.minZoomLevel; |
|
} |
|
|
|
if(this.zoomLevel > this.maxZoomLevel) { |
|
this.zoomLevel = this.maxZoomLevel; |
|
} |
|
} |
|
}; |
|
|
|
ORYX.Plugins.View = Clazz.extend(ORYX.Plugins.View); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
if(!Signavio){ var Signavio = {} }; |
|
if (!Signavio.Core) { Signavio.Core = {} }; |
|
Signavio.Core.Version = "1.0"; |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!Signavio) { |
|
var Signavio = new Object(); |
|
} |
|
|
|
if (!Signavio.Plugins) { |
|
Signavio.Plugins = new Object(); |
|
} |
|
|
|
if (!Signavio.Plugins.Utils) { |
|
Signavio.Plugins.Utils = new Object(); |
|
} |
|
|
|
if (!Signavio.Helper) { |
|
Signavio.Helper = new Object(); |
|
} |
|
|
|
|
|
new function() { |
|
|
|
/** |
|
* Provides an uniq id |
|
* @overwrite |
|
* @return {String} |
|
* |
|
*/ |
|
ORYX.Editor.provideId = function() { |
|
var res = [], hex = '0123456789ABCDEF'; |
|
|
|
for (var i = 0; i < 36; i++) res[i] = Math.floor(Math.random()*0x10); |
|
|
|
res[14] = 4; |
|
res[19] = (res[19] & 0x3) | 0x8; |
|
|
|
for (var i = 0; i < 36; i++) res[i] = hex[res[i]]; |
|
|
|
res[8] = res[13] = res[18] = res[23] = '-'; |
|
|
|
return "sid-" + res.join(''); |
|
}; |
|
|
|
|
|
}(); |
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) { |
|
ORYX.Plugins = new Object(); |
|
} |
|
|
|
/** |
|
* This plugin is responsible for displaying loading indicators and to prevent |
|
* the user from accidently unloading the page by, e.g., pressing the backspace |
|
* button and returning to the previous site in history. |
|
* @param {Object} facade The editor plugin facade to register enhancements with. |
|
*/ |
|
ORYX.Plugins.Loading = { |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
|
|
// The parent Node |
|
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", this.facade.getCanvas().getHTMLContainer().parentNode, ['div', { |
|
'class': 'LoadingIndicator' |
|
}, '']); |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_ENABLE, this.enableLoading.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_DISABLE, this.disableLoading.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADING_STATUS, this.showStatus.bind(this)); |
|
|
|
this.disableLoading(); |
|
}, |
|
|
|
enableLoading: function(options){ |
|
if(options.text) |
|
this.node.innerHTML = options.text + "..."; |
|
else |
|
this.node.innerHTML = ORYX.I18N.Loading.waiting; |
|
this.node.removeClassName('StatusIndicator'); |
|
this.node.addClassName('LoadingIndicator'); |
|
this.node.style.display = "block"; |
|
|
|
var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode; |
|
|
|
this.node.style.top = pos.offsetTop + 'px'; |
|
this.node.style.left = pos.offsetLeft +'px'; |
|
|
|
}, |
|
|
|
disableLoading: function(){ |
|
this.node.style.display = "none"; |
|
}, |
|
|
|
showStatus: function(options) { |
|
if(options.text) { |
|
this.node.innerHTML = options.text; |
|
this.node.addClassName('StatusIndicator'); |
|
this.node.removeClassName('LoadingIndicator'); |
|
this.node.style.display = 'block'; |
|
|
|
var pos = this.facade.getCanvas().rootNode.parentNode.parentNode.parentNode.parentNode; |
|
|
|
this.node.style.top = pos.offsetTop + 'px'; |
|
this.node.style.left = pos.offsetLeft +'px'; |
|
|
|
var tout = options.timeout ? options.timeout : 2000; |
|
|
|
window.setTimeout((function(){ |
|
|
|
this.disableLoading(); |
|
|
|
}).bind(this), tout); |
|
} |
|
|
|
} |
|
} |
|
|
|
ORYX.Plugins.Loading = Clazz.extend(ORYX.Plugins.Loading); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
if (!ORYX.Plugins) { |
|
ORYX.Plugins = new Object(); |
|
} |
|
|
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
/** |
|
* This plugin is responsible for resizing the canvas. |
|
* @param {Object} facade The editor plugin facade to register enhancements with. |
|
*/ |
|
ORYX.Plugins.CanvasResize = Clazz.extend({ |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
new ORYX.Plugins.CanvasResizeButton( this.facade.getCanvas(), "N", this.resize.bind(this)); |
|
new ORYX.Plugins.CanvasResizeButton( this.facade.getCanvas(), "W", this.resize.bind(this)); |
|
new ORYX.Plugins.CanvasResizeButton( this.facade.getCanvas(), "E", this.resize.bind(this)); |
|
new ORYX.Plugins.CanvasResizeButton( this.facade.getCanvas(), "S", this.resize.bind(this)); |
|
|
|
window.setTimeout(function(){jQuery(window).trigger('resize');}); |
|
|
|
}, |
|
|
|
resize: function( position, shrink ){ |
|
|
|
resizeCanvas = function(position, extentionSize, facade) { |
|
var canvas = facade.getCanvas(); |
|
var b = canvas.bounds; |
|
var scrollNode = facade.getCanvas().getHTMLContainer().parentNode.parentNode; |
|
|
|
if( position == "E" || position == "W"){ |
|
canvas.setSize({width: (b.width() + extentionSize)*canvas.zoomLevel, height: (b.height())*canvas.zoomLevel}) |
|
|
|
} else if( position == "S" || position == "N"){ |
|
canvas.setSize({width: (b.width())*canvas.zoomLevel, height: (b.height() + extentionSize)*canvas.zoomLevel}) |
|
} |
|
|
|
if( position == "N" || position == "W"){ |
|
|
|
var move = position == "N" ? {x: 0, y: extentionSize}: {x: extentionSize, y: 0 }; |
|
|
|
// Move all children |
|
canvas.getChildNodes(false, function(shape){ shape.bounds.moveBy(move) }) |
|
// Move all dockers, when the edge has at least one docked shape |
|
var edges = canvas.getChildEdges().findAll(function(edge){ return edge.getAllDockedShapes().length > 0}) |
|
var dockers = edges.collect(function(edge){ return edge.dockers.findAll(function(docker){ return !docker.getDockedShape() })}).flatten(); |
|
dockers.each(function(docker){ docker.bounds.moveBy(move)}) |
|
} else if( position == "S" ){ |
|
scrollNode.scrollTop += extentionSize; |
|
} else if( position == "E" ){ |
|
scrollNode.scrollLeft += extentionSize; |
|
} |
|
|
|
jQuery(window).trigger('resize'); |
|
|
|
canvas.update(); |
|
facade.updateSelection(); |
|
} |
|
|
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(position, extentionSize, facade){ |
|
this.position = position; |
|
this.extentionSize = extentionSize; |
|
this.facade = facade; |
|
}, |
|
execute: function(){ |
|
resizeCanvas(this.position, this.extentionSize, this.facade); |
|
}, |
|
rollback: function(){ |
|
resizeCanvas(this.position, -this.extentionSize, this.facade); |
|
}, |
|
update:function(){ |
|
} |
|
}); |
|
|
|
var extentionSize = ORYX.CONFIG.CANVAS_RESIZE_INTERVAL; |
|
if(shrink) extentionSize = -extentionSize; |
|
var command = new commandClass(position, extentionSize, this.facade); |
|
|
|
this.facade.executeCommands([command]); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
ORYX.Plugins.CanvasResizeButton = Clazz.extend({ |
|
|
|
construct: function(canvas, position, callback){ |
|
this.canvas = canvas; |
|
var parentNode = canvas.getHTMLContainer().parentNode; |
|
|
|
window.myParent=parentNode; |
|
|
|
var actualScrollNode = jQuery('#canvasSection')[0]; |
|
var scrollNode = actualScrollNode; |
|
var canvasNode = jQuery('#canvasSection').find(".ORYX_Editor")[0]; |
|
var svgRootNode = canvasNode.children[0]; |
|
|
|
var iconClass = 'glyphicon glyphicon-chevron-'; |
|
var iconClassShrink = 'glyphicon glyphicon-chevron-'; |
|
if(position == 'N') { |
|
iconClass += 'up'; |
|
iconClassShrink += 'down'; |
|
} else if(position == 'S') { |
|
iconClass += 'down'; |
|
iconClassShrink += 'up'; |
|
} else if(position == 'E') { |
|
iconClass += 'right'; |
|
iconClassShrink += 'left'; |
|
} else if(position == 'W') { |
|
iconClass += 'left'; |
|
iconClassShrink += 'right'; |
|
} |
|
|
|
// The buttons |
|
var idGrow = 'canvas-shrink-' + position; |
|
var idShrink = 'canvas-grow-' + position; |
|
|
|
var buttonGrow = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parentNode, ['div', {'class': 'canvas_resize_indicator canvas_resize_indicator_grow' + ' ' + position, 'id': idGrow ,'title':ORYX.I18N.RESIZE.tipGrow+ORYX.I18N.RESIZE[position]}, |
|
['i', {'class' : iconClass}] |
|
]); |
|
var buttonShrink = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parentNode, ['div', {'class': 'canvas_resize_indicator canvas_resize_indicator_shrink' + ' ' + position, 'id': idShrink ,'title':ORYX.I18N.RESIZE.tipGrow+ORYX.I18N.RESIZE[position]}, |
|
['i', {'class' : iconClassShrink}] |
|
]); |
|
// Defines a callback which gives back |
|
// a boolean if the current mouse event |
|
// is over the particular button area |
|
var offSetWidth = 60; |
|
var isOverOffset = function(event) { |
|
|
|
var isOverButton = event.target.id.indexOf("canvas-shrink") != -1 |
|
|| event.target.id.indexOf("canvas-grow") != -1 |
|
|| event.target.parentNode.id.indexOf("canvas-shrink") != -1 |
|
|| event.target.parentNode.id.indexOf("canvas-grow") != -1; |
|
if(isOverButton) { |
|
if(event.target.id == idGrow || event.target.id == idShrink || |
|
event.target.parentNode.id == idGrow || event.target.parentNode.id == idShrink ) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
if(event.target!=parentNode && event.target!=scrollNode&& event.target!=scrollNode.firstChild&& event.target!=svgRootNode&& event.target!=scrollNode){ return false; } |
|
|
|
//if(inCanvas){offSetWidth=30}else{offSetWidth=30*2} |
|
//Safari work around |
|
var X=event.offsetX !== undefined ? event.offsetX : event.layerX; |
|
var Y=event.offsetY !== undefined ? event.offsetY : event.layerY; |
|
|
|
var canvasOffset = 0; |
|
if(canvasNode.clientWidth < actualScrollNode.clientWidth) { |
|
var widthDiff = actualScrollNode.clientWidth - canvasNode.clientWidth; |
|
canvasOffset = widthDiff / 2; |
|
} |
|
|
|
// Adjust to relative location to the actual viewport |
|
Y = Y - actualScrollNode.scrollTop; |
|
X = X - actualScrollNode.scrollLeft; |
|
|
|
|
|
if(position == "N"){ |
|
return Y < offSetWidth; |
|
} else if(position == "W"){ |
|
return X < offSetWidth + canvasOffset; |
|
} else if(position == "E"){ |
|
return actualScrollNode.clientWidth - X < offSetWidth + canvasOffset; |
|
} else if(position == "S"){ |
|
return actualScrollNode.clientHeight - Y < offSetWidth; |
|
} |
|
|
|
return false; |
|
}; |
|
|
|
var showButtons = (function() { |
|
buttonGrow.show(); |
|
|
|
var w = canvas.bounds.width(); |
|
var h = canvas.bounds.height(); |
|
|
|
if(position=="N" && (h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT)) buttonShrink.show(); |
|
else if(position=="E" && (w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH)) buttonShrink.show(); |
|
else if(position=="S" && (h - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_HEIGHT)) buttonShrink.show(); |
|
else if(position=="W" && (w - ORYX.CONFIG.CANVAS_RESIZE_INTERVAL > ORYX.CONFIG.CANVAS_MIN_WIDTH)) buttonShrink.show(); |
|
else buttonShrink.hide(); |
|
|
|
|
|
}).bind(this); |
|
|
|
var hideButtons = function() { |
|
buttonGrow.hide(); |
|
buttonShrink.hide(); |
|
}; |
|
|
|
// If the mouse move is over the button area, show the button |
|
parentNode.parentNode.addEventListener( ORYX.CONFIG.EVENT_MOUSEMOVE, function(event){ if( isOverOffset(event) ){showButtons();} else {hideButtons()}} , false ); |
|
// If the mouse is over the button, show them |
|
buttonGrow.addEventListener( ORYX.CONFIG.EVENT_MOUSEOVER, function(event){showButtons();}, true ); |
|
buttonShrink.addEventListener( ORYX.CONFIG.EVENT_MOUSEOVER, function(event){showButtons();}, true ); |
|
// If the mouse is out, hide the button |
|
//scrollNode.addEventListener( ORYX.CONFIG.EVENT_MOUSEOUT, function(event){button.hide()}, true ) |
|
parentNode.parentNode.addEventListener( ORYX.CONFIG.EVENT_MOUSEOUT, function(event){hideButtons()} , true ); |
|
//svgRootNode.addEventListener( ORYX.CONFIG.EVENT_MOUSEOUT, function(event){ inCanvas = false } , true ); |
|
|
|
// Hide the button initialy |
|
hideButtons(); |
|
|
|
// Add the callbacks |
|
buttonGrow.addEventListener('click', function(){callback( position ); showButtons();}, true); |
|
buttonShrink.addEventListener('click', function(){callback( position, true ); showButtons();}, true); |
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.RenameShapes = Clazz.extend({ |
|
|
|
facade: undefined, |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_CANVAS_SCROLL, this.hideField.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DBLCLICK, this.actOnDBLClick.bind(this)); |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
keyCode: 113, // F2-Key |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.renamePerF2.bind(this) |
|
}); |
|
|
|
|
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.hide.bind(this), true); |
|
}, |
|
|
|
/** |
|
* This method handles the "F2" key down event. The selected shape are looked |
|
* up and the editing of title/name of it gets started. |
|
*/ |
|
renamePerF2 : function() { |
|
var selectedShapes = this.facade.getSelection(); |
|
this.actOnDBLClick(undefined, selectedShapes.first()); |
|
}, |
|
|
|
actOnDBLClick: function(evt, shape){ |
|
|
|
if( !(shape instanceof ORYX.Core.Shape) ){ return; } |
|
|
|
// Destroys the old input, if there is one |
|
this.destroy(); |
|
|
|
// Get all properties which where at least one ref to view is set |
|
var props = shape.getStencil().properties().findAll(function(item){ |
|
return (item.refToView() |
|
&& item.refToView().length > 0 |
|
&& item.directlyEditable()); |
|
}); |
|
// from these, get all properties where write access are and the type is String or Expression |
|
props = props.findAll(function(item){ return !item.readonly() && (item.type() == ORYX.CONFIG.TYPE_STRING || item.type() == ORYX.CONFIG.TYPE_EXPRESSION || item.type() == ORYX.CONFIG.TYPE_DATASOURCE); }); |
|
|
|
// Get all ref ids |
|
var allRefToViews = props.collect(function(prop){ return prop.refToView(); }).flatten().compact(); |
|
// Get all labels from the shape with the ref ids |
|
var labels = shape.getLabels().findAll(function(label){ return allRefToViews.any(function(toView){ return label.id.endsWith(toView); }); }); |
|
|
|
// If there are no referenced labels --> return |
|
if( labels.length == 0 ){ return; } |
|
|
|
// Define the nearest label |
|
var nearestLabel = labels.length <= 1 ? labels[0] : null; |
|
if( !nearestLabel ){ |
|
nearestLabel = labels.find(function(label){ return label.node == evt.target || label.node == evt.target.parentNode; }); |
|
if( !nearestLabel ){ |
|
|
|
var evtCoord = this.facade.eventCoordinates(evt); |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
evtCoord.x = evtCoord.x / additionalIEZoom; |
|
evtCoord.y = evtCoord.y / additionalIEZoom; |
|
} |
|
|
|
evtCoord.y += $("editor-header").clientHeight - $("canvasSection").scrollTop - 5; |
|
if (KISBPM.HEADER_CONFIG.showAppTitle == false) |
|
{ |
|
evtCoord.y += 61; |
|
} |
|
|
|
evtCoord.x -= $("canvasSection").scrollLeft; |
|
|
|
var trans = this.facade.getCanvas().rootNode.lastChild.getScreenCTM(); |
|
evtCoord.x *= trans.a; |
|
evtCoord.y *= trans.d; |
|
|
|
var diff = labels.collect(function(label){ |
|
var center = this.getCenterPosition( label.node ); |
|
var len = Math.sqrt( Math.pow(center.x - evtCoord.x, 2) + Math.pow(center.y - evtCoord.y, 2)); |
|
return {diff: len, label: label}; |
|
}.bind(this)); |
|
|
|
diff.sort(function(a, b){ return a.diff > b.diff; }); |
|
|
|
nearestLabel = diff[0].label; |
|
|
|
} |
|
} |
|
// Get the particular property for the label |
|
var prop = props.find(function(item){ return item.refToView().any(function(toView){ return nearestLabel.id == shape.id + toView; });}); |
|
|
|
// Get the center position from the nearest label |
|
var width = Math.min(Math.max(100, shape.bounds.width()), 200); |
|
var center = this.getCenterPosition( nearestLabel.node, shape ); |
|
center.x -= (width/2); |
|
var propId = prop.prefix() + "-" + prop.id(); |
|
var textInput = document.createElement("textarea"); |
|
textInput.id = 'shapeTextInput'; |
|
textInput.style.position = 'absolute'; |
|
textInput.style.width = width + 'px'; |
|
textInput.style.left = (center.x < 10) ? 10 : center.x + 'px'; |
|
textInput.style.top = (center.y - 15) + 'px'; |
|
textInput.className = 'x-form-textarea x-form-field x_form_text_set_absolute'; |
|
textInput.value = shape.properties[propId]; |
|
this.oldValueText = shape.properties[propId]; |
|
document.getElementById('canvasSection').appendChild(textInput); |
|
this.shownTextField = textInput; |
|
|
|
|
|
// Value change listener needs to be defined now since we reference it in the text field |
|
this.updateValueFunction = function(newValue, oldValue) { |
|
var currentEl = shape; |
|
var facade = this.facade; |
|
|
|
if (oldValue != newValue) { |
|
// Implement the specific command for property change |
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(){ |
|
this.el = currentEl; |
|
this.propId = propId; |
|
this.oldValue = oldValue; |
|
this.newValue = newValue; |
|
this.facade = facade; |
|
}, |
|
execute: function(){ |
|
this.el.setProperty(this.propId, this.newValue); |
|
//this.el.update(); |
|
this.facade.setSelection([this.el]); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
}, |
|
rollback: function(){ |
|
this.el.setProperty(this.propId, this.oldValue); |
|
//this.el.update(); |
|
this.facade.setSelection([this.el]); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
} |
|
}); |
|
// Instantiated the class |
|
var command = new commandClass(); |
|
|
|
// Execute the command |
|
this.facade.executeCommands([command]); |
|
} |
|
}.bind(this); |
|
|
|
jQuery("#shapeTextInput").focus(); |
|
|
|
jQuery("#shapeTextInput").autogrow(); |
|
|
|
// Disable the keydown in the editor (that when hitting the delete button, the shapes not get deleted) |
|
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN); |
|
|
|
}, |
|
|
|
getCenterPosition: function(svgNode, shape){ |
|
|
|
if (!svgNode) { return {x:0, y:0}; } |
|
|
|
var scale = this.facade.getCanvas().node.getScreenCTM(); |
|
var absoluteXY = shape.bounds.upperLeft(); |
|
|
|
var hasParent = true; |
|
var searchShape = shape; |
|
while (hasParent) |
|
{ |
|
if (searchShape.getParentShape().getStencil().idWithoutNs() === 'BPMNDiagram') |
|
{ |
|
hasParent = false; |
|
} |
|
else |
|
{ |
|
var parentXY = searchShape.getParentShape().bounds.upperLeft(); |
|
absoluteXY.x += parentXY.x; |
|
absoluteXY.y += parentXY.y; |
|
searchShape = searchShape.getParentShape(); |
|
} |
|
} |
|
|
|
var center = shape.bounds.midPoint(); |
|
center.x += absoluteXY.x + scale.e; |
|
center.y += absoluteXY.y + scale.f; |
|
|
|
center.x *= scale.a; |
|
center.y *= scale.d; |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom === 1) { |
|
center.y = center.y - jQuery("#canvasSection").offset().top + 5; |
|
center.x -= jQuery("#canvasSection").offset().left; |
|
|
|
} else { |
|
var canvasOffsetLeft = jQuery("#canvasSection").offset().left; |
|
var canvasScrollLeft = jQuery("#canvasSection").scrollLeft(); |
|
var canvasScrollTop = jQuery("#canvasSection").scrollTop(); |
|
|
|
var offset = scale.e - (canvasOffsetLeft * additionalIEZoom); |
|
var additionaloffset = 0; |
|
if (offset > 10) { |
|
additionaloffset = (offset / additionalIEZoom) - offset; |
|
} |
|
center.y = center.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + 5 + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop); |
|
center.x = center.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft); |
|
} |
|
|
|
|
|
return center; |
|
}, |
|
|
|
hide: function(e){ |
|
if (this.shownTextField && (!e || e.target !== this.shownTextField)) { |
|
var newValue = this.shownTextField.value; |
|
if (newValue !== this.oldValueText) |
|
{ |
|
this.updateValueFunction(newValue, this.oldValueText); |
|
} |
|
this.destroy(); |
|
} |
|
}, |
|
|
|
hideField: function(e){ |
|
if (this.shownTextField) { |
|
this.destroy(); |
|
} |
|
}, |
|
|
|
destroy: function(e){ |
|
var textInputComp = jQuery("#shapeTextInput"); |
|
if( textInputComp ){ |
|
textInputComp.remove(); |
|
delete this.shownTextField; |
|
|
|
this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN); |
|
} |
|
} |
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
/** |
|
* Supports EPCs by offering a syntax check and export and import ability.. |
|
* |
|
* |
|
*/ |
|
ORYX.Plugins.ProcessLink = Clazz.extend({ |
|
|
|
facade: undefined, |
|
|
|
/** |
|
* Offers the plugin functionality: |
|
* |
|
*/ |
|
construct: function(facade) { |
|
|
|
this.facade = facade; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPERTY_CHANGED, this.propertyChanged.bind(this) ); |
|
|
|
}, |
|
|
|
|
|
/** |
|
* |
|
* @param {Object} option |
|
*/ |
|
propertyChanged: function( option, node){ |
|
|
|
if( option.name !== "oryx-refuri" || !node instanceof ORYX.Core.Node ){ return } |
|
|
|
|
|
if( option.value && option.value.length > 0 && option.value != "undefined"){ |
|
|
|
this.show( node, option.value ); |
|
|
|
} else { |
|
|
|
this.hide( node ); |
|
|
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Shows the Link for a particular shape with a specific url |
|
* |
|
* @param {Object} shape |
|
* @param {Object} url |
|
*/ |
|
show: function( shape, url){ |
|
|
|
|
|
// Generate the svg-representation of a link |
|
var link = ORYX.Editor.graft("http://www.w3.org/2000/svg", null , |
|
[ 'a', |
|
{'target': '_blank'}, |
|
['path', |
|
{ "stroke-width": 1.0, "stroke":"#00DD00", "fill": "#00AA00", "d": "M3,3 l0,-2.5 l7.5,0 l0,-2.5 l7.5,4.5 l-7.5,3.5 l0,-2.5 l-8,0", "line-captions": "round"} |
|
] |
|
]); |
|
|
|
var link = ORYX.Editor.graft("http://www.w3.org/2000/svg", null , |
|
[ 'a', |
|
{'target': '_blank'}, |
|
['path', { "style": "fill:#92BFFC;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72", "d": "M0 1.44 L0 15.05 L11.91 15.05 L11.91 5.98 L7.37 1.44 L0 1.44 Z"}], |
|
['path', { "style": "stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72;fill:none;", "transform": "translate(7.5, -8.5)", "d": "M0 10.51 L0 15.05 L4.54 15.05"}], |
|
['path', { "style": "fill:#f28226;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72", "transform": "translate(-3, -1)", "d": "M0 8.81 L0 13.06 L5.95 13.06 L5.95 15.05 A50.2313 50.2313 -175.57 0 0 10.77 11.08 A49.9128 49.9128 -1.28 0 0 5.95 6.54 L5.95 8.81 L0 8.81 Z"}], |
|
]); |
|
|
|
/* |
|
* |
|
* [ 'a', |
|
{'target': '_blank'}, |
|
['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M7,4 l0,2"}], |
|
['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M4,8 l-2,0 l0,6"}], |
|
['path', { "style": "fill:none;stroke-width:0.5px; stroke:#000000", "d": "M10,8 l2,0 l0,6"}], |
|
['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 0}], |
|
['rect', { "style": "fill:#ffafff;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 4, "y": 6}], |
|
['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 0, "y": 12}], |
|
['rect', { "style": "fill:#96ff96;stroke:#000000;stroke-width:1", "width": 6, "height": 4, "x": 8, "y": 12}], |
|
['rect', { "style": "fill:none;stroke:none;pointer-events:all", "width": 14, "height": 16, "x": 0, "y": 0}] |
|
]); |
|
*/ |
|
|
|
// Set the link with the special namespace |
|
link.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", url); |
|
|
|
|
|
// Shows the link in the overlay |
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_OVERLAY_SHOW, |
|
id: "arissupport.urlref_" + shape.id, |
|
shapes: [shape], |
|
node: link, |
|
nodePosition: "SE" |
|
}); |
|
|
|
}, |
|
|
|
/** |
|
* Hides the Link for a particular shape |
|
* |
|
* @param {Object} shape |
|
*/ |
|
hide: function( shape ){ |
|
|
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_OVERLAY_HIDE, |
|
id: "arissupport.urlref_" + shape.id |
|
}); |
|
|
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
Array.prototype.insertFrom = function(from, to){ |
|
to = Math.max(0, to); |
|
from = Math.min( Math.max(0, from), this.length-1 ); |
|
|
|
var el = this[from]; |
|
var old = this.without(el); |
|
var newA = old.slice(0, to); |
|
newA.push(el); |
|
if(old.length > to ){ |
|
newA = newA.concat(old.slice(to)) |
|
}; |
|
return newA; |
|
} |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.Arrangement = ORYX.Plugins.AbstractPlugin.extend({ |
|
|
|
facade: undefined, |
|
|
|
construct: function(facade) { |
|
this.facade = facade; |
|
|
|
// Z-Ordering |
|
/** Hide for SIGNAVIO |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.btf, |
|
'functionality': this.setZLevel.bind(this, this.setToTop), |
|
'group': ORYX.I18N.Arrangement.groupZ, |
|
'icon': ORYX.PATH + "images/shape_move_front.png", |
|
'description': ORYX.I18N.Arrangement.btfDesc, |
|
'index': 1, |
|
'minShape': 1}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.btb, |
|
'functionality': this.setZLevel.bind(this, this.setToBack), |
|
'group': ORYX.I18N.Arrangement.groupZ, |
|
'icon': ORYX.PATH + "images/shape_move_back.png", |
|
'description': ORYX.I18N.Arrangement.btbDesc, |
|
'index': 2, |
|
'minShape': 1}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.bf, |
|
'functionality': this.setZLevel.bind(this, this.setForward), |
|
'group': ORYX.I18N.Arrangement.groupZ, |
|
'icon': ORYX.PATH + "images/shape_move_forwards.png", |
|
'description': ORYX.I18N.Arrangement.bfDesc, |
|
'index': 3, |
|
'minShape': 1}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.bb, |
|
'functionality': this.setZLevel.bind(this, this.setBackward), |
|
'group': ORYX.I18N.Arrangement.groupZ, |
|
'icon': ORYX.PATH + "images/shape_move_backwards.png", |
|
'description': ORYX.I18N.Arrangement.bbDesc, |
|
'index': 4, |
|
'minShape': 1}); |
|
|
|
// Aligment |
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.ab, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_BOTTOM]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_bottom.png", |
|
'description': ORYX.I18N.Arrangement.abDesc, |
|
'index': 1, |
|
'minShape': 2}); |
|
|
|
|
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.at, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_TOP]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_top.png", |
|
'description': ORYX.I18N.Arrangement.atDesc, |
|
'index': 3, |
|
'minShape': 2}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.al, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_LEFT]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_left.png", |
|
'description': ORYX.I18N.Arrangement.alDesc, |
|
'index': 4, |
|
'minShape': 2}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.ar, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_RIGHT]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_right.png", |
|
'description': ORYX.I18N.Arrangement.arDesc, |
|
'index': 6, |
|
'minShape': 2}); |
|
|
|
**/ |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.am, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_MIDDLE]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_middle.png", |
|
'description': ORYX.I18N.Arrangement.amDesc, |
|
'index': 1, |
|
'minShape': 2}); |
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.ac, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_CENTER]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_center.png", |
|
'description': ORYX.I18N.Arrangement.acDesc, |
|
'index': 2, |
|
'minShape': 2}); |
|
|
|
|
|
this.facade.offer({ |
|
'name':ORYX.I18N.Arrangement.as, |
|
'functionality': this.alignShapes.bind(this, [ORYX.CONFIG.EDITOR_ALIGN_SIZE]), |
|
'group': ORYX.I18N.Arrangement.groupA, |
|
'icon': ORYX.PATH + "images/shape_align_size.png", |
|
'description': ORYX.I18N.Arrangement.asDesc, |
|
'index': 3, |
|
'minShape': 2}); |
|
|
|
|
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, this.setZLevel.bind(this, this.setToTop) ); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACK, this.setZLevel.bind(this, this.setToBack) ); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_FORWARD, this.setZLevel.bind(this, this.setForward) ); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_ARRANGEMENT_BACKWARD, this.setZLevel.bind(this, this.setBackward) ); |
|
|
|
|
|
}, |
|
|
|
onSelectionChanged: function(elemnt){ |
|
var selection = this.facade.getSelection(); |
|
if (selection.length === 1 && selection[0] instanceof ORYX.Core.Edge) { |
|
this.setToTop(selection); |
|
} |
|
}, |
|
|
|
setZLevel:function(callback, event){ |
|
|
|
//Command-Pattern for dragging one docker |
|
var zLevelCommand = ORYX.Core.Command.extend({ |
|
construct: function(callback, elements, facade){ |
|
this.callback = callback; |
|
this.elements = elements; |
|
// For redo, the previous elements get stored |
|
this.elAndIndex = elements.map(function(el){ return {el:el, previous:el.parent.children[el.parent.children.indexOf(el)-1]} }) |
|
this.facade = facade; |
|
}, |
|
execute: function(){ |
|
|
|
// Call the defined z-order callback with the elements |
|
this.callback( this.elements ) |
|
this.facade.setSelection( this.elements ) |
|
}, |
|
rollback: function(){ |
|
|
|
// Sort all elements on the index of there containment |
|
var sortedEl = this.elAndIndex.sortBy( function( el ) { |
|
var value = el.el; |
|
var t = $A(value.node.parentNode.childNodes); |
|
return t.indexOf(value.node); |
|
}); |
|
|
|
// Every element get setted back bevor the old previous element |
|
for(var i=0; i<sortedEl.length; i++){ |
|
var el = sortedEl[i].el; |
|
var p = el.parent; |
|
var oldIndex = p.children.indexOf(el); |
|
var newIndex = p.children.indexOf(sortedEl[i].previous); |
|
newIndex = newIndex || 0 |
|
p.children = p.children.insertFrom(oldIndex, newIndex) |
|
el.node.parentNode.insertBefore(el.node, el.node.parentNode.childNodes[newIndex+1]); |
|
} |
|
|
|
// Reset the selection |
|
this.facade.setSelection( this.elements ) |
|
} |
|
}); |
|
|
|
// Instanziate the dockCommand |
|
var command = new zLevelCommand(callback, this.facade.getSelection(), this.facade); |
|
if( event.excludeCommand ){ |
|
command.execute(); |
|
} else { |
|
this.facade.executeCommands( [command] ); |
|
} |
|
|
|
}, |
|
|
|
setToTop: function(elements) { |
|
|
|
// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten. |
|
var tmpElem = elements.sortBy( function(value, index) { |
|
var t = $A(value.node.parentNode.childNodes); |
|
return t.indexOf(value.node); |
|
}); |
|
// Sortiertes Array wird nach oben verschoben. |
|
tmpElem.each( function(value) { |
|
var p = value.parent; |
|
if (p.children.last() === value){ |
|
return; |
|
} |
|
p.children = p.children.without( value ) |
|
p.children.push(value); |
|
value.node.parentNode.appendChild(value.node); |
|
}); |
|
}, |
|
|
|
setToBack: function(elements) { |
|
// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten. |
|
var tmpElem = elements.sortBy( function(value, index) { |
|
var t = $A(value.node.parentNode.childNodes); |
|
return t.indexOf(value.node); |
|
}); |
|
|
|
tmpElem = tmpElem.reverse(); |
|
|
|
// Sortiertes Array wird nach unten verschoben. |
|
tmpElem.each( function(value) { |
|
var p = value.parent |
|
p.children = p.children.without( value ) |
|
p.children.unshift( value ); |
|
value.node.parentNode.insertBefore(value.node, value.node.parentNode.firstChild); |
|
}); |
|
|
|
|
|
}, |
|
|
|
setBackward: function(elements) { |
|
// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten. |
|
var tmpElem = elements.sortBy( function(value, index) { |
|
var t = $A(value.node.parentNode.childNodes); |
|
return t.indexOf(value.node); |
|
}); |
|
|
|
// Reverse the elements |
|
tmpElem = tmpElem.reverse(); |
|
|
|
// Delete all Nodes who are the next Node in the nodes-Array |
|
var compactElem = tmpElem.findAll(function(el) {return !tmpElem.some(function(checkedEl){ return checkedEl.node == el.node.previousSibling})}); |
|
|
|
// Sortiertes Array wird nach eine Ebene nach oben verschoben. |
|
compactElem.each( function(el) { |
|
if(el.node.previousSibling === null) { return; } |
|
var p = el.parent; |
|
var index = p.children.indexOf(el); |
|
p.children = p.children.insertFrom(index, index-1) |
|
el.node.parentNode.insertBefore(el.node, el.node.previousSibling); |
|
}); |
|
|
|
|
|
}, |
|
|
|
setForward: function(elements) { |
|
// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten. |
|
var tmpElem = elements.sortBy( function(value, index) { |
|
var t = $A(value.node.parentNode.childNodes); |
|
return t.indexOf(value.node); |
|
}); |
|
|
|
|
|
// Delete all Nodes who are the next Node in the nodes-Array |
|
var compactElem = tmpElem.findAll(function(el) {return !tmpElem.some(function(checkedEl){ return checkedEl.node == el.node.nextSibling})}); |
|
|
|
|
|
// Sortiertes Array wird eine Ebene nach unten verschoben. |
|
compactElem.each( function(el) { |
|
var nextNode = el.node.nextSibling |
|
if(nextNode === null) { return; } |
|
var index = el.parent.children.indexOf(el); |
|
var p = el.parent; |
|
p.children = p.children.insertFrom(index, index+1) |
|
el.node.parentNode.insertBefore(nextNode, el.node); |
|
}); |
|
}, |
|
|
|
|
|
alignShapes: function(way) { |
|
|
|
var elements = this.facade.getSelection(); |
|
|
|
// Set the elements to all Top-Level elements |
|
elements = this.facade.getCanvas().getShapesWithSharedParent(elements); |
|
// Get only nodes |
|
elements = elements.findAll(function(value) { |
|
return (value instanceof ORYX.Core.Node) |
|
}); |
|
// Delete all attached intermediate events from the array |
|
elements = elements.findAll(function(value) { |
|
var d = value.getIncomingShapes() |
|
return d.length == 0 || !elements.include(d[0]) |
|
}); |
|
if(elements.length < 2) { return; } |
|
|
|
// get bounds of all shapes. |
|
var bounds = elements[0].absoluteBounds().clone(); |
|
elements.each(function(shape) { |
|
bounds.include(shape.absoluteBounds().clone()); |
|
}); |
|
|
|
// get biggest width and heigth |
|
var maxWidth = 0; |
|
var maxHeight = 0; |
|
elements.each(function(shape){ |
|
maxWidth = Math.max(shape.bounds.width(), maxWidth); |
|
maxHeight = Math.max(shape.bounds.height(), maxHeight); |
|
}); |
|
|
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(elements, bounds, maxHeight, maxWidth, way, plugin){ |
|
this.elements = elements; |
|
this.bounds = bounds; |
|
this.maxHeight = maxHeight; |
|
this.maxWidth = maxWidth; |
|
this.way = way; |
|
this.facade = plugin.facade; |
|
this.plugin = plugin; |
|
this.orgPos = []; |
|
}, |
|
setBounds: function(shape, maxSize) { |
|
if(!maxSize) |
|
maxSize = {width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE}; |
|
|
|
if(!shape.bounds) { throw "Bounds not definined." } |
|
|
|
var newBounds = { |
|
a: {x: shape.bounds.upperLeft().x - (this.maxWidth - shape.bounds.width())/2, |
|
y: shape.bounds.upperLeft().y - (this.maxHeight - shape.bounds.height())/2}, |
|
b: {x: shape.bounds.lowerRight().x + (this.maxWidth - shape.bounds.width())/2, |
|
y: shape.bounds.lowerRight().y + (this.maxHeight - shape.bounds.height())/2} |
|
} |
|
|
|
/* If the new width of shape exceeds the maximum width, set width value to maximum. */ |
|
if(this.maxWidth > maxSize.width) { |
|
newBounds.a.x = shape.bounds.upperLeft().x - |
|
(maxSize.width - shape.bounds.width())/2; |
|
|
|
newBounds.b.x = shape.bounds.lowerRight().x + (maxSize.width - shape.bounds.width())/2 |
|
} |
|
|
|
/* If the new height of shape exceeds the maximum height, set height value to maximum. */ |
|
if(this.maxHeight > maxSize.height) { |
|
newBounds.a.y = shape.bounds.upperLeft().y - |
|
(maxSize.height - shape.bounds.height())/2; |
|
|
|
newBounds.b.y = shape.bounds.lowerRight().y + (maxSize.height - shape.bounds.height())/2 |
|
} |
|
|
|
/* set bounds of shape */ |
|
shape.bounds.set(newBounds); |
|
|
|
}, |
|
execute: function(){ |
|
// align each shape according to the way that was specified. |
|
this.elements.each(function(shape, index) { |
|
this.orgPos[index] = shape.bounds.upperLeft(); |
|
|
|
var relBounds = this.bounds.clone(); |
|
var newCoordinates; |
|
if (shape.parent && !(shape.parent instanceof ORYX.Core.Canvas) ) { |
|
var upL = shape.parent.absoluteBounds().upperLeft(); |
|
relBounds.moveBy(-upL.x, -upL.y); |
|
} |
|
|
|
switch (this.way) { |
|
// align the shapes in the requested way. |
|
case ORYX.CONFIG.EDITOR_ALIGN_BOTTOM: |
|
newCoordinates = { |
|
x: shape.bounds.upperLeft().x, |
|
y: relBounds.b.y - shape.bounds.height() |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_MIDDLE: |
|
newCoordinates = { |
|
x: shape.bounds.upperLeft().x, |
|
y: (relBounds.a.y + relBounds.b.y - shape.bounds.height()) / 2 |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_TOP: |
|
newCoordinates = { |
|
x: shape.bounds.upperLeft().x, |
|
y: relBounds.a.y |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_LEFT: |
|
newCoordinates = { |
|
x: relBounds.a.x, |
|
y: shape.bounds.upperLeft().y |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_CENTER: |
|
newCoordinates = { |
|
x: (relBounds.a.x + relBounds.b.x - shape.bounds.width()) / 2, |
|
y: shape.bounds.upperLeft().y |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_RIGHT: |
|
newCoordinates = { |
|
x: relBounds.b.x - shape.bounds.width(), |
|
y: shape.bounds.upperLeft().y |
|
}; break; |
|
|
|
case ORYX.CONFIG.EDITOR_ALIGN_SIZE: |
|
if(shape.isResizable) { |
|
this.orgPos[index] = {a: shape.bounds.upperLeft(), b: shape.bounds.lowerRight()}; |
|
this.setBounds(shape, shape.maximumSize); |
|
} |
|
break; |
|
} |
|
|
|
if (newCoordinates){ |
|
var offset = { |
|
x: shape.bounds.upperLeft().x - newCoordinates.x, |
|
y: shape.bounds.upperLeft().y - newCoordinates.y |
|
} |
|
// Set the new position |
|
shape.bounds.moveTo(newCoordinates); |
|
this.plugin.layoutEdges(shape, shape.getAllDockedShapes(),offset); |
|
//shape.update() |
|
} |
|
}.bind(this)); |
|
|
|
//this.facade.getCanvas().update(); |
|
//this.facade.updateSelection(); |
|
}, |
|
rollback: function(){ |
|
this.elements.each(function(shape, index) { |
|
if (this.way == ORYX.CONFIG.EDITOR_ALIGN_SIZE) { |
|
if(shape.isResizable) {shape.bounds.set(this.orgPos[index]);} |
|
} else {shape.bounds.moveTo(this.orgPos[index]);} |
|
}.bind(this)); |
|
|
|
//this.facade.getCanvas().update(); |
|
//this.facade.updateSelection(); |
|
} |
|
}) |
|
|
|
var command = new commandClass(elements, bounds, maxHeight, maxWidth, parseInt(way), this); |
|
|
|
this.facade.executeCommands([command]); |
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.Save = Clazz.extend({ |
|
|
|
facade: undefined, |
|
|
|
processURI: undefined, |
|
|
|
changeSymbol : "*", |
|
|
|
construct: function(facade){ |
|
this.facade = facade; |
|
|
|
document.addEventListener("keydown", function(e){ |
|
if (e.ctrlKey&&e.keyCode === 83){ |
|
Event.stop(e); |
|
} |
|
}, false); |
|
|
|
window.onbeforeunload = this.onUnLoad.bind(this); |
|
|
|
this.changeDifference = 0; |
|
|
|
// Register on event for executing commands --> store all commands in a stack |
|
// --> Execute |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_UNDO_EXECUTE, function(){ this.changeDifference++; this.updateTitle(); }.bind(this) ); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_EXECUTE_COMMANDS, function(){ this.changeDifference++; this.updateTitle(); }.bind(this) ); |
|
// --> Saved from other places in the editor |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SAVED, function(){ this.changeDifference = 0; this.updateTitle(); }.bind(this) ); |
|
|
|
// --> Rollback |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_UNDO_ROLLBACK, function(){ this.changeDifference--; this.updateTitle(); }.bind(this) ); |
|
|
|
//TODO very critical for load time performance!!! |
|
//this.serializedDOM = DataManager.__persistDOM(this.facade); |
|
|
|
this.hasChanges = this._hasChanges.bind(this); |
|
}, |
|
|
|
updateTitle: function(){ |
|
|
|
var value = window.document.title || document.getElementsByTagName("title")[0].childNodes[0].nodeValue; |
|
|
|
if (this.changeDifference === 0 && value.startsWith(this.changeSymbol)){ |
|
window.document.title = value.slice(1); |
|
} else if (this.changeDifference !== 0 && !value.startsWith(this.changeSymbol)){ |
|
window.document.title = this.changeSymbol + "" + value; |
|
} |
|
}, |
|
|
|
_hasChanges: function() { |
|
return this.changeDifference !== 0 || (this.facade.getModelMetaData()['new'] && this.facade.getCanvas().getChildShapes().size() > 0); |
|
}, |
|
|
|
onUnLoad: function(){ |
|
if(this._hasChanges()) { |
|
return ORYX.I18N.Save.unsavedData; |
|
} |
|
} |
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.DragDropResize = ORYX.Plugins.AbstractPlugin.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param {Object} Facade: The Facade of the Editor |
|
*/ |
|
construct: function(facade) { |
|
this.facade = facade; |
|
|
|
// Initialize variables |
|
this.currentShapes = []; // Current selected Shapes |
|
//this.pluginsData = []; // Available Plugins |
|
this.toMoveShapes = []; // Shapes there will be moved |
|
this.distPoints = []; // Distance Points for Snap on Grid |
|
this.isResizing = false; // Flag: If there was currently resized |
|
this.dragEnable = false; // Flag: If Dragging is enabled |
|
this.dragIntialized = false; // Flag: If the Dragging is initialized |
|
this.edgesMovable = true; // Flag: If an edge is docked it is not movable |
|
this.offSetPosition = {x: 0, y: 0}; // Offset of the Dragging |
|
this.faktorXY = {x: 1, y: 1}; // The Current Zoom-Faktor |
|
this.containmentParentNode; // the current future parent node for the dragged shapes |
|
this.isAddingAllowed = false; // flag, if adding current selected shapes to containmentParentNode is allowed |
|
this.isAttachingAllowed = false; // flag, if attaching to the current shape is allowed |
|
|
|
this.callbackMouseMove = this.handleMouseMove.bind(this); |
|
this.callbackMouseUp = this.handleMouseUp.bind(this); |
|
|
|
// Get the SVG-Containernode |
|
var containerNode = this.facade.getCanvas().getSvgContainer(); |
|
|
|
// Create the Selected Rectangle in the SVG |
|
this.selectedRect = new ORYX.Plugins.SelectedRect(containerNode); |
|
|
|
// Show grid line if enabled |
|
if (ORYX.CONFIG.SHOW_GRIDLINE) { |
|
this.vLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_VERTICAL); |
|
this.hLine = new ORYX.Plugins.GridLine(containerNode, ORYX.Plugins.GridLine.DIR_HORIZONTAL); |
|
} |
|
|
|
// Get a HTML-ContainerNode |
|
containerNode = this.facade.getCanvas().getHTMLContainer(); |
|
|
|
this.scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode; |
|
|
|
// Create the southeastern button for resizing |
|
this.resizerSE = new ORYX.Plugins.Resizer(containerNode, "southeast", this.facade); |
|
this.resizerSE.registerOnResize(this.onResize.bind(this)); // register the resize callback |
|
this.resizerSE.registerOnResizeEnd(this.onResizeEnd.bind(this)); // register the resize end callback |
|
this.resizerSE.registerOnResizeStart(this.onResizeStart.bind(this)); // register the resize start callback |
|
|
|
// Create the northwestern button for resizing |
|
this.resizerNW = new ORYX.Plugins.Resizer(containerNode, "northwest", this.facade); |
|
this.resizerNW.registerOnResize(this.onResize.bind(this)); // register the resize callback |
|
this.resizerNW.registerOnResizeEnd(this.onResizeEnd.bind(this)); // register the resize end callback |
|
this.resizerNW.registerOnResizeStart(this.onResizeStart.bind(this)); // register the resize start callback |
|
|
|
// For the Drag and Drop |
|
// Register on MouseDown-Event on a Shape |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this)); |
|
}, |
|
|
|
/** |
|
* On Mouse Down |
|
* |
|
*/ |
|
handleMouseDown: function(event, uiObj) { |
|
// If the selection Bounds not intialized and the uiObj is not member of current selectio |
|
// then return |
|
if(!this.dragBounds || !this.currentShapes.member(uiObj) || !this.toMoveShapes.length) {return;}; |
|
|
|
// Start Dragging |
|
this.dragEnable = true; |
|
this.dragIntialized = true; |
|
this.edgesMovable = true; |
|
|
|
// Calculate the current zoom factor |
|
var a = this.facade.getCanvas().node.getScreenCTM(); |
|
this.faktorXY.x = a.a; |
|
this.faktorXY.y = a.d; |
|
|
|
var eventX = Event.pointerX(event); |
|
var eventY = Event.pointerY(event); |
|
|
|
// Set the offset position of dragging |
|
var upL = this.dragBounds.upperLeft(); |
|
this.offSetPosition = { |
|
x: eventX - (upL.x * this.faktorXY.x), |
|
y: eventY - (upL.y * this.faktorXY.y)}; |
|
|
|
this.offsetScroll = {x:this.scrollNode.scrollLeft,y:this.scrollNode.scrollTop}; |
|
|
|
// Register on Global Mouse-MOVE Event |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false); |
|
// Register on Global Mouse-UP Event |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true); |
|
|
|
return; |
|
}, |
|
|
|
/** |
|
* On Key Mouse Up |
|
* |
|
*/ |
|
handleMouseUp: function(event) { |
|
|
|
//disable containment highlighting |
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, |
|
highlightId:"dragdropresize.contain" |
|
}); |
|
|
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, |
|
highlightId:"dragdropresize.attached" |
|
}); |
|
|
|
// If Dragging is finished |
|
if(this.dragEnable) { |
|
|
|
// and update the current selection |
|
if(!this.dragIntialized) { |
|
|
|
// Do Method after Dragging |
|
this.afterDrag(); |
|
|
|
// Check if the Shape is allowed to dock to the other Shape |
|
if ( this.isAttachingAllowed && |
|
this.toMoveShapes.length == 1 && this.toMoveShapes[0] instanceof ORYX.Core.Node && |
|
this.toMoveShapes[0].dockers.length > 0) { |
|
|
|
// Get the position and the docker |
|
var position = this.facade.eventCoordinates( event ); |
|
var docker = this.toMoveShapes[0].dockers[0]; |
|
|
|
|
|
|
|
//Command-Pattern for dragging several Shapes |
|
var dockCommand = ORYX.Core.Command.extend({ |
|
construct: function(docker, position, newDockedShape, facade){ |
|
this.docker = docker; |
|
this.newPosition = position; |
|
this.newDockedShape = newDockedShape; |
|
this.newParent = newDockedShape.parent || facade.getCanvas(); |
|
this.oldPosition = docker.parent.bounds.center(); |
|
this.oldDockedShape = docker.getDockedShape(); |
|
this.oldParent = docker.parent.parent || facade.getCanvas(); |
|
this.facade = facade; |
|
|
|
if( this.oldDockedShape ){ |
|
this.oldPosition = docker.parent.absoluteBounds().center(); |
|
} |
|
|
|
}, |
|
execute: function(){ |
|
this.dock( this.newDockedShape, this.newParent, this.newPosition ); |
|
|
|
// Raise Event for having the docked shape on top of the other shape |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_ARRANGEMENT_TOP, excludeCommand: true}) |
|
}, |
|
rollback: function(){ |
|
this.dock( this.oldDockedShape, this.oldParent, this.oldPosition ); |
|
}, |
|
dock:function( toDockShape, parent, pos ){ |
|
// Add to the same parent Shape |
|
parent.add( this.docker.parent ) |
|
|
|
|
|
// Set the Docker to the new Shape |
|
this.docker.setDockedShape( undefined ); |
|
this.docker.bounds.centerMoveTo( pos ) |
|
this.docker.setDockedShape( toDockShape ); |
|
//this.docker.update(); |
|
|
|
this.facade.setSelection( [this.docker.parent] ); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
|
|
|
|
} |
|
}); |
|
|
|
// Instanziate the dockCommand |
|
var commands = [new dockCommand(docker, position, this.containmentParentNode, this.facade)]; |
|
this.facade.executeCommands(commands); |
|
|
|
|
|
// Check if adding is allowed to the other Shape |
|
} else if( this.isAddingAllowed ) { |
|
|
|
|
|
// Refresh all Shapes --> Set the new Bounds |
|
this.refreshSelectedShapes(); |
|
|
|
} |
|
|
|
this.facade.updateSelection(); |
|
|
|
//this.currentShapes.each(function(shape) {shape.update()}) |
|
// Raise Event: Dragging is finished |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_DRAGDROP_END}); |
|
} |
|
|
|
if (this.vLine) |
|
this.vLine.hide(); |
|
if (this.hLine) |
|
this.hLine.hide(); |
|
} |
|
|
|
// Disable |
|
this.dragEnable = false; |
|
|
|
|
|
// UnRegister on Global Mouse-UP/-Move Event |
|
document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.callbackMouseUp, true); |
|
document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.callbackMouseMove, false); |
|
|
|
return; |
|
}, |
|
|
|
/** |
|
* On Key Mouse Move |
|
* |
|
*/ |
|
handleMouseMove: function(event) { |
|
// If dragging is not enabled, go return |
|
if(!this.dragEnable) { return }; |
|
// If Dragging is initialized |
|
if(this.dragIntialized) { |
|
// Raise Event: Drag will be started |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_DRAGDROP_START}); |
|
this.dragIntialized = false; |
|
|
|
// And hide the resizers and the highlighting |
|
this.resizerSE.hide(); |
|
this.resizerNW.hide(); |
|
|
|
// if only edges are selected, containmentParentNode must be the canvas |
|
this._onlyEdges = this.currentShapes.all(function(currentShape) { |
|
return (currentShape instanceof ORYX.Core.Edge); |
|
}); |
|
|
|
// Do method before Drag |
|
this.beforeDrag(); |
|
|
|
this._currentUnderlyingNodes = []; |
|
|
|
} |
|
|
|
|
|
// Calculate the new position |
|
var position = { |
|
x: Event.pointerX(event) - this.offSetPosition.x, |
|
y: Event.pointerY(event) - this.offSetPosition.y} |
|
|
|
position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft; |
|
position.y -= this.offsetScroll.y - this.scrollNode.scrollTop; |
|
|
|
// If not the Control-Key are pressed |
|
var modifierKeyPressed = event.shiftKey || event.ctrlKey; |
|
if(ORYX.CONFIG.GRID_ENABLED && !modifierKeyPressed) { |
|
// Snap the current position to the nearest Snap-Point |
|
position = this.snapToGrid(position); |
|
} else { |
|
if (this.vLine) |
|
this.vLine.hide(); |
|
if (this.hLine) |
|
this.hLine.hide(); |
|
} |
|
|
|
// Adjust the point by the zoom faktor |
|
position.x /= this.faktorXY.x; |
|
position.y /= this.faktorXY.y; |
|
|
|
// Set that the position is not lower than zero |
|
position.x = Math.max( 0 , position.x) |
|
position.y = Math.max( 0 , position.y) |
|
|
|
// Set that the position is not bigger than the canvas |
|
var c = this.facade.getCanvas(); |
|
position.x = Math.min( c.bounds.width() - this.dragBounds.width(), position.x) |
|
position.y = Math.min( c.bounds.height() - this.dragBounds.height(), position.y) |
|
|
|
|
|
// Drag this bounds |
|
this.dragBounds.moveTo(position); |
|
|
|
// Update all selected shapes and the selection rectangle |
|
//this.refreshSelectedShapes(); |
|
this.resizeRectangle(this.dragBounds); |
|
|
|
this.isAttachingAllowed = false; |
|
|
|
//check, if a node can be added to the underlying node |
|
var eventCoordinates = this.facade.eventCoordinates(event); |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
eventCoordinates.x = eventCoordinates.x / additionalIEZoom; |
|
eventCoordinates.y = eventCoordinates.y / additionalIEZoom; |
|
} |
|
|
|
var underlyingNodes = $A(this.facade.getCanvas().getAbstractShapesAtPosition(eventCoordinates)); |
|
|
|
var checkIfAttachable = this.toMoveShapes.length == 1 && this.toMoveShapes[0] instanceof ORYX.Core.Node && this.toMoveShapes[0].dockers.length > 0 |
|
checkIfAttachable = checkIfAttachable && underlyingNodes.length != 1 |
|
|
|
|
|
if (!checkIfAttachable && |
|
underlyingNodes.length === this._currentUnderlyingNodes.length && |
|
underlyingNodes.all(function(node, index){return this._currentUnderlyingNodes[index] === node}.bind(this))) { |
|
|
|
return |
|
|
|
} else if(this._onlyEdges) { |
|
|
|
this.isAddingAllowed = true; |
|
this.containmentParentNode = this.facade.getCanvas(); |
|
|
|
} else { |
|
|
|
/* Check the containment and connection rules */ |
|
var options = { |
|
event : event, |
|
underlyingNodes : underlyingNodes, |
|
checkIfAttachable : checkIfAttachable |
|
}; |
|
this.checkRules(options); |
|
|
|
} |
|
|
|
this._currentUnderlyingNodes = underlyingNodes.reverse(); |
|
|
|
//visualize the containment result |
|
if( this.isAttachingAllowed ) { |
|
|
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId: "dragdropresize.attached", |
|
elements: [this.containmentParentNode], |
|
style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE, |
|
color: ORYX.CONFIG.SELECTION_VALID_COLOR |
|
}); |
|
|
|
} else { |
|
|
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, |
|
highlightId:"dragdropresize.attached" |
|
}); |
|
} |
|
|
|
if( !this.isAttachingAllowed ){ |
|
if( this.isAddingAllowed ) { |
|
|
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId:"dragdropresize.contain", |
|
elements:[this.containmentParentNode], |
|
color: ORYX.CONFIG.SELECTION_VALID_COLOR |
|
}); |
|
|
|
} else { |
|
|
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId:"dragdropresize.contain", |
|
elements:[this.containmentParentNode], |
|
color: ORYX.CONFIG.SELECTION_INVALID_COLOR |
|
}); |
|
|
|
} |
|
} else { |
|
this.facade.raiseEvent({ |
|
type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, |
|
highlightId:"dragdropresize.contain" |
|
}); |
|
} |
|
|
|
// Stop the Event |
|
//Event.stop(event); |
|
return; |
|
}, |
|
|
|
// /** |
|
// * Rollbacks the docked shape of an edge, if the edge is not movable. |
|
// */ |
|
// redockEdges: function() { |
|
// this._undockedEdgesCommand.dockers.each(function(el){ |
|
// el.docker.setDockedShape(el.dockedShape); |
|
// el.docker.setReferencePoint(el.refPoint); |
|
// }) |
|
// }, |
|
|
|
/** |
|
* Checks the containment and connection rules for the selected shapes. |
|
*/ |
|
checkRules : function(options) { |
|
var event = options.event; |
|
var underlyingNodes = options.underlyingNodes; |
|
var checkIfAttachable = options.checkIfAttachable; |
|
var noEdges = options.noEdges; |
|
|
|
//get underlying node that is not the same than one of the currently selected shapes or |
|
// a child of one of the selected shapes with the highest z Order. |
|
// The result is a shape or the canvas |
|
this.containmentParentNode = underlyingNodes.reverse().find((function(node) { |
|
return (node instanceof ORYX.Core.Canvas) || |
|
(((node instanceof ORYX.Core.Node) || ((node instanceof ORYX.Core.Edge) && !noEdges)) |
|
&& (!(this.currentShapes.member(node) || |
|
this.currentShapes.any(function(shape) { |
|
return (shape.children.length > 0 && shape.getChildNodes(true).member(node)); |
|
})))); |
|
}).bind(this)); |
|
|
|
if( checkIfAttachable ){ |
|
|
|
this.isAttachingAllowed = this.facade.getRules().canConnect({ |
|
sourceShape: this.containmentParentNode, |
|
edgeShape: this.toMoveShapes[0], |
|
targetShape: this.toMoveShapes[0] |
|
}); |
|
|
|
if ( this.isAttachingAllowed ) { |
|
var point = this.facade.eventCoordinates(event); |
|
this.isAttachingAllowed = this.containmentParentNode.isPointOverOffset( point.x, point.y ); |
|
} |
|
} |
|
|
|
if( !this.isAttachingAllowed ){ |
|
//check all selected shapes, if they can be added to containmentParentNode |
|
this.isAddingAllowed = this.toMoveShapes.all((function(currentShape) { |
|
if(currentShape instanceof ORYX.Core.Edge || |
|
currentShape instanceof ORYX.Core.Controls.Docker || |
|
this.containmentParentNode === currentShape.parent) { |
|
return true; |
|
} else if(this.containmentParentNode !== currentShape) { |
|
|
|
if(!(this.containmentParentNode instanceof ORYX.Core.Edge) || !noEdges) { |
|
|
|
if(this.facade.getRules().canContain({containingShape:this.containmentParentNode, |
|
containedShape:currentShape})) { |
|
return true; |
|
} |
|
} |
|
} |
|
return false; |
|
}).bind(this)); |
|
} |
|
|
|
if(!this.isAttachingAllowed && !this.isAddingAllowed && |
|
(this.containmentParentNode instanceof ORYX.Core.Edge)) { |
|
options.noEdges = true; |
|
options.underlyingNodes.reverse(); |
|
this.checkRules(options); |
|
} |
|
}, |
|
|
|
/** |
|
* Redraw the selected Shapes. |
|
* |
|
*/ |
|
refreshSelectedShapes: function() { |
|
// If the selection bounds not initialized, return |
|
if(!this.dragBounds) {return} |
|
|
|
// Calculate the offset between the bounds and the old bounds |
|
var upL = this.dragBounds.upperLeft(); |
|
var oldUpL = this.oldDragBounds.upperLeft(); |
|
var offset = { |
|
x: upL.x - oldUpL.x, |
|
y: upL.y - oldUpL.y }; |
|
|
|
// Instanciate the dragCommand |
|
var commands = [new ORYX.Core.Command.Move(this.toMoveShapes, offset, this.containmentParentNode, this.currentShapes, this)]; |
|
// If the undocked edges command is setted, add this command |
|
if( this._undockedEdgesCommand instanceof ORYX.Core.Command ){ |
|
commands.unshift( this._undockedEdgesCommand ); |
|
} |
|
// Execute the commands |
|
this.facade.executeCommands( commands ); |
|
|
|
// copy the bounds to the old bounds |
|
if( this.dragBounds ) |
|
this.oldDragBounds = this.dragBounds.clone(); |
|
|
|
}, |
|
|
|
/** |
|
* Callback for Resize |
|
* |
|
*/ |
|
onResize: function(bounds) { |
|
// If the selection bounds not initialized, return |
|
if(!this.dragBounds) {return} |
|
|
|
this.dragBounds = bounds; |
|
this.isResizing = true; |
|
|
|
// Update the rectangle |
|
this.resizeRectangle(this.dragBounds); |
|
}, |
|
|
|
onResizeStart: function() { |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_RESIZE_START}); |
|
}, |
|
|
|
onResizeEnd: function() { |
|
|
|
if (!(this.currentShapes instanceof Array)||this.currentShapes.length<=0) { |
|
return; |
|
} |
|
|
|
// If Resizing finished, the Shapes will be resize |
|
if(this.isResizing) { |
|
|
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(shape, newBounds, plugin){ |
|
this.shape = shape; |
|
this.oldBounds = shape.bounds.clone(); |
|
this.newBounds = newBounds; |
|
this.plugin = plugin; |
|
}, |
|
execute: function(){ |
|
this.shape.bounds.set(this.newBounds.a, this.newBounds.b); |
|
this.update(this.getOffset(this.oldBounds, this.newBounds)); |
|
|
|
}, |
|
rollback: function(){ |
|
this.shape.bounds.set(this.oldBounds.a, this.oldBounds.b); |
|
this.update(this.getOffset(this.newBounds, this.oldBounds)) |
|
}, |
|
|
|
getOffset:function(b1, b2){ |
|
return { |
|
x: b2.a.x - b1.a.x, |
|
y: b2.a.y - b1.a.y, |
|
xs: b2.width()/b1.width(), |
|
ys: b2.height()/b1.height() |
|
} |
|
}, |
|
update:function(offset){ |
|
this.shape.getLabels().each(function(label) { |
|
label.changed(); |
|
}); |
|
|
|
var allEdges = [].concat(this.shape.getIncomingShapes()) |
|
.concat(this.shape.getOutgoingShapes()) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return r instanceof ORYX.Core.Edge }.bind(this)) |
|
|
|
this.plugin.layoutEdges(this.shape, allEdges, offset); |
|
|
|
this.plugin.facade.setSelection([this.shape]); |
|
this.plugin.facade.getCanvas().update(); |
|
this.plugin.facade.updateSelection(); |
|
} |
|
}); |
|
|
|
var bounds = this.dragBounds.clone(); |
|
var shape = this.currentShapes[0]; |
|
|
|
if(shape.parent) { |
|
var parentPosition = shape.parent.absoluteXY(); |
|
bounds.moveBy(-parentPosition.x, -parentPosition.y); |
|
} |
|
|
|
var command = new commandClass(shape, bounds, this); |
|
|
|
this.facade.executeCommands([command]); |
|
|
|
this.isResizing = false; |
|
|
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_RESIZE_END}); |
|
} |
|
}, |
|
|
|
|
|
/** |
|
* Prepare the Dragging |
|
* |
|
*/ |
|
beforeDrag: function(){ |
|
|
|
var undockEdgeCommand = ORYX.Core.Command.extend({ |
|
construct: function(moveShapes){ |
|
this.dockers = moveShapes.collect(function(shape){ return shape instanceof ORYX.Core.Controls.Docker ? {docker:shape, dockedShape:shape.getDockedShape(), refPoint:shape.referencePoint} : undefined }).compact(); |
|
}, |
|
execute: function(){ |
|
this.dockers.each(function(el){ |
|
el.docker.setDockedShape(undefined); |
|
}) |
|
}, |
|
rollback: function(){ |
|
this.dockers.each(function(el){ |
|
el.docker.setDockedShape(el.dockedShape); |
|
el.docker.setReferencePoint(el.refPoint); |
|
//el.docker.update(); |
|
}) |
|
} |
|
}); |
|
|
|
this._undockedEdgesCommand = new undockEdgeCommand( this.toMoveShapes ); |
|
this._undockedEdgesCommand.execute(); |
|
|
|
}, |
|
|
|
hideAllLabels: function(shape) { |
|
|
|
// Hide all labels from the shape |
|
shape.getLabels().each(function(label) { |
|
label.hide(); |
|
}); |
|
// Hide all labels from docked shapes |
|
shape.getAllDockedShapes().each(function(dockedShape) { |
|
var labels = dockedShape.getLabels(); |
|
if(labels.length > 0) { |
|
labels.each(function(label) { |
|
label.hide(); |
|
}); |
|
} |
|
}); |
|
|
|
// Do this recursive for all child shapes |
|
// EXP-NICO use getShapes |
|
shape.getChildren().each((function(value) { |
|
if(value instanceof ORYX.Core.Shape) |
|
this.hideAllLabels(value); |
|
}).bind(this)); |
|
}, |
|
|
|
/** |
|
* Finished the Dragging |
|
* |
|
*/ |
|
afterDrag: function(){ |
|
|
|
}, |
|
|
|
/** |
|
* Show all Labels at these shape |
|
* |
|
*/ |
|
showAllLabels: function(shape) { |
|
|
|
// Show the label of these shape |
|
//shape.getLabels().each(function(label) { |
|
for(var i=0; i<shape.length ;i++){ |
|
var label = shape[i]; |
|
label.show(); |
|
}//); |
|
// Show all labels at docked shapes |
|
//shape.getAllDockedShapes().each(function(dockedShape) { |
|
var allDockedShapes = shape.getAllDockedShapes() |
|
for(var i=0; i<allDockedShapes.length ;i++){ |
|
var dockedShape = allDockedShapes[i]; |
|
var labels = dockedShape.getLabels(); |
|
if(labels.length > 0) { |
|
labels.each(function(label) { |
|
label.show(); |
|
}); |
|
} |
|
}//); |
|
|
|
// Do this recursive |
|
//shape.children.each((function(value) { |
|
for(var i=0; i<shape.children.length ;i++){ |
|
var value = shape.children[i]; |
|
if(value instanceof ORYX.Core.Shape) |
|
this.showAllLabels(value); |
|
}//).bind(this)); |
|
}, |
|
|
|
/** |
|
* Intialize Method, if there are new Plugins |
|
* |
|
*/ |
|
/*registryChanged: function(pluginsData) { |
|
// Save all new Plugin, sorted by group and index |
|
this.pluginsData = pluginsData.sortBy( function(value) { |
|
return (value.group + "" + value.index); |
|
}); |
|
},*/ |
|
|
|
/** |
|
* On the Selection-Changed |
|
* |
|
*/ |
|
onSelectionChanged: function(event) { |
|
var elements = event.elements; |
|
|
|
// Reset the drag-variables |
|
this.dragEnable = false; |
|
this.dragIntialized = false; |
|
this.resizerSE.hide(); |
|
this.resizerNW.hide(); |
|
|
|
// If there is no elements |
|
if(!elements || elements.length == 0) { |
|
// Hide all things and reset all variables |
|
this.selectedRect.hide(); |
|
this.currentShapes = []; |
|
this.toMoveShapes = []; |
|
this.dragBounds = undefined; |
|
this.oldDragBounds = undefined; |
|
} else { |
|
|
|
// Set the current Shapes |
|
this.currentShapes = elements; |
|
|
|
// Get all shapes with the highest parent in object hierarchy (canvas is the top most parent) |
|
var topLevelElements = this.facade.getCanvas().getShapesWithSharedParent(elements); |
|
this.toMoveShapes = topLevelElements; |
|
|
|
this.toMoveShapes = this.toMoveShapes.findAll( function(shape) { return shape instanceof ORYX.Core.Node && |
|
(shape.dockers.length === 0 || !elements.member(shape.dockers.first().getDockedShape()))}); |
|
|
|
elements.each((function(shape){ |
|
if(!(shape instanceof ORYX.Core.Edge)) {return;} |
|
|
|
var dks = shape.getDockers(); |
|
|
|
var hasF = elements.member(dks.first().getDockedShape()); |
|
var hasL = elements.member(dks.last().getDockedShape()); |
|
|
|
// if(!hasL) { |
|
// this.toMoveShapes.push(dks.last()); |
|
// } |
|
// if(!hasF){ |
|
// this.toMoveShapes.push(dks.first()) |
|
// } |
|
/* Enable movement of undocked edges */ |
|
if(!hasF && !hasL) { |
|
var isUndocked = !dks.first().getDockedShape() && !dks.last().getDockedShape(); |
|
if(isUndocked) { |
|
this.toMoveShapes = this.toMoveShapes.concat(dks); |
|
} |
|
} |
|
|
|
if( shape.dockers.length > 2 && hasF && hasL){ |
|
this.toMoveShapes = this.toMoveShapes.concat(dks.findAll(function(el,index){ return index > 0 && index < dks.length-1})); |
|
} |
|
|
|
}).bind(this)); |
|
|
|
// Calculate the new area-bounds of the selection |
|
var newBounds = undefined; |
|
this.toMoveShapes.each(function(value) { |
|
var shape = value; |
|
if(value instanceof ORYX.Core.Controls.Docker) { |
|
/* Get the Shape */ |
|
shape = value.parent; |
|
} |
|
|
|
if(!newBounds){ |
|
newBounds = shape.absoluteBounds(); |
|
} |
|
else { |
|
newBounds.include(shape.absoluteBounds()); |
|
} |
|
}.bind(this)); |
|
|
|
if(!newBounds){ |
|
elements.each(function(value){ |
|
if(!newBounds) { |
|
newBounds = value.absoluteBounds(); |
|
} else { |
|
newBounds.include(value.absoluteBounds()); |
|
} |
|
}); |
|
} |
|
|
|
// Set the new bounds |
|
this.dragBounds = newBounds; |
|
this.oldDragBounds = newBounds.clone(); |
|
|
|
// Update and show the rectangle |
|
this.resizeRectangle(newBounds); |
|
this.selectedRect.show(); |
|
|
|
// Show the resize button, if there is only one element and this is resizeable |
|
if(elements.length == 1 && elements[0].isResizable) { |
|
var aspectRatio = elements[0].getStencil().fixedAspectRatio() ? elements[0].bounds.width() / elements[0].bounds.height() : undefined; |
|
this.resizerSE.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio); |
|
this.resizerSE.show(); |
|
this.resizerNW.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio); |
|
this.resizerNW.show(); |
|
} else { |
|
this.resizerSE.setBounds(undefined); |
|
this.resizerNW.setBounds(undefined); |
|
} |
|
|
|
// If Snap-To-Grid is enabled, the Snap-Point will be calculate |
|
if(ORYX.CONFIG.GRID_ENABLED) { |
|
|
|
// Reset all points |
|
this.distPoints = []; |
|
|
|
if (this.distPointTimeout) |
|
window.clearTimeout(this.distPointTimeout) |
|
|
|
this.distPointTimeout = window.setTimeout(function(){ |
|
// Get all the shapes, there will consider at snapping |
|
// Consider only those elements who shares the same parent element |
|
var distShapes = this.facade.getCanvas().getChildShapes(true).findAll(function(value){ |
|
var parentShape = value.parent; |
|
while(parentShape){ |
|
if(elements.member(parentShape)) return false; |
|
parentShape = parentShape.parent |
|
} |
|
return true; |
|
}) |
|
|
|
// The current selection will delete from this array |
|
//elements.each(function(shape) { |
|
// distShapes = distShapes.without(shape); |
|
//}); |
|
|
|
// For all these shapes |
|
distShapes.each((function(value) { |
|
if(!(value instanceof ORYX.Core.Edge)) { |
|
var ul = value.absoluteXY(); |
|
var width = value.bounds.width(); |
|
var height = value.bounds.height(); |
|
|
|
// Add the upperLeft, center and lowerRight - Point to the distancePoints |
|
this.distPoints.push({ |
|
ul: { |
|
x: ul.x, |
|
y: ul.y |
|
}, |
|
c: { |
|
x: ul.x + (width / 2), |
|
y: ul.y + (height / 2) |
|
}, |
|
lr: { |
|
x: ul.x + width, |
|
y: ul.y + height |
|
} |
|
}); |
|
} |
|
}).bind(this)); |
|
|
|
}.bind(this), 10) |
|
|
|
|
|
} |
|
} |
|
}, |
|
|
|
/** |
|
* Adjust an Point to the Snap Points |
|
* |
|
*/ |
|
snapToGrid: function(position) { |
|
|
|
// Get the current Bounds |
|
var bounds = this.dragBounds; |
|
|
|
var point = {}; |
|
|
|
var ulThres = 6; |
|
var cThres = 10; |
|
var lrThres = 6; |
|
|
|
var scale = this.vLine ? this.vLine.getScale() : 1; |
|
|
|
var ul = { x: (position.x/scale), y: (position.y/scale)}; |
|
var c = { x: (position.x/scale) + (bounds.width()/2), y: (position.y/scale) + (bounds.height()/2)}; |
|
var lr = { x: (position.x/scale) + (bounds.width()), y: (position.y/scale) + (bounds.height())}; |
|
|
|
var offsetX, offsetY; |
|
var gridX, gridY; |
|
|
|
// For each distant point |
|
this.distPoints.each(function(value) { |
|
|
|
var x, y, gx, gy; |
|
if (Math.abs(value.c.x-c.x) < cThres){ |
|
x = value.c.x-c.x; |
|
gx = value.c.x; |
|
}/* else if (Math.abs(value.ul.x-ul.x) < ulThres){ |
|
x = value.ul.x-ul.x; |
|
gx = value.ul.x; |
|
} else if (Math.abs(value.lr.x-lr.x) < lrThres){ |
|
x = value.lr.x-lr.x; |
|
gx = value.lr.x; |
|
} */ |
|
|
|
|
|
if (Math.abs(value.c.y-c.y) < cThres){ |
|
y = value.c.y-c.y; |
|
gy = value.c.y; |
|
}/* else if (Math.abs(value.ul.y-ul.y) < ulThres){ |
|
y = value.ul.y-ul.y; |
|
gy = value.ul.y; |
|
} else if (Math.abs(value.lr.y-lr.y) < lrThres){ |
|
y = value.lr.y-lr.y; |
|
gy = value.lr.y; |
|
} */ |
|
|
|
if (x !== undefined) { |
|
offsetX = offsetX === undefined ? x : (Math.abs(x) < Math.abs(offsetX) ? x : offsetX); |
|
if (offsetX === x) |
|
gridX = gx; |
|
} |
|
|
|
if (y !== undefined) { |
|
offsetY = offsetY === undefined ? y : (Math.abs(y) < Math.abs(offsetY) ? y : offsetY); |
|
if (offsetY === y) |
|
gridY = gy; |
|
} |
|
}); |
|
|
|
|
|
if (offsetX !== undefined) { |
|
ul.x += offsetX; |
|
ul.x *= scale; |
|
if (this.vLine&&gridX) |
|
this.vLine.update(gridX); |
|
} else { |
|
ul.x = (position.x - (position.x % (ORYX.CONFIG.GRID_DISTANCE/2))); |
|
if (this.vLine) |
|
this.vLine.hide() |
|
} |
|
|
|
if (offsetY !== undefined) { |
|
ul.y += offsetY; |
|
ul.y *= scale; |
|
if (this.hLine&&gridY) |
|
this.hLine.update(gridY); |
|
} else { |
|
ul.y = (position.y - (position.y % (ORYX.CONFIG.GRID_DISTANCE/2))); |
|
if (this.hLine) |
|
this.hLine.hide(); |
|
} |
|
|
|
return ul; |
|
}, |
|
|
|
showGridLine: function(){ |
|
|
|
}, |
|
|
|
|
|
/** |
|
* Redraw of the Rectangle of the SelectedArea |
|
* @param {Object} bounds |
|
*/ |
|
resizeRectangle: function(bounds) { |
|
// Resize the Rectangle |
|
this.selectedRect.resize(bounds); |
|
} |
|
|
|
}); |
|
|
|
|
|
ORYX.Plugins.SelectedRect = Clazz.extend({ |
|
|
|
construct: function(parentId) { |
|
|
|
this.parentId = parentId; |
|
|
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", $(parentId), |
|
['g']); |
|
|
|
this.dashedArea = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node, |
|
['rect', {x: 0, y: 0, |
|
'stroke-width': 1, stroke: '#777777', fill: 'none', |
|
'stroke-dasharray': '2,2', |
|
'pointer-events': 'none'}]); |
|
|
|
this.hide(); |
|
|
|
}, |
|
|
|
hide: function() { |
|
this.node.setAttributeNS(null, 'display', 'none'); |
|
}, |
|
|
|
show: function() { |
|
this.node.setAttributeNS(null, 'display', ''); |
|
}, |
|
|
|
resize: function(bounds) { |
|
var upL = bounds.upperLeft(); |
|
|
|
var padding = ORYX.CONFIG.SELECTED_AREA_PADDING; |
|
|
|
this.dashedArea.setAttributeNS(null, 'width', bounds.width() + 2*padding); |
|
this.dashedArea.setAttributeNS(null, 'height', bounds.height() + 2*padding); |
|
this.node.setAttributeNS(null, 'transform', "translate("+ (upL.x - padding) +", "+ (upL.y - padding) +")"); |
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
ORYX.Plugins.GridLine = Clazz.extend({ |
|
|
|
construct: function(parentId, direction) { |
|
|
|
if (ORYX.Plugins.GridLine.DIR_HORIZONTAL !== direction && ORYX.Plugins.GridLine.DIR_VERTICAL !== direction) { |
|
direction = ORYX.Plugins.GridLine.DIR_HORIZONTAL |
|
} |
|
|
|
|
|
this.parent = $(parentId); |
|
this.direction = direction; |
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.parent, |
|
['g']); |
|
|
|
this.line = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node, |
|
['path', { |
|
'stroke-width': 1, stroke: 'silver', fill: 'none', |
|
'stroke-dasharray': '5,5', |
|
'pointer-events': 'none'}]); |
|
|
|
this.hide(); |
|
|
|
}, |
|
|
|
hide: function() { |
|
this.node.setAttributeNS(null, 'display', 'none'); |
|
}, |
|
|
|
show: function() { |
|
this.node.setAttributeNS(null, 'display', ''); |
|
}, |
|
|
|
getScale: function(){ |
|
try { |
|
return this.parent.parentNode.transform.baseVal.getItem(0).matrix.a; |
|
} catch(e) { |
|
return 1; |
|
} |
|
}, |
|
|
|
update: function(pos) { |
|
|
|
if (this.direction === ORYX.Plugins.GridLine.DIR_HORIZONTAL) { |
|
var y = pos instanceof Object ? pos.y : pos; |
|
var cWidth = this.parent.parentNode.parentNode.width.baseVal.value/this.getScale(); |
|
this.line.setAttributeNS(null, 'd', 'M 0 '+y+ ' L '+cWidth+' '+y); |
|
} else { |
|
var x = pos instanceof Object ? pos.x : pos; |
|
var cHeight = this.parent.parentNode.parentNode.height.baseVal.value/this.getScale(); |
|
this.line.setAttributeNS(null, 'd', 'M'+x+ ' 0 L '+x+' '+cHeight); |
|
} |
|
|
|
this.show(); |
|
} |
|
|
|
|
|
}); |
|
|
|
ORYX.Plugins.GridLine.DIR_HORIZONTAL = "hor"; |
|
ORYX.Plugins.GridLine.DIR_VERTICAL = "ver"; |
|
|
|
ORYX.Plugins.Resizer = Clazz.extend({ |
|
|
|
construct: function(parentId, orientation, facade) { |
|
|
|
this.parentId = parentId; |
|
this.orientation = orientation; |
|
this.facade = facade; |
|
|
|
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", $('canvasSection'), |
|
['div', {'class': 'resizer_'+ this.orientation, style:'left:0px; top:0px;position:absolute;'}]); |
|
|
|
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this), true); |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true); |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.handleMouseMove.bind(this), false); |
|
|
|
this.dragEnable = false; |
|
this.offSetPosition = {x: 0, y: 0}; |
|
this.bounds = undefined; |
|
|
|
this.canvasNode = this.facade.getCanvas().node; |
|
|
|
this.minSize = undefined; |
|
this.maxSize = undefined; |
|
|
|
this.aspectRatio = undefined; |
|
|
|
this.resizeCallbacks = []; |
|
this.resizeStartCallbacks = []; |
|
this.resizeEndCallbacks = []; |
|
this.hide(); |
|
|
|
// Calculate the Offset |
|
this.scrollNode = this.node.parentNode.parentNode.parentNode; |
|
|
|
}, |
|
|
|
handleMouseDown: function(event) { |
|
this.dragEnable = true; |
|
|
|
this.offsetScroll = {x:this.scrollNode.scrollLeft,y:this.scrollNode.scrollTop}; |
|
|
|
this.offSetPosition = { |
|
x: Event.pointerX(event) - this.position.x, |
|
y: Event.pointerY(event) - this.position.y}; |
|
|
|
this.resizeStartCallbacks.each((function(value) { |
|
value(this.bounds); |
|
}).bind(this)); |
|
|
|
}, |
|
|
|
handleMouseUp: function(event) { |
|
this.dragEnable = false; |
|
this.containmentParentNode = null; |
|
this.resizeEndCallbacks.each((function(value) { |
|
value(this.bounds); |
|
}).bind(this)); |
|
|
|
}, |
|
|
|
handleMouseMove: function(event) { |
|
if(!this.dragEnable) { return } |
|
|
|
if(event.shiftKey || event.ctrlKey) { |
|
this.aspectRatio = this.bounds.width() / this.bounds.height(); |
|
} else { |
|
this.aspectRatio = undefined; |
|
} |
|
|
|
var position = { |
|
x: Event.pointerX(event) - this.offSetPosition.x, |
|
y: Event.pointerY(event) - this.offSetPosition.y}; |
|
|
|
|
|
position.x -= this.offsetScroll.x - this.scrollNode.scrollLeft; |
|
position.y -= this.offsetScroll.y - this.scrollNode.scrollTop; |
|
|
|
position.x = Math.min( position.x, this.facade.getCanvas().bounds.width()); |
|
position.y = Math.min( position.y, this.facade.getCanvas().bounds.height()); |
|
|
|
var offset = { |
|
x: position.x - this.position.x, |
|
y: position.y - this.position.y |
|
}; |
|
|
|
if(this.aspectRatio) { |
|
// fixed aspect ratio |
|
newAspectRatio = (this.bounds.width()+offset.x) / (this.bounds.height()+offset.y); |
|
if(newAspectRatio>this.aspectRatio) { |
|
offset.x = this.aspectRatio * (this.bounds.height()+offset.y) - this.bounds.width(); |
|
} else if(newAspectRatio<this.aspectRatio) { |
|
offset.y = (this.bounds.width()+offset.x) / this.aspectRatio - this.bounds.height(); |
|
} |
|
} |
|
|
|
// respect minimum and maximum sizes of stencil |
|
if(this.orientation==="northwest") { |
|
|
|
if(this.bounds.width()-offset.x > this.maxSize.width) { |
|
offset.x = -(this.maxSize.width - this.bounds.width()); |
|
if(this.aspectRatio) |
|
offset.y = this.aspectRatio * offset.x; |
|
} |
|
if(this.bounds.width()-offset.x < this.minSize.width) { |
|
offset.x = -(this.minSize.width - this.bounds.width()); |
|
if(this.aspectRatio) |
|
offset.y = this.aspectRatio * offset.x; |
|
} |
|
if(this.bounds.height()-offset.y > this.maxSize.height) { |
|
offset.y = -(this.maxSize.height - this.bounds.height()); |
|
if(this.aspectRatio) |
|
offset.x = offset.y / this.aspectRatio; |
|
} |
|
if(this.bounds.height()-offset.y < this.minSize.height) { |
|
offset.y = -(this.minSize.height - this.bounds.height()); |
|
if(this.aspectRatio) |
|
offset.x = offset.y / this.aspectRatio; |
|
} |
|
|
|
} else { // defaults to southeast |
|
if(this.bounds.width()+offset.x > this.maxSize.width) { |
|
offset.x = this.maxSize.width - this.bounds.width(); |
|
if(this.aspectRatio) |
|
offset.y = this.aspectRatio * offset.x; |
|
} |
|
if(this.bounds.width()+offset.x < this.minSize.width) { |
|
offset.x = this.minSize.width - this.bounds.width(); |
|
if(this.aspectRatio) |
|
offset.y = this.aspectRatio * offset.x; |
|
} |
|
if(this.bounds.height()+offset.y > this.maxSize.height) { |
|
offset.y = this.maxSize.height - this.bounds.height(); |
|
if(this.aspectRatio) |
|
offset.x = offset.y / this.aspectRatio; |
|
} |
|
if(this.bounds.height()+offset.y < this.minSize.height) { |
|
offset.y = this.minSize.height - this.bounds.height(); |
|
if(this.aspectRatio) |
|
offset.x = offset.y / this.aspectRatio; |
|
} |
|
} |
|
|
|
if(this.orientation==="northwest") { |
|
this.bounds.extend({x:-offset.x, y:-offset.y}); |
|
this.bounds.moveBy(offset); |
|
} else { // defaults to southeast |
|
this.bounds.extend(offset); |
|
} |
|
|
|
this.update(); |
|
|
|
this.resizeCallbacks.each((function(value) { |
|
value(this.bounds); |
|
}).bind(this)); |
|
|
|
Event.stop(event); |
|
|
|
}, |
|
|
|
registerOnResizeStart: function(callback) { |
|
if(!this.resizeStartCallbacks.member(callback)) { |
|
this.resizeStartCallbacks.push(callback); |
|
} |
|
}, |
|
|
|
unregisterOnResizeStart: function(callback) { |
|
if(this.resizeStartCallbacks.member(callback)) { |
|
this.resizeStartCallbacks = this.resizeStartCallbacks.without(callback); |
|
} |
|
}, |
|
|
|
registerOnResizeEnd: function(callback) { |
|
if(!this.resizeEndCallbacks.member(callback)) { |
|
this.resizeEndCallbacks.push(callback); |
|
} |
|
}, |
|
|
|
unregisterOnResizeEnd: function(callback) { |
|
if(this.resizeEndCallbacks.member(callback)) { |
|
this.resizeEndCallbacks = this.resizeEndCallbacks.without(callback); |
|
} |
|
}, |
|
|
|
registerOnResize: function(callback) { |
|
if(!this.resizeCallbacks.member(callback)) { |
|
this.resizeCallbacks.push(callback); |
|
} |
|
}, |
|
|
|
unregisterOnResize: function(callback) { |
|
if(this.resizeCallbacks.member(callback)) { |
|
this.resizeCallbacks = this.resizeCallbacks.without(callback); |
|
} |
|
}, |
|
|
|
hide: function() { |
|
this.node.style.display = "none"; |
|
}, |
|
|
|
show: function() { |
|
if(this.bounds) |
|
this.node.style.display = ""; |
|
}, |
|
|
|
setBounds: function(bounds, min, max, aspectRatio) { |
|
this.bounds = bounds; |
|
|
|
if(!min) |
|
min = {width: ORYX.CONFIG.MINIMUM_SIZE, height: ORYX.CONFIG.MINIMUM_SIZE}; |
|
|
|
if(!max) |
|
max = {width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE}; |
|
|
|
this.minSize = min; |
|
this.maxSize = max; |
|
|
|
this.aspectRatio = aspectRatio; |
|
|
|
this.update(); |
|
}, |
|
|
|
update: function() { |
|
if(!this.bounds) { return; } |
|
|
|
var upL = this.bounds.upperLeft(); |
|
|
|
if(this.bounds.width() < this.minSize.width) { this.bounds.set(upL.x, upL.y, upL.x + this.minSize.width, upL.y + this.bounds.height());}; |
|
if(this.bounds.height() < this.minSize.height) { this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.minSize.height);}; |
|
if(this.bounds.width() > this.maxSize.width) { this.bounds.set(upL.x, upL.y, upL.x + this.maxSize.width, upL.y + this.bounds.height());}; |
|
if(this.bounds.height() > this.maxSize.height) { this.bounds.set(upL.x, upL.y, upL.x + this.bounds.width(), upL.y + this.maxSize.height);}; |
|
|
|
var a = this.canvasNode.getScreenCTM(); |
|
|
|
upL.x *= a.a; |
|
upL.y *= a.d; |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom === 1) { |
|
upL.y = upL.y - jQuery("#canvasSection").offset().top + a.f; |
|
upL.x = upL.x - jQuery("#canvasSection").offset().left + a.e; |
|
|
|
} else { |
|
var canvasOffsetLeft = jQuery("#canvasSection").offset().left; |
|
var canvasScrollLeft = jQuery("#canvasSection").scrollLeft(); |
|
var canvasScrollTop = jQuery("#canvasSection").scrollTop(); |
|
|
|
var offset = a.e - (canvasOffsetLeft * additionalIEZoom); |
|
var additionaloffset = 0; |
|
if (offset > 10) { |
|
additionaloffset = (offset / additionalIEZoom) - offset; |
|
} |
|
upL.y = upL.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop) + a.f; |
|
upL.x = upL.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft) + a.e; |
|
} |
|
|
|
if(this.orientation==="northwest") { |
|
upL.x -= 13; |
|
upL.y -= 13; |
|
} else { // defaults to southeast |
|
upL.x += (a.a * this.bounds.width()) + 3 ; |
|
upL.y += (a.d * this.bounds.height()) + 3; |
|
} |
|
|
|
this.position = upL; |
|
|
|
this.node.style.left = this.position.x + "px"; |
|
this.node.style.top = this.position.y + "px"; |
|
} |
|
}); |
|
|
|
|
|
|
|
/** |
|
* Implements a Command to move shapes |
|
* |
|
*/ |
|
ORYX.Core.Command.Move = ORYX.Core.Command.extend({ |
|
construct: function(moveShapes, offset, parent, selectedShapes, plugin){ |
|
this.moveShapes = moveShapes; |
|
this.selectedShapes = selectedShapes; |
|
this.offset = offset; |
|
this.plugin = plugin; |
|
// Defines the old/new parents for the particular shape |
|
this.newParents = moveShapes.collect(function(t){ return parent || t.parent }); |
|
this.oldParents = moveShapes.collect(function(shape){ return shape.parent }); |
|
this.dockedNodes= moveShapes.findAll(function(shape){ return shape instanceof ORYX.Core.Node && shape.dockers.length == 1}).collect(function(shape){ return {docker:shape.dockers[0], dockedShape:shape.dockers[0].getDockedShape(), refPoint:shape.dockers[0].referencePoint} }); |
|
}, |
|
execute: function(){ |
|
this.dockAllShapes() |
|
// Moves by the offset |
|
this.move( this.offset); |
|
// Addes to the new parents |
|
this.addShapeToParent( this.newParents ); |
|
// Set the selection to the current selection |
|
this.selectCurrentShapes(); |
|
this.plugin.facade.getCanvas().update(); |
|
this.plugin.facade.updateSelection(); |
|
}, |
|
rollback: function(){ |
|
// Moves by the inverted offset |
|
var offset = { x:-this.offset.x, y:-this.offset.y }; |
|
this.move( offset ); |
|
// Addes to the old parents |
|
this.addShapeToParent( this.oldParents ); |
|
this.dockAllShapes(true) |
|
|
|
// Set the selection to the current selection |
|
this.selectCurrentShapes(); |
|
this.plugin.facade.getCanvas().update(); |
|
this.plugin.facade.updateSelection(); |
|
|
|
}, |
|
move:function(offset, doLayout){ |
|
|
|
// Move all Shapes by these offset |
|
for(var i=0; i<this.moveShapes.length ;i++){ |
|
var value = this.moveShapes[i]; |
|
value.bounds.moveBy(offset); |
|
|
|
if (value instanceof ORYX.Core.Node) { |
|
|
|
(value.dockers||[]).each(function(d){ |
|
d.bounds.moveBy(offset); |
|
}) |
|
|
|
// Update all Dockers of Child shapes |
|
/*var childShapesNodes = value.getChildShapes(true).findAll(function(shape){ return shape instanceof ORYX.Core.Node }); |
|
var childDockedShapes = childShapesNodes.collect(function(shape){ return shape.getAllDockedShapes() }).flatten().uniq(); |
|
var childDockedEdge = childDockedShapes.findAll(function(shape){ return shape instanceof ORYX.Core.Edge }); |
|
childDockedEdge = childDockedEdge.findAll(function(shape){ return shape.getAllDockedShapes().all(function(dsh){ return childShapesNodes.include(dsh) }) }); |
|
var childDockedDockers = childDockedEdge.collect(function(shape){ return shape.dockers }).flatten(); |
|
|
|
for (var j = 0; j < childDockedDockers.length; j++) { |
|
var docker = childDockedDockers[j]; |
|
if (!docker.getDockedShape() && !this.moveShapes.include(docker)) { |
|
//docker.bounds.moveBy(offset); |
|
//docker.update(); |
|
} |
|
}*/ |
|
|
|
|
|
var allEdges = [].concat(value.getIncomingShapes()) |
|
.concat(value.getOutgoingShapes()) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return r instanceof ORYX.Core.Edge && !this.moveShapes.any(function(d){ return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)}) }.bind(this)) |
|
// Remove all edges which are between the node and a node contained in the selection from the list |
|
.findAll(function(r){ return (r.dockers.first().getDockedShape() == value || !this.moveShapes.include(r.dockers.first().getDockedShape())) && |
|
(r.dockers.last().getDockedShape() == value || !this.moveShapes.include(r.dockers.last().getDockedShape()))}.bind(this)) |
|
|
|
// Layout all outgoing/incoming edges |
|
this.plugin.layoutEdges(value, allEdges, offset); |
|
|
|
|
|
var allSameEdges = [].concat(value.getIncomingShapes()) |
|
.concat(value.getOutgoingShapes()) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return r instanceof ORYX.Core.Edge && r.dockers.first().isDocked() && r.dockers.last().isDocked() && !this.moveShapes.include(r) && !this.moveShapes.any(function(d){ return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)}) }.bind(this)) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return this.moveShapes.indexOf(r.dockers.first().getDockedShape()) > i || this.moveShapes.indexOf(r.dockers.last().getDockedShape()) > i}.bind(this)) |
|
|
|
for (var j = 0; j < allSameEdges.length; j++) { |
|
for (var k = 1; k < allSameEdges[j].dockers.length-1; k++) { |
|
var docker = allSameEdges[j].dockers[k]; |
|
if (!docker.getDockedShape() && !this.moveShapes.include(docker)) { |
|
docker.bounds.moveBy(offset); |
|
} |
|
} |
|
} |
|
|
|
/*var i=-1; |
|
var nodes = value.getChildShapes(true); |
|
var allEdges = []; |
|
while(++i<nodes.length){ |
|
var edges = [].concat(nodes[i].getIncomingShapes()) |
|
.concat(nodes[i].getOutgoingShapes()) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return r instanceof ORYX.Core.Edge && !allEdges.include(r) && r.dockers.any(function(d){ return !value.bounds.isIncluded(d.bounds.center)})}) |
|
allEdges = allEdges.concat(edges); |
|
if (edges.length <= 0){ continue } |
|
//this.plugin.layoutEdges(nodes[i], edges, offset); |
|
}*/ |
|
} |
|
} |
|
|
|
}, |
|
dockAllShapes: function(shouldDocked){ |
|
// Undock all Nodes |
|
for (var i = 0; i < this.dockedNodes.length; i++) { |
|
var docker = this.dockedNodes[i].docker; |
|
|
|
docker.setDockedShape( shouldDocked ? this.dockedNodes[i].dockedShape : undefined ) |
|
if (docker.getDockedShape()) { |
|
docker.setReferencePoint(this.dockedNodes[i].refPoint); |
|
//docker.update(); |
|
} |
|
} |
|
}, |
|
|
|
addShapeToParent:function( parents ){ |
|
|
|
// For every Shape, add this and reset the position |
|
for(var i=0; i<this.moveShapes.length ;i++){ |
|
var currentShape = this.moveShapes[i]; |
|
if(currentShape instanceof ORYX.Core.Node && |
|
currentShape.parent !== parents[i]) { |
|
|
|
// Calc the new position |
|
var unul = parents[i].absoluteXY(); |
|
var csul = currentShape.absoluteXY(); |
|
var x = csul.x - unul.x; |
|
var y = csul.y - unul.y; |
|
|
|
// Add the shape to the new contained shape |
|
parents[i].add(currentShape); |
|
// Add all attached shapes as well |
|
currentShape.getOutgoingShapes((function(shape) { |
|
if(shape instanceof ORYX.Core.Node && !this.moveShapes.member(shape)) { |
|
parents[i].add(shape); |
|
} |
|
}).bind(this)); |
|
|
|
// Set the new position |
|
if(currentShape instanceof ORYX.Core.Node && currentShape.dockers.length == 1){ |
|
var b = currentShape.bounds; |
|
x += b.width()/2;y += b.height()/2 |
|
currentShape.dockers.first().bounds.centerMoveTo(x, y); |
|
} else { |
|
currentShape.bounds.moveTo(x, y); |
|
} |
|
|
|
} |
|
|
|
// Update the shape |
|
//currentShape.update(); |
|
|
|
} |
|
}, |
|
selectCurrentShapes:function(){ |
|
this.plugin.facade.setSelection( this.selectedShapes ); |
|
} |
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.DragDocker = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param {Object} Facade: The Facade of the Editor |
|
*/ |
|
construct: function(facade) { |
|
this.facade = facade; |
|
|
|
// Set the valid and invalid color |
|
this.VALIDCOLOR = ORYX.CONFIG.SELECTION_VALID_COLOR; |
|
this.INVALIDCOLOR = ORYX.CONFIG.SELECTION_INVALID_COLOR; |
|
|
|
// Define Variables |
|
this.shapeSelection = undefined; |
|
this.docker = undefined; |
|
this.dockerParent = undefined; |
|
this.dockerSource = undefined; |
|
this.dockerTarget = undefined; |
|
this.lastUIObj = undefined; |
|
this.isStartDocker = undefined; |
|
this.isEndDocker = undefined; |
|
this.undockTreshold = 10; |
|
this.initialDockerPosition = undefined; |
|
this.outerDockerNotMoved = undefined; |
|
this.isValid = false; |
|
|
|
// For the Drag and Drop |
|
// Register on MouseDown-Event on a Docker |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DOCKERDRAG, this.handleDockerDrag.bind(this)); |
|
|
|
|
|
// Register on over/out to show / hide a docker |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOVER, this.handleMouseOver.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOUT, this.handleMouseOut.bind(this)); |
|
|
|
|
|
}, |
|
|
|
/** |
|
* MouseOut Handler |
|
* |
|
*/ |
|
handleMouseOut: function(event, uiObj) { |
|
// If there is a Docker, hide this |
|
if(!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) { |
|
uiObj.hide(); |
|
} else if(!this.docker && uiObj instanceof ORYX.Core.Edge) { |
|
uiObj.dockers.each(function(docker){ |
|
docker.hide(); |
|
}); |
|
} |
|
}, |
|
|
|
/** |
|
* MouseOver Handler |
|
* |
|
*/ |
|
handleMouseOver: function(event, uiObj) { |
|
// If there is a Docker, show this |
|
if(!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) { |
|
uiObj.show(); |
|
} else if(!this.docker && uiObj instanceof ORYX.Core.Edge) { |
|
uiObj.dockers.each(function(docker){ |
|
docker.show(); |
|
}); |
|
} |
|
}, |
|
/** |
|
* DockerDrag Handler |
|
* delegates the uiEvent of the drag event to the mouseDown function |
|
*/ |
|
handleDockerDrag: function(event, uiObj) { |
|
this.handleMouseDown(event.uiEvent, uiObj); |
|
}, |
|
|
|
/** |
|
* MouseDown Handler |
|
* |
|
*/ |
|
handleMouseDown: function(event, uiObj) { |
|
// If there is a Docker |
|
if(uiObj instanceof ORYX.Core.Controls.Docker && uiObj.isMovable) { |
|
|
|
/* Buffering shape selection and clear selection*/ |
|
this.shapeSelection = this.facade.getSelection(); |
|
this.facade.setSelection(); |
|
|
|
this.docker = uiObj; |
|
this.initialDockerPosition = this.docker.bounds.center(); |
|
this.outerDockerNotMoved = false; |
|
this.dockerParent = uiObj.parent; |
|
|
|
// Define command arguments |
|
this._commandArg = {docker:uiObj, dockedShape:uiObj.getDockedShape(), refPoint:uiObj.referencePoint || uiObj.bounds.center()}; |
|
|
|
// Show the Docker |
|
this.docker.show(); |
|
|
|
// If the Dockers Parent is an Edge, |
|
// and the Docker is either the first or last Docker of the Edge |
|
if(uiObj.parent instanceof ORYX.Core.Edge && |
|
(uiObj.parent.dockers.first() == uiObj || uiObj.parent.dockers.last() == uiObj)) { |
|
|
|
// Get the Edge Source or Target |
|
if(uiObj.parent.dockers.first() == uiObj && uiObj.parent.dockers.last().getDockedShape()) { |
|
this.dockerTarget = uiObj.parent.dockers.last().getDockedShape(); |
|
} else if(uiObj.parent.dockers.last() == uiObj && uiObj.parent.dockers.first().getDockedShape()) { |
|
this.dockerSource = uiObj.parent.dockers.first().getDockedShape(); |
|
} |
|
|
|
} else { |
|
// If there parent is not an Edge, undefined the Source and Target |
|
this.dockerSource = undefined; |
|
this.dockerTarget = undefined; |
|
} |
|
|
|
this.isStartDocker = this.docker.parent.dockers.first() === this.docker; |
|
this.isEndDocker = this.docker.parent.dockers.last() === this.docker; |
|
|
|
// add to canvas while dragging |
|
this.facade.getCanvas().add(this.docker.parent); |
|
|
|
// Hide all Labels from Docker |
|
this.docker.parent.getLabels().each(function(label) { |
|
label.hide(); |
|
}); |
|
|
|
var eventCoordinates = this.facade.eventCoordinates(event); |
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
eventCoordinates.x = eventCoordinates.x / additionalIEZoom; |
|
eventCoordinates.y = eventCoordinates.y / additionalIEZoom; |
|
} |
|
|
|
// Undocked the Docker from current Shape |
|
if ((!this.isStartDocker && !this.isEndDocker) || !this.docker.isDocked()) { |
|
|
|
this.docker.setDockedShape(undefined); |
|
// Set the Docker to the center of the mouse pointer |
|
this.docker.bounds.centerMoveTo(eventCoordinates); |
|
this.dockerParent._update(); |
|
} else { |
|
this.outerDockerNotMoved = true; |
|
} |
|
|
|
var option = {movedCallback: this.dockerMoved.bind(this), upCallback: this.dockerMovedFinished.bind(this)}; |
|
|
|
this.startEventPos = eventCoordinates; |
|
|
|
// Enable the Docker for Drag'n'Drop, give the mouseMove and mouseUp-Callback with |
|
ORYX.Core.UIEnableDrag(event, uiObj, option); |
|
} |
|
}, |
|
|
|
/** |
|
* Docker MouseMove Handler |
|
* |
|
*/ |
|
dockerMoved: function(event) { |
|
this.outerDockerNotMoved = false; |
|
var snapToMagnet = undefined; |
|
|
|
if (this.docker.parent) { |
|
if (this.isStartDocker || this.isEndDocker) { |
|
|
|
// Get the EventPosition and all Shapes on these point |
|
var evPos = this.facade.eventCoordinates(event); |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom !== 1) { |
|
evPos.x = evPos.x / additionalIEZoom; |
|
evPos.y = evPos.y / additionalIEZoom; |
|
} |
|
|
|
if(this.docker.isDocked()) { |
|
/* Only consider start/end dockers if they are moved over a treshold */ |
|
var distanceDockerPointer = |
|
ORYX.Core.Math.getDistancePointToPoint(evPos, this.initialDockerPosition); |
|
if(distanceDockerPointer < this.undockTreshold) { |
|
this.outerDockerNotMoved = true; |
|
return; |
|
} |
|
|
|
/* Undock the docker */ |
|
this.docker.setDockedShape(undefined); |
|
// Set the Docker to the center of the mouse pointer |
|
//this.docker.bounds.centerMoveTo(evPos); |
|
this.dockerParent._update(); |
|
} |
|
|
|
var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos); |
|
|
|
// Get the top level Shape on these, but not the same as Dockers parent |
|
var uiObj = shapes.pop(); |
|
if (this.docker.parent === uiObj) { |
|
uiObj = shapes.pop(); |
|
} |
|
|
|
// If the top level Shape the same as the last Shape, then return |
|
if (this.lastUIObj == uiObj) { |
|
//return; |
|
|
|
// If the top level uiObj instance of Shape and this isn't the parent of the docker |
|
} |
|
else |
|
if (uiObj instanceof ORYX.Core.Shape) { |
|
|
|
// Ask by the StencilSet if the source, the edge and the target valid connections. |
|
if (this.docker.parent instanceof ORYX.Core.Edge) { |
|
|
|
var highestParent = this.getHighestParentBeforeCanvas(uiObj); |
|
/* Ensure that the shape to dock is not a child shape |
|
* of the same edge. |
|
*/ |
|
if (highestParent instanceof ORYX.Core.Edge && this.docker.parent === highestParent) |
|
{ |
|
this.isValid = false; |
|
this.dockerParent._update(); |
|
return; |
|
} |
|
this.isValid = false; |
|
var curObj = uiObj, orgObj = uiObj; |
|
while (!this.isValid && curObj && !(curObj instanceof ORYX.Core.Canvas)) |
|
{ |
|
uiObj = curObj; |
|
this.isValid = this.facade.getRules().canConnect({ |
|
sourceShape: this.dockerSource ? // Is there a docked source |
|
this.dockerSource : // than set this |
|
(this.isStartDocker ? // if not and if the Docker is the start docker |
|
uiObj : // take the last uiObj |
|
undefined), // if not set it to undefined; |
|
edgeShape: this.docker.parent, |
|
targetShape: this.dockerTarget ? // Is there a docked target |
|
this.dockerTarget : // than set this |
|
(this.isEndDocker ? // if not and if the Docker is not the start docker |
|
uiObj : // take the last uiObj |
|
undefined) // if not set it to undefined; |
|
}); |
|
curObj = curObj.parent; |
|
} |
|
|
|
// Reset uiObj if no |
|
// valid parent is found |
|
if (!this.isValid){ |
|
uiObj = orgObj; |
|
} |
|
|
|
} |
|
else { |
|
this.isValid = this.facade.getRules().canConnect({ |
|
sourceShape: uiObj, |
|
edgeShape: this.docker.parent, |
|
targetShape: this.docker.parent |
|
}); |
|
} |
|
|
|
// If there is a lastUIObj, hide the magnets |
|
if (this.lastUIObj) { |
|
this.hideMagnets(this.lastUIObj); |
|
} |
|
|
|
// If there is a valid connection, show the magnets |
|
if (this.isValid) { |
|
this.showMagnets(uiObj); |
|
} |
|
|
|
// Set the Highlight Rectangle by these value |
|
this.showHighlight(uiObj, this.isValid ? this.VALIDCOLOR : this.INVALIDCOLOR); |
|
|
|
// Buffer the current Shape |
|
this.lastUIObj = uiObj; |
|
} |
|
else { |
|
// If there is no top level Shape, then hide the highligting of the last Shape |
|
this.hideHighlight(); |
|
this.lastUIObj ? this.hideMagnets(this.lastUIObj) : null; |
|
this.lastUIObj = undefined; |
|
this.isValid = false; |
|
} |
|
|
|
// Snap to the nearest Magnet |
|
if (this.lastUIObj && this.isValid && !(event.shiftKey || event.ctrlKey)) { |
|
snapToMagnet = this.lastUIObj.magnets.find(function(magnet){ |
|
return magnet.absoluteBounds().isIncluded(evPos); |
|
}); |
|
|
|
if (snapToMagnet) { |
|
this.docker.bounds.centerMoveTo(snapToMagnet.absoluteCenterXY()); |
|
//this.docker.update() |
|
} |
|
} |
|
} |
|
} |
|
// Snap to on the nearest Docker of the same parent |
|
if(!(event.shiftKey || event.ctrlKey) && !snapToMagnet) { |
|
var minOffset = ORYX.CONFIG.DOCKER_SNAP_OFFSET; |
|
var nearestX = minOffset + 1; |
|
var nearestY = minOffset + 1; |
|
|
|
var dockerCenter = this.docker.bounds.center(); |
|
|
|
if (this.docker.parent) { |
|
|
|
this.docker.parent.dockers.each((function(docker){ |
|
if (this.docker == docker) { |
|
return |
|
}; |
|
|
|
var center = docker.referencePoint ? docker.getAbsoluteReferencePoint() : docker.bounds.center(); |
|
|
|
nearestX = Math.abs(nearestX) > Math.abs(center.x - dockerCenter.x) ? center.x - dockerCenter.x : nearestX; |
|
nearestY = Math.abs(nearestY) > Math.abs(center.y - dockerCenter.y) ? center.y - dockerCenter.y : nearestY; |
|
|
|
|
|
}).bind(this)); |
|
|
|
if (Math.abs(nearestX) < minOffset || Math.abs(nearestY) < minOffset) { |
|
nearestX = Math.abs(nearestX) < minOffset ? nearestX : 0; |
|
nearestY = Math.abs(nearestY) < minOffset ? nearestY : 0; |
|
|
|
this.docker.bounds.centerMoveTo(dockerCenter.x + nearestX, dockerCenter.y + nearestY); |
|
//this.docker.update() |
|
} else { |
|
|
|
|
|
|
|
var previous = this.docker.parent.dockers[Math.max(this.docker.parent.dockers.indexOf(this.docker)-1, 0)]; |
|
var next = this.docker.parent.dockers[Math.min(this.docker.parent.dockers.indexOf(this.docker)+1, this.docker.parent.dockers.length-1)]; |
|
|
|
if (previous && next && previous !== this.docker && next !== this.docker){ |
|
var cp = previous.bounds.center(); |
|
var cn = next.bounds.center(); |
|
var cd = this.docker.bounds.center(); |
|
|
|
// Checks if the point is on the line between previous and next |
|
if (ORYX.Core.Math.isPointInLine(cd.x, cd.y, cp.x, cp.y, cn.x, cn.y, 10)) { |
|
// Get the rise |
|
var raise = (Number(cn.y)-Number(cp.y))/(Number(cn.x)-Number(cp.x)); |
|
// Calculate the intersection point |
|
var intersecX = ((cp.y-(cp.x*raise))-(cd.y-(cd.x*(-Math.pow(raise,-1)))))/((-Math.pow(raise,-1))-raise); |
|
var intersecY = (cp.y-(cp.x*raise))+(raise*intersecX); |
|
|
|
if(isNaN(intersecX) || isNaN(intersecY)) {return;} |
|
|
|
this.docker.bounds.centerMoveTo(intersecX, intersecY); |
|
} |
|
} |
|
|
|
} |
|
} |
|
} |
|
//this.facade.getCanvas().update(); |
|
this.dockerParent._update(); |
|
}, |
|
|
|
/** |
|
* Docker MouseUp Handler |
|
* |
|
*/ |
|
dockerMovedFinished: function(event) { |
|
|
|
/* Reset to buffered shape selection */ |
|
this.facade.setSelection(this.shapeSelection); |
|
|
|
// Hide the border |
|
this.hideHighlight(); |
|
|
|
// Show all Labels from Docker |
|
this.dockerParent.getLabels().each(function(label){ |
|
label.show(); |
|
//label.update(); |
|
}); |
|
|
|
// If there is a last top level Shape |
|
if(this.lastUIObj && (this.isStartDocker || this.isEndDocker)){ |
|
// If there is a valid connection, the set as a docked Shape to them |
|
if(this.isValid) { |
|
|
|
this.docker.setDockedShape(this.lastUIObj); |
|
|
|
this.facade.raiseEvent({ |
|
type :ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED, |
|
docker : this.docker, |
|
parent : this.docker.parent, |
|
target : this.lastUIObj |
|
}); |
|
} |
|
|
|
this.hideMagnets(this.lastUIObj); |
|
} |
|
|
|
// Hide the Docker |
|
this.docker.hide(); |
|
|
|
if(this.outerDockerNotMoved) { |
|
// Get the EventPosition and all Shapes on these point |
|
var evPos = this.facade.eventCoordinates(event); |
|
var shapes = this.facade.getCanvas().getAbstractShapesAtPosition(evPos); |
|
|
|
/* Remove edges from selection */ |
|
var shapeWithoutEdges = shapes.findAll(function(node) { |
|
return node instanceof ORYX.Core.Node; |
|
}); |
|
shapes = shapeWithoutEdges.length ? shapeWithoutEdges : shapes; |
|
this.facade.setSelection(shapes); |
|
} else { |
|
//Command-Pattern for dragging one docker |
|
var dragDockerCommand = ORYX.Core.Command.extend({ |
|
construct: function(docker, newPos, oldPos, newDockedShape, oldDockedShape, facade){ |
|
this.docker = docker; |
|
this.index = docker.parent.dockers.indexOf(docker); |
|
this.newPosition = newPos; |
|
this.newDockedShape = newDockedShape; |
|
this.oldPosition = oldPos; |
|
this.oldDockedShape = oldDockedShape; |
|
this.facade = facade; |
|
this.index = docker.parent.dockers.indexOf(docker); |
|
this.shape = docker.parent; |
|
|
|
}, |
|
execute: function(){ |
|
if (!this.docker.parent){ |
|
this.docker = this.shape.dockers[this.index]; |
|
} |
|
this.dock( this.newDockedShape, this.newPosition ); |
|
this.removedDockers = this.shape.removeUnusedDockers(); |
|
this.facade.updateSelection(); |
|
}, |
|
rollback: function(){ |
|
this.dock( this.oldDockedShape, this.oldPosition ); |
|
(this.removedDockers||$H({})).each(function(d){ |
|
this.shape.add(d.value, Number(d.key)); |
|
this.shape._update(true); |
|
}.bind(this)); |
|
this.facade.updateSelection(); |
|
}, |
|
dock:function( toDockShape, pos ){ |
|
// Set the Docker to the new Shape |
|
this.docker.setDockedShape( undefined ); |
|
if( toDockShape ){ |
|
this.docker.setDockedShape( toDockShape ); |
|
this.docker.setReferencePoint( pos ); |
|
//this.docker.update(); |
|
//this.docker.parent._update(); |
|
} else { |
|
this.docker.bounds.centerMoveTo( pos ); |
|
} |
|
|
|
this.facade.getCanvas().update(); |
|
} |
|
}); |
|
|
|
|
|
if (this.docker.parent){ |
|
// Instanziate the dockCommand |
|
var command = new dragDockerCommand(this.docker, this.docker.getDockedShape() ? this.docker.referencePoint : this.docker.bounds.center(), this._commandArg.refPoint, this.docker.getDockedShape(), this._commandArg.dockedShape, this.facade); |
|
this.facade.executeCommands( [command] ); |
|
} |
|
} |
|
|
|
// Update all Shapes |
|
//this.facade.updateSelection(); |
|
|
|
// Undefined all variables |
|
this.docker = undefined; |
|
this.dockerParent = undefined; |
|
this.dockerSource = undefined; |
|
this.dockerTarget = undefined; |
|
this.lastUIObj = undefined; |
|
}, |
|
|
|
/** |
|
* Hide the highlighting |
|
*/ |
|
hideHighlight: function() { |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'validDockedShape'}); |
|
}, |
|
|
|
/** |
|
* Show the highlighting |
|
* |
|
*/ |
|
showHighlight: function(uiObj, color) { |
|
|
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId:'validDockedShape', |
|
elements: [uiObj], |
|
color: color |
|
}); |
|
}, |
|
|
|
showMagnets: function(uiObj){ |
|
uiObj.magnets.each(function(magnet) { |
|
magnet.show(); |
|
}); |
|
}, |
|
|
|
hideMagnets: function(uiObj){ |
|
uiObj.magnets.each(function(magnet) { |
|
magnet.hide(); |
|
}); |
|
}, |
|
|
|
getHighestParentBeforeCanvas: function(shape) { |
|
if(!(shape instanceof ORYX.Core.Shape)) {return undefined;} |
|
|
|
var parent = shape.parent; |
|
while(parent && !(parent.parent instanceof ORYX.Core.Canvas)) { |
|
parent = parent.parent; |
|
} |
|
|
|
return parent; |
|
} |
|
|
|
}); |
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.AddDocker = Clazz.extend({ |
|
|
|
/** |
|
* Constructor |
|
* @param {Object} Facade: The Facade of the Editor |
|
*/ |
|
construct: function(facade) { |
|
this.facade = facade; |
|
this.enableAdd = false; |
|
this.enableRemove = false; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this)); |
|
}, |
|
|
|
setEnableAdd: function(enable){ |
|
this.enableAdd = enable; |
|
|
|
if(this.enableAdd) { |
|
jQuery("#add-bendpoint-button").addClass('pressed'); |
|
} else { |
|
jQuery("#add-bendpoint-button").removeClass('pressed'); |
|
jQuery("#add-bendpoint-button").blur(); |
|
} |
|
}, |
|
setEnableRemove: function(enable){ |
|
this.enableRemove = enable; |
|
|
|
if(this.enableRemove) { |
|
jQuery("#remove-bendpoint-button").addClass('pressed'); |
|
} else { |
|
jQuery("#remove-bendpoint-button").removeClass('pressed'); |
|
jQuery("#remove-bendpoint-button").blur(); |
|
} |
|
}, |
|
|
|
enabledAdd: function(enable){ |
|
return this.enableAdd; |
|
}, |
|
enabledRemove: function(){ |
|
return this.enableRemove; |
|
}, |
|
|
|
/** |
|
* MouseDown Handler |
|
* |
|
*/ |
|
handleMouseDown: function(event, uiObj) { |
|
if (this.enabledAdd() && uiObj instanceof ORYX.Core.Edge) { |
|
this.newDockerCommand({ |
|
edge: uiObj, |
|
position: this.facade.eventCoordinates(event) |
|
}); |
|
this.setEnableAdd(false); |
|
|
|
} else if (this.enabledRemove() && |
|
uiObj instanceof ORYX.Core.Controls.Docker && |
|
uiObj.parent instanceof ORYX.Core.Edge) { |
|
this.newDockerCommand({ |
|
edge: uiObj.parent, |
|
docker: uiObj |
|
}); |
|
this.setEnableRemove(false); |
|
} |
|
document.body.style.cursor = 'default'; |
|
}, |
|
|
|
// Options: edge (required), position (required if add), docker (required if delete) |
|
newDockerCommand: function(options){ |
|
if(!options.edge) |
|
return; |
|
|
|
var commandClass = ORYX.Core.Command.extend({ |
|
construct: function(addEnabled, deleteEnabled, edge, docker, pos, facade){ |
|
this.addEnabled = addEnabled; |
|
this.deleteEnabled = deleteEnabled; |
|
this.edge = edge; |
|
this.docker = docker; |
|
this.pos = pos; |
|
this.facade = facade; |
|
}, |
|
execute: function(){ |
|
if (this.addEnabled) { |
|
if (!this.docker){ |
|
this.docker = this.edge.addDocker(this.pos); |
|
this.index = this.edge.dockers.indexOf(this.docker); |
|
} else { |
|
this.edge.add(this.docker, this.index); |
|
} |
|
} |
|
else if (this.deleteEnabled) { |
|
this.index = this.edge.dockers.indexOf(this.docker); |
|
this.pos = this.docker.bounds.center(); |
|
this.edge.removeDocker(this.docker); |
|
} |
|
this.edge.getLabels().invoke("show"); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
}, |
|
rollback: function(){ |
|
if (this.addEnabled) { |
|
if (this.docker instanceof ORYX.Core.Controls.Docker) { |
|
this.edge.removeDocker(this.docker); |
|
} |
|
} |
|
else if (this.deleteEnabled) { |
|
this.edge.add(this.docker, this.index); |
|
} |
|
this.edge.getLabels().invoke("show"); |
|
this.facade.getCanvas().update(); |
|
this.facade.updateSelection(); |
|
} |
|
}) |
|
|
|
var command = new commandClass(this.enabledAdd(), this.enabledRemove(), options.edge, options.docker, options.position, this.facade); |
|
|
|
this.facade.executeCommands([command]); |
|
} |
|
}); |
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.SelectionFrame = Clazz.extend({ |
|
|
|
construct: function(facade) { |
|
this.facade = facade; |
|
|
|
// Register on MouseEvents |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this)); |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.handleMouseUp.bind(this), true); |
|
|
|
// Some initiale variables |
|
this.position = {x:0, y:0}; |
|
this.size = {width:0, height:0}; |
|
this.offsetPosition = {x: 0, y: 0}; |
|
|
|
// (Un)Register Mouse-Move Event |
|
this.moveCallback = undefined; |
|
this.offsetScroll = {x:0,y:0}; |
|
// HTML-Node of Selection-Frame |
|
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", $('canvasSection'), |
|
['div', {'class':'Oryx_SelectionFrame'}]); |
|
|
|
this.hide(); |
|
}, |
|
|
|
handleMouseDown: function(event, uiObj) { |
|
// If there is the Canvas |
|
if( uiObj instanceof ORYX.Core.Canvas ) { |
|
// Calculate the Offset |
|
var scrollNode = uiObj.rootNode.parentNode.parentNode; |
|
|
|
var a = this.facade.getCanvas().node.getScreenCTM(); |
|
this.offsetPosition = { |
|
x: a.e, |
|
y: a.f |
|
}; |
|
|
|
// Set the new Position |
|
this.setPos({ |
|
x: Event.pointerX(event) - jQuery("#canvasSection").offset().left, |
|
y: Event.pointerY(event) - jQuery("#canvasSection").offset().top + 5 |
|
}); |
|
|
|
// Reset the size |
|
this.resize({width:0, height:0}); |
|
this.moveCallback = this.handleMouseMove.bind(this); |
|
|
|
// Register Mouse-Move Event |
|
document.documentElement.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false); |
|
|
|
this.offsetScroll = {x:scrollNode.scrollLeft,y:scrollNode.scrollTop}; |
|
|
|
// Show the Frame |
|
this.show(); |
|
} |
|
|
|
Event.stop(event); |
|
}, |
|
|
|
handleMouseUp: function(event) { |
|
// If there was an MouseMoving |
|
if(this.moveCallback) { |
|
// Hide the Frame |
|
this.hide(); |
|
|
|
// Unregister Mouse-Move |
|
document.documentElement.removeEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.moveCallback, false); |
|
|
|
this.moveCallback = undefined; |
|
|
|
var corrSVG = this.facade.getCanvas().node.getScreenCTM(); |
|
|
|
// Calculate the positions of the Frame |
|
var a = { |
|
x: this.size.width > 0 ? this.position.x : this.position.x + this.size.width, |
|
y: this.size.height > 0 ? this.position.y : this.position.y + this.size.height |
|
}; |
|
|
|
var b = { |
|
x: a.x + Math.abs(this.size.width), |
|
y: a.y + Math.abs(this.size.height) |
|
}; |
|
|
|
var additionalIEZoom = 1; |
|
if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) { |
|
var ua = navigator.userAgent; |
|
if (ua.indexOf('MSIE') >= 0) { |
|
//IE 10 and below |
|
var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100); |
|
if (zoom !== 100) { |
|
additionalIEZoom = zoom / 100 |
|
} |
|
} |
|
} |
|
|
|
if (additionalIEZoom === 1) { |
|
a.x = a.x - (corrSVG.e - jQuery("#canvasSection").offset().left); |
|
a.y = a.y - (corrSVG.f - jQuery("#canvasSection").offset().top); |
|
b.x = b.x - (corrSVG.e - jQuery("#canvasSection").offset().left); |
|
b.y = b.y - (corrSVG.f - jQuery("#canvasSection").offset().top); |
|
|
|
} else { |
|
var canvasOffsetLeft = jQuery("#canvasSection").offset().left; |
|
var canvasScrollLeft = jQuery("#canvasSection").scrollLeft(); |
|
var canvasScrollTop = jQuery("#canvasSection").scrollTop(); |
|
|
|
var offset = a.e - (canvasOffsetLeft * additionalIEZoom); |
|
var additionaloffset = 0; |
|
if (offset > 10) { |
|
additionaloffset = (offset / additionalIEZoom) - offset; |
|
} |
|
|
|
a.x = a.x - (corrSVG.e - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft)); |
|
a.y = a.y - (corrSVG.f - (jQuery("#canvasSection").offset().top * additionalIEZoom) + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop)); |
|
b.x = b.x - (corrSVG.e - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft)); |
|
b.y = b.y - (corrSVG.f - (jQuery("#canvasSection").offset().top * additionalIEZoom) + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop)); |
|
} |
|
|
|
|
|
|
|
// Fit to SVG-Coordinates |
|
a.x /= corrSVG.a; a.y /= corrSVG.d; |
|
b.x /= corrSVG.a; b.y /= corrSVG.d; |
|
|
|
// Calculate the elements from the childs of the canvas |
|
var elements = this.facade.getCanvas().getChildShapes(true).findAll(function(value) { |
|
var absBounds = value.absoluteBounds(); |
|
|
|
var bA = absBounds.upperLeft(); |
|
var bB = absBounds.lowerRight(); |
|
|
|
if(bA.x > a.x && bA.y > a.y && bB.x < b.x && bB.y < b.y) |
|
return true; |
|
return false; |
|
}); |
|
|
|
// Set the selection |
|
this.facade.setSelection(elements); |
|
} |
|
}, |
|
|
|
handleMouseMove: function(event) { |
|
// Calculate the size |
|
var size = { |
|
width : Event.pointerX(event) - this.position.x - jQuery("#canvasSection").offset().left, |
|
height : Event.pointerY(event) - this.position.y - jQuery("#canvasSection").offset().top + 5 |
|
}; |
|
|
|
var scrollNode = this.facade.getCanvas().rootNode.parentNode.parentNode; |
|
size.width -= this.offsetScroll.x - scrollNode.scrollLeft; |
|
size.height -= this.offsetScroll.y - scrollNode.scrollTop; |
|
|
|
// Set the size |
|
this.resize(size); |
|
|
|
Event.stop(event); |
|
}, |
|
|
|
hide: function() { |
|
this.node.style.display = "none"; |
|
}, |
|
|
|
show: function() { |
|
this.node.style.display = ""; |
|
}, |
|
|
|
setPos: function(pos) { |
|
// Set the Position |
|
this.node.style.top = pos.y + "px"; |
|
this.node.style.left = pos.x + "px"; |
|
this.position = pos; |
|
}, |
|
|
|
resize: function(size) { |
|
|
|
// Calculate the negative offset |
|
this.setPos(this.position); |
|
this.size = Object.clone(size); |
|
|
|
if(size.width < 0) { |
|
this.node.style.left = (this.position.x + size.width) + "px"; |
|
size.width = - size.width; |
|
} |
|
if(size.height < 0) { |
|
this.node.style.top = (this.position.y + size.height) + "px"; |
|
size.height = - size.height; |
|
} |
|
|
|
// Set the size |
|
this.node.style.width = size.width + "px"; |
|
this.node.style.height = size.height + "px"; |
|
} |
|
|
|
}); |
|
|
|
|
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.ShapeHighlighting = Clazz.extend({ |
|
|
|
construct: function(facade) { |
|
|
|
this.parentNode = facade.getCanvas().getSvgContainer(); |
|
|
|
// The parent Node |
|
this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.parentNode, |
|
['g']); |
|
|
|
this.highlightNodes = {}; |
|
|
|
facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, this.setHighlight.bind(this)); |
|
facade.registerOnEvent(ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, this.hideHighlight.bind(this)); |
|
|
|
}, |
|
|
|
setHighlight: function(options) { |
|
if(options && options.highlightId){ |
|
var node = this.highlightNodes[options.highlightId]; |
|
|
|
if(!node){ |
|
node= ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node, |
|
['path', { |
|
"stroke-width": 2.0, "fill":"none" |
|
}]); |
|
|
|
this.highlightNodes[options.highlightId] = node; |
|
} |
|
|
|
if(options.elements && options.elements.length > 0) { |
|
|
|
this.setAttributesByStyle( node, options ); |
|
this.show(node); |
|
|
|
} else { |
|
|
|
this.hide(node); |
|
|
|
} |
|
|
|
} |
|
}, |
|
|
|
hideHighlight: function(options) { |
|
if(options && options.highlightId && this.highlightNodes[options.highlightId]){ |
|
this.hide(this.highlightNodes[options.highlightId]); |
|
} |
|
}, |
|
|
|
hide: function(node) { |
|
node.setAttributeNS(null, 'display', 'none'); |
|
}, |
|
|
|
show: function(node) { |
|
node.setAttributeNS(null, 'display', ''); |
|
}, |
|
|
|
setAttributesByStyle: function( node, options ){ |
|
|
|
// If the style say, that it should look like a rectangle |
|
if( options.style && options.style == ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE ){ |
|
|
|
// Set like this |
|
var bo = options.elements[0].absoluteBounds(); |
|
|
|
var strWidth = options.strokewidth ? options.strokewidth : ORYX.CONFIG.BORDER_OFFSET |
|
|
|
node.setAttributeNS(null, "d", this.getPathRectangle( bo.a, bo.b , strWidth ) ); |
|
node.setAttributeNS(null, "stroke", options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR); |
|
node.setAttributeNS(null, "stroke-opacity", options.opacity ? options.opacity : 0.2); |
|
node.setAttributeNS(null, "stroke-width", strWidth); |
|
|
|
} else if(options.elements.length == 1 |
|
&& options.elements[0] instanceof ORYX.Core.Edge && |
|
options.highlightId != "selection") { |
|
|
|
/* Highlight containment of edge's childs */ |
|
var path = this.getPathEdge(options.elements[0].dockers); |
|
if (path && path.length > 0) |
|
{ |
|
node.setAttributeNS(null, "d", path); |
|
} |
|
node.setAttributeNS(null, "stroke", options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR); |
|
node.setAttributeNS(null, "stroke-opacity", options.opacity ? options.opacity : 0.2); |
|
node.setAttributeNS(null, "stroke-width", ORYX.CONFIG.OFFSET_EDGE_BOUNDS); |
|
|
|
}else { |
|
// If not, set just the corners |
|
var path = this.getPathByElements(options.elements); |
|
if (path && path.length > 0) |
|
{ |
|
node.setAttributeNS(null, "d", path); |
|
} |
|
node.setAttributeNS(null, "stroke", options.color ? options.color : ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR); |
|
node.setAttributeNS(null, "stroke-opacity", options.opacity ? options.opacity : 1.0); |
|
node.setAttributeNS(null, "stroke-width", options.strokewidth ? options.strokewidth : 2.0); |
|
|
|
} |
|
}, |
|
|
|
getPathByElements: function(elements){ |
|
if(!elements || elements.length <= 0) {return undefined} |
|
|
|
// Get the padding and the size |
|
var padding = ORYX.CONFIG.SELECTED_AREA_PADDING; |
|
|
|
var path = "" |
|
|
|
// Get thru all Elements |
|
elements.each((function(element) { |
|
if(!element) {return} |
|
// Get the absolute Bounds and the two Points |
|
var bounds = element.absoluteBounds(); |
|
bounds.widen(padding) |
|
var a = bounds.upperLeft(); |
|
var b = bounds.lowerRight(); |
|
|
|
path = path + this.getPath(a ,b); |
|
|
|
}).bind(this)); |
|
|
|
return path; |
|
|
|
}, |
|
|
|
getPath: function(a, b){ |
|
|
|
return this.getPathCorners(a, b); |
|
|
|
}, |
|
|
|
getPathCorners: function(a, b){ |
|
|
|
var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE; |
|
|
|
var path = "" |
|
|
|
// Set: Upper left |
|
path = path + "M" + a.x + " " + (a.y + size) + " l0 -" + size + " l" + size + " 0 "; |
|
// Set: Lower left |
|
path = path + "M" + a.x + " " + (b.y - size) + " l0 " + size + " l" + size + " 0 "; |
|
// Set: Lower right |
|
path = path + "M" + b.x + " " + (b.y - size) + " l0 " + size + " l-" + size + " 0 "; |
|
// Set: Upper right |
|
path = path + "M" + b.x + " " + (a.y + size) + " l0 -" + size + " l-" + size + " 0 "; |
|
|
|
return path; |
|
}, |
|
|
|
getPathRectangle: function(a, b, strokeWidth){ |
|
|
|
var size = ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE; |
|
|
|
var path = "" |
|
var offset = strokeWidth / 2.0; |
|
|
|
// Set: Upper left |
|
path = path + "M" + (a.x + offset) + " " + (a.y); |
|
path = path + " L" + (a.x + offset) + " " + (b.y - offset); |
|
path = path + " L" + (b.x - offset) + " " + (b.y - offset); |
|
path = path + " L" + (b.x - offset) + " " + (a.y + offset); |
|
path = path + " L" + (a.x + offset) + " " + (a.y + offset); |
|
|
|
return path; |
|
}, |
|
|
|
getPathEdge: function(edgeDockers) { |
|
var length = edgeDockers.length; |
|
var path = "M" + edgeDockers[0].bounds.center().x + " " |
|
+ edgeDockers[0].bounds.center().y; |
|
|
|
for(i=1; i<length; i++) { |
|
var dockerPoint = edgeDockers[i].bounds.center(); |
|
path = path + " L" + dockerPoint.x + " " + dockerPoint.y; |
|
} |
|
|
|
return path; |
|
} |
|
|
|
}); |
|
|
|
|
|
ORYX.Plugins.HighlightingSelectedShapes = Clazz.extend({ |
|
|
|
construct: function(facade) { |
|
this.facade = facade; |
|
this.opacityFull = 0.9; |
|
this.opacityLow = 0.4; |
|
|
|
// Register on Dragging-Events for show/hide of ShapeMenu |
|
//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hide.bind(this)); |
|
//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END, this.show.bind(this)); |
|
}, |
|
|
|
/** |
|
* On the Selection-Changed |
|
* |
|
*/ |
|
onSelectionChanged: function(event) { |
|
if(event.elements && event.elements.length > 1) { |
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId:'selection', |
|
elements: event.elements.without(event.subSelection), |
|
color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR, |
|
opacity: !event.subSelection ? this.opacityFull : this.opacityLow |
|
}); |
|
|
|
if(event.subSelection){ |
|
this.facade.raiseEvent({ |
|
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, |
|
highlightId:'subselection', |
|
elements: [event.subSelection], |
|
color: ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR, |
|
opacity: this.opacityFull |
|
}); |
|
} else { |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'}); |
|
} |
|
|
|
} else { |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'selection'}); |
|
this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'}); |
|
} |
|
} |
|
});/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.Overlay = Clazz.extend({ |
|
|
|
facade: undefined, |
|
|
|
styleNode: undefined, |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
|
|
this.changes = []; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_SHOW, this.show.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_HIDE, this.hide.bind(this)); |
|
|
|
this.styleNode = document.createElement('style') |
|
this.styleNode.setAttributeNS(null, 'type', 'text/css') |
|
|
|
document.getElementsByTagName('head')[0].appendChild( this.styleNode ) |
|
|
|
}, |
|
|
|
/** |
|
* Show the overlay for specific nodes |
|
* @param {Object} options |
|
* |
|
* String options.id - MUST - Define the id of the overlay (is needed for the hiding of this overlay) |
|
* ORYX.Core.Shape[] options.shapes - MUST - Define the Shapes for the changes |
|
* attr-name:value options.changes - Defines all the changes which should be shown |
|
* |
|
* |
|
*/ |
|
show: function( options ){ |
|
|
|
// Checks if all arguments are available |
|
if( !options || |
|
!options.shapes || !options.shapes instanceof Array || |
|
!options.id || !options.id instanceof String || options.id.length == 0) { |
|
|
|
return |
|
|
|
} |
|
|
|
//if( this.changes[options.id]){ |
|
// this.hide( options ) |
|
//} |
|
|
|
|
|
// Checked if attributes are setted |
|
if( options.attributes ){ |
|
|
|
// FOR EACH - Shape |
|
options.shapes.each(function(el){ |
|
|
|
// Checks if the node is a Shape |
|
if( !el instanceof ORYX.Core.Shape){ return } |
|
|
|
this.setAttributes( el.node , options.attributes ) |
|
|
|
}.bind(this)) |
|
|
|
} |
|
|
|
var isSVG = true |
|
try { |
|
isSVG = options.node && options.node instanceof SVGElement; |
|
} catch(e){} |
|
|
|
// Checks if node is setted and if this is an SVGElement |
|
if ( options.node && isSVG) { |
|
|
|
options["_temps"] = [] |
|
|
|
// FOR EACH - Node |
|
options.shapes.each(function(el, index){ |
|
|
|
// Checks if the node is a Shape |
|
if( !el instanceof ORYX.Core.Shape){ return } |
|
|
|
var _temp = {} |
|
_temp.svg = options.dontCloneNode ? options.node : options.node.cloneNode( true ); |
|
|
|
// Add the svg node to the ORYX-Shape |
|
el.node.firstChild.appendChild( _temp.svg ) |
|
|
|
// If |
|
if (el instanceof ORYX.Core.Edge && !options.nodePosition) { |
|
options['nodePosition'] = "START" |
|
} |
|
|
|
// If the node position is setted, it has to be transformed |
|
if( options.nodePosition ){ |
|
|
|
var b = el.bounds; |
|
var p = options.nodePosition.toUpperCase(); |
|
|
|
// Check the values of START and END |
|
if( el instanceof ORYX.Core.Node && p == "START"){ |
|
p = "NW"; |
|
} else if(el instanceof ORYX.Core.Node && p == "END"){ |
|
p = "SE"; |
|
} else if(el instanceof ORYX.Core.Edge && p == "START"){ |
|
b = el.getDockers().first().bounds |
|
} else if(el instanceof ORYX.Core.Edge && p == "END"){ |
|
b = el.getDockers().last().bounds |
|
} |
|
|
|
// Create a callback for the changing the position |
|
// depending on the position string |
|
_temp.callback = function(){ |
|
|
|
var x = 0; var y = 0; |
|
|
|
if( p == "NW" ){ |
|
// Do Nothing |
|
} else if( p == "N" ) { |
|
x = b.width() / 2; |
|
} else if( p == "NE" ) { |
|
x = b.width(); |
|
} else if( p == "E" ) { |
|
x = b.width(); y = b.height() / 2; |
|
} else if( p == "SE" ) { |
|
x = b.width(); y = b.height(); |
|
} else if( p == "S" ) { |
|
x = b.width() / 2; y = b.height(); |
|
} else if( p == "SW" ) { |
|
y = b.height(); |
|
} else if( p == "W" ) { |
|
y = b.height() / 2; |
|
} else if( p == "START" || p == "END") { |
|
x = b.width() / 2; y = b.height() / 2; |
|
} else { |
|
return |
|
} |
|
|
|
if( el instanceof ORYX.Core.Edge){ |
|
x += b.upperLeft().x ; y += b.upperLeft().y ; |
|
} |
|
|
|
_temp.svg.setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")") |
|
|
|
}.bind(this) |
|
|
|
_temp.element = el; |
|
_temp.callback(); |
|
|
|
b.registerCallback( _temp.callback ); |
|
|
|
} |
|
|
|
|
|
options._temps.push( _temp ) |
|
|
|
}.bind(this)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
// Store the changes |
|
if( !this.changes[options.id] ){ |
|
this.changes[options.id] = []; |
|
} |
|
|
|
this.changes[options.id].push( options ); |
|
|
|
}, |
|
|
|
/** |
|
* Hide the overlay with the spefic id |
|
* @param {Object} options |
|
*/ |
|
hide: function( options ){ |
|
|
|
// Checks if all arguments are available |
|
if( !options || |
|
!options.id || !options.id instanceof String || options.id.length == 0 || |
|
!this.changes[options.id]) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
// Delete all added attributes |
|
// FOR EACH - Shape |
|
this.changes[options.id].each(function(option){ |
|
|
|
option.shapes.each(function(el, index){ |
|
|
|
// Checks if the node is a Shape |
|
if( !el instanceof ORYX.Core.Shape){ return } |
|
|
|
this.deleteAttributes( el.node ) |
|
|
|
}.bind(this)); |
|
|
|
|
|
if( option._temps ){ |
|
|
|
option._temps.each(function(tmp){ |
|
// Delete the added Node, if there is one |
|
if( tmp.svg && tmp.svg.parentNode ){ |
|
tmp.svg.parentNode.removeChild( tmp.svg ) |
|
} |
|
|
|
// If |
|
if( tmp.callback && tmp.element){ |
|
// It has to be unregistered from the edge |
|
tmp.element.bounds.unregisterCallback( tmp.callback ) |
|
} |
|
|
|
}.bind(this)) |
|
|
|
} |
|
|
|
|
|
}.bind(this)); |
|
|
|
|
|
this.changes[options.id] = null; |
|
|
|
|
|
}, |
|
|
|
|
|
/** |
|
* Set the given css attributes to that node |
|
* @param {HTMLElement} node |
|
* @param {Object} attributes |
|
*/ |
|
setAttributes: function( node, attributes ) { |
|
|
|
|
|
// Get all the childs from ME |
|
var childs = this.getAllChilds( node.firstChild.firstChild ) |
|
|
|
var ids = [] |
|
|
|
// Add all Attributes which have relation to another node in this document and concate the pure id out of it |
|
// This is for example important for the markers of a edge |
|
childs.each(function(e){ ids.push( $A(e.attributes).findAll(function(attr){ return attr.nodeValue.startsWith('url(#')}) )}) |
|
ids = ids.flatten().compact(); |
|
ids = ids.collect(function(s){return s.nodeValue}).uniq(); |
|
ids = ids.collect(function(s){return s.slice(5, s.length-1)}) |
|
|
|
// Add the node ID to the id |
|
ids.unshift( node.id + ' .me') |
|
|
|
var attr = $H(attributes); |
|
var attrValue = attr.toJSON().gsub(',', ';').gsub('"', ''); |
|
var attrMarkerValue = attributes.stroke ? attrValue.slice(0, attrValue.length-1) + "; fill:" + attributes.stroke + ";}" : attrValue; |
|
var attrTextValue; |
|
if( attributes.fill ){ |
|
var copyAttr = Object.clone(attributes); |
|
copyAttr.fill = "black"; |
|
attrTextValue = $H(copyAttr).toJSON().gsub(',', ';').gsub('"', ''); |
|
} |
|
|
|
// Create the CSS-Tags Style out of the ids and the attributes |
|
csstags = ids.collect(function(s, i){return "#" + s + " * " + (!i? attrValue : attrMarkerValue) + "" + (attrTextValue ? " #" + s + " text * " + attrTextValue : "") }) |
|
|
|
// Join all the tags |
|
var s = csstags.join(" ") + "\n" |
|
|
|
// And add to the end of the style tag |
|
this.styleNode.appendChild(document.createTextNode(s)); |
|
|
|
|
|
}, |
|
|
|
/** |
|
* Deletes all attributes which are |
|
* added in a special style sheet for that node |
|
* @param {HTMLElement} node |
|
*/ |
|
deleteAttributes: function( node ) { |
|
|
|
// Get all children which contains the node id |
|
var delEl = $A(this.styleNode.childNodes) |
|
.findAll(function(e){ return e.textContent.include( '#' + node.id ) }); |
|
|
|
// Remove all of them |
|
delEl.each(function(el){ |
|
el.parentNode.removeChild(el); |
|
}); |
|
}, |
|
|
|
getAllChilds: function( node ){ |
|
|
|
var childs = $A(node.childNodes) |
|
|
|
$A(node.childNodes).each(function( e ){ |
|
childs.push( this.getAllChilds( e ) ) |
|
}.bind(this)) |
|
|
|
return childs.flatten(); |
|
} |
|
|
|
|
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if (!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
ORYX.Plugins.KeysMove = ORYX.Plugins.AbstractPlugin.extend({ |
|
|
|
facade: undefined, |
|
|
|
construct: function(facade){ |
|
|
|
this.facade = facade; |
|
this.copyElements = []; |
|
|
|
//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this)); |
|
|
|
// SELECT ALL |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: 65, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.selectAll.bind(this) |
|
}); |
|
|
|
// MOVE LEFT SMALL |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: ORYX.CONFIG.KEY_CODE_LEFT, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, false) |
|
}); |
|
|
|
// MOVE LEFT |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
keyCode: ORYX.CONFIG.KEY_CODE_LEFT, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, true) |
|
}); |
|
|
|
// MOVE RIGHT SMALL |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: ORYX.CONFIG.KEY_CODE_RIGHT, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, false) |
|
}); |
|
|
|
// MOVE RIGHT |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
keyCode: ORYX.CONFIG.KEY_CODE_RIGHT, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, true) |
|
}); |
|
|
|
// MOVE UP SMALL |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: ORYX.CONFIG.KEY_CODE_UP, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, false) |
|
}); |
|
|
|
// MOVE UP |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
keyCode: ORYX.CONFIG.KEY_CODE_UP, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, true) |
|
}); |
|
|
|
// MOVE DOWN SMALL |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL], |
|
keyCode: ORYX.CONFIG.KEY_CODE_DOWN, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, false) |
|
}); |
|
|
|
// MOVE DOWN |
|
this.facade.offer({ |
|
keyCodes: [{ |
|
keyCode: ORYX.CONFIG.KEY_CODE_DOWN, |
|
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN |
|
} |
|
], |
|
functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, true) |
|
}); |
|
|
|
|
|
}, |
|
|
|
/** |
|
* Select all shapes in the editor |
|
* |
|
*/ |
|
selectAll: function(e){ |
|
Event.stop(e.event); |
|
this.facade.setSelection(this.facade.getCanvas().getChildShapes(true)) |
|
}, |
|
|
|
move: function(key, far, e) { |
|
|
|
Event.stop(e.event); |
|
|
|
// calculate the distance to move the objects and get the selection. |
|
var distance = far? 20 : 5; |
|
var selection = this.facade.getSelection(); |
|
var currentSelection = this.facade.getSelection(); |
|
var p = {x: 0, y: 0}; |
|
|
|
// switch on the key pressed and populate the point to move by. |
|
switch(key) { |
|
|
|
case ORYX.CONFIG.KEY_CODE_LEFT: |
|
p.x = -1*distance; |
|
break; |
|
case ORYX.CONFIG.KEY_CODE_RIGHT: |
|
p.x = distance; |
|
break; |
|
case ORYX.CONFIG.KEY_CODE_UP: |
|
p.y = -1*distance; |
|
break; |
|
case ORYX.CONFIG.KEY_CODE_DOWN: |
|
p.y = distance; |
|
break; |
|
} |
|
|
|
// move each shape in the selection by the point calculated and update it. |
|
selection = selection.findAll(function(shape){ |
|
// Check if this shape is docked to an shape in the selection |
|
if(shape instanceof ORYX.Core.Node && shape.dockers.length == 1 && selection.include( shape.dockers.first().getDockedShape() )){ |
|
return false |
|
} |
|
|
|
// Check if any of the parent shape is included in the selection |
|
var s = shape.parent; |
|
do{ |
|
if(selection.include(s)){ |
|
return false |
|
} |
|
}while(s = s.parent); |
|
|
|
// Otherwise, return true |
|
return true; |
|
|
|
}); |
|
|
|
/* Edges must not be movable, if only edges are selected and at least |
|
* one of them is docked. |
|
*/ |
|
var edgesMovable = true; |
|
var onlyEdgesSelected = selection.all(function(shape) { |
|
if(shape instanceof ORYX.Core.Edge) { |
|
if(shape.isDocked()) { |
|
edgesMovable = false; |
|
} |
|
return true; |
|
} |
|
return false; |
|
}); |
|
|
|
if(onlyEdgesSelected && !edgesMovable) { |
|
/* Abort moving shapes */ |
|
return; |
|
} |
|
|
|
selection = selection.map(function(shape){ |
|
if( shape instanceof ORYX.Core.Node ){ |
|
/*if( shape.dockers.length == 1 ){ |
|
return shape.dockers.first() |
|
} else {*/ |
|
return shape |
|
//} |
|
} else if( shape instanceof ORYX.Core.Edge ) { |
|
|
|
var dockers = shape.dockers; |
|
|
|
if( selection.include( shape.dockers.first().getDockedShape() ) ){ |
|
dockers = dockers.without( shape.dockers.first() ) |
|
} |
|
|
|
if( selection.include( shape.dockers.last().getDockedShape() ) ){ |
|
dockers = dockers.without( shape.dockers.last() ) |
|
} |
|
|
|
return dockers |
|
|
|
} else { |
|
return null |
|
} |
|
|
|
}).flatten().compact(); |
|
|
|
if (selection.size() > 0) { |
|
|
|
//Stop moving at canvas borders |
|
var selectionBounds = [ this.facade.getCanvas().bounds.lowerRight().x, |
|
this.facade.getCanvas().bounds.lowerRight().y, |
|
0, |
|
0 ]; |
|
selection.each(function(s) { |
|
selectionBounds[0] = Math.min(selectionBounds[0], s.bounds.upperLeft().x); |
|
selectionBounds[1] = Math.min(selectionBounds[1], s.bounds.upperLeft().y); |
|
selectionBounds[2] = Math.max(selectionBounds[2], s.bounds.lowerRight().x); |
|
selectionBounds[3] = Math.max(selectionBounds[3], s.bounds.lowerRight().y); |
|
}); |
|
if(selectionBounds[0]+p.x < 0) |
|
p.x = -selectionBounds[0]; |
|
if(selectionBounds[1]+p.y < 0) |
|
p.y = -selectionBounds[1]; |
|
if(selectionBounds[2]+p.x > this.facade.getCanvas().bounds.lowerRight().x) |
|
p.x = this.facade.getCanvas().bounds.lowerRight().x - selectionBounds[2]; |
|
if(selectionBounds[3]+p.y > this.facade.getCanvas().bounds.lowerRight().y) |
|
p.y = this.facade.getCanvas().bounds.lowerRight().y - selectionBounds[3]; |
|
|
|
if(p.x!=0 || p.y!=0) { |
|
// Instantiate the moveCommand |
|
var commands = [new ORYX.Core.Command.Move(selection, p, null, currentSelection, this)]; |
|
// Execute the commands |
|
this.facade.executeCommands(commands); |
|
} |
|
|
|
} |
|
}, |
|
|
|
getUndockedCommant: function(shapes){ |
|
|
|
var undockEdgeCommand = ORYX.Core.Command.extend({ |
|
construct: function(moveShapes){ |
|
this.dockers = moveShapes.collect(function(shape){ return shape instanceof ORYX.Core.Controls.Docker ? {docker:shape, dockedShape:shape.getDockedShape(), refPoint:shape.referencePoint} : undefined }).compact(); |
|
}, |
|
execute: function(){ |
|
this.dockers.each(function(el){ |
|
el.docker.setDockedShape(undefined); |
|
}) |
|
}, |
|
rollback: function(){ |
|
this.dockers.each(function(el){ |
|
el.docker.setDockedShape(el.dockedShape); |
|
el.docker.setReferencePoint(el.refPoint); |
|
//el.docker.update(); |
|
}) |
|
} |
|
}); |
|
|
|
command = new undockEdgeCommand( shapes ); |
|
command.execute(); |
|
return command; |
|
}, |
|
|
|
// /** |
|
// * The key handler for this plugin. Every action from the set of cut, copy, |
|
// * paste and delete should be accessible trough simple keyboard shortcuts. |
|
// * This method checks whether any event triggers one of those actions. |
|
// * |
|
// * @param {Object} event The keyboard event that should be analysed for |
|
// * triggering of this plugin. |
|
// */ |
|
// keyHandler: function(event){ |
|
// //TODO document what event.which is. |
|
// |
|
// ORYX.Log.debug("keysMove.js handles a keyEvent."); |
|
// |
|
// // assure we have the current event. |
|
// if (!event) |
|
// event = window.event; |
|
// |
|
// // get the currently pressed key and state of control key. |
|
// var pressedKey = event.which || event.keyCode; |
|
// var ctrlPressed = event.ctrlKey; |
|
// |
|
// // if the key is one of the arrow keys, forward to move and return. |
|
// if ([ORYX.CONFIG.KEY_CODE_LEFT, ORYX.CONFIG.KEY_CODE_RIGHT, |
|
// ORYX.CONFIG.KEY_CODE_UP, ORYX.CONFIG.KEY_CODE_DOWN].include(pressedKey)) { |
|
// |
|
// this.move(pressedKey, !ctrlPressed); |
|
// return; |
|
// } |
|
// |
|
// } |
|
|
|
}); |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) { ORYX.Plugins = {} } |
|
if(!ORYX.Plugins.Layouter) { ORYX.Plugins.Layouter = {} } |
|
|
|
new function(){ |
|
|
|
/** |
|
* Edge layouter is an implementation to layout an edge |
|
* @class ORYX.Plugins.Layouter.EdgeLayouter |
|
* @author Willi Tscheschner |
|
*/ |
|
ORYX.Plugins.Layouter.EdgeLayouter = ORYX.Plugins.AbstractLayouter.extend({ |
|
|
|
/** |
|
* Layout only Edges |
|
*/ |
|
layouted : [ "http://b3mn.org/stencilset/bpmn1.1#SequenceFlow", |
|
"http://b3mn.org/stencilset/bpmn1.1#MessageFlow", |
|
"http://b3mn.org/stencilset/timjpdl3#SequenceFlow", |
|
"http://b3mn.org/stencilset/jbpm4#SequenceFlow", |
|
"http://b3mn.org/stencilset/bpmn2.0#MessageFlow", |
|
"http://b3mn.org/stencilset/bpmn2.0#SequenceFlow", |
|
"http://b3mn.org/stencilset/bpmn2.0choreography#MessageFlow", |
|
"http://b3mn.org/stencilset/bpmn2.0choreography#SequenceFlow", |
|
"http://b3mn.org/stencilset/bpmn2.0conversation#ConversationLink", |
|
"http://b3mn.org/stencilset/epc#ControlFlow", |
|
"http://www.signavio.com/stencilsets/processmap#ProcessLink", |
|
"http://www.signavio.com/stencilsets/organigram#connection"], |
|
|
|
/** |
|
* Layout a set on edges |
|
* @param {Object} edges |
|
*/ |
|
layout: function(edges){ |
|
edges.each(function(edge){ |
|
this.doLayout(edge) |
|
}.bind(this)) |
|
}, |
|
|
|
/** |
|
* Layout one edge |
|
* @param {Object} edge |
|
*/ |
|
doLayout: function(edge){ |
|
// Get from and to node |
|
var from = edge.getIncomingNodes()[0]; |
|
var to = edge.getOutgoingNodes()[0]; |
|
|
|
// Return if one is null |
|
if (!from || !to) { return } |
|
|
|
var positions = this.getPositions(from, to, edge); |
|
|
|
if (positions.length > 0){ |
|
this.setDockers(edge, positions[0].a, positions[0].b); |
|
} |
|
|
|
}, |
|
|
|
/** |
|
* Returns a set on positions which are not containt either |
|
* in the bounds in from or to. |
|
* @param {Object} from Shape where the edge is come from |
|
* @param {Object} to Shape where the edge is leading to |
|
* @param {Object} edge Edge between from and to |
|
*/ |
|
getPositions : function(from, to, edge){ |
|
|
|
// Get absolute bounds |
|
var ab = from.absoluteBounds(); |
|
var bb = to.absoluteBounds(); |
|
|
|
// Get center from and to |
|
var a = ab.center(); |
|
var b = bb.center(); |
|
|
|
var am = ab.midPoint(); |
|
var bm = bb.midPoint(); |
|
|
|
// Get first and last reference point |
|
var first = Object.clone(edge.dockers.first().referencePoint); |
|
var last = Object.clone(edge.dockers.last().referencePoint); |
|
// Get the absolute one |
|
var aFirst = edge.dockers.first().getAbsoluteReferencePoint(); |
|
var aLast = edge.dockers.last().getAbsoluteReferencePoint(); |
|
|
|
// IF ------> |
|
// or | |
|
// V |
|
// Do nothing |
|
if (Math.abs(aFirst.x-aLast.x) < 1 || Math.abs(aFirst.y-aLast.y) < 1) { |
|
return [] |
|
} |
|
|
|
// Calc center position, between a and b |
|
// depending on there weight |
|
var m = {} |
|
m.x = a.x < b.x ? |
|
(((b.x - bb.width()/2) - (a.x + ab.width()/2))/2) + (a.x + ab.width()/2): |
|
(((a.x - ab.width()/2) - (b.x + bb.width()/2))/2) + (b.x + bb.width()/2); |
|
|
|
m.y = a.y < b.y ? |
|
(((b.y - bb.height()/2) - (a.y + ab.height()/2))/2) + (a.y + ab.height()/2): |
|
(((a.y - ab.height()/2) - (b.y + bb.height()/2))/2) + (b.y + bb.height()/2); |
|
|
|
|
|
// Enlarge both bounds with 10 |
|
ab.widen(5); // Wide the from less than |
|
bb.widen(20);// the to because of the arrow from the edge |
|
|
|
var positions = []; |
|
var off = this.getOffset.bind(this); |
|
|
|
// Checks ----+ |
|
// | |
|
// V |
|
if (!ab.isIncluded(b.x, a.y)&&!bb.isIncluded(b.x, a.y)) { |
|
positions.push({ |
|
a : {x:b.x+off(last,bm,"x"),y:a.y+off(first,am,"y")}, |
|
z : this.getWeight(from, a.x < b.x ? "r" : "l", to, a.y < b.y ? "t" : "b", edge) |
|
}); |
|
} |
|
|
|
// Checks | |
|
// +---> |
|
if (!ab.isIncluded(a.x, b.y)&&!bb.isIncluded(a.x, b.y)) { |
|
positions.push({ |
|
a : {x:a.x+off(first,am,"x"),y:b.y+off(last,bm,"y")}, |
|
z : this.getWeight(from, a.y < b.y ? "b" : "t", to, a.x < b.x ? "l" : "r", edge) |
|
}); |
|
} |
|
|
|
// Checks --+ |
|
// | |
|
// +---> |
|
if (!ab.isIncluded(m.x, a.y)&&!bb.isIncluded(m.x, b.y)) { |
|
positions.push({ |
|
a : {x:m.x,y:a.y+off(first,am,"y")}, |
|
b : {x:m.x,y:b.y+off(last,bm,"y")}, |
|
z : this.getWeight(from, "r", to, "l", edge, a.x > b.x) |
|
}); |
|
} |
|
|
|
// Checks | |
|
// +---+ |
|
// | |
|
// V |
|
if (!ab.isIncluded(a.x, m.y)&&!bb.isIncluded(b.x, m.y)) { |
|
positions.push({ |
|
a : {x:a.x+off(first,am,"x"),y:m.y}, |
|
b : {x:b.x+off(last,bm,"x"),y:m.y}, |
|
z : this.getWeight(from, "b", to, "t", edge, a.y > b.y) |
|
}); |
|
} |
|
|
|
// Sort DESC of weights |
|
return positions.sort(function(a,b){ return a.z < b.z ? 1 : (a.z == b.z ? -1 : -1)}); |
|
}, |
|
|
|
/** |
|
* Returns a offset for the pos to the center of the bounds |
|
* |
|
* @param {Object} val |
|
* @param {Object} pos2 |
|
* @param {String} dir Direction x|y |
|
*/ |
|
getOffset: function(pos, pos2, dir){ |
|
return pos[dir] - pos2[dir]; |
|
}, |
|
|
|
/** |
|
* Returns a value which shows the weight for this configuration |
|
* |
|
* @param {Object} from Shape which is coming from |
|
* @param {String} d1 Direction where is goes |
|
* @param {Object} to Shape which goes to |
|
* @param {String} d2 Direction where it comes to |
|
* @param {Object} edge Edge between from and to |
|
* @param {Boolean} reverse Reverse the direction (e.g. "r" -> "l") |
|
*/ |
|
getWeight: function(from, d1, to, d2, edge, reverse){ |
|
|
|
d1 = (d1||"").toLowerCase(); |
|
d2 = (d2||"").toLowerCase(); |
|
|
|
if (!["t","r","b","l"].include(d1)){ d1 = "r"} |
|
if (!["t","r","b","l"].include(d2)){ d1 = "l"} |
|
|
|
// If reverse is set |
|
if (reverse) { |
|
// Reverse d1 and d2 |
|
d1 = d1=="t"?"b":(d1=="r"?"l":(d1=="b"?"t":(d1=="l"?"r":"r"))) |
|
d2 = d2=="t"?"b":(d2=="r"?"l":(d2=="b"?"t":(d2=="l"?"r":"r"))) |
|
} |
|
|
|
|
|
var weight = 0; |
|
// Get rules for from "out" and to "in" |
|
var dr1 = this.facade.getRules().getLayoutingRules(from, edge)["out"]; |
|
var dr2 = this.facade.getRules().getLayoutingRules(to, edge)["in"]; |
|
|
|
var fromWeight = dr1[d1]; |
|
var toWeight = dr2[d2]; |
|
|
|
|
|
/** |
|
* Return a true if the center 1 is in the same direction than center 2 |
|
* @param {Object} direction |
|
* @param {Object} center1 |
|
* @param {Object} center2 |
|
*/ |
|
var sameDirection = function(direction, center1, center2){ |
|
switch(direction){ |
|
case "t": return Math.abs(center1.x - center2.x) < 2 && center1.y < center2.y |
|
case "r": return center1.x > center2.x && Math.abs(center1.y - center2.y) < 2 |
|
case "b": return Math.abs(center1.x - center2.x) < 2 && center1.y > center2.y |
|
case "l": return center1.x < center2.x && Math.abs(center1.y - center2.y) < 2 |
|
default: return false; |
|
} |
|
} |
|
|
|
// Check if there are same incoming edges from 'from' |
|
var sameIncomingFrom = from |
|
.getIncomingShapes() |
|
.findAll(function(a){ return a instanceof ORYX.Core.Edge}) |
|
.any(function(e){ |
|
return sameDirection(d1, e.dockers[e.dockers.length-2].bounds.center(), e.dockers.last().bounds.center()); |
|
}); |
|
|
|
// Check if there are same outgoing edges from 'to' |
|
var sameOutgoingTo = to |
|
.getOutgoingShapes() |
|
.findAll(function(a){ return a instanceof ORYX.Core.Edge}) |
|
.any(function(e){ |
|
return sameDirection(d2, e.dockers[1].bounds.center(), e.dockers.first().bounds.center()); |
|
}); |
|
|
|
// If there are equivalent edges, set 0 |
|
//fromWeight = sameIncomingFrom ? 0 : fromWeight; |
|
//toWeight = sameOutgoingTo ? 0 : toWeight; |
|
|
|
// Get the sum of "out" and the direction plus "in" and the direction |
|
return (sameIncomingFrom||sameOutgoingTo?0:fromWeight+toWeight); |
|
}, |
|
|
|
/** |
|
* Removes all current dockers from the node |
|
* (except the start and end) and adds two new |
|
* dockers, on the position a and b. |
|
* @param {Object} edge |
|
* @param {Object} a |
|
* @param {Object} b |
|
*/ |
|
setDockers: function(edge, a, b){ |
|
if (!edge){ return } |
|
|
|
// Remove all dockers (implicit, |
|
// start and end dockers will not removed) |
|
edge.dockers.each(function(r){ |
|
edge.removeDocker(r); |
|
}); |
|
|
|
// For a and b (if exists), create |
|
// a new docker and set position |
|
[a, b].compact().each(function(pos){ |
|
var docker = edge.createDocker(undefined, pos); |
|
docker.bounds.centerMoveTo(pos); |
|
}); |
|
|
|
// Update all dockers from the edge |
|
edge.dockers.each(function(docker){ |
|
docker.update() |
|
}) |
|
|
|
// Update edge |
|
//edge.refresh(); |
|
edge._update(true); |
|
|
|
} |
|
}); |
|
|
|
|
|
}() |
|
/* |
|
* Copyright 2005-2014 Alfresco Software, Ltd. All rights reserved. |
|
* License rights for this program may be obtained from Alfresco Software, Ltd. |
|
* pursuant to a written agreement and any use of this program without such an |
|
* agreement is prohibited. |
|
*/ |
|
/* |
|
* All code Copyright 2013 KIS Consultancy all rights reserved |
|
*/ |
|
|
|
if(!ORYX.Plugins) |
|
ORYX.Plugins = new Object(); |
|
|
|
new function(){ |
|
|
|
ORYX.Plugins.BPMN2_0 = { |
|
|
|
/** |
|
* Constructor |
|
* @param {Object} Facade: The Facade of the Editor |
|
*/ |
|
construct: function(facade){ |
|
this.facade = facade; |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED, this.handleDockerDocked.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED, this.handlePropertyChanged.bind(this)); |
|
this.facade.registerOnEvent('layout.bpmn2_0.pool', this.handleLayoutPool.bind(this)); |
|
this.facade.registerOnEvent('layout.bpmn2_0.subprocess', this.handleSubProcess.bind(this)); |
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, this.handleShapeRemove.bind(this)); |
|
|
|
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.afterLoad.bind(this)); |
|
|
|
|
|
this.namespace = undefined; |
|
}, |
|
|
|
/** |
|
* Force to update every pool |
|
*/ |
|
afterLoad: function(){ |
|
this.facade.getCanvas().getChildNodes().each(function(shape){ |
|
if (shape.getStencil().id().endsWith("Pool")) { |
|
this.handleLayoutPool({ |
|
shape: shape |
|
}); |
|
} |
|
}.bind(this)) |
|
}, |
|
|
|
/** |
|
* If a pool is selected and contains no lane, |
|
* a lane is created automagically |
|
*/ |
|
onSelectionChanged: function(event) { |
|
var selection = event.elements; |
|
|
|
if(selection && selection.length === 1) { |
|
var namespace = this.getNamespace(); |
|
var shape = selection[0]; |
|
if(shape.getStencil().idWithoutNs() === "Pool") { |
|
if(shape.getChildNodes().length === 0) { |
|
// create a lane inside the selected pool |
|
var option = { |
|
type:namespace + "Lane", |
|
position:{x:0,y:0}, |
|
namespace:shape.getStencil().namespace(), |
|
parent:shape |
|
}; |
|
this.facade.createShape(option); |
|
this.facade.getCanvas().update(); |
|
this.facade.setSelection([shape]); |
|
} |
|
} |
|
} |
|
|
|
// Preventing selection of all lanes but not the pool |
|
if(selection.any(function(s){ return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith("Lane")})){ |
|
var lanes = selection.findAll(function(s){ |
|
return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith("Lane") |
|
}); |
|
|
|
var pools = []; |
|
var unselectLanes = []; |
|
lanes.each(function(lane){ |
|
pools.push(this.getParentPool(lane)) |
|
}.bind(this)); |
|
|
|
pools = pools.uniq().findAll(function(pool){ |
|
var childLanes = this.getLanes(pool, true); |
|
if (childLanes.all(function(lane){ return lanes.include(lane)})){ |
|
unselectLanes = unselectLanes.concat(childLanes); |
|
return true; |
|
} else if (selection.include(pool) && childLanes.any(function(lane){ return lanes.include(lane)})) { |
|
unselectLanes = unselectLanes.concat(childLanes); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
}.bind(this)) |
|
|
|
if (unselectLanes.length > 0 && pools.length > 0){ |
|
selection = selection.without.apply(selection, unselectLanes); |
|
selection = selection.concat(pools); |
|
this.facade.setSelection(selection.uniq()); |
|
} |
|
} |
|
}, |
|
|
|
handleShapeRemove: function(option) { |
|
|
|
var sh = option.shape; |
|
var parent = option.parent; |
|
|
|
if (sh instanceof ORYX.Core.Node && sh.getStencil().idWithoutNs() === "Lane" && this.facade.isExecutingCommands()) { |
|
|
|
var pool = this.getParentPool(parent); |
|
if (pool&&pool.parent){ |
|
|
|
var isLeafFn = function(leaf){ |
|
return !leaf.getChildNodes().any(function(r){ return r.getStencil().idWithoutNs() === "Lane"}); |
|
} |
|
|
|
var isLeaf = isLeafFn(sh); |
|
var parentHasMoreLanes = parent.getChildNodes().any(function(r){ return r.getStencil().idWithoutNs() === "Lane"}); |
|
|
|
if (isLeaf && parentHasMoreLanes){ |
|
|
|
var command = new ResizeLanesCommand(sh, parent, pool, this); |
|
this.facade.executeCommands([command]); |
|
|
|
} else if( !isLeaf && |
|
!this.facade.getSelection().any(function(select){ // Find one of the selection, which is a lane and child of "sh" and is a leaf lane |
|
return select instanceof ORYX.Core.Node && select.getStencil().idWithoutNs() === "Lane" && |
|
select.isParent(sh) && isLeafFn(select);})) { |
|
|
|
var Command = ORYX.Core.Command.extend({ |
|
construct: function(shape, facade) { |
|
this.children = shape.getChildNodes(true); |
|
this.facade = facade; |
|
}, |
|
execute: function() { |
|
this.children.each(function(child){ |
|
child.bounds.moveBy(30,0) |
|
}); |
|
//this.facade.getCanvas().update(); |
|
}, |
|
rollback: function() { |
|
this.children.each(function(child){ |
|
child.bounds.moveBy(-30,0) |
|
}) |
|
//this.facade.getCanvas().update(); |
|
} |
|
}); |
|
this.facade.executeCommands([new Command(sh, this.facade)]); |
|
|
|
} else if (isLeaf&&!parentHasMoreLanes&&parent == pool){ |
|
parent.add(sh); |
|
} |
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
hashedSubProcesses: {}, |
|
|
|
hashChildShapes: function(shape){ |
|
var children = shape.getChildNodes(); |
|
children.each(function(child){ |
|
if (this.hashedSubProcesses[child.id]){ |
|
this.hashedSubProcesses[child.id] = child.absoluteXY(); |
|
this.hashedSubProcesses[child.id].width = child.bounds.width(); |
|
this.hashedSubProcesses[child.id].height = child.bounds.height(); |
|
this.hashChildShapes(child); |
|
} |
|
}.bind(this)); |
|
}, |
|
|
|
/** |
|
* Handle the layouting of a sub process. |
|
* Mainly to adjust the child dockers of a sub process. |
|
* |
|
*/ |
|
handleSubProcess : function(option) { |
|
|
|
var sh = option.shape; |
|
|
|
if (!this.hashedSubProcesses[sh.id]) { |
|
this.hashedSubProcesses[sh.id] = sh.absoluteXY(); |
|
this.hashedSubProcesses[sh.id].width = sh.bounds.width(); |
|
this.hashedSubProcesses[sh.id].height = sh.bounds.height(); |
|
return; |
|
} |
|
|
|
var offset = sh.absoluteXY(); |
|
offset.x -= this.hashedSubProcesses[sh.id].x; |
|
offset.y -= this.hashedSubProcesses[sh.id].y; |
|
|
|
var resized = this.hashedSubProcesses[sh.id].width !== sh.bounds.width() || this.hashedSubProcesses[sh.id].height !== sh.bounds.height(); |
|
|
|
this.hashedSubProcesses[sh.id] = sh.absoluteXY(); |
|
this.hashedSubProcesses[sh.id].width = sh.bounds.width(); |
|
this.hashedSubProcesses[sh.id].height = sh.bounds.height(); |
|
this.hashChildShapes(sh); |
|
|
|
|
|
// Move dockers only if currently is not resizing |
|
if (this.facade.isExecutingCommands()&&!resized) { |
|
this.moveChildDockers(sh, offset); |
|
} |
|
}, |
|
|
|
moveChildDockers: function(shape, offset){ |
|
|
|
if (!offset.x && !offset.y) { |
|
return; |
|
} |
|
|
|
var children = shape.getChildNodes(true); |
|
|
|
// Get all nodes |
|
var dockers = children |
|
// Get all incoming and outgoing edges |
|
.map(function(node){ |
|
return [].concat(node.getIncomingShapes()) |
|
.concat(node.getOutgoingShapes()) |
|
}) |
|
// Flatten all including arrays into one |
|
.flatten() |
|
// Get every edge only once |
|
.uniq() |
|
// Get all dockers |
|
.map(function(edge){ |
|
return edge.dockers.length > 2 ? |
|
edge.dockers.slice(1, edge.dockers.length-1) : |
|
[]; |
|
}) |
|
// Flatten the dockers lists |
|
.flatten(); |
|
|
|
var abs = shape.absoluteBounds(); |
|
abs.moveBy(-offset.x, -offset.y) |
|
var obj = {}; |
|
dockers.each(function(docker){ |
|
|
|
if (docker.isChanged){ |
|
return; |
|
} |
|
|
|
var off = Object.clone(offset); |
|
|
|
if (!abs.isIncluded(docker.bounds.center())){ |
|
var index = docker.parent.dockers.indexOf(docker); |
|
var size = docker.parent.dockers.length; |
|
var from = docker.parent.getSource(); |
|
var to = docker.parent.getTarget(); |
|
|
|
var bothAreIncluded = children.include(from) && children.include(to); |
|
|
|
if (!bothAreIncluded){ |
|
var previousIsOver = index !== 0 ? abs.isIncluded(docker.parent.dockers[index-1].bounds.center()) : false; |
|
var nextIsOver = index !== size-1 ? abs.isIncluded(docker.parent.dockers[index+1].bounds.center()) : false; |
|
|
|
if (!previousIsOver && !nextIsOver){ return; } |
|
|
|
var ref = docker.parent.dockers[previousIsOver ? index-1 : index+1]; |
|
if (Math.abs(-Math.abs(ref.bounds.center().x-docker.bounds.center().x)) < 2){ |
|
off.y = 0; |
|
} else if(Math.abs(-Math.abs(ref.bounds.center().y-docker.bounds.center().y)) < 2){ |
|
off.x = 0; |
|
} else { |
|
return; |
|
} |
|
} |
|
|
|
} |
|
|
|
obj[docker.getId()] = { |
|
docker:docker, |
|
offset:off |
|
} |
|
}) |
|
|
|
// Set dockers |
|
this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(obj)]); |
|
|
|
}, |
|
|
|
/** |
|
* DragDocker.Docked Handler |
|
* |
|
*/ |
|
handleDockerDocked: function(options) { |
|
var namespace = this.getNamespace(); |
|
|
|
var edge = options.parent; |
|
var edgeSource = options.target; |
|
|
|
if(edge.getStencil().id() === namespace + "SequenceFlow") { |
|
var isGateway = edgeSource.getStencil().groups().find(function(group) { |
|
if(group == "Gateways") |
|
return group; |
|
}); |
|
if(!isGateway && (edge.properties["oryx-conditiontype"] == "Expression")) |
|
// show diamond on edge source |
|
edge.setProperty("oryx-showdiamondmarker", true); |
|
else |
|
// do not show diamond on edge source |
|
edge.setProperty("oryx-showdiamondmarker", false); |
|
|
|
// update edge rendering |
|
//edge.update(); |
|
|
|
this.facade.getCanvas().update(); |
|
} |
|
}, |
|
|
|
/** |
|
* PropertyWindow.PropertyChanged Handler |
|
*/ |
|
handlePropertyChanged: function(option) { |
|
var namespace = this.getNamespace(); |
|
|
|
var shapes = option.elements; |
|
var propertyKey = option.key; |
|
var propertyValue = option.value; |
|
|
|
var changed = false; |
|
shapes.each(function(shape){ |
|
if((shape.getStencil().id() === namespace + "SequenceFlow") && |
|
(propertyKey === "oryx-conditiontype")) { |
|
|
|
if(propertyValue != "Expression") |
|
// Do not show the Diamond |
|
shape.setProperty("oryx-showdiamondmarker", false); |
|
else { |
|
var incomingShapes = shape.getIncomingShapes(); |
|
|
|
if(!incomingShapes) { |
|
shape.setProperty("oryx-showdiamondmarker", true); |
|
} |
|
|
|
var incomingGateway = incomingShapes.find(function(aShape) { |
|
var foundGateway = aShape.getStencil().groups().find(function(group) { |
|
if(group == "Gateways") |
|
return group; |
|
}); |
|
if(foundGateway) |
|
return foundGateway; |
|
}); |
|
|
|
if(!incomingGateway) |
|
// show diamond on edge source |
|
shape.setProperty("oryx-showdiamondmarker", true); |
|
else |
|
// do not show diamond |
|
shape.setProperty("oryx-showdiamondmarker", false); |
|
} |
|
|
|
changed = true; |
|
} |
|
}.bind(this)); |
|
|
|
if(changed) {this.facade.getCanvas().update();} |
|
|
|
}, |
|
|
|
hashedPoolPositions : {}, |
|
hashedLaneDepth : {}, |
|
hashedBounds : {}, |
|
hashedPositions: {}, |
|
|
|
/** |
|
* Handler for layouting event 'layout.bpmn2_0.pool' |
|
* @param {Object} event |
|
*/ |
|
handleLayoutPool: function(event){ |
|
|
|
|
|
var pool = event.shape; |
|
var selection = this.facade.getSelection(); |
|
var currentShape = selection.include(pool) ? pool : selection.first(); |
|
|
|
currentShape = currentShape || pool; |
|
|
|
this.currentPool = pool; |
|
|
|
// Check if it is a pool or a lane |
|
if (!(currentShape.getStencil().id().endsWith("Pool") || currentShape.getStencil().id().endsWith("Lane"))) { |
|
return; |
|
} |
|
|
|
// Check if the lane is within the pool and is not removed lately |
|
if (currentShape !== pool && !currentShape.isParent(pool) && !this.hashedBounds[pool.id][currentShape.id]){ |
|
return; |
|
} |
|
|
|
|
|
if (!this.hashedBounds[pool.id]) { |
|
this.hashedBounds[pool.id] = {}; |
|
} |
|
|
|
// Find all child lanes |
|
var lanes = this.getLanes(pool); |
|
|
|
if (lanes.length <= 0) { |
|
return |
|
} |
|
|
|
var allLanes = this.getLanes(pool, true), hp; |
|
var considerForDockers = allLanes.clone(); |
|
|
|
var hashedPositions = $H({}); |
|
allLanes.each(function(lane){ |
|
hashedPositions[lane.id] = lane.bounds.upperLeft(); |
|
}) |
|
|
|
|
|
|
|
// Show/hide caption regarding the number of lanes |
|
if (lanes.length === 1 && this.getLanes(lanes.first()).length <= 0) { |
|
// TRUE if there is a caption |
|
lanes.first().setProperty("oryx-showcaption", lanes.first().properties["oryx-name"].trim().length > 0); |
|
var rect = lanes.first().node.getElementsByTagName("rect"); |
|
rect[0].setAttributeNS(null, "display", "none"); |
|
} else { |
|
allLanes.invoke("setProperty", "oryx-showcaption", true); |
|
allLanes.each(function(lane){ |
|
var rect = lane.node.getElementsByTagName("rect"); |
|
rect[0].removeAttributeNS(null, "display"); |
|
}) |
|
} |
|
|
|
var deletedLanes = []; |
|
var addedLanes = []; |
|
|
|
// Get all new lanes |
|
var i=-1; |
|
while (++i<allLanes.length) { |
|
if (!this.hashedBounds[pool.id][allLanes[i].id]){ |
|
addedLanes.push(allLanes[i]) |
|
} |
|
} |
|
|
|
if (addedLanes.length > 0){ |
|
currentShape = addedLanes.first(); |
|
} |
|
|
|
|
|
// Get all deleted lanes |
|
var resourceIds = $H(this.hashedBounds[pool.id]).keys(); |
|
var i=-1; |
|
while (++i<resourceIds.length) { |
|
if (!allLanes.any(function(lane){ return lane.id == resourceIds[i]})){ |
|
deletedLanes.push(this.hashedBounds[pool.id][resourceIds[i]]); |
|
selection = selection.without(function(r){ return r.id == resourceIds[i] }); |
|
} |
|
} |
|
|
|
var height, width, x, y; |
|
|
|
if (deletedLanes.length > 0 || addedLanes.length > 0) { |
|
|
|
if (addedLanes.length === 1 && this.getLanes(addedLanes[0].parent).length === 1){ |
|
// Set height from the pool |
|
height = this.adjustHeight(lanes, addedLanes[0].parent); |
|
} else { |
|
// Set height from the pool |
|
height = this.updateHeight(pool); |
|
} |
|
// Set width from the pool |
|
width = this.adjustWidth(lanes, pool.bounds.width()); |
|
|
|
pool.update(); |
|
} |
|
|
|
/** |
|
* Set width/height depending on the pool |
|
*/ |
|
else if (pool == currentShape) { |
|
|
|
if (selection.length === 1 && this.isResized(pool, this.hashedPoolPositions[pool.id])) { |
|
var oldXY = this.hashedPoolPositions[pool.id].upperLeft(); |
|
var xy = pool.bounds.upperLeft(); |
|
var scale = 0; |
|
if (this.shouldScale(pool)){ |
|
var old = this.hashedPoolPositions[pool.id]; |
|
scale = old.height()/pool.bounds.height(); |
|
} |
|
|
|
this.adjustLanes(pool, allLanes, oldXY.x - xy.x, oldXY.y - xy.y, scale); |
|
} |
|
|
|
// Set height from the pool |
|
height = this.adjustHeight(lanes, undefined, pool.bounds.height()); |
|
// Set width from the pool |
|
width = this.adjustWidth(lanes, pool.bounds.width()); |
|
} |
|
|
|
/**??? |
|
* Set width/height depending on containing lanes |
|
*/ |
|
else { |
|
|
|
// Reposition the pool if one shape is selected and the upperleft has changed |
|
if (selection.length === 1 && this.isResized(currentShape, this.hashedBounds[pool.id][currentShape.id])){ |
|
var oldXY = this.hashedBounds[pool.id][currentShape.id].upperLeft(); |
|
var xy = currentShape.absoluteXY(); |
|
x = oldXY.x - xy.x; |
|
y = oldXY.y - xy.y; |
|
|
|
// Adjust all other lanes beneath this lane |
|
if (x||y){ |
|
considerForDockers = considerForDockers.without(currentShape); |
|
this.adjustLanes(pool, this.getAllExcludedLanes(pool, currentShape), x, 0); |
|
} |
|
|
|
// Adjust all child lanes |
|
var childLanes = this.getLanes(currentShape, true); |
|
if (childLanes.length > 0){ |
|
if (this.shouldScale(currentShape)){ |
|
var old = this.hashedBounds[pool.id][currentShape.id]; |
|
var scale = old.height()/currentShape.bounds.height(); |
|
this.adjustLanes(pool, childLanes, x, y, scale); |
|
} else { |
|
this.adjustLanes(pool, childLanes, x, y, 0); |
|
} |
|
} |
|
} |
|
|
|
// Cache all bounds |
|
var changes = allLanes.map(function(lane){ return { |
|
shape: lane, |
|
bounds: lane.bounds.clone() |
|
} }); |
|
|
|
// Get height and adjust child heights |
|
height = this.adjustHeight(lanes, currentShape); |
|
// Check if something has changed and maybe create a command |
|
this.checkForChanges(allLanes, changes); |
|
|
|
// Set width from the current shape |
|
width = this.adjustWidth(lanes, currentShape.bounds.width()+(this.getDepth(currentShape,pool)*30)); |
|
} |
|
|
|
this.setDimensions(pool, width, height, x, y); |
|
|
|
|
|
if (this.facade.isExecutingCommands() && (deletedLanes.length === 0 || addedLanes.length !== 0)){ |
|
// Update all dockers |
|
this.updateDockers(considerForDockers, pool); |
|
|
|
// Check if the order has changed |
|
if (this.hashedPositions[pool.id] && this.hashedPositions[pool.id].keys().any(function(key, i){ |
|
return (allLanes[i]||{}).id !== key; |
|
})){ |
|
|
|
var LanesHasBeenReordered = ORYX.Core.Command.extend({ |
|
construct: function(originPosition, newPosition, lanes, plugin, poolId) { |
|
this.originPosition = Object.clone(originPosition); |
|
this.newPosition = Object.clone(newPosition); |
|
this.lanes = lanes; |
|
this.plugin = plugin; |
|
this.pool = poolId; |
|
}, |
|
execute: function(){ |
|
if (!this.executed){ |
|
this.executed = true; |
|
this.lanes.each(function(lane){ |
|
if (this.newPosition[lane.id]) |
|
lane.bounds.moveTo(this.newPosition[lane.id]) |
|
}.bind(this)); |
|
this.plugin.hashedPositions[this.pool] = Object.clone(this.newPosition); |
|
} |
|
}, |
|
rollback: function(){ |
|
this.lanes.each(function(lane){ |
|
if (this.originPosition[lane.id]) |
|
lane.bounds.moveTo(this.originPosition[lane.id]) |
|
}.bind(this)); |
|
this.plugin.hashedPositions[this.pool] = Object.clone(this.originPosition); |
|
} |
|
}); |
|
|
|
var hp2 = $H({}); |
|
allLanes.each(function(lane){ |
|
hp2[lane.id] = lane.bounds.upperLeft(); |
|
}) |
|
|
|
var command = new LanesHasBeenReordered(hashedPositions, hp2, allLanes, this, pool.id); |
|
this.facade.executeCommands([command]); |
|
|
|
} |
|
} |
|
|
|
this.hashedBounds[pool.id] = {}; |
|
this.hashedPositions[pool.id] = hashedPositions; |
|
|
|
var i=-1; |
|
while (++i < allLanes.length) { |
|
// Cache positions |
|
this.hashedBounds[pool.id][allLanes[i].id] = allLanes[i].absoluteBounds(); |
|
|
|
// Cache also the bounds of child shapes, mainly for child subprocesses |
|
this.hashChildShapes(allLanes[i]); |
|
|
|
this.hashedLaneDepth[allLanes[i].id] = this.getDepth(allLanes[i], pool); |
|
|
|
this.forceToUpdateLane(allLanes[i]); |
|
} |
|
|
|
this.hashedPoolPositions[pool.id] = pool.bounds.clone(); |
|
|
|
|
|
// Update selection |
|
//this.facade.setSelection(selection); |
|
}, |
|
|
|
shouldScale: function(element){ |
|
var childLanes = element.getChildNodes().findAll(function(shape){ return shape.getStencil().id().endsWith("Lane") }) |
|
return childLanes.length > 1 || childLanes.any(function(lane){ return this.shouldScale(lane) }.bind(this)) |
|
}, |
|
|
|
/** |
|
* Lookup if some bounds has changed |
|
* @param {Object} lanes |
|
* @param {Object} changes |
|
*/ |
|
checkForChanges: function(lanes, changes){ |
|
// Check if something has changed |
|
if (this.facade.isExecutingCommands() && changes.any(function(change){ |
|
return change.shape.bounds.toString() !== change.bounds.toString(); |
|
})){ |
|
|
|
var Command = ORYX.Core.Command.extend({ |
|
construct: function(changes) { |
|
this.oldState = changes; |
|
this.newState = changes.map(function(s){ return {shape:s.shape, bounds:s.bounds.clone()}}); |
|
}, |
|
execute: function(){ |
|
if (this.executed){ |
|
this.applyState(this.newState); |
|
} |
|
this.executed = true; |
|
}, |
|
rollback: function(){ |
|
this.applyState(this.oldState); |
|
}, |
|
applyState: function(state){ |
|
state.each(function(s){ |
|
s.shape.bounds.set(s.bounds.upperLeft(), s.bounds.lowerRight()); |
|
}) |
|
} |
|
}); |
|
|
|
this.facade.executeCommands([new Command(changes)]); |
|
} |
|
}, |
|
|
|
isResized: function(shape, bounds){ |
|
|
|
if (!bounds||!shape){ |
|
return false; |
|
} |
|
|
|
var oldB = bounds; |
|
//var oldXY = oldB.upperLeft(); |
|
//var xy = shape.absoluteXY(); |
|
return Math.round(oldB.width() - shape.bounds.width()) !== 0 || Math.round(oldB.height() - shape.bounds.height()) !== 0 |
|
}, |
|
|
|
adjustLanes: function(pool, lanes, x, y, scale){ |
|
|
|
scale = scale || 0; |
|
|
|
// For every lane, adjust the child nodes with the offset |
|
lanes.each(function(l){ |
|
l.getChildNodes().each(function(child){ |
|
if (!child.getStencil().id().endsWith("Lane")){ |
|
var cy = scale ? child.bounds.center().y - (child.bounds.center().y/scale) : -y; |
|
child.bounds.moveBy((x||0), -cy); |
|
|
|
if (scale&&child.getStencil().id().endsWith("Subprocess")) { |
|
this.moveChildDockers(child, {x:(0), y:-cy}); |
|
} |
|
|
|
} |
|
}.bind(this)); |
|
this.hashedBounds[pool.id][l.id].moveBy(-(x||0), !scale?-y:0); |
|
if (scale) { |
|
l.isScaled = true; |
|
} |
|
}.bind(this)) |
|
|
|
}, |
|
|
|
getAllExcludedLanes: function(parent, lane){ |
|
var lanes = []; |
|
parent.getChildNodes().each(function(shape){ |
|
if ((!lane || shape !== lane) && shape.getStencil().id().endsWith("Lane")){ |
|
lanes.push(shape); |
|
lanes = lanes.concat(this.getAllExcludedLanes(shape, lane)); |
|
} |
|
}.bind(this)); |
|
return lanes; |
|
}, |
|
|
|
|
|
forceToUpdateLane: function(lane){ |
|
|
|
if (lane.bounds.height() !== lane._svgShapes[0].height) { |
|
lane.isChanged = true; |
|
lane.isResized = true; |
|
lane._update(); |
|
} |
|
}, |
|
|
|
getDepth: function(child, parent){ |
|
|
|
var i=0; |
|
while(child && child.parent && child !== parent){ |
|
child = child.parent; |
|
++i |
|
} |
|
return i; |
|
}, |
|
|
|
updateDepth: function(lane, fromDepth, toDepth){ |
|
|
|
var xOffset = (fromDepth - toDepth) * 30; |
|
|
|
lane.getChildNodes().each(function(shape){ |
|
shape.bounds.moveBy(xOffset, 0); |
|
|
|
[].concat(children[j].getIncomingShapes()) |
|
.concat(children[j].getOutgoingShapes()) |
|
|
|
}) |
|
|
|
}, |
|
|
|
setDimensions: function(shape, width, height, x, y){ |
|
var isLane = shape.getStencil().id().endsWith("Lane"); |
|
// Set the bounds |
|
shape.bounds.set( |
|
isLane ? 30 : (shape.bounds.a.x - (x || 0)), |
|
isLane ? shape.bounds.a.y : (shape.bounds.a.y - (y || 0)), |
|
width ? shape.bounds.a.x + width - (isLane?30:(x||0)) : shape.bounds.b.x, |
|
height ? shape.bounds.a.y + height - (isLane?0:(y||0)) : shape.bounds.b.y |
|
); |
|
}, |
|
|
|
setLanePosition: function(shape, y){ |
|
|
|
shape.bounds.moveTo(30, y); |
|
|
|
}, |
|
|
|
adjustWidth: function(lanes, width) { |
|
|
|
// Set width to each lane |
|
(lanes||[]).each(function(lane){ |
|
this.setDimensions(lane, width); |
|
this.adjustWidth(this.getLanes(lane), width-30); |
|
}.bind(this)); |
|
|
|
return width; |
|
}, |
|
|
|
|
|
adjustHeight: function(lanes, changedLane, propagateHeight){ |
|
|
|
var oldHeight = 0; |
|
if (!changedLane && propagateHeight){ |
|
var i=-1; |
|
while (++i<lanes.length){ |
|
oldHeight += lanes[i].bounds.height(); |
|
} |
|
} |
|
|
|
var i=-1; |
|
var height = 0; |
|
|
|
// Iterate trough every lane |
|
while (++i<lanes.length){ |
|
|
|
if (lanes[i] === changedLane) { |
|
// Propagate new height down to the children |
|
this.adjustHeight(this.getLanes(lanes[i]), undefined, lanes[i].bounds.height()); |
|
|
|
lanes[i].bounds.set({x:30, y:height}, {x:lanes[i].bounds.width()+30, y:lanes[i].bounds.height()+height}) |
|
|
|
} else if (!changedLane && propagateHeight) { |
|
|
|
var tempHeight = (lanes[i].bounds.height() * propagateHeight) / oldHeight; |
|
// Propagate height |
|
this.adjustHeight(this.getLanes(lanes[i]), undefined, tempHeight); |
|
// Set height propotional to the propagated and old height |
|
this.setDimensions(lanes[i], null, tempHeight); |
|
this.setLanePosition(lanes[i], height); |
|
} else { |
|
// Get height from children |
|
var tempHeight = this.adjustHeight(this.getLanes(lanes[i]), changedLane, propagateHeight); |
|
if (!tempHeight) { |
|
tempHeight = lanes[i].bounds.height(); |
|
} |
|
this.setDimensions(lanes[i], null, tempHeight); |
|
this.setLanePosition(lanes[i], height); |
|
} |
|
|
|
height += lanes[i].bounds.height(); |
|
} |
|
|
|
return height; |
|
|
|
}, |
|
|
|
|
|
updateHeight: function(root){ |
|
|
|
var lanes = this.getLanes(root); |
|
|
|
if (lanes.length == 0){ |
|
return root.bounds.height(); |
|
} |
|
|
|
var height = 0; |
|
var i=-1; |
|
while (++i < lanes.length) { |
|
this.setLanePosition(lanes[i], height); |
|
height += this.updateHeight(lanes[i]); |
|
} |
|
|
|
this.setDimensions(root, null, height); |
|
|
|
return height; |
|
}, |
|
|
|
getOffset: function(lane, includePool, pool){ |
|
|
|
var offset = {x:0,y:0}; |
|
|
|
|
|
/*var parent = lane; |
|
while(parent) { |
|
|
|
|
|
var offParent = this.hashedBounds[pool.id][parent.id] ||(includePool === true ? this.hashedPoolPositions[parent.id] : undefined); |
|
if (offParent){ |
|
var ul = parent.bounds.upperLeft(); |
|
var ulo = offParent.upperLeft(); |
|
offset.x += ul.x-ulo.x; |
|
offset.y += ul.y-ulo.y; |
|
} |
|
|
|
if (parent.getStencil().id().endsWith("Pool")) { |
|
break; |
|
} |
|
|
|
parent = parent.parent; |
|
} */ |
|
|
|
var offset = lane.absoluteXY(); |
|
|
|
var hashed = this.hashedBounds[pool.id][lane.id] ||(includePool === true ? this.hashedPoolPositions[lane.id] : undefined); |
|
if (hashed) { |
|
offset.x -= hashed.upperLeft().x; |
|
offset.y -= hashed.upperLeft().y; |
|
} else { |
|
return {x:0,y:0} |
|
} |
|
return offset; |
|
}, |
|
|
|
getNextLane: function(shape){ |
|
while(shape && !shape.getStencil().id().endsWith("Lane")){ |
|
if (shape instanceof ORYX.Core.Canvas) { |
|
return null; |
|
} |
|
shape = shape.parent; |
|
} |
|
return shape; |
|
}, |
|
|
|
getParentPool: function(shape){ |
|
while(shape && !shape.getStencil().id().endsWith("Pool")){ |
|
if (shape instanceof ORYX.Core.Canvas) { |
|
return null; |
|
} |
|
shape = shape.parent; |
|
} |
|
return shape; |
|
}, |
|
|
|
updateDockers: function(lanes, pool){ |
|
|
|
var absPool = pool.absoluteBounds(), movedShapes = []; |
|
var oldPool = (this.hashedPoolPositions[pool.id]||absPool).clone(); |
|
|
|
var i=-1, j=-1, k=-1, l=-1, docker; |
|
var dockers = {}; |
|
|
|
while (++i < lanes.length) { |
|
|
|
if (!this.hashedBounds[pool.id][lanes[i].id]) { |
|
continue; |
|
} |
|
|
|
var isScaled = lanes[i].isScaled; |
|
delete lanes[i].isScaled; |
|
var children = lanes[i].getChildNodes(); |
|
var absBounds = lanes[i].absoluteBounds(); |
|
var oldBounds = (this.hashedBounds[pool.id][lanes[i].id]||absBounds); |
|
//oldBounds.moveBy((absBounds.upperLeft().x-lanes[i].bounds.upperLeft().x), (absBounds.upperLeft().y-lanes[i].bounds.upperLeft().y)); |
|
var offset = this.getOffset(lanes[i], true, pool); |
|
var xOffsetDepth = 0; |
|
|
|
var depth = this.getDepth(lanes[i], pool); |
|
if ( this.hashedLaneDepth[lanes[i].id] !== undefined && this.hashedLaneDepth[lanes[i].id] !== depth) { |
|
xOffsetDepth = (this.hashedLaneDepth[lanes[i].id] - depth) * 30; |
|
offset.x += xOffsetDepth; |
|
} |
|
|
|
j=-1; |
|
|
|
while (++j < children.length) { |
|
|
|
if (xOffsetDepth && !children[j].getStencil().id().endsWith("Lane")) { |
|
movedShapes.push({xOffset:xOffsetDepth, shape: children[j]}); |
|
children[j].bounds.moveBy(xOffsetDepth, 0); |
|
} |
|
|
|
if (children[j].getStencil().id().endsWith("Subprocess")) { |
|
this.moveChildDockers(children[j], offset); |
|
} |
|
|
|
var edges = [].concat(children[j].getIncomingShapes()) |
|
.concat(children[j].getOutgoingShapes()) |
|
// Remove all edges which are included in the selection from the list |
|
.findAll(function(r){ return r instanceof ORYX.Core.Edge }) |
|
|
|
k=-1; |
|
while (++k < edges.length) { |
|
|
|
if (edges[k].getStencil().id().endsWith("MessageFlow")) { |
|
this.layoutEdges(children[j], [edges[k]], offset); |
|
continue; |
|
} |
|
|
|
l=-1; |
|
while (++l < edges[k].dockers.length) { |
|
|
|
docker = edges[k].dockers[l]; |
|
|
|
if (docker.getDockedShape()||docker.isChanged){ |
|
continue; |
|
} |
|
|
|
|
|
pos = docker.bounds.center(); |
|
|
|
// Check if the modified center included the new position |
|
var isOverLane = oldBounds.isIncluded(pos); |
|
// Check if the original center is over the pool |
|
var isOutSidePool = !oldPool.isIncluded(pos); |
|
var previousIsOverLane = l == 0 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l-1].bounds.center()); |
|
var nextIsOverLane = l == edges[k].dockers.length-1 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l+1].bounds.center()); |
|
var off = Object.clone(offset); |
|
|
|
// If the |
|
if (isScaled && isOverLane && this.isResized(lanes[i], this.hashedBounds[pool.id][lanes[i].id])){ |
|
var relY = (pos.y - absBounds.upperLeft().y + off.y); |
|
off.y -= (relY - (relY * (absBounds.height()/oldBounds.height()))); |
|
} |
|
|
|
// Check if the previous dockers docked shape is from this lane |
|
// Otherwise, check if the docker is over the lane OR is outside the lane |
|
// but the previous/next was over this lane |
|
if (isOverLane){ |
|
dockers[docker.id] = {docker: docker, offset:off}; |
|
} |
|
/*else if (l == 1 && edges[k].dockers.length>2 && edges[k].dockers[l-1].isDocked()){ |
|
var dockedLane = this.getNextLane(edges[k].dockers[l-1].getDockedShape()); |
|
if (dockedLane != lanes[i]) |
|
continue; |
|
dockers[docker.id] = {docker: docker, offset:offset}; |
|
} |
|
// Check if the next dockers docked shape is from this lane |
|
else if (l == edges[k].dockers.length-2 && edges[k].dockers.length>2 && edges[k].dockers[l+1].isDocked()){ |
|
var dockedLane = this.getNextLane(edges[k].dockers[l+1].getDockedShape()); |
|
if (dockedLane != lanes[i]) |
|
continue; |
|
dockers[docker.id] = {docker: docker, offset:offset}; |
|
} |
|
|
|
else if (isOutSidePool) { |
|
dockers[docker.id] = {docker: docker, offset:this.getOffset(lanes[i], true, pool)}; |
|
}*/ |
|
|
|
|
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
// Move the moved children |
|
var MoveChildCommand = ORYX.Core.Command.extend({ |
|
construct: function(state){ |
|
this.state = state; |
|
}, |
|
execute: function(){ |
|
if (this.executed){ |
|
this.state.each(function(s){ |
|
s.shape.bounds.moveBy(s.xOffset, 0); |
|
}); |
|
} |
|
this.executed = true; |
|
}, |
|
rollback: function(){ |
|
this.state.each(function(s){ |
|
s.shape.bounds.moveBy(-s.xOffset, 0); |
|
}); |
|
} |
|
}) |
|
|
|
|
|
// Set dockers |
|
this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(dockers), new MoveChildCommand(movedShapes)]); |
|
|
|
}, |
|
|
|
moveBy: function(pos, offset){ |
|
pos.x += offset.x; |
|
pos.y += offset.y; |
|
return pos; |
|
}, |
|
|
|
getHashedBounds: function(shape){ |
|
return this.currentPool && this.hashedBounds[this.currentPool.id][shape.id] ? this.hashedBounds[this.currentPool.id][shape.id] : shape.absoluteBounds(); |
|
}, |
|
|
|
/** |
|
* Returns a set on all child lanes for the given Shape. If recursive is TRUE, also indirect children will be returned (default is FALSE) |
|
* The set is sorted with first child the lowest y-coordinate and the last one the highest. |
|
* @param {ORYX.Core.Shape} shape |
|
* @param {boolean} recursive |
|
*/ |
|
getLanes: function(shape, recursive){ |
|
var namespace = this.getNamespace(); |
|
|
|
// Get all the child lanes |
|
var lanes = shape.getChildNodes(recursive||false).findAll(function(node) { return (node.getStencil().id() === namespace + "Lane"); }); |
|
|
|
// Sort all lanes by there y coordinate |
|
lanes = lanes.sort(function(a, b){ |
|
|
|
// Get y coordinates for upper left and lower right |
|
var auy = Math.round(a.bounds.upperLeft().y); |
|
var buy = Math.round(b.bounds.upperLeft().y); |
|
var aly = Math.round(a.bounds.lowerRight().y); |
|
var bly = Math.round(b.bounds.lowerRight().y); |
|
|
|
var ha = this.getHashedBounds(a); |
|
var hb = this.getHashedBounds(b); |
|
|
|
// Get the old y coordinates |
|
var oauy = Math.round(ha.upperLeft().y); |
|
var obuy = Math.round(hb.upperLeft().y); |
|
var oaly = Math.round(ha.lowerRight().y); |
|
var obly = Math.round(hb.lowerRight().y); |
|
|
|
// If equal, than use the old one |
|
if (auy == buy && aly == bly) { |
|
auy = oauy; buy = obuy; aly = oaly; bly = obly; |
|
} |
|
|
|
if (Math.round(a.bounds.height()-ha.height()) === 0 && Math.round(b.bounds.height()-hb.height()) === 0){ |
|
return auy < buy ? -1 : (auy > buy ? 1: 0); |
|
} |
|
|
|
// Check if upper left and lower right is completely above/below |
|
var above = auy < buy && aly < bly; |
|
var below = auy > buy && aly > bly; |
|
// Check if a is above b including the old values |
|
var slightlyAboveBottom = auy < buy && aly >= bly && oaly < obly; |
|
var slightlyAboveTop = auy >= buy && aly < bly && oauy < obuy; |
|
// Check if a is below b including the old values |
|
var slightlyBelowBottom = auy > buy && aly <= bly && oaly > obly; |
|
var slightlyBelowTop = auy <= buy && aly > bly && oauy > obuy; |
|
|
|
// Return -1 if a is above b, 1 if b is above a, or 0 otherwise |
|
return (above || slightlyAboveBottom || slightlyAboveTop ? -1 : (below || slightlyBelowBottom || slightlyBelowTop ? 1 : 0)) |
|
}.bind(this)); |
|
|
|
// Return lanes |
|
return lanes; |
|
}, |
|
|
|
getNamespace: function() { |
|
if(!this.namespace) { |
|
var stencilsets = this.facade.getStencilSets(); |
|
if(stencilsets.keys()) { |
|
this.namespace = stencilsets.keys()[0]; |
|
} else { |
|
return undefined; |
|
} |
|
} |
|
return this.namespace; |
|
} |
|
}; |
|
|
|
var ResizeLanesCommand = ORYX.Core.Command.extend({ |
|
|
|
construct: function(shape, parent, pool, plugin) { |
|
|
|
this.facade = plugin.facade; |
|
this.plugin = plugin; |
|
this.shape = shape; |
|
this.changes; |
|
|
|
this.pool = pool; |
|
|
|
this.parent = parent; |
|
|
|
|
|
this.shapeChildren = []; |
|
|
|
/* |
|
* The Bounds have to be stored |
|
* separate because they would |
|
* otherwise also be influenced |
|
*/ |
|
this.shape.getChildShapes().each(function(childShape) { |
|
this.shapeChildren.push({ |
|
shape: childShape, |
|
bounds: { |
|
a: { |
|
x: childShape.bounds.a.x, |
|
y: childShape.bounds.a.y |
|
}, |
|
b: { |
|
x: childShape.bounds.b.x, |
|
y: childShape.bounds.b.y |
|
} |
|
} |
|
}); |
|
}.bind(this)); |
|
|
|
this.shapeUpperLeft = this.shape.bounds.upperLeft(); |
|
|
|
// If there is no parent, |
|
// correct the abs position with the parents abs. |
|
/*if (!this.shape.parent) { |
|
var pAbs = parent.absoluteXY(); |
|
this.shapeUpperLeft.x += pAbs.x; |
|
this.shapeUpperLeft.y += pAbs.y; |
|
}*/ |
|
this.parentHeight = this.parent.bounds.height(); |
|
|
|
}, |
|
|
|
getLeafLanes: function(lane){ |
|
var childLanes = this.plugin.getLanes(lane).map(function(child){ |
|
return this.getLeafLanes(child); |
|
}.bind(this)).flatten(); |
|
return childLanes.length > 0 ? childLanes : [lane]; |
|
}, |
|
|
|
findNewLane: function(){ |
|
|
|
var lanes = this.plugin.getLanes(this.parent); |
|
|
|
var leafLanes = this.getLeafLanes(this.parent); |
|
/*leafLanes = leafLanes.sort(function(a,b){ |
|
var aupl = a.absoluteXY().y; |
|
var bupl = b.absoluteXY().y; |
|
return aupl < bupl ? -1 : (aupl > bupl ? 1 : 0) |
|
})*/ |
|
this.lane = leafLanes.find(function(l){ return l.bounds.upperLeft().y >= this.shapeUpperLeft.y }.bind(this)) || leafLanes.last(); |
|
this.laneUpperLeft = this.lane.bounds.upperLeft(); |
|
}, |
|
|
|
execute: function() { |
|
|
|
if(this.changes) { |
|
this.executeAgain(); |
|
return; |
|
} |
|
|
|
/* |
|
* Rescue all ChildShapes of the deleted |
|
* Shape into the lane that takes its |
|
* place |
|
*/ |
|
|
|
if (!this.lane){ |
|
this.findNewLane(); |
|
} |
|
|
|
if(this.lane) { |
|
|
|
var laUpL = this.laneUpperLeft; |
|
var shUpL = this.shapeUpperLeft; |
|
|
|
var depthChange = this.plugin.getDepth(this.lane, this.parent)-1; |
|
|
|
this.changes = $H({}); |
|
|
|
// Selected lane is BELOW the removed lane |
|
if (laUpL.y >= shUpL.y) { |
|
this.lane.getChildShapes().each(function(childShape) { |
|
|
|
/* |
|
* Cache the changes for rollback |
|
*/ |
|
if(!this.changes[childShape.getId()]) { |
|
this.changes[childShape.getId()] = this.computeChanges(childShape, this.lane, this.lane, this.shape.bounds.height()); |
|
} |
|
|
|
childShape.bounds.moveBy(0, this.shape.bounds.height()); |
|
}.bind(this)); |
|
|
|
this.plugin.hashChildShapes(this.lane); |
|
|
|
this.shapeChildren.each(function(shapeChild) { |
|
shapeChild.shape.bounds.set(shapeChild.bounds); |
|
shapeChild.shape.bounds.moveBy((shUpL.x-30)-(depthChange*30), 0); |
|
|
|
/* |
|
* Cache the changes for rollback |
|
*/ |
|
if(!this.changes[shapeChild.shape.getId()]) { |
|
this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0); |
|
} |
|
|
|
this.lane.add(shapeChild.shape); |
|
|
|
}.bind(this)); |
|
|
|
this.lane.bounds.moveBy(0, shUpL.y-laUpL.y); |
|
|
|
// Selected lane is ABOVE the removed lane |
|
} else if(shUpL.y > laUpL.y){ |
|
|
|
this.shapeChildren.each(function(shapeChild) { |
|
shapeChild.shape.bounds.set(shapeChild.bounds); |
|
shapeChild.shape.bounds.moveBy((shUpL.x-30)-(depthChange*30), this.lane.bounds.height()); |
|
|
|
/* |
|
* Cache the changes for rollback |
|
*/ |
|
if(!this.changes[shapeChild.shape.getId()]) { |
|
this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0); |
|
} |
|
|
|
this.lane.add(shapeChild.shape); |
|
|
|
}.bind(this)); |
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
/* |
|
* Adjust the height of the lanes |
|
*/ |
|
// Get the height values |
|
var oldHeight = this.lane.bounds.height(); |
|
var newHeight = this.lane.length === 1 ? this.parentHeight : this.lane.bounds.height() + this.shape.bounds.height(); |
|
|
|
// Set height |
|
this.setHeight(newHeight, oldHeight, this.parent, this.parentHeight, true); |
|
|
|
// Cache all sibling lanes |
|
//this.changes[this.shape.getId()] = this.computeChanges(this.shape, this.parent, this.parent, 0); |
|
this.plugin.getLanes(this.parent).each(function(childLane){ |
|
if(!this.changes[childLane.getId()] && childLane !== this.lane && childLane !== this.shape) { |
|
this.changes[childLane.getId()] = this.computeChanges(childLane, this.parent, this.parent, 0); |
|
} |
|
}.bind(this)) |
|
|
|
// Update |
|
this.update(); |
|
}, |
|
|
|
setHeight: function(newHeight, oldHeight, parent, parentHeight, store){ |
|
|
|
// Set heigh of the lane |
|
this.plugin.setDimensions(this.lane, this.lane.bounds.width(), newHeight); |
|
this.plugin.hashedBounds[this.pool.id][this.lane.id] = this.lane.absoluteBounds(); |
|
|
|
// Adjust child lanes |
|
this.plugin.adjustHeight(this.plugin.getLanes(parent), this.lane); |
|
|
|
if (store === true){ |
|
// Store changes |
|
this.changes[this.shape.getId()] = this.computeChanges(this.shape, parent, parent, 0, oldHeight, newHeight); |
|
} |
|
|
|
// Set parents height |
|
this.plugin.setDimensions(parent, parent.bounds.width(), parentHeight); |
|
|
|
if (parent !== this.pool){ |
|
this.plugin.setDimensions(this.pool, this.pool.bounds.width(), this.pool.bounds.height() + (newHeight-oldHeight)); |
|
} |
|
}, |
|
|
|
update: function(){ |
|
|
|
// Hack to prevent the updating of the dockers |
|
this.plugin.hashedBounds[this.pool.id]["REMOVED"] = true; |
|
// Update |
|
//this.facade.getCanvas().update(); |
|
}, |
|
|
|
rollback: function() { |
|
|
|
var laUpL = this.laneUpperLeft; |
|
var shUpL = this.shapeUpperLeft; |
|
|
|
this.changes.each(function(pair) { |
|
|
|
var parent = pair.value.oldParent; |
|
var shape = pair.value.shape; |
|
var parentHeight = pair.value.parentHeight; |
|
var oldHeight = pair.value.oldHeight; |
|
var newHeight = pair.value.newHeight; |
|
|
|
// Move siblings |
|
if (shape.getStencil().id().endsWith("Lane")){ |
|
shape.bounds.moveTo(pair.value.oldPosition); |
|
} |
|
|
|
// If lane |
|
if(oldHeight) { |
|
this.setHeight(oldHeight, newHeight, parent, parent.bounds.height() + (oldHeight - newHeight)); |
|
if (laUpL.y >= shUpL.y) { |
|
this.lane.bounds.moveBy(0, this.shape.bounds.height()-1); |
|
} |
|
} else { |
|
parent.add(shape); |
|
shape.bounds.moveTo(pair.value.oldPosition); |
|
|
|
} |
|
|
|
|
|
}.bind(this)); |
|
|
|
// Update |
|
//this.update(); |
|
|
|
}, |
|
|
|
executeAgain: function() { |
|
|
|
this.changes.each(function(pair) { |
|
var parent = pair.value.newParent; |
|
var shape = pair.value.shape; |
|
var newHeight = pair.value.newHeight; |
|
var oldHeight = pair.value.oldHeight; |
|
|
|
// If lane |
|
if(newHeight) { |
|
var laUpL = this.laneUpperLeft.y; |
|
var shUpL = this.shapeUpperLeft.y; |
|
|
|
if (laUpL >= shUpL) { |
|
this.lane.bounds.moveBy(0, shUpL - laUpL); |
|
} |
|
this.setHeight(newHeight, oldHeight, parent, parent.bounds.height() + (newHeight-oldHeight)); |
|
} else { |
|
parent.add(shape); |
|
shape.bounds.moveTo(pair.value.newPosition); |
|
} |
|
|
|
}.bind(this)); |
|
|
|
// Update |
|
this.update(); |
|
}, |
|
|
|
computeChanges: function(shape, oldParent, parent, yOffset, oldHeight, newHeight) { |
|
|
|
oldParent = this.changes[shape.getId()] ? this.changes[shape.getId()].oldParent : oldParent; |
|
var oldPosition = this.changes[shape.getId()] ? this.changes[shape.getId()].oldPosition : shape.bounds.upperLeft(); |
|
|
|
var sUl = shape.bounds.upperLeft(); |
|
|
|
var pos = {x: sUl.x, y: sUl.y + yOffset}; |
|
|
|
var changes = { |
|
shape : shape, |
|
parentHeight: oldParent.bounds.height(), |
|
oldParent : oldParent, |
|
oldPosition : oldPosition, |
|
oldHeight : oldHeight, |
|
newParent : parent, |
|
newPosition : pos, |
|
newHeight : newHeight |
|
}; |
|
|
|
return changes; |
|
} |
|
|
|
}); |
|
|
|
|
|
ORYX.Plugins.BPMN2_0 = ORYX.Plugins.AbstractPlugin.extend(ORYX.Plugins.BPMN2_0); |
|
|
|
}()
|
|
|