/** * @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.; 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 = '
' + '' + razielType + '
'; 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 += '' } 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) { // 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('') )); 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 : '
'; 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_MOUSEWHEEL = "mousewheel"; 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"; ORYX.CONFIG.EVENT_SHOW_DUPLICATE_SHAPE = "extends.showDuplicateShape"; ORYX.CONFIG.EVENT_VALIDATE_SHAPE = "extends.validateShape"; /* 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) { 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(/ 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"; new Ajax.Request('editor-app/editor/stencilset.json?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('')); 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; }, getLoadedPluginByName: function (name) { return this.loadedPlugins.find(function (plugin) { return plugin.type === name; }); }, 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), getLoadedPluginByName: this.getLoadedPluginByName.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 [] 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); }); var serObj = JSON.parse(serJsonObject); if (serObj.childShapes && typeof serObj.childShapes == 'string') { serObj.childShapes = JSON.parse(serObj.childShapes); } if (serObj.ssextensions && typeof serObj.ssextensions == 'string') { serObj.ssextensions = JSON.parse(serObj.ssextensions); } return serObj; }, /** * 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 || ss_extension_namespaces.length == 0) 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(); 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]; //" 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 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("" + 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", "" ); */ 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(""); 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(""); 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 = '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + serializedDOM + ''; 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 = {}; var htPlugins = this.facade.getLoadedPluginByName("ORYX.Plugins.HtPlugins"); // Iterate over all shapes canvas.eachChild(function (shape, parent) { if(shape.stencil && shape.stencil.id && shape.properties){ // 根据当前元素的类型来获取该类型的最大数量 var max = htPlugins.getMaxNumberOfType(shape.stencil.id); if(shape.properties.overrideid){ // 修正复制出来的元素的id shape.properties.overrideid = shape.properties.overrideid.replace(/^(.*)\d+$/, "$1"+max); } if(shape.properties.name){ // 修正复制出来的元素的name shape.properties.name = shape.properties.name.replace(/^(.*)\d+$/, "$1"+max); } } // 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.tipShrink + 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(evt); 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(evt); 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]); // 发布事件来更新属性 this.facade.raiseEvent({ type: ORYX.CONFIG.EVENT_SELECTION_CHANGED, elements: [shape], force: true, triggerByRaise: true }); } }.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 (event) { if (!event) { return { x: 0, y: 0 }; } var offset = jQuery("#canvasSection").parent().offset(); return { x: event.clientX - offset.left - 60, y: event.clientY - offset.top - 5 }; }, 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= 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.recursionChildNodes(this.facade.getCanvas().getChildNodes()); }, recursionChildNodes: function (childNodes) { childNodes.each(function (shape) { shape["isFromServer"] = true; if (shape.getStencil().id().endsWith("Pool")) { this.handleLayoutPool({ shape: shape }); } if (shape.getChildNodes().length) { this.recursionChildNodes(shape.getChildNodes()); } }.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); }()