text: 
  /**
   * 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) {
  			//get the url of the stencil set json file
  			new Ajax.Request(source, {
  	            asynchronous: false,
  	            method: 'get',
  	            onSuccess: this._getJSONURL.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.get(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.set(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.set(oStencil.id(), oStencil);
  						this._availableStencils.set(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) {
  						this._availableStencils.unset(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.get(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);            
  					this._stencils.unset(oStencil.id()); // maybe not ??
  					this._availableStencils.unset(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.set(sId, this._stencils.get(sId));
  				}.bind(this));
  			}
  		}
  		this._extensions.unset(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 = "";
          }
      },
  	
  	_getJSONURL: function(response) {
  		this._baseUrl = response.responseText.substring(0, response.responseText.lastIndexOf("/") + 1);
  		this._source = response.responseText;
  		new Ajax.Request(response.responseText, {
              asynchronous: false,
              method: 'get',
              onSuccess: this._init.bind(this),
              onFailure: this._cancelInit.bind(this)
          });
  	},
      
      /**
       * 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.set(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.set(oStencil.id(), oStencil);
  			this._availableStencils.set(oStencil.id(), oStencil);
              
          }).bind(this));
      },
      
      _cancelInit: function(response){
          this.errornous = true;
      },
      
      toString: function(){
          return "StencilSet " + this.title() + " (" + this.namespace() + ")";
      }
  });
  