text: /**
   * 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: ""
  };
  
  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) ? ('&lt;' + subject.value + '&gt;') : '') +
  		//		((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 '&lt;' + this.value + '&gt;';
  	}
  	
  };
  
  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 = '<div xmlns="http://www.w3.org/1999/xhtml">' +
  					'<span class="raziel-type">'+razielType+'</span></div>';
  
  				var r = ResourceManager.__createResource(div);
  				shape.resourceId = r.id();
  				
  			} else {
  		
  				var r = ResourceManager.__createResource();
  				shape.resourceId = r.id();
  			}
  
  		});
  
  		shapes.each( function(shape) {
  			
  			// store all shapes.
  			DataManager.__storeShape(shape);
  		});
  	},
  	
  	/**
  	 * This method serializes a single div into a string that satisfies the
  	 * client/server communication protocol. It ingnores all elements that have
  	 * an attribute named class that includes 'transient'.
  	 * @param {Object} node the element to serialize.
  	 * @param {Object} preserveNamespace whether to preserve the parent's
  	 *                 namespace. If you are not sure about namespaces, provide
  	 *                 just the element to be serialized.
  	 */
  	serialize: function(node, preserveNamespace) {
  
  		if (node.nodeType == node.ELEMENT_NODE) {
  			// serialize an element node.
  			
  			var children = $A(node.childNodes);
  			var attributes = $A(node.attributes);
  			var clazz = new String(node.getAttribute('class'));
  			var ignore = clazz.split(' ').member('transient');
  
  			// ignore transients.
  
  			if(ignore)
  				return '';
  
  			// start serialization.
  			
  			var result = '<' + node.nodeName;
  			
  			// preserve namespace?
  			if(!preserveNamespace) 
  				result += ' xmlns="' + (node.namespaceURI ? node.namespaceURI : XMLNS.XHTML) + '" xmlns:oryx="http://oryx-editor.org"';
  			
  			// add all attributes.
  			
  			attributes.each(function(attribute) {
  				result += ' ' + attribute.nodeName + '="' +
  					attribute.nodeValue + '"';});
  			
  			// close if no children.
  			
  			if(children.length == 0)
  				result += '/>';
  				
  			else {
  				
  				// serialize all children.
  				
  				result += '>';
  				children.each(function(_node) {
  					result += DataManager.serialize(_node, true)});
  				result += '</' + node.nodeName + '>'
  			}
  
  			return result;
  			
  		} else if (node.nodeType == node.TEXT_NODE) {
  			
  			// serialize a text node.
  			return  node.nodeValue;
  		}
  		
  		//TODO serialize cdata areas also.
  		//TODO work on namespace awareness.
  	},
  
  	addTriple: function(triple) {
  
  		// assert the subject is a resource
  		
  		if(!triple.subject.type == ERDF.LITERAL)
  			throw 'Cannot add the triple ' + triple.toString() +
  				' because the subject is not a resource.'
  		
  		// get the element which represents this triple's subject.
  		var elementId = ERDF.__stripHashes(triple.subject.value);
  		var element = $(elementId);
  				
  		// assert the subject is inside this document.
  		if(!element)
  			throw 'Cannot add the triple ' + triple.toString() +
  				' because the subject "'+elementId+'" is not in the document.';
  
  		if(triple.object.type == ERDF.LITERAL)
  
  			// object is literal
  			DataManager.graft(XMLNS.XHTML, element, [
  				'span', {'class': (triple.predicate.prefix + "-" +
  					triple.predicate.name)}, triple.object.value.escapeHTML()
  			]);
  			
  		else {
  
  			// object is resource
  			DataManager.graft(XMLNS.XHTML, element, [
  				'a', {'rel': (triple.predicate.prefix + "-" +
  					triple.predicate.name), 'href': triple.object.value}
  			]);
  			
  		}
  
  		return true;
  	},
  	
  	removeTriples: function(triples) {
  
  		// alert('Removing ' +triples.length+' triples.');
  
  		// from all the triples select those ...
  		var removed = triples.select(
  
  			function(triple) {
  				
  				// TODO remove also from triple store.
  				// ... that were actually removed.
  				return DataManager.__removeTriple(triple);
  			});
  		
  		// sync and return removed triples.
  		// DataManager.__synclocal();
  		return removed;
  	},
  	
  	removeTriple: function(triple) {
  		
  		// remember whether the triple was actually removed.
  		var result = DataManager.__removeTriple(triple);
  
  		// sync and return removed triples.
  		// DataManager.__synclocal();
  		return result;
  	},
  
  	__removeTriple: function(triple) {
  		
  		// assert the subject is a resource
  		if(!triple.subject.type == ERDF.LITERAL)
  		
  			throw 'Cannot remove the triple ' + triple.toString() +
  				' because the subject is not a resource.';
  
  		// get the element which represents this triple's subject.
  		var elementId = ERDF.__stripHashes(triple.subject.value);
  		var element = $(elementId);
  
  		// assert the subject is inside this document.
  		if(!element)
  		
  			throw 'Cannot remove the triple ' + triple.toString() +
  				' because the subject is not in the document.';
  	  
  		if(triple.object.type == ERDF.LITERAL) {
  	  
    			// continue searching actively for the triple.
  			var result = DataManager.__removeTripleRecursively(triple, element);
  			return result;
  		}
  	},
  
  	__removeTripleRecursively: function(triple, continueFrom) {  
  
  		// return when this node is not an element node.
  		if(continueFrom.nodeType != continueFrom.ELEMENT_NODE)
  			return false;
  		
  		var classes = new String(continueFrom.getAttribute('class'));
  		var children = $A(continueFrom.childNodes);
  		
  		if(classes.include(triple.predicate.prefix + '-' + triple.predicate.name)) {
  		  
  			var content = continueFrom.textContent;
  			if(	(triple.object.type == ERDF.LITERAL) &&
  				(triple.object.value == content))
  
  				continueFrom.parentNode.removeChild(continueFrom);
  			
  			return true;
  		  
  		} else {
  		 
  			children.each(function(_node) {
  			DataManager.__removeTripleRecursively(triple, _node)});
  			return false;
  		}
  
  	},
  
  	/**
  	 * graft() function
  	 * Originally by Sean M. Burke from interglacial.com, altered for usage with
  	 * SVG and namespace (xmlns) support. Be sure you understand xmlns before
  	 * using this funtion, as it creates all grafted elements in the xmlns
  	 * provided by you and all element's attribures in default xmlns. If you
  	 * need to graft elements in a certain xmlns and wish to assign attributes
  	 * in both that and another xmlns, you will need to do stepwise grafting,
  	 * adding non-default attributes yourself or you'll have to enhance this
  	 * function. Latter, I would appreciate: martin�apfelfabrik.de
  	 * @param {Object} namespace The namespace in which
  	 * 					elements should be grafted.
  	 * @param {Object} parent The element that should contain the grafted
  	 * 					structure after the function returned.
  	 * @param {Object} t the crafting structure.
  	 * @param {Object} doc the document in which grafting is performed.
  	 */
  	graft: function(namespace, parent, t, doc) {
  		
  	    doc = (doc || (parent && parent.ownerDocument) || document);
  	    var e;
  	    if(t === undefined) {
  	        echo( "Can't graft an undefined value");
  	    } else if(t.constructor == String) {
  	        e = doc.createTextNode( t );
  	    } else {
  	        for(var i = 0; i < t.length; i++) {
  	            if( i === 0 && t[i].constructor == String ) {
  					var snared = t[i].match( /^([a-z][a-z0-9]*)\.([^\s\.]+)$/i );
  	                if( snared ) {
  	                    e = doc.createElementNS(namespace, snared[1]);
  	                    e.setAttributeNS(null, 'class', snared[2] );
  	                    continue;
  	                }
  	                snared = t[i].match( /^([a-z][a-z0-9]*)$/i );
  	                if( snared ) {
  	                    e = doc.createElementNS(namespace, snared[1]);  // but no class
  	                    continue;
  	                }
  	
  	                // Otherwise:
  	                e = doc.createElementNS(namespace, "span");
  	                e.setAttribute(null, "class", "namelessFromLOL" );
  	            }
  	
  	            if( t[i] === undefined ) {
  	                echo("Can't graft an undefined value in a list!");
  	            } else if( t[i].constructor == String || t[i].constructor == Array) {
  	                this.graft(namespace, e, t[i], doc );
  	            } else if(  t[i].constructor == Number ) {
  	                this.graft(namespace, e, t[i].toString(), doc );
  	            } else if(  t[i].constructor == Object ) {
  	                // hash's properties => element's attributes
  	                for(var k in t[i]) { e.setAttributeNS(null, k, t[i][k] ); }
  	            } else if(  t[i].constructor == Boolean ) {
  	                this.graft(namespace, e, t[i] ? 'true' : 'false', doc );
  				} else
  					throw "Object " + t[i] + " is inscrutable as an graft arglet.";
  	        }
  	    }
  		
  		if(parent) parent.appendChild(e);
  	
  	    return Element.extend(e); // return the topmost created node
  	},
  
  	setObject: function(triple) {
  
  		/**
  		 * Erwartungen von Arvid an diese Funktion:
  		 * - Es existiert genau ein triple mit dem Subjekt und Praedikat,
  		 *   das uebergeben wurde, und dieses haelt uebergebenes Objekt.
  		 */
  
  		var triples = DataManager.query(
  			triple.subject,
  			triple.predicate,
  			undefined
  		);
  		
  		DataManager.removeTriples(triples);
  
  		DataManager.addTriple(triple);
  
  		return true;
  	},
  	
  	query: function(subject, predicate, object) {
  
  		/*
  		 * Typical triple.
  		 *	{value: subject, type: subjectType},
  		 *	{prefix: schema.prefix, name: property},
  		 *	{value: object, type: objectType});
  		 */	
  		 	
  		return DataManager._triples.select(function(triple) {
  			
  			var select = ((subject) ?
  				(triple.subject.type == subject.type) &&
  				(triple.subject.value == subject.value) : true);
  			if(predicate) {
  				select = select && ((predicate.prefix) ?
  					(triple.predicate.prefix == predicate.prefix) : true);
  				select = select && ((predicate.name) ?
  					(triple.predicate.name == predicate.name) : true);
  			}
  			select = select && ((object) ?
  				(triple.object.type == object.type) &&
  				(triple.object.value == object.value) : true);
  			return select;
  		});
  	}
  }
  
  Kickstart.register(DataManager.init);
  
  function assert(expr, m) { if(!expr) throw m; };
  
  function DMCommand(action, triple) {
  	
  	// store action and triple.
  	this.action = action;
  	this.triple = triple;
  	
  	this.toString = function() {
  		return 'Command('+action+', '+triple+')';
  	};
  }
  
  function DMCommandHandler(nextHandler) {
  	
  	/**
  	 * Private method to set the next handler in the Chain of Responsibility
  	 * (see http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern for
  	 * details).
  	 * @param {DMCommandHandler} handler The handler that is next in the chain.
  	 */
  	this.__setNext = function(handler) {
  		var _next = this.__next;
  		this.__next = nextHandler;
  		return _next ? _next : true;
  	};
  	this.__setNext(nextHandler);
  
  	/**
  	 * Invokes the next handler. If there is no next handler, this method
  	 * returns false, otherwise it forwards the result of the handling.
  	 * @param {Object} command The command object to be processed.
  	 */
  	this.__invokeNext = function(command) {
  		return this.__next ? this.__next.handle(command) : false;
  	};
  	
  	/**
  	 * Handles a command. The abstract method process() is called with the
  	 * command object that has been passed. If the process method catches the
  	 * command (returns true on completion), the handle() method returns true.
  	 * If the process() method doesn't catch the command, the next handler will
  	 * be invoked.
  	 * @param {Object} command The command object to be processed.
  	 */
  	this.handle = function(command) {
  		return this.process(command) ? true : this.__invokeNext(command);
  	}
  	
  	/**
  	 * Empty process() method returning false. If javascript knew abstract
  	 * class members, this would be one.
  	 * @param {Object} command The command object to process.
  	 */
  	this.process = function(command) { return false; };
  };
  
  /**
   * This Handler manages the addition and the removal of meta elements in the
   * head of the document.
   * @param {DMCommandHandler} next The handler that is next in the chain.
   */
  function MetaTagHandler(next) {
  	
  	DMCommandHandler.apply(this, [next]);
  	this.process = function(command) {
  		
  		with(command.triple) {
  			
  			/* assert prerequisites */
  			if( !(
  				(subject instanceof ERDF.Resource) &&
  				(subject.isCurrentDocument()) &&
  				(object instanceof ERDF.Literal)
  			))	return false;
  		}
  		
  	};
  };
  
  var chain =	new MetaTagHandler();
  var command = new DMCommand(TRIPLE_ADD, new ERDF.Triple(
  	new ERDF.Resource(''),
  	'rdf:tool',
  	new ERDF.Literal('')
  ));
  
  /*
  if(chain.handle(command))
  	alert('Handled!');
  */
  
  ResourceManager = {
  	
  	__corrupt: false,
  	__latelyCreatedResource: undefined,
  	__listeners: $H(),
  	__token: 1,
  	
  	addListener: function(listener, mask) {
  
  		if(!(listener instanceof Function))
  			throw 'Resource event listener is not a function!';
  		if(!(mask))
  			throw 'Invalid mask for resource event listener registration.';
  
  		// construct controller and token.
  		var controller = {listener: listener, mask: mask};
  		var token = ResourceManager.__token++;
  		
  		// add new listener.
  		ResourceManager.__listeners[token] = controller;
  		
  		// return the token generated.
  		return token;
  	},
  	
  	removeListener: function(token) {
  		
  		// remove the listener with the token and return it.
  		return ResourceManager.__listners.remove(token);
  	},
  	
  	__Event: function(action, resourceId) {
  		this.action = action;
  		this.resourceId = resourceId;
  	},
  	
  	__dispatchEvent: function(event) {
  		
  		// get all listeners. for each listener, ...
  		ResourceManager.__listeners.values().each(function(controller) {
  			
  			// .. if listener subscribed to this type of event ...
  			if(event.action & controller.mask)
  				return controller.listener(event);
  		});
  	},
  
  	getResource: function(id) {
  
  		// get all possible resources for this.
  		id = ERDF.__stripHashes(id);
  		var resources = DataManager.query(
  			new ERDF.Resource('#'+id),
  			{prefix: 'raziel', name: 'entry'},
  			undefined
  		);
  
  		// check for consistency.
  		if((resources.length == 1) && (resources[0].object.isResource())) {
  			var entryUrl = resources[0].object.value;
  			return new ResourceManager.__Resource(id, entryUrl);
  		}
  
  		// else throw an error message.
  		throw ('Resource with id ' +id+ ' not recognized as such. ' +
  			((resources.length > 1) ?
  				' There is more than one raziel:entry URL.' :
  				' There is no raziel:entry URL.'));
  
  		return false;
  	},
  
  	__createResource: function(alternativeDiv) {
  		
  		var collectionUrls = DataManager.query(
  			new ERDF.Resource(''),
  			// TODO This will become raziel:collection in near future.
  			{prefix: 'raziel', name: 'collection'},
  			undefined
  		);
  
  		// check for consistency.
  		
  		if(	(collectionUrls.length == 1) &&
  			(collectionUrls[0].object.isResource())) {
  
  			// get the collection url.
  			
  			var collectionUrl = collectionUrls[0].object.value;
  			var resource = undefined;
  			
  			// if there is an old id, serialize the dummy div from there,
  			// otherwise create a dummy div on the fly.
  			
  			var serialization = alternativeDiv? alternativeDiv : 
  					'<div xmlns="http://www.w3.org/1999/xhtml"></div>';
  					
  			ResourceManager.__request(
  				'POST', collectionUrl, serialization,
  
  				// on success
  				function() {
  					
  					// get div and id that have been generated by the server.
  					
  					var response = (this.responseXML);
  					var div = response.childNodes[0];
  					var id = div.getAttribute('id');
  					
  					// store div in DOM
  					if(!$$('.' + PROCESSDATA_REF)[0])
  						DataManager.graft(XMLNS.XHTML,
  							document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), ['div', {'class': PROCESSDATA_REF, 'style':'display:none;'}]);
  				
  					$$('.' + PROCESSDATA_REF)[0].appendChild(div.cloneNode(true));
  
  					// parse local erdf data once more.
  					
  					DataManager.__synclocal();
  					
  					// get new resource object.
  
  					resource = new ResourceManager.getResource(id);
  
  					// set up an action informing of the creation.
  					
  					ResourceManager.__resourceActionSucceeded(
  						this, RESOURCE_CREATED, undefined);
  				},
  
  				function() { ResourceManager.__resourceActionFailed(
  					this, RESOURCE_CREATED, undefined);},
  				false
  			);
  			
  			return resource;
  		}
  		
  		// else
  		throw 'Could not create resource! raziel:collection URL is missing!';
  		return false;
  
  	},
  	
  	__Resource: function(id, url) {
  		
  		this.__id = id;
  		this.__url = url;
  		
  		/*
  		 * Process URL is no longer needed to refer to the shape element on the
  		 * canvas. AReSS uses the id's to gather information on fireing
  		 * behaviour now.
  		 */
  		
  //		// find the process url.		
  //		var processUrl = undefined;
  //		
  //		var urls = DataManager.query(
  //			new ERDF.Resource('#'+this.__id),
  //			{prefix: 'raziel', name: 'process'},
  //			undefined
  //		);
  //		
  //		if(urls.length == 0) { throw 'The resource with the id ' +id+ ' has no process url.'};
  //		
  //		urls.each( function(triple) {
  //			
  //			// if there are more urls, use the last one.
  //			processUrl = triple.object.value;
  //		});
  //		
  //		this.__processUrl = processUrl;
  //
  //		// convenience function for getting the process url.
  //		this.processUrl = function() {
  //			return this.__processUrl;
  //		}
  
  
  		// convenience finction for getting the id.
  		this.id = function() {
  			return this.__id;
  		}
  
  		// convenience finction for getting the entry url.
  		this.url = function() {
  			return this.__url;
  		}
  		
  		this.reload = function() {
  			var _url = this.__url;
  			var _id = this.__id;
  			ResourceManager.__request(
  				'GET', _url, null,
  				function() { ResourceManager.__resourceActionSucceeded(
  					this, RESOURCE_RELOADED, _id); },
  				function() { ResourceManager.__resourceActionFailed(
  					this, RESURCE_RELOADED, _id); },
  				USE_ASYNCHRONOUS_REQUESTS
  			);
  		};
  		
  		this.save = function(synchronize) {
  			var _url = this.__url;
  			var _id = this.__id;
  			data = DataManager.serialize($(_id));
  			ResourceManager.__request(
  				'PUT', _url, data,
  				function() { ResourceManager.__resourceActionSucceeded(
  					this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE_SAVED, _id); },
  				function() { ResourceManager.__resourceActionFailed(
  					this, synchronize ? RESOURCE_SAVED | RESOURCE_SYNCHRONIZED : RESOURCE.SAVED, _id); },
  				USE_ASYNCHRONOUS_REQUESTS
  			);
  		};
  		
  		this.remove = function() {
  			var _url = this.__url;
  			var _id = this.__id;
  			ResourceManager.__request(
  				'DELETE', _url, null,
  				function() { ResourceManager.__resourceActionSucceeded(
  					this, RESOURCE_REMOVED, _id); },
  				function() { ResourceManager.__resourceActionFailed(
  					this, RESOURCE_REMOVED, _id);},
  				USE_ASYNCHRONOUS_REQUESTS
  			);
  		};
  	},
  
  	request: function(url, requestOptions) {
  
  		var options = {
  			method:       'get',
  			asynchronous: true,
  			parameters:   {}
  		};
  
  		Object.extend(options, requestOptions || {});
   		
  		var params = Hash.toQueryString(options.parameters);
  		if (params) 
  			url += (url.include('?') ? '&' : '?') + params;
     
  		return ResourceManager.__request(
  			options.method, 
  			url, 
  			options.data, 
  			(options.onSuccess instanceof Function ? function() { options.onSuccess(this); } : undefined ), 
  			(options.onFailure instanceof Function ? function() { options.onFailure(this); } : undefined ), 
  			options.asynchronous && USE_ASYNCHRONOUS_REQUESTS,
  			options.headers);
  	},
  	
  	__request: function(method, url, data, success, error, async, headers) {
  		
  		// get a request object
  		var httpRequest = Try.these(
  
  			/* do the Mozilla/Safari/Opera stuff */
  			function() { return new XMLHttpRequest(); },
  			
  			/* do the IE stuff */
  			function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
  			function() { return new ActiveXObject("Microsoft.XMLHTTP") }
  		);
  
  		// if there is no request object ...
          if (!httpRequest) {
  			if(!this.__corrupt)
  				throw 'This browser does not provide any AJAX functionality. You will not be able to use the software provided with the page you are viewing. Please consider installing appropriate extensions.';
  			this.__corrupt = true;
  			return false;
          }
  		
  		if(success instanceof Function)
  			httpRequest.onload = success;
  		if(error instanceof Function) {
  			httpRequest.onerror = error;
  		}
  		
  		var h = $H(headers)
  		h.keys().each(function(key) {
  			
  			httpRequest.setRequestHeader(key, h[key]);
  		}); 
  		
  		try {
  
  			if(SHOW_DEBUG_ALERTS_WHEN_SAVING)
  			
  				alert(method + ' ' + url + '\n' +
  					SHOW_EXTENDED_DEBUG_INFORMATION ? data : '');
  
  			// TODO Remove synchronous calls to the server as soon as xenodot
  			// handles asynchronous requests without failure.
  	        httpRequest.open(method, url, !async?false:true);
  	        httpRequest.send(data);
  			
  		} catch(e) {
  			return false;
  		}
  		return true;
      },
  
  	__resourceActionSucceeded: function(transport, action, id) {
  		
  		var status = transport.status;
  		var response = transport.responseText;
  		
  		if(SHOW_DEBUG_ALERTS_WHEN_SAVING)
  
  			alert(status + ' ' + url + '\n' +
  				SHOW_EXTENDED_DEBUG_INFORMATION ? data : '');
  
  		// if the status code is not in 2xx, throw an error.
  		if(status >= 300)
  			throw 'The server responded with an error: ' + status + '\n' + (SHOW_EXTENDED_DEBUG_INFORMATION ? + data : 'If you need additional information here, including the data sent by the server, consider setting SHOW_EXTENDED_DEBUG_INFORMATION to true.');
  
  		switch(action) {
  			
  			case RESOURCE_REMOVED:
  
  				// get div and id
  				var response = (transport.responseXML);
  				var div = response.childNodes[0];
  				var id = div.getAttribute('id');
  				
  				// remove the resource from DOM
  				var localDiv = document.getElementById(id);
  				localDiv.parentNode.removeChild(localDiv);
  				break;
  
  			case RESOURCE_CREATED:
  
  				// nothing remains to be done.
  				break;
  	
  			case RESOURCE_SAVED | RESOURCE_SYNCHRONIZED:
  
  				DataManager.__synclocal();
  
  			case RESOURCE_SAVED:
  
  				// nothing remains to be done.
  				break;
  
  			case RESOURCE_RELOADED:
  			
  				// get div and id
  				var response = (transport.responseXML);
  				var div = response.childNodes[0];
  				var id = div.getAttribute('id');
  				
  				// remove the local resource representation from DOM
  				var localDiv = document.getElementById(id)
  				localDiv.parentNode.removeChild(localDiv);
  				
  				// store div in DOM
  				if(!$$(PROCESSDATA_REF)[0])
  					DataManager.graft(XMLNS.XHTML,
  						document.getElementsByTagNameNS(XMLNS.XHTML, 'body').item(0), ['div', {'class': PROCESSDATA_REF, 'style':'display:none;'}]);
  				
  				$$(PROCESSDATA_REF)[0].appendChild(div.cloneNode(true));
  				DataManager.__synclocal();
  				break;
  
  			default:
  				DataManager.__synclocal();
  
  		}
  		 
  		// dispatch to all listeners ...
  		ResourceManager.__dispatchEvent(
  
  			// ... an event describing the change that happened here.
  			new ResourceManager.__Event(action, id)
  		);
  	},
  
  	__resourceActionFailed: function(transport, action, id) {
  		throw "Fatal: Resource action failed. There is something horribly " +
  			"wrong with either the server, the transport protocol or your " +
  			"online status. Sure you're online?";
  	}
  }/**
   * Copyright (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 	= 	false;
  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<arguments.length; i++)
  		result = result.replace('%' + (i-1), arguments[i]);
  	return result;
  }
  
  // oryx constants.
  var ORYX_LOGLEVEL_TRACE = 5;
  var ORYX_LOGLEVEL_DEBUG = 4;
  var ORYX_LOGLEVEL_INFO = 3;
  var ORYX_LOGLEVEL_WARN = 2;
  var ORYX_LOGLEVEL_ERROR = 1;
  var ORYX_LOGLEVEL_FATAL = 0;
  var ORYX_LOGLEVEL = 3;
  var ORYX_CONFIGURATION_DELAY = 100;
  var ORYX_CONFIGURATION_WAIT_ATTEMPTS = 10;
  
  if(!ORYX) var ORYX = {};
  
  ORYX = Object.extend(ORYX, {
  
  	//set the path in the config.js file!!!!
  	PATH: ORYX.CONFIG.ROOT_PATH,
  	//CONFIGURATION: "config.js",
  
  	URLS: [
  	
  		/*
  		 * No longer needed, since compiled into one source file that
  		 * contains all of this files concatenated in the exact order
  		 * as defined in build.xml.
  		 */
  		
  /*
  		"scripts/Core/SVG/editpathhandler.js",
  		"scripts/Core/SVG/minmaxpathhandler.js",
  		"scripts/Core/SVG/pointspathhandler.js",
  		"scripts/Core/SVG/svgmarker.js",
  		"scripts/Core/SVG/svgshape.js",
  		"scripts/Core/SVG/label.js",
  		"scripts/Core/Math/math.js",		
  		"scripts/Core/StencilSet/stencil.js",
  		"scripts/Core/StencilSet/property.js",
  		"scripts/Core/StencilSet/propertyitem.js",
  		"scripts/Core/StencilSet/rules.js",
  		"scripts/Core/StencilSet/stencilset.js",
  		"scripts/Core/StencilSet/stencilsets.js",
  		"scripts/Core/bounds.js",
  		"scripts/Core/uiobject.js",
  		"scripts/Core/abstractshape.js",
  		"scripts/Core/canvas.js",
  		"scripts/Core/main.js",
  		"scripts/Core/svgDrag.js",
  		"scripts/Core/shape.js",
  		"scripts/Core/Controls/control.js",
  		"scripts/Core/Controls/docker.js",
  		"scripts/Core/Controls/magnet.js",		
  		"scripts/Core/node.js",
  		"scripts/Core/edge.js"
  */	],
  
  	alreadyLoaded: [],
  
  	configrationRetries: 0,
  
  	Version: '0.1.1',
  
  	availablePlugins: [],
  
  	/**
  	 * The ORYX.Log logger.
  	 */
  	Log: {
  	
  		__appenders: [
  			{ append: function(message) {
  				console.log(message); }}
  		],
  	
  		trace: function() {	if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_TRACE)
  			ORYX.Log.__log('TRACE', arguments); },
  		debug: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_DEBUG)
  			ORYX.Log.__log('DEBUG', arguments); },
  		info: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_INFO)
  			ORYX.Log.__log('INFO', arguments); },
  		warn: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_WARN)
  			ORYX.Log.__log('WARN', arguments); },
  		error: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_ERROR)
  			ORYX.Log.__log('ERROR', arguments); },
  		fatal: function() { if(ORYX_LOGLEVEL >= ORYX_LOGLEVEL_FATAL)
  			ORYX.Log.__log('FATAL', arguments); },
  		
  		__log: function(prefix, messageParts) {
  			
  			messageParts[0] = (new Date()).getTime() + " "
  				+ prefix + " " + messageParts[0];
  			var message = printf.apply(null, messageParts);
  			
  			
  			ORYX.Log.__appenders.each(function(appender) {
  				appender.append(message);
  			});
  		},
  		
  		addAppender: function(appender) {
  			ORYX.Log.__appenders.push(appender);
  		}
  	},
  
  	/**
  	 * First bootstrapping layer. The Oryx loading procedure begins. In this
  	 * step, all preliminaries that are not in the responsibility of Oryx to be
  	 * met have to be checked here, such as the existance of the prototpe
  	 * library in the current execution environment. After that, the second
  	 * bootstrapping layer is being invoked. Failing to ensure that any
  	 * preliminary condition is not met has to fail with an error.
  	 */
  	load: function() {
  		
  		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:'<span style="font-size:11px;">' + ORYX.I18N.Oryx.pleaseWait + '</span>'})
  			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<size; i++){
  			        pointsArray.push(this.element.points.getItem(i).x)
  			        pointsArray.push(this.element.points.getItem(i).y)
  			    }
  			} else {
  				var points = this.element.getAttributeNS(null, "points");
  				if(points) {
  					points = points.replace(/,/g , " ");
  					pointsArray = points.split(" ");
  					pointsArray = pointsArray.without("");
  				} else {
  					throw "Missing attribute in element " + this.element;
  				}
  			}
  			
  
  			if(pointsArray && pointsArray.length && pointsArray.length > 1) {
  				var minX = parseFloat(pointsArray[0]);
  				var minY = parseFloat(pointsArray[1]);
  				var maxX = parseFloat(pointsArray[0]);
  				var maxY = parseFloat(pointsArray[1]);
  
  				for(var i = 0; i < pointsArray.length; i++) {
  					minX = Math.min(minX, parseFloat(pointsArray[i]));
  					maxX = Math.max(maxX, parseFloat(pointsArray[i]));
  					i++;
  					minY = Math.min(minY, parseFloat(pointsArray[i]));
  					maxY = Math.max(maxY, parseFloat(pointsArray[i]));
  				}
  
  				this.oldX = minX;
  				this.oldY = minY;
  				this.oldWidth = maxX-minX;
  				this.oldHeight = maxY-minY;
  			} else {
  				throw "Missing attribute in element " + this.element;
  			}
  
  		} else if(ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
  			this.type = "Path";
  			
  			this.editPathParser = new PathParser();
  			this.editPathHandler = new ORYX.Core.SVG.EditPathHandler();
  			this.editPathParser.setHandler(this.editPathHandler);
  		
  			var parser = new PathParser();
  			var handler = new ORYX.Core.SVG.MinMaxPathHandler();
  			parser.setHandler(handler);
  			parser.parsePath(this.element);
  
  			this.oldX = handler.minX;
  			this.oldY = handler.minY;
  			this.oldWidth = handler.maxX - handler.minX;
  			this.oldHeight = handler.maxY - handler.minY;
  
  			delete parser;
  			delete handler;
  		} else {
  			throw "Element is not a shape.";
  		}
  
  		/** initialize attributes of oryx namespace */
  		//resize
  		var resizeAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "resize");
  		if(resizeAttr) {
  			resizeAttr = resizeAttr.toLowerCase();
  			if(resizeAttr.match(/horizontal/)) {
  				this.isHorizontallyResizable = true;
  			} else {
  				this.isHorizontallyResizable = false;
  			}
  			if(resizeAttr.match(/vertical/)) {
  				this.isVerticallyResizable = true;
  			} else {
  				this.isVerticallyResizable = false;
  			}
  		} else {
  			this.isHorizontallyResizable = false;
  			this.isVerticallyResizable = false;
  		}
  
  		//anchors
  		var anchorAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "anchors");
  		if(anchorAttr) {
  			anchorAttr = anchorAttr.replace("/,/g", " ");
  			var anchors = anchorAttr.split(" ").without("");
  			
  			for(var i = 0; i < anchors.length; i++) {
  				switch(anchors[i].toLowerCase()) {
  					case "left":
  						this.anchorLeft = true;
  						break;
  					case "right":
  						this.anchorRight = true;
  						break;
  					case "top":
  						this.anchorTop = true;
  						break;
  					case "bottom":
  						this.anchorBottom = true;
  						break;
  				}
  			}
  		}
  		
  		//allowDockers and resizeMarkerMid
  		if(ORYX.Editor.checkClassType(this.element, SVGPathElement)) {
  			var allowDockersAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "allowDockers"); 
  			if(allowDockersAttr) {
  				if(allowDockersAttr.toLowerCase() === "no") {
  					this.allowDockers = false; 
  				} else {
  					this.allowDockers = true;
  				}
  			}
  			
  			var resizeMarkerMidAttr = this.element.getAttributeNS(NAMESPACE_ORYX, "resizeMarker-mid"); 
  			if(resizeMarkerMidAttr) {
  				if(resizeMarkerMidAttr.toLowerCase() === "yes") {
  					this.resizeMarkerMid = true; 
  				} else {
  					this.resizeMarkerMid = false;
  				}
  			}
  		}	
  			
  		this.x = this.oldX;
  		this.y = this.oldY;
  		this.width = this.oldWidth;
  		this.height = this.oldHeight;
  	},
  
  	/**
  	 * Writes the changed values into the SVG element.
  	 */
  	update: function() {
  		
  		if(this.x !== this.oldX || this.y !== this.oldY || this.width !== this.oldWidth || this.height !== this.oldHeight) {
  			switch(this.type) {
  				case "Rect":
  					if(this.x !== this.oldX) this.element.setAttributeNS(null, "x", this.x);
  					if(this.y !== this.oldY) this.element.setAttributeNS(null, "y", this.y);
  				 	if(this.width !== this.oldWidth) this.element.setAttributeNS(null, "width", this.width);
  					if(this.height !== this.oldHeight) this.element.setAttributeNS(null, "height", this.height);
  					break;
  				case "Circle":
  					//calculate the radius
  					//var r;
  //					if(this.width/this.oldWidth <= this.height/this.oldHeight) {
  //						this.radiusX = ((this.width > this.height) ? this.width : this.height)/2.0;
  //					} else {
  					 	this.radiusX = ((this.width < this.height) ? this.width : this.height)/2.0;
  					//}
  	
  					this.element.setAttributeNS(null, "cx", this.x + this.width/2.0);
  					this.element.setAttributeNS(null, "cy", this.y + this.height/2.0);
  					this.element.setAttributeNS(null, "r", this.radiusX);
  					break;
  				case "Ellipse":
  					this.radiusX = this.width/2;
  					this.radiusY = this.height/2;
  	
  					this.element.setAttributeNS(null, "cx", this.x + this.radiusX);
  					this.element.setAttributeNS(null, "cy", this.y + this.radiusY);
  					this.element.setAttributeNS(null, "rx", this.radiusX);
  					this.element.setAttributeNS(null, "ry", this.radiusY);
  					break;
  				case "Line":
  					if(this.x !== this.oldX)
  						this.element.setAttributeNS(null, "x1", this.x);
  						
  					if(this.y !== this.oldY)
  						this.element.setAttributeNS(null, "y1", this.y);
  						
  					if(this.x !== this.oldX || this.width !== this.oldWidth)
  						this.element.setAttributeNS(null, "x2", this.x + this.width);
  					
  					if(this.y !== this.oldY || this.height !== this.oldHeight)
  						this.element.setAttributeNS(null, "y2", this.y + this.height);
  					break;
  				case "Polyline":
  					var points = this.element.getAttributeNS(null, "points");
  					if(points) {
  						points = points.replace(/,/g, " ").split(" ").without("");
  	
  						if(points && points.length && points.length > 1) {
  	
  							//TODO what if oldWidth == 0?
  							var widthDelta = (this.oldWidth === 0) ? 0 : this.width / this.oldWidth;
  						    var heightDelta = (this.oldHeight === 0) ? 0 : this.height / this.oldHeight;
  	
  							var updatedPoints = "";
  						    for(var i = 0; i < points.length; i++) {
  								var x = (parseFloat(points[i])-this.oldX)*widthDelta + this.x;
  								i++;
  								var y = (parseFloat(points[i])-this.oldY)*heightDelta + this.y;
  		    					updatedPoints += x + " " + y + " ";
  						    }
  							this.element.setAttributeNS(null, "points", updatedPoints);
  						} else {
  							//TODO error
  						}
  					} else {
  						//TODO error
  					}
  					break;
  				case "Path":
  					//calculate scaling delta
  					//TODO what if oldWidth == 0?
  					var widthDelta = (this.oldWidth === 0) ? 0 : this.width / this.oldWidth;
  					var heightDelta = (this.oldHeight === 0) ? 0 : this.height / this.oldHeight;
  	
  					//use path parser to edit each point of the path
  					this.editPathHandler.init(this.x, this.y, this.oldX, this.oldY, widthDelta, heightDelta);
  					this.editPathParser.parsePath(this.element);
  	
  					//change d attribute of path
  					this.element.setAttributeNS(null, "d", this.editPathHandler.d);
  					break;
  			}
  
  			this.oldX = this.x;
  			this.oldY = this.y;
  			this.oldWidth = this.width;
  			this.oldHeight = this.height;
  		}
  		
  		// Remove cached variables
  		delete this.visible;
  		delete this.handler;
  	},
  	
  	isPointIncluded: function(pointX, pointY) {
  
  		// Check if there are the right arguments and if the node is visible
  		if(!pointX || !pointY || !this.isVisible()) {
  			return false;
  		}
  
  		switch(this.type) {
  			case "Rect":
  				return (pointX >= this.x && pointX <= this.x + this.width &&
  						pointY >= this.y && pointY <= this.y+this.height);
  				break;
  			case "Circle":
  				//calculate the radius
  //				var r;
  //				if(this.width/this.oldWidth <= this.height/this.oldHeight) {
  //					r = ((this.width > this.height) ? this.width : this.height)/2.0;
  //				} else {
  //				 	r = ((this.width < this.height) ? this.width : this.height)/2.0;
  //				}
  				return ORYX.Core.Math.isPointInEllipse(pointX, pointY, this.x + this.width/2.0, this.y + this.height/2.0, this.radiusX, this.radiusX);
  				break;
  			case "Ellipse":
  				return ORYX.Core.Math.isPointInEllipse(pointX, pointY, this.x + this.radiusX, this.y + this.radiusY, this.radiusX, this.radiusY);			
  				break;
  			case "Line":
  				return ORYX.Core.Math.isPointInLine(pointX, pointY, this.x, this.y, this.x + this.width, this.y + this.height);
  				break;
  			case "Polyline":
  				var points = this.element.getAttributeNS(null, "points");
  	
  				if(points) {
  					points = points.replace(/,/g , " ").split(" ").without("");
  	
  					points = points.collect(function(n) {
  						return parseFloat(n);
  					});
  					
  					return ORYX.Core.Math.isPointInPolygone(pointX, pointY, points);
  				} else {
  					return false;
  				}
  				break;
  			case "Path":
  				
  				// Cache Path handler
  				if (!this.handler) {
  					var parser = new PathParser();
  					this.handler = new ORYX.Core.SVG.PointsPathHandler();
  					parser.setHandler(this.handler);
  					parser.parsePath(this.element);
  				}
  				
  				return ORYX.Core.Math.isPointInPolygone(pointX, pointY, this.handler.points);
  
  				break;
  			default:
  				return false;
  		}
  	},
  
  	/**
  	 * Returns true if the element is visible
  	 * @param {SVGElement} elem
  	 * @return boolean
  	 */
  	isVisible: function(elem) {
  		
  		if (this.visible !== undefined){
  			return this.visible;
  		}
  			
  		if (!elem) {
  			elem = this.element;
  		}
  
  		var hasOwnerSVG = false;
  		try { 
  			hasOwnerSVG = !!elem.ownerSVGElement;
  		} catch(e){}
  		
  		// Is SVG context
  		if ( hasOwnerSVG ) {
  			// IF G-Element
  			if (ORYX.Editor.checkClassType(elem, SVGGElement)) {
  				if (elem.className && elem.className.baseVal == "me") {
  					this.visible = true;
  					return this.visible;
  				}
  			}
  
  			// Check if fill or stroke is set
  			var fill = elem.getAttributeNS(null, "fill");
  			var stroke = elem.getAttributeNS(null, "stroke");
  			if (fill && fill == "none" && stroke && stroke == "none") {
  				this.visible = false;
  			} else {
  				// Check if displayed
  				var attr = elem.getAttributeNS(null, "display");
  				if(!attr)
  					this.visible = this.isVisible(elem.parentNode);
  				else if (attr == "none") 
  					this.visible = false;
  				else
  					this.visible = true;
  			}
  		} else {
  			this.visible = true;
  		}
  		
  		return this.visible;
  	},
  
  	toString: function() { return (this.element) ? "SVGShape " + this.element.id : "SVGShape " + this.element;}
   });/**
   * Copyright (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 : "<div id='oryx_editor_header'><a href=\""+ORYX.CONFIG.WEB_URL+"\" target=\"_self\"><img src='"+ORYX.PATH+"images/oryx.small.gif' border=\"0\" /></a><div style='clear: both;'></div><div id='close_editor'></div></div>" 
  		});
  
  		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 ? "<img src='"+ORYX.PATH+"images/page_white_put.png'/>" : "";
  		var maModelAuthI= maActive ? "<span class='mashupinfo'><img src='"+ORYX.PATH+"images/" +( maCanRun ? "plugin_error" : "plugin") +".png'/>" + maModelImage + "</span>" : "";
  		
  		
  		// 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 =  "<div id='editor_header'>" +
                  "<div id='header_logo_image'>" +                
                      "<img src='../explorer/src/img/signavio/smoky/logo2.png' border=\"0\" usemap=\"#kisbpmmap\"/>" + 
                      "<map id=\"kisbpmmap\" name=\"kisbpmmap\"><area shape=\"rect\" alt=\"kisbpm.com\" title=\"kisbpm.com\" coords=\"15,2,322,44\" href=\"http://kisbpm.com\" target=\"_blank\" /></map>" +
                  "</div>" +
                  "<span class='openid " + (publicText == user ? "not" : "") + "'>" + 
                    (unescape(user)) + 
                    maModelAuthI + 
                  "</span>" + 
                  "<div id='header_close_image'>" +
                    "<a href=\""+ORYX.CONFIG.WEB_URL+"\" target=\"_self\" title=\"close modeler\">" +
                      "<img src='../editor/images/close_button.png' border=\"0\" />" + 
                    "</a>" +
                  "</div>" + 
                "</div>";
  			
  			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 <Oryx.Core.Command>[] Array of commands
  	 */
  	executeCommands: function(commands){
  		
  		if (!this.commandStack){
  			this.commandStack = [];
  		}
  		if (!this.commandStackExecuted){
  			this.commandStackExecuted = [];
  		}
  		
  		
  		this.commandStack = [].concat(this.commandStack)
  							  .concat(commands);
  		
  		// Check if already executes
  		if (this.commandExecuting){ return; }
  		
  		// Start execution
  		this.commandExecuting = true;
  		
  		// Iterate over all commands
  		while(this.commandStack.length > 0){
  			var command = this.commandStack.shift();
  			// and execute it
  			command.execute();
  			this.commandStackExecuted.push(command);
  		}
  		
  		// Raise event for executing commands
  		this.handleEvents({
  			type		: ORYX.CONFIG.EVENT_EXECUTE_COMMANDS,
  			commands	: this.commandStackExecuted
  		});
  		
  		// Remove temporary vars
  		delete this.commandStack;
  		delete this.commandStackExecuted;
  		delete this.commandExecuting;
  		
  		
  		this.updateSelection();
  
  	},
  	
      /**
       * Returns JSON of underlying canvas (calls ORYX.Canvas#toJSON()).
       * @return {Object} Returns JSON representation as JSON object.
       */
      getJSON: function(){
          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 = '<?xml version="1.0" encoding="utf-8"?>' +
  						'<html xmlns="http://www.w3.org/1999/xhtml" ' +
  						'xmlns:b3mn="http://b3mn.org/2007/b3mn" ' +
  						'xmlns:ext="http://b3mn.org/2007/ext" ' +
  						'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ' +
  						'xmlns:atom="http://b3mn.org/2007/atom+xhtml">' +
  						'<head profile="http://purl.org/NET/erdf/profile">' +
  						'<link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />' +
  						'<link rel="schema.dcTerms" href="http://purl.org/dc/terms/ " />' +
  						'<link rel="schema.b3mn" href="http://b3mn.org" />' +
  						'<link rel="schema.oryx" href="http://oryx-editor.org/" />' +
  						'<link rel="schema.raziel" href="http://raziel.org/" />' +
  						'<base href="' +
  						location.href.split("?")[0] +
  						'" />' +
  						'</head><body>' +
  						serializedDOM +
  						'</body></html>';
  		
  		return serializedDOM;				
  	},
      
  	/**
  	* 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("<?xml") ? "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + 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.createAttribute("title");
          attributeTitle.nodeValue = this.getStencil().title();
          svgNode.setAttributeNode(attributeTitle);
          
          var attributeId = svgDocument.ownerDocument.createAttribute("id");
          attributeId.nodeValue = this.id;
          svgNode.setAttributeNode(attributeId);
          
          // 
          var stencilTargetNode = this.node.childNodes[0].childNodes[0]; //<g class=me>"
          svgNode = stencilTargetNode.appendChild(svgNode);
          
          // Add to the EventHandler
          this.addEventHandlers(svgNode.parentNode);
          
          /**set minimum and maximum size*/
          var minSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "minimumSize");
          if (minSizeAttr) {
              minSizeAttr = minSizeAttr.replace("/,/g", " ");
              var minSizeValues = minSizeAttr.split(" ");
              minSizeValues = minSizeValues.without("");
              
              if (minSizeValues.length > 1) {
                  this.minimumSize = {
                      width: parseFloat(minSizeValues[0]),
                      height: parseFloat(minSizeValues[1])
                  };
              }
              else {
                  //set minimumSize to (1,1), so that width and height of the stencil can never be (0,0)
                  this.minimumSize = {
                      width: 1,
                      height: 1
                  };
              }
          }
          
          var maxSizeAttr = svgNode.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "maximumSize");
          if (maxSizeAttr) {
              maxSizeAttr = maxSizeAttr.replace("/,/g", " ");
              var maxSizeValues = maxSizeAttr.split(" ");
              maxSizeValues = maxSizeValues.without("");
              
              if (maxSizeValues.length > 1) {
                  this.maximumSize = {
                      width: parseFloat(maxSizeValues[0]),
                      height: parseFloat(maxSizeValues[1])
                  };
              }
          }
          
          if (this.minimumSize && this.maximumSize &&
          (this.minimumSize.width > this.maximumSize.width ||
          this.minimumSize.height > this.maximumSize.height)) {
          
              //TODO wird verschluckt!!!
              throw this + ": Minimum Size must be greater than maxiumSize.";
          }
          
          /**get current bounds and adjust it to upperLeft == (0,0)*/
          //initialize all SVGShape objects
          this._svgShapes = this._initSVGShapes(svgNode);
          
          //get upperLeft and lowerRight of stencil
          var upperLeft = {
              x: undefined,
              y: undefined
          };
          var lowerRight = {
              x: undefined,
              y: undefined
          };
          var me = this;
          this._svgShapes.each(function(svgShape){
              upperLeft.x = (upperLeft.x !== undefined) ? Math.min(upperLeft.x, svgShape.x) : svgShape.x;
              upperLeft.y = (upperLeft.y !== undefined) ? Math.min(upperLeft.y, svgShape.y) : svgShape.y;
              lowerRight.x = (lowerRight.x !== undefined) ? Math.max(lowerRight.x, svgShape.x + svgShape.width) : svgShape.x + svgShape.width;
              lowerRight.y = (lowerRight.y !== undefined) ? Math.max(lowerRight.y, svgShape.y + svgShape.height) : svgShape.y + svgShape.height;
              
              /** set if resizing is enabled */
              //TODO isResizable durch die beiden anderen booleans ersetzen?
              if (svgShape.isHorizontallyResizable) {
                  me.isHorizontallyResizable = true;
                  me.isResizable = true;
              }
              if (svgShape.isVerticallyResizable) {
                  me.isVerticallyResizable = true;
                  me.isResizable = true;
              }
              if (svgShape.anchorTop && svgShape.anchorBottom) {
                  me.isVerticallyResizable = true;
                  me.isResizable = true;
              }
              if (svgShape.anchorLeft && svgShape.anchorRight) {
                  me.isHorizontallyResizable = true;
                  me.isResizable = true;
              }
          });
          
          //move all SVGShapes by -upperLeft
          this._svgShapes.each(function(svgShape){
              svgShape.x -= upperLeft.x;
              svgShape.y -= upperLeft.y;
              svgShape.update();
          });
          
          //set bounds of shape
          //the offsets are also needed for positioning the magnets and the docker
          var offsetX = upperLeft.x;
          var offsetY = upperLeft.y;
          
          lowerRight.x -= offsetX;
          lowerRight.y -= offsetY;
          upperLeft.x = 0;
          upperLeft.y = 0;
          
          //prevent that width or height of initial bounds is 0
          if (lowerRight.x === 0) {
              lowerRight.x = 1;
          }
          if (lowerRight.y === 0) {
              lowerRight.y = 1;
          }
          
          this._oldBounds.set(upperLeft, lowerRight);
          this.bounds.set(upperLeft, lowerRight);
          
          /**initialize magnets */
          
          var magnets = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "magnets");
          
          if (magnets && magnets.length > 0) {
          
              magnets = $A(magnets[0].getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "magnet"));
              
              var me = this;
              magnets.each(function(magnetElem){
                  var magnet = new ORYX.Core.Controls.Magnet({
                      eventHandlerCallback: me.eventHandlerCallback
                  });
                  var cx = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cx"));
                  var cy = parseFloat(magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cy"));
                  magnet.bounds.centerMoveTo({
                      x: cx - offsetX,
                      y: cy - offsetY
                  });
                  
                  //get anchors
                  var anchors = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "anchors");
                  if (anchors) {
                      anchors = anchors.replace("/,/g", " ");
                      anchors = anchors.split(" ").without("");
                      for(var i = 0; i < anchors.length; i++) {
  						switch(anchors[i].toLowerCase()) {
  							case "left":
  								magnet.anchorLeft = true;
  								break;
  							case "right":
  								magnet.anchorRight = true;
  								break;
  							case "top":
  								magnet.anchorTop = true;
  								break;
  							case "bottom":
  								magnet.anchorBottom = true;
  								break;
  						}
  					}
                  }
                  
                  me.add(magnet);
                  
                  //check, if magnet is default magnet
                  if (!this._defaultMagnet) {
                      var defaultAttr = magnetElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "default");
                      if (defaultAttr && defaultAttr.toLowerCase() === "yes") {
                          me._defaultMagnet = magnet;
                      }
                  }
              });
          }
          else {
              // Add a Magnet in the Center of Shape			
              var magnet = new ORYX.Core.Controls.Magnet();
              magnet.bounds.centerMoveTo(this.bounds.width() / 2, this.bounds.height() / 2);
              this.add(magnet);
          }
          
          /**initialize docker */
          var dockerElem = svgDocument.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_ORYX, "docker");
          
          if (dockerElem && dockerElem.length > 0) {
              dockerElem = dockerElem[0];
              var docker = this.createDocker();
              var cx = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cx"));
              var cy = parseFloat(dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "cy"));
              docker.bounds.centerMoveTo({
                  x: cx - offsetX,
                  y: cy - offsetY
              });
              
              //get anchors
              var anchors = dockerElem.getAttributeNS(ORYX.CONFIG.NAMESPACE_ORYX, "anchors");
              if (anchors) {
                  anchors = anchors.replace("/,/g", " ");
                  anchors = anchors.split(" ").without("");
                  
  				for(var i = 0; i < anchors.length; i++) {
  					switch(anchors[i].toLowerCase()) {
  						case "left":
  							docker.anchorLeft = true;
  							break;
  						case "right":
  							docker.anchorRight = true;
  							break;
  						case "top":
  							docker.anchorTop = true;
  							break;
  						case "bottom":
  							docker.anchorBottom = true;
  							break;
  					}
  				}
              }
          }
          
          /**initialize labels*/
          var textElems = svgNode.getElementsByTagNameNS(ORYX.CONFIG.NAMESPACE_SVG, 'text');
          $A(textElems).each((function(textElem){
              var label = new ORYX.Core.SVG.Label({
                  textElement: textElem,
  				shapeId: this.id
              });
              label.x -= offsetX;
              label.y -= offsetY;
              this._labels[label.id] = label;
  			
  			label.registerOnChange(this.layout.bind(this));
  			
          }).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<length;i++) {
  			var lineP1 = this.dockers[i-1].bounds.center();
  			var lineP2 = this.dockers[i].bounds.center();
  			this.relativizePoint(lineP1);
  			this.relativizePoint(lineP2);
  			
  			var nodeCenterPoint = node.bounds.center();
  			var distance = ORYX.Core.Math.distancePointLinie(
  															lineP1,
  															lineP2, 
  															nodeCenterPoint, 
  															true);
  			
  			if((distance || distance == 0) && ((!smallestDistance && smallestDistance != 0) 
  						|| distance < smallestDistance)) {
  				
  				smallestDistance = distance;
  				
  				this.attachedNodePositionData[node.getId()].segment.docker1 = 
  													this.dockers[i-1];
  				this.attachedNodePositionData[node.getId()].segment.docker2 = 
  													this.dockers[i];
  	
  			}
  			
  			/* Either the distance does not match the segment or the distance
  			 * between docker1 and docker2 is 0
  			 * 
  			 * In this case choose the nearest docker as attaching point.
  			 * 
  			 */
  			if(!distance && !smallestDistance && smallestDistance != 0) {
  				(ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP1)
  					< ORYX.Core.Math.getDistancePointToPoint(nodeCenterPoint, lineP2)) ?
  					this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 0 :
  					this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 = 1;
  				this.attachedNodePositionData[node.getId()].segment.docker1 = 
  													this.dockers[i-1];
  				this.attachedNodePositionData[node.getId()].segment.docker2 = 
  													this.dockers[i];
  			}
  		}
  		
  		/* Calculate position on edge segment for the node */
  		if(smallestDistance || smallestDistance == 0) {
  			this.attachedNodePositionData[node.getId()].relativDistanceFromDocker1 =
  			this.getLineParameterForPosition(
  					this.attachedNodePositionData[node.getId()].segment.docker1,
  					this.attachedNodePositionData[node.getId()].segment.docker2,
  					node);
  		}
  	},
  	
  	
  	/**
  	 *
  	 * @param {ORYX.Core.Node|Object} node or position
  	 * @return {Object} An object with the following attribute: {ORYX.Core.Docker} fromDocker, {ORYX.Core.Docker} toDocker, {X/Y} position, {int} distance
  	 */
   	findSegment: function(node){
  		
  		var length = this.dockers.length;
  		var result;
  		
  		var nodeCenterPoint = node instanceof ORYX.Core.UIObject ? node.bounds.center() : node;
  			
  		for (i = 1; i < length; i++) {
  			var lineP1 = this.dockers[i - 1].bounds.center();
  			var lineP2 = this.dockers[i].bounds.center();
  			
  			var distance = ORYX.Core.Math.distancePointLinie(lineP1, lineP2, nodeCenterPoint, true);
  			
  			if (typeof distance == "number" && (result === undefined || distance < result.distance)) {
  				result = {
  					distance: distance,
  					fromDocker: this.dockers[i - 1],
  					toDocker: this.dockers[i]
  				}
  				
  			}
  		}
  		return result;
  	},
  	
  	/**
  	 * Returns the value of the scalar to determine the position of the node on 
  	 * line defined by docker1 and docker2.
  	 * 
  	 * @param {point} docker1
  	 * 		The docker that defines the start of the line segment
  	 * @param {point} docker2
  	 * 		The docker that defines the end of the line segment
  	 * @param {ORYX.Core.Node} node
  	 * 		The concerning node
  	 * 
  	 * @return {float} positionParameter
  	 * 		The scalar value to determine the position on the line
  	 */
  	getLineParameterForPosition: function(docker1, docker2, node) {
  		var dockerPoint1 = docker1.bounds.center();
  		var dockerPoint2 = docker2.bounds.center();
  		this.relativizePoint(dockerPoint1);
  		this.relativizePoint(dockerPoint2);
  		
  		var intersectionPoint = ORYX.Core.Math.getPointOfIntersectionPointLine(
  									dockerPoint1,
  									dockerPoint2,
  									node.bounds.center(), true);
  		if(!intersectionPoint) {
  			return 0;
  		}
  		
  		var relativeDistance = 
  			ORYX.Core.Math.getDistancePointToPoint(intersectionPoint, dockerPoint1) /
  			ORYX.Core.Math.getDistancePointToPoint(dockerPoint1, dockerPoint2);
  		
  		return relativeDistance;
  	},
  	/**
  	 * Makes point relative to the upper left of the edge's bound.
  	 * 
  	 * @param {point} point
  	 * 		The point to relativize
  	 */
  	relativizePoint: function(point) {
  		point.x -= this.bounds.upperLeft().x;
  		point.y -= this.bounds.upperLeft().y;		
  	},
  	
  	/**
  	 * Move the first and last docker and calls the refresh method.
  	 * Attention: This does not calculates intersection point between the
  	 * edge and the bounded nodes. This only works if only the nodes are
  	 * moves.
  	 *
  	 */
  	optimizedUpdate: function(){
  		
  		var updateDocker = function(docker){
  			if (!docker._dockedShape || !docker._dockedShapeBounds)
  				return;
  			var off = {
  			    x: docker._dockedShape.bounds.a.x - docker._dockedShapeBounds.a.x,
  			    y: docker._dockedShape.bounds.a.y - docker._dockedShapeBounds.a.y
  			};
  			docker.bounds.moveBy(off);
  			docker._dockedShapeBounds.moveBy(off);
  		}
  		
  		updateDocker(this.dockers.first());
  		updateDocker(this.dockers.last());
  		
  		this.refresh();
  	},
      
      refresh: function(){
          //call base class refresh method
          arguments.callee.$.refresh.apply(this, arguments);
          
          //TODO consider points for marker mids
          var lastPoint;
          this._paths.each((function(path, index){
              var dockers = this._dockersByPath[path.id];
              var c = undefined;
  			var d = undefined;
              if (lastPoint) {
                  d = "M" + lastPoint.x + " " + lastPoint.y;
              }
              else {
                  c = dockers[0].bounds.center();
                  lastPoint = c;
                  
                  d = "M" + c.x + " " + c.y;
              }
              
              for (var i = 1; i < dockers.length; i++) {
                  // for each docker, draw a line to the center
                  c = dockers[i].bounds.center();
                  d = d + "L" + c.x + " " + c.y + " ";
                  lastPoint = c;
              }
              
              path.setAttributeNS(null, "d", d);
              this._interactionPaths[index].setAttributeNS(null, "d", d);
              
          }).bind(this));
  		
  		
  		/* move child shapes of an edge */
  		if(this.getChildNodes().length > 0) {
  	        var x = this.bounds.upperLeft().x;
  	        var y = this.bounds.upperLeft().y;
  	        
  			this.node.firstChild.childNodes[1].setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")");
  		}
  		
      },
      
      /**
       * Calculate the Border Intersection Point between two points
       * @param {PointA}
       * @param {PointB}
       */
      getIntersectionPoint: function(){
      
          var length = Math.floor(this.dockers.length / 2)
          
          return ORYX.Core.Math.midPoint(this.dockers[length - 1].bounds.center(), this.dockers[length].bounds.center())
      },
      
  	/**
       * Returns TRUE if the bounds is over the edge
       * @param {Bounds}
       *
       */
      isBoundsIncluded: function(bounds){
  		var dockers = this.dockers, size = dockers.length;
  		return dockers.any(function(docker, i){
  			if (i == size-1){ return false; }
  			var a = docker.bounds.center();
  			var b = dockers[i+1].bounds.center();
  			
  			return ORYX.Core.Math.isRectOverLine(a.x, a.y, b.x, b.y, bounds.a.x, bounds.a.y, bounds.b.x, bounds.b.y);
  		});
  	},
      
      /**
       * Calculate if the point is inside the Shape
       * @param {PointX}
       * @param {PointY} 
       */
      isPointIncluded: function(pointX, pointY){
      
          var isbetweenAB = this.absoluteBounds().isIncluded(pointX, pointY, 
  												ORYX.CONFIG.OFFSET_EDGE_BOUNDS);
          
  		var isPointIncluded = undefined;
  		
          if (isbetweenAB && this.dockers.length > 0) {
  		
  			var i = 0;
  			var point1, point2;
  			
  			
  			do {
  			
  				point1 = this.dockers[i].bounds.center();
  				point2 = this.dockers[++i].bounds.center();
  				
  				isPointIncluded = ORYX.Core.Math.isPointInLine(pointX, pointY, 
  											point1.x, point1.y, 
  											point2.x, point2.y, 
  											ORYX.CONFIG.OFFSET_EDGE_BOUNDS);
  				
  			} while (!isPointIncluded && i < this.dockers.length - 1)
  			
  		}
  		
  		return isPointIncluded;
  
      },
    
      
      /**
       * Calculate if the point is over an special offset area
       * @param {Point}
       */
      isPointOverOffset: function(){
  		return  false
  	},
  	
  	/**
  	 * Returns TRUE if the given node
  	 * is a child node of the shapes node
  	 * @param {Element} node
  	 * @return {Boolean}
  	 *
  	 */
  	containsNode: function(node){
  		if (this._paths.include(node) || 
         		this._interactionPaths.include(node)){
  			return true;		
  		}	
  		return false;
  	},
  	
  	/**
  	* Returns the angle of the line between two dockers
  	* (0 - 359.99999999)
  	*/
  	_getAngle: function(docker1, docker2) {
  		var p1 = docker1 instanceof ORYX.Core.Controls.Docker ? docker1.absoluteCenterXY() : docker1;
  		var p2 = docker2 instanceof ORYX.Core.Controls.Docker ? docker2.absoluteCenterXY() : docker2;
  		
  		return ORYX.Core.Math.getAngle(p1, p2);
  	},
  	    
      alignDockers: function(){
          this._update(true);
          
          var firstPoint = this.dockers.first().bounds.center();
          var lastPoint = this.dockers.last().bounds.center();
          
          var deltaX = lastPoint.x - firstPoint.x;
          var deltaY = lastPoint.y - firstPoint.y;
          
          var numOfDockers = this.dockers.length - 1;
          
          this.dockers.each((function(docker, index){
              var part = index / numOfDockers;
              docker.bounds.unregisterCallback(this._dockerChangedCallback);
              docker.bounds.moveTo(firstPoint.x + part * deltaX, firstPoint.y + part * deltaY);
              docker.bounds.registerCallback(this._dockerChangedCallback);
          }).bind(this));
          
          this._dockerChanged();
      },
      
  	add: function(shape){
          arguments.callee.$.add.apply(this, arguments);
  		
  		// If the new shape is a Docker which is not contained
  		if (shape instanceof ORYX.Core.Controls.Docker && this.dockers.include(shape)){
  			// Add it to the dockers list ordered by paths		
  			var pathArray = this._dockersByPath.values()[0];
  			if (pathArray) {
  				pathArray.splice(this.dockers.indexOf(shape), 0, shape);
  			}
  			
  			/* Perform nessary adjustments on the edge's child shapes */
  			this.handleChildShapesAfterAddDocker(shape);
  		}
  	},
  	
  	/**
  	 * Performs nessary adjustments on the edge's child shapes.
  	 * 
  	 * @param {ORYX.Core.Controls.Docker} docker
  	 * 		The added docker
  	 */
  	handleChildShapesAfterAddDocker: function(docker) {
  		/* Ensure type of Docker */
  		if(!docker instanceof ORYX.Core.Controls.Docker) {return undefined;}
  		
  		var index = this.dockers.indexOf(docker);
  		if(!(0 < index && index < this.dockers.length - 1)) {
  		/* Exception: Expect added docker between first and last node of the edge */
  			return undefined;
  		} 
  			
  		/* Get child nodes concerning the segment of the new docker */
  		var startDocker = this.dockers[index-1];
  		var endDocker = this.dockers[index+1];
  		
  		/* Adjust the position of edge's child nodes */
  		var segmentElements = 
  			this.getAttachedNodePositionDataForSegment(startDocker, endDocker);
  		
  		var lengthSegmentPart1 = ORYX.Core.Math.getDistancePointToPoint(
  										startDocker.bounds.center(),
  										docker.bounds.center());
  		var lengthSegmentPart2 = ORYX.Core.Math.getDistancePointToPoint(
  										endDocker.bounds.center(),
  										docker.bounds.center());
  										
  		if(!(lengthSegmentPart1 + lengthSegmentPart2)) {return;}
  		
  		var relativDockerPosition = lengthSegmentPart1 / (lengthSegmentPart1 + lengthSegmentPart2);
  			
  		segmentElements.each(function(nodePositionData) {
  			/* Assign child node to the new segment */
  			if(nodePositionData.value.relativDistanceFromDocker1 < relativDockerPosition) {
  				/* Case: before added Docker */
  				nodePositionData.value.segment.docker2 = docker;
  				nodePositionData.value.relativDistanceFromDocker1 = 
  					nodePositionData.value.relativDistanceFromDocker1 / relativDockerPosition;
  			} else {
  				/* Case: after added Docker */
  				nodePositionData.value.segment.docker1 = docker;
  				var newFullDistance = 1 - relativDockerPosition;
  				var relativPartOfSegment = 
  							nodePositionData.value.relativDistanceFromDocker1
  							- relativDockerPosition;
  				
  				nodePositionData.value.relativDistanceFromDocker1 = 
  										relativPartOfSegment / newFullDistance;
  				
  			}
  		})
  		
  		
  		// Update all labels reference points
  		this.getLabels().each(function(label){
  
  			var ref = label.getReferencePoint();
  			if (!ref) {
  				return;
  			}
  			var index = this.dockers.indexOf(docker);
  			if (index >= ref.segment.fromIndex && index <= ref.segment.toIndex){
  				
  				var segment = this.findSegment(ref.intersection);
  				if (!segment){ 
  					// Choose whether the first of the last segment
  					segment.fromDocker = ref.segment.fromIndex >= (this.dockers.length/2) ? this.dockers[0] : this.dockers[this.dockers.length-2]; 
  					segment.toDocker = this.dockers[this.dockers.indexOf(from)+1]; // The next one if the to docker
  				}
  				
  				var fromPosition = segment.fromDocker.bounds.center(), toPosition = segment.toDocker.bounds.center();
  			
  				var intersection = ORYX.Core.Math.getPointOfIntersectionPointLine(
  										fromPosition, 		// P1 - Center of the first docker
  										toPosition, 		// P2 - Center of the second docker
  										ref.intersection, 	// P3 - Center of the label
  										true);
  				//var oldDistance = ORYX.Core.Math.getDistanceBetweenTwoPoints(ref.segment.fromPosition, ref.segment.toPosition, ref.intersection);
  				//intersection = ORYX.Core.Math.getPointBetweenTwoPoints(fromPosition, toPosition, isNaN(oldDistance) ? 0.5 : (lengthOld*oldDistance)/lengthNew);
  					
  				// Update the reference point
  				this.updateReferencePointOfLabel(label, intersection, segment.fromDocker, segment.toDocker, true);
  			}
  		}.bind(this));
  		
  		/* Update attached nodes visual representation */
  		this.refreshAttachedNodes();
  	},
  	
  	/**
  	 *	Returns elements from {@link attachedNodePositiondata} that match the
  	 *  segement defined by startDocker and endDocker.
  	 *  
  	 *  @param {ORYX.Core.Controls.Docker} startDocker
  	 *  	The docker defining the begin of the segment.
  	 *  @param {ORYX.Core.Controls.Docker} endDocker
  	 *  	The docker defining the begin of the segment.
  	 *  
  	 *  @return {Hash} attachedNodePositionData
  	 *  	Child elements matching the segment
  	 */
  	getAttachedNodePositionDataForSegment: function(startDocker, endDocker) {
  		/* Ensure that the segment is defined correctly */
  		if(!((startDocker instanceof ORYX.Core.Controls.Docker) 
  			&& (endDocker instanceof ORYX.Core.Controls.Docker))) {
  				return [];
  			}
  			
  		/* Get elements of the segment */
  		var elementsOfSegment = 
  			this.attachedNodePositionData.findAll(function(nodePositionData) {
  				return nodePositionData.value.segment.docker1 === startDocker &&
  						nodePositionData.value.segment.docker2 === endDocker;
  			});
  		
  		/* Return a Hash in each case */
  		if(!elementsOfSegment) {return [];}
  		
  		return elementsOfSegment;
  	},
  	
  	/**
  	 * Removes an edge's child shape
  	 */
  	remove: function(shape) {
  		arguments.callee.$.remove.apply(this, arguments);
  		
  		if(this.attachedNodePositionData[shape.getId()]) {
  			delete this.attachedNodePositionData[shape.getId()];
  		}
  		
  		/* Adjust child shapes if neccessary */
  		if(shape instanceof ORYX.Core.Controls.Docker) {
  			this.handleChildShapesAfterRemoveDocker(shape);
  		}
  	},
  	
  	updateReferencePointOfLabel: function(label, intersection, from, to, dirty){
  		if (!label.getReferencePoint() || !label.isVisible) {
  			return;
  		}
  		
  		var ref = label.getReferencePoint();
  		
  		//
  		if (ref.orientation && ref.orientation !== "ce"){
  			var angle = this._getAngle(from, to);
  			if (ref.distance >= 0){
  				if(angle == 0){
  					label.horizontalAlign("left");//ref.orientation == "lr" ? "right" : "left");
  					label.verticalAlign("bottom");
  				} else if (angle > 0 && angle < 90){
  					label.horizontalAlign("right");
  					label.verticalAlign("bottom");
  				} else if (angle == 90){
  					label.horizontalAlign("right");
  					label.verticalAlign("top");//ref.orientation == "lr" ? "bottom" : "top");
  				} else if (angle > 90 && angle < 180){
  					label.horizontalAlign("right");
  					label.verticalAlign("top");
  				} else if (angle == 180){
  					label.horizontalAlign("left");//ref.orientation == "ur" ? "right" : "left");
  					label.verticalAlign("top");
  				} else if (angle > 180 && angle < 270){
  					label.horizontalAlign("left");
  					label.verticalAlign("top");				
  				} else if (angle == 270){
  					label.horizontalAlign("left");
  					label.verticalAlign("top");//ref.orientation == "ll" ? "bottom" : "top");
  				} else if (angle > 270 && angle <= 360){
  					label.horizontalAlign("left");
  					label.verticalAlign("bottom");
  				}
  			} else {
  				if(angle == 0){
  					label.horizontalAlign("left");//ref.orientation == "ur" ? "right" : "left");
  					label.verticalAlign("top");
  				} else if (angle > 0 && angle < 90){
  					label.horizontalAlign("left");
  					label.verticalAlign("top");
  				} else if (angle == 90){
  					label.horizontalAlign("left");
  					label.verticalAlign("top");//ref.orientation == "ll" ? "bottom" : "top");
  				} else if (angle > 90 && angle < 180){
  					label.horizontalAlign("left");
  					label.verticalAlign("bottom");
  				} else if (angle == 180){
  					label.horizontalAlign("left");//ref.orientation == "lr" ? "right" : "left");
  					label.verticalAlign("bottom");
  				} else if (angle > 180 && angle < 270){
  					label.horizontalAlign("right");
  					label.verticalAlign("bottom");
  				} else if (angle == 270){
  					label.horizontalAlign("right");
  					label.verticalAlign("top");//ref.orientation == "lr" ? "bottom" : "top");
  				} else if (angle > 270 && angle <= 360){
  					label.horizontalAlign("right");
  					label.verticalAlign("top");
  				}			
  			}
  			ref.iorientation = ref.iorientation || ref.orientation;
  			ref.orientation = (label.verticalAlign()=="top"?"u":"l") + (label.horizontalAlign()=="left"?"l":"r");
  		}
  	
  		label.setReferencePoint(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("<?xml") ? "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializedData : serializedData;
              
              return serializedData;
              
          }catch (error) {
              return -1;
          }
          
  	},
  	
  	/**
  	 * Opens a new window that shows the given XML content.
  	 * @methodOf ORYX.Plugins.AbstractPlugin.prototype
  	 * @param {Object} content The XML content to be shown.
  	 * @example
  	 * openDownloadWindow( "my.xml", "<exampleXML />" );
  	 */
  	openXMLWindow: function(content) {
  		var win = window.open(
  		   'data:application/xml,' + encodeURIComponent(
  		     content
  		   ),
  		   '_blank', "resizable=yes,width=600,height=600,toolbar=0,scrollbars=yes"
  		);
  	},
  	
      /**
       * Opens a download window for downloading the given content.
       * @methodOf ORYX.Plugins.AbstractPlugin.prototype
       * @param {String} filename The content's file name
       * @param {String} content The content to download
       */
  	openDownloadWindow: function(filename, content) {
  		var win = window.open("");
  		if (win != null) {
  			win.document.open();
  			win.document.write("<html><body>");
  			var submitForm = win.document.createElement("form");
  			win.document.body.appendChild(submitForm);
  			
  			var createHiddenElement = function(name, value) {
  				var newElement = document.createElement("input");
  				newElement.name=name;
  				newElement.type="hidden";
  				newElement.value = value;
  				return newElement
  			}
  			
  			submitForm.appendChild( createHiddenElement("download", content) );
  			submitForm.appendChild( createHiddenElement("file", filename) );
  			
  			
  			submitForm.method = "POST";
  			win.document.write("</body></html>");
  			win.document.close();
  			submitForm.action= ORYX.PATH + "/download";
  			submitForm.submit();
  		}		
  	},
      
      /**
       * Serializes DOM.
       * @methodOf ORYX.Plugins.AbstractPlugin.prototype
       * @type {String} Serialized DOM
       */
      getSerializedDOM: function(){
          // Force to set all resource IDs
          var serializedDOM = DataManager.serializeDOM( this.facade );
  
          //add namespaces
          serializedDOM = '<?xml version="1.0" encoding="utf-8"?>' +
          '<html xmlns="http://www.w3.org/1999/xhtml" ' +
          'xmlns:b3mn="http://b3mn.org/2007/b3mn" ' +
          'xmlns:ext="http://b3mn.org/2007/ext" ' +
          'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" ' +
          'xmlns:atom="http://b3mn.org/2007/atom+xhtml">' +
          '<head profile="http://purl.org/NET/erdf/profile">' +
          '<link rel="schema.dc" href="http://purl.org/dc/elements/1.1/" />' +
          '<link rel="schema.dcTerms" href="http://purl.org/dc/terms/ " />' +
          '<link rel="schema.b3mn" href="http://b3mn.org" />' +
          '<link rel="schema.oryx" href="http://oryx-editor.org/" />' +
          '<link rel="schema.raziel" href="http://raziel.org/" />' +
          '<base href="' +
          location.href.split("?")[0] +
          '" />' +
          '</head><body>' +
          serializedDOM +
          '</body></html>';
          
          return serializedDOM;
      },
      
      /**
       * Sets the editor in read only mode: Edges/ dockers cannot be moved anymore,
       * shapes cannot be selected anymore.
       * @methodOf ORYX.Plugins.AbstractPlugin.prototype
       */
      enableReadOnlyMode: function(){
          //Edges cannot be moved anymore
          this.facade.disableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN);
          
          // Stop the user from editing the diagram while the plugin is active
          this._stopSelectionChange = function(){
              if(this.facade.getSelection().length > 0) {
                  this.facade.setSelection([]);
              }
          };
          this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this));
      },
      /**
       * Disables read only mode, see @see
       * @methodOf ORYX.Plugins.AbstractPlugin.prototype
       * @see ORYX.Plugins.AbstractPlugin.prototype.enableReadOnlyMode
       */
      disableReadOnlyMode: function(){
          // Edges can be moved now again
          this.facade.enableEvent(ORYX.CONFIG.EVENT_MOUSEDOWN);
          
          if (this._stopSelectionChange) {
              this.facade.unregisterOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, this._stopSelectionChange.bind(this));
              this._stopSelectionChange = undefined;
          }
      },
      
      /**
       * Extracts RDF from DOM.
       * @methodOf ORYX.Plugins.AbstractPlugin.prototype
       * @type {String} Extracted RFD. Null if there are transformation errors.
       */
      getRDFFromDOM: function(){
          //convert to RDF
  		try {
  			var xsl = "";
  			source=ORYX.PATH + "lib/extract-rdf.xsl";
  			new Ajax.Request(source, {
  				asynchronous: false,
  				method: 'get',
  				onSuccess: function(transport){
  					xsl = transport.responseText
  				}.bind(this),
  				onFailure: (function(transport){
  					ORYX.Log.error("XSL load failed" + transport);
  				}).bind(this)
  			});
  			/*
  			 var 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 = 	"<div class='mask-logo' style='"+ss+"'>"+
  									//"<div><img style='"+simg+"' src='"+ORYX.CONFIG.EXPLORER_PATH+"/src/img/signavio/smoky/logo.png' /></div>"+
  									"<span style='font-size:14px;line-height:24px;color:#555;'>正在加载流程设计器，请稍等...</span><div></div>"+
  									"<span class='mask-text' style='"+stext+";line-height:24px;'>"+
  										"<span class='mask-title' style='"+stitle+"'>Editor</span>"+
  										"<span class='mask-version' style='"+sversion+"'>Version "+Signavio.Core.Version+"</span>"+
  										"<img style='"+sloading+"' src='"+(ORYX.CONFIG.BLANK_IMAGE||Ext.BLANK_IMAGE_URL)+"'/>"+
  									"</span>" +
  								"</div>";
  
  		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 = '<img src="' + this.image + '" title="' + this.text + '"' + ( this.imageStyle ? ' style="' + this.imageStyle + '"/>': '/>')
  				} 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<possibleMorphs.size(); i++) {
  						canContain = this.facade.getRules().canContain({
  							containingShape:candidate, 
  							containedStencil:possibleMorphs[i]
  						});
  						if(canContain) {
  							option.backupOptions = Object.clone(option);
  							option.type = possibleMorphs[i].id();
  							option.namespace = possibleMorphs[i].namespace();
  							break;
  						}
  					}
  				}
  					
  				this._currentReference = canContain ? candidate : undefined;
  					
  	
  			} else { //Edge
  			
  				var curCan = candidate, orgCan = candidate;
  				var canConnect = false;
  				while(!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)){
  					candidate = curCan;
  					//check connection rules
  					canConnect = this.facade.getRules().canConnect({
  											sourceShape: this.currentShapes.first(), 
  											edgeStencil: stencil, 
  											targetShape: curCan
  											});	
  					curCan = curCan.parent;
  				}
  
  			 	// if not canConnect, try to find a morph which can be connected
  				if(!canConnect) {
  					
  					candidate = orgCan;
  					var possibleMorphs = this.facade.getRules().morphStencils({stencil: stencil});
  					for(var i=0; i<possibleMorphs.size(); i++) {
  						var curCan = candidate;
  						var canConnect = false;
  						while(!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)){
  							candidate = curCan;
  							//check connection rules
  							canConnect = this.facade.getRules().canConnect({
  														sourceShape:	this.currentShapes.first(), 
  														edgeStencil:	possibleMorphs[i], 
  														targetShape:	curCan
  													});	
  							curCan = curCan.parent;
  						}
  						if(canConnect) {
  							option.backupOptions = Object.clone(option);
  							option.type = possibleMorphs[i].id();
  							option.namespace = possibleMorphs[i].namespace();
  							break;
  						} else {
  							candidate = orgCan;
  						}
  					}
  				}
  										
  				this._currentReference = canConnect ? candidate : undefined;		
  				
  			}	
  
  			this.facade.raiseEvent({
  											type:		ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
  											highlightId:'shapeMenu',
  											elements:	[candidate],
  											color:		this._currentReference ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
  										});
  												
  			var pr = dragZone.getProxy();
  			pr.setStatus(this._currentReference ? pr.dropAllowed : pr.dropNotAllowed );
  			pr.sync();
  										
  		}
  		
  		this._lastOverElement = el;
  		
  		return false;
  	},	
  
  	afterDragging: function(dragZone, target, event) {
  		
  		if (!(this.currentShapes instanceof Array)||this.currentShapes.length<=0) {
  			return;
  		}
  		var sourceShape = this.currentShapes;
  		
  		this._lastOverElement = undefined;
  		
  		// Hide the highlighting
  		this.facade.raiseEvent({type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'shapeMenu'});
  		
  		// Check if drop is allowed
  		var proxy = dragZone.getProxy()
  		if(proxy.dropStatus == proxy.dropNotAllowed) { return this.facade.updateSelection();}
  				
  		// Check if there is a current Parent
  		if(!this._currentReference) { return }
  				
  		var option = Ext.dd.Registry.getHandle(target.DDM.currentTarget);
  		option['parent'] = this._currentReference;
  
  		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;
  		// Correcting the ScrollOffset
  		pos.x -= document.documentElement.scrollLeft;
  		pos.y -= document.documentElement.scrollTop;
  
  		var parentAbs = this._currentReference.absoluteXY();
  		pos.x -= parentAbs.x;
  		pos.y -= parentAbs.y;
  		
  		// If the ctrl key is not pressed, 
  		// snapp the new shape to the center 
  		// if it is near to the center of the other shape
  		if (!event.ctrlKey){
  			// Get the center of the shape
  			var cShape = this.currentShapes[0].bounds.center();
  			// Snapp +-20 Pixel horizontal to the center 
  			if (20 > 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("<a href='") < 0) {
  			// Shows the Value in the Grid in each Line
  			value = String(value).gsub("<", "&lt;");
  			value = String(value).gsub(">", "&gt;");
  			value = String(value).gsub("%", "&#37;");
  			value = String(value).gsub("&", "&amp;");
  
  			if(record.data.gridProperties.type == ORYX.CONFIG.TYPE_COLOR) {
  				value = "<div class='prop-background-color' style='background-color:" + value + "' />";
  			}			
  
  			record.data.icons.each(function(each) {
  				if(each.name == value) {
  					if(each.icon) {
  						value = "<img src='" + each.icon + "' /> " + 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: '<tpl for="."><div class="x-combo-list-item">{[(values.icon) ? "<img src=\'" + values.icon + "\' />" : ""]} {title}</div></tpl>',
  						        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 = "<a href='" + attribute + "' target='_blank'>" + attribute.split("://")[1] + "</a>"
  				}
  				
  				// 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 <b>appended</b> 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('<option value="{value}">{value}</option>');
  				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 <b>appended</b> 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('<option value="{value}">{value}</option>');
  				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('<option value="{value}">{value}</option>');
  					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 '<center><img src="'+ORYX.CONFIG.ROOT_PATH+'images/processimagepreview.png" onclick="new ImageViewer({title: \'Process Image\', width: \'1100\', height: \'900\', autoScroll: true, fixedcenter: true, src: \''+val+'\',hideAction: \'close\'}).show();" alt="Click to view Process Image"/></center>';
  									} else {
  										return "<center>Process image not available.</center>";
  									}
  								}
  							}]),
  							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: '<center>Select Process Id and click "Save" to select.</center>',
  							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 );
  					} else {
  						Ext.Msg.alert('Unable to find other processes in pacakge.');
  					}
  				} catch(e) {
  					Ext.Msg.alert('Error resolving other process info :\n' + e);
  				}
  			}.bind(this),
  			failure: function(){
  				loadProcessesMask.hide();
  				Ext.Msg.alert('Error resolving other process info.');
  			}
  		});
  	}
  });
  
  Ext.form.ListenerDefinitionField = Ext.extend(Ext.form.TriggerField, {
  	onTriggerClick : function() {
  		if(this.disabled){
  			return;
  		}
  	        
  		var ListenerDef = Ext.data.Record.create([{
  			name: 'class'
  		}, {
  			name: 'expression'
  		}, {
  			name: 'event'
  		}]);
  	    
  		var calldefsProxy = new Ext.data.MemoryProxy({
  			root: []
  		});
  	    
  		var calldefs = new Ext.data.Store({
  			autoDestroy: true,
  			reader: new Ext.data.JsonReader({
  				root: "root"
  			}, ListenerDef),
  			proxy: calldefsProxy,
  			sorters: [{
  				property: 'event',
  				direction:'ASC'
  			}]
  		});
  		calldefs.load();
  
  		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: 'class',
  				editor: new Ext.form.TextField({ allowBlank: false, disabled: true })
  			}, {
  				id: 'revi',
  				header: 'Revision',
  				width: 200,
  				dataIndex: 'expression',
  				editor: new Ext.form.TextField({ allowBlank: false, disabled: true })
  			},{
  				id: 'pim',
  				header: 'Process Image',
  				width: 250,
  				dataIndex: 'event',
  				renderer: function(val) {
  					if(val && val.length > 0) {
  						return '<center><img src="'+ORYX.CONFIG.ROOT_PATH+'images/processimagepreview.png" onclick="new ImageViewer({title: \'Process Image\', width: \'1100\', height: \'900\', autoScroll: true, fixedcenter: true, src: \''+val+'\',hideAction: \'close\'}).show();" alt="Click to view Process Image"/></center>';
  					} else {
  						return "<center>Process image not available.</center>";
  					}
  				}
  			}]),
  			autoHeight: true
  		});
  
  		var calledElementsPanel = new Ext.Panel({
  			id: 'calledElementsPanel',
  			title: '<center>Select Process Id and click "Save" to select.</center>',
  			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<sortedEl.length; i++){
  					var el			= sortedEl[i].el;
  					var p 			= el.parent;			
  					var oldIndex 	= p.children.indexOf(el);
  					var newIndex 	= p.children.indexOf(sortedEl[i].previous);
  					newIndex		= newIndex || 0
  					p.children 	= p.children.insertFrom(oldIndex, newIndex)			
  					el.node.parentNode.insertBefore(el.node, el.node.parentNode.childNodes[newIndex+1]);
  				}
  
  				// Reset the selection
  				this.facade.setSelection( this.elements )
  			}
  		});
  	
  		// Instanziate the dockCommand
  		var command = new zLevelCommand(callback, this.facade.getSelection(), this.facade);
  		if( event.excludeCommand ){
  			command.execute();
  		} else {
  			this.facade.executeCommands( [command] );	
  		}
  		
  	},
  
  	setToTop: function(elements) {
  
  		// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
  		var tmpElem =  elements.sortBy( function(value, index) {
  			var t = $A(value.node.parentNode.childNodes);
  			return t.indexOf(value.node);
  		});
  		// Sortiertes Array wird nach oben verschoben.
  		tmpElem.each( function(value) {
  			var p = value.parent;
  			if (p.children.last() === value){
  				return;
  			}
  			p.children = p.children.without( value )
  			p.children.push(value);
  			value.node.parentNode.appendChild(value.node);			
  		});
  	},
  
  	setToBack: function(elements) {
  		// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
  		var tmpElem =  elements.sortBy( function(value, index) {
  			var t = $A(value.node.parentNode.childNodes);
  			return t.indexOf(value.node);
  		});
  
  		tmpElem = tmpElem.reverse();
  
  		// Sortiertes Array wird nach unten verschoben.
  		tmpElem.each( function(value) {
  			var p = value.parent
  			p.children = p.children.without( value )
  			p.children.unshift( value );
  			value.node.parentNode.insertBefore(value.node, value.node.parentNode.firstChild);
  		});
  		
  		
  	},
  
  	setBackward: function(elements) {
  		// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
  		var tmpElem =  elements.sortBy( function(value, index) {
  			var t = $A(value.node.parentNode.childNodes);
  			return t.indexOf(value.node);
  		});
  
  		// Reverse the elements
  		tmpElem = tmpElem.reverse();
  		
  		// Delete all Nodes who are the next Node in the nodes-Array
  		var compactElem = tmpElem.findAll(function(el) {return !tmpElem.some(function(checkedEl){ return checkedEl.node == el.node.previousSibling})});
  		
  		// Sortiertes Array wird nach eine Ebene nach oben verschoben.
  		compactElem.each( function(el) {
  			if(el.node.previousSibling === null) { return; }
  			var p 		= el.parent;			
  			var index 	= p.children.indexOf(el);
  			p.children 	= p.children.insertFrom(index, index-1)			
  			el.node.parentNode.insertBefore(el.node, el.node.previousSibling);
  		});
  		
  		
  	},
  
  	setForward: function(elements) {
  		// Sortieren des Arrays nach dem Index des SVGKnotens im Bezug auf dem Elternknoten.
  		var tmpElem =  elements.sortBy( function(value, index) {
  			var t = $A(value.node.parentNode.childNodes);
  			return t.indexOf(value.node);
  		});
  
  
  		// Delete all Nodes who are the next Node in the nodes-Array
  		var compactElem = tmpElem.findAll(function(el) {return !tmpElem.some(function(checkedEl){ return checkedEl.node == el.node.nextSibling})});
  	
  			
  		// Sortiertes Array wird eine Ebene nach unten verschoben.
  		compactElem.each( function(el) {
  			var nextNode = el.node.nextSibling		
  			if(nextNode === null) { return; }
  			var index 	= el.parent.children.indexOf(el);
  			var p 		= el.parent;
  			p.children 	= p.children.insertFrom(index, index+1)			
  			el.node.parentNode.insertBefore(nextNode, el.node);
  		});
  	},
  
  
  	alignShapes: function(way) {
  
  		var elements = this.facade.getSelection();
  
  		// Set the elements to all Top-Level elements
  		elements = this.facade.getCanvas().getShapesWithSharedParent(elements);
  		// Get only nodes
  		elements = elements.findAll(function(value) {
  			return (value instanceof ORYX.Core.Node)
  		});
  		// Delete all attached intermediate events from the array
  		elements = elements.findAll(function(value) {
  			var d = value.getIncomingShapes()
  			return d.length == 0 || !elements.include(d[0])
  		});
  		if(elements.length < 2) { return; }
  
  		// get bounds of all shapes.
  		var bounds = elements[0].absoluteBounds().clone();
  		elements.each(function(shape) {
  		        bounds.include(shape.absoluteBounds().clone());
  		});
  		
  		// get biggest width and heigth
  		var maxWidth = 0;
  		var maxHeight = 0;
  		elements.each(function(shape){
  			maxWidth = Math.max(shape.bounds.width(), maxWidth);
  			maxHeight = Math.max(shape.bounds.height(), maxHeight);
  		});
  
  		var commandClass = ORYX.Core.Command.extend({
  			construct: function(elements, bounds, maxHeight, maxWidth, way, plugin){
  				this.elements = elements;
  				this.bounds = bounds;
  				this.maxHeight = maxHeight;
  				this.maxWidth = maxWidth;
  				this.way = way;
  				this.facade = plugin.facade;
  				this.plugin = plugin;
  				this.orgPos = [];
  			},
  			setBounds: function(shape, maxSize) {
  				if(!maxSize)
  					maxSize = {width: ORYX.CONFIG.MAXIMUM_SIZE, height: ORYX.CONFIG.MAXIMUM_SIZE};
  
  				if(!shape.bounds) { throw "Bounds not definined." }
  				
  				var newBounds = {
                      a: {x: shape.bounds.upperLeft().x - (this.maxWidth - shape.bounds.width())/2,
                          y: shape.bounds.upperLeft().y - (this.maxHeight - shape.bounds.height())/2},
                      b: {x: shape.bounds.lowerRight().x + (this.maxWidth - shape.bounds.width())/2,
                          y: shape.bounds.lowerRight().y + (this.maxHeight - shape.bounds.height())/2}
  	            }
  				
  				/* If the new width of shape exceeds the maximum width, set width value to maximum. */
  				if(this.maxWidth > maxSize.width) {
  					newBounds.a.x = shape.bounds.upperLeft().x - 
  									(maxSize.width - shape.bounds.width())/2;
  					
  					newBounds.b.x =	shape.bounds.lowerRight().x + (maxSize.width - shape.bounds.width())/2
  				}
  				
  				/* If the new height of shape exceeds the maximum height, set height value to maximum. */
  				if(this.maxHeight > maxSize.height) {
  					newBounds.a.y = shape.bounds.upperLeft().y - 
  									(maxSize.height - shape.bounds.height())/2;
  					
  					newBounds.b.y =	shape.bounds.lowerRight().y + (maxSize.height - shape.bounds.height())/2
  				}
  				
  				/* set bounds of shape */
  				shape.bounds.set(newBounds);
  				
  			},			
  			execute: function(){
  				// align each shape according to the way that was specified.
  				this.elements.each(function(shape, index) {
  					this.orgPos[index] = shape.bounds.upperLeft();
  					
  					var relBounds = this.bounds.clone();
  					var newCoordinates;
  					if (shape.parent && !(shape.parent instanceof ORYX.Core.Canvas) ) {
  						var upL = shape.parent.absoluteBounds().upperLeft();
  						relBounds.moveBy(-upL.x, -upL.y);
  					}
  					
  					switch (this.way) {
  						// align the shapes in the requested way.
  						case ORYX.CONFIG.EDITOR_ALIGN_BOTTOM:
  			                newCoordinates = {
  								x: shape.bounds.upperLeft().x,
  								y: relBounds.b.y - shape.bounds.height()
  							}; break;
  		
  				        case ORYX.CONFIG.EDITOR_ALIGN_MIDDLE:
  			                newCoordinates = {
  								x: shape.bounds.upperLeft().x,
  								y: (relBounds.a.y + relBounds.b.y - shape.bounds.height()) / 2
  							}; break;
  		
  				        case ORYX.CONFIG.EDITOR_ALIGN_TOP:
  			                newCoordinates = {
  								x: shape.bounds.upperLeft().x,
  								y: relBounds.a.y
  							}; break;
  		
  				        case ORYX.CONFIG.EDITOR_ALIGN_LEFT:
  			                newCoordinates = {
  								x: relBounds.a.x,
  								y: shape.bounds.upperLeft().y
  							}; break;
  		
  				        case ORYX.CONFIG.EDITOR_ALIGN_CENTER:
  			                newCoordinates = {
  								x: (relBounds.a.x + relBounds.b.x - shape.bounds.width()) / 2,
  								y: shape.bounds.upperLeft().y
  							}; break;
  		
  				        case ORYX.CONFIG.EDITOR_ALIGN_RIGHT:
  			                newCoordinates = {
  								x: relBounds.b.x - shape.bounds.width(),
  								y: shape.bounds.upperLeft().y
  							}; break;
  							
  						case ORYX.CONFIG.EDITOR_ALIGN_SIZE:
  							if(shape.isResizable) {
  								this.orgPos[index] = {a: shape.bounds.upperLeft(), b: shape.bounds.lowerRight()};
  								this.setBounds(shape, shape.maximumSize);
  							}
  							break;
  					}
  					
  					if (newCoordinates){
  						var offset =  {
  							x: shape.bounds.upperLeft().x - newCoordinates.x,
  							y: shape.bounds.upperLeft().y - newCoordinates.y
  						}
  						// Set the new position
  						shape.bounds.moveTo(newCoordinates);
  						this.plugin.layoutEdges(shape, shape.getAllDockedShapes(),offset);
  						//shape.update()
  					}			
  				}.bind(this));
  		
  				//this.facade.getCanvas().update();
  				//this.facade.updateSelection();
  			},
  			rollback: function(){
  				this.elements.each(function(shape, index) {
  					if (this.way == ORYX.CONFIG.EDITOR_ALIGN_SIZE) {
  						if(shape.isResizable) {shape.bounds.set(this.orgPos[index]);}
  					} else {shape.bounds.moveTo(this.orgPos[index]);}
  				}.bind(this));
  				
  				//this.facade.getCanvas().update();
  				//this.facade.updateSelection();
  			}
  		})
  		
  		var command = new commandClass(elements, bounds, maxHeight, maxWidth, parseInt(way), this);
  		
  		this.facade.executeCommands([command]);	
  	}
  });/**
   * Copyright (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 ;)
  					'<form class="oryx_repository_edit_model" action="#" id="edit_model" onsubmit="return false;">',
  									
  						'<fieldset>',
  							'<p class="description">' + ORYX.I18N.Save.dialogDesciption + '</p>',
  							'<input type="hidden" name="namespace" value="{namespace}" />',
  							'<p><label for="edit_model_title">' + ORYX.I18N.Save.dialogLabelTitle + '</label><input type="text" class="text" name="title" value="{title}" id="edit_model_title" onfocus="this.className = \'text activated\'" onblur="this.className = \'text\'"/></p>',
  							'<p><label for="edit_model_summary">' + ORYX.I18N.Save.dialogLabelDesc + '</label><textarea rows="5" name="summary" id="edit_model_summary" onfocus="this.className = \'activated\'" onblur="this.className = \'\'">{summary}</textarea></p>',
  							(modelMeta.versioning) ? '<p><label for="edit_model_comment">' + ORYX.I18N.Save.dialogLabelComment + '</label><textarea rows="5" name="comment" id="edit_model_comment" onfocus="this.className = \'activated\'" onblur="this.className = \'\'">{comment}</textarea></p>' : '',
  							'<p><label for="edit_model_type">' + ORYX.I18N.Save.dialogLabelType + '</label><input type="text" name="type" class="text disabled" value="{type}" disabled="disabled" id="edit_model_type" /></p>',
  							
  						'</fieldset>',
  					
  					'</form>')
  		
  		// 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<size; ++i) {
  							var sh = allNodes[i];
  							if (sh.resourceId == rId) {
  								for (var prop in sh.properties) {
  									if (prop === pKe) {
  										sh.properties[prop] = this.facade.generateGlossaryURL(entry.glossary, sh.properties[prop]);
  										break;
  									}
  								}
  								break;
  							}
  						}*/
  						
  						
  						// Replace SVG
  						if (entry.property.refToView() && entry.property.refToView().length > 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:"<div style='font-weight:bold;margin-bottom:10px'>"+ORYX.I18N.Save.savedDescription+":</div><span><a href='" + modelURL +"' target='_blank'>" + modelURL + "</a></span>",
  							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<shape.length ;i++){
  				var label = shape[i];
  				label.show();
  			}//);
  			// Show all labels at docked shapes
  			//shape.getAllDockedShapes().each(function(dockedShape) {
  			var allDockedShapes = shape.getAllDockedShapes()
  			for(var i=0; i<allDockedShapes.length ;i++){
  				var dockedShape = allDockedShapes[i];				
  				var labels = dockedShape.getLabels();
  				if(labels.length > 0) {
  					labels.each(function(label) {
  						label.show();
  					});
  				}
  			}//);
  
  			// Do this recursive
  			//shape.children.each((function(value) {
  			for(var i=0; i<shape.children.length ;i++){
  				var value = shape.children[i];	
  				if(value instanceof ORYX.Core.Shape)
  					this.showAllLabels(value);
  			}//).bind(this));
  	},
  
  	/**
  	 * Intialize Method, if there are new Plugins
  	 *
  	 */
  	/*registryChanged: function(pluginsData) {
  		// Save all new Plugin, sorted by group and index
  		this.pluginsData = pluginsData.sortBy( function(value) {
  			return (value.group + "" + value.index);
  		});
  	},*/
  
  	/**
  	 * On the Selection-Changed
  	 *
  	 */
  	onSelectionChanged: function(event) {
  		var elements = event.elements;
  		
  		// Reset the drag-variables
  		this.dragEnable = false;
  		this.dragIntialized = false;
  		this.resizerSE.hide();
  		this.resizerNW.hide();
  
  		// If there is no elements
  		if(!elements || elements.length == 0) {
  			// Hide all things and reset all variables
  			this.selectedRect.hide();
  			this.currentShapes = [];
  			this.toMoveShapes = [];
  			this.dragBounds = undefined;
  			this.oldDragBounds = undefined;
  		} else {
  
  			// Set the current Shapes
  			this.currentShapes = elements;
  
  			// Get all shapes with the highest parent in object hierarchy (canvas is the top most parent)
  			var topLevelElements = this.facade.getCanvas().getShapesWithSharedParent(elements);
  			this.toMoveShapes = topLevelElements;
  			
  			this.toMoveShapes = this.toMoveShapes.findAll( function(shape) { return shape instanceof ORYX.Core.Node && 
  																			(shape.dockers.length === 0 || !elements.member(shape.dockers.first().getDockedShape()))});		
  																			
  			elements.each((function(shape){
  				if(!(shape instanceof ORYX.Core.Edge)) {return}
  				
  				var dks = shape.getDockers() 
  								
  				var hasF = elements.member(dks.first().getDockedShape());
  				var hasL = elements.member(dks.last().getDockedShape());	
  						
  //				if(!hasL) {
  //					this.toMoveShapes.push(dks.last());
  //				}
  //				if(!hasF){
  //					this.toMoveShapes.push(dks.first())
  //				} 
  				/* Enable movement of undocked edges */
  				if(!hasF && !hasL) {
  					var isUndocked = !dks.first().getDockedShape() && !dks.last().getDockedShape()
  					if(isUndocked) {
  						this.toMoveShapes = this.toMoveShapes.concat(dks);
  					}
  				}
  				
  				if( shape.dockers.length > 2 && hasF && hasL){
  					this.toMoveShapes = this.toMoveShapes.concat(dks.findAll(function(el,index){ return index > 0 && index < dks.length-1}))
  				}
  				
  			}).bind(this));
  			
  			// Calculate the new area-bounds of the selection
  			var newBounds = undefined;
  			this.toMoveShapes.each(function(value) {
  				var shape = value;
  				if(value instanceof ORYX.Core.Controls.Docker) {
  					/* Get the Shape */
  					shape = value.parent;
  				}
  				
  				if(!newBounds){
  					newBounds = shape.absoluteBounds();
  				}
  				else {
  					newBounds.include(shape.absoluteBounds());
  				}
  			}.bind(this));
  			
  			if(!newBounds){
  				elements.each(function(value){
  					if(!newBounds) {
  						newBounds = value.absoluteBounds();
  					} else {
  						newBounds.include(value.absoluteBounds());
  					}
  				});
  			}
  			
  			// Set the new bounds
  			this.dragBounds = newBounds;
  			this.oldDragBounds = newBounds.clone();
  
  			// Update and show the rectangle
  			this.resizeRectangle(newBounds);
  			this.selectedRect.show();
  			
  			// Show the resize button, if there is only one element and this is resizeable
  			if(elements.length == 1 && elements[0].isResizable) {
  				var aspectRatio = elements[0].getStencil().fixedAspectRatio() ? elements[0].bounds.width() / elements[0].bounds.height() : undefined;
  				this.resizerSE.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio);
  				this.resizerSE.show();
  				this.resizerNW.setBounds(this.dragBounds, elements[0].minimumSize, elements[0].maximumSize, aspectRatio);
  				this.resizerNW.show();
  			} else {
  				this.resizerSE.setBounds(undefined);
  				this.resizerNW.setBounds(undefined);
  			}
  
  			// If Snap-To-Grid is enabled, the Snap-Point will be calculate
  			if(ORYX.CONFIG.GRID_ENABLED) {
  
  				// Reset all points
  				this.distPoints = [];
  
  				if (this.distPointTimeout)
  					window.clearTimeout(this.distPointTimeout)
  				
  				this.distPointTimeout = window.setTimeout(function(){
  					// Get all the shapes, there will consider at snapping
  					// Consider only those elements who shares the same parent element
  					var distShapes = this.facade.getCanvas().getChildShapes(true).findAll(function(value){
  						var parentShape = value.parent;
  						while(parentShape){
  							if(elements.member(parentShape)) return false;
  							parentShape = parentShape.parent
  						}
  						return true;
  					})
  					
  					// The current selection will delete from this array
  					//elements.each(function(shape) {
  					//	distShapes = distShapes.without(shape);
  					//});
  
  					// For all these shapes
  					distShapes.each((function(value) {
  						if(!(value instanceof ORYX.Core.Edge)) {
  							var ul = value.absoluteXY();
  							var width = value.bounds.width();
  							var height = value.bounds.height();
  
  							// Add the upperLeft, center and lowerRight - Point to the distancePoints
  							this.distPoints.push({
  								ul: {
  									x: ul.x,
  									y: ul.y
  								},
  								c: {
  									x: ul.x + (width / 2),
  									y: ul.y + (height / 2)
  								},
  								lr: {
  									x: ul.x + width,
  									y: ul.y + height
  								}
  							});
  						}
  					}).bind(this));
  					
  				}.bind(this), 10)
  
  
  			}
  		}
  	},
  
  	/**
  	 * Adjust an Point to the Snap Points
  	 *
  	 */
  	snapToGrid: function(position) {
  
  		// Get the current Bounds
  		var bounds = this.dragBounds;
  		
  		var point = {};
  
  		var ulThres = 6;
  		var cThres = 10;
  		var lrThres = 6;
  
  		var scale = this.vLine ? this.vLine.getScale() : 1;
  		
  		var ul = { x: (position.x/scale), y: (position.y/scale)};
  		var c = { x: (position.x/scale) + (bounds.width()/2), y: (position.y/scale) + (bounds.height()/2)};
  		var lr = { x: (position.x/scale) + (bounds.width()), y: (position.y/scale) + (bounds.height())};
  
  		var offsetX, offsetY;
  		var gridX, gridY;
  		
  		// For each distant point
  		this.distPoints.each(function(value) {
  
  			var x, y, gx, gy;
  			if (Math.abs(value.c.x-c.x) < cThres){
  				x = value.c.x-c.x;
  				gx = value.c.x;
  			}/* else if (Math.abs(value.ul.x-ul.x) < ulThres){
  				x = value.ul.x-ul.x;
  				gx = value.ul.x;
  			} else if (Math.abs(value.lr.x-lr.x) < lrThres){
  				x = value.lr.x-lr.x;
  				gx = value.lr.x;
  			} */
  			
  
  			if (Math.abs(value.c.y-c.y) < cThres){
  				y = value.c.y-c.y;
  				gy = value.c.y;
  			}/* else if (Math.abs(value.ul.y-ul.y) < ulThres){
  				y = value.ul.y-ul.y;
  				gy = value.ul.y;
  			} else if (Math.abs(value.lr.y-lr.y) < lrThres){
  				y = value.lr.y-lr.y;
  				gy = value.lr.y;
  			} */
  
  			if (x !== undefined) {
  				offsetX = offsetX === undefined ? x : (Math.abs(x) < Math.abs(offsetX) ? x : offsetX);
  				if (offsetX === x)
  					gridX = gx;
  			}
  
  			if (y !== undefined) {
  				offsetY = offsetY === undefined ? y : (Math.abs(y) < Math.abs(offsetY) ? y : offsetY);
  				if (offsetY === y)
  					gridY = gy;
  			}
  		});
  		
  		
  		if (offsetX !== undefined) {
  			ul.x += offsetX;	
  			ul.x *= scale;
  			if (this.vLine&&gridX)
  				this.vLine.update(gridX);
  		} else {
  			ul.x = (position.x - (position.x % (ORYX.CONFIG.GRID_DISTANCE/2)));
  			if (this.vLine)
  				this.vLine.hide()
  		}
  		
  		if (offsetY !== undefined) {	
  			ul.y += offsetY;
  			ul.y *= scale;
  			if (this.hLine&&gridY)
  				this.hLine.update(gridY);
  		} else {
  			ul.y = (position.y - (position.y % (ORYX.CONFIG.GRID_DISTANCE/2)));
  			if (this.hLine)
  				this.hLine.hide();
  		}
  		
  		return ul;
  	},
  	
  	showGridLine: function(){
  		
  	},
  
  
  	/**
  	 * Redraw of the Rectangle of the SelectedArea
  	 * @param {Object} bounds
  	 */
  	resizeRectangle: function(bounds) {
  		// Resize the Rectangle
  		this.selectedRect.resize(bounds);
  	}
  
  });
  
  
  ORYX.Plugins.SelectedRect = Clazz.extend({
  
  	construct: function(parentId) {
  
  		this.parentId = parentId;
  
  		this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", $(parentId),
  					['g']);
  
  		this.dashedArea = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node,
  			['rect', {x: 0, y: 0,
  				'stroke-width': 1, stroke: '#777777', fill: 'none',
  				'stroke-dasharray': '2,2',
  				'pointer-events': 'none'}]);
  
  		this.hide();
  
  	},
  
  	hide: function() {
  		this.node.setAttributeNS(null, 'display', 'none');
  	},
  
  	show: function() {
  		this.node.setAttributeNS(null, 'display', '');
  	},
  
  	resize: function(bounds) {
  		var upL = bounds.upperLeft();
  
  		var padding = ORYX.CONFIG.SELECTED_AREA_PADDING;
  
  		this.dashedArea.setAttributeNS(null, 'width', bounds.width() + 2*padding);
  		this.dashedArea.setAttributeNS(null, 'height', bounds.height() + 2*padding);
  		this.node.setAttributeNS(null, 'transform', "translate("+ (upL.x - padding) +", "+ (upL.y - padding) +")");
  	}
  
  
  });
  
  
  
  ORYX.Plugins.GridLine = Clazz.extend({
  	
  	construct: function(parentId, direction) {
  
  		if (ORYX.Plugins.GridLine.DIR_HORIZONTAL !== direction && ORYX.Plugins.GridLine.DIR_VERTICAL !== direction) {
  			direction = ORYX.Plugins.GridLine.DIR_HORIZONTAL
  		}
  		
  	
  		this.parent = $(parentId);
  		this.direction = direction;
  		this.node = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.parent,
  					['g']);
  
  		this.line = ORYX.Editor.graft("http://www.w3.org/2000/svg", this.node,
  			['path', {
  				'stroke-width': 1, stroke: 'silver', fill: 'none',
  				'stroke-dasharray': '5,5',
  				'pointer-events': 'none'}]);
  
  		this.hide();
  
  	},
  
  	hide: function() {
  		this.node.setAttributeNS(null, 'display', 'none');
  	},
  
  	show: function() {
  		this.node.setAttributeNS(null, 'display', '');
  	},
  
  	getScale: function(){
  		try {
  			return this.parent.parentNode.transform.baseVal.getItem(0).matrix.a;
  		} catch(e) {
  			return 1;
  		}
  	},
  	
  	update: function(pos) {
  		
  		if (this.direction === ORYX.Plugins.GridLine.DIR_HORIZONTAL) {
  			var y = pos instanceof Object ? pos.y : pos; 
  			var cWidth = this.parent.parentNode.parentNode.width.baseVal.value/this.getScale();
  			this.line.setAttributeNS(null, 'd', 'M 0 '+y+ ' L '+cWidth+' '+y);
  		} else {
  			var x = pos instanceof Object ? pos.x : pos; 
  			var cHeight = this.parent.parentNode.parentNode.height.baseVal.value/this.getScale();
  			this.line.setAttributeNS(null, 'd', 'M'+x+ ' 0 L '+x+' '+cHeight);
  		}
  		
  		this.show();
  	}
  
  
  });
  
  ORYX.Plugins.GridLine.DIR_HORIZONTAL = "hor";
  ORYX.Plugins.GridLine.DIR_VERTICAL = "ver";
  
  ORYX.Plugins.Resizer = Clazz.extend({
  
  	construct: function(parentId, orientation, facade) {
  
  		this.parentId 		= parentId;
  		this.orientation	= orientation;
  		this.facade			= facade;
  		this.node = ORYX.Editor.graft("http://www.w3.org/1999/xhtml", $(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.aspectRatio) {
  				offset.y = (this.bounds.width()+offset.x) / this.aspectRatio - this.bounds.height();
  			}
  		}
  		
  		// respect minimum and maximum sizes of stencil
  		if(this.orientation==="northwest") {
  			if(this.bounds.width()-offset.x > this.maxSize.width) {
  				offset.x = -(this.maxSize.width - this.bounds.width());
  				if(this.aspectRatio)
  					offset.y = this.aspectRatio * offset.x;
  			}
  			if(this.bounds.width()-offset.x < this.minSize.width) {
  				offset.x = -(this.minSize.width - this.bounds.width());
  				if(this.aspectRatio)
  					offset.y = this.aspectRatio * offset.x;
  			}
  			if(this.bounds.height()-offset.y > this.maxSize.height) {
  				offset.y = -(this.maxSize.height - this.bounds.height());
  				if(this.aspectRatio)
  					offset.x = offset.y / this.aspectRatio;
  			}
  			if(this.bounds.height()-offset.y < this.minSize.height) {
  				offset.y = -(this.minSize.height - this.bounds.height());
  				if(this.aspectRatio)
  					offset.x = offset.y / this.aspectRatio;
  			}
  		} else { // defaults to southeast
  			if(this.bounds.width()+offset.x > this.maxSize.width) {
  				offset.x = this.maxSize.width - this.bounds.width();
  				if(this.aspectRatio)
  					offset.y = this.aspectRatio * offset.x;
  			}
  			if(this.bounds.width()+offset.x < this.minSize.width) {
  				offset.x = this.minSize.width - this.bounds.width();
  				if(this.aspectRatio)
  					offset.y = this.aspectRatio * offset.x;
  			}
  			if(this.bounds.height()+offset.y > this.maxSize.height) {
  				offset.y = this.maxSize.height - this.bounds.height();
  				if(this.aspectRatio)
  					offset.x = offset.y / this.aspectRatio;
  			}
  			if(this.bounds.height()+offset.y < this.minSize.height) {
  				offset.y = this.minSize.height - this.bounds.height();
  				if(this.aspectRatio)
  					offset.x = offset.y / this.aspectRatio;
  			}
  		}
  
  		if(this.orientation==="northwest") {
  			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<this.moveShapes.length ;i++){
  			var value = this.moveShapes[i];					
  			value.bounds.moveBy(offset);
  			
  			if (value instanceof ORYX.Core.Node) {
  				
  				(value.dockers||[]).each(function(d){
  					d.bounds.moveBy(offset);
  				})
  				
  				// Update all Dockers of Child shapes
  				/*var childShapesNodes = value.getChildShapes(true).findAll(function(shape){ return shape instanceof ORYX.Core.Node });							
  				var childDockedShapes = childShapesNodes.collect(function(shape){ return shape.getAllDockedShapes() }).flatten().uniq();							
  				var childDockedEdge = childDockedShapes.findAll(function(shape){ return shape instanceof ORYX.Core.Edge });							
  				childDockedEdge = childDockedEdge.findAll(function(shape){ return shape.getAllDockedShapes().all(function(dsh){ return childShapesNodes.include(dsh) }) });							
  				var childDockedDockers = childDockedEdge.collect(function(shape){ return shape.dockers }).flatten();
  				
  				for (var j = 0; j < childDockedDockers.length; j++) {
  					var docker = childDockedDockers[j];
  					if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
  						//docker.bounds.moveBy(offset);
  						//docker.update();
  					}
  				}*/
  				
  				
  				var allEdges = [].concat(value.getIncomingShapes())
  					.concat(value.getOutgoingShapes())
  					// Remove all edges which are included in the selection from the list
  					.findAll(function(r){ return	r instanceof ORYX.Core.Edge && !this.moveShapes.any(function(d){ return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)}) }.bind(this))
  					// Remove all edges which are between the node and a node contained in the selection from the list
  					.findAll(function(r){ return 	(r.dockers.first().getDockedShape() == value || !this.moveShapes.include(r.dockers.first().getDockedShape())) &&  
  													(r.dockers.last().getDockedShape() == value || !this.moveShapes.include(r.dockers.last().getDockedShape()))}.bind(this))
  													
  				// Layout all outgoing/incoming edges
  				this.plugin.layoutEdges(value, allEdges, offset);
  				
  				
  				var allSameEdges = [].concat(value.getIncomingShapes())
  					.concat(value.getOutgoingShapes())
  					// Remove all edges which are included in the selection from the list
  					.findAll(function(r){ return r instanceof ORYX.Core.Edge && r.dockers.first().isDocked() && r.dockers.last().isDocked() && !this.moveShapes.include(r) && !this.moveShapes.any(function(d){ return d == r || (d instanceof ORYX.Core.Controls.Docker && d.parent == r)}) }.bind(this))
  					// Remove all edges which are included in the selection from the list
  					.findAll(function(r){ return this.moveShapes.indexOf(r.dockers.first().getDockedShape()) > i ||  this.moveShapes.indexOf(r.dockers.last().getDockedShape()) > i}.bind(this))
  
  				for (var j = 0; j < allSameEdges.length; j++) {
  					for (var k = 1; k < allSameEdges[j].dockers.length-1; k++) {
  						var docker = allSameEdges[j].dockers[k];
  						if (!docker.getDockedShape() && !this.moveShapes.include(docker)) {
  							docker.bounds.moveBy(offset);
  						}
  					}
  				}	
  				
  				/*var i=-1;
  				var nodes = value.getChildShapes(true);
  				var allEdges = [];
  				while(++i<nodes.length){
  					var edges = [].concat(nodes[i].getIncomingShapes())
  						.concat(nodes[i].getOutgoingShapes())
  						// Remove all edges which are included in the selection from the list
  						.findAll(function(r){ return r instanceof ORYX.Core.Edge && !allEdges.include(r) && r.dockers.any(function(d){ return !value.bounds.isIncluded(d.bounds.center)})})
  					allEdges = allEdges.concat(edges);
  					if (edges.length <= 0){ continue }
  					//this.plugin.layoutEdges(nodes[i], edges, offset);
  				}*/
  			}
  		}
  										
  	},
  	dockAllShapes: function(shouldDocked){
  		// Undock all Nodes
  		for (var i = 0; i < this.dockedNodes.length; i++) {
  			var docker = this.dockedNodes[i].docker;
  			
  			docker.setDockedShape( shouldDocked ? this.dockedNodes[i].dockedShape : undefined )
  			if (docker.getDockedShape()) {
  				docker.setReferencePoint(this.dockedNodes[i].refPoint);
  				//docker.update();
  			}
  		}
  	},
  	
  	addShapeToParent:function( parents ){
  		
  		// For every Shape, add this and reset the position		
  		for(var i=0; i<this.moveShapes.length ;i++){
  			var currentShape = this.moveShapes[i];
  			if(currentShape instanceof ORYX.Core.Node &&
  			   currentShape.parent !== parents[i]) {
  				
  				// Calc the new position
  				var unul = parents[i].absoluteXY();
  				var csul = currentShape.absoluteXY();
  				var x = csul.x - unul.x;
  				var y = csul.y - unul.y;
  
  				// Add the shape to the new contained shape
  				parents[i].add(currentShape);
  				// Add all attached shapes as well
  				currentShape.getOutgoingShapes((function(shape) {
  					if(shape instanceof ORYX.Core.Node && !this.moveShapes.member(shape)) {
  						parents[i].add(shape);
  					}
  				}).bind(this));
  
  				// Set the new position
  				if(currentShape instanceof ORYX.Core.Node && currentShape.dockers.length == 1){
  					var b = currentShape.bounds;
  					x += b.width()/2;y += b.height()/2
  					currentShape.dockers.first().bounds.centerMoveTo(x, y);
  				} else {
  					currentShape.bounds.moveTo(x, y);
  				}
  				
  			} 
  			
  			// Update the shape
  			//currentShape.update();
  			
  		}
  	},
  	selectCurrentShapes:function(){
  		this.plugin.facade.setSelection( this.selectedShapes );
  	}
  });
  /**
   * Copyright (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<length; i++) {
  			var dockerPoint = edgeDockers[i].bounds.center();
  			path = path + " L" + dockerPoint.x + " " +  dockerPoint.y;
  		}
  		
  		return path;
  	}
  	
  });
  
   
  ORYX.Plugins.HighlightingSelectedShapes = Clazz.extend({
  
  	construct: function(facade) {
  		this.facade = facade;
  		this.opacityFull = 0.9;
  		this.opacityLow = 0.4;
  
  		// Register on Dragging-Events for show/hide of ShapeMenu
  		//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hide.bind(this));
  		//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END,  this.show.bind(this));		
  	},
  
  	/**
  	 * On the Selection-Changed
  	 *
  	 */
  	onSelectionChanged: function(event) {
  		if(event.elements && event.elements.length > 1) {
  			this.facade.raiseEvent({
  										type:		ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
  										highlightId:'selection',
  										elements:	event.elements.without(event.subSelection),
  										color:		ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
  										opacity: 	!event.subSelection ? this.opacityFull : this.opacityLow
  									});
  
  			if(event.subSelection){
  				this.facade.raiseEvent({
  											type:		ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
  											highlightId:'subselection',
  											elements:	[event.subSelection],
  											color:		ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
  											opacity: 	this.opacityFull
  										});	
  			} else {
  				this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'});				
  			}						
  			
  		} else {
  			this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'selection'});
  			this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'});
  		}		
  	}
  });/**
   * Copyright (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<length; i++) {
  			var dockerPoint = edgeDockers[i].bounds.center();
  			path = path + " L" + dockerPoint.x + " " +  dockerPoint.y;
  		}
  		
  		return path;
  	}
  	
  });
  
   
  ORYX.Plugins.HighlightingSelectedShapes = Clazz.extend({
  
  	construct: function(facade) {
  		this.facade = facade;
  		this.opacityFull = 0.9;
  		this.opacityLow = 0.4;
  
  		// Register on Dragging-Events for show/hide of ShapeMenu
  		//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_START, this.hide.bind(this));
  		//this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDROP_END,  this.show.bind(this));		
  	},
  
  	/**
  	 * On the Selection-Changed
  	 *
  	 */
  	onSelectionChanged: function(event) {
  		if(event.elements && event.elements.length > 1) {
  			this.facade.raiseEvent({
  										type:		ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
  										highlightId:'selection',
  										elements:	event.elements.without(event.subSelection),
  										color:		ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
  										opacity: 	!event.subSelection ? this.opacityFull : this.opacityLow
  									});
  
  			if(event.subSelection){
  				this.facade.raiseEvent({
  											type:		ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
  											highlightId:'subselection',
  											elements:	[event.subSelection],
  											color:		ORYX.CONFIG.SELECTION_HIGHLIGHT_COLOR,
  											opacity: 	this.opacityFull
  										});	
  			} else {
  				this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'});				
  			}						
  			
  		} else {
  			this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'selection'});
  			this.facade.raiseEvent({type:ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE, highlightId:'subselection'});
  		}		
  	}
  });/**
   * Copyright (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( <option> )
   * 
   * 	As an example please have a look in the overlayexample.js
   * 
   * 	The option object should/have to have following attributes:
   * 
   * 	Key				Value-Type							Description
   * 	================================================================
   * 
   *	type 			ORYX.CONFIG.EVENT_OVERLAY_SHOW | ORYX.CONFIG.EVENT_OVERLAY_HIDE		This is the type of the event	
   *	id				<String>							You have to use an unified id for later on hiding this overlay
   *	shapes 			<ORYX.Core.Shape[]>					The Shapes where the attributes should be changed
   *	attributes 		<Object>							An object with svg-style attributes as key-value pair
   *	node			<SVGElement>						An SVG-Element could be specified for adding this to the Shape
   *	nodePosition	"N"|"NE"|"E"|"SE"|"S"|"SW"|"W"|"NW"|"START"|"END"	The position for the SVG-Element relative to the 
   *														specified Shape. "START" and "END" are just using for a Edges, then
   *														the relation is the start or ending Docker of this edge.
   *	
   * 
   **/
  if (!ORYX.Plugins) 
      ORYX.Plugins = new Object();
  
  ORYX.Plugins.Overlay = Clazz.extend({
  
      facade: undefined,
  	
  	styleNode: undefined,
      
      construct: function(facade){
  		
          this.facade = facade;
  
  		this.changes = [];
  
  		this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_SHOW, this.show.bind(this));
  		this.facade.registerOnEvent(ORYX.CONFIG.EVENT_OVERLAY_HIDE, this.hide.bind(this));	
  
  		this.styleNode = document.createElement('style')
  		this.styleNode.setAttributeNS(null, 'type', 'text/css')
  		
  		document.getElementsByTagName('head')[0].appendChild( this.styleNode )
  
      },
  	
  	/**
  	 * Show the overlay for specific nodes
  	 * @param {Object} options
  	 * 
  	 * 	String				options.id		- MUST - Define the id of the overlay (is needed for the hiding of this overlay)		
  	 *	ORYX.Core.Shape[] 	options.shapes 	- MUST - Define the Shapes for the changes
  	 * 	attr-name:value		options.changes	- Defines all the changes which should be shown
  	 * 
  	 * 
  	 */
  	show: function( options ){
  		
  		// Checks if all arguments are available
  		if( 	!options || 
  				!options.shapes || !options.shapes instanceof Array ||
  				!options.id	|| !options.id instanceof String || options.id.length == 0) { 
  				
  					return
  					
  		}
  		
  		//if( this.changes[options.id]){
  		//	this.hide( options )
  		//}
  			
  
  		// Checked if attributes are setted
  		if( options.attributes ){
  			
  			// FOR EACH - Shape
  			options.shapes.each(function(el){
  				
  				// Checks if the node is a Shape
  				if( !el instanceof ORYX.Core.Shape){ return }
  				
  				this.setAttributes( el.node , options.attributes )
  				
  			}.bind(this))
  
  		}	
  		
  		var isSVG = true
  		try {
  			isSVG = options.node && options.node instanceof SVGElement;
  		} catch(e){}
  		
  		// Checks if node is setted and if this is an SVGElement		
  		if ( options.node && isSVG) {
  			
  			options["_temps"] = []
  						
  			// FOR EACH - Node
  			options.shapes.each(function(el, index){
  				
  				// Checks if the node is a Shape
  				if( !el instanceof ORYX.Core.Shape){ return }
  				
  				var _temp = {}
  				_temp.svg = options.dontCloneNode ? options.node : options.node.cloneNode( true );
  				
  				// Add the svg node to the ORYX-Shape
  				el.node.firstChild.appendChild( _temp.svg )		
  				
  				// If
  				if (el instanceof ORYX.Core.Edge && !options.nodePosition) {
  					options['nodePosition'] = "START"
  				}
  						
  				// If the node position is setted, it has to be transformed
  				if( options.nodePosition ){
  					
  					var b = el.bounds;
  					var p = options.nodePosition.toUpperCase();
  					
  					// Check the values of START and END
  					if( el instanceof ORYX.Core.Node && p == "START"){
  						p = "NW";
  					} else if(el instanceof ORYX.Core.Node && p == "END"){
  						p = "SE";
  					} else if(el instanceof ORYX.Core.Edge && p == "START"){
  						b = el.getDockers().first().bounds
  					} else if(el instanceof ORYX.Core.Edge && p == "END"){
  						b = el.getDockers().last().bounds
  					}
  
  					// Create a callback for the changing the position 
  					// depending on the position string
  					_temp.callback = function(){
  						
  						var x = 0; var y = 0;
  						
  						if( p == "NW" ){
  							// Do Nothing
  						} else if( p == "N" ) {
  							x = b.width() / 2;
  						} else if( p == "NE" ) {
  							x = b.width();
  						} else if( p == "E" ) {
  							x = b.width(); y = b.height() / 2;
  						} else if( p == "SE" ) {
  							x = b.width(); y = b.height();
  						} else if( p == "S" ) {
  							x = b.width() / 2; y = b.height();
  						} else if( p == "SW" ) {
  							y = b.height();
  						} else if( p == "W" ) {
  							y = b.height() / 2;
  						} else if( p == "START" || p == "END") {
  							x = b.width() / 2; y = b.height() / 2;
  						} else {
  							return
  						}
  						
  						if( el instanceof ORYX.Core.Edge){
  							x  += b.upperLeft().x ; y  += b.upperLeft().y ;
  						}
  						
  						_temp.svg.setAttributeNS(null, "transform", "translate(" + x + ", " + y + ")")
  					
  					}.bind(this)
  					
  					_temp.element = el;
  					_temp.callback();
  					
  					b.registerCallback( _temp.callback );
  					
  				}
  				
  				
  				options._temps.push( _temp )	
  				
  			}.bind(this))
  			
  			
  			
  		}		
  	
  
  		// Store the changes
  		if( !this.changes[options.id] ){
  			this.changes[options.id] = [];
  		}
  		
  		this.changes[options.id].push( options );
  				
  	},
  	
  	/**
  	 * Hide the overlay with the spefic id
  	 * @param {Object} options
  	 */
  	hide: function( options ){
  		
  		// Checks if all arguments are available
  		if( 	!options || 
  				!options.id	|| !options.id instanceof String || options.id.length == 0 ||
  				!this.changes[options.id]) { 
  				
  					return
  					
  		}		
  		
  		
  		// Delete all added attributes
  		// FOR EACH - Shape
  		this.changes[options.id].each(function(option){
  			
  			option.shapes.each(function(el, index){
  				
  				// Checks if the node is a Shape
  				if( !el instanceof ORYX.Core.Shape){ return }
  				
  				this.deleteAttributes( el.node )
  							
  			}.bind(this));
  
  	
  			if( option._temps ){
  				
  				option._temps.each(function(tmp){
  					// Delete the added Node, if there is one
  					if( tmp.svg && tmp.svg.parentNode ){
  						tmp.svg.parentNode.removeChild( tmp.svg )
  					}
  		
  					// If 
  					if( tmp.callback && tmp.element){
  						// It has to be unregistered from the edge
  						tmp.element.bounds.unregisterCallback( tmp.callback )
  					}
  							
  				}.bind(this))
  				
  			}
  		
  			
  		}.bind(this));
  
  		
  		this.changes[options.id] = null;
  		
  		
  	},
  	
  	
  	/**
  	 * Set the given css attributes to that node
  	 * @param {HTMLElement} node
  	 * @param {Object} attributes
  	 */
  	setAttributes: function( node, attributes ) {
  		
  		
  		// Get all the childs from ME
  		var childs = this.getAllChilds( node.firstChild.firstChild )
  		
  		var ids = []
  		
  		// Add all Attributes which have relation to another node in this document and concate the pure id out of it
  		// This is for example important for the markers of a edge
  		childs.each(function(e){ ids.push( $A(e.attributes).findAll(function(attr){ return attr.nodeValue.startsWith('url(#')}) )})
  		ids = ids.flatten().compact();
  		ids = ids.collect(function(s){return s.nodeValue}).uniq();
  		ids = ids.collect(function(s){return s.slice(5, s.length-1)})
  		
  		// Add the node ID to the id
  		ids.unshift( node.id + ' .me')
  		
  		var attr				= $H(attributes);
          var attrValue			= attr.toJSON().gsub(',', ';').gsub('"', '');
          var attrMarkerValue		= attributes.stroke ? attrValue.slice(0, attrValue.length-1) + "; fill:" + attributes.stroke + ";}" : attrValue;
          var attrTextValue;
          if( attributes.fill ){
              var copyAttr        = Object.clone(attributes);
          	copyAttr.fill		= "black";
          	attrTextValue		= $H(copyAttr).toJSON().gsub(',', ';').gsub('"', '');
          }
                  	
          // Create the CSS-Tags Style out of the ids and the attributes
          csstags = ids.collect(function(s, i){return "#" + s + " * " + (!i? attrValue : attrMarkerValue) + "" + (attrTextValue ? " #" + s + " text * " + attrTextValue : "") })
  		
  		// Join all the tags
  		var s = csstags.join(" ") + "\n" 
  		
  		// And add to the end of the style tag
  		this.styleNode.appendChild(document.createTextNode(s));
  		
  		
  	},
  	
  	/**
  	 * Deletes all attributes which are
  	 * added in a special style sheet for that node
  	 * @param {HTMLElement} node 
  	 */
  	deleteAttributes: function( node ) {
  				
  		// Get all children which contains the node id		
  		var delEl = $A(this.styleNode.childNodes)
  					 .findAll(function(e){ return e.textContent.include( '#' + node.id ) });
  		
  		// Remove all of them
  		delEl.each(function(el){
  			el.parentNode.removeChild(el);
  		});		
  	},
  	
  	getAllChilds: function( node ){
  		
  		var childs = $A(node.childNodes)
  		
  		$A(node.childNodes).each(function( e ){ 
  		        childs.push( this.getAllChilds( e ) )
  		}.bind(this))
  
      	return childs.flatten();
  	}
  
      
  });
  /**
   * Copyright (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.Edit = Clazz.extend({
      
      construct: function(facade){
      
          this.facade = facade;
          this.clipboard = new ORYX.Plugins.Edit.ClipBoard();
          
          //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));
          
          this.facade.offer({
           name: ORYX.I18N.Edit.cut,
           description: ORYX.I18N.Edit.cutDesc,
           icon: ORYX.PATH + "images/cut.png",
  		 keyCodes: [{
  				metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: 88,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
  			}
  		 ],
           functionality: this.callEdit.bind(this, this.editCut),
           group: ORYX.I18N.Edit.group,
           index: 1,
           minShape: 1
           });
           
          this.facade.offer({
           name: ORYX.I18N.Edit.copy,
           description: ORYX.I18N.Edit.copyDesc,
           icon: ORYX.PATH + "images/page_copy.png",
  		 keyCodes: [{
  				metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: 67,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
  			}
  		 ],
           functionality: this.callEdit.bind(this, this.editCopy, [true, false]),
           group: ORYX.I18N.Edit.group,
           index: 2,
           minShape: 1
           });
           
          this.facade.offer({
           name: ORYX.I18N.Edit.paste,
           description: ORYX.I18N.Edit.pasteDesc,
           icon: ORYX.PATH + "images/page_paste.png",
  		 keyCodes: [{
  				metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: 86,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
  			}
  		 ],
           functionality: this.callEdit.bind(this, this.editPaste),
           isEnabled: this.clipboard.isOccupied.bind(this.clipboard),
           group: ORYX.I18N.Edit.group,
           index: 3,
           minShape: 0,
           maxShape: 0
           });
           
          this.facade.offer({
              name: ORYX.I18N.Edit.del,
              description: ORYX.I18N.Edit.delDesc,
              icon: ORYX.PATH + "images/cross.png",
  			keyCodes: [{
  					metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  					keyCode: 8,
  					keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
  				},
  				{	
  					keyCode: 46,
  					keyAction: ORYX.CONFIG.KEY_ACTION_DOWN
  				}
  			],
              functionality: this.callEdit.bind(this, this.editDelete),
              group: ORYX.I18N.Edit.group,
              index: 4,
              minShape: 1
          });
      },
  	
  	callEdit: function(fn, args){
  		window.setTimeout(function(){
  			fn.apply(this, (args instanceof Array ? args : []));
  		}.bind(this), 1);
  	},
  	
  	/**
  	 * Handles the mouse down event and starts the copy-move-paste action, if
  	 * control or meta key is pressed.
  	 */
  	handleMouseDown: function(event) {
  		if(this._controlPressed) {
  			this._controlPressed = false;
  			this.editCopy();
  //			console.log("copiedEle: %0",this.clipboard.shapesAsJson)
  //			console.log("mousevent: %o",event)
  			this.editPaste();
  			event.forceExecution = true;
  			this.facade.raiseEvent(event, this.clipboard.shapesAsJson);
  			
  		}
  	},
      
      /**
       * The key handler for this plugin. Every action from the set of cut, copy,
       * paste and delete should be accessible trough simple keyboard shortcuts.
       * This method checks whether any event triggers one of those actions.
       *
       * @param {Object} event The keyboard event that should be analysed for
       *     triggering of this plugin.
       */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //        
  //        ORYX.Log.debug("edit.js handles a keyEvent.");
  //        
  //        // assure we have the current event.
  //        if (!event) 
  //            event = window.event;
  //        
  //        
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //        
  //        // if the object is to be deleted, do so, and return immediately.
  //        if ((pressedKey == ORYX.CONFIG.KEY_CODE_DELETE) ||
  //        ((pressedKey == ORYX.CONFIG.KEY_CODE_BACKSPACE) &&
  //        (event.metaKey || event.appleMetaKey))) {
  //        
  //            ORYX.Log.debug("edit.js deletes the shape.");
  //            this.editDelete();
  //            return;
  //        }
  //        
  //         // if control key is not pressed, we're not interested anymore.
  //         if (!ctrlPressed)
  //         return;
  //         
  //         // when ctrl is pressed, switch trough the possibilities.
  //         switch (pressedKey) {
  //         
  //	         // cut.
  //	         case ORYX.CONFIG.KEY_CODE_X:
  //	         this.editCut();
  //	         break;
  //	         
  //	         // copy.
  //	         case ORYX.CONFIG.KEY_CODE_C:
  //	         this.editCopy();
  //	         break;
  //	         
  //	         // paste.
  //	         case ORYX.CONFIG.KEY_CODE_V:
  //	         this.editPaste();
  //	         break;
  //         }
  //    },
  	
      /**
       * Returns a list of shapes which should be considered while copying.
       * Besides the shapes of given ones, edges and attached nodes are added to the result set.
       * If one of the given shape is a child of another given shape, it is not put into the result. 
       */
      getAllShapesToConsider: function(shapes){
          var shapesToConsider = []; // only top-level shapes
          var childShapesToConsider = []; // all child shapes of top-level shapes
          
          shapes.each(function(shape){
              //Throw away these shapes which have a parent in given shapes
              isChildShapeOfAnother = shapes.any(function(s2){
                  return s2.hasChildShape(shape);
              });
              if(isChildShapeOfAnother) return;
              
              // This shape should be considered
              shapesToConsider.push(shape);
              // Consider attached nodes (e.g. intermediate events)
              if (shape instanceof ORYX.Core.Node) {
  				var attached = shape.getOutgoingNodes();
  				attached = attached.findAll(function(a){ return !shapes.include(a) });
                  shapesToConsider = shapesToConsider.concat(attached);
              }
              
              childShapesToConsider = childShapesToConsider.concat(shape.getChildShapes(true));
          }.bind(this));
          
          // All edges between considered child shapes should be considered
          // Look for these edges having incoming and outgoing in childShapesToConsider
          var edgesToConsider = this.facade.getCanvas().getChildEdges().select(function(edge){
              // Ignore if already added
              if(shapesToConsider.include(edge)) return false;
              // Ignore if there are no docked shapes
              if(edge.getAllDockedShapes().size() === 0) return false; 
              // True if all docked shapes are in considered child shapes
              return edge.getAllDockedShapes().all(function(shape){
                  // Remember: Edges can have other edges on outgoing, that is why edges must not be included in childShapesToConsider
                  return shape instanceof ORYX.Core.Edge || childShapesToConsider.include(shape);
              });
          });
          shapesToConsider = shapesToConsider.concat(edgesToConsider);
          
          return shapesToConsider;
      },
      
      /**
       * Performs the cut operation by first copy-ing and then deleting the
       * current selection.
       */
      editCut: function(){
          //TODO document why this returns false.
          //TODO document what the magic boolean parameters are supposed to do.
          
          this.editCopy(false, true);
          this.editDelete(true);
          return false;
      },
      
      /**
       * Performs the copy operation.
       * @param {Object} will_not_update ??
       */
      editCopy: function( will_update, useNoOffset ){
          var selection = this.facade.getSelection();
          
          //if the selection is empty, do not remove the previously copied elements
          if(selection.length == 0) return;
          
          this.clipboard.refresh(selection, this.getAllShapesToConsider(selection), this.facade.getCanvas().getStencil().stencilSet().namespace(), useNoOffset);
  
          if( will_update ) this.facade.updateSelection();
      },
      
      /**
       * Performs the paste operation.
       */
      editPaste: function(){
          // Create a new canvas with childShapes 
  		//and stencilset namespace to be JSON Import conform
  		var canvas = {
              childShapes: this.clipboard.shapesAsJson,
  			stencilset:{
  				namespace:this.clipboard.SSnamespace
  			}
          }
          // Apply json helper to iterate over json object
          Ext.apply(canvas, ORYX.Core.AbstractShape.JSONHelper);
          
          var childShapeResourceIds =  canvas.getChildShapes(true).pluck("resourceId");
          var outgoings = {};
          // Iterate over all shapes
          canvas.eachChild(function(shape, parent){
              // Throw away these references where referenced shape isn't copied
              shape.outgoing = shape.outgoing.select(function(out){
                  return childShapeResourceIds.include(out.resourceId);
              });
  			shape.outgoing.each(function(out){
  				if (!outgoings[out.resourceId]){ outgoings[out.resourceId] = [] }
  				outgoings[out.resourceId].push(shape)
  			});
  			
              return shape;
          }.bind(this), true, true);
          
  
          // Iterate over all shapes
          canvas.eachChild(function(shape, parent){
              
          	// Check if there has a valid target
              if(shape.target && !(childShapeResourceIds.include(shape.target.resourceId))){
                  shape.target = undefined;
                  shape.targetRemoved = true;
              }
      		
      		// Check if the first docker is removed
      		if(	shape.dockers && 
      			shape.dockers.length >= 1 && 
      			shape.dockers[0].getDocker &&
      			((shape.dockers[0].getDocker().getDockedShape() &&
      			!childShapeResourceIds.include(shape.dockers[0].getDocker().getDockedShape().resourceId)) || 
      			!shape.getShape().dockers[0].getDockedShape()&&!outgoings[shape.resourceId])) {
      				
      			shape.sourceRemoved = true;
      		}
  			
              return shape;
          }.bind(this), true, true);
  
  		
          // Iterate over top-level shapes
          canvas.eachChild(function(shape, parent){
              // All top-level shapes should get an offset in their bounds
              // Move the shape occording to COPY_MOVE_OFFSET
          	if (this.clipboard.useOffset) {
  	            shape.bounds = {
  	                lowerRight: {
  	                    x: shape.bounds.lowerRight.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
  	                    y: shape.bounds.lowerRight.y + ORYX.CONFIG.COPY_MOVE_OFFSET
  	                },
  	                upperLeft: {
  	                    x: shape.bounds.upperLeft.x + ORYX.CONFIG.COPY_MOVE_OFFSET,
  	                    y: shape.bounds.upperLeft.y + ORYX.CONFIG.COPY_MOVE_OFFSET
  	                }
  	            };
          	}
              // Only apply offset to shapes with a target
              if (shape.dockers){
                  shape.dockers = shape.dockers.map(function(docker, i){
                      // If shape had a target but the copied does not have anyone anymore,
                      // migrate the relative dockers to absolute ones.
                      if( (shape.targetRemoved === true && i == shape.dockers.length - 1&&docker.getDocker) ||
  						(shape.sourceRemoved === true && i == 0&&docker.getDocker)){
  
                          docker = docker.getDocker().bounds.center();
                      }
  
  					// If it is the first docker and it has a docked shape, 
  					// just return the coordinates
  				   	if ((i == 0 && docker.getDocker instanceof Function && 
  				   		shape.sourceRemoved !== true && (docker.getDocker().getDockedShape() || ((outgoings[shape.resourceId]||[]).length > 0 && (!(shape.getShape() instanceof ORYX.Core.Node) || outgoings[shape.resourceId][0].getShape() instanceof ORYX.Core.Node)))) || 
  						(i == shape.dockers.length - 1 && docker.getDocker instanceof Function && 
  						shape.targetRemoved !== true && (docker.getDocker().getDockedShape() || shape.target))){
  							
  						return {
                          	x: docker.x, 
                          	y: docker.y,
                          	getDocker: docker.getDocker
  						}
  					} else if (this.clipboard.useOffset) {
  	                    return {
  		                        x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET, 
  		                        y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
  	                        	getDocker: docker.getDocker
  		                    };
  				   	} else {
  				   		return {
                          	x: docker.x, 
                          	y: docker.y,
                          	getDocker: docker.getDocker
  						};
  				   	}
                  }.bind(this));
  
              } else if (shape.getShape() instanceof ORYX.Core.Node && shape.dockers && shape.dockers.length > 0 && (!shape.dockers.first().getDocker || shape.sourceRemoved === true || !(shape.dockers.first().getDocker().getDockedShape() || outgoings[shape.resourceId]))){
              	
              	shape.dockers = shape.dockers.map(function(docker, i){
              		
                      if((shape.sourceRemoved === true && i == 0&&docker.getDocker)){
                      	docker = docker.getDocker().bounds.center();
                      }
                      
                      if (this.clipboard.useOffset) {
  	            		return {
  	                        x: docker.x + ORYX.CONFIG.COPY_MOVE_OFFSET, 
  	                        y: docker.y + ORYX.CONFIG.COPY_MOVE_OFFSET,
  	                    	getDocker: docker.getDocker
  	                    };
                      } else {
  	            		return {
  	                        x: docker.x, 
  	                        y: docker.y,
  	                    	getDocker: docker.getDocker
  	                    };
                      }
              	}.bind(this));
              }
              
              return shape;
          }.bind(this), false, true);
  
          this.clipboard.useOffset = true;
          this.facade.importJSON(canvas);
      },
      
      /**
       * Performs the delete operation. No more asking.
       */
      editDelete: function(){
          var selection = this.facade.getSelection();
          
          var clipboard = new ORYX.Plugins.Edit.ClipBoard();
          clipboard.refresh(selection, this.getAllShapesToConsider(selection));
          
  		var command = new ORYX.Plugins.Edit.DeleteCommand(clipboard , this.facade);
                                         
  		this.facade.executeCommands([command]);
      }
  }); 
  
  ORYX.Plugins.Edit.ClipBoard = Clazz.extend({
      construct: function(){
          this.shapesAsJson = [];
          this.selection = [];
  		this.SSnamespace="";
  		this.useOffset=true;
      },
      isOccupied: function(){
          return this.shapesAsJson.length > 0;
      },
      refresh: function(selection, shapes, namespace, useNoOffset){
          this.selection = selection;
          this.SSnamespace=namespace;
          // Store outgoings, targets and parents to restore them later on
          this.outgoings = {};
          this.parents = {};
          this.targets = {};
          this.useOffset = useNoOffset !== true;
          
          this.shapesAsJson = shapes.map(function(shape){
              var s = shape.toJSON();
              s.parent = {resourceId : shape.getParentShape().resourceId};
              s.parentIndex = shape.getParentShape().getChildShapes().indexOf(shape)
              return s;
          });
      }
  });
  
  ORYX.Plugins.Edit.DeleteCommand = ORYX.Core.Command.extend({
      construct: function(clipboard, facade){
          this.clipboard          = clipboard;
          this.shapesAsJson       = clipboard.shapesAsJson;
          this.facade             = facade;
          
          // Store dockers of deleted shapes to restore connections
          this.dockers            = this.shapesAsJson.map(function(shapeAsJson){
              var shape = shapeAsJson.getShape();
              var incomingDockers = shape.getIncomingShapes().map(function(s){return s.getDockers().last()})
              var outgoingDockers = shape.getOutgoingShapes().map(function(s){return s.getDockers().first()})
              var dockers = shape.getDockers().concat(incomingDockers, outgoingDockers).compact().map(function(docker){
                  return {
                      object: docker,
                      referencePoint: docker.referencePoint,
                      dockedShape: docker.getDockedShape()
                  };
              });
              return dockers;
          }).flatten();
      },          
      execute: function(){
          this.shapesAsJson.each(function(shapeAsJson){
              // Delete shape
              this.facade.deleteShape(shapeAsJson.getShape());
          }.bind(this));
          
          this.facade.setSelection([]);
          this.facade.getCanvas().update();		
  		this.facade.updateSelection();
          
      },
      rollback: function(){
          this.shapesAsJson.each(function(shapeAsJson) {
              var shape = shapeAsJson.getShape();
              var parent = this.facade.getCanvas().getChildShapeByResourceId(shapeAsJson.parent.resourceId) || this.facade.getCanvas();
              parent.add(shape, shape.parentIndex);
          }.bind(this));
          
          //reconnect shapes
          this.dockers.each(function(d) {
              d.object.setDockedShape(d.dockedShape);
              d.object.setReferencePoint(d.referencePoint);
          }.bind(this));
          
          this.facade.setSelection(this.selectedShapes);
          this.facade.getCanvas().update();	
  		this.facade.updateSelection();
          
      }
  });/**
   * Copyright (c) 2009
   * Jan-Felix Schwarz
   *
   * 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.KeysMove = ORYX.Plugins.AbstractPlugin.extend({
  
      facade: undefined,
      
      construct: function(facade){
      
          this.facade = facade;
          this.copyElements = [];
          
          //this.facade.registerOnEvent(ORYX.CONFIG.EVENT_KEYDOWN, this.keyHandler.bind(this));
  
  		// SELECT ALL
  		this.facade.offer({
  		keyCodes: [{
  		 		metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: 65,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.selectAll.bind(this)
           });
  		 
  		// MOVE LEFT SMALL		
  		this.facade.offer({
  		keyCodes: [{
  		 		metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, false)
           });
  		 
  		 // MOVE LEFT
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: ORYX.CONFIG.KEY_CODE_LEFT,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_LEFT, true)
           });
  		 
  		// MOVE RIGHT SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, false)
           });
  		 
  		// MOVE RIGHT	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: ORYX.CONFIG.KEY_CODE_RIGHT,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_RIGHT, true)
           });
  		 
  		// MOVE UP SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: ORYX.CONFIG.KEY_CODE_UP,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, false)
           });
  		 
  		// MOVE UP	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: ORYX.CONFIG.KEY_CODE_UP,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_UP, true)
           });
  		 
  		// MOVE DOWN SMALL	
  		 this.facade.offer({
  		 keyCodes: [{
  		 		metaKeys: [ORYX.CONFIG.META_KEY_META_CTRL],
  				keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, false)
           });
  		 
  		// MOVE DOWN	
  		 this.facade.offer({
  		 keyCodes: [{
  				keyCode: ORYX.CONFIG.KEY_CODE_DOWN,
  				keyAction: ORYX.CONFIG.KEY_ACTION_DOWN 
  			}
  		 ],
           functionality: this.move.bind(this, ORYX.CONFIG.KEY_CODE_DOWN, true)
           });
  		 
           
      },
      
  	/**
  	 * Select all shapes in the editor
  	 *
  	 */
  	selectAll: function(e){
      	Event.stop(e.event);
  		this.facade.setSelection(this.facade.getCanvas().getChildShapes(true))
  	},
  	
  	move: function(key, far, e) {
  		
      	Event.stop(e.event);
  
  		// calculate the distance to move the objects and get the selection.
  		var distance = far? 20 : 5;
  		var selection = this.facade.getSelection();
  		var currentSelection = this.facade.getSelection();
  		var p = {x: 0, y: 0};
  		
  		// switch on the key pressed and populate the point to move by.
  		switch(key) {
  
  			case ORYX.CONFIG.KEY_CODE_LEFT:
  				p.x = -1*distance;
  				break;
  			case ORYX.CONFIG.KEY_CODE_RIGHT:
  				p.x = distance;
  				break;
  			case ORYX.CONFIG.KEY_CODE_UP:
  				p.y = -1*distance;
  				break;
  			case ORYX.CONFIG.KEY_CODE_DOWN:
  				p.y = distance;
  				break;
  		}
  		
  		// move each shape in the selection by the point calculated and update it.
  		selection = selection.findAll(function(shape){ 
  			// Check if this shape is docked to an shape in the selection			
  			if(shape instanceof ORYX.Core.Node && shape.dockers.length == 1 && selection.include( shape.dockers.first().getDockedShape() )){ 
  				return false 
  			} 
  			
  			// Check if any of the parent shape is included in the selection
  			var s = shape.parent; 
  			do{ 
  				if(selection.include(s)){ 
  					return false
  				}
  			}while(s = s.parent); 
  			
  			// Otherwise, return true
  			return true;
  			
  		});
  		
  		/* Edges must not be movable, if only edges are selected and at least 
  		 * one of them is docked.
  		 */
  		var edgesMovable = true;
  		var onlyEdgesSelected = selection.all(function(shape) {
  			if(shape instanceof ORYX.Core.Edge) {
  				if(shape.isDocked()) {
  					edgesMovable = false;
  				}
  				return true;	
  			}
  			return false;
  		});
  		
  		if(onlyEdgesSelected && !edgesMovable) {
  			/* Abort moving shapes */
  			return;
  		}
  		
  		selection = selection.map(function(shape){ 
  			if( shape instanceof ORYX.Core.Node ){
  				/*if( shape.dockers.length == 1 ){
  					return shape.dockers.first()
  				} else {*/
  					return shape
  				//}
  			} else if( shape instanceof ORYX.Core.Edge ) {
  				
  				var dockers = shape.dockers;
  				
  				if( selection.include( shape.dockers.first().getDockedShape() ) ){
  					dockers = dockers.without( shape.dockers.first() )
  				}
  
  				if( selection.include( shape.dockers.last().getDockedShape() ) ){
  					dockers = dockers.without( shape.dockers.last() )
  				}
  				
  				return dockers	
  							
  			} else {
  				return null
  			}
  		
  		}).flatten().compact();
  		
  		if (selection.size() > 0) {
  			
  			//Stop moving at canvas borders
  			var selectionBounds = [ this.facade.getCanvas().bounds.lowerRight().x,
  			                        this.facade.getCanvas().bounds.lowerRight().y,
  			                        0,
  			                        0 ];
  			selection.each(function(s) {
  				selectionBounds[0] = Math.min(selectionBounds[0], s.bounds.upperLeft().x);
  				selectionBounds[1] = Math.min(selectionBounds[1], s.bounds.upperLeft().y);
  				selectionBounds[2] = Math.max(selectionBounds[2], s.bounds.lowerRight().x);
  				selectionBounds[3] = Math.max(selectionBounds[3], s.bounds.lowerRight().y);
  			});
  			if(selectionBounds[0]+p.x < 0)
  				p.x = -selectionBounds[0];
  			if(selectionBounds[1]+p.y < 0)
  				p.y = -selectionBounds[1];
  			if(selectionBounds[2]+p.x > this.facade.getCanvas().bounds.lowerRight().x)
  				p.x = this.facade.getCanvas().bounds.lowerRight().x - selectionBounds[2];
  			if(selectionBounds[3]+p.y > this.facade.getCanvas().bounds.lowerRight().y)
  				p.y = this.facade.getCanvas().bounds.lowerRight().y - selectionBounds[3];
  			
  			if(p.x!=0 || p.y!=0) {
  				// Instantiate the moveCommand
  				var commands = [new ORYX.Core.Command.Move(selection, p, null, currentSelection, this)];
  				// Execute the commands			
  				this.facade.executeCommands(commands);
  			}
  			
  		}
  	},
  	
  	getUndockedCommant: function(shapes){
  
  		var undockEdgeCommand = ORYX.Core.Command.extend({
  			construct: function(moveShapes){
  				this.dockers = moveShapes.collect(function(shape){ return shape instanceof ORYX.Core.Controls.Docker ? {docker:shape, dockedShape:shape.getDockedShape(), refPoint:shape.referencePoint} : undefined }).compact();
  			},			
  			execute: function(){
  				this.dockers.each(function(el){
  					el.docker.setDockedShape(undefined);
  				})
  			},
  			rollback: function(){
  				this.dockers.each(function(el){
  					el.docker.setDockedShape(el.dockedShape);
  					el.docker.setReferencePoint(el.refPoint);
  					//el.docker.update();
  				})
  			}
  		});
  		
  		command = new undockEdgeCommand( shapes );
  		command.execute();	
  		return command;
  	},
  	
  //    /**
  //     * The key handler for this plugin. Every action from the set of cut, copy,
  //     * paste and delete should be accessible trough simple keyboard shortcuts.
  //     * This method checks whether any event triggers one of those actions.
  //     *
  //     * @param {Object} event The keyboard event that should be analysed for
  //     *     triggering of this plugin.
  //     */
  //    keyHandler: function(event){
  //        //TODO document what event.which is.
  //        
  //        ORYX.Log.debug("keysMove.js handles a keyEvent.");
  //        
  //        // assure we have the current event.
  //        if (!event) 
  //            event = window.event;
  //        
  //        // get the currently pressed key and state of control key.
  //        var pressedKey = event.which || event.keyCode;
  //        var ctrlPressed = event.ctrlKey;
  //
  //		// if the key is one of the arrow keys, forward to move and return.
  //		if ([ORYX.CONFIG.KEY_CODE_LEFT, ORYX.CONFIG.KEY_CODE_RIGHT,
  //			ORYX.CONFIG.KEY_CODE_UP, ORYX.CONFIG.KEY_CODE_DOWN].include(pressedKey)) {
  //			
  //			this.move(pressedKey, !ctrlPressed);
  //			return;
  //		}
  //		
  //    }
  	
  });
  /**
   * Copyright (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.Plugins) { ORYX.Plugins = {} }
  if(!ORYX.Plugins.Layouter) { ORYX.Plugins.Layouter = {} }
  
  new function(){
  	
  	/**
  	 * Edge layouter is an implementation to layout an edge
  	 * @class ORYX.Plugins.Layouter.EdgeLayouter
  	 * @author Willi Tscheschner
  	 */
  	ORYX.Plugins.Layouter.EdgeLayouter = ORYX.Plugins.AbstractLayouter.extend({
  		
  		/**
  		 * Layout only Edges
  		 */
  		layouted : [	"http://b3mn.org/stencilset/bpmn1.1#SequenceFlow", 
  						"http://b3mn.org/stencilset/bpmn1.1#MessageFlow",
  						"http://b3mn.org/stencilset/timjpdl3#SequenceFlow", 
  						"http://b3mn.org/stencilset/jbpm4#SequenceFlow", 
  						"http://b3mn.org/stencilset/bpmn2.0#MessageFlow",
  						"http://b3mn.org/stencilset/bpmn2.0#SequenceFlow", 
  						"http://b3mn.org/stencilset/bpmn2.0choreography#MessageFlow",
  						"http://b3mn.org/stencilset/bpmn2.0choreography#SequenceFlow", 
  						"http://b3mn.org/stencilset/bpmn2.0conversation#ConversationLink",
  						"http://b3mn.org/stencilset/epc#ControlFlow",
  						"http://www.signavio.com/stencilsets/processmap#ProcessLink",
  						"http://www.signavio.com/stencilsets/organigram#connection"],
  		
  		/**
  		 * Layout a set on edges
  		 * @param {Object} edges
  		 */
  		layout: function(edges){
  			edges.each(function(edge){
  				this.doLayout(edge)
  			}.bind(this))
  		},
  		
  		/**
  		 * Layout one edge
  		 * @param {Object} edge
  		 */
  		doLayout: function(edge){
  			// Get from and to node
  			var from 	= edge.getIncomingNodes()[0]; 
  			var to 		= edge.getOutgoingNodes()[0];
  			
  			// Return if one is null
  			if (!from || !to) { return }
  			
  			var positions = this.getPositions(from, to, edge);
  		
  			if (positions.length > 0){
  				this.setDockers(edge, positions[0].a, positions[0].b);
  			}
  				
  		},
  		
  		/**
  		 * Returns a set on positions which are not containt either 
  		 * in the bounds in from or to.
  		 * @param {Object} from Shape where the edge is come from
  		 * @param {Object} to Shape where the edge is leading to
  		 * @param {Object} edge Edge between from and to
  		 */
  		getPositions : function(from, to, edge){
  			
  			// Get absolute bounds
  			var ab = from.absoluteBounds();
  			var bb = to.absoluteBounds();
  			
  			// Get center from and to
  			var a = ab.center();
  			var b = bb.center();
  			
  			var am = ab.midPoint();
  			var bm = bb.midPoint();
  		
  			// Get first and last reference point
  			var first = Object.clone(edge.dockers.first().referencePoint);
  			var last = Object.clone(edge.dockers.last().referencePoint);
  			// Get the absolute one
  			var aFirst = edge.dockers.first().getAbsoluteReferencePoint();
  			var aLast = edge.dockers.last().getAbsoluteReferencePoint(); 
  			
  			// IF ------>
  			// or  |
  			//     V
  			// Do nothing
  			if (Math.abs(aFirst.x-aLast.x) < 1 || Math.abs(aFirst.y-aLast.y) < 1) {
  				return []
  			}
  			
  			// Calc center position, between a and b
  			// depending on there weight
  			var m = {}
  			m.x = a.x < b.x ? 
  					(((b.x - bb.width()/2) - (a.x + ab.width()/2))/2) + (a.x + ab.width()/2): 
  					(((a.x - ab.width()/2) - (b.x + bb.width()/2))/2) + (b.x + bb.width()/2);
  
  			m.y = a.y < b.y ? 
  					(((b.y - bb.height()/2) - (a.y + ab.height()/2))/2) + (a.y + ab.height()/2): 
  					(((a.y - ab.height()/2) - (b.y + bb.height()/2))/2) + (b.y + bb.height()/2);
  								
  								
  			// Enlarge both bounds with 10
  			ab.widen(5); // Wide the from less than 
  			bb.widen(20);// the to because of the arrow from the edge
  								
  			var positions = [];
  			var off = this.getOffset.bind(this);
  			
  			// Checks ----+
  			//            |
  			//            V
  			if (!ab.isIncluded(b.x, a.y)&&!bb.isIncluded(b.x, a.y)) {
  				positions.push({
  					a : {x:b.x+off(last,bm,"x"),y:a.y+off(first,am,"y")},
  					z : this.getWeight(from, a.x < b.x ? "r" : "l", to, a.y < b.y ? "t" : "b", edge)
  				});
  			}
  						
  			// Checks | 
  			//        +--->
  			if (!ab.isIncluded(a.x, b.y)&&!bb.isIncluded(a.x, b.y)) {
  				positions.push({
  					a : {x:a.x+off(first,am,"x"),y:b.y+off(last,bm,"y")},
  					z : this.getWeight(from, a.y < b.y ? "b" : "t", to, a.x < b.x ? "l" : "r", edge)
  				});
  			}
  						
  			// Checks  --+
  			//           |
  			//           +--->
  			if (!ab.isIncluded(m.x, a.y)&&!bb.isIncluded(m.x, b.y)) {
  				positions.push({
  					a : {x:m.x,y:a.y+off(first,am,"y")},
  					b : {x:m.x,y:b.y+off(last,bm,"y")},
  					z : this.getWeight(from, "r", to, "l", edge, a.x > b.x)
  				});
  			}
  			
  			// Checks | 
  			//        +---+
  			//            |
  			//            V
  			if (!ab.isIncluded(a.x, m.y)&&!bb.isIncluded(b.x, m.y)) {
  				positions.push({
  					a : {x:a.x+off(first,am,"x"),y:m.y},
  					b : {x:b.x+off(last,bm,"x"),y:m.y},
  					z : this.getWeight(from, "b", to, "t", edge, a.y > b.y)
  				});
  			}	
  			
  			// Sort DESC of weights
  			return positions.sort(function(a,b){ return a.z < b.z ? 1 : (a.z == b.z ? -1 : -1)});
  		},
  		
  		/**
  		 * Returns a offset for the pos to the center of the bounds
  		 * 
  		 * @param {Object} val
  		 * @param {Object} pos2
  		 * @param {String} dir Direction x|y
  		 */
  		getOffset: function(pos, pos2, dir){
  			return pos[dir] - pos2[dir];
  		},
  		
  		/**
  		 * Returns a value which shows the weight for this configuration
  		 * 
  		 * @param {Object} from Shape which is coming from
  		 * @param {String} d1 Direction where is goes
  		 * @param {Object} to Shape which goes to
  		 * @param {String} d2 Direction where it comes to
  		 * @param {Object} edge Edge between from and to
  		 * @param {Boolean} reverse Reverse the direction (e.g. "r" -> "l")
  		 */
  		getWeight: function(from, d1, to, d2, edge, reverse){
  			
  			d1 = (d1||"").toLowerCase();
  			d2 = (d2||"").toLowerCase();
  			
  			if (!["t","r","b","l"].include(d1)){ d1 = "r"}
  			if (!["t","r","b","l"].include(d2)){ d1 = "l"}
  			
  			// If reverse is set
  			if (reverse) {
  				// Reverse d1 and d2
  				d1 = d1=="t"?"b":(d1=="r"?"l":(d1=="b"?"t":(d1=="l"?"r":"r")))
  				d2 = d2=="t"?"b":(d2=="r"?"l":(d2=="b"?"t":(d2=="l"?"r":"r")))
  			}
  			
  					
  			var weight = 0;
  			// Get rules for from "out" and to "in"
  			var dr1 = this.facade.getRules().getLayoutingRules(from, edge)["out"];
  			var dr2 = this.facade.getRules().getLayoutingRules(to, edge)["in"];
  
  			var fromWeight = dr1[d1];
  			var toWeight = dr2[d2];
  
  
  			/**
  			 * Return a true if the center 1 is in the same direction than center 2
  			 * @param {Object} direction
  			 * @param {Object} center1
  			 * @param {Object} center2
  			 */
  			var sameDirection = function(direction, center1, center2){
  				switch(direction){
  					case "t": return Math.abs(center1.x - center2.x) < 2 && center1.y < center2.y
  					case "r": return center1.x > center2.x && Math.abs(center1.y - center2.y) < 2
  					case "b": return Math.abs(center1.x - center2.x) < 2 && center1.y > center2.y
  					case "l": return center1.x < center2.x && Math.abs(center1.y - center2.y) < 2
  					default: return false;
  				}
  			}
  
  			// Check if there are same incoming edges from 'from'
  			var sameIncomingFrom = from
  								.getIncomingShapes()
  								.findAll(function(a){ return a instanceof ORYX.Core.Edge})
  								.any(function(e){ 
  									return sameDirection(d1, e.dockers[e.dockers.length-2].bounds.center(), e.dockers.last().bounds.center());
  								});
  
  			// Check if there are same outgoing edges from 'to'
  			var sameOutgoingTo = to
  								.getOutgoingShapes()
  								.findAll(function(a){ return a instanceof ORYX.Core.Edge})
  								.any(function(e){ 
  									return sameDirection(d2, e.dockers[1].bounds.center(), e.dockers.first().bounds.center());
  								});
  			
  			// If there are equivalent edges, set 0
  			//fromWeight = sameIncomingFrom ? 0 : fromWeight;
  			//toWeight = sameOutgoingTo ? 0 : toWeight;
  			
  			// Get the sum of "out" and the direction plus "in" and the direction 						
  			return (sameIncomingFrom||sameOutgoingTo?0:fromWeight+toWeight);
  		},
  		
  		/**
  		 * Removes all current dockers from the node 
  		 * (except the start and end) and adds two new
  		 * dockers, on the position a and b.
  		 * @param {Object} edge
  		 * @param {Object} a
  		 * @param {Object} b
  		 */
  		setDockers: function(edge, a, b){
  			if (!edge){ return }
  			
  			// Remove all dockers (implicit,
  			// start and end dockers will not removed)
  			edge.dockers.each(function(r){
  				edge.removeDocker(r);
  			});
  			
  			// For a and b (if exists), create
  			// a new docker and set position
  			[a, b].compact().each(function(pos){
  				var docker = edge.createDocker(undefined, pos);
  				docker.bounds.centerMoveTo(pos);
  			});
  			
  			// Update all dockers from the edge
  			edge.dockers.each(function(docker){
  				docker.update()
  			})
  			
  			// Update edge
  			//edge.refresh();
  			edge._update(true);
  			
  		}
  	});
  	
  	
  }()
  /**
   * Copyright (c) 2009
   * Sven Wagner-Boysen, 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();
  
  new function(){
  	
  	ORYX.Plugins.BPMN2_0 = {
  	
  		/**
  		 *	Constructor
  		 *	@param {Object} Facade: The Facade of the Editor
  		 */
  		construct: function(facade){
  			this.facade = facade;
  			
  			this.facade.registerOnEvent(ORYX.CONFIG.EVENT_DRAGDOCKER_DOCKED, this.handleDockerDocked.bind(this));
  			this.facade.registerOnEvent(ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED, this.handlePropertyChanged.bind(this));
  			this.facade.registerOnEvent('layout.bpmn2_0.pool', this.handleLayoutPool.bind(this));
  			this.facade.registerOnEvent('layout.bpmn2_0.subprocess', this.handleSubProcess.bind(this));
  			this.facade.registerOnEvent(ORYX.CONFIG.EVENT_SHAPEREMOVED, this.handleShapeRemove.bind(this));
  			//this.facade.registerOnEvent('layout.bpmn11.lane', this.handleLayoutLane.bind(this));
  
  			this.facade.registerOnEvent(ORYX.CONFIG.EVENT_LOADED, this.afterLoad.bind(this));
  			
  
  			this.namespace = undefined;
  		},
  		
  		/**
  		 * Force to update every pool
  		 */
  		afterLoad: function(){
  			this.facade.getCanvas().getChildNodes().each(function(shape){
  				if (shape.getStencil().id().endsWith("Pool")) {
  					this.handleLayoutPool({
  						shape: shape
  					});
  				}
  			}.bind(this))
  		},
  		
  		/**
  		 * If a pool is selected and contains no lane,
  		 * a lane is created automagically
  		 */
  		onSelectionChanged: function(event) {
  			var selection = event.elements;
  			
  			if(selection && selection.length === 1) {
  				var namespace = this.getNamespace();
  				var shape = selection[0];
  				if(shape.getStencil().idWithoutNs() === "Pool") {
  					if(shape.getChildNodes().length === 0) {
  						// create a lane inside the selected pool
  						var option = {
  								type:namespace + "Lane",
  								position:{x:0,y:0},
  								namespace:shape.getStencil().namespace(),
  								parent:shape
  						};
  						this.facade.createShape(option);
  						this.facade.getCanvas().update();
  						this.facade.setSelection([shape]);
  					}
  				}
  			}
  			
  			// Preventing selection of all lanes but not the pool
  			if(selection.any(function(s){ return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith("Lane")})){
  				var lanes = selection.findAll(function(s){
  					return s instanceof ORYX.Core.Node && s.getStencil().id().endsWith("Lane")
  				});
  				
  				var pools = [];
  				var unselectLanes = [];
  				lanes.each(function(lane){
  					pools.push(this.getParentPool(lane))
  				}.bind(this));
  				
  				pools = pools.uniq().findAll(function(pool){
  					var childLanes = this.getLanes(pool, true);
  					if (childLanes.all(function(lane){ return lanes.include(lane)})){
  						unselectLanes = unselectLanes.concat(childLanes);
  						return true;
  					} else if (selection.include(pool) && childLanes.any(function(lane){ return lanes.include(lane)})) {
  						unselectLanes = unselectLanes.concat(childLanes);
  						return true;
  					} else {
  						return false;
  					}
  				}.bind(this))
  				
  				if (unselectLanes.length > 0 && pools.length > 0){
  					selection = selection.without.apply(selection, unselectLanes);
  					selection = selection.concat(pools);
  					this.facade.setSelection(selection.uniq());
  				}
  			}
  		},
  		
  		handleShapeRemove: function(option) {
  			
  			var sh 				= option.shape;
  			var parent 			= option.parent;
  						
  			if (sh instanceof ORYX.Core.Node && sh.getStencil().idWithoutNs() === "Lane" && this.facade.isExecutingCommands()) {
  			
  				var pool = this.getParentPool(parent);
  				if (pool&&pool.parent){		
  				
  					var isLeafFn = function(leaf){
  						return !leaf.getChildNodes().any(function(r){ return r.getStencil().idWithoutNs() === "Lane"});
  					}
  					
  					var isLeaf = isLeafFn(sh);
  					var parentHasMoreLanes = parent.getChildNodes().any(function(r){ return r.getStencil().idWithoutNs() === "Lane"});
  					
  					if (isLeaf && parentHasMoreLanes){
  						
  						var command = new ResizeLanesCommand(sh, parent, pool, this);
  						this.facade.executeCommands([command]);
  						
  					} else if(	!isLeaf &&
  								!this.facade.getSelection().any(function(select){ // Find one of the selection, which is a lane and child of "sh" and is a leaf lane
  										return 	select instanceof ORYX.Core.Node && select.getStencil().idWithoutNs() === "Lane" &&
  												select.isParent(sh) && isLeafFn(select);})) {
  													
  						var Command = ORYX.Core.Command.extend({
  							construct: function(shape, facade) {
  								this.children = shape.getChildNodes(true);
  								this.facade = facade;
  							},
  							execute: function() {
  								this.children.each(function(child){
  									child.bounds.moveBy(30,0)
  								});
  								//this.facade.getCanvas().update();
  							},
  							rollback: function() {
  								this.children.each(function(child){
  									child.bounds.moveBy(-30,0)
  								})
  								//this.facade.getCanvas().update();
  							}
  						});
  						this.facade.executeCommands([new Command(sh, this.facade)]);
  						
  					} else if (isLeaf&&!parentHasMoreLanes&&parent == pool){
  						parent.add(sh);
  					}
  				}
  			
  			}
  			
  		},
  		
  		hashedSubProcesses: {},
  		
  		hashChildShapes: function(shape){
  			var children = shape.getChildNodes();
  			children.each(function(child){
  				if (this.hashedSubProcesses[child.id]){
  					this.hashedSubProcesses[child.id] = child.absoluteXY();
  					this.hashedSubProcesses[child.id].width 	= child.bounds.width();
  					this.hashedSubProcesses[child.id].height 	= child.bounds.height();
  					this.hashChildShapes(child);
  				}
  			}.bind(this));
  		},
  	
  		/**
  		 * Handle the layouting of a sub process.
  		 * Mainly to adjust the child dockers of a sub process. 
  		 *
  		 */
  		handleSubProcess : function(option) {
  			
  			var sh = option.shape;
  			
  			if (!this.hashedSubProcesses[sh.id]) {
  				this.hashedSubProcesses[sh.id] = sh.absoluteXY();
  				this.hashedSubProcesses[sh.id].width 	= sh.bounds.width();
  				this.hashedSubProcesses[sh.id].height 	= sh.bounds.height();
  				return;
  			}
  			
  			var offset = sh.absoluteXY();
  			offset.x -= this.hashedSubProcesses[sh.id].x;
  			offset.y -= this.hashedSubProcesses[sh.id].y;
  			
  			var resized = this.hashedSubProcesses[sh.id].width !== sh.bounds.width() || this.hashedSubProcesses[sh.id].height !== sh.bounds.height();
  			
  			this.hashedSubProcesses[sh.id] = sh.absoluteXY();
  			this.hashedSubProcesses[sh.id].width 	= sh.bounds.width();
  			this.hashedSubProcesses[sh.id].height 	= sh.bounds.height();
  			this.hashChildShapes(sh);
  			
  			
  			// Move dockers only if currently is not resizing
  			if (this.facade.isExecutingCommands()&&!resized) {
  				this.moveChildDockers(sh, offset);
  			}
  		},
  		
  		moveChildDockers: function(shape, offset){
  			
  			if (!offset.x && !offset.y) {
  				return;
  			} 
  			
  			var children = shape.getChildNodes(true);
  			
  			// Get all nodes
  			var dockers = children
  				// Get all incoming and outgoing edges
  				.map(function(node){
  					return [].concat(node.getIncomingShapes())
  							.concat(node.getOutgoingShapes())
  				})
  				// Flatten all including arrays into one
  				.flatten()
  				// Get every edge only once
  				.uniq()
  				// Get all dockers
  				.map(function(edge){
  					return edge.dockers.length > 2 ? 
  							edge.dockers.slice(1, edge.dockers.length-1) : 
  							[];
  				})
  				// Flatten the dockers lists
  				.flatten();
  	
  			var abs = shape.absoluteBounds();
  			abs.moveBy(-offset.x, -offset.y)
  			var obj = {};
  			dockers.each(function(docker){
  				
  				if (docker.isChanged){
  					return;
  				}
  				
  				var off = Object.clone(offset);
  				
  				if (!abs.isIncluded(docker.bounds.center())){
  					var index 	= docker.parent.dockers.indexOf(docker);
  					var size	= docker.parent.dockers.length;
  					var from 	= docker.parent.getSource();
  					var to 		= docker.parent.getTarget();
  					
  					var bothAreIncluded = children.include(from) && children.include(to);
  					
  					if (!bothAreIncluded){
  						var previousIsOver = index !== 0 ? abs.isIncluded(docker.parent.dockers[index-1].bounds.center()) : false;
  						var nextIsOver = index !== size-1 ? abs.isIncluded(docker.parent.dockers[index+1].bounds.center()) : false;
  						
  						if (!previousIsOver && !nextIsOver){ return; }
  						
  						var ref = docker.parent.dockers[previousIsOver ? index-1 : index+1];
  						if (Math.abs(-Math.abs(ref.bounds.center().x-docker.bounds.center().x)) < 2){
  							off.y = 0;
  						} else if(Math.abs(-Math.abs(ref.bounds.center().y-docker.bounds.center().y)) < 2){
  							off.x = 0;
  						} else {
  							return;
  						}
  					}
  					
  				}
  				
  				obj[docker.getId()] = {
  					docker:docker,
  					offset:off
  				}
  			})
  			
  			// Set dockers
  			this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(obj)]);
  				
  		},
  		
  		/**
  		 * DragDocker.Docked Handler
  		 *
  		 */	
  		handleDockerDocked: function(options) {
  			var namespace = this.getNamespace();
  			
  			var edge = options.parent;
  			var edgeSource = options.target;
  			
  			if(edge.getStencil().id() === namespace + "SequenceFlow") {
  				var isGateway = edgeSource.getStencil().groups().find(function(group) {
  						if(group == "Gateways") 
  							return group;
  					});
  				if(!isGateway && (edge.properties["oryx-conditiontype"] == "Expression"))
  					// show diamond on edge source
  					edge.setProperty("oryx-showdiamondmarker", true);
  				else 
  					// do not show diamond on edge source
  					edge.setProperty("oryx-showdiamondmarker", false);
  				
  				// update edge rendering
  				//edge.update();
  				
  				this.facade.getCanvas().update();
  			}
  		},
  		
  		/**
  		 * PropertyWindow.PropertyChanged Handler
  		 */
  		handlePropertyChanged: function(option) {
  			var namespace = this.getNamespace();
  			
  			var shapes = option.elements;
  			var propertyKey = option.key;
  			var propertyValue = option.value;
  			
  			var changed = false;
  			shapes.each(function(shape){
  				if((shape.getStencil().id() === namespace + "SequenceFlow") &&
  					(propertyKey === "oryx-conditiontype")) {
  					
  					if(propertyValue != "Expression")
  						// Do not show the Diamond
  						shape.setProperty("oryx-showdiamondmarker", false);
  					else {
  						var incomingShapes = shape.getIncomingShapes();
  						
  						if(!incomingShapes) {
  							shape.setProperty("oryx-showdiamondmarker", true);
  						}
  						
  						var incomingGateway = incomingShapes.find(function(aShape) {
  							var foundGateway = aShape.getStencil().groups().find(function(group) {
  								if(group == "Gateways") 
  									return group;
  							});
  							if(foundGateway)
  								return foundGateway;
  						});
  						
  						if(!incomingGateway) 
  							// show diamond on edge source
  							shape.setProperty("oryx-showdiamondmarker", true);
  						else
  							// do not show diamond
  							shape.setProperty("oryx-showdiamondmarker", false);
  					}
  					
  					changed = true;
  				}
  			}.bind(this));
  			
  			if(changed) {this.facade.getCanvas().update();}
  			
  		},
  		
  		hashedPoolPositions : {},
  		hashedLaneDepth : {},
  		hashedBounds : {},
  		hashedPositions: {},
  		
  		/**
  		 * Handler for layouting event 'layout.bpmn2_0.pool'
  		 * @param {Object} event
  		 */
  		handleLayoutPool: function(event){
  			
  			
  			var pool = event.shape;
  			var selection = this.facade.getSelection(); 
  			var currentShape = selection.include(pool) ? pool : selection.first();
  			
  			currentShape = currentShape || pool;
  			
  			this.currentPool = pool;
  			
  			// Check if it is a pool or a lane
  			if (!(currentShape.getStencil().id().endsWith("Pool") || currentShape.getStencil().id().endsWith("Lane"))) {
  				return;
  			}
  			
  			// Check if the lane is within the pool and is not removed lately 
  			if (currentShape !== pool && !currentShape.isParent(pool) && !this.hashedBounds[pool.id][currentShape.id]){
  				return;
  			}
  			
  			
  			if (!this.hashedBounds[pool.id]) {
  				this.hashedBounds[pool.id] = {};
  			}
  			
  			// Find all child lanes
  			var lanes = this.getLanes(pool);
  			
  			if (lanes.length <= 0) {
  				return
  			}
  			
  			var allLanes = this.getLanes(pool, true), hp;
  			var considerForDockers = allLanes.clone();
  			
  			var hashedPositions = $H({});
  			allLanes.each(function(lane){
  				hashedPositions[lane.id] = lane.bounds.upperLeft();
  			})
  			
  			
  			
  			// Show/hide caption regarding the number of lanes
  			if (lanes.length === 1 && this.getLanes(lanes.first()).length <= 0) {
  				// TRUE if there is a caption
  				lanes.first().setProperty("oryx-showcaption", lanes.first().properties["oryx-name"].trim().length > 0);
  				var rect = lanes.first().node.getElementsByTagName("rect");
  				rect[0].setAttributeNS(null, "display", "none");
  			} else {
  				allLanes.invoke("setProperty", "oryx-showcaption", true);
  				allLanes.each(function(lane){
  					var rect = lane.node.getElementsByTagName("rect");
  					rect[0].removeAttributeNS(null, "display");
  				})
  			}
  			
  			var deletedLanes = [];
  			var addedLanes = [];
  			
  			// Get all new lanes
  			var i=-1;
  			while (++i<allLanes.length) {
  				if (!this.hashedBounds[pool.id][allLanes[i].id]){
  					addedLanes.push(allLanes[i])
  				}
  			}
  			
  			if (addedLanes.length > 0){
  				currentShape = addedLanes.first();
  			}
  			
  			
  			// Get all deleted lanes
  			var resourceIds = $H(this.hashedBounds[pool.id]).keys();
  			var i=-1;
  			while (++i<resourceIds.length) {
  				if (!allLanes.any(function(lane){ return lane.id == resourceIds[i]})){
  					deletedLanes.push(this.hashedBounds[pool.id][resourceIds[i]]);
  					selection = selection.without(function(r){ return r.id == resourceIds[i] });
  				}
  			}		
  			
  			var height, width, x, y;
  			
  			if (deletedLanes.length > 0 || addedLanes.length > 0) {
  				
  				if (addedLanes.length === 1 && this.getLanes(addedLanes[0].parent).length === 1){
  					// Set height from the pool
  					height = this.adjustHeight(lanes, addedLanes[0].parent);
  				} else {
  					// Set height from the pool
  					height = this.updateHeight(pool);
  				}
  				// Set width from the pool
  				width = this.adjustWidth(lanes, pool.bounds.width());	
  				
  				pool.update();
  			}
  			
  			/**
  			 * Set width/height depending on the pool
  			 */
  			else if (pool == currentShape) {
  				
  				if (selection.length === 1 && this.isResized(pool, this.hashedPoolPositions[pool.id])) {
  					var oldXY = this.hashedPoolPositions[pool.id].upperLeft();
  					var xy = pool.bounds.upperLeft();
  					var scale = 0;
  					if (this.shouldScale(pool)){
  						var old = this.hashedPoolPositions[pool.id];
  						scale = old.height()/pool.bounds.height();
  					}
  				
  					this.adjustLanes(pool, allLanes, oldXY.x - xy.x, oldXY.y - xy.y, scale);
  				}
  				
  				// Set height from the pool
  				height = this.adjustHeight(lanes, undefined, pool.bounds.height());
  				// Set width from the pool
  				width = this.adjustWidth(lanes, pool.bounds.width());		
  			}
  			
  			/**‚
  			 * Set width/height depending on containing lanes
  			 */		
  			else {
  				
  				// Reposition the pool if one shape is selected and the upperleft has changed
  				if (selection.length === 1 && this.isResized(currentShape, this.hashedBounds[pool.id][currentShape.id])){
  					var oldXY = this.hashedBounds[pool.id][currentShape.id].upperLeft();
  					var xy = currentShape.absoluteXY();
  					x = oldXY.x - xy.x;
  					y = oldXY.y - xy.y;
  					
  					// Adjust all other lanes beneath this lane
  					if (x||y){
  						considerForDockers = considerForDockers.without(currentShape);
  						this.adjustLanes(pool, this.getAllExcludedLanes(pool, currentShape), x, 0);
  					}
  					
  					// Adjust all child lanes
  					var childLanes = this.getLanes(currentShape, true);
  					if (childLanes.length > 0){
  						if (this.shouldScale(currentShape)){
  							var old = this.hashedBounds[pool.id][currentShape.id];
  							var scale = old.height()/currentShape.bounds.height();
  							this.adjustLanes(pool, childLanes, x, y, scale);
  						} else {
  							this.adjustLanes(pool, childLanes, x, y, 0);
  						}
  					}
  				}
  				
  				// Cache all bounds
  				var changes = allLanes.map(function(lane){ return {
  					shape: lane,
  					bounds: lane.bounds.clone()
  				} });
  				
  				// Get height and adjust child heights
  				height = this.adjustHeight(lanes, currentShape);
  				// Check if something has changed and maybe create a command
  				this.checkForChanges(allLanes, changes);
  				
  				// Set width from the current shape
  				width = this.adjustWidth(lanes, currentShape.bounds.width()+(this.getDepth(currentShape,pool)*30));
  			}
  			
  			this.setDimensions(pool, width, height, x, y);
  			
  			
  			if (this.facade.isExecutingCommands() && (deletedLanes.length === 0 || addedLanes.length !== 0)){ 
  				// Update all dockers
  				this.updateDockers(considerForDockers, pool);
  				
  				// Check if the order has changed
  				if (this.hashedPositions[pool.id] && this.hashedPositions[pool.id].keys().any(function(key, i){
  						return (allLanes[i]||{}).id	!== key;
  					})){
  					
  					var LanesHasBeenReordered = ORYX.Core.Command.extend({
  						construct: function(originPosition, newPosition, lanes, plugin, poolId) {
  							this.originPosition = Object.clone(originPosition);
  							this.newPosition = Object.clone(newPosition);
  							this.lanes = lanes;
  							this.plugin = plugin;
  							this.pool = poolId;
  						}, 
  						execute: function(){
  							if (!this.executed){
  								this.executed = true;
  								this.lanes.each(function(lane){
  									if (this.newPosition[lane.id])
  										lane.bounds.moveTo(this.newPosition[lane.id])
  								}.bind(this));
  								this.plugin.hashedPositions[this.pool] = Object.clone(this.newPosition);
  							}
  						}, 
  						rollback: function(){
  							this.lanes.each(function(lane){
  								if (this.originPosition[lane.id])
  									lane.bounds.moveTo(this.originPosition[lane.id])
  							}.bind(this));
  							this.plugin.hashedPositions[this.pool] = Object.clone(this.originPosition);
  						}
  					});
  					
  					var hp2 = $H({});
  					allLanes.each(function(lane){
  						hp2[lane.id] = lane.bounds.upperLeft();
  					})
  				
  					var command = new LanesHasBeenReordered(hashedPositions, hp2, allLanes, this, pool.id);
  					this.facade.executeCommands([command]);
  						
  				}
  			}
  			
  			this.hashedBounds[pool.id] = {};
  			this.hashedPositions[pool.id] = hashedPositions;
  			
  			var i=-1;
  			while (++i < allLanes.length) {
  				// Cache positions
  				this.hashedBounds[pool.id][allLanes[i].id] = allLanes[i].absoluteBounds();
  				
  				// Cache also the bounds of child shapes, mainly for child subprocesses
  				this.hashChildShapes(allLanes[i]);
  			
  				this.hashedLaneDepth[allLanes[i].id] = this.getDepth(allLanes[i], pool);
  				
  				this.forceToUpdateLane(allLanes[i]);
  			}
  			
  			this.hashedPoolPositions[pool.id] = pool.bounds.clone();
  			
  			
  			// Update selection
  			//this.facade.setSelection(selection);		
  		},
  		
  		shouldScale: function(element){
  			var childLanes = element.getChildNodes().findAll(function(shape){ return shape.getStencil().id().endsWith("Lane") })
  			return childLanes.length > 1 || childLanes.any(function(lane){ return this.shouldScale(lane) }.bind(this)) 
  		},
  		
  		/**
  		 * Lookup if some bounds has changed
  		 * @param {Object} lanes
  		 * @param {Object} changes
  		 */
  		checkForChanges: function(lanes, changes){
  			// Check if something has changed
  			if (this.facade.isExecutingCommands() && changes.any(function(change){
  				return change.shape.bounds.toString() !== change.bounds.toString();
  			})){
  				
  				var Command = ORYX.Core.Command.extend({
  							construct: function(changes) {
  								this.oldState = changes;
  								this.newState = changes.map(function(s){ return {shape:s.shape, bounds:s.bounds.clone()}});
  							}, 
  							execute: function(){
  								if (this.executed){
  									this.applyState(this.newState);
  								}
  								this.executed = true;
  							}, 
  							rollback: function(){
  								this.applyState(this.oldState);
  							},
  							applyState: function(state){
  								state.each(function(s){
  									s.shape.bounds.set(s.bounds.upperLeft(), s.bounds.lowerRight());
  								})
  							}
  						});
  						
  				this.facade.executeCommands([new Command(changes)]);
  			}
  		},
  		
  		isResized: function(shape, bounds){
  			
  			if (!bounds||!shape){
  				return false;
  			}
  			
  			var oldB = bounds;
  			//var oldXY = oldB.upperLeft();
  			//var xy = shape.absoluteXY();
  			return Math.round(oldB.width() - shape.bounds.width()) !== 0 || Math.round(oldB.height() - shape.bounds.height()) !== 0
  		},
  		
  		adjustLanes: function(pool, lanes, x, y, scale){
  			
  			scale = scale || 0;
  	
  			// For every lane, adjust the child nodes with the offset
  			lanes.each(function(l){
  				l.getChildNodes().each(function(child){
  					if (!child.getStencil().id().endsWith("Lane")){
  						var cy = scale ? child.bounds.center().y - (child.bounds.center().y/scale) : -y;
  						child.bounds.moveBy((x||0), -cy);
  						
  						if (scale&&child.getStencil().id().endsWith("Subprocess")) {
  							this.moveChildDockers(child, {x:(0), y:-cy});
  						}
  					
  					}
  				}.bind(this));
  				this.hashedBounds[pool.id][l.id].moveBy(-(x||0), !scale?-y:0);
  				if (scale) {
  					l.isScaled = true;
  				}
  			}.bind(this))
  			
  		},
  		
  		getAllExcludedLanes: function(parent, lane){
  			var lanes = [];
  			parent.getChildNodes().each(function(shape){
  				if ((!lane || shape !== lane) && shape.getStencil().id().endsWith("Lane")){
  					lanes.push(shape);
  					lanes = lanes.concat(this.getAllExcludedLanes(shape, lane));
  				}
  			}.bind(this));
  			return lanes;
  		},
  		
  		
  		forceToUpdateLane: function(lane){
  			
  			if (lane.bounds.height() !== lane._svgShapes[0].height) {	
  				lane.isChanged = true;
  				lane.isResized = true;
  				lane._update();
  			}
  		},
  		
  		getDepth: function(child, parent){
  			
  			var i=0;
  			while(child && child.parent && child !== parent){
  				child = child.parent;
  				++i
  			}
  			return i;
  		},
  		
  		updateDepth: function(lane, fromDepth, toDepth){
  			
  			var xOffset = (fromDepth - toDepth) * 30;
  			
  			lane.getChildNodes().each(function(shape){
  				shape.bounds.moveBy(xOffset, 0);
  				
  				[].concat(children[j].getIncomingShapes())
  						.concat(children[j].getOutgoingShapes())
  						
  			})
  			
  		},
  		
  		setDimensions: function(shape, width, height, x, y){
  			var isLane = shape.getStencil().id().endsWith("Lane");
  			// Set the bounds
  			shape.bounds.set(
  					isLane 	? 30 : (shape.bounds.a.x - (x || 0)), 
  					isLane 	? shape.bounds.a.y : (shape.bounds.a.y - (y || 0)), 
  					width	? shape.bounds.a.x + width - (isLane?30:(x||0)) : shape.bounds.b.x, 
  					height 	? shape.bounds.a.y + height - (isLane?0:(y||0)) : shape.bounds.b.y
  				);
  		},
  	
  		setLanePosition: function(shape, y){
  			
  			shape.bounds.moveTo(30, y);
  		
  		},
  			
  		adjustWidth: function(lanes, width) {
  			
  			// Set width to each lane
  			(lanes||[]).each(function(lane){
  				this.setDimensions(lane, width);
  				this.adjustWidth(this.getLanes(lane), width-30);
  			}.bind(this));
  			
  			return width;
  		},
  		
  		
  		adjustHeight: function(lanes, changedLane, propagateHeight){
  			
  			var oldHeight = 0;
  			if (!changedLane && propagateHeight){
  				var i=-1;
  				while (++i<lanes.length){	
  					oldHeight += lanes[i].bounds.height();		
  				}
  			}
  			
  			var i=-1;
  			var height = 0;
  			
  			// Iterate trough every lane
  			while (++i<lanes.length){
  				
  				if (lanes[i] === changedLane) {
  					// Propagate new height down to the children
  					this.adjustHeight(this.getLanes(lanes[i]), undefined, lanes[i].bounds.height());
  					
  					lanes[i].bounds.set({x:30, y:height}, {x:lanes[i].bounds.width()+30, y:lanes[i].bounds.height()+height})
  									
  				} else if (!changedLane && propagateHeight) {
  					
  					var tempHeight = (lanes[i].bounds.height() * propagateHeight) / oldHeight;
  					// Propagate height
  					this.adjustHeight(this.getLanes(lanes[i]), undefined, tempHeight);
  					// Set height propotional to the propagated and old height
  					this.setDimensions(lanes[i], null, tempHeight);
  					this.setLanePosition(lanes[i], height);
  				} else {
  					// Get height from children
  					var tempHeight = this.adjustHeight(this.getLanes(lanes[i]), changedLane, propagateHeight);
  					if (!tempHeight) {
  						tempHeight = lanes[i].bounds.height();
  					}
  					this.setDimensions(lanes[i], null, tempHeight);
  					this.setLanePosition(lanes[i], height);
  				}
  				
  				height += lanes[i].bounds.height();
  			}
  			
  			return height;
  			
  		},
  		
  		
  		updateHeight: function(root){
  			
  			var lanes = this.getLanes(root);
  			
  			if (lanes.length == 0){
  				return root.bounds.height();
  			}
  			
  			var height = 0;
  			var i=-1;
  			while (++i < lanes.length) {
  				this.setLanePosition(lanes[i], height);
  				height += this.updateHeight(lanes[i]);
  			}
  			
  			this.setDimensions(root, null, height);
  			
  			return height;
  		},
  		
  		getOffset: function(lane, includePool, pool){
  			
  			var offset = {x:0,y:0};
  			
  			
  			/*var parent = lane; 
  			 while(parent) {
  			 				
  				
  				var offParent = this.hashedBounds[pool.id][parent.id] ||(includePool === true ? this.hashedPoolPositions[parent.id] : undefined);
  				if (offParent){
  					var ul = parent.bounds.upperLeft();
  					var ulo = offParent.upperLeft();
  					offset.x += ul.x-ulo.x;
  					offset.y += ul.y-ulo.y;
  				}
  				
  				if (parent.getStencil().id().endsWith("Pool")) {
  					break;
  				}
  				
  				parent = parent.parent;
  			}	*/
  			
  			var offset = lane.absoluteXY();
  			
  			var hashed = this.hashedBounds[pool.id][lane.id] ||(includePool === true ? this.hashedPoolPositions[lane.id] : undefined);
  			if (hashed) {
  				offset.x -= hashed.upperLeft().x; 	
  				offset.y -= hashed.upperLeft().y;		
  			} else {
  				return {x:0,y:0}
  			}		
  			return offset;
  		},
  		
  		getNextLane: function(shape){
  			while(shape && !shape.getStencil().id().endsWith("Lane")){
  				if (shape instanceof ORYX.Core.Canvas) {
  					return null;
  				}
  				shape = shape.parent;
  			}
  			return shape;
  		},
  		
  		getParentPool: function(shape){
  			while(shape && !shape.getStencil().id().endsWith("Pool")){
  				if (shape instanceof ORYX.Core.Canvas) {
  					return null;
  				}
  				shape = shape.parent;
  			}
  			return shape;
  		},
  		
  		updateDockers: function(lanes, pool){
  			
  			var absPool = pool.absoluteBounds(), movedShapes = [];
  			var oldPool = (this.hashedPoolPositions[pool.id]||absPool).clone();
  			
  			var i=-1, j=-1, k=-1, l=-1, docker;
  			var dockers = {};
  			
  			while (++i < lanes.length) {
  				
  				if (!this.hashedBounds[pool.id][lanes[i].id]) {
  					continue;
  				}
  				
  				var isScaled = lanes[i].isScaled;
  				delete lanes[i].isScaled;
  				var children = lanes[i].getChildNodes();
  				var absBounds = lanes[i].absoluteBounds();
  				var oldBounds = (this.hashedBounds[pool.id][lanes[i].id]||absBounds);
  				//oldBounds.moveBy((absBounds.upperLeft().x-lanes[i].bounds.upperLeft().x), (absBounds.upperLeft().y-lanes[i].bounds.upperLeft().y));
  				var offset = this.getOffset(lanes[i], true, pool);
  				var xOffsetDepth = 0;
  	
  				var depth = this.getDepth(lanes[i], pool);
  				if ( this.hashedLaneDepth[lanes[i].id] !== undefined &&  this.hashedLaneDepth[lanes[i].id] !== depth) {
  					xOffsetDepth = (this.hashedLaneDepth[lanes[i].id] - depth) * 30;
  					offset.x += xOffsetDepth;
  				}
  				
  				j=-1;
  				
  				while (++j < children.length) {
  					
  					if (xOffsetDepth && !children[j].getStencil().id().endsWith("Lane")) {
  						movedShapes.push({xOffset:xOffsetDepth, shape: children[j]});
  						children[j].bounds.moveBy(xOffsetDepth, 0);
  					}
  					
  					if (children[j].getStencil().id().endsWith("Subprocess")) {
  						this.moveChildDockers(children[j], offset);
  					}
  					
  					var edges = [].concat(children[j].getIncomingShapes())
  						.concat(children[j].getOutgoingShapes())
  						// Remove all edges which are included in the selection from the list
  						.findAll(function(r){ return r instanceof ORYX.Core.Edge })
  	
  					k=-1;
  					while (++k < edges.length) {			
  						
  						if (edges[k].getStencil().id().endsWith("MessageFlow")) {
  							this.layoutEdges(children[j], [edges[k]], offset);
  							continue;
  						}
  						
  						l=-1;
  						while (++l < edges[k].dockers.length) {
  							
  							docker = edges[k].dockers[l];
  							
  							if (docker.getDockedShape()||docker.isChanged){
  								continue;
  							}
  						
  						
  							pos = docker.bounds.center();
  							
  							// Check if the modified center included the new position
  							var isOverLane = oldBounds.isIncluded(pos);
  							// Check if the original center is over the pool
  							var isOutSidePool = !oldPool.isIncluded(pos);
  							var previousIsOverLane = l == 0 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l-1].bounds.center());
  							var nextIsOverLane = l == edges[k].dockers.length-1 ? isOverLane : oldBounds.isIncluded(edges[k].dockers[l+1].bounds.center());
  							var off = Object.clone(offset);
  							
  							// If the 
  							if (isScaled && isOverLane && this.isResized(lanes[i], this.hashedBounds[pool.id][lanes[i].id])){
  								var relY = (pos.y - absBounds.upperLeft().y + off.y);
  								off.y -= (relY - (relY * (absBounds.height()/oldBounds.height()))); 
  							}
  							
  							// Check if the previous dockers docked shape is from this lane
  							// Otherwise, check if the docker is over the lane OR is outside the lane 
  							// but the previous/next was over this lane
  							if (isOverLane){
  								dockers[docker.id] = {docker: docker, offset:off};
  							} 
  							/*else if (l == 1 && edges[k].dockers.length>2 && edges[k].dockers[l-1].isDocked()){
  								var dockedLane = this.getNextLane(edges[k].dockers[l-1].getDockedShape());
  								if (dockedLane != lanes[i])
  									continue;
  								dockers[docker.id] = {docker: docker, offset:offset};
  							}
  							// Check if the next dockers docked shape is from this lane
  							else if (l == edges[k].dockers.length-2 && edges[k].dockers.length>2 && edges[k].dockers[l+1].isDocked()){
  								var dockedLane = this.getNextLane(edges[k].dockers[l+1].getDockedShape());
  								if (dockedLane != lanes[i])
  									continue;
  								dockers[docker.id] = {docker: docker, offset:offset};
  							}
  													
  							else if (isOutSidePool) {
  								dockers[docker.id] = {docker: docker, offset:this.getOffset(lanes[i], true, pool)};
  							}*/
  							
  						
  						}
  					}
  							
  				}
  			}
  			
  			// Move the moved children 
  			var MoveChildCommand = ORYX.Core.Command.extend({
  				construct: function(state){
  					this.state = state;
  				},
  				execute: function(){
  					if (this.executed){
  						this.state.each(function(s){
  							s.shape.bounds.moveBy(s.xOffset, 0);
  						});
  					}
  					this.executed = true;
  				}, 
  				rollback: function(){
  					this.state.each(function(s){
  						s.shape.bounds.moveBy(-s.xOffset, 0);
  					});
  				}
  			})
  			
  			
  			// Set dockers
  			this.facade.executeCommands([new ORYX.Core.MoveDockersCommand(dockers), new MoveChildCommand(movedShapes)]);
  	
  		},
  		
  		moveBy: function(pos, offset){
  			pos.x += offset.x;
  			pos.y += offset.y;
  			return pos;
  		},
  		
  		getHashedBounds: function(shape){
  			return this.currentPool && this.hashedBounds[this.currentPool.id][shape.id] ? this.hashedBounds[this.currentPool.id][shape.id] : shape.absoluteBounds();
  		},
  		
  		/**
  		 * Returns a set on all child lanes for the given Shape. If recursive is TRUE, also indirect children will be returned (default is FALSE)
  		 * The set is sorted with first child the lowest y-coordinate and the last one the highest.
  		 * @param {ORYX.Core.Shape} shape
  		 * @param {boolean} recursive
  		 */
  		getLanes: function(shape, recursive){
  			var namespace = this.getNamespace();
  			
  			// Get all the child lanes
  			var lanes = shape.getChildNodes(recursive||false).findAll(function(node) { return (node.getStencil().id() === namespace + "Lane"); });
  			
  			// Sort all lanes by there y coordinate
  			lanes = lanes.sort(function(a, b){
  				
  						// Get y coordinates for upper left and lower right
  						var auy = Math.round(a.bounds.upperLeft().y);
  						var buy = Math.round(b.bounds.upperLeft().y);
  						var aly = Math.round(a.bounds.lowerRight().y);
  						var bly = Math.round(b.bounds.lowerRight().y);
  						
  						var ha	= this.getHashedBounds(a);
  						var hb	= this.getHashedBounds(b);
  						
  						// Get the old y coordinates
  						var oauy = Math.round(ha.upperLeft().y);
  						var obuy = Math.round(hb.upperLeft().y);
  						var oaly = Math.round(ha.lowerRight().y);
  						var obly = Math.round(hb.lowerRight().y);
  						
  						// If equal, than use the old one
  						if (auy == buy && aly == bly) {
  							auy = oauy; buy = obuy; aly = oaly; bly = obly;
  						}
  						
  						if (Math.round(a.bounds.height()-ha.height()) === 0 && Math.round(b.bounds.height()-hb.height()) === 0){
  							return auy < buy ? -1 : (auy > buy ? 1: 0);
  						}
  						
  						// Check if upper left and lower right is completely above/below
  						var above = auy < buy && aly < bly;
  						var below = auy > buy && aly > bly;
  						// Check if a is above b including the old values
  						var slightlyAboveBottom = auy < buy && aly >= bly && oaly < obly;
  						var slightlyAboveTop = auy >= buy && aly < bly && oauy < obuy;
  						// Check if a is below b including the old values
  						var slightlyBelowBottom = auy > buy && aly <= bly && oaly > obly;
  						var slightlyBelowTop = auy <= buy && aly > bly && oauy > obuy;
  						
  						// Return -1 if a is above b, 1 if b is above a, or 0 otherwise
  						return  (above || slightlyAboveBottom || slightlyAboveTop ? -1 : (below || slightlyBelowBottom || slightlyBelowTop ? 1 : 0))
  					}.bind(this));
  					
  			// Return lanes
  			return lanes;
  		},
  				
  		getNamespace: function() {
  			if(!this.namespace) {
  				var stencilsets = this.facade.getStencilSets();
  				if(stencilsets.keys()) {
  					this.namespace = stencilsets.keys()[0];
  				} else {
  					return undefined;
  				}
  			}
  			return this.namespace;
  		}
  	};
  	
  	var ResizeLanesCommand = ORYX.Core.Command.extend({
  	
  		construct: function(shape, parent, pool, plugin) {
  		
  			this.facade  = plugin.facade;
  			this.plugin  = plugin;
  			this.shape	 = shape;
  			this.changes;
  			
  			this.pool	= pool;
  			
  			this.parent	= parent;
  			
  			
  			this.shapeChildren = [];
  			
  			/*
  			 * The Bounds have to be stored 
  			 * separate because they would
  			 * otherwise also be influenced 
  			 */
  			this.shape.getChildShapes().each(function(childShape) {
  				this.shapeChildren.push({
  					shape: childShape,
  					bounds: {
  						a: {
  							x: childShape.bounds.a.x,
  							y: childShape.bounds.a.y
  						},
  						b: {
  							x: childShape.bounds.b.x,
  							y: childShape.bounds.b.y
  						}
  					}
  				});
  			}.bind(this));
  	
  			this.shapeUpperLeft = this.shape.bounds.upperLeft();
  			
  			// If there is no parent, 
  			// correct the abs position with the parents abs.
  			/*if (!this.shape.parent) { 
  				var pAbs = parent.absoluteXY();
  				this.shapeUpperLeft.x += pAbs.x;
  				this.shapeUpperLeft.y += pAbs.y;
  			}*/
  			this.parentHeight 	= this.parent.bounds.height(); 
  	
  		},
  		
  		getLeafLanes: function(lane){
  			var childLanes = this.plugin.getLanes(lane).map(function(child){
  				return this.getLeafLanes(child);
  			}.bind(this)).flatten();
  			return childLanes.length > 0 ? childLanes : [lane];
  		},
  		
  		findNewLane: function(){
  			
  			var lanes = this.plugin.getLanes(this.parent);
  	
  			var leafLanes = this.getLeafLanes(this.parent);
  			/*leafLanes = leafLanes.sort(function(a,b){
  				var aupl = a.absoluteXY().y;
  				var bupl = b.absoluteXY().y;
  				return aupl < bupl ? -1 : (aupl > bupl ? 1 : 0)
  			})*/
  			this.lane = leafLanes.find(function(l){ return l.bounds.upperLeft().y >= this.shapeUpperLeft.y }.bind(this)) || leafLanes.last();
  			this.laneUpperLeft = this.lane.bounds.upperLeft();	
  		},
  		
  		execute: function() {
  			
  			if(this.changes) {
  				this.executeAgain();
  				return;
  			}
  	
  			/* 
  			 * Rescue all ChildShapes of the deleted
  			 * Shape into the lane that takes its 
  			 * place 
  			 */
  			
  			if (!this.lane){
  				this.findNewLane();
  			}
  			
  			if(this.lane) {			
  				
  				var laUpL = this.laneUpperLeft;
  				var shUpL = this.shapeUpperLeft;
  				
  				var depthChange = this.plugin.getDepth(this.lane, this.parent)-1;
  							
  				this.changes = $H({});
  				
  				// Selected lane is BELOW the removed lane
  				if (laUpL.y >= shUpL.y) {				
  					this.lane.getChildShapes().each(function(childShape) {
  						
  						/*
  						 * Cache the changes for rollback
  						 */
  						if(!this.changes[childShape.getId()]) {
  							this.changes[childShape.getId()] = this.computeChanges(childShape, this.lane, this.lane, this.shape.bounds.height());
  						}
  						
  						childShape.bounds.moveBy(0, this.shape.bounds.height());
  					}.bind(this));
  					
  					this.plugin.hashChildShapes(this.lane);
  					
  					this.shapeChildren.each(function(shapeChild) {
  						shapeChild.shape.bounds.set(shapeChild.bounds);
  						shapeChild.shape.bounds.moveBy((shUpL.x-30)-(depthChange*30), 0);
  						
  						/*
  						 * Cache the changes for rollback
  						 */
  						if(!this.changes[shapeChild.shape.getId()]) {
  							this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0);
  						}
  						
  						this.lane.add(shapeChild.shape);
  						
  					}.bind(this));		
  				
  					this.lane.bounds.moveBy(0, shUpL.y-laUpL.y);
  				
  				// Selected lane is ABOVE the removed lane	
  				} else if(shUpL.y > laUpL.y){
  					
  					this.shapeChildren.each(function(shapeChild) {
  						shapeChild.shape.bounds.set(shapeChild.bounds);		
  						shapeChild.shape.bounds.moveBy((shUpL.x-30)-(depthChange*30), this.lane.bounds.height());			
  						
  						/*
  						 * Cache the changes for rollback
  						 */
  						if(!this.changes[shapeChild.shape.getId()]) {
  							this.changes[shapeChild.shape.getId()] = this.computeChanges(shapeChild.shape, this.shape, this.lane, 0);
  						}
  						
  						this.lane.add(shapeChild.shape);
  						
  					}.bind(this));
  				}
  				
  				
  
  				
  			}
  					
  			/*
  			 * Adjust the height of the lanes
  			 */
  			// Get the height values
  			var oldHeight	= this.lane.bounds.height();				
  			var newHeight	= this.lane.length === 1 ? this.parentHeight : this.lane.bounds.height() + this.shape.bounds.height();
  	
  			// Set height
  			this.setHeight(newHeight, oldHeight, this.parent, this.parentHeight, true);
  			
  			// Cache all sibling lanes
  			//this.changes[this.shape.getId()] = this.computeChanges(this.shape, this.parent, this.parent, 0);
  			this.plugin.getLanes(this.parent).each(function(childLane){
  				if(!this.changes[childLane.getId()] && childLane !== this.lane && childLane !== this.shape) {
  					this.changes[childLane.getId()] = this.computeChanges(childLane, this.parent, this.parent, 0);
  				}
  			}.bind(this))
  				
  			// Update
  			this.update();
  		},
  		
  		setHeight: function(newHeight, oldHeight, parent, parentHeight, store){
  			
  			// Set heigh of the lane
  			this.plugin.setDimensions(this.lane, this.lane.bounds.width(), newHeight);
  			this.plugin.hashedBounds[this.pool.id][this.lane.id] = this.lane.absoluteBounds();
  			
  			// Adjust child lanes
  			this.plugin.adjustHeight(this.plugin.getLanes(parent), this.lane);
  			
  			if (store === true){
  				// Store changes
  				this.changes[this.shape.getId()] = this.computeChanges(this.shape, parent, parent, 0, oldHeight, newHeight);	
  			}
  			
  			// Set parents height
  			this.plugin.setDimensions(parent, parent.bounds.width(), parentHeight);
  			
  			if (parent !== this.pool){
  				this.plugin.setDimensions(this.pool, this.pool.bounds.width(), this.pool.bounds.height() + (newHeight-oldHeight));
  			}
  		},
  		
  		update: function(){
  			
  			// Hack to prevent the updating of the dockers
  			this.plugin.hashedBounds[this.pool.id]["REMOVED"] = true;
  			// Update
  			//this.facade.getCanvas().update();
  		},
  		
  		rollback: function() {
  			
  			var laUpL = this.laneUpperLeft;
  			var shUpL = this.shapeUpperLeft;
  				
  			this.changes.each(function(pair) {
  				
  				var parent 	  		= pair.value.oldParent;
  				var shape  	  		= pair.value.shape;
  				var parentHeight 	= pair.value.parentHeight;
  				var oldHeight 		= pair.value.oldHeight;
  				var newHeight 		= pair.value.newHeight;
  				
  				// Move siblings
  				if (shape.getStencil().id().endsWith("Lane")){
  					shape.bounds.moveTo(pair.value.oldPosition);	
  				}
  				
  				// If lane
  				if(oldHeight) {					
  					this.setHeight(oldHeight, newHeight, parent, parent.bounds.height() + (oldHeight - newHeight));
  					if (laUpL.y >= shUpL.y) {
  						this.lane.bounds.moveBy(0, this.shape.bounds.height()-1);
  					}
  				} else {
  					parent.add(shape);
  					shape.bounds.moveTo(pair.value.oldPosition);
  					
  				}
  
  				
  			}.bind(this));
  			
  			// Update
  			//this.update();
  			
  		},
  		
  		executeAgain: function() {
  			
  			this.changes.each(function(pair) {
  				var parent 	  = pair.value.newParent;
  				var shape  	  = pair.value.shape;
  				var newHeight = pair.value.newHeight;
  				var oldHeight = pair.value.oldHeight;
  				
  				// If lane
  				if(newHeight) {
  					var laUpL = this.laneUpperLeft.y;
  					var shUpL = this.shapeUpperLeft.y;
  				
  					if (laUpL >= shUpL) {
  						this.lane.bounds.moveBy(0, shUpL - laUpL);
  					}
  					this.setHeight(newHeight, oldHeight, parent, parent.bounds.height() + (newHeight-oldHeight));
  				} else {
  					parent.add(shape);
  					shape.bounds.moveTo(pair.value.newPosition);
  				}
  				
  			}.bind(this));
  			
  			// Update
  			this.update();
  		},
  		
  		computeChanges: function(shape, oldParent, parent, yOffset, oldHeight, newHeight) {
  			
  			oldParent = this.changes[shape.getId()] ? this.changes[shape.getId()].oldParent : oldParent;
  			var oldPosition = this.changes[shape.getId()] ? this.changes[shape.getId()].oldPosition : shape.bounds.upperLeft();
  			
  			var sUl = shape.bounds.upperLeft();
  			
  			var pos = {x: sUl.x, y: sUl.y + yOffset};
  			
  			var changes = {
  				shape		: shape,
  				parentHeight: oldParent.bounds.height(),
  				oldParent	: oldParent,
  				oldPosition	: oldPosition,
  				oldHeight	: oldHeight,
  				newParent	: parent,
  				newPosition : pos,
  				newHeight	: newHeight
  			};
  				
  			return changes;
  		}
  		
  	});
  	
  		
  	ORYX.Plugins.BPMN2_0 = ORYX.Plugins.AbstractPlugin.extend(ORYX.Plugins.BPMN2_0);
  	
  }()	
  