/**
* Copyright (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* @namespace Oryx name space for different utility methods
* @name ORYX.Utils
*/
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
var ERDF = {
LITERAL: 0x01,
RESOURCE: 0x02,
DELIMITERS: ['.', '-'],
HASH: '#',
HYPHEN: "-",
schemas: [],
callback: undefined,
log: undefined,
init: function(callback) {
// init logging.
//ERDF.log = Log4js.getLogger("oryx");
//ERDF.log.setLevel(Log4js.Level.ALL);
//ERDF.log.addAppender(new ConsoleAppender(ERDF.log, false));
//if(ERDF.log.isTraceEnabled())
// ERDF.log.trace("ERDF Parser is initialized.");
// register callbacks and default schemas.
ERDF.callback = callback;
ERDF.registerSchema('schema', XMLNS.SCHEMA);
ERDF.registerSchema('rdfs', XMLNS.RDFS);
},
run: function() {
//if(ERDF.log.isTraceEnabled())
// ERDF.log.trace("ERDF Parser is running.");
// do the work.
return ERDF._checkProfile() && ERDF.parse();
},
parse: function() {
//(ERDF.log.isDebugEnabled())
// ERDF.log.debug("Begin parsing document metadata.");
// time measuring
ERDF.__startTime = new Date();
var bodies = document.getElementsByTagNameNS(XMLNS.XHTML, 'body');
var subject = {type: ERDF.RESOURCE, value: ''};
var result = ERDF._parseDocumentMetadata() &&
ERDF._parseFromTag(bodies[0], subject);
// time measuring
ERDF.__stopTime = new Date();
var duration = (ERDF.__stopTime - ERDF.__startTime)/1000.;
//alert('ERDF parsing took ' + duration + ' s.');
return result;
},
_parseDocumentMetadata: function() {
// get links from head element.
var heads = document.getElementsByTagNameNS(XMLNS.XHTML, 'head');
var links = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'link');
var metas = heads[0].getElementsByTagNameNS(XMLNS.XHTML, 'meta');
// process links first, since they could contain schema definitions.
$A(links).each(function(link) {
var properties = link.getAttribute('rel');
var reversedProperties = link.getAttribute('rev');
var value = link.getAttribute('href');
ERDF._parseTriplesFrom(
ERDF.RESOURCE, '',
properties,
ERDF.RESOURCE, value);
ERDF._parseTriplesFrom(
ERDF.RESOURCE, value,
reversedProperties,
ERDF.RESOURCE, '');
});
// continue with metas.
$A(metas).each(function(meta) {
var property = meta.getAttribute('name');
var value = meta.getAttribute('content');
ERDF._parseTriplesFrom(
ERDF.RESOURCE, '',
property,
ERDF.LITERAL, value);
});
return true;
},
_parseFromTag: function(node, subject, depth) {
// avoid parsing non-xhtml content.
if(node.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 && 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/*
* 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 += '' + node.nodeName + '>'
}
return result;
} else if (node.nodeType == node.TEXT_NODE) {
// serialize a text node.
return node.nodeValue;
}
//TODO serialize cdata areas also.
//TODO work on namespace awareness.
},
addTriple: function(triple) {
// assert the subject is a resource
if(!triple.subject.type == ERDF.LITERAL)
throw 'Cannot add the triple ' + triple.toString() +
' because the subject is not a resource.'
// get the element which represents this triple's subject.
var elementId = ERDF.__stripHashes(triple.subject.value);
var element = $(elementId);
// assert the subject is inside this document.
if(!element)
throw 'Cannot add the triple ' + triple.toString() +
' because the subject "'+elementId+'" is not in the document.';
if(triple.object.type == ERDF.LITERAL)
// object is literal
DataManager.graft(XMLNS.XHTML, element, [
'span', {'class': (triple.predicate.prefix + "-" +
triple.predicate.name)}, triple.object.value.escapeHTML()
]);
else {
// object is resource
DataManager.graft(XMLNS.XHTML, element, [
'a', {'rel': (triple.predicate.prefix + "-" +
triple.predicate.name), 'href': triple.object.value}
]);
}
return true;
},
removeTriples: function(triples) {
// alert('Removing ' +triples.length+' triples.');
// from all the triples select those ...
var removed = triples.select(
function(triple) {
// TODO remove also from triple store.
// ... that were actually removed.
return DataManager.__removeTriple(triple);
});
// sync and return removed triples.
// DataManager.__synclocal();
return removed;
},
removeTriple: function(triple) {
// remember whether the triple was actually removed.
var result = DataManager.__removeTriple(triple);
// sync and return removed triples.
// DataManager.__synclocal();
return result;
},
__removeTriple: function(triple) {
// assert the subject is a resource
if(!triple.subject.type == ERDF.LITERAL)
throw 'Cannot remove the triple ' + triple.toString() +
' because the subject is not a resource.';
// get the element which represents this triple's subject.
var elementId = ERDF.__stripHashes(triple.subject.value);
var element = $(elementId);
// assert the subject is inside this document.
if(!element)
throw 'Cannot remove the triple ' + triple.toString() +
' because the subject is not in the document.';
if(triple.object.type == ERDF.LITERAL) {
// continue searching actively for the triple.
var result = DataManager.__removeTripleRecursively(triple, element);
return result;
}
},
__removeTripleRecursively: function(triple, continueFrom) {
// return when this node is not an element node.
if(continueFrom.nodeType != continueFrom.ELEMENT_NODE)
return false;
var classes = new String(continueFrom.getAttribute('class'));
var children = $A(continueFrom.childNodes);
if(classes.include(triple.predicate.prefix + '-' + triple.predicate.name)) {
var content = continueFrom.textContent;
if( (triple.object.type == ERDF.LITERAL) &&
(triple.object.value == content))
continueFrom.parentNode.removeChild(continueFrom);
return true;
} else {
children.each(function(_node) {
DataManager.__removeTripleRecursively(triple, _node)});
return false;
}
},
/**
* graft() function
* Originally by Sean M. Burke from interglacial.com, altered for usage with
* SVG and namespace (xmlns) support. Be sure you understand xmlns before
* using this funtion, as it creates all grafted elements in the xmlns
* provided by you and all element's attribures in default xmlns. If you
* need to graft elements in a certain xmlns and wish to assign attributes
* in both that and another xmlns, you will need to do stepwise grafting,
* adding non-default attributes yourself or you'll have to enhance this
* function. Latter, I would appreciate: martin�apfelfabrik.de
* @param {Object} namespace The namespace in which
* elements should be grafted.
* @param {Object} parent The element that should contain the grafted
* structure after the function returned.
* @param {Object} t the crafting structure.
* @param {Object} doc the document in which grafting is performed.
*/
graft: function(namespace, parent, t, doc) {
doc = (doc || (parent && parent.ownerDocument) || document);
var e;
if(t === undefined) {
echo( "Can't graft an undefined value");
} else if(t.constructor == String) {
e = doc.createTextNode( t );
} else {
for(var i = 0; i < t.length; i++) {
if( i === 0 && t[i].constructor == String ) {
var snared = t[i].match( /^([a-z][a-z0-9]*)\.([^\s\.]+)$/i );
if( snared ) {
e = doc.createElementNS(namespace, snared[1]);
e.setAttributeNS(null, 'class', snared[2] );
continue;
}
snared = t[i].match( /^([a-z][a-z0-9]*)$/i );
if( snared ) {
e = doc.createElementNS(namespace, snared[1]); // but no class
continue;
}
// Otherwise:
e = doc.createElementNS(namespace, "span");
e.setAttribute(null, "class", "namelessFromLOL" );
}
if( t[i] === undefined ) {
echo("Can't graft an undefined value in a list!");
} else if( t[i].constructor == String || t[i].constructor == Array) {
this.graft(namespace, e, t[i], doc );
} else if( t[i].constructor == Number ) {
this.graft(namespace, e, t[i].toString(), doc );
} else if( t[i].constructor == Object ) {
// hash's properties => element's attributes
for(var k in t[i]) { e.setAttributeNS(null, k, t[i][k] ); }
} else if( t[i].constructor == Boolean ) {
this.graft(namespace, e, t[i] ? 'true' : 'false', doc );
} else
throw "Object " + t[i] + " is inscrutable as an graft arglet.";
}
}
if(parent) parent.appendChild(e);
return Element.extend(e); // return the topmost created node
},
setObject: function(triple) {
/**
* Erwartungen von Arvid an diese Funktion:
* - Es existiert genau ein triple mit dem Subjekt und Praedikat,
* das uebergeben wurde, und dieses haelt uebergebenes Objekt.
*/
var triples = DataManager.query(
triple.subject,
triple.predicate,
undefined
);
DataManager.removeTriples(triples);
DataManager.addTriple(triple);
return true;
},
query: function(subject, predicate, object) {
/*
* Typical triple.
* {value: subject, type: subjectType},
* {prefix: schema.prefix, name: property},
* {value: object, type: objectType});
*/
return DataManager._triples.select(function(triple) {
var select = ((subject) ?
(triple.subject.type == subject.type) &&
(triple.subject.value == subject.value) : true);
if(predicate) {
select = select && ((predicate.prefix) ?
(triple.predicate.prefix == predicate.prefix) : true);
select = select && ((predicate.name) ?
(triple.predicate.name == predicate.name) : true);
}
select = select && ((object) ?
(triple.object.type == object.type) &&
(triple.object.value == object.value) : true);
return select;
});
}
}
Kickstart.register(DataManager.init);
function assert(expr, m) { if(!expr) throw m; };
function DMCommand(action, triple) {
// store action and triple.
this.action = action;
this.triple = triple;
this.toString = function() {
return 'Command('+action+', '+triple+')';
};
}
function DMCommandHandler(nextHandler) {
/**
* Private method to set the next handler in the Chain of Responsibility
* (see http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern for
* details).
* @param {DMCommandHandler} handler The handler that is next in the chain.
*/
this.__setNext = function(handler) {
var _next = this.__next;
this.__next = nextHandler;
return _next ? _next : true;
};
this.__setNext(nextHandler);
/**
* Invokes the next handler. If there is no next handler, this method
* returns false, otherwise it forwards the result of the handling.
* @param {Object} command The command object to be processed.
*/
this.__invokeNext = function(command) {
return this.__next ? this.__next.handle(command) : false;
};
/**
* Handles a command. The abstract method process() is called with the
* command object that has been passed. If the process method catches the
* command (returns true on completion), the handle() method returns true.
* If the process() method doesn't catch the command, the next handler will
* be invoked.
* @param {Object} command The command object to be processed.
*/
this.handle = function(command) {
return this.process(command) ? true : this.__invokeNext(command);
}
/**
* Empty process() method returning false. If javascript knew abstract
* class members, this would be one.
* @param {Object} command The command object to process.
*/
this.process = function(command) { return false; };
};
/**
* This Handler manages the addition and the removal of meta elements in the
* head of the document.
* @param {DMCommandHandler} next The handler that is next in the chain.
*/
function MetaTagHandler(next) {
DMCommandHandler.apply(this, [next]);
this.process = function(command) {
with(command.triple) {
/* assert prerequisites */
if( !(
(subject instanceof ERDF.Resource) &&
(subject.isCurrentDocument()) &&
(object instanceof ERDF.Literal)
)) return false;
}
};
};
var chain = new MetaTagHandler();
var command = new DMCommand(TRIPLE_ADD, new ERDF.Triple(
new ERDF.Resource(''),
'rdf:tool',
new ERDF.Literal('')
));
/*
if(chain.handle(command))
alert('Handled!');
*/
ResourceManager = {
__corrupt: false,
__latelyCreatedResource: undefined,
__listeners: $H(),
__token: 1,
addListener: function(listener, mask) {
if(!(listener instanceof Function))
throw 'Resource event listener is not a function!';
if(!(mask))
throw 'Invalid mask for resource event listener registration.';
// construct controller and token.
var controller = {listener: listener, mask: mask};
var token = ResourceManager.__token++;
// add new listener.
ResourceManager.__listeners[token] = controller;
// return the token generated.
return token;
},
removeListener: function(token) {
// remove the listener with the token and return it.
return ResourceManager.__listners.remove(token);
},
__Event: function(action, resourceId) {
this.action = action;
this.resourceId = resourceId;
},
__dispatchEvent: function(event) {
// get all listeners. for each listener, ...
ResourceManager.__listeners.values().each(function(controller) {
// .. if listener subscribed to this type of event ...
if(event.action & controller.mask)
return controller.listener(event);
});
},
getResource: function(id) {
// get all possible resources for this.
id = ERDF.__stripHashes(id);
var resources = DataManager.query(
new ERDF.Resource('#'+id),
{prefix: 'raziel', name: 'entry'},
undefined
);
// check for consistency.
if((resources.length == 1) && (resources[0].object.isResource())) {
var entryUrl = resources[0].object.value;
return new ResourceManager.__Resource(id, entryUrl);
}
// else throw an error message.
throw ('Resource with id ' +id+ ' not recognized as such. ' +
((resources.length > 1) ?
' There is more than one raziel:entry URL.' :
' There is no raziel:entry URL.'));
return false;
},
__createResource: function(alternativeDiv) {
var collectionUrls = DataManager.query(
new ERDF.Resource(''),
// TODO This will become raziel:collection in near future.
{prefix: 'raziel', name: 'collection'},
undefined
);
// check for consistency.
if( (collectionUrls.length == 1) &&
(collectionUrls[0].object.isResource())) {
// get the collection url.
var collectionUrl = collectionUrls[0].object.value;
var resource = undefined;
// if there is an old id, serialize the dummy div from there,
// otherwise create a dummy div on the fly.
var serialization = alternativeDiv? alternativeDiv :
'';
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2010
* Signavio GmbH
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 = ORYX.CONFIG.SERVER_HANDLER_ROOT + "/editor/plugins";
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.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";/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 = 'Activiti BPM suite';
ORYX.CONFIG.WEB_URL = "../";
ORYX.CONFIG.BLANK_IMAGE = ORYX.CONFIG.LIBS_PATH + '/ext-2.0.2/resources/images/default/s.gif';
/* 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 = 400;
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 = 1485;
ORYX.CONFIG.CANVAS_HEIGHT = 1050;
ORYX.CONFIG.CANVAS_RESIZE_INTERVAL = 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 = 12;
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_MODEL_LINK = "modellink";
ORYX.CONFIG.TYPE_LISTENER = "listener";
ORYX.CONFIG.TYPE_EPC_FREQ = "epcfrequency";
ORYX.CONFIG.TYPE_GLOSSARY_LINK = "glossarylink";
/* Vertical line distance of multiline labels */
ORYX.CONFIG.LABEL_LINE_DISTANCE = 2;
ORYX.CONFIG.LABEL_DEFAULT_LINE_HEIGHT = 12;
/* Open Morph Menu with Hover */
ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER = false;
/* Editor constants come here */
ORYX.CONFIG.EDITOR_ALIGN_BOTTOM = 0x01;
ORYX.CONFIG.EDITOR_ALIGN_MIDDLE = 0x02;
ORYX.CONFIG.EDITOR_ALIGN_TOP = 0x04;
ORYX.CONFIG.EDITOR_ALIGN_LEFT = 0x08;
ORYX.CONFIG.EDITOR_ALIGN_CENTER = 0x10;
ORYX.CONFIG.EDITOR_ALIGN_RIGHT = 0x20;
ORYX.CONFIG.EDITOR_ALIGN_SIZE = 0x30;
/* Event types */
ORYX.CONFIG.EVENT_MOUSEDOWN = "mousedown";
ORYX.CONFIG.EVENT_MOUSEUP = "mouseup";
ORYX.CONFIG.EVENT_MOUSEOVER = "mouseover";
ORYX.CONFIG.EVENT_MOUSEOUT = "mouseout";
ORYX.CONFIG.EVENT_MOUSEMOVE = "mousemove";
ORYX.CONFIG.EVENT_DBLCLICK = "dblclick";
ORYX.CONFIG.EVENT_KEYDOWN = "keydown";
ORYX.CONFIG.EVENT_KEYUP = "keyup";
ORYX.CONFIG.EVENT_LOADED = "editorloaded";
ORYX.CONFIG.EVENT_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_SHOW_PROPERTYWINDOW = "propertywindow.show";
ORYX.CONFIG.EVENT_ABOUT_TO_SAVE = "file.aboutToSave";
/* Selection Shapes Highlights */
ORYX.CONFIG.SELECTION_HIGHLIGHT_SIZE = 5;
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR = "#4444FF";
ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR2 = "#9999FF";
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_CORNER = "corner";
ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE = "rectangle";
ORYX.CONFIG.SELECTION_VALID_COLOR = "#00FF00";
ORYX.CONFIG.SELECTION_INVALID_COLOR = "#FF0000";
ORYX.CONFIG.DOCKER_DOCKED_COLOR = "#00FF00";
ORYX.CONFIG.DOCKER_UNDOCKED_COLOR = "#FF0000";
ORYX.CONFIG.DOCKER_SNAP_OFFSET = 10;
/* Copy & Paste */
ORYX.CONFIG.EDIT_OFFSET_PASTE = 10;
/* Key-Codes */
ORYX.CONFIG.KEY_CODE_X = 88;
ORYX.CONFIG.KEY_CODE_C = 67;
ORYX.CONFIG.KEY_CODE_V = 86;
ORYX.CONFIG.KEY_CODE_DELETE = 46;
ORYX.CONFIG.KEY_CODE_META = 224;
ORYX.CONFIG.KEY_CODE_BACKSPACE = 8;
ORYX.CONFIG.KEY_CODE_LEFT = 37;
ORYX.CONFIG.KEY_CODE_RIGHT = 39;
ORYX.CONFIG.KEY_CODE_UP = 38;
ORYX.CONFIG.KEY_CODE_DOWN = 40;
// TODO Determine where the lowercase constants are still used and remove them from here.
ORYX.CONFIG.KEY_Code_enter = 12;
ORYX.CONFIG.KEY_Code_left = 37;
ORYX.CONFIG.KEY_Code_right = 39;
ORYX.CONFIG.KEY_Code_top = 38;
ORYX.CONFIG.KEY_Code_bottom = 40;
/* Supported Meta Keys */
ORYX.CONFIG.META_KEY_META_CTRL = "metactrl";
ORYX.CONFIG.META_KEY_ALT = "alt";
ORYX.CONFIG.META_KEY_SHIFT = "shift";
/* Key Actions */
ORYX.CONFIG.KEY_ACTION_DOWN = "down";
ORYX.CONFIG.KEY_ACTION_UP = "up";
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
function printf() {
var result = arguments[0];
for (var i=1; i= 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() {
if (ORYX.CONFIG.PREVENT_LOADINGMASK_AT_READY !== true) {
var waitingpanel = new Ext.Window({renderTo:Ext.getBody(),id:'oryx-loading-panel',bodyStyle:'padding: 8px;background:white',title:ORYX.I18N.Oryx.title,width:'auto',height:'auto',modal:true,resizable:false,closable:false,html:'' + ORYX.I18N.Oryx.pleaseWait + ''})
waitingpanel.show()
}
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.");
}
});
ORYX.Log.debug('Registering Oryx with Kickstart');
Kickstart.register(ORYX.load);
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
*
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
*
* 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 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 || !Ext.isGecko) {
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);
} 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 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.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'tspan');
var tspans = this.node.childNodes;
var fontSize = this.getFontSize(this.node);
var invalidTSpans = [];
var x = this.x, y = this.y;
if (this.position){
x = this.position.x;
y = this.position.y;
}
x = Math.floor(x); y = Math.floor(y);
var i = 0, indic = []; // Cache indices if the _positionText is called again, before update is called
var is =(this.indices || $R(0,tspans.length-1).toArray());
var length = is.length;
is.each((function(index){
if ("undefined" == typeof index){
return;
}
var tspan = tspans[i++];
if(tspan.textContent.trim() === "") {
invalidTSpans.push(tspan);
} else {
//set vertical position
var dy = 0;
switch (this._verticalAlign) {
case 'bottom':
dy = -(length - index - 1) * (fontSize);
break;
case 'middle':
dy = -(length / 2.0 - index - 1) * (fontSize);
dy -= ORYX.CONFIG.LABEL_LINE_DISTANCE / 2;
break;
case 'top':
dy = index * (fontSize);
dy += fontSize;
break;
}
tspan.setAttributeNS(null, 'dy', Math.floor(dy));
tspan.setAttributeNS(null, 'x', x);
tspan.setAttributeNS(null, 'y', y);
indic.push(index);
}
}).bind(this));
indic.length = tspans.length;
this.indices = this.indices || indic;
invalidTSpans.each(function(tspan) {
this.node.removeChild(tspan)
}.bind(this));
//set horizontal alignment
switch (this._horizontalAlign) {
case 'left':
this.node.setAttributeNS(null, 'text-anchor', 'start');
break;
case 'center':
this.node.setAttributeNS(null, 'text-anchor', 'middle');
break;
case 'right':
this.node.setAttributeNS(null, 'text-anchor', 'end');
break;
}
} catch(e) {
//console.log(e);
this._isChanged = true;
}
if(this.isVisible) {
this.node.removeAttributeNS(null, 'visibility');
}
// Finished
delete this._isUpdating;
// Raise change event
(this.changeCallbacks||[]).each(function(fn){
fn.apply(fn);
})
},
/**
* Returns the text length of the text content of an SVG tspan element.
* For all browsers but Firefox 3 the values are estimated.
* @param {TSpanSVGElement} tspan
* @param {int} startIndex Optional, for sub strings
* @param {int} endIndex Optional, for sub strings
*/
_getRenderedTextLength: function(tspan, startIndex, endIndex, fontSize) {
//if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) && new Number(RegExp.$1) >= 3) {
if(startIndex === undefined) {
//test string: abcdefghijklmnopqrstuvwxyzöäü,.-#+ 1234567890ßABCDEFGHIJKLMNOPQRSTUVWXYZ;:_'*ÜÄÖ!"§$%&/()=?[]{}|<>'~´`\^°µ@€²³
// for(var i = 0; i < tspan.textContent.length; i++) {
// console.log(tspan.textContent.charAt(i), tspan.getSubStringLength(i,1), this._estimateCharacterWidth(tspan.textContent.charAt(i))*(fontSize/14.0));
// }
return tspan.getComputedTextLength();
} else {
return tspan.getSubStringLength(startIndex, endIndex);
}
/*} else {
if(startIndex === undefined) {
return this._estimateTextWidth(tspan.textContent, fontSize);
} else {
return this._estimateTextWidth(tspan.textContent.substr(startIndex, endIndex).trim(), fontSize);
}
}*/
},
/**
* Estimates the text width for a string.
* Used for word wrapping in all browser but FF3.
* @param {Object} text
*/
_estimateTextWidth: function(text, fontSize) {
var sum = 0.0;
for(var i = 0; i < text.length; i++) {
sum += this._estimateCharacterWidth(text.charAt(i));
}
return sum*(fontSize/14.0);
},
/**
* Estimates the width of a single character for font size 14.
* Used for word wrapping in all browser but FF3.
* @param {Object} character
*/
_estimateCharacterWidth: function(character) {
for(var i = 0; i < this._characterSets.length; i++) {
if(this._characterSets[i].indexOf(character) >= 0) {
return this._characterSetValues[i];
}
}
return 9;
},
getReferencedElementWidth: function() {
var refNode = this.node.ownerDocument.getElementById(this.fitToElemId);
if(refNode) {
var refbb = refNode.getBBox();
if(refbb) {
return (this._rotate != 0
&& this._rotate % 180 != 0
&& this._rotate % 90 == 0 ?
refbb.height : refbb.width);
}
}
return undefined;
},
/**
* If no parameter is provided, this method returns the current text.
* @param text {String} Optional. Replaces the old text with this one.
*/
text: function() {
switch (arguments.length) {
case 0:
return this._text
break;
case 1:
var oldText = this._text;
if(arguments[0]) {
this._text = arguments[0].toString();
} 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;
}
},
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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(Prototype.Browser.WebKit) {
var req = new XMLHttpRequest;
req.open("GET", url, false);
req.overrideMimeType('text/xml');
req.send(null);
req.onload = (function() { _loadSVGOnSuccess(req.responseXML); }).bind(this);
// else just do it.
} else
*/
if(this._jsonStencil.view.trim().match(/)) {
var parser = new DOMParser();
var xml = parser.parseFromString( this._jsonStencil.view ,"text/xml");
//check if result is a SVG document
if( ORYX.Editor.checkClassType( xml.documentElement, SVGSVGElement )) {
this._view = xml.documentElement;
//updating link to images
var imageElems = this._view.getElementsByTagNameNS("http://www.w3.org/2000/svg", "image");
$A(imageElems).each((function(imageElem) {
var link = imageElem.getAttributeNodeNS("http://www.w3.org/1999/xlink", "href");
if(link && link.value.indexOf("://") == -1) {
link.textContent = this._source + "view/" + link.value;
}
}).bind(this));
} else {
throw "ORYX.Core.StencilSet.Stencil(_loadSVGOnSuccess): The response is not a SVG document."
}
} else {
new Ajax.Request(
url, {
asynchronous:false, method:'get',
onSuccess:this._loadSVGOnSuccess.bind(this),
onFailure:this._loadSVGOnFailure.bind(this)
});
}
},
postProcessProperties: function() {
// add image path to icon
if(this._jsonStencil.icon && this._jsonStencil.icon.indexOf("://") === -1) {
this._jsonStencil.icon = this._source + "icons/" + this._jsonStencil.icon;
} else {
this._jsonStencil.icon = "";
}
// init property packages
if(this._jsonStencil.propertyPackages && this._jsonStencil.propertyPackages instanceof Array) {
this._jsonStencil.propertyPackages.each((function(ppId) {
var pp = this._propertyPackages[ppId];
if(pp) {
pp.each((function(prop){
var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this);
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp;
}).bind(this));
}
}).bind(this));
}
// init properties
if(this._jsonStencil.properties && this._jsonStencil.properties instanceof Array) {
this._jsonStencil.properties.each((function(prop) {
var oProp = new ORYX.Core.StencilSet.Property(prop, this._namespace, this);
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp;
}).bind(this));
}
},
/**
* @param {ORYX.Core.StencilSet.Stencil} stencil
* @return {Boolean} True, if stencil has the same namespace and type.
*/
equals: function(stencil) {
return (this.id() === stencil.id());
},
stencilSet: function() {
return this._stencilSet;
},
type: function() {
return this._jsonStencil.type;
},
namespace: function() {
return this._namespace;
},
id: function() {
return this._jsonStencil.id;
},
idWithoutNs: function(){
return this.id().replace(this.namespace(),"");
},
title: function() {
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "title");
},
description: function() {
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "description");
},
groups: function() {
return ORYX.Core.StencilSet.getTranslation(this._jsonStencil, "groups");
},
position: function() {
return (isNaN(this._jsonStencil.position) ? 0 : this._jsonStencil.position);
},
view: function() {
return this._view.cloneNode(true) || this._view;
},
icon: function() {
return this._jsonStencil.icon;
},
fixedAspectRatio: function() {
return this._jsonStencil.fixedAspectRatio === true;
},
hasMultipleRepositoryEntries: function() {
return (this.getRepositoryEntries().length > 0);
},
getRepositoryEntries: function() {
return (this._jsonStencil.repositoryEntries) ?
$A(this._jsonStencil.repositoryEntries) : $A([]);
},
properties: function() {
return this._properties.values();
},
property: function(id) {
return this._properties[id];
},
roles: function() {
return this._jsonStencil.roles;
},
defaultAlign: function() {
if(!this._jsonStencil.defaultAlign)
return "east";
return this._jsonStencil.defaultAlign;
},
serialize: function(shape, data) {
return this._jsonStencil.serialize;
//return this._jsonStencil.serialize(shape, data);
},
deserialize: function(shape, data) {
return this._jsonStencil.deserialize;
//return this._jsonStencil.deserialize(shape, data);
},
// in which case is targetShape used?
// layout: function(shape, targetShape) {
// return this._jsonStencil.layout(shape, targetShape);
// },
// layout property to store events for layouting in plugins
layout: function(shape) {
return this._jsonStencil.layout
},
addProperty: function(property, namespace) {
if(property && namespace) {
var oProp = new ORYX.Core.StencilSet.Property(property, namespace, this);
this._properties[oProp.prefix() + "-" + oProp.id()] = oProp;
}
},
removeProperty: function(propertyId) {
if(propertyId) {
var oProp = this._properties.values().find(function(prop) {
return (propertyId == prop.id());
});
if(oProp)
delete this._properties[oProp.prefix() + "-" + oProp.id()];
}
},
_loadSVGOnSuccess: function(result) {
var xml = null;
/*
* We want to get a dom object for the requested file. Unfortunately,
* safari has some issues here. this is meant as a fallback for all
* browsers that don't recognize the svg mimetype as XML but support
* data: urls on Ajax calls.
*/
// responseXML != undefined.
// if(!(result.responseXML))
// get the dom by data: url.
// xml = _evenMoreEvilHack(result.responseText, 'text/xml');
// else
// get it the usual way.
xml = result.responseXML;
//check if result is a SVG document
if( ORYX.Editor.checkClassType( xml.documentElement, SVGSVGElement )) {
this._view = xml.documentElement;
//updating link to images
var imageElems = this._view.getElementsByTagNameNS("http://www.w3.org/2000/svg", "image");
$A(imageElems).each((function(imageElem) {
var link = imageElem.getAttributeNodeNS("http://www.w3.org/1999/xlink", "href");
if(link && link.value.indexOf("://") == -1) {
link.textContent = this._source + "view/" + link.value;
}
}).bind(this));
} 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 = {};
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));
}
else {
throw "ORYX.Core.StencilSet.Property(construct): No complex property items defined."
}
}
// 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;
}
});
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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){
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("../service/editor/stencilset", {
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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, editorId) {
var stencilSet = ORYX.Core.StencilSet._stencilSetsByUrl[url];
if(!stencilSet) {
//load stencil set
stencilSet = new ORYX.Core.StencilSet.StencilSet(url);
//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 (c) 2006
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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() {
return {x:this.a.x, y:this.a.y};
},
/**
* Returns the lower Right left corner's point regardless of the
* bound delimiter points.
*/
lowerRight: function() {
return {x:this.b.x, y:this.b.y};
},
/**
* @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() {
return {
x: (this.a.x + this.b.x)/2.0,
y: (this.a.y + this.b.y)/2.0
};
},
/**
* @return {Point} The center point of this bounds relative to upperLeft.
*/
midPoint: function() {
return {
x: (this.b.x - this.a.x)/2.0,
y: (this.b.y - this.a.y)/2.0
};
},
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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();
return {x: pXY.x + this.bounds.upperLeft().x , y: pXY.y + this.bounds.upperLeft().y};
} else {
return {x: this.bounds.upperLeft().x , y: this.bounds.upperLeft().y};
}
},
/**
* @return {Point} The absolute position from the Center of this UIObject.
*/
absoluteCenterXY: function() {
if(this.parent) {
var pXY = this.parent.absoluteXY();
return {x: pXY.x + this.bounds.center().x , y: pXY.y + this.bounds.center().y};
} else {
return {x: this.bounds.center().x , y: this.bounds.center().y};
}
},
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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) {
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(Ext.type(value) === "object") value = Ext.encode(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: Ext.apply({}, 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
&& Ext.type(value) === "string"){
try {value = Ext.decode(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;
})
}
Ext.apply(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
* Ext.apply(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)){
Ext.apply(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)){
Ext.apply(shape, ORYX.Core.AbstractShape.JSONHelper);
}
allShapes = allShapes.concat(shape.getChildShapes(deep));
}, true);
}
return allShapes;
},
/**
* @return {String} Serialized JSON object
*/
serialize: function(){
return Ext.encode(this);
}
}
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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) {
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;
}
//TODO: set document resource id
this.resourceId = options.id;
this.nodes = [];
this.edges = [];
//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"}]);
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', 'black');
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;};
},
getScrollNode: function(){
return Ext.get(this.rootNode).parent("div{overflow=auto}", true);
},
focus: function(){
try {
// Get a href
if (!this.focusEl) {
this.focusEl = Ext.getBody().createChild({
tag: "a",
href: "#",
cls: "x-grid3-focus x-grid3-focus-canvas",
tabIndex: "-1"
});
this.focusEl.swallowEvent("click", true);
}
// Focus it
if (Ext.isGecko) {
this.focusEl.focus();
}
else {
this.focusEl.focus.defer(1, this.focusEl);
}
this.focusEl.blur.defer(3, this.focusEl);
} catch(e){}
},
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);
// 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 = [];
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;
},
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;
try {
var bb = this.getRootNode().childNodes[1].getBBox();
x1 = bb.x;
y1 = bb.y;
x2 = bb.x + bb.width;
y2 = bb.y + bb.height;
} catch(e) {
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 - x1;
height = y2 - y1;
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);
svgClone.childNodes[1].firstChild.setAttributeNS(null, 'transform', 'translate(' + tx + ", " + ty + ')');
//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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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() {
/* When the blank image url is not set programatically to a local
* representation, a spacer gif on the site of ext is loaded from the
* internet. This causes problems when internet or the ext site are not
* available. */
Ext.BLANK_IMAGE_URL = (ORYX.CONFIG.BLANK_IMAGE) || (ORYX.PATH + 'libs/ext-2.0.2/resources/images/default/s.gif');
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 if this is a newly created model
else if(window.location.pathname.include(ORYX.CONFIG.ORYX_NEW_URL)){
new ORYX.Editor({
id: 'oryx-canvas123',
fullscreen: true,
stencilset: {
url: "/oryx" + ORYX.Utils.getParamFromUrl("stencilset")
}
});
}
// Else fetch the model from server and display editor
else {
//HACK for distinguishing between different backends
// Backend of 2008 uses /self URL ending
var modelUrl = window.location.href.replace(/#.*/g, "");
if(modelUrl.endsWith("/self")) {
modelUrl = modelUrl.replace("/self","/json");
} else {
var modelId = window.location.search.substring(4);
modelUrl = "../service/model/" + modelId + "/json";
}
ORYX.Editor.createByUrl(modelUrl, {
id: 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;
if(config.model) {
model = config.model;
}
this.id = model.resourceId;
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;
if (Signavio&&Signavio.Helper&&Signavio.Helper.ShowMask instanceof Function) {
Signavio.Helper.ShowMask(true, !this.fullscreen ? this.id : Ext.getBody());
}
// 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(ORYX.CONFIG.STENCILSET_HANDLER + ssUrl, this.id);
} else {
var ssUrl = model.stencilset.url;
ORYX.Core.StencilSet.loadStencilSet(ssUrl, 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)
// disable key events when Ext modal window is active
ORYX.Editor.makeExtModalWindowKeysave(this._getPluginFacade());
// 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() {
if(Ext.getCmp('oryx-loading-panel')){
Ext.getCmp('oryx-loading-panel').hide()
}
// Do Layout for viewport
this.layout.doLayout();
// Generate a drop target
new Ext.dd.DropTarget(this.getCanvas().rootNode.parentNode);
// Fixed the problem that the viewport can not
// start with collapsed panels correctly
if (ORYX.CONFIG.PANEL_RIGHT_COLLAPSED === true){
this.layout_regions.east.collapse();
}
if (ORYX.CONFIG.PANEL_LEFT_COLLAPSED === true){
this.layout_regions.west.collapse();
}
// 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() {
//TODO make the height be read from eRDF data from the canvas.
// default, a non-fullscreen editor shall define its height by layout.setHeight(int)
// Defines the layout hight if it's NOT fullscreen
var layoutHeight = ORYX.CONFIG.WINDOW_HEIGHT;
var canvasParent = this.getCanvas().rootNode.parentNode;
/**
* Extend the Region implementation so that,
* the clicking area can be extend to the whole collapse area and
* an title can now be shown.
*
*/
var oldGetCollapsedEl = Ext.layout.BorderLayout.Region.prototype.getCollapsedEl;
Ext.layout.BorderLayout.Region.prototype.getCollapsedEl = function(){
oldGetCollapsedEl.apply(this, arguments);
if(this.collapseMode !== 'mini' && this.floatable === false && this.expandTriggerAll === true){
this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
this.collapsedEl.on("mouseover", this.collapsedEl.addClass.bind(this.collapsedEl, "x-layout-collapsed-over"));
this.collapsedEl.on("click", this.onExpandClick, this);
}
if (this.collapseTitle){
/* // Use CSS3 Attribute
this.collapsedEl.createChild({
cls: "x-collapse-text", html: this.collapseTitle
});*/
// Use SVG to rotate text
var svg = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.collapsedEl.dom,
['svg', {style:"position:relative;left:"+(this.position === "west" ? 4 : 6)+"px;top:"+(this.position === "west" ? 2 : 5)+"px;"},
['text', {transform:"rotate(90)", x:0, y:0, "stroke-width":"0px", fill:"#EEEEEE", style:"font-weight:bold;", "font-size":"11"}, this.collapseTitle]
]);
var text = svg.childNodes[0];
svg.setAttribute("xmlns:svg", "http://www.w3.org/2000/svg");
// Rotate the west into the other side
if (this.position === "west" && text.getComputedTextLength instanceof Function){
// Wait till rendered
window.setTimeout(function(){
var length = text.getComputedTextLength();
text.setAttributeNS(null, "transform", "rotate(-90, " + ((length / 2) + 7) + ", " + ((length / 2) - 3) + ")");;
}, 1)
}
delete this.collapseTitle;
}
return this.collapsedEl;
}
// DEFINITION OF THE VIEWPORT AREAS
this.layout_regions = {
// DEFINES TOP-AREA
north : new Ext.Panel({ //TOOO make a composite of the oryx header and addable elements (for toolbar), second one should contain margins
region : 'north',
cls : 'x-panel-editor-north',
autoEl : 'div',
border : false
}),
// DEFINES RIGHT-AREA
east : new Ext.Panel({
region : 'east',
layout : 'fit',
cls : 'x-panel-editor-east',
/*layout: 'accordion',
layoutConfig: {
// layout-specific configs go here
titleCollapse: true,
animate: true,
activeOnTop: true
},*/
autoEl : 'div',
collapseTitle : ORYX.I18N.View.East,
border :false,
cmargins: {left:0, right:0},
floatable: false,
expandTriggerAll:true,
collapsible : true,
width : ORYX.CONFIG.PANEL_RIGHT_WIDTH || 200,
split : true,
title : "East"
}),
// DEFINES BOTTOM-AREA
south : new Ext.Panel({
region : 'south',
cls : 'x-panel-editor-south',
autoEl : 'div',
border : false
}),
// DEFINES LEFT-AREA
west : new Ext.Panel({
region : 'west',
layout : 'anchor',
autoEl : 'div',
cls : 'x-panel-editor-west',
collapsible : true,
collapseTitle : ORYX.I18N.View.West,
width : ORYX.CONFIG.PANEL_LEFT_WIDTH || 200,
autoScroll:true,
cmargins: {left:0, right:0},
floatable: false,
expandTriggerAll:true,
split : true,
title : "West"
}),
// DEFINES CENTER-AREA (FOR THE EDITOR)
center : new Ext.Panel({
region : 'center',
cls : 'x-panel-editor-center',
autoScroll: true,
items : {
layout : "fit",
autoHeight: true,
el : canvasParent
}
})
}
// Hide every region except the center
for (region in this.layout_regions) {
if ( region != "center" ) {
//this.layout_regions[ region ].hide();
}
}
// Config for the Ext.Viewport
var layout_config = {
layout: 'border',
items: [
this.layout_regions.north,
this.layout_regions.east,
this.layout_regions.south,
this.layout_regions.west,
this.layout_regions.center
]
}
// IF Fullscreen, use a viewport
if (this.fullscreen) {
this.layout = new Ext.Viewport( layout_config )
// IF NOT, use a panel and render it to the given id
} else {
layout_config.renderTo = this.id;
layout_config.height = layoutHeight;
this.layout = new Ext.Panel( layout_config )
}
//Generates the ORYX-Header
//this._generateHeader();
// 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
});
},
_generateHeader: function(){
var headerPanel = new Ext.Panel({
height : 30,
autoHeight : false,
border : false,
html : "
"
});
var maActive = ORYX.MashupAPI && ORYX.MashupAPI.isUsed;
var maKey = maActive ? ORYX.MashupAPI.key : "";
var maCanRun = maActive ? ORYX.MashupAPI.canRun : false;
var maIsRemoteM = maActive ? ORYX.MashupAPI.isModelRemote : true;
var maModelImage= maIsRemoteM ? "" : "";
var maModelAuthI= maActive ? "" + maModelImage + "" : "";
// Callback if the user changes
var fn = function(val){
var publicText = ORYX.I18N.Oryx.notLoggedOn;
var user = val && val.identifier && val.identifier != "public" ? decodeURI(val.identifier.gsub('"', "")).replace(/\+/g," ") : "";
if( user.length <= 0 ){
user = publicText;
}
var content = "
";
if( headerPanel.body ){
headerPanel.body.dom.innerHTML = content;
} else {
headerPanel.html = content
}
};
ORYX.Editor.Cookie.onChange(fn);
fn(ORYX.Editor.Cookie.getParams());
// The oryx header
this.addToRegion("north", headerPanel );
},
/**
* adds a component to the specified region
*
* @param {String} region
* @param {Ext.Component} component
* @param {String} title, optional
* @return {Ext.Component} dom reference to the current region or null if specified region is unknown
*/
addToRegion: function(region, component, title) {
if (region.toLowerCase && this.layout_regions[region.toLowerCase()]) {
var current_region = this.layout_regions[region.toLowerCase()];
current_region.add(component);
/*if( (region.toLowerCase() == 'east' || region.toLowerCase() == 'west') && current_region.items.length == 2){ //!current_region.getLayout() instanceof Ext.layout.Accordion ){
var layout = new Ext.layout.Accordion( current_region.layoutConfig );
current_region.setLayout( layout );
var items = current_region.items.clone();
current_region.items.each(function(item){ current_region.remove( item )})
items.each(function(item){ current_region.add( item )})
} */
ORYX.Log.debug("original dimensions of region %0: %1 x %2", current_region.region, current_region.width, current_region.height)
// update dimensions of region if required.
if (!current_region.width && component.initialConfig && component.initialConfig.width) {
ORYX.Log.debug("resizing width of region %0: %1", current_region.region, component.initialConfig.width)
current_region.setWidth(component.initialConfig.width)
}
if (component.initialConfig && component.initialConfig.height) {
ORYX.Log.debug("resizing height of region %0: %1", current_region.region, component.initialConfig.height)
var current_height = current_region.height || 0;
current_region.height = component.initialConfig.height + current_height;
current_region.setHeight(component.initialConfig.height + current_height)
}
// set title if provided as parameter.
if (typeof title == "string") {
current_region.setTitle(title);
}
// trigger doLayout() and show the pane
current_region.ownerCt.doLayout();
current_region.show();
if(Ext.isMac)
ORYX.Editor.resizeFix();
return current_region;
}
return null;
},
getAvailablePlugins: function(){
var curAvailablePlugins=ORYX.availablePlugins.clone();
curAvailablePlugins.each(function(plugin){
if(this.loadedPlugins.find(function(loadedPlugin){
return loadedPlugin.type==this.name;
}.bind(plugin))){
plugin.engaged=true;
}else{
plugin.engaged=false;
}
}.bind(this));
return curAvailablePlugins;
},
loadScript: function (url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
},
/**
* activate Plugin
*
* @param {String} name
* @param {Function} callback
* callback(sucess, [errorCode])
* errorCodes: NOTUSEINSTENCILSET, REQUIRESTENCILSET, NOTFOUND, YETACTIVATED
*/
activatePluginByName: function(name, callback, loadTry){
var match=this.getAvailablePlugins().find(function(value){return value.name==name});
if(match && (!match.engaged || (match.engaged==='false'))){
var loadedStencilSetsNamespaces = this.getStencilSets().keys();
var facade = this._getPluginFacade();
var newPlugin;
var me=this;
ORYX.Log.debug("Initializing plugin '%0'", match.name);
if (!match.requires || !match.requires.namespaces || match.requires.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) ){
if(!match.notUsesIn || !match.notUsesIn.namespaces || !match.notUsesIn.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 })){
try {
var className = eval(match.name);
var newPlugin = new className(facade, match);
newPlugin.type = match.name;
// If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
if (newPlugin.registryChanged)
newPlugin.registryChanged(me.pluginsData);
// If there have an onSelection-Method it will pushed to the Editor Event-Handler
if (newPlugin.onSelectionChanged)
me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, newPlugin.onSelectionChanged.bind(newPlugin));
this.loadedPlugins.push(newPlugin);
this.loadedPlugins.each(function(loaded){
if(loaded.registryChanged)
loaded.registryChanged(this.pluginsData);
}.bind(me));
callback(true);
} catch(e) {
ORYX.Log.warn("Plugin %0 is not available", match.name);
if(!!loadTry){
callback(false,"INITFAILED");
return;
}
this.loadScript("plugins/scripts/"+match.source, this.activatePluginByName.bind(this,match.name,callback,true));
}
}else{
callback(false,"NOTUSEINSTENCILSET");
ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name);
}
} else {
callback(false,"REQUIRESTENCILSET");
ORYX.Log.info("Plugin need a stencilset which is not loaded'", match.name);
}
}else{
callback(false, match?"NOTFOUND":"YETACTIVATED");
//TODO error handling
}
},
/**
* Laden der Plugins
*/
loadPlugins: function() {
// if there should be plugins but still are none, try again.
// TODO this should wait for every plugin respectively.
/*if (!ORYX.Plugins && ORYX.availablePlugins.length > 0) {
window.setTimeout(this.loadPlugins.bind(this), 100);
return;
}*/
var me = this;
var newPlugins = [];
var loadedStencilSetsNamespaces = this.getStencilSets().keys();
// Available Plugins will be initalize
var facade = this._getPluginFacade();
// If there is an Array where all plugins are described, than only take those
// (that comes from the usage of oryx with a mashup api)
if( ORYX.MashupAPI && ORYX.MashupAPI.loadablePlugins && ORYX.MashupAPI.loadablePlugins instanceof Array ){
// Get the plugins from the available plugins (those who are in the plugins.xml)
ORYX.availablePlugins = $A(ORYX.availablePlugins).findAll(function(value){
return ORYX.MashupAPI.loadablePlugins.include( value.name )
})
// Add those plugins to the list, which are only in the loadablePlugins list
ORYX.MashupAPI.loadablePlugins.each(function( className ){
if( !(ORYX.availablePlugins.find(function(val){ return val.name == className }))){
ORYX.availablePlugins.push( {name: className } );
}
})
}
ORYX.availablePlugins.each(function(value) {
ORYX.Log.debug("Initializing plugin '%0'", value.name);
if( (!value.requires || !value.requires.namespaces || value.requires.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) ) &&
(!value.notUsesIn || !value.notUsesIn.namespaces || !value.notUsesIn.namespaces.any(function(req){ return loadedStencilSetsNamespaces.indexOf(req) >= 0 }) )&&
/*only load activated plugins or undefined */
(value.engaged || (value.engaged===undefined)) ){
try {
var className = eval(value.name);
if( className ){
var plugin = new className(facade, value);
plugin.type = value.name;
newPlugins.push( plugin );
plugin.engaged=true;
}
} catch(e) {
ORYX.Log.warn("Plugin %0 is not available %1", value.name, e);
}
} else {
ORYX.Log.info("Plugin need a stencilset which is not loaded'", value.name);
}
});
newPlugins.each(function(value) {
// If there is an GUI-Plugin, they get all Plugins-Offer-Meta-Data
if(value.registryChanged)
value.registryChanged(me.pluginsData);
// If there have an onSelection-Method it will pushed to the Editor Event-Handler
if(value.onSelectionChanged)
me.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, value.onSelectionChanged.bind(value));
});
this.loadedPlugins = newPlugins;
// Hack for the Scrollbars
if(Ext.isMac) {
ORYX.Editor.resizeFix();
}
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);
if (canvasConfig) {
// Migrate canvasConfig to an RDF-like structure
//FIXME this isn't nice at all because we don't want rdf any longer
var properties = [];
for(field in canvasConfig){
properties.push({
prefix: 'oryx',
name: field,
value: canvasConfig[field]
});
}
this._canvas.deserialize(properties);
}
},
/**
* Returns a per-editor singleton plugin facade.
* To be used in plugin initialization.
*/
_getPluginFacade: function() {
// if there is no pluginfacade already created:
if(!(this._pluginFacade))
// create it.
this._pluginFacade = {
activatePluginByName: this.activatePluginByName.bind(this),
//deactivatePluginByName: this.deactivatePluginByName.bind(this),
getAvailablePlugins: this.getAvailablePlugins.bind(this),
offer: this.offer.bind(this),
getStencilSets: this.getStencilSets.bind(this),
getStencilSetExtensionDefinition:function(){ return Object.clone(this.ss_extensions_def||{})}.bind(this),
getRules: this.getRules.bind(this),
loadStencilSet: this.loadStencilSet.bind(this),
createShape: this.createShape.bind(this),
deleteShape: this.deleteShape.bind(this),
getSelection: this.getSelection.bind(this),
setSelection: this.setSelection.bind(this),
updateSelection: this.updateSelection.bind(this),
getCanvas: this.getCanvas.bind(this),
importJSON: this.importJSON.bind(this),
importERDF: this.importERDF.bind(this),
getERDF: this.getERDF.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),
addToRegion: this.addToRegion.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(){
var canvas = this.getCanvas().toJSON();
canvas.ssextensions = this.getStencilSets().values()[0].extensions().keys().findAll(function(sse){ return !sse.endsWith('/meta#') });
return canvas;
},
/**
* Serializes a call to toJSON().
* @return {String} Returns JSON representation as string.
*/
getSerializedJSON: function(){
return Ext.encode(this.getJSON());
},
/**
* @return {String} Returns eRDF representation.
* @deprecated Use ORYX.Editor#getJSON instead, if possible.
*/
getERDF:function(){
// Get the serialized dom
var serializedDOM = DataManager.serializeDOM( this._getPluginFacade() );
// Add xml definition if there is no
serializedDOM = '' +
'' +
'' +
'' +
'' +
'' +
'' +
'' +
'' +
'' +
serializedDOM +
'';
return serializedDOM;
},
/**
* 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()) {
Ext.Msg.alert(ORYX.I18N.JSONImport.title, 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(Ext.type(jsonObject) === "string"){
try {
var serJsonObject = jsonObject;
jsonObject = Ext.decode(jsonObject);
} catch(error){
throw new SyntaxError(error.message);
}
} else {
var serJsonObject = Ext.encode(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.gsub('"'+oldResourceId+'"', '"'+newResourceId+'"')
});
return Ext.decode(serJsonObject);
},
/**
* Import erdf structure to the editor
*
*/
importERDF: function( erdfDOM ){
var serialized = this.parseToSerializeObjects( erdfDOM );
if(serialized)
return this.importJSON(serialized, true);
},
/**
* Parses one model (eRDF) to the serialized form (JSON)
*
* @param {Object} oneProcessData
* @return {Object} The JSON form of given eRDF model, or null if it couldn't be extracted
*/
parseToSerializeObjects: function( oneProcessData ){
// Firefox splits a long text node into chunks of 4096 characters.
// To prevent truncation of long property values the normalize method must be called
if(oneProcessData.normalize) oneProcessData.normalize();
try {
var xsl = "";
var 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 = oneProcessData;
var xslObject = domParser.parseFromString(xsl, "text/xml");
var xsltProcessor = new XSLTProcessor();
var xslRef = document.implementation.createDocument("", "", null);
xsltProcessor.importStylesheet(xslObject);
var new_rdf = xsltProcessor.transformToFragment(xmlObject, document);
var serialized_rdf = (new XMLSerializer()).serializeToString(new_rdf);
}catch(e){
Ext.Msg.alert("Oryx", error);
var serialized_rdf = "";
}
// Firefox 2 to 3 problem?!
serialized_rdf = !serialized_rdf.startsWith("" + serialized_rdf : serialized_rdf;
var req = new Ajax.Request(ORYX.CONFIG.ROOT_PATH+"rdf2json", {
method: 'POST',
asynchronous: false,
onSuccess: function(transport) {
Ext.decode(transport.responseText);
},
parameters: {
rdf: serialized_rdf
}
});
return Ext.decode(req.transport.responseText);
},
/**
* 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 = Ext.encode(value);
}
this.getCanvas().setProperty("oryx-" + key, value);
}
}
this.getCanvas().updateSize();
// Force to update the selection
this.selection = [null];
this.setSelection([]);
return shapes;
},
/**
* Return the namespace of the extension which
* provided all the self defined meta data
* @return {String} Returns null if no extension is defined, otherwise the namespace
*
*/
getExtensionForMetaData: function(){
if (!this.ss_extensions_def||!(this.ss_extensions_def.extensions instanceof Array)){
return null;
}
var stencilsets = this.getStencilSets();
var extension = this.ss_extensions_def.extensions.find(function(ex){
return !!stencilsets[ex["extends"]] && ex.namespace.endsWith("/meta#");
});
return extension ? extension.namespace || null : null;
},
/**
* Calls ORYX.Editor.prototype.ss_extension_namespace for each element
* @param {Array} ss_extension_namespaces An array of stencil set extension namespaces.
*/
loadSSExtensions: function(ss_extension_namespaces){
if(!ss_extension_namespaces) return;
ss_extension_namespaces.each(function(ss_extension_namespace){
this.loadSSExtension(ss_extension_namespace);
}.bind(this));
},
/**
* Loads a stencil set extension.
* The stencil set extensions definiton file must already
* be loaded when the editor is initialized.
*/
loadSSExtension: function(ss_extension_namespace) {
if (this.ss_extensions_def) {
var extension = this.ss_extensions_def.extensions.find(function(ex){
return (ex.namespace == ss_extension_namespace);
});
if (!extension) {
return;
}
var stencilset = this.getStencilSets()[extension["extends"]];
if (!stencilset) {
return;
}
// Check if absolute or relative url
if ((extension["definition"]||"").startsWith("/")){
stencilset.addExtension(extension["definition"])
} else {
stencilset.addExtension(ORYX.CONFIG.SS_EXTENSIONS_FOLDER + extension["definition"])
}
//stencilset.addExtension("/oryx/build/stencilsets/extensions/" + extension["definition"])
this.getRules().initializeRules(stencilset);
this._getPluginFacade().raiseEvent({
type: ORYX.CONFIG.EVENT_STENCIL_SET_LOADED
});
}
},
disableEvent: function(eventType){
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
this._keydownEnabled = false;
}
if(eventType == ORYX.CONFIG.EVENT_KEYUP) {
this._keyupEnabled = false;
}
if(this.DOMEventListeners.keys().member(eventType)) {
var value = this.DOMEventListeners.remove(eventType);
this.DOMEventListeners['disable_' + eventType] = value;
}
},
enableEvent: function(eventType){
if(eventType == ORYX.CONFIG.EVENT_KEYDOWN) {
this._keydownEnabled = true;
}
if(eventType == ORYX.CONFIG.EVENT_KEYUP) {
this._keyupEnabled = true;
}
if(this.DOMEventListeners.keys().member("disable_" + eventType)) {
var value = this.DOMEventListeners.remove("disable_" + eventType);
this.DOMEventListeners[eventType] = value;
}
},
/**
* Methods for the PluginFacade
*/
registerOnEvent: function(eventType, callback) {
if(!(this.DOMEventListeners.keys().member(eventType))) {
this.DOMEventListeners[eventType] = [];
}
this.DOMEventListeners[eventType].push(callback);
},
unregisterOnEvent: function(eventType, callback) {
if(this.DOMEventListeners.keys().member(eventType)) {
this.DOMEventListeners[eventType] = this.DOMEventListeners[eventType].without(callback);
} else {
// Event is not supported
// TODO: Error Handling
}
},
getSelection: function() {
return this.selection || [];
},
getStencilSets: function() {
return ORYX.Core.StencilSet.stencilSets(this.id);
},
getRules: function() {
return ORYX.Core.StencilSet.rules(this.id);
},
loadStencilSet: function(source) {
try {
ORYX.Core.StencilSet.loadStencilSet(source, this.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);
} else {
var newShapeObject = new ORYX.Core.Edge({'eventHandlerCallback':this.handleEvents.bind(this)}, stencil);
}
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))
} else {
newShapeObject = new ORYX.Core.Edge({'eventHandlerCallback':this.handleEvents.bind(this)}, sset.stencil(shapetype))
}
// 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());
}
} else {
var b = newShapeObject.bounds
if( newShapeObject instanceof ORYX.Core.Node && newShapeObject.dockers.length == 1){
b = newShapeObject.dockers.first().bounds
}
b.centerMoveTo(point);
var upL = b.upperLeft();
b.moveBy( -Math.min(upL.x, 0) , -Math.min(upL.y, 0) )
var lwR = b.lowerRight();
b.moveBy( -Math.max(lwR.x-canvas.bounds.width(), 0) , -Math.max(lwR.y-canvas.bounds.height(), 0) )
}
// Update the shape
if (newShapeObject instanceof ORYX.Core.Edge) {
newShapeObject._update(false);
}
// And refresh the selection
if(!(newShapeObject instanceof ORYX.Core.Edge)&&!(option.dontUpdateSelection)) {
this.setSelection([newShapeObject]);
}
if(con && con.alignDockers) {
con.alignDockers();
}
if(newShapeObject.alignDockers) {
newShapeObject.alignDockers();
}
return newShapeObject;
},
deleteShape: function(shape) {
if (!shape || !shape.parent){ return }
//remove shape from parent
// this also removes it from DOM
shape.parent.remove(shape);
//delete references to outgoing edges
shape.getOutgoingShapes().each(function(os) {
var docker = os.getDockers().first();
if(docker && docker.getDockedShape() == shape) {
docker.setDockedShape(undefined);
}
});
//delete references to incoming edges
shape.getIncomingShapes().each(function(is) {
var docker = is.getDockers().last();
if(docker && docker.getDockedShape() == shape) {
docker.setDockedShape(undefined);
}
});
//delete references of the shape's dockers
shape.getDockers().each(function(docker) {
docker.setDockedShape(undefined);
});
},
/**
* Returns an object with meta data about the model.
* Like name, description, ...
*
* Empty object with the current backend.
*
* @return {Object} Meta data about the model
*/
getModelMetaData: function() {
return this.modelMetaData;
},
/* Event-Handler Methods */
/**
* Helper method to execute an event immediately. The event is not
* scheduled in the _eventsQueue. Needed to handle Layout-Callbacks.
*/
_executeEventImmediately: function(eventObj) {
if(this.DOMEventListeners.keys().member(eventObj.event.type)) {
this.DOMEventListeners[eventObj.event.type].each((function(value) {
value(eventObj.event, eventObj.arg);
}).bind(this));
}
},
_executeEvents: function() {
this._queueRunning = true;
while(this._eventsQueue.length > 0) {
var val = this._eventsQueue.shift();
this._executeEventImmediately(val);
}
this._queueRunning = false;
},
/**
* Leitet die Events an die Editor-Spezifischen Event-Methoden weiter
* @param {Object} event Event , welches gefeuert wurde
* @param {Object} uiObj Target-UiObj
*/
handleEvents: function(event, uiObj) {
ORYX.Log.trace("Dispatching event type %0 on %1", event.type, uiObj);
switch(event.type) {
case ORYX.CONFIG.EVENT_MOUSEDOWN:
this._handleMouseDown(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEMOVE:
this._handleMouseMove(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEUP:
this._handleMouseUp(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEOVER:
this._handleMouseHover(event, uiObj);
break;
case ORYX.CONFIG.EVENT_MOUSEOUT:
this._handleMouseOut(event, uiObj);
break;
}
/* Force execution if necessary. Used while handle Layout-Callbacks. */
if(event.forceExecution) {
this._executeEventImmediately({event: event, arg: uiObj});
} else {
this._eventsQueue.push({event: event, arg: uiObj});
}
if(!this._queueRunning) {
this._executeEvents();
}
// TODO: Make this return whether no listener returned false.
// So that, when one considers bubbling undesireable, it won't happen.
return false;
},
isValidEvent: function(e){
try {
var isInput = ["INPUT", "TEXTAREA"].include(e.target.tagName.toUpperCase());
var gridHasFocus = e.target.className.include("x-grid3-focus") && !e.target.className.include("x-grid3-focus-canvas");
return !isInput && !gridHasFocus;
} catch(e){
return false;
}
},
catchKeyUpEvents: function(event) {
if(!this._keyupEnabled) {
return;
}
/* assure we have the current event. */
if (!event)
event = window.event;
// Checks if the event comes from some input field
if (!this.isValidEvent(event)){
return;
}
/* Create key up event type */
var keyUpEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_UP);
ORYX.Log.debug("Key Event to handle: %0", keyUpEvent);
/* forward to dispatching. */
this.handleEvents({type: keyUpEvent, event:event});
},
/**
* Catches all key down events and forward the appropriated event to
* dispatching concerning to the pressed keys.
*
* @param {Event}
* The key down event to handle
*/
catchKeyDownEvents: function(event) {
if(!this._keydownEnabled) {
return;
}
/* Assure we have the current event. */
if (!event)
event = window.event;
/* Fixed in FF3 */
// This is a mac-specific fix. The mozilla event object has no knowledge
// of meta key modifier on osx, however, it is needed for certain
// shortcuts. This fix adds the metaKey field to the event object, so
// that all listeners that registered per Oryx plugin facade profit from
// this. The original bug is filed in
// https://bugzilla.mozilla.org/show_bug.cgi?id=418334
//if (this.__currentKey == ORYX.CONFIG.KEY_CODE_META) {
// event.appleMetaKey = true;
//}
//this.__currentKey = pressedKey;
// Checks if the event comes from some input field
if (!this.isValidEvent(event)){
return;
}
/* Create key up event type */
var keyDownEvent = this.createKeyCombEvent(event, ORYX.CONFIG.KEY_ACTION_DOWN);
ORYX.Log.debug("Key Event to handle: %0", keyDownEvent);
/* Forward to dispatching. */
this.handleEvents({type: keyDownEvent,event: event});
},
/**
* Creates the event type name concerning to the pressed keys.
*
* @param {Event} keyDownEvent
* The source keyDownEvent to build up the event name
*/
createKeyCombEvent: function(keyEvent, keyAction) {
/* Get the currently pressed key code. */
var pressedKey = keyEvent.which || keyEvent.keyCode;
//this.__currentKey = pressedKey;
/* Event name */
var eventName = "key.event";
/* Key action */
if(keyAction) {
eventName += "." + keyAction;
}
/* Ctrl or apple meta key is pressed */
if(keyEvent.ctrlKey || keyEvent.metaKey) {
eventName += "." + ORYX.CONFIG.META_KEY_META_CTRL;
}
/* Alt key is pressed */
if(keyEvent.altKey) {
eventName += "." + ORYX.CONFIG.META_KEY_ALT;
}
/* Alt key is pressed */
if(keyEvent.shiftKey) {
eventName += "." + ORYX.CONFIG.META_KEY_SHIFT;
}
/* Return the composed event name */
return eventName + "." + pressedKey;
},
_handleMouseDown: function(event, uiObj) {
// get canvas.
var canvas = this.getCanvas();
// Try to get the focus
canvas.focus()
// find the shape that is responsible for this element's id.
var element = event.currentTarget;
var elementController = uiObj;
// gather information on selection.
var currentIsSelectable = (elementController !== null) &&
(elementController !== undefined) && (elementController.isSelectable);
var currentIsMovable = (elementController !== null) &&
(elementController !== undefined) && (elementController.isMovable);
var modifierKeyPressed = event.shiftKey || event.ctrlKey;
var noObjectsSelected = this.selection.length === 0;
var currentIsSelected = this.selection.member(elementController);
// Rule #1: When there is nothing selected, select the clicked object.
if(currentIsSelectable && noObjectsSelected) {
this.setSelection([elementController]);
ORYX.Log.trace("Rule #1 applied for mouse down on %0", element.id);
// Rule #3: When at least one element is selected, and there is no
// control key pressed, and the clicked object is not selected, select
// the clicked object.
} else if(currentIsSelectable && !noObjectsSelected &&
!modifierKeyPressed && !currentIsSelected) {
this.setSelection([elementController]);
//var objectType = elementController.readAttributes();
//alert(objectType[0] + ": " + objectType[1]);
ORYX.Log.trace("Rule #3 applied for mouse down on %0", element.id);
// Rule #4: When the control key is pressed, and the current object is
// not selected, add it to the selection.
} else if(currentIsSelectable && modifierKeyPressed
&& !currentIsSelected) {
var newSelection = this.selection.clone();
newSelection.push(elementController)
this.setSelection(newSelection)
ORYX.Log.trace("Rule #4 applied for mouse down on %0", element.id);
// Rule #6
} else if(currentIsSelectable && currentIsSelected &&
modifierKeyPressed) {
var newSelection = this.selection.clone();
this.setSelection(newSelection.without(elementController))
ORYX.Log.trace("Rule #6 applied for mouse down on %0", elementController.id);
// Rule #5: When there is at least one object selected and no control
// key pressed, we're dragging.
/*} else if(currentIsSelectable && !noObjectsSelected
&& !modifierKeyPressed) {
if(this.log.isTraceEnabled())
this.log.trace("Rule #5 applied for mouse down on "+element.id);
*/
// Rule #2: When clicked on something that is neither
// selectable nor movable, clear the selection, and return.
} else if (!currentIsSelectable && !currentIsMovable) {
this.setSelection([]);
ORYX.Log.trace("Rule #2 applied for mouse down on %0", element.id);
return;
// Rule #7: When the current object is not selectable but movable,
// it is probably a control. Leave the selection unchanged but set
// the movedObject to the current one and enable Drag. Dockers will
// be processed in the dragDocker plugin.
} else if(!currentIsSelectable && currentIsMovable && !(elementController instanceof ORYX.Core.Controls.Docker)) {
// TODO: If there is any moveable elements, do this in a plugin
//ORYX.Core.UIEnableDrag(event, elementController);
ORYX.Log.trace("Rule #7 applied for mouse down on %0", element.id);
// Rule #8: When the element is selectable and is currently selected and no
// modifier key is pressed
} else if(currentIsSelectable && currentIsSelected &&
!modifierKeyPressed) {
this._subSelection = this._subSelection != elementController ? elementController : undefined;
this.setSelection(this.selection, this._subSelection);
ORYX.Log.trace("Rule #8 applied for mouse down on %0", element.id);
}
// prevent event from bubbling, return.
//Event.stop(event);
return;
},
_handleMouseMove: function(event, uiObj) {
return;
},
_handleMouseUp: function(event, uiObj) {
// get canvas.
var canvas = this.getCanvas();
// find the shape that is responsible for this elemement's id.
var elementController = uiObj;
//get event position
var evPos = this.eventCoordinates(event);
//Event.stop(event);
},
_handleMouseHover: function(event, uiObj) {
return;
},
_handleMouseOut: function(event, uiObj) {
return;
},
/**
* Calculates the event coordinates to SVG document coordinates.
* @param {Event} event
* @return {SVGPoint} The event coordinates in the SVG document
*/
eventCoordinates: function(event) {
var canvas = this.getCanvas();
var svgPoint = canvas.node.ownerSVGElement.createSVGPoint();
svgPoint.x = event.clientX;
svgPoint.y = event.clientY;
var 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, config){
if(!config) config = {};
new Ajax.Request(modelUrl, {
method: 'GET',
onSuccess: function(transport) {
var editorConfig = Ext.decode(transport.responseText);
editorConfig = Ext.applyIf(editorConfig, config);
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( 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
}
};
ORYX.Editor.makeExtModalWindowKeysave = function(facade) {
Ext.override(Ext.Window,{
beforeShow : function(){
delete this.el.lastXY;
delete this.el.lastLT;
if(this.x === undefined || this.y === undefined){
var xy = this.el.getAlignToXY(this.container, 'c-c');
var pos = this.el.translatePoints(xy[0], xy[1]);
this.x = this.x === undefined? pos.left : this.x;
this.y = this.y === undefined? pos.top : this.y;
}
this.el.setLeftTop(this.x, this.y);
if(this.expandOnShow){
this.expand(false);
}
if(this.modal){
facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
Ext.getBody().addClass("x-body-masked");
this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
this.mask.show();
}
},
afterHide : function(){
this.proxy.hide();
if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
Ext.EventManager.removeResizeListener(this.onWindowResize, this);
}
if(this.modal){
this.mask.hide();
facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
Ext.getBody().removeClass("x-body-masked");
}
if(this.keyMap){
this.keyMap.disable();
}
this.fireEvent("hide", this);
},
beforeDestroy : function(){
if(this.modal)
facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
Ext.destroy(
this.resizer,
this.dd,
this.proxy,
this.mask
);
Ext.Window.superclass.beforeDestroy.call(this);
}
});
}
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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) {
// call base class constructor
arguments.callee.$.construct.apply(this, arguments);
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;
//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_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_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){
// 0.0 <= value <= 1.0
value = Math.max(Math.min((typeof value == "number" ? value : 1.0), 1.0), 0.0);
//if (animate !== true){
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");
}
/*} else {
var args = {opacity:{to:value}};
if (!this.isVisible){
this.show();
}
if (this.currentAnim){
this.currentAnim.stop();
}
this.currentAnim = Ext.lib.Anim.run(this.node, args, 0.4, "easeOut", function(){
if (args.opacity.to === 0.0){
this.hide();
} else if (args.opacity.to === 1.0){
this.node.removeAttributeNS(null, "style");
}
delete this.currentAnim;
}, this)
}*/
},
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* 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){
arguments.callee.$.construct.apply(this, arguments);
this.isSelectable = true;
this.isMovable = true;
this._dockerUpdated = false;
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 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;
//set translation in transform attribute
/*var attributeTransform = document.createAttributeNS(ORYX.CONFIG.NAMESPACE_SVG, "transform");
attributeTransform.nodeValue = "translate(" + x + ", " + y + ")";
this.node.firstChild.setAttributeNode(attributeTransform);*/
// 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.createAttributeNS(null, "title");
attributeTitle.nodeValue = this.getStencil().title();
svgNode.setAttributeNode(attributeTitle);
var attributeId = svgDocument.ownerDocument.createAttributeNS(null, "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));
}).bind(this));
},
/**
* 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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){
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();
}
},
/**
* 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 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(Ext.apply({},{
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();
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);
// Remove new Docker from 'to add' dockers
//pair.value = pair.value.without(newDocker);
//pair.value.splice(this.dockers.indexOf(lastDocker) + 1, 0, newDocker);
// Remove the Docker from the Docker list and add the Docker to the new position
//this.dockers = this.dockers.without(newDocker);
//this.dockers.splice(this.dockers.indexOf(lastDocker) + 1, 0, newDocker);
//this._update(true);
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.id.concat(markerUrl.replace(/\)$/, ''));
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.id.concat(markerUrl.replace(/\)$/, ''));
path.setAttributeNS(null, "marker-mid", "url(#" + markerMidId + ")");
markersByThisPath.push(this._markers[markerMidId]);
}
markerUrl = path.getAttributeNS(null, "marker-end");
if (markerUrl && markerUrl !== "") {
markerUrl = markerUrl.strip();
markerUrl = markerUrl.replace(/^url\(#/, '');
var markerEndId = this.id.concat(markerUrl.replace(/\)$/, ''));
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;
});
//this._update(true);
},
/**
* Adds all necessary markers of this Edge to the SVG document.
* Has to be called, while this.node is part of DOM.
*/
addMarkers: function(defs){
this._markers.each(function(marker){
if (!defs.ownerDocument.getElementById(marker.value.id)) {
marker.value.element = defs.appendChild(marker.value.element);
}
});
},
/**
* Removes all necessary markers of this Edge from the SVG document.
* Has to be called, while this.node is part of DOM.
*/
removeMarkers: function(){
var svgElement = this.node.ownerSVGElement;
if (svgElement) {
var defs = svgElement.getElementsByTagNameNS(NAMESPACE_SVG, "defs");
if (defs.length > 0) {
defs = defs[0];
this._markers.each(function(marker){
var foundMarker = defs.ownerDocument.getElementById(marker.value.id);
if (foundMarker) {
marker.value.element = defs.removeChild(marker.value.element);
}
});
}
}
},
/**
* Calls when a docker has changed
*/
_dockerChanged: function(){
//this._update(true);
this._dockerUpdated = true;
},
serialize: function(){
var result = arguments.callee.$.serialize.apply(this);
//add dockers triple
var value = "";
this._dockersByPath.each((function(pair){
pair.value.each(function(docker){
var position = docker.getDockedShape() && docker.referencePoint ? docker.referencePoint : docker.bounds.center();
value = value.concat(position.x + " " + position.y + " ");
});
value += " # ";
}).bind(this));
result.push({
name: 'dockers',
prefix: 'oryx',
value: value,
type: 'literal'
});
//add parent triple dependant on the dockedShapes
//TODO change this when canvas becomes a resource
/* var source = this.dockers.first().getDockedShape();
var target = this.dockers.last().getDockedShape();
var sharedParent;
if (source && target) {
//get shared parent
while (source.parent) {
source = source.parent;
if (source instanceof ORYX.Core.Canvas) {
sharedParent = source;
break;
}
else {
var targetParent = target.parent;
var found;
while (targetParent) {
if (source === targetParent) {
sharedParent = source;
found = true;
break;
}
else {
targetParent = targetParent.parent;
}
}
if (found) {
break;
}
}
}
}
else
if (source) {
sharedParent = source.parent;
}
else
if (target) {
sharedParent = target.parent;
}
*/
//if (sharedParent) {
/* result.push({
name: 'parent',
prefix: 'raziel',
//value: '#' + ERDF.__stripHashes(sharedParent.resourceId),
value: '#' + ERDF.__stripHashes(this.getCanvas().resourceId),
type: 'resource'
});*/
//}
//serialize target and source
var lastDocker = this.dockers.last();
var target = lastDocker.getDockedShape();
if(target) {
result.push({
name: 'target',
prefix: 'raziel',
value: '#' + ERDF.__stripHashes(target.resourceId),
type: 'resource'
});
}
try {
//result = this.getStencil().serialize(this, result);
var serializeEvent = this.getStencil().serialize();
/*
* call serialize callback by reference, result should be found
* in serializeEvent.result
*/
if(serializeEvent.type) {
serializeEvent.shape = this;
serializeEvent.data = result;
serializeEvent.result = undefined;
serializeEvent.forceExecution = true;
this._delegateEvent(serializeEvent);
if(serializeEvent.result) {
result = serializeEvent.result;
}
}
}
catch (e) {
}
return result;
},
deserialize: function(data){
try {
//data = this.getStencil().deserialize(this, data);
var deserializeEvent = this.getStencil().deserialize();
/*
* call serialize callback by reference, result should be found
* in serializeEventInfo.result
*/
if(deserializeEvent.type) {
deserializeEvent.shape = this;
deserializeEvent.data = data;
deserializeEvent.result = undefined;
deserializeEvent.forceExecution = true;
this._delegateEvent(deserializeEvent);
if(deserializeEvent.result) {
data = deserializeEvent.result;
}
}
}
catch (e) {
}
// Set the outgoing shapes
var target = data.find(function(ser) {return (ser.prefix+"-"+ser.name) == 'raziel-target'});
var targetShape;
if(target) {
targetShape = this.getCanvas().getChildShapeByResourceId(target.value);
}
var outgoing = data.findAll(function(ser){ return (ser.prefix+"-"+ser.name) == 'raziel-outgoing'});
outgoing.each((function(obj){
// TODO: Look at Canvas
if(!this.parent) {return};
// Set outgoing Shape
var next = this.getCanvas().getChildShapeByResourceId(obj.value);
if(next){
if(next == targetShape) {
// If this is an edge, set the last docker to the next shape
this.dockers.last().setDockedShape(next);
this.dockers.last().setReferencePoint({x: next.bounds.width() / 2.0, y: next.bounds.height() / 2.0});
} else if(next instanceof ORYX.Core.Edge) {
//Set the first docker of the next shape
next.dockers.first().setDockedShape(this);
//next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
} /*else if(next.dockers.length > 0) { //next is a node and next has a docker
next.dockers.first().setDockedShape(this);
next.dockers.first().setReferencePoint({x: this.bounds.width() / 2.0, y: this.bounds.height() / 2.0});
}*/
}
}).bind(this));
var oryxDockers = data.find(function(obj){
return (obj.prefix === "oryx" &&
obj.name === "dockers");
});
if (oryxDockers) {
var dataByPath = oryxDockers.value.split("#").without("").without(" ");
dataByPath.each((function(data, index){
var values = data.replace(/,/g, " ").split(" ").without("");
//for each docker two values must be defined
if (values.length % 2 === 0) {
var path = this._paths[index];
if (path) {
if (index === 0) {
while (this._dockersByPath[path.id].length > 2) {
this.removeDocker(this._dockersByPath[path.id][1]);
}
}
else {
while (this._dockersByPath[path.id].length > 1) {
this.removeDocker(this._dockersByPath[path.id][0]);
}
}
var dockersByPath = this._dockersByPath[path.id];
if (index === 0) {
//set position of first docker
var x = parseFloat(values.shift());
var y = parseFloat(values.shift());
if (dockersByPath.first().getDockedShape()) {
dockersByPath.first().setReferencePoint({
x: x,
y: y
});
}
else {
dockersByPath.first().bounds.centerMoveTo(x, y);
}
}
//set position of last docker
y = parseFloat(values.pop());
x = parseFloat(values.pop());
if (dockersByPath.last().getDockedShape()) {
dockersByPath.last().setReferencePoint({
x: x,
y: y
});
} else {
dockersByPath.last().bounds.centerMoveTo(x, y);
}
//add additional dockers
for (var i = 0; i < values.length; i++) {
x = parseFloat(values[i]);
y = parseFloat(values[++i]);
var newDocker = this.createDocker();
newDocker.bounds.centerMoveTo(x, y);
//this.dockers = this.dockers.without(newDocker);
//this.dockers.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
//dockersByPath.splice(this.dockers.indexOf(dockersByPath.last()), 0, newDocker);
}
}
}
}).bind(this));
} else {
this.alignDockers();
}
arguments.callee.$.deserialize.apply(this, arguments);
this._changed();
},
toString: function(){
return this.getStencil().title() + " " + this.id;
},
/**
* @return {ORYX.Core.Shape} Returns last docked shape or null.
*/
getTarget: function(){
return this.dockers.last() ? this.dockers.last().getDockedShape() : null;
},
/**
* @return {ORYX.Core.Shape} Returns the first docked shape or null
*/
getSource: function() {
return this.dockers.first() ? this.dockers.first().getDockedShape() : null;
},
/**
* Checks whether the edge is at least docked to one shape.
*
* @return {boolean} True if edge is docked
*/
isDocked: function() {
var isDocked = false;
this.dockers.each(function(docker) {
if(docker.isDocked()) {
isDocked = true;
throw $break;
}
});
return isDocked;
},
/**
* Calls {@link ORYX.Core.AbstractShape#toJSON} and add a some stencil set information.
*/
toJSON: function() {
var json = arguments.callee.$.toJSON.apply(this, arguments);
if(this.getTarget()) {
json.target = {
resourceId: this.getTarget().resourceId
};
}
return json;
}
};
ORYX.Core.Edge = ORYX.Core.Shape.extend(ORYX.Core.Edge);
/**
* Copyright (c) 2009
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 parser = new DOMParser();
var parsedDOM = parser.parseFromString(this.getSerializedDOM(), "text/xml");
var xsltPath = ORYX.PATH + "lib/extract-rdf.xsl";
var xsltProcessor = new XSLTProcessor();
var xslRef = document.implementation.createDocument("", "", null);
xslRef.async = false;
xslRef.load(xsltPath);
xsltProcessor.importStylesheet(xslRef);
try {
var rdf = xsltProcessor.transformToDocument(parsedDOM);
return (new XMLSerializer()).serializeToString(rdf);
} catch (error) {
Ext.Msg.alert("Oryx", error);
return null;
}*/
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){
Ext.Msg.alert("Oryx", error);
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
this.facade.raiseEvent({
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 (c) 2009
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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.")
}
});ImageViewer = Ext.extend(Ext.Window, {
initComponent: function() {
this.bodyCfg = {
tag: 'img',
src: this.src,
autoscroll: true,
fixedcenter : true
};
ImageViewer.superclass.initComponent.apply(this, arguments);
},
onRender: function() {
ImageViewer.superclass.onRender.apply(this, arguments);
this.body.on('load', this.onImageLoad, this, {single: true});
},
onImageLoad: function() {
// var h = this.getFrameHeight(),
// w = this.getFrameWidth();
// this.setSize(this.body.dom.offsetWidth + w, this.body.dom.offsetHeight + h);
},
setSrc: function(src) {
this.body.on('load', this.onImageLoad, this, {single: true});
//this.body.dom.style.width = this.body.dom.style.width = 'auto';
this.body.dom.src = src;
},
initEvents: function() {
ImageViewer.superclass.initEvents.apply(this, arguments);
if (this.resizer) {
this.resizer.preserveRatio = true;
}
}
});if(!Signavio){ var Signavio = {} };
if (!Signavio.Core) { Signavio.Core = {} };
Signavio.Core.Version = "1.0";
/**
* Copyright (c) 2009
*
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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(){
var mask;
Signavio.Plugins.Utils.getFFVersion = function(){
try {
return Number(window.navigator.userAgent.match("Firefox.([0-9]+[\.][0-9]+)")[1]) || 0 ;
} catch(e){
return 0;
}
}
/**
* Shows an overlay of signavio
*/
Signavio.Helper.ShowMask = function(force, parent){
if (!force && ORYX.CONFIG.PREVENT_LOADINGMASK_AT_READY === true){
return;
}
if (mask){
return;
}
var s = "background:white;bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%;z-index:100000;"
var ss = "left:50%;margin-left:-200px;margin-top:-90px;position:absolute;top:50%;display:none;width:391px;"
var sversion = "color:#ad0f5b;padding-right:10px;font-family:tahoma,arial,san-serif;font-size:12px;";
var stext = "display:block;position:relative;text-align:right;top:0;width:100%;";
var stitle = "color:#ad0f5b;font-weight:bold;padding-right:10px;font-family:tahoma,arial,san-serif;font-size:12px;"
var sloading = "height:16px;width:16px;margin-bottom:-4px;background: transparent url(../libs/ext-2.0.2/resources/images/default/tree/loading.gif) no-repeat center;"
var simg = "padding-bottom:10px;border-bottom:1px solid #ad0f5b;";
// Define the parent
parent = (parent ? Ext.get(parent) : null) || Ext.getBody();
if (parent !== Ext.getBody()){
parent.setStyle("position", "relative")
}
mask = Ext.get(document.createElement("div"));
parent.appendChild(mask);
mask.dom.setAttribute("style", s);
mask.dom.innerHTML = "
";
mask.first().show({duration:0.3});
}
// When body is loaded, show overlay
Ext.onReady(Signavio.Helper.ShowMask);
/**
* Hides the overlay
*/
Signavio.Helper.HideMask = function(){
window.setTimeout(function(){
if (mask){
mask.first().hide({duration:0.4, remove:true, block:true});
mask.hide({duration:0.3, remove:true, block :true});
delete mask;
}
}.bind(this), 2000)
}
Signavio.Plugins.Loading = {
facade: undefined,
construct: function(facade) {
this.facade = facade;
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, Signavio.Helper.HideMask);
/**
* Overwrite the toJSON method in the Canvas
* to set the correct stencilset namespace.
*
*/
/*(var me = this;
new function(){
// Copy prototype method
var toJSON = ORYX.Core.Canvas.prototype.toJSON;
ORYX.Core.Canvas.prototype.toJSON = function(){
// Call super
var json = toJSON.call(this);
// Check for replace stencil set namespace
json.stencilset.namespace = me.facade.getModelMetaData().model.stencilset.namespace;
return json;
}
}()*/
}
}
Signavio.Plugins.Loading = Clazz.extend(Signavio.Plugins.Loading);
/**
* 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('');
};
}();
/**
* Ext specific extension
*
*
*
*/
new function(){
/**
* Implementation of an Ext-LinkButton
*
*
*/
Ext.LinkButton = Ext.extend(Ext.BoxComponent, {
// On Click Handler
click: null,
// Image url
image: null,
// Image style (only if an image url is setted)
imageStyle: null,
toggle:false,
toggleStyle:null,
selected:false,
href:false,
el: null,
// private
onRender : function(ct, position){
if( this.el == null ){
this.el = document.createElement('a');
if (this.tabIndex)
this.el.setAttribute("tabindex", this.tabIndex)
this.el.id = this.getId();
this.el.className = this.cls||"x-link-button";
if( !this.disabled )
this.el.href = this.href ? this.href : "#" + this.text;
if( !this.disabled ){
Element.observe( this.el, 'click', this.onClick.bind(this));
}
if( this.image ){
this.el.innerHTML = '': '/>')
} else {
this.el.innerHTML = this.text ? Ext.util.Format.htmlEncode(this.text) : (this.html || '');
}
if(this.forId){
this.el.setAttribute('htmlFor', this.forId);
}
}
Ext.LinkButton.superclass.onRender.call(this, ct, position);
},
onClick: function(e){
if( this.disabled ){ Event.stop(e); return; }
// Toggle the button
if( this.toggle ){
this.selected = !this.selected;
if( this.toggleStyle ){
this._setStyle( this.el.dom, '')
this.el.dom.setAttribute('style','')
if( this.selected ){
this.el.applyStyles( this.toggleStyle )
} else {
this.el.applyStyles( this.initialConfig.style )
}
}
}
if( this.click instanceof Function )
this.click.apply(this.click, [this, e]);
Event.stop(e)
},
setText: function(t, encode){
this.text = t;
if(this.rendered){
this.el.dom.innerHTML = encode !== false ? Ext.util.Format.htmlEncode(t) : t;
}
return this;
},
_setStyle: function(node, style){
if( Ext.isIE ){
node.style.setAttribute('cssText', style );
} else {
node.setAttribute('style', style );
}
}
});
Ext.reg('linkbutton', Ext.LinkButton);
}();
/**
* Helper Methods
*
*/
new function(){
Signavio.Helper.RecordReader = function(meta){
meta = meta || {};
this.rels = meta.rels || this.rels;
Signavio.Helper.RecordReader.superclass.constructor.call(this, meta, ['rep','href','rel']);
};
Ext.extend(Signavio.Helper.RecordReader, Ext.data.JsonReader, {
rels: ["gitem"],
read : function(response){
var json = response.responseText;
var o = eval("("+json+")");
if(!o) {
throw {message: "JsonReader.read: Json object not found"};
}
var Record = this.recordType;
var records = [], total = 0;
o.each(function(rec){
if (this.rels.include(rec.rel)) {
records.push(new Record(rec));
}
if (rec.rel == "info" && rec.rep.size){
total = rec.rep.size;
}
}.bind(this))
return {
success : true,
records : records,
totalRecords : total || records.length
}
}
})
/**
* Creates a new record, including 'rel', 'href', and 'rep' attributes
* @param {String} rel
* @param {String} href
* @param {Object} rep
*/
Signavio.Helper.createRecord = function(rel, href, rep){
var Rec = Ext.data.Record.create(["rel", "href", "rep"]);
var record = new Rec({
rel : rel,
href: href,
rep : rep
});
return record;
}
}()
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if(!ORYX.Plugins) {
ORYX.Plugins = new Object();
}
ORYX.Plugins.Toolbar = Clazz.extend({
facade: undefined,
plugs: [],
construct: function(facade, ownPluginData) {
this.facade = facade;
this.groupIndex = new Hash();
ownPluginData.properties.each((function(value){
if(value.group && value.index != undefined) {
this.groupIndex[value.group] = value.index
}
}).bind(this));
Ext.QuickTips.init();
this.buttons = [];
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_BUTTON_UPDATE, this.onButtonUpdate.bind(this));
},
/**
* Can be used to manipulate the state of a button.
* @example
* this.facade.raiseEvent({
* type: ORYX.CONFIG.EVENT_BUTTON_UPDATE,
* id: this.buttonId, // have to be generated before and set in the offer method
* pressed: true
* });
* @param {Object} event
*/
onButtonUpdate: function(event){
var button = this.buttons.find(function(button){
return button.id === event.id;
});
if(event.pressed !== undefined){
button.buttonInstance.toggle(event.pressed);
}
},
registryChanged: function(pluginsData) {
// Sort plugins by group and index
var newPlugs = pluginsData.sortBy((function(value) {
return ((this.groupIndex[value.group] != undefined ? this.groupIndex[value.group] : "" ) + value.group + "" + value.index).toLowerCase();
}).bind(this));
var plugs = $A(newPlugs).findAll(function(value){
return !this.plugs.include(value) && (!value.target || value.target === ORYX.Plugins.Toolbar)
}.bind(this));
if(plugs.length<1)
return;
this.buttons = [];
ORYX.Log.trace("Creating a toolbar.")
if(!this.toolbar){
this.toolbar = new Ext.ux.SlicedToolbar({
height: 24
});
var region = this.facade.addToRegion("north", this.toolbar, "Toolbar");
}
var currentGroupsName = this.plugs.last()?this.plugs.last().group:plugs[0].group;
// Map used to store all drop down buttons of current group
var currentGroupsDropDownButton = {};
plugs.each((function(value) {
if(!value.name) {return}
this.plugs.push(value);
// Add seperator if new group begins
if(currentGroupsName != value.group) {
this.toolbar.add('-');
currentGroupsName = value.group;
currentGroupsDropDownButton = {};
}
// If an drop down group icon is provided, a split button should be used
if(value.dropDownGroupIcon){
var splitButton = currentGroupsDropDownButton[value.dropDownGroupIcon];
// Create a new split button if this is the first plugin using it
if(splitButton === undefined){
splitButton = currentGroupsDropDownButton[value.dropDownGroupIcon] = new Ext.Toolbar.SplitButton({
cls: "x-btn-icon", //show icon only
icon: value.dropDownGroupIcon,
menu: new Ext.menu.Menu({
items: [] // items are added later on
}),
listeners: {
click: function(button, event){
// The "normal" button should behave like the arrow button
if(!button.menu.isVisible() && !button.ignoreNextClick){
button.showMenu();
} else {
button.hideMenu();
}
}
}
});
this.toolbar.add(splitButton);
}
// General config button which will be used either to create a normal button
// or a check button (if toggling is enabled)
var buttonCfg = {
icon: value.icon,
text: value.name,
itemId: value.id,
handler: value.toggle ? undefined : value.functionality,
checkHandler: value.toggle ? value.functionality : undefined,
listeners: {
render: function(item){
// After rendering, a tool tip should be added to component
if (value.description) {
new Ext.ToolTip({
target: item.getEl(),
title: value.description
})
}
}
}
}
// Create buttons depending on toggle
if(value.toggle) {
var button = new Ext.menu.CheckItem(buttonCfg);
} else {
var button = new Ext.menu.Item(buttonCfg);
}
splitButton.menu.add(button);
} else if(value.addFill) {
this.toolbar.addFill();
} else { // create normal, simple button
var button = new Ext.Toolbar.Button({
icon: value.icon, // icons can also be specified inline
cls: 'x-btn-icon', // Class who shows only the icon
itemId: value.id,
tooltip: value.description, // Set the tooltip
tooltipType: 'title', // Tooltip will be shown as in the html-title attribute
handler: value.toggle ? null : value.functionality, // Handler for mouse click
enableToggle: value.toggle, // Option for enabling toggling
toggleHandler: value.toggle ? value.functionality : null // Handler for toggle (Parameters: button, active)
});
this.toolbar.add(button);
button.getEl().onclick = function() {this.blur()}
}
value['buttonInstance'] = button;
this.buttons.push(value);
}).bind(this));
this.enableButtons([]);
//TODO this should be done when resizing and adding elements!!!!
this.toolbar.calcSlices();
window.addEventListener("resize", function(event){this.toolbar.calcSlices()}.bind(this), false);
window.addEventListener("onresize", function(event){this.toolbar.calcSlices()}.bind(this), false);
},
onSelectionChanged: function(event) {
this.enableButtons(event.elements);
},
enableButtons: function(elements) {
// Show the Buttons
this.buttons.each((function(value){
value.buttonInstance.enable();
// If there is less elements than minShapes
if(value.minShape && value.minShape > elements.length)
value.buttonInstance.disable();
// If there is more elements than minShapes
if(value.maxShape && value.maxShape < elements.length)
value.buttonInstance.disable();
// If the plugin is not enabled
if(value.isEnabled && !value.isEnabled(value.buttonInstance))
value.buttonInstance.disable();
}).bind(this));
}
});
Ext.ns("Ext.ux");
Ext.ux.SlicedToolbar = Ext.extend(Ext.Toolbar, {
currentSlice: 0,
iconStandardWidth: 22, //22 px
seperatorStandardWidth: 2, //2px, minwidth for Ext.Toolbar.Fill
toolbarStandardPadding: 2,
initComponent: function(){
Ext.apply(this, {
});
Ext.ux.SlicedToolbar.superclass.initComponent.apply(this, arguments);
},
onRender: function(){
Ext.ux.SlicedToolbar.superclass.onRender.apply(this, arguments);
},
onResize: function(){
Ext.ux.SlicedToolbar.superclass.onResize.apply(this, arguments);
},
calcSlices: function(){
var slice = 0;
this.sliceMap = {};
var sliceWidth = 0;
var toolbarWidth = this.getEl().getWidth();
this.items.getRange().each(function(item, index){
//Remove all next and prev buttons
if (item.helperItem) {
item.destroy();
return;
}
var itemWidth = item.getEl().getWidth();
if(sliceWidth + itemWidth + 5 * this.iconStandardWidth > toolbarWidth){
var itemIndex = this.items.indexOf(item);
this.insertSlicingButton("next", slice, itemIndex);
if (slice !== 0) {
this.insertSlicingButton("prev", slice, itemIndex);
}
this.insertSlicingSeperator(slice, itemIndex);
slice += 1;
sliceWidth = 0;
}
this.sliceMap[item.id] = slice;
sliceWidth += itemWidth;
}.bind(this));
// Add prev button at the end
if(slice > 0){
this.insertSlicingSeperator(slice, this.items.getCount()+1);
this.insertSlicingButton("prev", slice, this.items.getCount()+1);
var spacer = new Ext.Toolbar.Spacer();
this.insertSlicedHelperButton(spacer, slice, this.items.getCount()+1);
Ext.get(spacer.id).setWidth(this.iconStandardWidth);
}
this.maxSlice = slice;
// Update view
this.setCurrentSlice(this.currentSlice);
},
insertSlicedButton: function(button, slice, index){
this.insertButton(index, button);
this.sliceMap[button.id] = slice;
},
insertSlicedHelperButton: function(button, slice, index){
button.helperItem = true;
this.insertSlicedButton(button, slice, index);
},
insertSlicingSeperator: function(slice, index){
// Align right
this.insertSlicedHelperButton(new Ext.Toolbar.Fill(), slice, index);
},
// type => next or prev
insertSlicingButton: function(type, slice, index){
var nextHandler = function(){this.setCurrentSlice(this.currentSlice+1)}.bind(this);
var prevHandler = function(){this.setCurrentSlice(this.currentSlice-1)}.bind(this);
var button = new Ext.Toolbar.Button({
cls: "x-btn-icon",
icon: ORYX.CONFIG.ROOT_PATH + "images/toolbar_"+type+".png",
handler: (type === "next") ? nextHandler : prevHandler
});
this.insertSlicedHelperButton(button, slice, index);
},
setCurrentSlice: function(slice){
if(slice > this.maxSlice || slice < 0) return;
this.currentSlice = slice;
this.items.getRange().each(function(item){
item.setVisible(slice === this.sliceMap[item.id]);
}.bind(this));
}
});/**
* Copyright (c) 2009
* Jan-Felix Schwarz, Willi Tscheschner, Nicolas Peters, Martin Czuchra, Daniel Polak
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if(!ORYX.Plugins) {
ORYX.Plugins = new Object();
}
ORYX.Plugins.ShapeMenuPlugin = {
construct: function(facade) {
this.facade = facade;
this.alignGroups = new Hash();
var containerNode = this.facade.getCanvas().getHTMLContainer();
this.shapeMenu = new ORYX.Plugins.ShapeMenu(containerNode);
this.currentShapes = [];
// Register on dragging and resizing events for show/hide of ShapeMenu
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hideShapeMenu.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END, this.showShapeMenu.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_RESIZE_START, (function(){
this.hideShapeMenu();
this.hideMorphMenu();
}).bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_RESIZE_END, this.showShapeMenu.bind(this));
// Enable DragZone
var DragZone = new Ext.dd.DragZone(containerNode.parentNode, {shadow: !Ext.isMac});
DragZone.afterDragDrop = this.afterDragging.bind(this, DragZone);
DragZone.beforeDragOver = this.beforeDragOver.bind(this, DragZone);
// Memory of created Buttons
this.createdButtons = {};
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_STENCIL_SET_LOADED, (function(){ this.registryChanged() }).bind(this));
this.timer = null;
this.resetElements = true;
},
hideShapeMenu: function(event) {
window.clearTimeout(this.timer);
this.timer = null;
this.shapeMenu.hide();
},
showShapeMenu: function( dontGenerateNew ) {
if( !dontGenerateNew || this.resetElements ){
window.clearTimeout(this.timer);
this.timer = window.setTimeout(function(){
// Close all Buttons
this.shapeMenu.closeAllButtons();
// Show the Morph Button
this.showMorphButton(this.currentShapes);
// Show the Stencil Buttons
this.showStencilButtons(this.currentShapes);
// Show the ShapeMenu
this.shapeMenu.show(this.currentShapes);
this.resetElements = false;
}.bind(this), 300)
} else {
window.clearTimeout(this.timer);
this.timer = null;
// Show the ShapeMenu
this.shapeMenu.show(this.currentShapes);
}
},
registryChanged: function(pluginsData) {
if(pluginsData) {
pluginsData = pluginsData.each(function(value) {value.group = value.group ? value.group : 'unknown'});
this.pluginsData = pluginsData.sortBy( function(value) {
return (value.group + "" + value.index);
});
}
this.shapeMenu.removeAllButtons();
this.shapeMenu.setNumberOfButtonsPerLevel(ORYX.CONFIG.SHAPEMENU_RIGHT, 2);
this.createdButtons = {};
this.createMorphMenu();
if( !this.pluginsData ){
this.pluginsData = [];
}
this.baseMorphStencils = this.facade.getRules().baseMorphs();
// Checks if the stencil set has morphing attributes
var isMorphing = this.facade.getRules().containsMorphingRules();
// Create Buttons for all Stencils of all loaded stencilsets
var stencilsets = this.facade.getStencilSets();
stencilsets.values().each((function(stencilSet){
var nodes = stencilSet.nodes();
nodes.each((function(stencil) {
// Create a button for each node
var option = {type: stencil.id(), namespace: stencil.namespace(), connectingType: true};
var button = new ORYX.Plugins.ShapeMenuButton({
callback: this.newShape.bind(this, option),
icon: stencil.icon(),
align: ORYX.CONFIG.SHAPEMENU_RIGHT,
group: 0,
//dragcallback: this.hideShapeMenu.bind(this),
msg: stencil.title() + " - " + ORYX.I18N.ShapeMenuPlugin.clickDrag
});
// Add button to shape menu
this.shapeMenu.addButton(button);
// Add to the created Button Array
this.createdButtons[stencil.namespace() + stencil.type() + stencil.id()] = button;
// Drag'n'Drop will enable
Ext.dd.Registry.register(button.node.lastChild, option);
}).bind(this));
var edges = stencilSet.edges();
edges.each((function(stencil) {
// Create a button for each edge
var option = {type: stencil.id(), namespace: stencil.namespace()};
var button = new ORYX.Plugins.ShapeMenuButton({
callback: this.newShape.bind(this, option),
// icon: isMorphing ? ORYX.PATH + "images/edges.png" : stencil.icon(),
icon: stencil.icon(),
align: ORYX.CONFIG.SHAPEMENU_RIGHT,
group: 1,
//dragcallback: this.hideShapeMenu.bind(this),
msg: (isMorphing ? ORYX.I18N.Edge : stencil.title()) + " - " + ORYX.I18N.ShapeMenuPlugin.drag
});
// Add button to shape menu
this.shapeMenu.addButton(button);
// Add to the created Button Array
this.createdButtons[stencil.namespace() + stencil.type() + stencil.id()] = button;
// Drag'n'Drop will enable
Ext.dd.Registry.register(button.node.lastChild, option);
}).bind(this));
}).bind(this));
},
createMorphMenu: function() {
this.morphMenu = new Ext.menu.Menu({
id: 'Oryx_morph_menu',
items: []
});
this.morphMenu.on("mouseover", function() {
this.morphMenuHovered = true;
}, this);
this.morphMenu.on("mouseout", function() {
this.morphMenuHovered = false;
}, this);
// Create the button to show the morph menu
var button = new ORYX.Plugins.ShapeMenuButton({
hovercallback: (ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER ? this.showMorphMenu.bind(this) : undefined),
resetcallback: (ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER ? this.hideMorphMenu.bind(this) : undefined),
callback: (ORYX.CONFIG.ENABLE_MORPHMENU_BY_HOVER ? undefined : this.toggleMorphMenu.bind(this)),
icon: ORYX.PATH + 'images/wrench_orange.png',
align: ORYX.CONFIG.SHAPEMENU_BOTTOM,
group: 0,
msg: ORYX.I18N.ShapeMenuPlugin.morphMsg
});
this.shapeMenu.setNumberOfButtonsPerLevel(ORYX.CONFIG.SHAPEMENU_BOTTOM, 1)
this.shapeMenu.addButton(button);
this.morphMenu.getEl().appendTo(button.node);
this.morphButton = button;
},
showMorphMenu: function() {
this.morphMenu.show(this.morphButton.node);
this._morphMenuShown = true;
},
hideMorphMenu: function() {
this.morphMenu.hide();
this._morphMenuShown = false;
},
toggleMorphMenu: function() {
if(this._morphMenuShown)
this.hideMorphMenu();
else
this.showMorphMenu();
},
onSelectionChanged: function(event) {
var elements = event.elements;
this.hideShapeMenu();
this.hideMorphMenu();
if( this.currentShapes.inspect() !== elements.inspect() ){
this.currentShapes = elements;
this.resetElements = true;
this.showShapeMenu();
} else {
this.showShapeMenu(true)
}
},
/**
* Show button for morphing the selected shape into another stencil
*/
showMorphButton: function(elements) {
if(elements.length != 1) return;
var possibleMorphs = this.facade.getRules().morphStencils({ stencil: elements[0].getStencil() });
possibleMorphs = possibleMorphs.select(function(morph) {
if(elements[0].getStencil().type() === "node") {
//check containment rules
return this.facade.getRules().canContain({containingShape:elements[0].parent, containedStencil:morph});
} else {
//check connect rules
return this.facade.getRules().canConnect({
sourceShape: elements[0].dockers.first().getDockedShape(),
edgeStencil: morph,
targetShape: elements[0].dockers.last().getDockedShape()
});
}
}.bind(this));
if(possibleMorphs.size()<=1) return; // if morphing to other stencils is not possible, don't show button
this.morphMenu.removeAll();
// populate morph menu with the possible morph stencils ordered by their position
possibleMorphs = possibleMorphs.sortBy(function(stencil) { return stencil.position(); });
possibleMorphs.each((function(morph) {
var menuItem = new Ext.menu.Item({
text: morph.title(),
icon: morph.icon(),
disabled: morph.id()==elements[0].getStencil().id(),
disabledClass: ORYX.CONFIG.MORPHITEM_DISABLED,
handler: (function() { this.morphShape(elements[0], morph); }).bind(this)
});
this.morphMenu.add(menuItem);
}).bind(this));
this.morphButton.prepareToShow();
},
/**
* Show buttons for creating following shapes
*/
showStencilButtons: function(elements) {
if(elements.length != 1) return;
//TODO temporaere nutzung des stencilsets
var sset = this.facade.getStencilSets()[elements[0].getStencil().namespace()];
// Get all available edges
var edges = this.facade.getRules().outgoingEdgeStencils({canvas:this.facade.getCanvas(), sourceShape:elements[0]});
// And find all targets for each Edge
var targets = new Array();
var addedEdges = new Array();
var isMorphing = this.facade.getRules().containsMorphingRules();
edges.each((function(edge) {
if (isMorphing){
if(this.baseMorphStencils.include(edge)) {
var shallAppear = true;
} else {
// if edge is member of a morph groups where none of the base morphs is in the outgoing edges
// we want to display the button (but only for the first one)
var possibleMorphs = this.facade.getRules().morphStencils({ stencil: edge });
var shallAppear = !possibleMorphs.any((function(morphStencil) {
if(this.baseMorphStencils.include(morphStencil) && edges.include(morphStencil)) return true;
return addedEdges.include(morphStencil);
}).bind(this));
}
}
if(shallAppear || !isMorphing) {
if(this.createdButtons[edge.namespace() + edge.type() + edge.id()])
this.createdButtons[edge.namespace() + edge.type() + edge.id()].prepareToShow();
addedEdges.push(edge);
}
// get all targets for this edge
targets = targets.concat(this.facade.getRules().targetStencils(
{canvas:this.facade.getCanvas(), sourceShape:elements[0], edgeStencil:edge}));
}).bind(this));
targets.uniq();
var addedTargets = new Array();
// Iterate all possible target
targets.each((function(target) {
if (isMorphing){
// continue with next target stencil
if (target.type()==="edge") return;
// continue when stencil should not shown in the shape menu
if (!this.facade.getRules().showInShapeMenu(target)) return
// if target is not a base morph
if(!this.baseMorphStencils.include(target)) {
// if target is member of a morph groups where none of the base morphs is in the targets
// we want to display the button (but only for the first one)
var possibleMorphs = this.facade.getRules().morphStencils({ stencil: target });
if(possibleMorphs.size()==0) return; // continue with next target
var baseMorphInTargets = possibleMorphs.any((function(morphStencil) {
if(this.baseMorphStencils.include(morphStencil) && targets.include(morphStencil)) return true;
return addedTargets.include(morphStencil);
}).bind(this));
if(baseMorphInTargets) return; // continue with next target
}
}
// if this is reached the button shall appear in the shape menu:
if(this.createdButtons[target.namespace() + target.type() + target.id()])
this.createdButtons[target.namespace() + target.type() + target.id()].prepareToShow();
addedTargets.push(target);
}).bind(this));
},
beforeDragOver: function(dragZone, target, event){
if (this.shapeMenu.isVisible){
this.hideShapeMenu();
}
var coord = this.facade.eventCoordinates(event.browserEvent);
var aShapes = this.facade.getCanvas().getAbstractShapesAtPosition(coord);
if(aShapes.length <= 0) {return false;}
var el = aShapes.last();
if(this._lastOverElement == el) {
return false;
} else {
// check containment rules
var option = Ext.dd.Registry.getHandle(target.DDM.currentTarget);
// revert to original options if these were modified
if(option.backupOptions) {
for(key in option.backupOptions) {
option[key] = option.backupOptions[key];
}
delete option.backupOptions;
}
var stencilSet = this.facade.getStencilSets()[option.namespace];
var stencil = stencilSet.stencil(option.type);
var candidate = aShapes.last();
if(stencil.type() === "node") {
//check containment rules
var canContain = this.facade.getRules().canContain({containingShape:candidate, containedStencil:stencil});
// if not canContain, try to find a morph which can be contained
if(!canContain) {
var possibleMorphs = this.facade.getRules().morphStencils({stencil: stencil});
for(var i=0; i Math.abs(cShape.x - pos.x)){
pos.x = cShape.x;
}
// Snapp +-20 Pixel vertical to the center
if (20 > Math.abs(cShape.y - pos.y)){
pos.y = cShape.y;
}
}
option['position'] = pos;
option['connectedShape'] = this.currentShapes[0];
if(option['connectingType']) {
var stencilset = this.facade.getStencilSets()[option.namespace];
var containedStencil = stencilset.stencil(option.type);
var args = { sourceShape: this.currentShapes[0], targetStencil: containedStencil };
option['connectingType'] = this.facade.getRules().connectMorph(args).id();
}
if (ORYX.CONFIG.SHAPEMENU_DISABLE_CONNECTED_EDGE===true) {
delete option['connectingType'];
}
var command = new ORYX.Plugins.ShapeMenuPlugin.CreateCommand(Object.clone(option), this._currentReference, pos, this);
this.facade.executeCommands([command]);
// Inform about completed Drag
this.facade.raiseEvent({type: ORYX.CONFIG.EVENT_SHAPE_MENU_CLOSE, source:sourceShape, destination:this.currentShapes});
// revert to original options if these were modified
if(option.backupOptions) {
for(key in option.backupOptions) {
option[key] = option.backupOptions[key];
}
delete option.backupOptions;
}
this._currentReference = undefined;
},
newShape: function(option, event) {
var stencilset = this.facade.getStencilSets()[option.namespace];
var containedStencil = stencilset.stencil(option.type);
if(this.facade.getRules().canContain({
containingShape:this.currentShapes.first().parent,
"containedStencil":containedStencil
})) {
option['connectedShape'] = this.currentShapes[0];
option['parent'] = this.currentShapes.first().parent;
option['containedStencil'] = containedStencil;
var args = { sourceShape: this.currentShapes[0], targetStencil: containedStencil };
var targetStencil = this.facade.getRules().connectMorph(args);
if (!targetStencil){ return }// Check if there can be a target shape
option['connectingType'] = targetStencil.id();
if (ORYX.CONFIG.SHAPEMENU_DISABLE_CONNECTED_EDGE===true) {
delete option['connectingType'];
}
var command = new ORYX.Plugins.ShapeMenuPlugin.CreateCommand(option, undefined, undefined, this);
this.facade.executeCommands([command]);
}
},
/**
* Morph a shape to a new stencil
* {Command implemented}
* @param {Shape} shape
* @param {Stencil} stencil
*/
morphShape: function(shape, stencil) {
var MorphTo = ORYX.Core.Command.extend({
construct: function(shape, stencil, facade){
this.shape = shape;
this.stencil = stencil;
this.facade = facade;
},
execute: function(){
var shape = this.shape;
var stencil = this.stencil;
var resourceId = shape.resourceId;
// Serialize all attributes
var serialized = shape.serialize();
stencil.properties().each((function(prop) {
if(prop.readonly()) {
serialized = serialized.reject(function(serProp) {
return serProp.name==prop.id();
});
}
}).bind(this));
// Get shape if already created, otherwise create a new shape
if (this.newShape){
newShape = this.newShape;
this.facade.getCanvas().add(newShape);
} else {
newShape = this.facade.createShape({
type: stencil.id(),
namespace: stencil.namespace(),
resourceId: resourceId
});
}
// calculate new bounds using old shape's upperLeft and new shape's width/height
var boundsObj = serialized.find(function(serProp){
return (serProp.prefix === "oryx" && serProp.name === "bounds");
});
var changedBounds = null;
if(!this.facade.getRules().preserveBounds(shape.getStencil())) {
var bounds = boundsObj.value.split(",");
if (parseInt(bounds[0], 10) > parseInt(bounds[2], 10)) { // if lowerRight comes first, swap array items
var tmp = bounds[0];
bounds[0] = bounds[2];
bounds[2] = tmp;
tmp = bounds[1];
bounds[1] = bounds[3];
bounds[3] = tmp;
}
bounds[2] = parseInt(bounds[0], 10) + newShape.bounds.width();
bounds[3] = parseInt(bounds[1], 10) + newShape.bounds.height();
boundsObj.value = bounds.join(",");
} else {
var height = shape.bounds.height();
var width = shape.bounds.width();
// consider the minimum and maximum size of
// the new shape
if (newShape.minimumSize) {
if (shape.bounds.height() < newShape.minimumSize.height) {
height = newShape.minimumSize.height;
}
if (shape.bounds.width() < newShape.minimumSize.width) {
width = newShape.minimumSize.width;
}
}
if(newShape.maximumSize) {
if(shape.bounds.height() > newShape.maximumSize.height) {
height = newShape.maximumSize.height;
}
if(shape.bounds.width() > newShape.maximumSize.width) {
width = newShape.maximumSize.width;
}
}
changedBounds = {
a : {
x: shape.bounds.a.x,
y: shape.bounds.a.y
},
b : {
x: shape.bounds.a.x + width,
y: shape.bounds.a.y + height
}
};
}
var oPos = shape.bounds.center();
if(changedBounds !== null) {
newShape.bounds.set(changedBounds);
}
// Set all related dockers
this.setRelatedDockers(shape, newShape);
// store DOM position of old shape
var parentNode = shape.node.parentNode;
var nextSibling = shape.node.nextSibling;
// Delete the old shape
this.facade.deleteShape(shape);
// Deserialize the new shape - Set all attributes
newShape.deserialize(serialized);
/*
* Change color to default if unchanged
* 23.04.2010
*/
if(shape.getStencil().property("oryx-bgcolor")
&& shape.properties["oryx-bgcolor"]
&& shape.getStencil().property("oryx-bgcolor").value().toUpperCase()== shape.properties["oryx-bgcolor"].toUpperCase()){
if(newShape.getStencil().property("oryx-bgcolor")){
newShape.setProperty("oryx-bgcolor", newShape.getStencil().property("oryx-bgcolor").value());
}
}
if(changedBounds !== null) {
newShape.bounds.set(changedBounds);
}
if(newShape.getStencil().type()==="edge" || (newShape.dockers.length==0 || !newShape.dockers[0].getDockedShape())) {
newShape.bounds.centerMoveTo(oPos);
}
if(newShape.getStencil().type()==="node" && (newShape.dockers.length==0 || !newShape.dockers[0].getDockedShape())) {
this.setRelatedDockers(newShape, newShape);
}
// place at the DOM position of the old shape
if(nextSibling) parentNode.insertBefore(newShape.node, nextSibling);
else parentNode.appendChild(newShape.node);
// Set selection
this.facade.setSelection([newShape]);
this.facade.getCanvas().update();
this.facade.updateSelection();
this.newShape = newShape;
},
rollback: function(){
if (!this.shape || !this.newShape || !this.newShape.parent) {return}
// Append shape to the parent
this.newShape.parent.add(this.shape);
// Set dockers
this.setRelatedDockers(this.newShape, this.shape);
// Delete new shape
this.facade.deleteShape(this.newShape);
// Set selection
this.facade.setSelection([this.shape]);
// Update
this.facade.getCanvas().update();
this.facade.updateSelection();
},
/**
* Set all incoming and outgoing edges from the shape to the new shape
* @param {Shape} shape
* @param {Shape} newShape
*/
setRelatedDockers: function(shape, newShape){
if(shape.getStencil().type()==="node") {
(shape.incoming||[]).concat(shape.outgoing||[])
.each(function(i) {
i.dockers.each(function(docker) {
if (docker.getDockedShape() == shape) {
var rPoint = Object.clone(docker.referencePoint);
// Move reference point per percent
var rPointNew = {
x: rPoint.x*newShape.bounds.width()/shape.bounds.width(),
y: rPoint.y*newShape.bounds.height()/shape.bounds.height()
};
docker.setDockedShape(newShape);
// Set reference point and center to new position
docker.setReferencePoint(rPointNew);
if(i instanceof ORYX.Core.Edge) {
docker.bounds.centerMoveTo(rPointNew);
} else {
var absXY = shape.absoluteXY();
docker.bounds.centerMoveTo({x:rPointNew.x+absXY.x, y:rPointNew.y+absXY.y});
//docker.bounds.moveBy({x:rPointNew.x-rPoint.x, y:rPointNew.y-rPoint.y});
}
}
});
});
// for attached events
if(shape.dockers.length>0&&shape.dockers.first().getDockedShape()) {
newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
newShape.dockers.first().setReferencePoint(Object.clone(shape.dockers.first().referencePoint));
}
} else { // is edge
newShape.dockers.first().setDockedShape(shape.dockers.first().getDockedShape());
newShape.dockers.first().setReferencePoint(shape.dockers.first().referencePoint);
newShape.dockers.last().setDockedShape(shape.dockers.last().getDockedShape());
newShape.dockers.last().setReferencePoint(shape.dockers.last().referencePoint);
}
}
});
// Create and execute command (for undo/redo)
var command = new MorphTo(shape, stencil, this.facade);
this.facade.executeCommands([command]);
}
}
ORYX.Plugins.ShapeMenuPlugin = ORYX.Plugins.AbstractPlugin.extend(ORYX.Plugins.ShapeMenuPlugin);
ORYX.Plugins.ShapeMenu = {
/***
* Constructor.
*/
construct: function(parentNode) {
this.bounds = undefined;
this.shapes = undefined;
this.buttons = [];
this.isVisible = false;
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", $(parentNode),
['div', {id: ORYX.Editor.provideId(), 'class':'Oryx_ShapeMenu'}]);
this.alignContainers = new Hash();
this.numberOfButtonsPerLevel = new Hash();
},
addButton: function(button) {
this.buttons.push(button);
// lazy grafting of the align containers
if(!this.alignContainers[button.align]) {
this.alignContainers[button.align] = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", this.node,
['div', {'class':button.align}]);
this.node.appendChild(this.alignContainers[button.align]);
// add event listeners for hover effect
var onBubble = false;
this.alignContainers[button.align].addEventListener(ORYX.CONFIG.EVENT_MOUSEOVER, this.hoverAlignContainer.bind(this, button.align), onBubble);
this.alignContainers[button.align].addEventListener(ORYX.CONFIG.EVENT_MOUSEOUT, this.resetAlignContainer.bind(this, button.align), onBubble);
this.alignContainers[button.align].addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.hoverAlignContainer.bind(this, button.align), onBubble);
}
this.alignContainers[button.align].appendChild(button.node);
},
deleteButton: function(button) {
this.buttons = this.buttons.without(button);
this.node.removeChild(button.node);
},
removeAllButtons: function() {
var me = this;
this.buttons.each(function(value){
if (value.node&&value.node.parentNode)
value.node.parentNode.removeChild(value.node);
});
this.buttons = [];
},
closeAllButtons: function() {
this.buttons.each(function(value){ value.prepareToHide() });
this.isVisible = false;
},
/**
* Show the shape menu
*/
show: function(shapes) {
//shapes = (shapes||[]).findAll(function(r){ return r && r.node && r.node.parent });
if(shapes.length <= 0 )
return
this.shapes = shapes;
var newBounds = undefined;
var tmpBounds = undefined;
this.shapes.each(function(value) {
var a = value.node.getScreenCTM();
var upL = value.absoluteXY();
a.e = a.a*upL.x;
a.f = a.d*upL.y;
tmpBounds = new ORYX.Core.Bounds(a.e, a.f, a.e+a.a*value.bounds.width(), a.f+a.d*value.bounds.height());
/*if(value instanceof ORYX.Core.Edge) {
tmpBounds.moveBy(value.bounds.upperLeft())
}*/
if(!newBounds)
newBounds = tmpBounds
else
newBounds.include(tmpBounds);
});
this.bounds = newBounds;
//this.bounds.moveBy({x:document.documentElement.scrollLeft, y:document.documentElement.scrollTop});
var bounds = this.bounds;
var a = this.bounds.upperLeft();
var left = 0,
leftButtonGroup = 0;
var top = 0,
topButtonGroup = 0;
var bottom = 0,
bottomButtonGroup;
var right = 0
rightButtonGroup = 0;
var size = 22;
this.getWillShowButtons().sortBy(function(button) {
return button.group;
});
this.getWillShowButtons().each(function(button){
var numOfButtonsPerLevel = this.getNumberOfButtonsPerLevel(button.align);
if (button.align == ORYX.CONFIG.SHAPEMENU_LEFT) {
// vertical levels
if(button.group!=leftButtonGroup) {
left = 0;
leftButtonGroup = button.group;
}
var x = Math.floor(left / numOfButtonsPerLevel)
var y = left % numOfButtonsPerLevel;
button.setLevel(x);
button.setPosition(a.x-5 - (x+1)*size,
a.y+numOfButtonsPerLevel*button.group*size + button.group*0.3*size + y*size);
//button.setPosition(a.x-22, a.y+left*size);
left++;
} else if (button.align == ORYX.CONFIG.SHAPEMENU_TOP) {
// horizontal levels
if(button.group!=topButtonGroup) {
top = 0;
topButtonGroup = button.group;
}
var x = top % numOfButtonsPerLevel;
var y = Math.floor(top / numOfButtonsPerLevel);
button.setLevel(y);
button.setPosition(a.x+numOfButtonsPerLevel*button.group*size + button.group*0.3*size + x*size,
a.y-5 - (y+1)*size);
top++;
} else if (button.align == ORYX.CONFIG.SHAPEMENU_BOTTOM) {
// horizontal levels
if(button.group!=bottomButtonGroup) {
bottom = 0;
bottomButtonGroup = button.group;
}
var x = bottom % numOfButtonsPerLevel;
var y = Math.floor(bottom / numOfButtonsPerLevel);
button.setLevel(y);
button.setPosition(a.x+numOfButtonsPerLevel*button.group*size + button.group*0.3*size + x*size,
a.y+bounds.height() + 5 + y*size);
bottom++;
} else {
// vertical levels
if(button.group!=rightButtonGroup) {
right = 0;
rightButtonGroup = button.group;
}
var x = Math.floor(right / numOfButtonsPerLevel)
var y = right % numOfButtonsPerLevel;
button.setLevel(x);
button.setPosition(a.x+bounds.width() + 5 + x*size,
a.y+numOfButtonsPerLevel*button.group*size + button.group*0.3*size + y*size - 5);
right++;
}
button.show();
}.bind(this));
this.isVisible = true;
},
/**
* Hide the shape menu
*/
hide: function() {
this.buttons.each(function(button){
button.hide();
});
this.isVisible = false;
//this.bounds = undefined;
//this.shape = undefined;
},
hoverAlignContainer: function(align, evt) {
this.buttons.each(function(button){
if(button.align == align) button.showOpaque();
});
},
resetAlignContainer: function(align, evt) {
this.buttons.each(function(button){
if(button.align == align) button.showTransparent();
});
},
isHover: function() {
return this.buttons.any(function(value){
return value.isHover();
});
},
getWillShowButtons: function() {
return this.buttons.findAll(function(value){return value.willShow});
},
/**
* Returns a set on buttons for that align value
* @params {String} align
* @params {String} group
*/
getButtons: function(align, group){
return this.getWillShowButtons().findAll(function(b){ return b.align == align && (group === undefined || b.group == group)})
},
/**
* Set the number of buttons to display on each level of the shape menu in the specified align group.
* Example: setNumberOfButtonsPerLevel(ORYX.CONFIG.SHAPEMENU_RIGHT, 2) causes that the buttons of the right align group
* will be rendered in 2 rows.
*/
setNumberOfButtonsPerLevel: function(align, number) {
this.numberOfButtonsPerLevel[align] = number;
},
/**
* Returns the number of buttons to display on each level of the shape menu in the specified align group.
* Default value is 1
*/
getNumberOfButtonsPerLevel: function(align) {
if(this.numberOfButtonsPerLevel[align])
return Math.min(this.getButtons(align,0).length, this.numberOfButtonsPerLevel[align]);
else
return 1;
}
}
ORYX.Plugins.ShapeMenu = Clazz.extend(ORYX.Plugins.ShapeMenu);
ORYX.Plugins.ShapeMenuButton = {
/**
* Constructor
* @param option A key map specifying the configuration options:
* id: (String) The id of the parent DOM element for the new button
* icon: (String) The url to the icon of the button
* msg: (String) A tooltip message
* caption:(String) The caption of the button (attention: button width > 22, only set for single column button layouts)
* align: (String) The direction in which the button is aligned
* group: (Integer) The button group in the specified alignment
* (buttons in the same group will be aligned side by side)
* callback: (Function) A callback that is executed when the button is clicked
* dragcallback: (Function) A callback that is executed when the button is dragged
* hovercallback: (Function) A callback that is executed when the button is hovered
* resetcallback: (Function) A callback that is executed when the button is reset
* arguments: (Array) An argument array to pass to the callback functions
*/
construct: function(option) {
if(option) {
this.option = option;
if(!this.option.arguments)
this.option.arguments = [];
} else {
//TODO error
}
this.parentId = this.option.id ? this.option.id : null;
// graft the button.
var buttonClassName = this.option.caption ? "Oryx_button_with_caption" : "Oryx_button";
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", $(this.parentId),
['div', {'class':buttonClassName}]);
var imgOptions = {src:this.option.icon};
if(this.option.msg){
imgOptions.title = this.option.msg;
}
// graft and update icon (not in grafting for ns reasons).
//TODO Enrich graft()-function to do this in one of the above steps.
if(this.option.icon)
ORYX.Editor.graft("http://www.w3.org/1999/xhtml", this.node,
['img', imgOptions]);
if(this.option.caption) {
var captionNode = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", this.node, ['span']);
ORYX.Editor.graft("http://www.w3.org/1999/xhtml", captionNode, this.option.caption);
}
var onBubble = false;
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOVER, this.hover.bind(this), onBubble);
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEOUT, this.reset.bind(this), onBubble);
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEDOWN, this.activate.bind(this), onBubble);
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEUP, this.hover.bind(this), onBubble);
this.node.addEventListener('click', this.trigger.bind(this), onBubble);
this.node.addEventListener(ORYX.CONFIG.EVENT_MOUSEMOVE, this.move.bind(this), onBubble);
this.align = this.option.align ? this.option.align : ORYX.CONFIG.SHAPEMENU_RIGHT;
this.group = this.option.group ? this.option.group : 0;
this.hide();
this.dragStart = false;
this.isVisible = false;
this.willShow = false;
this.resetTimer;
},
hide: function() {
this.node.style.display = "none";
this.isVisible = false;
},
show: function() {
this.node.style.display = "";
this.node.style.opacity = this.opacity;
this.isVisible = true;
},
showOpaque: function() {
this.node.style.opacity = 1.0;
},
showTransparent: function() {
this.node.style.opacity = this.opacity;
},
prepareToShow: function() {
this.willShow = true;
},
prepareToHide: function() {
this.willShow = false;
this.hide();
},
setPosition: function(x, y) {
this.node.style.left = x + "px";
this.node.style.top = y + "px";
},
setLevel: function(level) {
if(level==0) this.opacity = 0.5;
else if(level==1) this.opacity = 0.2;
//else if(level==2) this.opacity = 0.1;
else this.opacity = 0.0;
},
setChildWidth: function(width) {
this.childNode.style.width = width + "px";
},
reset: function(evt) {
// Delete the timeout for hiding
window.clearTimeout( this.resetTimer )
this.resetTimer = window.setTimeout( this.doReset.bind(this), 100)
if(this.option.resetcallback) {
this.option.arguments.push(evt);
var state = this.option.resetcallback.apply(this, this.option.arguments);
this.option.arguments.remove(evt);
}
},
doReset: function() {
if(this.node.hasClassName('Oryx_down'))
this.node.removeClassName('Oryx_down');
if(this.node.hasClassName('Oryx_hover'))
this.node.removeClassName('Oryx_hover');
},
activate: function(evt) {
this.node.addClassName('Oryx_down');
//Event.stop(evt);
this.dragStart = true;
},
isHover: function() {
return this.node.hasClassName('Oryx_hover') ? true: false;
},
hover: function(evt) {
// Delete the timeout for hiding
window.clearTimeout( this.resetTimer )
this.resetTimer = null;
this.node.addClassName('Oryx_hover');
this.dragStart = false;
if(this.option.hovercallback) {
this.option.arguments.push(evt);
var state = this.option.hovercallback.apply(this, this.option.arguments);
this.option.arguments.remove(evt);
}
},
move: function(evt) {
if(this.dragStart && this.option.dragcallback) {
this.option.arguments.push(evt);
var state = this.option.dragcallback.apply(this, this.option.arguments);
this.option.arguments.remove(evt);
}
},
trigger: function(evt) {
if(this.option.callback) {
//Event.stop(evt);
this.option.arguments.push(evt);
var state = this.option.callback.apply(this, this.option.arguments);
this.option.arguments.remove(evt);
}
this.dragStart = false;
},
toString: function() {
return "HTML-Button " + this.id;
}
}
ORYX.Plugins.ShapeMenuButton = Clazz.extend(ORYX.Plugins.ShapeMenuButton);
//create command for undo/redo
ORYX.Plugins.ShapeMenuPlugin.CreateCommand = ORYX.Core.Command.extend({
construct: function(option, currentReference, position, plugin){
this.option = option;
this.currentReference = currentReference;
this.position = position;
this.plugin = plugin;
this.shape;
this.edge;
this.targetRefPos;
this.sourceRefPos;
/*
* clone options parameters
*/
this.connectedShape = option.connectedShape;
this.connectingType = option.connectingType;
this.namespace = option.namespace;
this.type = option.type;
this.containedStencil = option.containedStencil;
this.parent = option.parent;
this.currentReference = currentReference;
this.shapeOptions = option.shapeOptions;
},
execute: function(){
var resume = false;
if (this.shape) {
if (this.shape instanceof ORYX.Core.Node) {
this.parent.add(this.shape);
if (this.edge) {
this.plugin.facade.getCanvas().add(this.edge);
this.edge.dockers.first().setDockedShape(this.connectedShape);
this.edge.dockers.first().setReferencePoint(this.sourceRefPos);
this.edge.dockers.last().setDockedShape(this.shape);
this.edge.dockers.last().setReferencePoint(this.targetRefPos);
}
this.plugin.facade.setSelection([this.shape]);
} else if (this.shape instanceof ORYX.Core.Edge) {
this.plugin.facade.getCanvas().add(this.shape);
this.shape.dockers.first().setDockedShape(this.connectedShape);
this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
}
resume = true;
}
else {
this.shape = this.plugin.facade.createShape(this.option);
this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined;
}
if (this.currentReference && this.position) {
if (this.shape instanceof ORYX.Core.Edge) {
if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
this.shape.dockers.last().setDockedShape(this.currentReference);
// @deprecated It now uses simply the midpoint
var upL = this.currentReference.absoluteXY();
var refPos = {
x: this.position.x - upL.x,
y: this.position.y - upL.y
};
this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint());
}
else {
this.shape.dockers.last().bounds.centerMoveTo(this.position);
//this.shape.dockers.last().update();
}
this.sourceRefPos = this.shape.dockers.first().referencePoint;
this.targetRefPos = this.shape.dockers.last().referencePoint;
} else if (this.edge){
this.sourceRefPos = this.edge.dockers.first().referencePoint;
this.targetRefPos = this.edge.dockers.last().referencePoint;
}
} else {
var containedStencil = this.containedStencil;
var connectedShape = this.connectedShape;
var bc = connectedShape.bounds;
var bs = this.shape.bounds;
var pos = bc.center();
if(containedStencil.defaultAlign()==="north") {
pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
} else if(containedStencil.defaultAlign()==="northeast") {
pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
} else if(containedStencil.defaultAlign()==="southeast") {
pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
} else if(containedStencil.defaultAlign()==="south") {
pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
} else if(containedStencil.defaultAlign()==="southwest") {
pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
} else if(containedStencil.defaultAlign()==="west") {
pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
} else if(containedStencil.defaultAlign()==="northwest") {
pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
} else {
pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
}
// Move shape to the new position
this.shape.bounds.centerMoveTo(pos);
// Move all dockers of a node to the position
if (this.shape instanceof ORYX.Core.Node){
(this.shape.dockers||[]).each(function(docker){
docker.bounds.centerMoveTo(pos);
})
}
//this.shape.update();
this.position = pos;
if (this.edge){
this.sourceRefPos = this.edge.dockers.first().referencePoint;
this.targetRefPos = this.edge.dockers.last().referencePoint;
}
}
this.plugin.facade.getCanvas().update();
this.plugin.facade.updateSelection();
if (!resume) {
// If there is a connected shape
if (this.edge){
// Try to layout it
this.plugin.doLayout(this.edge);
} else if (this.shape instanceof ORYX.Core.Edge){
// Try to layout it
this.plugin.doLayout(this.shape);
}
}
},
rollback: function(){
this.plugin.facade.deleteShape(this.shape);
if(this.edge) {
this.plugin.facade.deleteShape(this.edge);
}
//this.currentParent.update();
this.plugin.facade.setSelection(this.plugin.facade.getSelection().without(this.shape, this.edge));
}
});/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if (!ORYX.Plugins) {
ORYX.Plugins = new Object();
}
ORYX.Plugins.ShapeRepository = {
facade: undefined,
construct: function(facade) {
this.facade = facade;
this._currentParent;
this._canContain = undefined;
this._canAttach = undefined;
this.shapeList = new Ext.tree.TreeNode({
});
var panel = new Ext.tree.TreePanel({
cls:'shaperepository',
loader: new Ext.tree.TreeLoader(),
root: this.shapeList,
autoScroll:true,
rootVisible: false,
lines: false,
anchors: '0, -30'
});
var region = this.facade.addToRegion("west", panel, ORYX.I18N.ShapeRepository.title);
// Create a Drag-Zone for Drag'n'Drop
var DragZone = new Ext.dd.DragZone(this.shapeList.getUI().getEl(), {shadow: !Ext.isMac});
DragZone.afterDragDrop = this.drop.bind(this, DragZone);
DragZone.beforeDragOver = this.beforeDragOver.bind(this, DragZone);
DragZone.beforeDragEnter = function(){this._lastOverElement = false; return true}.bind(this);
// Load all Stencilssets
this.setStencilSets();
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_STENCIL_SET_LOADED, this.setStencilSets.bind(this));
},
/**
* Load all stencilsets in the shaperepository
*/
setStencilSets: function() {
// Remove all childs
var child = this.shapeList.firstChild;
while(child) {
this.shapeList.removeChild(child);
child = this.shapeList.firstChild;
}
ORYX.Log.info("stencilsets " + this.facade.getStencilSets());
// Go thru all Stencilsets and stencils
this.facade.getStencilSets().values().each((function(sset) {
// For each Stencilset create and add a new Tree-Node
var stencilSetNode;
var typeTitle = sset.title();
this.shapeList.appendChild(stencilSetNode = new Ext.tree.TreeNode({
text:typeTitle, // Stencilset Name
allowDrag:false,
allowDrop:false,
iconCls:'headerShapeRepImg',
cls:'headerShapeRep',
singleClickExpand:true}));
ORYX.Log.info("stencilSetNode " + stencilSetNode.text);
this.shapeList.appendChild(stencilSetNode);
stencilSetNode.render();
stencilSetNode.expand();
// Get Stencils from Stencilset
var stencils = sset.stencils(this.facade.getCanvas().getStencil(),
this.facade.getRules());
var treeGroups = new Hash();
// Sort the stencils according to their position and add them to the repository
stencils = stencils.sortBy(function(value) { return value.position(); } );
stencils.each((function(value) {
// Show stencils in no group if there is less than 10 shapes
//if(stencils.length <= ORYX.CONFIG.MAX_NUM_SHAPES_NO_GROUP) {
// this.createStencilTreeNode(stencilSetNode, value);
// return;
//}
// Get the groups name
var groups = value.groups();
// For each Group-Entree
groups.each((function(group) {
// If there is a new group
if(!treeGroups[group]) {
// Create a new group
treeGroups[group] = new Ext.tree.TreeNode({
text:group, // Group-Name
allowDrag:false,
allowDrop:false,
iconCls:'headerShapeRepImg', // Css-Class for Icon
cls:'headerShapeRepChild', // CSS-Class for Stencil-Group
singleClickExpand:true});
// Add the Group to the ShapeRepository
stencilSetNode.appendChild(treeGroups[group]);
treeGroups[group].render();
}
// Create the Stencil-Tree-Node
this.createStencilTreeNode(treeGroups[group], value);
}).bind(this));
// If there is no group
if(groups.length == 0) {
// Create the Stencil-Tree-Node
this.createStencilTreeNode(stencilSetNode, value);
}
}).bind(this));
}).bind(this));
},
createStencilTreeNode: function(parentTreeNode, stencil) {
// Create and add the Stencil to the Group
var newElement = new Ext.tree.TreeNode({
text: stencil.title(), // Text of the stencil
icon: stencil.icon(), // Icon of the stencil
allowDrag: false, // Don't use the Drag and Drop of Ext-Tree
allowDrop: false,
iconCls: 'ShapeRepEntreeImg', // CSS-Class for Icon
cls: 'ShapeRepEntree' // CSS-Class for the Tree-Entree
});
parentTreeNode.appendChild(newElement);
newElement.render();
var ui = newElement.getUI();
// Set the tooltip
ui.elNode.setAttributeNS(null, "title", stencil.description());
// Register the Stencil on Drag and Drop
Ext.dd.Registry.register(ui.elNode, {
node: ui.node,
handles: [ui.elNode, ui.textNode].concat($A(ui.elNode.childNodes)), // Set the Handles
isHandle: false,
type: stencil.id(), // Set Type of stencil
namespace: stencil.namespace() // Set Namespace of stencil
});
},
drop: function(dragZone, target, event) {
this._lastOverElement = undefined;
// Hide the highlighting
this.facade.raiseEvent({type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'shapeRepo.added'});
this.facade.raiseEvent({type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'shapeRepo.attached'});
// Check if drop is allowed
var proxy = dragZone.getProxy()
if(proxy.dropStatus == proxy.dropNotAllowed) { return }
// Check if there is a current Parent
if(!this._currentParent) { return }
var option = Ext.dd.Registry.getHandle(target.DDM.currentTarget);
var xy = event.getXY();
var pos = {x: xy[0], y: xy[1]};
var a = this.facade.getCanvas().node.getScreenCTM();
// Correcting the UpperLeft-Offset
pos.x -= a.e; pos.y -= a.f;
// Correcting the Zoom-Faktor
pos.x /= a.a; pos.y /= a.d;
// Correting the ScrollOffset
pos.x -= document.documentElement.scrollLeft;
pos.y -= document.documentElement.scrollTop;
// Correct position of parent
var parentAbs = this._currentParent.absoluteXY();
pos.x -= parentAbs.x;
pos.y -= parentAbs.y;
// Set position
option['position'] = pos
// Set parent
if( this._canAttach && this._currentParent instanceof ORYX.Core.Node ){
option['parent'] = undefined;
} else {
option['parent'] = this._currentParent;
}
var commandClass = ORYX.Core.Command.extend({
construct: function(option, currentParent, canAttach, position, facade){
this.option = option;
this.currentParent = currentParent;
this.canAttach = canAttach;
this.position = position;
this.facade = facade;
this.selection = this.facade.getSelection();
this.shape;
this.parent;
},
execute: function(){
if (!this.shape) {
this.shape = this.facade.createShape(option);
this.parent = this.shape.parent;
} else {
this.parent.add(this.shape);
}
if( this.canAttach && this.currentParent instanceof ORYX.Core.Node && this.shape.dockers.length > 0){
var docker = this.shape.dockers[0];
if( this.currentParent.parent instanceof ORYX.Core.Node ) {
this.currentParent.parent.add( docker.parent );
}
docker.bounds.centerMoveTo( this.position );
docker.setDockedShape( this.currentParent );
//docker.update();
}
//this.currentParent.update();
//this.shape.update();
this.facade.setSelection([this.shape]);
this.facade.getCanvas().update();
this.facade.updateSelection();
},
rollback: function(){
this.facade.deleteShape(this.shape);
//this.currentParent.update();
this.facade.setSelection(this.selection.without(this.shape));
this.facade.getCanvas().update();
this.facade.updateSelection();
}
});
var position = this.facade.eventCoordinates( event.browserEvent );
var command = new commandClass(option, this._currentParent, this._canAttach, position, this.facade);
this.facade.executeCommands([command]);
this._currentParent = undefined;
},
beforeDragOver: function(dragZone, target, event){
var coord = this.facade.eventCoordinates(event.browserEvent);
var aShapes = this.facade.getCanvas().getAbstractShapesAtPosition( coord );
if(aShapes.length <= 0) {
var pr = dragZone.getProxy();
pr.setStatus(pr.dropNotAllowed);
pr.sync();
return false;
}
var el = aShapes.last();
if(aShapes.lenght == 1 && aShapes[0] instanceof ORYX.Core.Canvas) {
return false;
} else {
// check containment rules
var option = Ext.dd.Registry.getHandle(target.DDM.currentTarget);
var stencilSet = this.facade.getStencilSets()[option.namespace];
var stencil = stencilSet.stencil(option.type);
if(stencil.type() === "node") {
var parentCandidate = aShapes.reverse().find(function(candidate) {
return (candidate instanceof ORYX.Core.Canvas
|| candidate instanceof ORYX.Core.Node
|| candidate instanceof ORYX.Core.Edge);
});
if( parentCandidate !== this._lastOverElement){
this._canAttach = undefined;
this._canContain = undefined;
}
if( parentCandidate ) {
//check containment rule
if (!(parentCandidate instanceof ORYX.Core.Canvas) && parentCandidate.isPointOverOffset(coord.x, coord.y) && this._canAttach == undefined) {
this._canAttach = this.facade.getRules().canConnect({
sourceShape: parentCandidate,
edgeStencil: stencil,
targetStencil: stencil
});
if( this._canAttach ){
// Show Highlight
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
highlightId: "shapeRepo.attached",
elements: [parentCandidate],
style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
color: ORYX.CONFIG.SELECTION_VALID_COLOR
});
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
highlightId: "shapeRepo.added"
});
this._canContain = undefined;
}
}
if(!(parentCandidate instanceof ORYX.Core.Canvas) && !parentCandidate.isPointOverOffset(coord.x, coord.y)){
this._canAttach = this._canAttach == false ? this._canAttach : undefined;
}
if( this._canContain == undefined && !this._canAttach) {
this._canContain = this.facade.getRules().canContain({
containingShape:parentCandidate,
containedStencil:stencil
});
// Show Highlight
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
highlightId:'shapeRepo.added',
elements: [parentCandidate],
color: this._canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
});
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
highlightId:"shapeRepo.attached"
});
}
this._currentParent = this._canContain || this._canAttach ? parentCandidate : undefined;
this._lastOverElement = parentCandidate;
var pr = dragZone.getProxy();
pr.setStatus(this._currentParent ? pr.dropAllowed : pr.dropNotAllowed );
pr.sync();
}
} else { //Edge
this._currentParent = this.facade.getCanvas();
var pr = dragZone.getProxy();
pr.setStatus(pr.dropAllowed);
pr.sync();
}
}
return false
}
}
ORYX.Plugins.ShapeRepository = Clazz.extend(ORYX.Plugins.ShapeRepository);
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if(!ORYX.Plugins) {
ORYX.Plugins = new Object();
}
ORYX.Plugins.PropertyWindow = {
facade: undefined,
construct: function(facade) {
// Reference to the Editor-Interface
this.facade = facade;
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SHOW_PROPERTYWINDOW, this.init.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.selectDiagram.bind(this));
this.init();
},
init: function(){
// The parent div-node of the grid
this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml",
null,
['div']);
// If the current property in focus is of type 'Date', the date format
// is stored here.
this.currentDateFormat;
// the properties array
this.popularProperties = [];
this.properties = [];
/* The currently selected shapes whos properties will shown */
this.shapeSelection = new Hash();
this.shapeSelection.shapes = new Array();
this.shapeSelection.commonProperties = new Array();
this.shapeSelection.commonPropertiesValues = new Hash();
this.updaterFlag = false;
// creating the column model of the grid.
this.columnModel = new Ext.grid.ColumnModel([
{
//id: 'name',
header: ORYX.I18N.PropertyWindow.name,
dataIndex: 'name',
width: 90,
sortable: true,
renderer: this.tooltipRenderer.bind(this)
}, {
//id: 'value',
header: ORYX.I18N.PropertyWindow.value,
dataIndex: 'value',
id: 'propertywindow_column_value',
width: 110,
editor: new Ext.form.TextField({
allowBlank: false
}),
renderer: this.renderer.bind(this)
},
{
header: "Pop",
dataIndex: 'popular',
hidden: true,
sortable: true
}
]);
// creating the store for the model.
this.dataSource = new Ext.data.GroupingStore({
proxy: new Ext.data.MemoryProxy(this.properties),
reader: new Ext.data.ArrayReader({}, [
{name: 'popular'},
{name: 'name'},
{name: 'value'},
{name: 'icons'},
{name: 'gridProperties'}
]),
sortInfo: {field: 'popular', direction: "ASC"},
sortData : function(f, direction){
direction = direction || 'ASC';
var st = this.fields.get(f).sortType;
var fn = function(r1, r2){
var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
var p1 = r1.data['popular'], p2 = r2.data['popular'];
return p1 && !p2 ? -1 : (!p1 && p2 ? 1 : (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0)));
};
this.data.sort(direction, fn);
if(this.snapshot && this.snapshot != this.data){
this.snapshot.sort(direction, fn);
}
},
groupField: 'popular'
});
this.dataSource.load();
this.grid = new Ext.grid.EditorGridPanel({
clicksToEdit: 1,
stripeRows: true,
autoExpandColumn: "propertywindow_column_value",
width:'auto',
// the column model
colModel: this.columnModel,
enableHdMenu: false,
view: new Ext.grid.GroupingView({
forceFit: true,
groupTextTpl: '{[values.rs.first().data.popular ? ORYX.I18N.PropertyWindow.oftenUsed : ORYX.I18N.PropertyWindow.moreProps]}'
}),
// the data store
store: this.dataSource
});
region = this.facade.addToRegion('east', new Ext.Panel({
width: 220,
layout: "fit",
border: false,
title: 'Properties',
items: [
this.grid
]
}), ORYX.I18N.PropertyWindow.title)
// Register on Events
this.grid.on('beforeedit', this.beforeEdit, this, true);
this.grid.on('afteredit', this.afterEdit, this, true);
this.grid.view.on('refresh', this.hideMoreAttrs, this, true);
//this.grid.on(ORYX.CONFIG.EVENT_KEYDOWN, this.keyDown, this, true);
// Renderer the Grid
this.grid.enableColumnMove = false;
//this.grid.render();
// Sort as Default the first column
//this.dataSource.sort('name');
},
// Select the Canvas when the editor is ready
selectDiagram: function() {
this.shapeSelection.shapes = [this.facade.getCanvas()];
this.setPropertyWindowTitle();
this.identifyCommonProperties();
this.createProperties();
},
specialKeyDown: function(field, event) {
// If there is a TextArea and the Key is an Enter
if(field instanceof Ext.form.TextArea && event.button == ORYX.CONFIG.KEY_Code_enter) {
// Abort the Event
return false
}
},
tooltipRenderer: function(value, p, record) {
/* Prepare tooltip */
p.cellAttr = 'title="' + record.data.gridProperties.tooltip + '"';
return value;
},
renderer: function(value, p, record) {
this.tooltipRenderer(value, p, record);
if(value instanceof Date) {
// TODO: Date-Schema is not generic
value = value.dateFormat(ORYX.I18N.PropertyWindow.dateFormat);
} else if(String(value).search("";
}
record.data.icons.each(function(each) {
if(each.name == value) {
if(each.icon) {
value = " " + value;
}
}
});
}
return value;
},
beforeEdit: function(option) {
var editorGrid = this.dataSource.getAt(option.row).data.gridProperties.editor;
var editorRenderer = this.dataSource.getAt(option.row).data.gridProperties.renderer;
if(editorGrid) {
// Disable KeyDown
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
option.grid.getColumnModel().setEditor(1, editorGrid);
editorGrid.field.row = option.row;
// Render the editor to the grid, therefore the editor is also available
// for the first and last row
editorGrid.render(this.grid);
//option.grid.getColumnModel().setRenderer(1, editorRenderer);
editorGrid.setSize(option.grid.getColumnModel().getColumnWidth(1), editorGrid.height);
} else {
return false;
}
var key = this.dataSource.getAt(option.row).data.gridProperties.propId;
this.oldValues = new Hash();
this.shapeSelection.shapes.each(function(shape){
this.oldValues[shape.getId()] = shape.properties[key];
}.bind(this));
},
afterEdit: function(option) {
//Ext1.0: option.grid.getDataSource().commitChanges();
option.grid.getStore().commitChanges();
var key = option.record.data.gridProperties.propId;
var selectedElements = this.shapeSelection.shapes;
var oldValues = this.oldValues;
var newValue = option.value;
var facade = this.facade;
// Implement the specific command for property change
var commandClass = ORYX.Core.Command.extend({
construct: function(){
this.key = key;
this.selectedElements = selectedElements;
this.oldValues = oldValues;
this.newValue = newValue;
this.facade = facade;
},
execute: function(){
this.selectedElements.each(function(shape){
if(!shape.getStencil().property(this.key).readonly()) {
shape.setProperty(this.key, this.newValue);
}
}.bind(this));
this.facade.setSelection(this.selectedElements);
this.facade.getCanvas().update();
this.facade.updateSelection();
},
rollback: function(){
this.selectedElements.each(function(shape){
shape.setProperty(this.key, this.oldValues[shape.getId()]);
}.bind(this));
this.facade.setSelection(this.selectedElements);
this.facade.getCanvas().update();
this.facade.updateSelection();
}
})
// Instanciated the class
var command = new commandClass();
// Execute the command
this.facade.executeCommands([command]);
// extended by Kerstin (start)
//
this.facade.raiseEvent({
type : ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
elements : selectedElements,
key : key,
value : option.value
});
// extended by Kerstin (end)
},
// Changes made in the property window will be shown directly
editDirectly:function(key, value){
this.shapeSelection.shapes.each(function(shape){
if(!shape.getStencil().property(key).readonly()) {
shape.setProperty(key, value);
//shape.update();
}
}.bind(this));
/* Propagate changed properties */
var selectedElements = this.shapeSelection.shapes;
this.facade.raiseEvent({
type : ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
elements : selectedElements,
key : key,
value : value
});
this.facade.getCanvas().update();
},
// if a field becomes invalid after editing the shape must be restored to the old value
updateAfterInvalid : function(key) {
this.shapeSelection.shapes.each(function(shape) {
if(!shape.getStencil().property(key).readonly()) {
shape.setProperty(key, this.oldValues[shape.getId()]);
shape.update();
}
}.bind(this));
this.facade.getCanvas().update();
},
// extended by Kerstin (start)
dialogClosed: function(data) {
var row = this.field ? this.field.row : this.row
this.scope.afterEdit({
grid:this.scope.grid,
record:this.scope.grid.getStore().getAt(row),
//value:this.scope.grid.getStore().getAt(this.row).get("value")
value: data
})
// reopen the text field of the complex list field again
this.scope.grid.startEditing(row, this.col);
},
// extended by Kerstin (end)
/**
* Changes the title of the property window panel according to the selected shapes.
*/
setPropertyWindowTitle: function() {
if(this.shapeSelection.shapes.length == 1) {
// add the name of the stencil of the selected shape to the title
region.setTitle(ORYX.I18N.PropertyWindow.title +' ('+this.shapeSelection.shapes.first().getStencil().title()+')' );
} else {
region.setTitle(ORYX.I18N.PropertyWindow.title +' ('
+ this.shapeSelection.shapes.length
+ ' '
+ ORYX.I18N.PropertyWindow.selected
+')');
}
},
/**
* Sets this.shapeSelection.commonPropertiesValues.
* If the value for a common property is not equal for each shape the value
* is left empty in the property window.
*/
setCommonPropertiesValues: function() {
this.shapeSelection.commonPropertiesValues = new Hash();
this.shapeSelection.commonProperties.each(function(property){
var key = property.prefix() + "-" + property.id();
var emptyValue = false;
var firstShape = this.shapeSelection.shapes.first();
this.shapeSelection.shapes.each(function(shape){
if(firstShape.properties[key] != shape.properties[key]) {
emptyValue = true;
}
}.bind(this));
/* Set property value */
if(!emptyValue) {
this.shapeSelection.commonPropertiesValues[key]
= firstShape.properties[key];
}
}.bind(this));
},
/**
* Returns the set of stencils used by the passed shapes.
*/
getStencilSetOfSelection: function() {
var stencils = new Hash();
this.shapeSelection.shapes.each(function(shape) {
stencils[shape.getStencil().id()] = shape.getStencil();
})
return stencils;
},
/**
* Identifies the common Properties of the selected shapes.
*/
identifyCommonProperties: function() {
this.shapeSelection.commonProperties.clear();
/*
* A common property is a property, that is part of
* the stencil definition of the first and all other stencils.
*/
var stencils = this.getStencilSetOfSelection();
var firstStencil = stencils.values().first();
var comparingStencils = stencils.values().without(firstStencil);
if(comparingStencils.length == 0) {
this.shapeSelection.commonProperties = firstStencil.properties();
} else {
var properties = new Hash();
/* put all properties of on stencil in a Hash */
firstStencil.properties().each(function(property){
properties[property.namespace() + '-' + property.id()
+ '-' + property.type()] = property;
});
/* Calculate intersection of properties. */
comparingStencils.each(function(stencil){
var intersection = new Hash();
stencil.properties().each(function(property){
if(properties[property.namespace() + '-' + property.id()
+ '-' + property.type()]){
intersection[property.namespace() + '-' + property.id()
+ '-' + property.type()] = property;
}
});
properties = intersection;
});
this.shapeSelection.commonProperties = properties.values();
}
},
onSelectionChanged: function(event) {
/* Event to call afterEdit method */
this.grid.stopEditing();
/* Selected shapes */
this.shapeSelection.shapes = event.elements;
/* Case: nothing selected */
if(event.elements.length == 0) {
this.shapeSelection.shapes = [this.facade.getCanvas()];
}
/* subselection available */
if(event.subSelection){
this.shapeSelection.shapes = [event.subSelection];
}
this.setPropertyWindowTitle();
this.identifyCommonProperties();
this.setCommonPropertiesValues();
// Create the Properties
this.createProperties();
},
/**
* Creates the properties for the ExtJS-Grid from the properties of the
* selected shapes.
*/
createProperties: function() {
this.properties = [];
this.popularProperties = [];
if(this.shapeSelection.commonProperties) {
// add new property lines
this.shapeSelection.commonProperties.each((function(pair, index) {
var key = pair.prefix() + "-" + pair.id();
// Get the property pair
var name = pair.title();
var icons = [];
var attribute = this.shapeSelection.commonPropertiesValues[key];
var editorGrid = undefined;
var editorRenderer = null;
if(!pair.readonly()){
switch(pair.type()) {
case ORYX.CONFIG.TYPE_STRING:
// If the Text is MultiLine
if(pair.wrapLines()) {
// Set the Editor as TextArea
var editorTextArea = new Ext.form.TextArea({alignment: "tl-tl", allowBlank: pair.optional(), msgTarget:'title', maxLength:pair.length()});
editorTextArea.on('keyup', function(textArea, event) {
this.editDirectly(key, textArea.getValue());
}.bind(this));
editorGrid = new Ext.Editor(editorTextArea);
} else {
// If not, set the Editor as InputField
var editorInput = new Ext.form.TextField({allowBlank: pair.optional(), msgTarget:'title', maxLength:pair.length()});
editorInput.on('keyup', function(input, event) {
this.editDirectly(key, input.getValue());
}.bind(this));
// reverts the shape if the editor field is invalid
editorInput.on('blur', function(input) {
if(!input.isValid(false))
this.updateAfterInvalid(key);
}.bind(this));
editorInput.on("specialkey", function(input, e) {
if(!input.isValid(false))
this.updateAfterInvalid(key);
}.bind(this));
editorGrid = new Ext.Editor(editorInput);
}
break;
case ORYX.CONFIG.TYPE_BOOLEAN:
// Set the Editor as a CheckBox
var editorCheckbox = new Ext.form.Checkbox();
editorCheckbox.on('check', function(c,checked) {
this.editDirectly(key, checked);
}.bind(this));
editorGrid = new Ext.Editor(editorCheckbox);
break;
case ORYX.CONFIG.TYPE_INTEGER:
// Set as an Editor for Integers
var numberField = new Ext.form.NumberField({allowBlank: pair.optional(), allowDecimals:false, msgTarget:'title', minValue: pair.min(), maxValue: pair.max()});
numberField.on('keyup', function(input, event) {
this.editDirectly(key, input.getValue());
}.bind(this));
editorGrid = new Ext.Editor(numberField);
break;
case ORYX.CONFIG.TYPE_FLOAT:
// Set as an Editor for Float
var numberField = new Ext.form.NumberField({ allowBlank: pair.optional(), allowDecimals:true, msgTarget:'title', minValue: pair.min(), maxValue: pair.max()});
numberField.on('keyup', function(input, event) {
this.editDirectly(key, input.getValue());
}.bind(this));
editorGrid = new Ext.Editor(numberField);
break;
case ORYX.CONFIG.TYPE_COLOR:
// Set as a ColorPicker
// Ext1.0 editorGrid = new gEdit(new form.ColorField({ allowBlank: pair.optional(), msgTarget:'title' }));
var editorPicker = new Ext.ux.ColorField({ allowBlank: pair.optional(), msgTarget:'title', facade: this.facade });
/*this.facade.registerOnEvent(ORYX.CONFIG.EVENT_COLOR_CHANGE, function(option) {
this.editDirectly(key, option.value);
}.bind(this));*/
editorGrid = new Ext.Editor(editorPicker);
break;
case ORYX.CONFIG.TYPE_CHOICE:
var items = pair.items();
var options = [];
items.each(function(value) {
if(value.value() == attribute)
attribute = value.title();
options.push([value.icon(), value.title(), value.value()]);
icons.push({
name: value.title(),
icon: value.icon()
});
});
var store = new Ext.data.SimpleStore({
fields: [{name: 'icon'},
{name: 'title'},
{name: 'value'} ],
data : options // from states.js
});
// Set the grid Editor
var editorCombo = new Ext.form.ComboBox({
tpl: '
{[(values.icon) ? "" : ""]} {title}
',
store: store,
displayField:'title',
valueField: 'value',
typeAhead: true,
mode: 'local',
triggerAction: 'all',
selectOnFocus:true
});
editorCombo.on('select', function(combo, record, index) {
this.editDirectly(key, combo.getValue());
}.bind(this))
editorGrid = new Ext.Editor(editorCombo);
break;
case ORYX.CONFIG.TYPE_DATE:
var currFormat = ORYX.I18N.PropertyWindow.dateFormat
if(!(attribute instanceof Date))
attribute = Date.parseDate(attribute, currFormat)
editorGrid = new Ext.Editor(new Ext.form.DateField({ allowBlank: pair.optional(), format:currFormat, msgTarget:'title'}));
break;
case ORYX.CONFIG.TYPE_TEXT:
var cf = new Ext.form.ComplexTextField({
allowBlank: pair.optional(),
dataSource:this.dataSource,
grid:this.grid,
row:index,
facade:this.facade
});
cf.on('dialogClosed', this.dialogClosed, {scope:this, row:index, col:1,field:cf});
editorGrid = new Ext.Editor(cf);
break;
case ORYX.CONFIG.TYPE_MODEL_LINK:
var cf = new Ext.form.ComplexModelLinkField({
allowBlank: pair.optional(),
dataSource:this.dataSource,
grid:this.grid,
row:index,
facade:this.facade
});
cf.on('dialogClosed', this.dialogClosed, {scope:this, row:index, col:1,field:cf});
editorGrid = new Ext.Editor(cf);
break;
case ORYX.CONFIG.TYPE_LISTENER:
var cf = new Ext.form.ListenerDefinitionField({
allowBlank: pair.optional(),
dataSource:this.dataSource,
grid:this.grid,
row:index,
facade:this.facade
});
cf.on('dialogClosed', this.dialogClosed, {scope:this, row:index, col:1,field:cf});
editorGrid = new Ext.Editor(cf);
break;
// extended by Kerstin (start)
case ORYX.CONFIG.TYPE_COMPLEX:
var cf = new Ext.form.ComplexListField({ allowBlank: pair.optional()}, pair.complexItems(), key, this.facade);
cf.on('dialogClosed', this.dialogClosed, {scope:this, row:index, col:1,field:cf});
editorGrid = new Ext.Editor(cf);
break;
// extended by Kerstin (end)
case ORYX.CONFIG.TYPE_MULTIPLECOMPLEX:
var cf = new Ext.form.MultipleComplexListField({ allowBlank: pair.optional()}, pair.complexItems(), key, this.facade);
cf.on('dialogClosed', this.dialogClosed, {scope:this, row:index, col:1,field:cf});
editorGrid = new Ext.Editor(cf);
break;
// extended by Gerardo (Start)
case "CPNString":
var editorInput = new Ext.form.TextField(
{
allowBlank: pair.optional(),
msgTarget:'title',
maxLength:pair.length(),
enableKeyEvents: true
});
editorInput.on('keyup', function(input, event) {
this.editDirectly(key, input.getValue());
console.log(input.getValue());
alert("huhu");
}.bind(this));
editorGrid = new Ext.Editor(editorInput);
break;
// extended by Gerardo (End)
default:
var editorInput = new Ext.form.TextField({ allowBlank: pair.optional(), msgTarget:'title', maxLength:pair.length(), enableKeyEvents: true});
editorInput.on('keyup', function(input, event) {
this.editDirectly(key, input.getValue());
}.bind(this));
editorGrid = new Ext.Editor(editorInput);
}
// Register Event to enable KeyDown
editorGrid.on('beforehide', this.facade.enableEvent.bind(this, ORYX.CONFIG.EVENT_KEYDOWN));
editorGrid.on('specialkey', this.specialKeyDown.bind(this));
} else if(pair.type() === ORYX.CONFIG.TYPE_URL || pair.type() === ORYX.CONFIG.TYPE_DIAGRAM_LINK){
attribute = String(attribute).search("http") !== 0 ? ("http://" + attribute) : attribute;
attribute = "" + attribute.split("://")[1] + ""
}
// Push to the properties-array
if(pair.visible()) {
// Popular Properties are those which are set to be popular
if (pair.popular()) {
pair.setPopular();
}
if(pair.popular()) {
this.popularProperties.push([pair.popular(), name, attribute, icons, {
editor: editorGrid,
propId: key,
type: pair.type(),
tooltip: pair.description(),
renderer: editorRenderer
}]);
}
else {
this.properties.push([pair.popular(), name, attribute, icons, {
editor: editorGrid,
propId: key,
type: pair.type(),
tooltip: pair.description(),
renderer: editorRenderer
}]);
}
}
}).bind(this));
}
this.setProperties();
},
hideMoreAttrs: function(panel) {
// TODO: Implement the case that the canvas has no attributes
if (this.properties.length <= 0){ return }
// collapse the "more attr" group
this.grid.view.toggleGroup(this.grid.view.getGroupId(this.properties[0][0]), false);
// prevent the more attributes pane from closing after a attribute has been edited
this.grid.view.un("refresh", this.hideMoreAttrs, this);
},
setProperties: function() {
var props = this.popularProperties.concat(this.properties);
this.dataSource.loadData(props);
}
}
ORYX.Plugins.PropertyWindow = Clazz.extend(ORYX.Plugins.PropertyWindow);
/**
* Editor for complex type
*
* When starting to edit the editor, it creates a new dialog where new attributes
* can be specified which generates json out of this and put this
* back to the input field.
*
* This is implemented from Kerstin Pfitzner
*
* @param {Object} config
* @param {Object} items
* @param {Object} key
* @param {Object} facade
*/
Ext.form.ComplexListField = function(config, items, key, facade){
Ext.form.ComplexListField.superclass.constructor.call(this, config);
this.items = items;
this.key = key;
this.facade = facade;
};
/**
* This is a special trigger field used for complex properties.
* The trigger field opens a dialog that shows a list of properties.
* The entered values will be stored as trigger field value in the JSON format.
*/
Ext.extend(Ext.form.ComplexListField, Ext.form.TriggerField, {
/**
* @cfg {String} triggerClass
* An additional CSS class used to style the trigger button. The trigger will always get the
* class 'x-form-trigger' and triggerClass will be appended if specified.
*/
triggerClass: 'x-form-complex-trigger',
readOnly: true,
emptyText: ORYX.I18N.PropertyWindow.clickIcon,
/**
* Builds the JSON value from the data source of the grid in the dialog.
*/
buildValue: function() {
var ds = this.grid.getStore();
ds.commitChanges();
if (ds.getCount() == 0) {
return "";
}
var jsonString = "[";
for (var i = 0; i < ds.getCount(); i++) {
var data = ds.getAt(i);
jsonString += "{";
for (var j = 0; j < this.items.length; j++) {
var key = this.items[j].id();
jsonString += key + ':' + ("" + data.get(key)).toJSON();
if (j < (this.items.length - 1)) {
jsonString += ", ";
}
}
jsonString += "}";
if (i < (ds.getCount() - 1)) {
jsonString += ", ";
}
}
jsonString += "]";
jsonString = "{'totalCount':" + ds.getCount().toJSON() +
", 'items':" + jsonString + "}";
return Object.toJSON(jsonString.evalJSON());
},
/**
* Returns the field key.
*/
getFieldKey: function() {
return this.key;
},
/**
* Returns the actual value of the trigger field.
* If the table does not contain any values the empty
* string will be returned.
*/
getValue : function(){
// return actual value if grid is active
if (this.grid) {
return this.buildValue();
} else if (this.data == undefined) {
return "";
} else {
return this.data;
}
},
/**
* Sets the value of the trigger field.
* In this case this sets the data that will be shown in
* the grid of the dialog.
*
* @param {Object} value The value to be set (JSON format or empty string)
*/
setValue: function(value) {
if (value.length > 0 && value.indexOf('<') == -1) {
// set only if this.data not set yet
// only to initialize the grid
if (this.data == undefined) {
this.data = value;
}
}
},
/**
* Returns false. In this way key events will not be propagated
* to other elements.
*
* @param {Object} event The keydown event.
*/
keydownHandler: function(event) {
return false;
},
/**
* The listeners of the dialog.
*
* If the dialog is hidded, a dialogClosed event will be fired.
* This has to be used by the parent element of the trigger field
* to reenable the trigger field (focus gets lost when entering values
* in the dialog).
*/
dialogListeners : {
show : function(){ // retain focus styling
this.onFocus();
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keydownHandler.bind(this));
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
return;
},
hide : function(){
var dl = this.dialogListeners;
this.dialog.un("show", dl.show, this);
this.dialog.un("hide", dl.hide, this);
this.dialog.destroy(true);
this.grid.destroy(true);
delete this.grid;
delete this.dialog;
this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keydownHandler.bind(this));
this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
// store data and notify parent about the closed dialog
// parent has to handel this event and start editing the text field again
this.fireEvent('dialogClosed', this.data);
Ext.form.ComplexListField.superclass.setValue.call(this, this.data);
}
},
/**
* Builds up the initial values of the grid.
*
* @param {Object} recordType The record type of the grid.
* @param {Object} items The initial items of the grid (columns)
*/
buildInitial: function(recordType, items) {
var initial = new Hash();
for (var i = 0; i < items.length; i++) {
var id = items[i].id();
initial[id] = items[i].value();
}
var RecordTemplate = Ext.data.Record.create(recordType);
return new RecordTemplate(initial);
},
/**
* Builds up the column model of the grid. The parent element of the
* grid.
*
* Sets up the editors for the grid columns depending on the
* type of the items.
*
* @param {Object} parent The
*/
buildColumnModel: function(parent) {
var cols = [];
for (var i = 0; i < this.items.length; i++) {
var id = this.items[i].id();
var header = this.items[i].name();
var width = this.items[i].width();
var type = this.items[i].type();
var editor;
if (type == ORYX.CONFIG.TYPE_STRING) {
editor = new Ext.form.TextField({ allowBlank : this.items[i].optional(), width : width});
} else if (type == ORYX.CONFIG.TYPE_CHOICE) {
var items = this.items[i].items();
var select = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parent, ['select', {style:'display:none'}]);
var optionTmpl = new Ext.Template('');
items.each(function(value){
optionTmpl.append(select, {value:value.value()});
});
editor = new Ext.form.ComboBox(
{ typeAhead: true, triggerAction: 'all', transform:select, lazyRender:true, msgTarget:'title', width : width});
} else if (type == ORYX.CONFIG.TYPE_BOOLEAN) {
editor = new Ext.form.Checkbox( { width : width } );
} else if (type == ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
cols.push({
id: id,
header: header,
dataIndex: id,
resizable: true,
editor: editor,
width: width
});
}
return new Ext.grid.ColumnModel(cols);
},
/**
* After a cell was edited the changes will be commited.
*
* @param {Object} option The option that was edited.
*/
afterEdit: function(option) {
option.grid.getStore().commitChanges();
},
/**
* Before a cell is edited it has to be checked if this
* cell is disabled by another cell value. If so, the cell editor will
* be disabled.
*
* @param {Object} option The option to be edited.
*/
beforeEdit: function(option) {
var state = this.grid.getView().getScrollState();
var col = option.column;
var row = option.row;
var editId = this.grid.getColumnModel().config[col].id;
// check if there is an item in the row, that disables this cell
for (var i = 0; i < this.items.length; i++) {
// check each item that defines a "disable" property
var item = this.items[i];
var disables = item.disable();
if (disables != undefined) {
// check if the value of the column of this item in this row is equal to a disabling value
var value = this.grid.getStore().getAt(row).get(item.id());
for (var j = 0; j < disables.length; j++) {
var disable = disables[j];
if (disable.value == value) {
for (var k = 0; k < disable.items.length; k++) {
// check if this value disables the cell to select
// (id is equals to the id of the column to edit)
var disItem = disable.items[k];
if (disItem == editId) {
this.grid.getColumnModel().getCellEditor(col, row).disable();
return;
}
}
}
}
}
}
this.grid.getColumnModel().getCellEditor(col, row).enable();
//this.grid.getView().restoreScroll(state);
},
onCellClick: function() {
alert()
},
/**
* If the trigger was clicked a dialog has to be opened
* to enter the values for the complex property.
*/
onTriggerClick : function(){
if(this.disabled){
return;
}
//if(!this.dialog) {
var dialogWidth = 0;
var recordType = [];
for (var i = 0; i < this.items.length; i++) {
var id = this.items[i].id();
var width = this.items[i].width();
var type = this.items[i].type();
if (type == ORYX.CONFIG.TYPE_CHOICE || type == ORYX.CONFIG.TYPE_COMPLEX) {
type = ORYX.CONFIG.TYPE_STRING;
}
dialogWidth += width;
recordType[i] = {name:id, type:type};
}
if (dialogWidth > 800) {
dialogWidth = 800;
}
dialogWidth += 22;
var data = this.data;
if (data == "") {
// empty string can not be parsed
data = "{}";
}
var ds = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(eval("(" + data + ")")),
reader: new Ext.data.JsonReader({
root: 'items',
totalProperty: 'totalCount'
}, recordType)
});
ds.load();
var cm = this.buildColumnModel();
//var gridHead = this.grid.getView().getHeaderPanel(true);
var toolbar = new Ext.Toolbar(
[{
text: ORYX.I18N.PropertyWindow.add,
icon: '../editor/images/add.png',
iconCls: "x-dummy",
handler: function(){
var ds = this.grid.getStore();
var index = ds.getCount();
this.grid.stopEditing();
var p = this.buildInitial(recordType, this.items);
ds.insert(index, p);
ds.commitChanges();
this.grid.startEditing(index, 0);
}.bind(this)
},{
text: ORYX.I18N.PropertyWindow.rem,
icon: '../editor/images/delete.png',
iconCls: "x-dummy",
handler : function(){
var ds = this.grid.getStore();
var selection = this.grid.getSelectionModel().getSelectedCell();
if (selection == undefined) {
return;
}
this.grid.getSelectionModel().clearSelections();
this.grid.stopEditing();
var record = ds.getAt(selection[0]);
ds.remove(record);
ds.commitChanges();
}.bind(this)
}]);
this.grid = new Ext.grid.EditorGridPanel({
store: ds,
cm: cm,
stripeRows: true,
clicksToEdit: 1,
autoScroll: true,
stateId: "x-editor-complex-grid",
anchor: "100% 100%",
enableHdMenu: false, // Disable header menu
selModel: new Ext.grid.CellSelectionModel(),
tbar:toolbar
});
// Basic Dialog
this.dialog = new Ext.Window({
autoCreate: true,
layout: "anchor",
title: ORYX.I18N.PropertyWindow.complex,
height: 350,
width: dialogWidth,
modal:true,
collapsible:false,
fixedcenter: true,
shadow:true,
proxyDrag: true,
keys:[{
key: 27,
fn: function(){
this.dialog.hide
}.bind(this)
}],
items:[this.grid],
bodyStyle:"background-color:#FFFFFF",
buttons: [{
text: ORYX.I18N.PropertyWindow.ok,
handler: function(){
this.grid.stopEditing();
// store dialog input
this.data = this.buildValue();
this.dialog.hide()
}.bind(this)
}, {
text: ORYX.I18N.PropertyWindow.cancel,
handler: function(){
this.dialog.hide()
}.bind(this)
}]
});
this.dialog.on(Ext.apply({}, this.dialogListeners, {
scope:this
}));
this.dialog.show();
this.grid.on('beforeedit', this.beforeEdit, this, true);
this.grid.on('afteredit', this.afterEdit, this, true);
this.grid.render();
/*} else {
this.dialog.show();
}*/
}
});
Ext.form.MultipleComplexListField = function(config, items, key, facade){
Ext.form.MultipleComplexListField.superclass.constructor.call(this, config);
this.items = items;
this.key = key;
this.facade = facade;
};
Ext.extend(Ext.form.MultipleComplexListField, Ext.form.TriggerField, {
/**
* @cfg {String} triggerClass
* An additional CSS class used to style the trigger button. The trigger will always get the
* class 'x-form-trigger' and triggerClass will be appended if specified.
*/
triggerClass: 'x-form-complex-trigger',
readOnly: true,
emptyText: ORYX.I18N.PropertyWindow.clickIcon,
/**
* Builds the JSON value from the data source of the grid in the dialog.
*/
buildValue: function() {
var ds = this.grid.getStore();
ds.commitChanges();
if (ds.getCount() == 0) {
return "";
}
var jsonString = "[";
for (var i = 0; i < ds.getCount(); i++) {
var data = ds.getAt(i);
jsonString += "{";
for (var j = 0; j < this.items.length; j++) {
var key = this.items[j].id();
jsonString += key + ':' + ("" + data.get(key)).toJSON();
if (j < (this.items.length - 1)) {
jsonString += ", ";
}
}
jsonString += "}";
if (i < (ds.getCount() - 1)) {
jsonString += ", ";
}
}
jsonString += "]";
jsonString = "{'totalCount':" + ds.getCount().toJSON() +
", 'items':" + jsonString + "}";
return Object.toJSON(jsonString.evalJSON());
},
/**
* Returns the field key.
*/
getFieldKey: function() {
return this.key;
},
/**
* Returns the actual value of the trigger field.
* If the table does not contain any values the empty
* string will be returned.
*/
getValue : function(){
// return actual value if grid is active
if (this.grid) {
return this.buildValue();
} else if (this.data == undefined) {
return "";
} else {
return this.data;
}
},
/**
* Sets the value of the trigger field.
* In this case this sets the data that will be shown in
* the grid of the dialog.
*
* @param {Object} value The value to be set (JSON format or empty string)
*/
setValue: function(value) {
if (value.length > 0 && value.indexOf('<') == -1) {
// set only if this.data not set yet
// only to initialize the grid
if (this.data == undefined) {
this.data = value;
}
}
},
/**
* Returns false. In this way key events will not be propagated
* to other elements.
*
* @param {Object} event The keydown event.
*/
keydownHandler: function(event) {
return false;
},
/**
* The listeners of the dialog.
*
* If the dialog is hidded, a dialogClosed event will be fired.
* This has to be used by the parent element of the trigger field
* to reenable the trigger field (focus gets lost when entering values
* in the dialog).
*/
dialogListeners : {
show : function(){ // retain focus styling
this.onFocus();
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keydownHandler.bind(this));
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
return;
},
hide : function(){
var dl = this.dialogListeners;
this.dialog.un("show", dl.show, this);
this.dialog.un("hide", dl.hide, this);
this.dialog.destroy(true);
this.grid.destroy(true);
delete this.grid;
delete this.dialog;
this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keydownHandler.bind(this));
this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
// store data and notify parent about the closed dialog
// parent has to handel this event and start editing the text field again
this.fireEvent('dialogClosed', this.data);
Ext.form.ComplexListField.superclass.setValue.call(this, this.data);
}
},
/**
* Builds up the initial values of the grid.
*
* @param {Object} recordType The record type of the grid.
* @param {Object} items The initial items of the grid (columns)
*/
buildInitial: function(recordType, items) {
var initial = new Hash();
for (var i = 0; i < items.length; i++) {
var id = items[i].id();
initial[id] = items[i].value();
}
var RecordTemplate = Ext.data.Record.create(recordType);
return new RecordTemplate(initial);
},
/**
* Builds up the column model of the grid. The parent element of the
* grid.
*
* Sets up the editors for the grid columns depending on the
* type of the items.
*
* @param {Object} parent The
*/
buildColumnModel: function(parent) {
var cols = [];
for (var i = 0; i < this.items.length; i++) {
var id = this.items[i].id();
var header = this.items[i].name();
var width = this.items[i].width();
var type = this.items[i].type();
var editor;
if (type == ORYX.CONFIG.TYPE_STRING) {
editor = new Ext.form.TextField({ allowBlank : this.items[i].optional(), width : width});
} else if (type == ORYX.CONFIG.TYPE_CHOICE) {
var items = this.items[i].items();
var select = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parent, ['select', {style:'display:none'}]);
var optionTmpl = new Ext.Template('');
items.each(function(value){
optionTmpl.append(select, {value:value.value()});
});
editor = new Ext.form.ComboBox(
{ typeAhead: true, triggerAction: 'all', transform:select, lazyRender:true, msgTarget:'title', width : width});
} else if (type == ORYX.CONFIG.TYPE_BOOLEAN) {
editor = new Ext.form.Checkbox( { width : width } );
} else if (type == ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
cols.push({
id: id,
header: header,
dataIndex: id,
resizable: true,
editor: editor,
width: width
});
}
return new Ext.grid.ColumnModel(cols);
},
buildSecondColumnModel: function(parent) {
var cols = [];
for (var i = 0; i < this.items.length; i++) {
var parentType = this.items[i].type();
if (parentType != ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
var complexItems = this.items[i].complexItems();
for (var j = 0; j < complexItems.length; j++) {
var id = complexItems[j].id();
var header = complexItems[j].name();
var type = complexItems[j].type();
var width = complexItems[j].width();
var editor;
if (type == ORYX.CONFIG.TYPE_STRING) {
editor = new Ext.form.TextField({ allowBlank : complexItems[j].optional(), width : width});
} else if (type == ORYX.CONFIG.TYPE_CHOICE) {
var items = complexItems[j].items();
var select = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parent, ['select', {style:'display:none'}]);
var optionTmpl = new Ext.Template('');
items.each(function(value){
optionTmpl.append(select, {value:value.value()});
});
editor = new Ext.form.ComboBox(
{ typeAhead: true, triggerAction: 'all', transform:select, lazyRender:true, msgTarget:'title', width : width});
} else if (type == ORYX.CONFIG.TYPE_BOOLEAN) {
editor = new Ext.form.Checkbox( { width : width } );
} else if (type == ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
cols.push({
id: id,
header: header,
dataIndex: id,
resizable: true,
editor: editor,
width: width
});
}
}
return new Ext.grid.ColumnModel(cols);
},
/**
* After a cell was edited the changes will be commited.
*
* @param {Object} option The option that was edited.
*/
afterEdit: function(option) {
option.grid.getStore().commitChanges();
},
afterEditSecondGrid: function(option) {
this.secondGrid.getStore().commitChanges();
var selectedCell = this.grid.getSelectionModel().getSelectedCell();
if (selectedCell.length == 2) {
var row = selectedCell[0];
var jsonString = "[";
for (var i = 0; i < this.secondGrid.getStore().getCount(); i++) {
var data = this.secondGrid.getStore().getAt(i);
jsonString += "{";
for (var j = 0; j < this.items.length; j++) {
var parentType = this.items[j].type();
if (parentType != ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
var complexItems = this.items[j].complexItems();
for (var k = 0; k < complexItems.length; k++) {
var key = complexItems[k].id();
jsonString += key + ':' + ("" + data.get(key)).toJSON();
if (k < (complexItems.length - 1)) {
jsonString += ", ";
}
}
}
jsonString += "}";
if (i < (this.secondGrid.getStore().getCount() - 1)) {
jsonString += ", ";
}
}
jsonString += "]";
jsonString = "{'totalCount':" + this.secondGrid.getStore().getCount().toJSON() +
", 'items':" + jsonString + "}";
var activeRecord = this.grid.getStore().getAt(row);
activeRecord.set(this.complexFieldId, Object.toJSON(jsonString.evalJSON()));
}
},
/**
* Before a cell is edited it has to be checked if this
* cell is disabled by another cell value. If so, the cell editor will
* be disabled.
*
* @param {Object} option The option to be edited.
*/
beforeEdit: function(option) {
var state = this.grid.getView().getScrollState();
var col = option.column;
var row = option.row;
var editId = this.grid.getColumnModel().config[col].id;
// check if there is an item in the row, that disables this cell
for (var i = 0; i < this.items.length; i++) {
if (this.items[i].type() == ORYX.CONFIG.TYPE_COMPLEX) {
continue;
}
// check each item that defines a "disable" property
var item = this.items[i];
var disables = item.disable();
if (disables != undefined) {
// check if the value of the column of this item in this row is equal to a disabling value
var value = this.grid.getStore().getAt(row).get(item.id());
for (var j = 0; j < disables.length; j++) {
var disable = disables[j];
if (disable.value == value) {
for (var k = 0; k < disable.items.length; k++) {
// check if this value disables the cell to select
// (id is equals to the id of the column to edit)
var disItem = disable.items[k];
if (disItem == editId) {
this.grid.getColumnModel().getCellEditor(col, row).disable();
return;
}
}
}
}
}
}
this.grid.getColumnModel().getCellEditor(col, row).enable();
//this.grid.getView().restoreScroll(state);
},
/**
* If the trigger was clicked a dialog has to be opened
* to enter the values for the complex property.
*/
onTriggerClick : function(){
if(this.disabled){
return;
}
//if(!this.dialog) {
var dialogWidth = 0;
var recordType = [];
var secondRecordType = [];
this.complexFieldId;
var complexItems;
for (var i = 0; i < this.items.length; i++) {
var id = this.items[i].id();
var width = this.items[i].width();
var type = this.items[i].type();
if (type == ORYX.CONFIG.TYPE_CHOICE) {
type = ORYX.CONFIG.TYPE_STRING;
}
if (type == ORYX.CONFIG.TYPE_COMPLEX) {
this.complexFieldId = id;
type = ORYX.CONFIG.TYPE_STRING;
complexItems = this.items[i].complexItems();
for (var j = 0; j < complexItems.length; j++) {
var secondId = complexItems[j].id();
var secondWidth = complexItems[j].width();
var secondType = complexItems[j].type();
if (secondType == ORYX.CONFIG.TYPE_CHOICE) {
secondType = ORYX.CONFIG.TYPE_STRING;
}
secondRecordType[j] = {name:secondId, type:secondType};
}
} else {
dialogWidth += width;
}
recordType[i] = {name:id, type:type};
}
if (dialogWidth > 800) {
dialogWidth = 800;
}
dialogWidth += 22;
var data = this.data;
if (data == "") {
// empty string can not be parsed
data = "{}";
}
var ds = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(eval("(" + data + ")")),
reader: new Ext.data.JsonReader({
root: 'items',
totalProperty: 'totalCount'
}, recordType)
});
ds.load();
var secondDs = new Ext.data.Store();
var cm = this.buildColumnModel();
var secondCm = this.buildSecondColumnModel();
//var gridHead = this.grid.getView().getHeaderPanel(true);
var toolbar = new Ext.Toolbar(
[{
text: ORYX.I18N.PropertyWindow.add,
icon: '../editor/images/add.png',
iconCls: "x-dummy",
handler: function(){
var ds = this.grid.getStore();
var index = ds.getCount();
this.grid.stopEditing();
var p = this.buildInitial(recordType, this.items);
ds.insert(index, p);
ds.commitChanges();
this.grid.startEditing(index, 0);
}.bind(this)
},{
text: ORYX.I18N.PropertyWindow.rem,
icon: '../editor/images/delete.png',
iconCls: "x-dummy",
handler : function(){
var ds = this.grid.getStore();
var selection = this.grid.getSelectionModel().getSelectedCell();
if (selection == undefined) {
return;
}
this.grid.getSelectionModel().clearSelections();
this.grid.stopEditing();
var record = ds.getAt(selection[0]);
ds.remove(record);
ds.commitChanges();
}.bind(this)
}]);
this.grid = new Ext.grid.EditorGridPanel({
store: ds,
cm: cm,
stripeRows: true,
clicksToEdit: 1,
autoScroll: true,
stateId: "x-editor-complex-grid",
height: 280,
anchor: '100%',
enableHdMenu: false, // Disable header menu
selModel: new Ext.grid.CellSelectionModel({
listeners: {
scope: this,
cellselect: function(sm,row,col) {
secondDs.removeAll(true);
var masterRecord = ds.getAt(row);
if (masterRecord.get('' + this.complexFieldId) != undefined && masterRecord.get('' + this.complexFieldId) != 'undefined') {
var newDs = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(eval("(" + masterRecord.get('' + this.complexFieldId) + ")")),
reader: new Ext.data.JsonReader({
root: 'items',
totalProperty: 'totalCount'
}, secondRecordType)
});
newDs.load();
secondDs.removeAll(true);
for (var i = 0; i < newDs.getCount(); i++) {
secondDs.add(newDs.getAt(i));
}
}
secondDs.commitChanges();
}
}
}),
tbar:toolbar
});
var secondToolbar = new Ext.Toolbar(
[{
text: ORYX.I18N.PropertyWindow.add,
icon: '../editor/images/add.png',
iconCls: "x-dummy",
handler: function(){
var ds = this.secondGrid.getStore();
var index = ds.getCount();
this.secondGrid.stopEditing();
var p = this.buildInitial(secondRecordType, complexItems);
ds.insert(index, p);
ds.commitChanges();
this.secondGrid.startEditing(index, 0);
}.bind(this)
},{
text: ORYX.I18N.PropertyWindow.rem,
icon: '../editor/images/delete.png',
iconCls: "x-dummy",
handler : function(){
var ds = this.secondGrid.getStore();
var selection = this.secondGrid.getSelectionModel().getSelectedCell();
if (selection == undefined) {
return;
}
this.secondGrid.getSelectionModel().clearSelections();
this.secondGrid.stopEditing();
var record = ds.getAt(selection[0]);
ds.remove(record);
ds.commitChanges();
this.afterEditSecondGrid();
}.bind(this)
}]);
this.secondGrid = new Ext.grid.EditorGridPanel({
store: secondDs,
cm: secondCm,
stripeRows: true,
clicksToEdit: 1,
autoScroll: true,
stateId: "x-editor-complex-grid",
height: 280,
anchor: '100%',
enableHdMenu: false, // Disable header menu
selModel: new Ext.grid.CellSelectionModel(),
tbar:secondToolbar
});
// Basic Dialog
this.dialog = new Ext.Window({
autoCreate: true,
layout: "anchor",
title: ORYX.I18N.PropertyWindow.complex,
height: 600,
width: dialogWidth,
modal:true,
collapsible:false,
fixedcenter: true,
shadow:true,
proxyDrag: true,
keys:[{
key: 27,
fn: function(){
this.dialog.hide
}.bind(this)
}],
items:[this.grid, this.secondGrid],
bodyStyle:"background-color:#FFFFFF",
buttons: [{
text: ORYX.I18N.PropertyWindow.ok,
handler: function(){
this.grid.stopEditing();
// store dialog input
this.data = this.buildValue();
this.dialog.hide()
}.bind(this)
}]
});
this.dialog.on(Ext.apply({}, this.dialogListeners, {
scope:this
}));
this.dialog.show();
this.grid.on('beforeedit', this.beforeEdit, this, true);
this.grid.on('afteredit', this.afterEdit, this, true);
this.secondGrid.on('beforeedit', this.beforeEdit, this, true);
this.secondGrid.on('afteredit', this.afterEditSecondGrid, this, true);
this.grid.render();
this.secondGrid.render();
if (ds.getCount() > 0) {
this.grid.getSelectionModel().select(0,0);
}
/*} else {
this.dialog.show();
}*/
}
});
Ext.form.ComplexTextField = Ext.extend(Ext.form.TriggerField, {
defaultAutoCreate : {tag: "textarea", rows:1, style:"height:16px;overflow:hidden;" },
/**
* If the trigger was clicked a dialog has to be opened
* to enter the values for the complex property.
*/
onTriggerClick : function(){
if(this.disabled){
return;
}
var grid = new Ext.form.TextArea({
anchor : '100% 100%',
value : this.value,
listeners : {
focus: function(){
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
}.bind(this)
}
})
// Basic Dialog
var dialog = new Ext.Window({
layout : 'anchor',
autoCreate : true,
title : ORYX.I18N.PropertyWindow.text,
height : 500,
width : 500,
modal : true,
collapsible : false,
fixedcenter : true,
shadow : true,
proxyDrag : true,
keys:[{
key : 27,
fn : function(){
dialog.hide()
}.bind(this)
}],
items :[grid],
listeners :{
hide: function(){
this.fireEvent('dialogClosed', this.value);
//this.focus.defer(10, this);
dialog.destroy();
}.bind(this)
},
buttons : [{
text: ORYX.I18N.PropertyWindow.ok,
handler: function(){
// store dialog input
var value = grid.getValue();
this.setValue(value);
this.dataSource.getAt(this.row).set('value', value)
this.dataSource.commitChanges()
dialog.hide()
}.bind(this)
}, {
text: ORYX.I18N.PropertyWindow.cancel,
handler: function(){
this.setValue(this.value);
dialog.hide()
}.bind(this)
}]
});
dialog.show();
grid.render();
this.grid.stopEditing();
grid.focus( false, 100 );
}
});
Ext.form.ComplexModelLinkField = Ext.extend(Ext.form.TriggerField, {
onTriggerClick : function() {
if(this.disabled){
return;
}
var CallElementDef = Ext.data.Record.create([{
name: 'name'
}, {
name: 'revision'
}, {
name: 'imgsrc'
}]);
var calldefsProxy = new Ext.data.MemoryProxy({
root: []
});
var calldefs = new Ext.data.Store({
autoDestroy: true,
reader: new Ext.data.JsonReader({
root: "root"
}, CallElementDef),
proxy: calldefsProxy,
sorters: [{
property: 'name',
direction:'ASC'
}]
});
calldefs.load();
var loadProcessesMask = new Ext.LoadMask(Ext.getBody(), {msg:'Loading Process Information'});
loadProcessesMask.show();
Ext.Ajax.request({
url: ORYX.CONFIG.MODEL_LIST_URL,
method: 'GET',
success: function(response) {
try {
loadProcessesMask.hide();
if(response.responseText.length > 0 && response.responseText != "false") {
var responseJson = Ext.decode(response.responseText);
var modelsArray = responseJson["models"];
for(var i = 0; i < modelsArray.length; i++) {
var modelObj = modelsArray[i];
calldefs.add(new CallElementDef({
name: modelObj["name"],
revision: modelObj["revision"],
imgsrc: ORYX.CONFIG.SERVER_MODEL_HANDLER + "/" + modelObj["modelId"] + "/svg"
}));
}
calldefs.commitChanges();
var gridId = Ext.id();
var grid = new Ext.grid.EditorGridPanel({
store: calldefs,
id: gridId,
stripeRows: true,
cm: new Ext.grid.ColumnModel([new Ext.grid.RowNumberer(), {
id: 'pid',
header: 'Process Id',
width: 200,
dataIndex: 'name',
editor: new Ext.form.TextField({ allowBlank: false, disabled: true })
}, {
id: 'revi',
header: 'Revision',
width: 200,
dataIndex: 'revision',
editor: new Ext.form.TextField({ allowBlank: false, disabled: true })
},{
id: 'pim',
header: 'Process Image',
width: 250,
dataIndex: 'imgsrc',
renderer: function(val) {
if(val && val.length > 0) {
return '
';
} else {
return "
Process image not available.
";
}
}
}]),
autoHeight: true
});
grid.on('afterrender', function(e) {
if(this.value.length > 0) {
var index = 0;
var val = this.value;
var mygrid = grid;
calldefs.data.each(function() {
if(this.data['name'] == val) {
mygrid.getSelectionModel().select(index, 1);
}
index++;
});
}
}.bind(this));
var calledElementsPanel = new Ext.Panel({
id: 'calledElementsPanel',
title: '
";
}
}
}]),
autoHeight: true
});
var calledElementsPanel = new Ext.Panel({
id: 'calledElementsPanel',
title: '
Select Process Id and click "Save" to select.
',
layout:'column',
items:[
grid
],
layoutConfig: {
columns: 1
},
defaults: {
columnWidth: 1.0
}
});
var dialog = new Ext.Window({
layout : 'anchor',
autoCreate : true,
title : 'Editor for Called Elements',
height : 350,
width : 680,
modal : true,
collapsible : false,
fixedcenter : true,
shadow : true,
resizable : true,
proxyDrag : true,
autoScroll : true,
keys:[{
key : 27,
fn : function(){
dialog.hide()
}.bind(this)
}],
items :[calledElementsPanel],
listeners :{
hide: function(){
this.fireEvent('dialogClosed', this.value);
dialog.destroy();
}.bind(this)
},
buttons : [{
text: 'Save',
handler: function(){
if(grid.getSelectionModel().getSelectedCell() != null) {
var selectedIndex = grid.getSelectionModel().getSelectedCell()[0];
var outValue = calldefs.getAt(selectedIndex).data['name'];
grid.stopEditing();
grid.getView().refresh();
this.setValue(outValue);
this.dataSource.getAt(this.row).set('value', outValue)
this.dataSource.commitChanges()
dialog.hide()
} else {
Ext.Msg.alert('Please select a process id.');
}
}.bind(this)
}, {
text: ORYX.I18N.PropertyWindow.cancel,
handler: function(){
this.setValue(this.value);
dialog.hide()
}.bind(this)
}]
});
dialog.show();
grid.render();
grid.fireEvent('afterrender');
this.grid.stopEditing();
grid.focus( false, 100 );
}
});/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if (!ORYX.Plugins) {
ORYX.Plugins = new Object();
}
/**
* 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));
},
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;
}
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.parentNode.parentNode;
window.myParent=parentNode
var scrollNode = parentNode.firstChild;
var svgRootNode = scrollNode.firstChild.firstChild;
// The buttons
var buttonGrow = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parentNode, ['div', { 'class': 'canvas_resize_indicator canvas_resize_indicator_grow' + ' ' + position ,'title':ORYX.I18N.RESIZE.tipGrow+ORYX.I18N.RESIZE[position]}]);
var buttonShrink = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", parentNode, ['div', { 'class': 'canvas_resize_indicator canvas_resize_indicator_shrink' + ' ' + position ,'title':ORYX.I18N.RESIZE.tipShrink+ORYX.I18N.RESIZE[position]}]);
// 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){
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.layerX !== undefined ? event.layerX : event.offsetX;
var Y=event.layerY !== undefined ? event.layerY : event.offsetY;
if((X - scrollNode.scrollLeft)<0 ||Ext.isSafari){ X+=scrollNode.scrollLeft;}
if((Y - scrollNode.scrollTop )<0 ||Ext.isSafari){ Y+=scrollNode.scrollTop ;}
//
if(position == "N"){
return Y < offSetWidth+scrollNode.firstChild.offsetTop;
} else if(position == "W"){
return X < offSetWidth + scrollNode.firstChild.offsetLeft;
} else if(position == "E"){
//other offset
var offsetRight=(scrollNode.offsetWidth-(scrollNode.firstChild.offsetLeft + scrollNode.firstChild.offsetWidth));
if(offsetRight<0)offsetRight=0;
return X > scrollNode.scrollWidth-offsetRight-offSetWidth;
} else if(position == "S"){
//other offset
var offsetDown=(scrollNode.offsetHeight-(scrollNode.firstChild.offsetTop + scrollNode.firstChild.offsetHeight));
if(offsetDown<0)offsetDown=0;
return Y > scrollNode.scrollHeight -offsetDown- offSetWidth;
}
return false;
}
var showButtons = (function() {
buttonGrow.show();
var x1, y1, x2, y2;
try {
var bb = this.canvas.getRootNode().childNodes[1].getBBox();
x1 = bb.x;
y1 = bb.y;
x2 = bb.x + bb.width;
y2 = bb.y + bb.height;
} catch(e) {
this.canvas.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 w = canvas.bounds.width();
var h = canvas.bounds.height();
var isEmpty = canvas.getChildNodes().size()==0;
if(position=="N" && (y1>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL || (isEmpty && h>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL))) buttonShrink.show();
else if(position=="E" && (w-x2)>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL) buttonShrink.show();
else if(position=="S" && (h-y2)>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL) buttonShrink.show();
else if(position=="W" && (x1>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL || (isEmpty && w>ORYX.CONFIG.CANVAS_RESIZE_INTERVAL))) 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
scrollNode.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.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 (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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_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
props = props.findAll(function(item){ return !item.readonly() && item.type() == ORYX.CONFIG.TYPE_STRING });
// 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 trans = this.facade.getCanvas().rootNode.lastChild.getScreenCTM();
evtCoord.x *= trans.a;
evtCoord.y *= trans.d;
var diff = labels.collect(function(label){
var center = this.getCenterPosition( label.node );
var len = Math.sqrt( Math.pow(center.x - evtCoord.x, 2) + Math.pow(center.y - evtCoord.y, 2));
return {diff: len, label: label}
}.bind(this));
diff.sort(function(a, b){ return a.diff > b.diff })
nearestLabel = diff[0].label;
}
}
// Get the particular property for the label
var prop = props.find(function(item){ return item.refToView().any(function(toView){ return nearestLabel.id == shape.id + toView })});
// Set all particular config values
var htmlCont = this.facade.getCanvas().getHTMLContainer().id;
// Get the center position from the nearest label
var width = Math.min(Math.max(100, shape.bounds.width()), 200);
var center = this.getCenterPosition( nearestLabel.node );
center.x -= (width/2);
var propId = prop.prefix() + "-" + prop.id();
// Set the config values for the TextField/Area
var config = {
renderTo : htmlCont,
value : shape.properties[propId],
x : (center.x < 10) ? 10 : center.x,
y : center.y,
width : width,
style : 'position:absolute',
allowBlank : prop.optional(),
maxLength : prop.length(),
emptyText : prop.title(),
cls : 'x_form_text_set_absolute'
}
// Depending on the property, generate
// ether an TextArea or TextField
if(prop.wrapLines()) {
config.y -= (60/2);
config['grow'] = true;
this.shownTextField = new Ext.form.TextArea(config);
} else {
config.y -= (20/2);
this.shownTextField = new Ext.form.TextField(config);
}
//focus
this.shownTextField.focus();
// Define event handler
// Blur -> Destroy
// Change -> Set new values
this.shownTextField.on( 'blur', this.destroy.bind(this) )
this.shownTextField.on( 'change', function(node, value){
var currentEl = shape;
var oldValue = currentEl.properties[propId];
var newValue = value;
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();
}
})
// Instanciated the class
var command = new commandClass();
// Execute the command
this.facade.executeCommands([command]);
}
}.bind(this) )
// Diable the keydown in the editor (that when hitting the delete button, the shapes not get deleted)
this.facade.disableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
},
getCenterPosition: function(svgNode){
if (!svgNode) { return {x:0, y:0} }
var center = {x: 0, y:0 };
var trans,scale,transLocal,bounds;
var useParent = false;
try {
if (('hidden' === svgNode.getAttributeNS(null, 'visibility')&&svgNode.childNodes.length>0)
||svgNode.childNodes.length === 0) {
useParent = true;
}
var el = useParent ? svgNode.parentNode : svgNode;
trans = el.getTransformToElement(this.facade.getCanvas().rootNode.lastChild);
scale = this.facade.getCanvas().rootNode.lastChild.getScreenCTM();
transLocal = el.getTransformToElement(el.parentNode);
} catch(e){
return {x:0, y:0}
}
center.x = trans.e - transLocal.e;
center.y = trans.f - transLocal.f;
try {
bounds = svgNode.getBBox();
if (!useParent&&!(bounds.x<-1000)) {
bounds.y -= 1;
} else {
bounds = {x:Number(svgNode.getAttribute('x')), y:Number(svgNode.getAttribute('y')), width:0, height:0};
}
} catch(e){
bounds = {x:Number(svgNode.getAttribute('x')), y:Number(svgNode.getAttribute('y')), width:0, height:0};
}
center.x += bounds.x;
center.y += bounds.y;
center.x += bounds.width/2;
center.y += bounds.height/2;
center.x *= scale.a;
center.y *= scale.d;
return center;
},
hide: function(e){
if (this.shownTextField && (!e || !this.shownTextField.el || e.target !== this.shownTextField.el.dom)) {
this.shownTextField.onBlur();
}
},
destroy: function(e){
if( this.shownTextField ){
this.shownTextField.destroy();
delete this.shownTextField;
this.facade.enableEvent(ORYX.CONFIG.EVENT_KEYDOWN);
}
}
});
/**
* Copyright (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* This plugin offer the functionality of undo/redo
* Therewith the command pattern is used.
*
* A Plugin which want that the changes could get undo/redo has
* to implement a command-class (which implements the method .execute(), .rollback()).
* Those instance of class must be execute thru the facade.executeCommands(). If so,
* those command get stored here in the undo/redo stack and can get reset/restore.
*
**/
if (!ORYX.Plugins)
ORYX.Plugins = new Object();
ORYX.Plugins.Undo = Clazz.extend({
// Defines the facade
facade : undefined,
// Defines the undo/redo Stack
undoStack : [],
redoStack : [],
// Constructor
construct: function(facade){
this.facade = facade;
// Offers the functionality of undo
this.facade.offer({
name : ORYX.I18N.Undo.undo,
description : ORYX.I18N.Undo.undoDesc,
icon : ORYX.PATH + "images/arrow_undo.png",
keyCodes: [{
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
keyCode: 90,
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
}
],
functionality : this.doUndo.bind(this),
group : ORYX.I18N.Undo.group,
isEnabled : function(){ return this.undoStack.length > 0 }.bind(this),
index : 0
});
// Offers the functionality of redo
this.facade.offer({
name : ORYX.I18N.Undo.redo,
description : ORYX.I18N.Undo.redoDesc,
icon : ORYX.PATH + "images/arrow_redo.png",
keyCodes: [{
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
keyCode: 89,
keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
}
],
functionality : this.doRedo.bind(this),
group : ORYX.I18N.Undo.group,
isEnabled : function(){ return this.redoStack.length > 0 }.bind(this),
index : 1
});
// Register on event for executing commands --> store all commands in a stack
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_EXECUTE_COMMANDS, this.handleExecuteCommands.bind(this) );
},
/**
* Stores all executed commands in a stack
*
* @param {Object} evt
*/
handleExecuteCommands: function( evt ){
// If the event has commands
if( !evt.commands ){ return }
// Add the commands to a undo stack ...
this.undoStack.push( evt.commands );
// ...and delete the redo stack
this.redoStack = [];
// Update
this.facade.getCanvas().update();
this.facade.updateSelection();
},
/**
* Does the undo
*
*/
doUndo: function(){
// Get the last commands
var lastCommands = this.undoStack.pop();
if( lastCommands ){
// Add the commands to the redo stack
this.redoStack.push( lastCommands );
// Rollback every command
for(var i=lastCommands.length-1; i>=0; --i){
lastCommands[i].rollback();
}
// Update and refresh the canvas
//this.facade.getCanvas().update();
//this.facade.updateSelection();
this.facade.raiseEvent({
type : ORYX.CONFIG.EVENT_UNDO_ROLLBACK,
commands: lastCommands
});
// Update
this.facade.getCanvas().update();
this.facade.updateSelection();
}
},
/**
* Does the redo
*
*/
doRedo: function(){
// Get the last commands from the redo stack
var lastCommands = this.redoStack.pop();
if( lastCommands ){
// Add this commands to the undo stack
this.undoStack.push( lastCommands );
// Execute those commands
lastCommands.each(function(command){
command.execute();
});
// Update and refresh the canvas
//this.facade.getCanvas().update();
//this.facade.updateSelection();
this.facade.raiseEvent({
type : ORYX.CONFIG.EVENT_UNDO_EXECUTE,
commands: lastCommands
});
// Update
this.facade.getCanvas().update();
this.facade.updateSelection();
}
}
});
/**
* Copyright (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if (!ORYX.Plugins)
ORYX.Plugins = new Object();
ORYX.Plugins.Save = Clazz.extend({
facade: undefined,
processURI: undefined,
changeSymbol : "*",
construct: function(facade){
this.facade = facade;
this.facade.offer({
'name': ORYX.I18N.Save.save,
'functionality': this.save.bind(this,false),
'group': ORYX.I18N.Save.group,
'icon': ORYX.PATH + "images/disk.png",
'description': ORYX.I18N.Save.saveDesc,
'index': 1,
'minShape': 0,
'maxShape': 0,
keyCodes: [{
metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
keyCode: 83, // s-Keycode
keyAction: ORYX.CONFIG.KEY_ACTION_UP
}
]
});
document.addEventListener("keydown", function(e){
if (e.ctrlKey&&e.keyCode === 83){
Event.stop(e);
}
}, false)
/*this.facade.offer({
'name': ORYX.I18N.Save.saveAs,
'functionality': this.save.bind(this,true),
'group': ORYX.I18N.Save.group,
'icon': ORYX.PATH + "images/disk_multi.png",
'description': ORYX.I18N.Save.saveAsDesc,
'index': 2,
'minShape': 0,
'maxShape': 0
}); */
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) );
// --> 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);
},
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;
}
},
onUnLoad: function(){
if(this.changeDifference !== 0 || (this.facade.getModelMetaData()['new'] && this.facade.getCanvas().getChildShapes().size() > 0)) {
return ORYX.I18N.Save.unsavedData;
}
},
saveSynchronously: function(forceNew, modelInfo){
if (!modelInfo) {
return;
}
var modelMeta = this.facade.getModelMetaData();
var reqURI = modelMeta.modelHandler;
// Get the stencilset
var ss = this.facade.getStencilSets().values()[0]
var typeTitle = ss.title();
// Define Default values
var name = (modelMeta["new"] && modelMeta.name === "") ? ORYX.I18N.Save.newProcess : modelInfo.name;
var defaultData = {title:Signavio.Utils.escapeHTML(name||""), summary:Signavio.Utils.escapeHTML(modelInfo.description||""), type:typeTitle, url: reqURI, namespace: modelInfo.model.stencilset.namespace, comment: '' }
// Create a Template
var dialog = new Ext.XTemplate(
// TODO find some nice words here -- copy from above ;)
'')
// Create the callback for the template
callback = function(form){
// raise loading enable event
/*this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_ENABLE,
text: ORYX.I18N.Save.saving
});*/
var title = form.elements["title"].value.strip();
title = title.length == 0 ? defaultData.title : title;
var summary = form.elements["summary"].value.strip();
summary = summary.length == 0 ? defaultData.summary : summary;
var namespace = form.elements["namespace"].value.strip();
namespace = namespace.length == 0 ? defaultData.namespace : namespace;
modelMeta.name = title;
modelMeta.description = summary;
modelMeta.parent = modelInfo.parent;
modelMeta.namespace = namespace;
//added changing title of page after first save, but with the changed flag
if(!forceNew) window.document.title = this.changeSymbol + title + " | " + ORYX.CONFIG.APPNAME;
// Get json
var json = this.facade.getJSON();
var glossary = [];
//Support for glossary
if (this.facade.hasGlossaryExtension) {
Ext.apply(json, ORYX.Core.AbstractShape.JSONHelper);
var allNodes = json.getChildShapes(true);
var orders = {};
this.facade.getGlossary().each(function(entry){
if ("undefined" == typeof orders[entry.shape.resourceId+"-"+entry.property.prefix()+"-"+entry.property.id()]){
orders[entry.shape.resourceId+"-"+entry.property.prefix()+"-"+entry.property.id()] = 0;
}
// Add entry
glossary.push({
itemId : entry.glossary,
elementId : entry.shape.resourceId,
propertyId : entry.property.prefix()+"-"+entry.property.id(),
order : orders[entry.shape.resourceId+"-"+entry.property.prefix()+"-"+entry.property.id()]++
});
// Replace the property with the generated glossary url
/*var rId = entry.shape.resourceId;
var pKe = entry.property.id();
for (var i=0, size=allNodes.length; i 0) {
entry.property.refToView().each(function(ref){
var node = $(entry.shape.id+""+ref);
if (node)
node.setAttribute("oryx:glossaryIds", entry.glossary + ";")
})
}
}.bind(this))
// Set the json as string
json = json.serialize();
} else {
json = Ext.encode(json);
}
// Set the glossaries as string
glossary = Ext.encode(glossary);
var selection = this.facade.getSelection();
this.facade.setSelection([]);
// Get the serialized svg image source
var svgClone = this.facade.getCanvas().getSVGRepresentation(true);
this.facade.setSelection(selection);
if (this.facade.getCanvas().properties["oryx-showstripableelements"] === false) {
var stripOutArray = svgClone.getElementsByClassName("stripable-element");
for (var i=stripOutArray.length-1; i>=0; i--) {
stripOutArray[i].parentNode.removeChild(stripOutArray[i]);
}
}
// Remove all forced stripable elements
var stripOutArray = svgClone.getElementsByClassName("stripable-element-force");
for (var i=stripOutArray.length-1; i>=0; i--) {
stripOutArray[i].parentNode.removeChild(stripOutArray[i]);
}
// Parse dom to string
var svgDOM = DataManager.serialize(svgClone);
var params = {
json_xml: json,
svg_xml: svgDOM,
name: title,
type: defaultData.type,
parent: modelMeta.parent,
description: summary,
glossary_xml: glossary,
namespace: modelMeta.namespace,
views: Ext.util.JSON.encode(modelMeta.views || [])
};
var success = false;
var successFn = function(transport) {
var loc = transport.getResponseHeader.location;
if (!this.processURI && loc) {
this.processURI = loc;
}
if( forceNew ){
var resJSON = transport.responseText.evalJSON();
var modelURL = location.href.substring(0, location.href.indexOf(location.search)) + '?id=' + resJSON.href.substring(7);
var newURLWin = new Ext.Window({
title: ORYX.I18N.Save.savedAs,
bodyStyle: "background:white;padding:10px",
width: 'auto',
height: 'auto',
html:"
"+ORYX.I18N.Save.savedDescription+":
" + modelURL + "",
buttons:[{text:'Ok',handler:function(){newURLWin.destroy()}}]
});
newURLWin.show();
window.open(modelURL);
}
//show saved status
/*this.facade.raiseEvent({
type:ORYX.CONFIG.EVENT_LOADING_STATUS,
text:ORYX.I18N.Save.saved
});*/
success = true;
win.close();
if (success) {
// Reset changes
this.changeDifference = 0;
this.updateTitle();
var resJSON = transport.responseText.evalJSON();
if(resJSON.modelId) {
modelMeta.modelId = resJSON.modelId;
}
if(modelMeta["new"]) {
modelMeta["new"] = false;
}
}
delete this.saving;
}.bind(this);
var failure = function(transport) {
// raise loading disable event.
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_DISABLE
});
win.close();
if(transport.status && transport.status === 401) {
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.notAuthorized).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).center().syncSize();
} else if(transport.status && transport.status === 403) {
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.noRights).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).center().syncSize();
} else if(transport.statusText === "transaction aborted") {
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.transAborted).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).center().syncSize();
} else if(transport.statusText === "communication failure") {
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.comFailed).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).center().syncSize();
} else {
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.failed).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).center().syncSize();
}
delete this.saving;
}.bind(this);
if(modelMeta["new"]) {
// Send the request out
params.id = modelMeta.modelId;
this.sendSaveRequest('POST', reqURI, params, true, successFn, failure);
} else if(forceNew) {
this.sendSaveRequest('POST', reqURI, params, true, successFn, failure);
} else {
params.id = modelMeta.modelId;
// Send the request out
this.sendSaveRequest('PUT', reqURI, params, false, successFn, failure);
}
}.bind(this);
// Create a new window
win = new Ext.Window({
id : 'Propertie_Window',
width : 'auto',
height : 'auto',
title : forceNew ? ORYX.I18N.Save.saveAsTitle : ORYX.I18N.Save.save,
modal : true,
resize : false,
bodyStyle: 'background:#FFFFFF',
html : dialog.apply( defaultData ),
defaultButton: 0,
buttons:[{
text: ORYX.I18N.Save.saveBtn,
handler: function(){
win.body.mask(ORYX.I18N.Save.pleaseWait, "x-waiting-box");
window.setTimeout(function(){
callback($('edit_model'));
}.bind(this), 10);
},
listeners:{
render:function(){
this.focus();
}
}
},{
text: ORYX.I18N.Save.close,
handler: function(){
win.close();
}.bind(this)
}],
listeners: {
close: function(){
win.destroy();
delete this.saving;
}.bind(this)
}
});
win.show();
},
/**
* Get the model data and call the success callback
*
* @param {Function} success Success callback
*/
retrieveModelData: function(success){
var onComplete = function(){
Ext.getBody().unmask();
}
var modelMeta = this.facade.getModelMetaData();
new Ajax.Request("../service/model/" + modelMeta.modelId + "/json", {
method: 'get',
asynchronous: true,
requestHeaders: {
"Accept":"application/json"
},
encoding: 'UTF-8',
onSuccess: (function(transport) {
modelInfo = (transport.responseText||"{}").evalJSON();
onComplete();
success(modelInfo);
}).bind(this),
onException: function(){
// raise loading disable event.
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_DISABLE
});
delete this.saving;
onComplete();
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.exception).setIcon(Ext.Msg.ERROR).getDialog().setWidth(260).syncSize();
}.bind(this),
onFailure: (function(transport) {
// raise loading disable event.
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_DISABLE
});
delete this.saving;
onComplete();
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.failed).setIcon(Ext.Msg.ERROR).getDialog().setWidth(260).syncSize();
}).bind(this),
on401: (function(transport) {
// raise loading disable event.
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_DISABLE
});
delete this.saving;
onComplete();
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.notAuthorized).setIcon(Ext.Msg.WARNING).getDialog().setWidth(260).syncSize();
}).bind(this),
on403: (function(transport) {
// raise loading disable event.
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_LOADING_DISABLE
});
delete this.saving;
onComplete();
Ext.Msg.alert(ORYX.I18N.Oryx.title, ORYX.I18N.Save.noRights).setIcon(Ext.Msg.ERROR).getDialog().setWidth(260).syncSize();
}).bind(this)
});
},
sendSaveRequest: function(method, url, params, forceNew, success, failure){
var saveUri;
if(forceNew == false) {
saveUri = "../service/model/" + params.id + "/save";
} else {
saveUri = "../service/model/new";
}
// Send the request to the server.
Ext.Ajax.request({
url : saveUri,
method : method,
timeout : 1800000,
disableCaching : true,
headers : {'Accept':"application/json", 'Content-Type':'charset=UTF-8'},
params : params,
success : success,
failure : failure
});
},
/**
* Saves the current process to the server.
*/
save: function(forceNew, event){
// Check if currently is saving
if (this.saving){
return;
}
this.saving = true;
this.facade.raiseEvent({
type: ORYX.CONFIG.EVENT_ABOUT_TO_SAVE
});
// ... save synchronously
window.setTimeout((function(){
var meta = this.facade.getModelMetaData();
// Check if new...
if (meta["new"]){
this.saveSynchronously(forceNew, meta);
} else {
Ext.getBody().mask(ORYX.I18N.Save.retrieveData, "x-waiting-box");
// ...otherwise, get the current model data first.
this.retrieveModelData(this.saveSynchronously.bind(this, forceNew))
}
}).bind(this), 10);
return true;
}
});
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
/**
* @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
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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;
// Set the offset position of dragging
var upL = this.dragBounds.upperLeft();
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};
// 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);
});
// /* If only edges are selected, check if they are movable. An Edge is
// * movable in case it is not docked
// */
// if(this._onlyEdges) {
// this.currentShapes.each(function(edge) {
// if(edge.isDocked()) {
// this.edgesMovable = false;
// throw $break;
// }
// }.bind(this));
// }
// 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 underlyingNodes = $A(this.facade.getCanvas().getAbstractShapesAtPosition(this.facade.eventCoordinates(event)));
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 0) {
labels.each(function(label) {
label.show();
});
}
}//);
// Do this recursive
//shape.children.each((function(value) {
for(var i=0; i 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", $(this.parentId),
['div', {'class': 'resizer_'+ this.orientation, style:'left:0px; top:0px;'}]);
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.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") {
var oldLR = {x: this.bounds.lowerRight().x, y: this.bounds.lowerRight().y};
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;
if(this.orientation==="northwest") {
upL.x -= 13;
upL.y -= 26;
} 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 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) {
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 */
node.setAttributeNS(null, "d", this.getPathEdge(options.elements[0].dockers));
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
node.setAttributeNS(null, "d", this.getPathByElements(options.elements));
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 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
if(!ORYX.Plugins)
ORYX.Plugins = new Object();
ORYX.Plugins.DragDocker = Clazz.extend({
/**
* Constructor
* @param {Object} Facade: The Facade of the Editor
*/
construct: function(facade) {
this.facade = facade;
// Set the valid and invalid color
this.VALIDCOLOR = ORYX.CONFIG.SELECTION_VALID_COLOR;
this.INVALIDCOLOR = ORYX.CONFIG.SELECTION_INVALID_COLOR;
// Define Variables
this.shapeSelection = undefined;
this.docker = undefined;
this.dockerParent = undefined;
this.dockerSource = undefined;
this.dockerTarget = undefined;
this.lastUIObj = undefined;
this.isStartDocker = undefined;
this.isEndDocker = undefined;
this.undockTreshold = 10;
this.initialDockerPosition = undefined;
this.outerDockerNotMoved = undefined;
this.isValid = false;
// For the Drag and Drop
// Register on MouseDown-Event on a Docker
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DOCKERDRAG, this.handleDockerDrag.bind(this));
// Register on over/out to show / hide a docker
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOVER, this.handleMouseOver.bind(this));
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEOUT, this.handleMouseOut.bind(this));
},
/**
* MouseOut Handler
*
*/
handleMouseOut: function(event, uiObj) {
// If there is a Docker, hide this
if(!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
uiObj.hide()
} else if(!this.docker && uiObj instanceof ORYX.Core.Edge) {
uiObj.dockers.each(function(docker){
docker.hide();
})
}
},
/**
* MouseOver Handler
*
*/
handleMouseOver: function(event, uiObj) {
// If there is a Docker, show this
if(!this.docker && uiObj instanceof ORYX.Core.Controls.Docker) {
uiObj.show()
} else if(!this.docker && uiObj instanceof ORYX.Core.Edge) {
uiObj.dockers.each(function(docker){
docker.show();
})
}
},
/**
* DockerDrag Handler
* delegates the uiEvent of the drag event to the mouseDown function
*/
handleDockerDrag: function(event, uiObj) {
this.handleMouseDown(event.uiEvent, uiObj);
},
/**
* MouseDown Handler
*
*/
handleMouseDown: function(event, uiObj) {
// If there is a Docker
if(uiObj instanceof ORYX.Core.Controls.Docker && uiObj.isMovable) {
/* Buffering shape selection and clear selection*/
this.shapeSelection = this.facade.getSelection();
this.facade.setSelection();
this.docker = uiObj;
this.initialDockerPosition = this.docker.bounds.center();
this.outerDockerNotMoved = false;
this.dockerParent = uiObj.parent;
// Define command arguments
this._commandArg = {docker:uiObj, dockedShape:uiObj.getDockedShape(), refPoint:uiObj.referencePoint || uiObj.bounds.center()};
// Show the Docker
this.docker.show();
// If the Dockers Parent is an Edge,
// and the Docker is either the first or last Docker of the Edge
if(uiObj.parent instanceof ORYX.Core.Edge &&
(uiObj.parent.dockers.first() == uiObj || uiObj.parent.dockers.last() == uiObj)) {
// Get the Edge Source or Target
if(uiObj.parent.dockers.first() == uiObj && uiObj.parent.dockers.last().getDockedShape()) {
this.dockerTarget = uiObj.parent.dockers.last().getDockedShape()
} else if(uiObj.parent.dockers.last() == uiObj && uiObj.parent.dockers.first().getDockedShape()) {
this.dockerSource = uiObj.parent.dockers.first().getDockedShape()
}
} else {
// If there parent is not an Edge, undefined the Source and Target
this.dockerSource = undefined;
this.dockerTarget = undefined;
}
this.isStartDocker = this.docker.parent.dockers.first() === this.docker
this.isEndDocker = this.docker.parent.dockers.last() === this.docker
// add to canvas while dragging
this.facade.getCanvas().add(this.docker.parent);
// Hide all Labels from Docker
this.docker.parent.getLabels().each(function(label) {
label.hide();
});
// 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
var evPos = this.facade.eventCoordinates(event);
this.docker.bounds.centerMoveTo(evPos);
//this.docker.update()
//this.facade.getCanvas().update();
this.dockerParent._update();
} else {
this.outerDockerNotMoved = true;
}
var option = {movedCallback: this.dockerMoved.bind(this), upCallback: this.dockerMovedFinished.bind(this)}
// 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);
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) {
// Get the StencilSet of the Edge
var sset = this.docker.parent.getStencil().stencilSet();
// 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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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.facade.offer({
'name':ORYX.I18N.AddDocker.add,
'functionality': this.enableAddDocker.bind(this),
'group': ORYX.I18N.AddDocker.group,
'icon': ORYX.PATH + "images/vector_add.png",
'description': ORYX.I18N.AddDocker.addDesc,
'index': 1,
'toggle': true,
'minShape': 0,
'maxShape': 0});
this.facade.offer({
'name':ORYX.I18N.AddDocker.del,
'functionality': this.enableDeleteDocker.bind(this),
'group': ORYX.I18N.AddDocker.group,
'icon': ORYX.PATH + "images/vector_delete.png",
'description': ORYX.I18N.AddDocker.delDesc,
'index': 2,
'toggle': true,
'minShape': 0,
'maxShape': 0});
this.facade.registerOnEvent(ORYX.CONFIG.EVENT_MOUSEDOWN, this.handleMouseDown.bind(this));
},
enableAddDocker: function(button, pressed) {
//FIXME This should be done while construct, but this isn't possible right now!
this.addDockerButton = button;
// Unpress deleteDockerButton
if(pressed && this.deleteDockerButton)
this.deleteDockerButton.toggle(false);
},
enableDeleteDocker: function(button, pressed) {
//FIXME This should be done while construct, but this isn't possible right now!
this.deleteDockerButton = button;
// Unpress addDockerButton
if(pressed && this.addDockerButton)
this.addDockerButton.toggle(false);
},
enabledAdd: function(){
return this.addDockerButton ? this.addDockerButton.pressed : false;
},
enabledDelete: function(){
return this.deleteDockerButton ? this.deleteDockerButton.pressed : false;
},
/**
* MouseDown Handler
*
*/
handleMouseDown: function(event, uiObj) {
if (this.enabledAdd() && uiObj instanceof ORYX.Core.Edge) {
this.newDockerCommand({
edge: uiObj,
position: this.facade.eventCoordinates(event)
});
} else if (this.enabledDelete() &&
uiObj instanceof ORYX.Core.Controls.Docker &&
uiObj.parent instanceof ORYX.Core.Edge) {
this.newDockerCommand({
edge: uiObj.parent,
docker: uiObj
});
} else if ( this.enabledAdd() ){
this.addDockerButton.toggle(false);
} else if ( this.enabledDelete() ) {
this.deleteDockerButton.toggle(false);
}
},
// 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;
//this.index = docker.parent.dockers.indexOf(docker);
},
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.enabledDelete(), options.edge, options.docker, options.position, this.facade);
this.facade.executeCommands([command]);
}
});
/**
* Copyright (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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", this.facade.getCanvas().getHTMLContainer(),
['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)-this.offsetPosition.x, y:Event.pointerY(event)-this.offsetPosition.y});
// 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)
}
// 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 - this.offsetPosition.x,
height : Event.pointerY(event) - this.position.y - this.offsetPosition.y,
}
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 (c) 2006
* Martin Czuchra, Nicolas Peters, Daniel Polak, Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
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 */
node.setAttributeNS(null, "d", this.getPathEdge(options.elements[0].dockers));
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
node.setAttributeNS(null, "d", this.getPathByElements(options.elements));
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 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 (c) 2008
* Willi Tscheschner
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* HOW to USE the OVERLAY PLUGIN:
* You can use it via the event mechanism from the editor
* by using facade.raiseEvent(