Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Azure/DAL.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Azure/DAL.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Azure/DAL.cs	(revision 9305)
@@ -101,10 +101,13 @@
     }
 
-    public ExperimentEntity(string user, string experimentName, string experimentUrl) {
+    public ExperimentEntity(string user, string experimentName, string experimentId, string experimentUrl) {
       PartitionKey = "ScenarioPartition";
       RowKey = user + "_" + experimentName;      
       User = user;
+      ExperimentId = experimentId;
       ExperimentJsonUrl = experimentUrl;
     }
+
+    public string ExperimentId { get; set; }
 
     public string User { get; set; }
@@ -122,20 +125,38 @@
         return false;
 
-      // TODO: Save the whole experiment, not just the algorithm names!!!
       CloudBlobContainer container = BlobClient.GetContainerReference(AzureConstants.EXPERIMENT_BLOB_CONTAINER);
       container.CreateIfNotExist();
       // For now we store it as JSON element in the blob store
-      // TODO: Make sure, that all required properties are part of the experiment!!
-      var experimentJson = AlgorithmConverter.ConvertJson(experiment);
+      var experimentJson = AlgorithmConverter.ConvertExperimentToJson(experiment);
       Guid experimentJsonGuid = Guid.NewGuid();
       var experimentJsonId = experiment.Name + "_" + experimentJsonGuid.ToString();
+      experimentJson["nodeId"] = experimentJsonId.ToString();
       var blob = container.GetBlobReference(experimentJsonId);      
-      blob.UploadText(experimentJson);
+      blob.UploadText(experimentJson.ToString());
+      experiment.Id = experimentJsonId;
 
       TableServiceContext serviceContext = TableClient.GetDataServiceContext();      
       TableClient.CreateTableIfNotExist(AzureConstants.EXPERIMENT_TABLE);
-      var entity = new ExperimentEntity(username, experiment.Name, blob.Uri.ToString());
+      var entity = new ExperimentEntity(username, experiment.Name, experiment.Id, blob.Uri.ToString());
       serviceContext.AddObject(AzureConstants.EXPERIMENT_TABLE, entity);
       serviceContext.SaveChangesWithRetries();
+      return true;
+    }
+
+    public bool Update(string username, Experiment experiment) {
+      TableServiceContext serviceContext = TableClient.GetDataServiceContext();
+      TableClient.CreateTableIfNotExist(AzureConstants.EXPERIMENT_TABLE);
+      var entity = (from e in serviceContext.CreateQuery<ExperimentEntity>(AzureConstants.EXPERIMENT_TABLE)
+                    where e.ExperimentId == experiment.Id
+                    select e).FirstOrDefault();
+      if (entity == null) {
+        return false;
+      }
+
+      CloudBlobContainer container = BlobClient.GetContainerReference(AzureConstants.EXPERIMENT_BLOB_CONTAINER);
+      container.CreateIfNotExist();
+      var blob = container.GetBlobReference(entity.ExperimentJsonUrl);
+      var experimentJson = AlgorithmConverter.ConvertExperimentToJson(experiment).ToString();
+      blob.UploadText(experimentJson);
       return true;
     }
@@ -177,5 +198,5 @@
       container.CreateIfNotExist();
       var blob = container.GetBlobReference(entity.ExperimentJsonUrl);
-      return AlgorithmConverter.ConvertExperimentSimple(blob.DownloadText());
+      return AlgorithmConverter.ConvertJsonToExperiment(blob.DownloadText());
     }
 
@@ -199,5 +220,5 @@
       foreach (var entity in entites) {
         var blob = container.GetBlobReference(entity.ExperimentJsonUrl);
-        experiments.Add(AlgorithmConverter.ConvertExperimentSimple(blob.DownloadText()));
+        experiments.Add(AlgorithmConverter.ConvertJsonToExperiment(blob.DownloadText()));
       }
       return experiments;
@@ -218,7 +239,28 @@
       container.CreateIfNotExist();
       var blob = container.GetBlobReference(entity.ExperimentJsonUrl);
-      var exp = AlgorithmConverter.ConvertExperimentSimple(blob.DownloadText());
+      var exp = AlgorithmConverter.ConvertJsonToExperiment(blob.DownloadText());
       return exp;
     }
+
+
+    public Experiment GetExperimentById(User user, string nodeId) {
+      TableServiceContext serviceContext = TableClient.GetDataServiceContext();
+      TableClient.CreateTableIfNotExist(AzureConstants.EXPERIMENT_TABLE);
+      var entity = (from e in serviceContext.CreateQuery<ExperimentEntity>(AzureConstants.EXPERIMENT_TABLE)
+                    where e.ExperimentId == nodeId
+                    select e).FirstOrDefault();
+      if (entity == null || entity.ExperimentJsonUrl == null) {
+        return null;
+      }
+
+      if (entity.User != user.Username)
+        return null;
+
+      CloudBlobContainer container = BlobClient.GetContainerReference(AzureConstants.EXPERIMENT_BLOB_CONTAINER);
+      container.CreateIfNotExist();
+      var blob = container.GetBlobReference(entity.ExperimentJsonUrl);
+      return AlgorithmConverter.ConvertJsonToExperiment(blob.DownloadText());
+    }
+
   }
 
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HL/HiveScenarioManager.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HL/HiveScenarioManager.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HL/HiveScenarioManager.cs	(revision 9305)
@@ -97,6 +97,6 @@
         foreach (var child in entry.Children) {
           // This is a template experiment; 
-          if (child.Name == null) {
-            var parent = new HeuristicLab.Optimization.Experiment(child.Name);
+          if (child.IsExperiment) {
+            var parent = new HeuristicLab.Optimization.Experiment();
             entry.Parent.Optimizers.Add(parent);
             stack.Push(new StackEntry() { Parent = parent, Children = child.ChildAlgorithms });          
@@ -104,17 +104,10 @@
           // This entity is mapable
           else {           
-            IScenarioMapper mapper = GetMapper(child.Name);
-            if (mapper == null) { // TODO: We should really be able to difference Experiment/Algorithm types; this is a workaround
-              var parent = new HeuristicLab.Optimization.Experiment(child.Name);
-              entry.Parent.Optimizers.Add(parent);
-              stack.Push(new StackEntry() { Parent = parent, Children = child.ChildAlgorithms });          
-            }
-            else {
-              var optScen = new OptimizationScenario() { Id = child.Name };
-              optScen.Algorithm.Add(child);
-              IAlgorithm algo;
-              mapper.MapScenario(optScen, out algo);
-              entry.Parent.Optimizers.Add(algo);
-            }
+            IScenarioMapper mapper = GetMapper(child.Id);
+            var optScen = new OptimizationScenario() { Id = child.Id };
+            optScen.Algorithm.Add(child);
+            IAlgorithm algo;
+            mapper.MapScenario(optScen, out algo);
+            entry.Parent.Optimizers.Add(algo);
           }  
         }
@@ -166,5 +159,6 @@
     private IScenarioMapper GetMapper(string scenarioId) {
       var id = scenarioId;
-      if (!mappers.ContainsKey(id)) {
+      IScenarioMapper mapper = null;      
+      if (!mappers.TryGetValue(id, out mapper)) {
         lock (lockable) {
           if (mappers.ContainsKey(id))
@@ -173,5 +167,5 @@
           var mapperString = GetMapperFromBlobStore(id);
           if (mapperString == null) return null;
-          var mapper = CompileMapper(mapperString);
+          mapper = CompileMapper(mapperString);
 
           mappers[id] = mapper;
@@ -179,5 +173,5 @@
         } // lock        
        } // if
-      return null;
+      return mapper;
      }  
 
@@ -471,9 +465,15 @@
     }
 
-    public bool SaveExperiment(User user, Model.Experiment experiment) {
-      return dal.ExperimentDao.Add(user.Username, experiment);
-    }
-
-    public IEnumerable<string> GetExperiments(User user) {
+    public string SaveExperiment(User user, Model.Experiment experiment) {
+      if (experiment.Id != null) {
+        dal.ExperimentDao.Update(user.Username, experiment);
+      }
+      else {
+        dal.ExperimentDao.Add(user.Username, experiment);
+      }
+      return experiment.Id;
+    }
+
+    public IEnumerable<string> GetExperimentNames(User user) {
       return (from exp in dal.ExperimentDao.GetExperiments(user.Username)
               select exp.Name);
@@ -564,6 +564,11 @@
     }
 
-
-
+    public IEnumerable<Model.Experiment> GetExperiments(User user) {
+      return dal.ExperimentDao.GetExperiments(user.Username);
+    }
+
+    public Model.Experiment GetExperimentById(User user, string nodeId) {
+      return dal.ExperimentDao.GetExperimentById(user, nodeId);
+    }
   }
 }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HeuristicLab.Services.Optimization.ControllerService.csproj
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HeuristicLab.Services.Optimization.ControllerService.csproj	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/HeuristicLab.Services.Optimization.ControllerService.csproj	(revision 9305)
@@ -189,5 +189,7 @@
   </ItemGroup>
   <ItemGroup>
-    <None Include="packages.config" />
+    <None Include="packages.config">
+      <SubType>Designer</SubType>
+    </None>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/DAL.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/DAL.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/DAL.cs	(revision 9305)
@@ -20,8 +20,11 @@
   public interface IExperimentDao {
     bool Add(string username, Experiment experiment);
+    bool Update(string username, Experiment experiment);
     bool DeleteByName(string username, string experiment);
     Experiment FindByName(string username, string experiment);
     IEnumerable<Experiment> GetExperiments(string user);
     Experiment GetExperimentByName(string username, string scenario);
+
+    Experiment GetExperimentById(User user, string nodeId);
   }
 
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IControllerService.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IControllerService.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IControllerService.cs	(revision 9305)
@@ -44,8 +44,11 @@
 
     [OperationContract]
-    bool SaveExperiment(User user, Experiment experiment);
+    string SaveExperiment(User user, Experiment experiment);
 
     [OperationContract]
-    IEnumerable<string> GetExperiments(User user);
+    IEnumerable<string> GetExperimentNames(User user);
+
+    [OperationContract]
+    IEnumerable<Experiment> GetExperiments(User user);
 
     [OperationContract]
@@ -60,4 +63,7 @@
     [OperationContract]
     Experiment GetExperimentByName(User user, string scenario);
+
+    [OperationContract]
+    Experiment GetExperimentById(User u, string nodeId);
   }
 }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IScenarioManager.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IScenarioManager.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/IScenarioManager.cs	(revision 9305)
@@ -15,6 +15,10 @@
     bool DeleteScenario(User user, string scenarioName);
 
-    bool SaveExperiment(User user, Experiment experiment);
-    IEnumerable<string> GetExperiments(User user);
+    string SaveExperiment(User user, Experiment experiment);
+    IEnumerable<string> GetExperimentNames(User user);
+    IEnumerable<Model.Experiment> GetExperiments(User user);
+    Experiment GetExperimentByName(User user, string scenario);
+    Experiment GetExperimentById(User user, string nodeId);
+
     bool DeleteExperiment(User user, string experiment);
 
@@ -22,5 +26,5 @@
     Job GetTasks(User u, string jobId);
 
-    Experiment GetExperimentByName(User user, string scenario);
+    
     bool DispatchExperiment(User user, Experiment exp, JobExecutionDetails details);
   }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/Model/ControllerModel.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/Model/ControllerModel.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Interfaces/Model/ControllerModel.cs	(revision 9305)
@@ -277,4 +277,7 @@
 
     [DataMember]
+    public string Id { get; set; }
+
+    [DataMember]
     public AlgorithmParameters Parameters { get; set; }
 
@@ -293,4 +296,7 @@
     [DataMember]
     public string Name { get; set; }
+
+    [DataMember]
+    public bool IsExperiment { get; set; }
   }
 
@@ -315,4 +321,7 @@
     [DataMember]
     public IList<Algorithm> Algorithm { get; set; }
+
+    [DataMember]
+    public string Id { get; set; }
 
     public Experiment() {
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Mockup/MockupScenarioManager.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Mockup/MockupScenarioManager.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Mockup/MockupScenarioManager.cs	(revision 9305)
@@ -241,10 +241,10 @@
 
 
-    public bool SaveExperiment(User user, Model.Experiment experiment) {
-      throw new NotImplementedException();
-    }
-
-
-    public IEnumerable<string> GetExperiments(User user) {
+    public string SaveExperiment(User user, Model.Experiment experiment) {
+      throw new NotImplementedException();
+    }
+
+
+    public IEnumerable<string> GetExperimentNames(User user) {
       throw new NotImplementedException();
     }
@@ -271,4 +271,17 @@
 
     public bool DispatchExperiment(User user, Model.Experiment exp, JobExecutionDetails details) {
+      throw new NotImplementedException();
+    }
+
+
+    string IScenarioManager.SaveExperiment(User user, Model.Experiment experiment) {
+      throw new NotImplementedException();
+    }
+
+    public IEnumerable<Model.Experiment> GetExperiments(User user) {
+      throw new NotImplementedException();
+    }
+
+    public Model.Experiment GetExperimentById(User user, string nodeId) {
       throw new NotImplementedException();
     }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Parsers/AlgorithmConverter.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Parsers/AlgorithmConverter.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/Parsers/AlgorithmConverter.cs	(revision 9305)
@@ -9,76 +9,23 @@
 namespace HeuristicLab.Services.Optimization.ControllerService.Parsers {
   public static class AlgorithmConverter {
-
-    public static Algorithm Convert(string json) {
-      var o = JObject.Parse(json);
-      return ParseAlgorithm(o);      
-    }
-
-    private static Algorithm ParseAlgorithm(JToken obj) {
-      var algorithm = new Algorithm();
-
-      foreach (JProperty param in obj["Algorithm Parameters"]) {
-        Parameter parameter = CreateParameter(param);
-        algorithm.Parameters.Items.Add(parameter);
-      }
-
-      var problemParams = obj["Problem Parameters"];
-      if (problemParams != null) {
-        algorithm.Problem = new Problem();
-        foreach (JProperty param in problemParams) {
-          Parameter parameter = CreateParameter(param);
-          algorithm.Problem.Parameters.Items.Add(parameter);
-        }
-      }
-      return algorithm;
-    }
-
-    public static Algorithm ConvertAppend(string json, Algorithm algorithm) {
-      var o = JObject.Parse(json);
-      foreach (JProperty param in o["Algorithm Parameters"]) {
-        Parameter parameter = CreateParameter(param);
-        var oldParameter = (from p in algorithm.Parameters.Items where p.Value.Name == parameter.Value.Name select p).FirstOrDefault();
-        if (oldParameter != null) {
-          oldParameter.Value = parameter.Value;
-        }
-        else {
-          algorithm.Parameters.Items.Add(parameter);
-        }
-      }
-
-      var problemParams = o["Problem Parameters"];
-      if (problemParams != null) {
-        algorithm.Problem = new Problem();
-        foreach (JProperty param in problemParams) {
-          Parameter parameter = CreateParameter(param);          
-          var oldParameter = (from p in algorithm.Problem.Parameters.Items where p.Value.Name == parameter.Value.Name select p).FirstOrDefault();
-          if (oldParameter != null) {
-            oldParameter.Value = parameter.Value;
-          }
-          else {
-            algorithm.Problem.Parameters.Items.Add(parameter);
-          }
-        }
-      }
-      return algorithm;
-    }
-
-    private static Parameter CreateParameter(JProperty property) {
-      JToken token = property.Value;
-      switch (token.Type) {
+    #region private methods
+    private static Parameter CreateParameter(JObject property) {
+      var name = (string)property["Name"];
+      var value = property["Value"];
+      switch (value.Type) {
         case JTokenType.Integer:
         case JTokenType.Float:
-          return new Parameter() { Type = ParameterType.Decimal, Value = new DecimalValue() { Name = property.Name, Value = (double)property.Value } };
+          return new Parameter() { Type = ParameterType.Decimal, Value = new DecimalValue() { Name = name, Value = (double)value } };
         case JTokenType.Boolean:
-          return new Parameter() { Type = ParameterType.Boolean, Value = new BoolValue() { Name = property.Name, Value = (bool)property.Value } };
+          return new Parameter() { Type = ParameterType.Boolean, Value = new BoolValue() { Name = name, Value = (bool)value } };
         case JTokenType.String:
-          return new Parameter() { Type = ParameterType.Type, Value = new TypeValue() { Name = property.Name, Value = (string)property.Value } };                  
+          return new Parameter() { Type = ParameterType.Type, Value = new TypeValue() { Name = name, Value = (string)value, Options = (from e in property["Options"] select (string)e).ToArray() } };
         case JTokenType.Array:
-          var arr = (JArray)token;
+          var arr = (JArray)value;
           // its a matrix
           if (arr[0].Type == JTokenType.Array) {
-            return CreateMatrixParameter(property.Name, arr);
+            return CreateMatrixParameter(name, arr);
           }
-          return CreateVectorParameter(property.Name, arr);
+          return CreateVectorParameter(name, arr);
         default:
           throw new Exception("Unhandled datatype: " + property.Type);
@@ -89,5 +36,5 @@
       double[][] entries = new double[arr.Count][];
       for (int i = 0; i < entries.Length; i++) {
-        entries[i] = (from d in arr[i] select (double)d).ToArray<double>();  
+        entries[i] = (from d in arr[i] select (double)d).ToArray<double>();
       }
       return new Parameter { Type = ParameterType.DecimalMatrix, Value = new DecimalMatrix() { Name = name, Value = entries } };
@@ -99,9 +46,4 @@
     }
 
-    public static string ConvertJson(Algorithm algorithm) {
-      return JsonConvert.SerializeObject(algorithm);
-    }
-
-    
     private class StackEntry {
       public Algorithm Parent { get; set; }
@@ -109,39 +51,174 @@
     }
 
-    public static Experiment ConvertExperimentSimple(string json) {
-      return JsonConvert.DeserializeObject<Experiment>(json, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto });
-    }
-
-    public static string ConvertJson(Experiment experiment) {
-      return JsonConvert.SerializeObject(experiment, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
-    }
-
-    public static Experiment ConvertExperiment(string json) {
+    private static JArray ConvertParametersToJson(IList<Parameter> parameters) {
+      var array = new JArray();
+      foreach (var param in parameters) {
+        array.Add(JObject.FromObject(param.Value));
+      }
+      return array;
+    }
+
+    private struct Entry {
+      public JObject TargetAlgorithm { get; set; }
+
+      public Algorithm SourceAlgorithm { get; set; }
+    }
+
+    private static Algorithm ParseAlgorithm(JToken jsonAlgorithm) {
+      var algorithm = new Algorithm();
+
+      foreach (JObject param in jsonAlgorithm["AlgorithmParameters"]) {
+        Parameter parameter = CreateParameter(param);
+        algorithm.Parameters.Items.Add(parameter);
+      }
+
+      var problemParams = jsonAlgorithm["ProblemParameters"];
+      if (problemParams != null) {
+        algorithm.Problem = new Problem();
+        foreach (JObject param in problemParams) {
+          Parameter parameter = CreateParameter(param);
+          algorithm.Problem.Parameters.Items.Add(parameter);
+        }
+      }
+      return algorithm;
+    }
+    #endregion
+
+
+    #region public methods
+
+
+    public static JObject ConvertExperimentToJson(Experiment experiment) {
+      var exp = new JObject();
+      exp["title"] = experiment.Name;
+      exp["children"] = new JArray();
+      exp["nodeId"] = experiment.Id;
+      exp["isExperiment"] = true;
+
+      var stack = new Stack<Entry>();
+      foreach (var algo in experiment.Algorithm)
+        stack.Push(new Entry() { TargetAlgorithm = exp, SourceAlgorithm = algo });
+
+      while (stack.Count > 0) {
+        var entry = stack.Pop();
+        var algorithm = ConvertAlgorithmToJson(entry.SourceAlgorithm);
+        (entry.TargetAlgorithm["children"] as JArray).Add(algorithm);
+
+        if (entry.SourceAlgorithm.ChildAlgorithms.Count > 0) {
+          // push children
+          foreach (var child in entry.SourceAlgorithm.ChildAlgorithms) {
+            stack.Push(new Entry() { TargetAlgorithm = algorithm, SourceAlgorithm = child });
+          }
+        }
+
+      }
+      return exp;
+    }
+
+    public static JArray ConvertExperimentsToJson(IEnumerable<Experiment> experiments) {
+      var jarray = new JArray();
+      foreach (var exp in experiments)
+        jarray.Add(ConvertExperimentToJson(exp));
+      return jarray;
+    }
+
+    public static JArray ConvertAlgorithmsToJson(IEnumerable<Algorithm> algorithms) {
+      var jarray = new JArray();
+      foreach (var algo in algorithms)
+        jarray.Add(ConvertAlgorithmToJson(algo));
+      return jarray;
+    }
+
+
+    public static JArray ConvertScenariosToJson(IEnumerable<OptimizationScenario> scenarios) {
+      var jarray = new JArray();
+      foreach (var scen in scenarios)
+        jarray.Add(ConvertScenarioToJson(scen));
+      return jarray;
+    }
+
+    public static JObject ConvertScenarioToJson(OptimizationScenario scenario) {
+      var exp = new JObject();
+      exp["title"] = scenario.Id;
+      exp["children"] = new JArray();
+      exp["nodeId"] = scenario.Id;
+      exp["isExperiment"] = false;
+
+      var stack = new Stack<Entry>();
+      var baseAlgorithm = ConvertAlgorithmToJson(scenario.FirstAlgorithm);
+      exp["data"] = baseAlgorithm["data"];
+      if (scenario.FirstAlgorithm.ChildAlgorithms.Count > 0) {
+        foreach (var child in scenario.FirstAlgorithm.ChildAlgorithms)
+          stack.Push(new Entry() { TargetAlgorithm = exp, SourceAlgorithm = child});
+      }
+
+      while (stack.Count > 0) {
+        var entry = stack.Pop();
+        var algorithm = ConvertAlgorithmToJson(entry.SourceAlgorithm);
+        (entry.TargetAlgorithm["children"] as JArray).Add(algorithm);
+
+        if (entry.SourceAlgorithm.ChildAlgorithms.Count > 0) {
+          // push children
+          foreach (var child in entry.SourceAlgorithm.ChildAlgorithms) {
+            stack.Push(new Entry() { TargetAlgorithm = algorithm, SourceAlgorithm = child });
+          }
+        }
+
+      }
+      return exp;
+    }
+
+    public static JObject ConvertAlgorithmToJson(Algorithm algorithm) {
+      var jalgo = new JObject();
+      jalgo["title"] = algorithm.Name;
+      jalgo["nodeId"] = algorithm.Id;
+      var jalgoData = new JObject();
+      jalgo["data"] = jalgoData;
+      jalgo["isExperiment"] = algorithm.IsExperiment;
+      jalgoData["AlgorithmParameters"] = ConvertParametersToJson(algorithm.Parameters.Items);
+      if (algorithm.Problem != null && algorithm.Problem.Parameters != null)
+        jalgoData["ProblemParameters"] = ConvertParametersToJson(algorithm.Problem.Parameters.Items);
+
+      jalgo["children"] = new JArray();
+      return jalgo;
+    }
+
+    public static Experiment ConvertJsonToExperiment(string json) {
       var experiment = new Experiment();
       var jsonExperiment = JObject.Parse(json);
-      experiment.Name = (string)jsonExperiment["name"];
+      experiment.Name = (string)jsonExperiment["title"];
+      experiment.Id = (string)jsonExperiment["nodeId"];      
       var stack = new Stack<StackEntry>();
       var root = new Algorithm();
 
-      if ((bool)jsonExperiment["run"]) {
+      if (jsonExperiment["run"] != null && (bool)jsonExperiment["run"]) {
         experiment.JobDetails = new JobExecutionDetails() {
-           Group = (string)jsonExperiment["group"],
-           Repititions = (int)jsonExperiment["repititions"]
+          Group = (string)jsonExperiment["group"],
+          Repititions = (int)jsonExperiment["repititions"]
         };
       }
 
-      foreach (var algo in jsonExperiment["algorithms"]["children"]) {
-        stack.Push(new StackEntry(){ Parent = root, Child = algo });
-      }
+      // ignore experiment node, skip to its children
+      if (jsonExperiment["experiment"] != null)
+        foreach (var algo in jsonExperiment["experiment"]["children"]) {
+          stack.Push(new StackEntry() { Parent = root, Child = algo });
+        }
+
+      if (jsonExperiment["children"] != null)
+        foreach (var algo in jsonExperiment["children"]) {
+          stack.Push(new StackEntry() { Parent = root, Child = algo });
+        }
 
       while (stack.Count > 0) {
         var entry = stack.Pop();
         var data = entry.Child["data"];
-        var currentAlgo = data == null ? new Algorithm() : ParseAlgorithm(entry.Child["data"]);
-        currentAlgo.Name = (string)entry.Child["name"];
+        var currentAlgo = data == null || !data.HasValues ? new Algorithm() : ParseAlgorithm(entry.Child["data"]);
+        currentAlgo.Name = (string)entry.Child["title"];
+        currentAlgo.Id = (string)entry.Child["nodeId"];
+        currentAlgo.IsExperiment = (bool)entry.Child["isExperiment"];
         entry.Parent.ChildAlgorithms.Add(currentAlgo);
         // push children on stack (inverse order to preserve ordering)
         var cnt = entry.Child["children"].Count();
-        for (var i=0; i < cnt; i++) {
+        for (var i = 0; i < cnt; i++) {
           stack.Push(new StackEntry() { Parent = currentAlgo, Child = entry.Child["children"][cnt - 1 - i] });
         }
@@ -154,4 +231,5 @@
       return experiment;
     }
+    #endregion
   }
 }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/PlaceholderControllerService.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/PlaceholderControllerService.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/PlaceholderControllerService.cs	(revision 9305)
@@ -121,5 +121,5 @@
 
 
-    public bool SaveExperiment(User user, Experiment experiment) {      
+    public string SaveExperiment(User user, Experiment experiment) {      
       // make sure all algorithms store their choices aswell
       var scenarioMap = new Dictionary<string, OptimizationScenario>();
@@ -146,15 +146,15 @@
 
       // finally store the experiment
-      var success = hiveManager.SaveExperiment(user, experiment);
+      var id = hiveManager.SaveExperiment(user, experiment);
       // if it has been stored, and jobdetails are present, run the job!
-      if (success && experiment.JobDetails != null) {
-        return ScheduleExperiment(user, experiment.Name, experiment.JobDetails);
+      if (id != null && experiment.JobDetails != null) {
+        ScheduleExperiment(user, experiment.Id, experiment.JobDetails);
       }
-      return success;
+      return id;
     }
 
 
-    public IEnumerable<string> GetExperiments(User user) {
-      return hiveManager.GetExperiments(user);
+    public IEnumerable<string> GetExperimentNames(User user) {
+      return hiveManager.GetExperimentNames(user);
     }
 
@@ -179,7 +179,16 @@
 
 
-    public bool ScheduleExperiment(User user, string experiment, JobExecutionDetails details) {
-      var exp = hiveManager.GetExperimentByName(user, experiment);
+    public bool ScheduleExperiment(User user, string experimentId, JobExecutionDetails details) {
+      var exp = hiveManager.GetExperimentById(user, experimentId);
       return hiveManager.DispatchExperiment(user, exp, details);
+    }
+
+
+    public IEnumerable<Experiment> GetExperiments(User user) {
+      return hiveManager.GetExperiments(user);
+    }
+
+    public Experiment GetExperimentById(User u, string nodeId) {
+      return hiveManager.GetExperimentById(u, nodeId);
     }
   }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Controller/packages.config
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Controller/packages.config	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Controller/packages.config	(revision 9305)
@@ -1,4 +1,4 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
-<packages>
+<packages>  
   <package id="Newtonsoft.Json" version="4.5.11" />
 </packages>
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Binders/AlgorithmJsonAttribute.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Binders/AlgorithmJsonAttribute.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Binders/AlgorithmJsonAttribute.cs	(revision 9305)
@@ -13,38 +13,4 @@
 namespace Mvc3TestApplication.Binders
 {
-  public class AlgorithmJsonAttribute : CustomModelBinderAttribute
-  {
-    public override IModelBinder GetBinder()
-    {
-      return new AlgorithmJsonBinder();
-    }
-  }
-
-  public class AlgorithmJsonBinder : IModelBinder
-  {
-    public AlgorithmJsonBinder()
-    {
-    }
-
-    public object BindModel(ControllerContext controllerCtx, ModelBindingContext bindingCtx)
-    {
-      var contentType = controllerCtx.HttpContext.Request.ContentType;
-      if (!contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
-        return null;
-
-      string body;
-      using (var stream = controllerCtx.HttpContext.Request.InputStream)
-      {
-        stream.Seek(0, System.IO.SeekOrigin.Begin);
-        using (var reader = new StreamReader(stream))
-          body = reader.ReadToEnd();
-      }
-      if (string.IsNullOrEmpty(body)) return null;
-      return AlgorithmConverter.Convert(body);
-    }
-
-    
-  }
-
   public class ExperimentJsonAttribute : CustomModelBinderAttribute {
     public override IModelBinder GetBinder() {
@@ -69,5 +35,5 @@
       }
       if (string.IsNullOrEmpty(body)) return null;
-      return AlgorithmConverter.ConvertExperiment(body);
+      return AlgorithmConverter.ConvertJsonToExperiment(body);
     }
 
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/Site.css
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/Site.css	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/Site.css	(revision 9305)
@@ -356,2 +356,12 @@
   border: 1px solid black;
 }
+
+
+.leftEntry {
+    float: left;
+    width: 30%;
+}
+
+.rightEntry{
+    margin-left: 30%;
+}
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-min.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-min.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-min.js	(revision 9305)
@@ -0,0 +1,42 @@
+// Backbone.js 0.9.10
+
+// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://backbonejs.org
+(function(){var n=this,B=n.Backbone,h=[],C=h.push,u=h.slice,D=h.splice,g;g="undefined"!==typeof exports?exports:n.Backbone={};g.VERSION="0.9.10";var f=n._;!f&&"undefined"!==typeof require&&(f=require("underscore"));g.$=n.jQuery||n.Zepto||n.ender;g.noConflict=function(){n.Backbone=B;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var v=/\s+/,q=function(a,b,c,d){if(!c)return!0;if("object"===typeof c)for(var e in c)a[b].apply(a,[e,c[e]].concat(d));else if(v.test(c)){c=c.split(v);e=0;for(var f=c.length;e<
+f;e++)a[b].apply(a,[c[e]].concat(d))}else return!0},w=function(a,b){var c,d=-1,e=a.length;switch(b.length){case 0:for(;++d<e;)(c=a[d]).callback.call(c.ctx);break;case 1:for(;++d<e;)(c=a[d]).callback.call(c.ctx,b[0]);break;case 2:for(;++d<e;)(c=a[d]).callback.call(c.ctx,b[0],b[1]);break;case 3:for(;++d<e;)(c=a[d]).callback.call(c.ctx,b[0],b[1],b[2]);break;default:for(;++d<e;)(c=a[d]).callback.apply(c.ctx,b)}},h=g.Events={on:function(a,b,c){if(!q(this,"on",a,[b,c])||!b)return this;this._events||(this._events=
+{});(this._events[a]||(this._events[a]=[])).push({callback:b,context:c,ctx:c||this});return this},once:function(a,b,c){if(!q(this,"once",a,[b,c])||!b)return this;var d=this,e=f.once(function(){d.off(a,e);b.apply(this,arguments)});e._callback=b;this.on(a,e,c);return this},off:function(a,b,c){var d,e,t,g,j,l,k,h;if(!this._events||!q(this,"off",a,[b,c]))return this;if(!a&&!b&&!c)return this._events={},this;g=a?[a]:f.keys(this._events);j=0;for(l=g.length;j<l;j++)if(a=g[j],d=this._events[a]){t=[];if(b||
+c){k=0;for(h=d.length;k<h;k++)e=d[k],(b&&b!==e.callback&&b!==e.callback._callback||c&&c!==e.context)&&t.push(e)}this._events[a]=t}return this},trigger:function(a){if(!this._events)return this;var b=u.call(arguments,1);if(!q(this,"trigger",a,b))return this;var c=this._events[a],d=this._events.all;c&&w(c,b);d&&w(d,arguments);return this},listenTo:function(a,b,c){var d=this._listeners||(this._listeners={}),e=a._listenerId||(a._listenerId=f.uniqueId("l"));d[e]=a;a.on(b,"object"===typeof b?this:c,this);
+return this},stopListening:function(a,b,c){var d=this._listeners;if(d){if(a)a.off(b,"object"===typeof b?this:c,this),!b&&!c&&delete d[a._listenerId];else{"object"===typeof b&&(c=this);for(var e in d)d[e].off(b,c,this);this._listeners={}}return this}}};h.bind=h.on;h.unbind=h.off;f.extend(g,h);var r=g.Model=function(a,b){var c,d=a||{};this.cid=f.uniqueId("c");this.attributes={};b&&b.collection&&(this.collection=b.collection);b&&b.parse&&(d=this.parse(d,b)||{});if(c=f.result(this,"defaults"))d=f.defaults({},
+d,c);this.set(d,b);this.changed={};this.initialize.apply(this,arguments)};f.extend(r.prototype,h,{changed:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},sync:function(){return g.sync.apply(this,arguments)},get:function(a){return this.attributes[a]},escape:function(a){return f.escape(this.get(a))},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e,g,p,j,l,k;if(null==a)return this;"object"===typeof a?(e=a,c=b):(e={})[a]=b;c||(c={});
+if(!this._validate(e,c))return!1;g=c.unset;p=c.silent;a=[];j=this._changing;this._changing=!0;j||(this._previousAttributes=f.clone(this.attributes),this.changed={});k=this.attributes;l=this._previousAttributes;this.idAttribute in e&&(this.id=e[this.idAttribute]);for(d in e)b=e[d],f.isEqual(k[d],b)||a.push(d),f.isEqual(l[d],b)?delete this.changed[d]:this.changed[d]=b,g?delete k[d]:k[d]=b;if(!p){a.length&&(this._pending=!0);b=0;for(d=a.length;b<d;b++)this.trigger("change:"+a[b],this,k[a[b]],c)}if(j)return this;
+if(!p)for(;this._pending;)this._pending=!1,this.trigger("change",this,c);this._changing=this._pending=!1;return this},unset:function(a,b){return this.set(a,void 0,f.extend({},b,{unset:!0}))},clear:function(a){var b={},c;for(c in this.attributes)b[c]=void 0;return this.set(b,f.extend({},a,{unset:!0}))},hasChanged:function(a){return null==a?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._changing?
+this._previousAttributes:this.attributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return null==a||!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=a.success;a.success=function(a,d,e){if(!a.set(a.parse(d,e),e))return!1;b&&b(a,d,e)};return this.sync("read",this,a)},save:function(a,b,c){var d,e,g=this.attributes;
+null==a||"object"===typeof a?(d=a,c=b):(d={})[a]=b;if(d&&(!c||!c.wait)&&!this.set(d,c))return!1;c=f.extend({validate:!0},c);if(!this._validate(d,c))return!1;d&&c.wait&&(this.attributes=f.extend({},g,d));void 0===c.parse&&(c.parse=!0);e=c.success;c.success=function(a,b,c){a.attributes=g;var k=a.parse(b,c);c.wait&&(k=f.extend(d||{},k));if(f.isObject(k)&&!a.set(k,c))return!1;e&&e(a,b,c)};a=this.isNew()?"create":c.patch?"patch":"update";"patch"===a&&(c.attrs=d);a=this.sync(a,this,c);d&&c.wait&&(this.attributes=
+g);return a},destroy:function(a){a=a?f.clone(a):{};var b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};a.success=function(a,b,e){(e.wait||a.isNew())&&d();c&&c(a,b,e)};if(this.isNew())return a.success(this,null,a),!1;var e=this.sync("delete",this,a);a.wait||d();return e},url:function(){var a=f.result(this,"urlRoot")||f.result(this.collection,"url")||x();return this.isNew()?a:a+("/"===a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},
+isNew:function(){return null==this.id},isValid:function(a){return!this.validate||!this.validate(this.attributes,a)},_validate:function(a,b){if(!b.validate||!this.validate)return!0;a=f.extend({},this.attributes,a);var c=this.validationError=this.validate(a,b)||null;if(!c)return!0;this.trigger("invalid",this,c,b||{});return!1}});var s=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);void 0!==b.comparator&&(this.comparator=b.comparator);this.models=[];this._reset();this.initialize.apply(this,
+arguments);a&&this.reset(a,f.extend({silent:!0},b))};f.extend(s.prototype,h,{model:r,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},sync:function(){return g.sync.apply(this,arguments)},add:function(a,b){a=f.isArray(a)?a.slice():[a];b||(b={});var c,d,e,g,p,j,l,k,h,m;l=[];k=b.at;h=this.comparator&&null==k&&!1!=b.sort;m=f.isString(this.comparator)?this.comparator:null;c=0;for(d=a.length;c<d;c++)(e=this._prepareModel(g=a[c],b))?(p=this.get(e))?b.merge&&(p.set(g===
+e?e.attributes:g,b),h&&(!j&&p.hasChanged(m))&&(j=!0)):(l.push(e),e.on("all",this._onModelEvent,this),this._byId[e.cid]=e,null!=e.id&&(this._byId[e.id]=e)):this.trigger("invalid",this,g,b);l.length&&(h&&(j=!0),this.length+=l.length,null!=k?D.apply(this.models,[k,0].concat(l)):C.apply(this.models,l));j&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=l.length;c<d;c++)(e=l[c]).trigger("add",e,this,b);j&&this.trigger("sort",this,b);return this},remove:function(a,b){a=f.isArray(a)?a.slice():[a];
+b||(b={});var c,d,e,g;c=0;for(d=a.length;c<d;c++)if(g=this.get(a[c]))delete this._byId[g.id],delete this._byId[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:this.length},b));return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},
+b));return a},shift:function(a){var b=this.at(0);this.remove(b,a);return b},slice:function(a,b){return this.models.slice(a,b)},get:function(a){if(null!=a)return this._idAttr||(this._idAttr=this.model.prototype.idAttribute),this._byId[a.id||a.cid||a[this._idAttr]||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){if(!this.comparator)throw Error("Cannot sort a set without a comparator");
+a||(a={});f.isString(this.comparator)||1===this.comparator.length?this.models=this.sortBy(this.comparator,this):this.models.sort(f.bind(this.comparator,this));a.silent||this.trigger("sort",this,a);return this},pluck:function(a){return f.invoke(this.models,"get",a)},update:function(a,b){b=f.extend({add:!0,merge:!0,remove:!0},b);b.parse&&(a=this.parse(a,b));var c,d,e,g,h=[],j=[],l={};f.isArray(a)||(a=a?[a]:[]);if(b.add&&!b.remove)return this.add(a,b);d=0;for(e=a.length;d<e;d++)c=a[d],g=this.get(c),
+b.remove&&g&&(l[g.cid]=!0),(b.add&&!g||b.merge&&g)&&h.push(c);if(b.remove){d=0;for(e=this.models.length;d<e;d++)c=this.models[d],l[c.cid]||j.push(c)}j.length&&this.remove(j,b);h.length&&this.add(h,b);return this},reset:function(a,b){b||(b={});b.parse&&(a=this.parse(a,b));for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);b.previousModels=this.models.slice();this._reset();a&&this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=
+a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=a.success;a.success=function(a,d,e){a[e.update?"update":"reset"](d,e);b&&b(a,d,e)};return this.sync("read",this,a)},create:function(a,b){b=b?f.clone(b):{};if(!(a=this._prepareModel(a,b)))return!1;b.wait||this.add(a,b);var c=this,d=b.success;b.success=function(a,b,f){f.wait&&c.add(a,f);d&&d(a,b,f)};a.save(null,b);return a},parse:function(a){return a},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models.length=
+0;this._byId={}},_prepareModel:function(a,b){if(a instanceof r)return a.collection||(a.collection=this),a;b||(b={});b.collection=this;var c=new this.model(a,b);return!c._validate(a,b)?!1:c},_removeReference:function(a){this===a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"===a||"remove"===a)&&c!==this||("destroy"===a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],null!=b.id&&(this._byId[b.id]=
+b)),this.trigger.apply(this,arguments))},sortedIndex:function(a,b,c){b||(b=this.comparator);var d=f.isFunction(b)?b:function(a){return a.get(b)};return f.sortedIndex(this.models,a,d,c)}});f.each("forEach each map collect reduce foldl inject reduceRight foldr find detect filter select reject every all some any include contains invoke max min toArray size first head take initial rest tail drop last without indexOf shuffle lastIndexOf isEmpty chain".split(" "),function(a){s.prototype[a]=function(){var b=
+u.call(arguments);b.unshift(this.models);return f[a].apply(f,b)}});f.each(["groupBy","countBy","sortBy"],function(a){s.prototype[a]=function(b,c){var d=f.isFunction(b)?b:function(a){return a.get(b)};return f[a](this.models,d,c)}});var y=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},E=/\((.*?)\)/g,F=/(\(\?)?:\w+/g,G=/\*\w+/g,H=/[\-{}\[\]+?.,\\\^$|#\s]/g;f.extend(y.prototype,h,{initialize:function(){},route:function(a,b,c){f.isRegExp(a)||
+(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));this.trigger("route",b,d);g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b);return this},_bindRoutes:function(){if(this.routes)for(var a,b=f.keys(this.routes);null!=(a=b.pop());)this.route(a,this.routes[a])},_routeToRegExp:function(a){a=a.replace(H,"\\$&").replace(E,"(?:$1)?").replace(F,
+function(a,c){return c?a:"([^/]+)"}).replace(G,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl");"undefined"!==typeof window&&(this.location=window.location,this.history=window.history)},z=/^[#\/]|\s+$/g,I=/^\/+|\/+$/g,J=/msie [\w.]+/,K=/\/$/;m.started=!1;f.extend(m.prototype,h,{interval:50,getHash:function(a){return(a=(a||this).location.href.match(/#(.*)$/))?a[1]:""},getFragment:function(a,
+b){if(null==a)if(this._hasPushState||!this._wantsHashChange||b){a=this.location.pathname;var c=this.root.replace(K,"");a.indexOf(c)||(a=a.substr(c.length))}else a=this.getHash();return a.replace(z,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this.root=this.options.root;this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!(!this.options.pushState||
+!this.history||!this.history.pushState);a=this.getFragment();var b=document.documentMode,b=J.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b);this.root=("/"+this.root+"/").replace(I,"/");b&&this._wantsHashChange&&(this.iframe=g.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a));if(this._hasPushState)g.$(window).on("popstate",this.checkUrl);else if(this._wantsHashChange&&"onhashchange"in window&&!b)g.$(window).on("hashchange",this.checkUrl);
+else this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval));this.fragment=a;a=this.location;b=a.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),this.location.replace(this.root+this.location.search+"#"+this.fragment),!0;this._wantsPushState&&(this._hasPushState&&b&&a.hash)&&(this.fragment=this.getHash().replace(z,""),this.history.replaceState({},document.title,
+this.root+this.fragment+a.search));if(!this.options.silent)return this.loadUrl()},stop:function(){g.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a===this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a===this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},
+loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};a=this.getFragment(a||"");if(this.fragment!==a){this.fragment=a;var c=this.root+a;if(this._hasPushState)this.history[b.replace?"replaceState":"pushState"]({},document.title,c);else if(this._wantsHashChange)this._updateHash(this.location,a,b.replace),this.iframe&&a!==this.getFragment(this.getHash(this.iframe))&&
+(b.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,a,b.replace));else return this.location.assign(c);b.trigger&&this.loadUrl(a)}},_updateHash:function(a,b,c){c?(c=a.href.replace(/(javascript:|#).*$/,""),a.replace(c+"#"+b)):a.hash="#"+b}});g.history=new m;var A=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},L=/^(\S+)\s*(.*)$/,M="model collection el id attributes className tagName events".split(" ");
+f.extend(A.prototype,h,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof g.$?a:g.$(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=f.result(this,"events"))){this.undelegateEvents();for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);
+if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(L),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);if(""===d)this.$el.on(e,c);else this.$el.on(e,d,c)}}},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},f.result(this,"options"),a));f.extend(this,f.pick(a,M));this.options=a},_ensureElement:function(){if(this.el)this.setElement(f.result(this,"el"),!1);else{var a=f.extend({},f.result(this,"attributes"));
+this.id&&(a.id=f.result(this,"id"));this.className&&(a["class"]=f.result(this,"className"));a=g.$("<"+f.result(this,"tagName")+">").attr(a);this.setElement(a,!1)}}});var N={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=N[a];f.defaults(c||(c={}),{emulateHTTP:g.emulateHTTP,emulateJSON:g.emulateJSON});var e={type:d,dataType:"json"};c.url||(e.url=f.result(b,"url")||x());if(null==c.data&&b&&("create"===a||"update"===a||"patch"===a))e.contentType="application/json",
+e.data=JSON.stringify(c.attrs||b.toJSON(c));c.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(c.emulateHTTP&&("PUT"===d||"DELETE"===d||"PATCH"===d)){e.type="POST";c.emulateJSON&&(e.data._method=d);var h=c.beforeSend;c.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d);if(h)return h.apply(this,arguments)}}"GET"!==e.type&&!c.emulateJSON&&(e.processData=!1);var m=c.success;c.success=function(a){m&&m(b,a,c);b.trigger("sync",b,a,c)};
+var j=c.error;c.error=function(a){j&&j(b,a,c);b.trigger("error",b,a,c)};a=c.xhr=g.ajax(f.extend(e,c));b.trigger("request",b,a,c);return a};g.ajax=function(){return g.$.ajax.apply(g.$,arguments)};r.extend=s.extend=y.extend=A.extend=m.extend=function(a,b){var c=this,d;d=a&&f.has(a,"constructor")?a.constructor:function(){return c.apply(this,arguments)};f.extend(d,c,b);var e=function(){this.constructor=d};e.prototype=c.prototype;d.prototype=new e;a&&f.extend(d.prototype,a);d.__super__=c.prototype;return d};
+var x=function(){throw Error('A "url" property or function must be specified');}}).call(this);
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-relational.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-relational.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/backbone-relational.js	(revision 9305)
@@ -0,0 +1,1706 @@
+/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
+/**
+ * Backbone-relational.js 0.7.1
+ * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
+ * 
+ * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
+ * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
+ * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
+ */
+( function( undefined ) {
+	"use strict";
+	
+	/**
+	 * CommonJS shim
+	 **/
+	var _, Backbone, exports;
+	if ( typeof window === 'undefined' ) {
+		_ = require( 'underscore' );
+		Backbone = require( 'backbone' );
+		exports = module.exports = Backbone;
+	}
+	else {
+		_ = window._;
+		Backbone = window.Backbone;
+		exports = window;
+	}
+
+	Backbone.Relational = {
+		showWarnings: true
+	};
+
+	/**
+	 * Semaphore mixin; can be used as both binary and counting.
+	 **/
+	Backbone.Semaphore = {
+		_permitsAvailable: null,
+		_permitsUsed: 0,
+		
+		acquire: function() {
+			if ( this._permitsAvailable && this._permitsUsed >= this._permitsAvailable ) {
+				throw new Error( 'Max permits acquired' );
+			}
+			else {
+				this._permitsUsed++;
+			}
+		},
+		
+		release: function() {
+			if ( this._permitsUsed === 0 ) {
+				throw new Error( 'All permits released' );
+			}
+			else {
+				this._permitsUsed--;
+			}
+		},
+		
+		isLocked: function() {
+			return this._permitsUsed > 0;
+		},
+		
+		setAvailablePermits: function( amount ) {
+			if ( this._permitsUsed > amount ) {
+				throw new Error( 'Available permits cannot be less than used permits' );
+			}
+			this._permitsAvailable = amount;
+		}
+	};
+	
+	/**
+	 * A BlockingQueue that accumulates items while blocked (via 'block'),
+	 * and processes them when unblocked (via 'unblock').
+	 * Process can also be called manually (via 'process').
+	 */
+	Backbone.BlockingQueue = function() {
+		this._queue = [];
+	};
+	_.extend( Backbone.BlockingQueue.prototype, Backbone.Semaphore, {
+		_queue: null,
+		
+		add: function( func ) {
+			if ( this.isBlocked() ) {
+				this._queue.push( func );
+			}
+			else {
+				func();
+			}
+		},
+		
+		process: function() {
+			while ( this._queue && this._queue.length ) {
+				this._queue.shift()();
+			}
+		},
+		
+		block: function() {
+			this.acquire();
+		},
+		
+		unblock: function() {
+			this.release();
+			if ( !this.isBlocked() ) {
+				this.process();
+			}
+		},
+		
+		isBlocked: function() {
+			return this.isLocked();
+		}
+	});
+	/**
+	 * Global event queue. Accumulates external events ('add:<key>', 'remove:<key>' and 'update:<key>')
+	 * until the top-level object is fully initialized (see 'Backbone.RelationalModel').
+	 */
+	Backbone.Relational.eventQueue = new Backbone.BlockingQueue();
+	
+	/**
+	 * Backbone.Store keeps track of all created (and destruction of) Backbone.RelationalModel.
+	 * Handles lookup for relations.
+	 */
+	Backbone.Store = function() {
+		this._collections = [];
+		this._reverseRelations = [];
+		this._subModels = [];
+		this._modelScopes = [ exports ];
+	};
+	_.extend( Backbone.Store.prototype, Backbone.Events, {
+		addModelScope: function( scope ) {
+			this._modelScopes.push( scope );
+		},
+
+		/**
+		 * Add a set of subModelTypes to the store, that can be used to resolve the '_superModel'
+		 * for a model later in 'setupSuperModel'.
+		 *
+		 * @param {Backbone.RelationalModel} subModelTypes
+		 * @param {Backbone.RelationalModel} superModelType
+		 */
+		addSubModels: function( subModelTypes, superModelType ) {
+			this._subModels.push({
+				'superModelType': superModelType,
+				'subModels': subModelTypes
+			});
+		},
+
+		/**
+		 * Check if the given modelType is registered as another model's subModel. If so, add it to the super model's
+		 * '_subModels', and set the modelType's '_superModel', '_subModelTypeName', and '_subModelTypeAttribute'.
+		 *
+		 * @param {Backbone.RelationalModel} modelType
+		 */
+		setupSuperModel: function( modelType ) {
+			_.find( this._subModels || [], function( subModelDef ) {
+				return _.find( subModelDef.subModels || [], function( subModelTypeName, typeValue ) {
+					var subModelType = this.getObjectByName( subModelTypeName );
+
+					if ( modelType === subModelType ) {
+						// Set 'modelType' as a child of the found superModel
+						subModelDef.superModelType._subModels[ typeValue ] = modelType;
+
+						// Set '_superModel', '_subModelTypeValue', and '_subModelTypeAttribute' on 'modelType'.
+						modelType._superModel = subModelDef.superModelType;
+						modelType._subModelTypeValue = typeValue;
+						modelType._subModelTypeAttribute = subModelDef.superModelType.prototype.subModelTypeAttribute;
+						return true;
+					}
+				}, this );
+			}, this );
+		},
+		
+		/**
+		 * Add a reverse relation. Is added to the 'relations' property on model's prototype, and to
+		 * existing instances of 'model' in the store as well.
+		 * @param {Object} relation
+		 * @param {Backbone.RelationalModel} relation.model
+		 * @param {String} relation.type
+		 * @param {String} relation.key
+		 * @param {String|Object} relation.relatedModel
+		 */
+		addReverseRelation: function( relation ) {
+			var exists = _.any( this._reverseRelations || [], function( rel ) {
+					return _.all( relation || [], function( val, key ) {
+							return val === rel[ key ];
+						});
+				});
+			
+			if ( !exists && relation.model && relation.type ) {
+				this._reverseRelations.push( relation );
+				
+				var addRelation = function( model, relation ) {
+					if ( !model.prototype.relations ) {
+						model.prototype.relations = [];
+					}
+					model.prototype.relations.push( relation );
+					
+					_.each( model._subModels || [], function( subModel ) {
+							addRelation( subModel, relation );
+						}, this );
+				};
+				
+				addRelation( relation.model, relation );
+				
+				this.retroFitRelation( relation );
+			}
+		},
+		
+		/**
+		 * Add a 'relation' to all existing instances of 'relation.model' in the store
+		 * @param {Object} relation
+		 */
+		retroFitRelation: function( relation ) {
+			var coll = this.getCollection( relation.model );
+			coll.each( function( model ) {
+				if ( !( model instanceof relation.model ) ) {
+					return;
+				}
+
+				new relation.type( model, relation );
+			}, this);
+		},
+		
+		/**
+		 * Find the Store's collection for a certain type of model.
+		 * @param {Backbone.RelationalModel} model
+		 * @return {Backbone.Collection} A collection if found (or applicable for 'model'), or null
+		 */
+		getCollection: function( model ) {
+			if ( model instanceof Backbone.RelationalModel ) {
+				model = model.constructor;
+			}
+			
+			var rootModel = model;
+			while ( rootModel._superModel ) {
+				rootModel = rootModel._superModel;
+			}
+			
+			var coll = _.detect( this._collections, function( c ) {
+				return c.model === rootModel;
+			});
+			
+			if ( !coll ) {
+				coll = this._createCollection( rootModel );
+			}
+			
+			return coll;
+		},
+		
+		/**
+		 * Find a type on the global object by name. Splits name on dots.
+		 * @param {String} name
+		 * @return {Object}
+		 */
+		getObjectByName: function( name ) {
+			var parts = name.split( '.' ),
+				type = null;
+
+			_.find( this._modelScopes || [], function( scope ) {
+				type = _.reduce( parts || [], function( memo, val ) {
+					return memo ? memo[ val ] : undefined;
+				}, scope );
+
+				if ( type && type !== scope ) {
+					return true;
+				}
+			}, this );
+
+			return type;
+		},
+		
+		_createCollection: function( type ) {
+			var coll;
+			
+			// If 'type' is an instance, take its constructor
+			if ( type instanceof Backbone.RelationalModel ) {
+				type = type.constructor;
+			}
+			
+			// Type should inherit from Backbone.RelationalModel.
+			if ( type.prototype instanceof Backbone.RelationalModel ) {
+				coll = new Backbone.Collection();
+				coll.model = type;
+				
+				this._collections.push( coll );
+			}
+			
+			return coll;
+		},
+
+		/**
+		 * Find the attribute that is to be used as the `id` on a given object
+		 * @param type
+		 * @param {String|Number|Object|Backbone.RelationalModel} item
+		 * @return {String|Number}
+		 */
+		resolveIdForItem: function( type, item ) {
+			var id = _.isString( item ) || _.isNumber( item ) ? item : null;
+
+			if ( id === null ) {
+				if ( item instanceof Backbone.RelationalModel ) {
+					id = item.id;
+				}
+				else if ( _.isObject( item ) ) {
+					id = item[ type.prototype.idAttribute ];
+				}
+			}
+
+			// Make all falsy values `null` (except for 0, which could be an id.. see '/issues/179')
+			if ( !id && id !== 0 ) {
+				id = null;
+			}
+
+			return id;
+		},
+
+		/**
+		 *
+		 * @param type
+		 * @param {String|Number|Object|Backbone.RelationalModel} item
+		 */
+		find: function( type, item ) {
+			var id = this.resolveIdForItem( type, item );
+			var coll = this.getCollection( type );
+			
+			// Because the found object could be of any of the type's superModel
+			// types, only return it if it's actually of the type asked for.
+			if ( coll ) {
+				var obj = coll.get( id );
+
+				if ( obj instanceof type ) {
+					return obj;
+				}
+			}
+
+			return null;
+		},
+		
+		/**
+		 * Add a 'model' to it's appropriate collection. Retain the original contents of 'model.collection'.
+		 * @param {Backbone.RelationalModel} model
+		 */
+		register: function( model ) {
+			var coll = this.getCollection( model );
+
+			if ( coll ) {
+				if ( coll.get( model ) ) {
+					throw new Error( "Cannot instantiate more than one Backbone.RelationalModel with the same id per type!" );
+				}
+
+				var modelColl = model.collection;
+				coll.add( model );
+				model.bind( 'destroy', this.unregister, this );
+				model.collection = modelColl;
+			}
+		},
+		
+		/**
+		 * Explicitly update a model's id in it's store collection
+		 * @param {Backbone.RelationalModel} model
+		 */
+		update: function( model ) {
+			var coll = this.getCollection( model );
+			coll._onModelEvent( 'change:' + model.idAttribute, model, coll );
+		},
+		
+		/**
+		 * Remove a 'model' from the store.
+		 * @param {Backbone.RelationalModel} model
+		 */
+		unregister: function( model ) {
+			model.unbind( 'destroy', this.unregister );
+			var coll = this.getCollection( model );
+			coll && coll.remove( model );
+		}
+	});
+	Backbone.Relational.store = new Backbone.Store();
+	
+	/**
+	 * The main Relation class, from which 'HasOne' and 'HasMany' inherit. Internally, 'relational:<key>' events
+	 * are used to regulate addition and removal of models from relations.
+	 *
+	 * @param {Backbone.RelationalModel} instance
+	 * @param {Object} options
+	 * @param {string} options.key
+	 * @param {Backbone.RelationalModel.constructor} options.relatedModel
+	 * @param {Boolean|String} [options.includeInJSON=true] Serialize the given attribute for related model(s)' in toJSON, or just their ids.
+	 * @param {Boolean} [options.createModels=true] Create objects from the contents of keys if the object is not found in Backbone.store.
+	 * @param {Object} [options.reverseRelation] Specify a bi-directional relation. If provided, Relation will reciprocate
+	 *    the relation to the 'relatedModel'. Required and optional properties match 'options', except that it also needs
+	 *    {Backbone.Relation|String} type ('HasOne' or 'HasMany').
+	 */
+	Backbone.Relation = function( instance, options ) {
+		this.instance = instance;
+		// Make sure 'options' is sane, and fill with defaults from subclasses and this object's prototype
+		options = _.isObject( options ) ? options : {};
+		this.reverseRelation = _.defaults( options.reverseRelation || {}, this.options.reverseRelation );
+		this.reverseRelation.type = !_.isString( this.reverseRelation.type ) ? this.reverseRelation.type :
+			Backbone[ this.reverseRelation.type ] || Backbone.Relational.store.getObjectByName( this.reverseRelation.type );
+		this.model = options.model || this.instance.constructor;
+		this.options = _.defaults( options, this.options, Backbone.Relation.prototype.options );
+		
+		this.key = this.options.key;
+		this.keySource = this.options.keySource || this.key;
+		this.keyDestination = this.options.keyDestination || this.keySource || this.key;
+
+		// 'exports' should be the global object where 'relatedModel' can be found on if given as a string.
+		this.relatedModel = this.options.relatedModel;
+		if ( _.isString( this.relatedModel ) ) {
+			this.relatedModel = Backbone.Relational.store.getObjectByName( this.relatedModel );
+		}
+
+		if ( !this.checkPreconditions() ) {
+			return;
+		}
+
+		if ( instance ) {
+			var contentKey = this.keySource;
+			if ( contentKey !== this.key && typeof this.instance.get( this.key ) === 'object' ) {
+				contentKey = this.key;
+			}
+
+			this.keyContents = this.instance.get( contentKey );
+
+			// Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
+			if ( this.keySource !== this.key ) {
+				this.instance.unset( this.keySource, { silent: true } );
+			}
+
+			// Add this Relation to instance._relations
+			this.instance._relations.push( this );
+		}
+
+		// Add the reverse relation on 'relatedModel' to the store's reverseRelations
+		if ( !this.options.isAutoRelation && this.reverseRelation.type && this.reverseRelation.key ) {
+			Backbone.Relational.store.addReverseRelation( _.defaults( {
+					isAutoRelation: true,
+					model: this.relatedModel,
+					relatedModel: this.model,
+					reverseRelation: this.options // current relation is the 'reverseRelation' for it's own reverseRelation
+				},
+				this.reverseRelation // Take further properties from this.reverseRelation (type, key, etc.)
+			) );
+		}
+
+		_.bindAll( this, '_modelRemovedFromCollection', '_relatedModelAdded', '_relatedModelRemoved' );
+
+		if ( instance ) {
+			this.initialize();
+
+			if ( options.autoFetch ) {
+				this.instance.fetchRelated( options.key, _.isObject( options.autoFetch ) ? options.autoFetch : {} );
+			}
+
+			// When a model in the store is destroyed, check if it is 'this.instance'.
+			Backbone.Relational.store.getCollection( this.instance )
+				.bind( 'relational:remove', this._modelRemovedFromCollection );
+
+			// When 'relatedModel' are created or destroyed, check if it affects this relation.
+			Backbone.Relational.store.getCollection( this.relatedModel )
+				.bind( 'relational:add', this._relatedModelAdded )
+				.bind( 'relational:remove', this._relatedModelRemoved );
+		}
+	};
+	// Fix inheritance :\
+	Backbone.Relation.extend = Backbone.Model.extend;
+	// Set up all inheritable **Backbone.Relation** properties and methods.
+	_.extend( Backbone.Relation.prototype, Backbone.Events, Backbone.Semaphore, {
+		options: {
+			createModels: true,
+			includeInJSON: true,
+			isAutoRelation: false,
+			autoFetch: false
+		},
+		
+		instance: null,
+		key: null,
+		keyContents: null,
+		relatedModel: null,
+		reverseRelation: null,
+		related: null,
+		
+		_relatedModelAdded: function( model, coll, options ) {
+			// Allow 'model' to set up it's relations, before calling 'tryAddRelated'
+			// (which can result in a call to 'addRelated' on a relation of 'model')
+			var dit = this;
+			model.queue( function() {
+				dit.tryAddRelated( model, options );
+			});
+		},
+		
+		_relatedModelRemoved: function( model, coll, options ) {
+			this.removeRelated( model, options );
+		},
+		
+		_modelRemovedFromCollection: function( model ) {
+			if ( model === this.instance ) {
+				this.destroy();
+			}
+		},
+		
+		/**
+		 * Check several pre-conditions.
+		 * @return {Boolean} True if pre-conditions are satisfied, false if they're not.
+		 */
+		checkPreconditions: function() {
+			var i = this.instance,
+				k = this.key,
+				m = this.model,
+				rm = this.relatedModel,
+				warn = Backbone.Relational.showWarnings && typeof console !== 'undefined';
+
+			if ( !m || !k || !rm ) {
+				warn && console.warn( 'Relation=%o; no model, key or relatedModel (%o, %o, %o)', this, m, k, rm );
+				return false;
+			}
+			// Check if the type in 'model' inherits from Backbone.RelationalModel
+			if ( !( m.prototype instanceof Backbone.RelationalModel ) ) {
+				warn && console.warn( 'Relation=%o; model does not inherit from Backbone.RelationalModel (%o)', this, i );
+				return false;
+			}
+			// Check if the type in 'relatedModel' inherits from Backbone.RelationalModel
+			if ( !( rm.prototype instanceof Backbone.RelationalModel ) ) {
+				warn && console.warn( 'Relation=%o; relatedModel does not inherit from Backbone.RelationalModel (%o)', this, rm );
+				return false;
+			}
+			// Check if this is not a HasMany, and the reverse relation is HasMany as well
+			if ( this instanceof Backbone.HasMany && this.reverseRelation.type === Backbone.HasMany ) {
+				warn && console.warn( 'Relation=%o; relation is a HasMany, and the reverseRelation is HasMany as well.', this );
+				return false;
+			}
+
+			// Check if we're not attempting to create a duplicate relationship
+			if ( i && i._relations.length ) {
+				var exists = _.any( i._relations || [], function( rel ) {
+						var hasReverseRelation = this.reverseRelation.key && rel.reverseRelation.key;
+						return rel.relatedModel === rm && rel.key === k &&
+							( !hasReverseRelation || this.reverseRelation.key === rel.reverseRelation.key );
+					}, this );
+
+				if ( exists ) {
+					warn && console.warn( 'Relation=%o between instance=%o.%s and relatedModel=%o.%s already exists',
+						this, i, k, rm, this.reverseRelation.key );
+					return false;
+				}
+			}
+
+			return true;
+		},
+
+		/**
+		 * Set the related model(s) for this relation
+		 * @param {Backbone.Model|Backbone.Collection} related
+		 * @param {Object} [options]
+		 */
+		setRelated: function( related, options ) {
+			this.related = related;
+
+			this.instance.acquire();
+			this.instance.attributes[ this.key ] = related;
+			this.instance.release();
+		},
+		
+		/**
+		 * Determine if a relation (on a different RelationalModel) is the reverse
+		 * relation of the current one.
+		 * @param {Backbone.Relation} relation
+		 * @return {Boolean}
+		 */
+		_isReverseRelation: function( relation ) {
+			if ( relation.instance instanceof this.relatedModel && this.reverseRelation.key === relation.key &&
+					this.key === relation.reverseRelation.key ) {
+				return true;
+			}
+			return false;
+		},
+		
+		/**
+		 * Get the reverse relations (pointing back to 'this.key' on 'this.instance') for the currently related model(s).
+		 * @param {Backbone.RelationalModel} [model] Get the reverse relations for a specific model.
+		 *    If not specified, 'this.related' is used.
+		 * @return {Backbone.Relation[]}
+		 */
+		getReverseRelations: function( model ) {
+			var reverseRelations = [];
+			// Iterate over 'model', 'this.related.models' (if this.related is a Backbone.Collection), or wrap 'this.related' in an array.
+			var models = !_.isUndefined( model ) ? [ model ] : this.related && ( this.related.models || [ this.related ] );
+			_.each( models || [], function( related ) {
+					_.each( related.getRelations() || [], function( relation ) {
+							if ( this._isReverseRelation( relation ) ) {
+								reverseRelations.push( relation );
+							}
+						}, this );
+				}, this );
+			
+			return reverseRelations;
+		},
+		
+		/**
+		 * Rename options.silent to options.silentChange, so events propagate properly.
+		 * (for example in HasMany, from 'addRelated'->'handleAddition')
+		 * @param {Object} [options]
+		 * @return {Object}
+		 */
+		sanitizeOptions: function( options ) {
+			options = options ? _.clone( options ) : {};
+			if ( options.silent ) {
+				options.silentChange = true;
+				delete options.silent;
+			}
+			return options;
+		},
+
+		/**
+		 * Rename options.silentChange to options.silent, so events are silenced as intended in Backbone's
+		 * original functions.
+		 * @param {Object} [options]
+		 * @return {Object}
+		 */
+		unsanitizeOptions: function( options ) {
+			options = options ? _.clone( options ) : {};
+			if ( options.silentChange ) {
+				options.silent = true;
+				delete options.silentChange;
+			}
+			return options;
+		},
+		
+		// Cleanup. Get reverse relation, call removeRelated on each.
+		destroy: function() {
+			Backbone.Relational.store.getCollection( this.instance )
+				.unbind( 'relational:remove', this._modelRemovedFromCollection );
+			
+			Backbone.Relational.store.getCollection( this.relatedModel )
+				.unbind( 'relational:add', this._relatedModelAdded )
+				.unbind( 'relational:remove', this._relatedModelRemoved );
+			
+			_.each( this.getReverseRelations() || [], function( relation ) {
+					relation.removeRelated( this.instance );
+				}, this );
+		}
+	});
+	
+	Backbone.HasOne = Backbone.Relation.extend({
+		options: {
+			reverseRelation: { type: 'HasMany' }
+		},
+		
+		initialize: function() {
+			_.bindAll( this, 'onChange' );
+
+			this.instance.bind( 'relational:change:' + this.key, this.onChange );
+
+			var model = this.findRelated( { silent: true } );
+			this.setRelated( model );
+
+			// Notify new 'related' object of the new relation.
+			_.each( this.getReverseRelations() || [], function( relation ) {
+					relation.addRelated( this.instance );
+				}, this );
+		},
+		
+		findRelated: function( options ) {
+			var item = this.keyContents;
+			var model = null;
+			
+			if ( item instanceof this.relatedModel ) {
+				model = item;
+			}
+			else if ( item || item === 0 ) { // since 0 can be a valid `id` as well
+				model = this.relatedModel.findOrCreate( item, { create: this.options.createModels } );
+			}
+			
+			return model;
+		},
+		
+		/**
+		 * If the key is changed, notify old & new reverse relations and initialize the new relation
+		 */
+		onChange: function( model, attr, options ) {
+			// Don't accept recursive calls to onChange (like onChange->findRelated->findOrCreate->initializeRelations->addRelated->onChange)
+			if ( this.isLocked() ) {
+				return;
+			}
+			this.acquire();
+			options = this.sanitizeOptions( options );
+			
+			// 'options._related' is set by 'addRelated'/'removeRelated'. If it is set, the change
+			// is the result of a call from a relation. If it's not, the change is the result of 
+			// a 'set' call on this.instance.
+			var changed = _.isUndefined( options._related );
+			var oldRelated = changed ? this.related : options._related;
+			
+			if ( changed ) {	
+				this.keyContents = attr;
+				
+				// Set new 'related'
+				if ( attr instanceof this.relatedModel ) {
+					this.related = attr;
+				}
+				else if ( attr ) {
+					var related = this.findRelated( options );
+					this.setRelated( related );
+				}
+				else {
+					this.setRelated( null );
+				}
+			}
+			
+			// Notify old 'related' object of the terminated relation
+			if ( oldRelated && this.related !== oldRelated ) {
+				_.each( this.getReverseRelations( oldRelated ) || [], function( relation ) {
+						relation.removeRelated( this.instance, options );
+					}, this );
+			}
+			
+			// Notify new 'related' object of the new relation. Note we do re-apply even if this.related is oldRelated;
+			// that can be necessary for bi-directional relations if 'this.instance' was created after 'this.related'.
+			// In that case, 'this.instance' will already know 'this.related', but the reverse might not exist yet.
+			_.each( this.getReverseRelations() || [], function( relation ) {
+					relation.addRelated( this.instance, options );
+				}, this);
+			
+			// Fire the 'update:<key>' event if 'related' was updated
+			if ( !options.silentChange && this.related !== oldRelated ) {
+				var dit = this;
+				Backbone.Relational.eventQueue.add( function() {
+					dit.instance.trigger( 'update:' + dit.key, dit.instance, dit.related, options );
+				});
+			}
+			this.release();
+		},
+		
+		/**
+		 * If a new 'this.relatedModel' appears in the 'store', try to match it to the last set 'keyContents'
+		 */
+		tryAddRelated: function( model, options ) {
+			if ( this.related ) {
+				return;
+			}
+			options = this.sanitizeOptions( options );
+			
+			var item = this.keyContents;
+			if ( item || item === 0 ) { // since 0 can be a valid `id` as well
+				var id = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
+				if ( !_.isNull( id ) && model.id === id ) {
+					this.addRelated( model, options );
+				}
+			}
+		},
+		
+		addRelated: function( model, options ) {
+			if ( model !== this.related ) {
+				var oldRelated = this.related || null;
+				this.setRelated( model );
+				this.onChange( this.instance, model, { _related: oldRelated } );
+			}
+		},
+		
+		removeRelated: function( model, options ) {
+			if ( !this.related ) {
+				return;
+			}
+			
+			if ( model === this.related ) {
+				var oldRelated = this.related || null;
+				this.setRelated( null );
+				this.onChange( this.instance, model, { _related: oldRelated } );
+			}
+		}
+	});
+	
+	Backbone.HasMany = Backbone.Relation.extend({
+		collectionType: null,
+		
+		options: {
+			reverseRelation: { type: 'HasOne' },
+			collectionType: Backbone.Collection,
+			collectionKey: true,
+			collectionOptions: {}
+		},
+		
+		initialize: function() {
+			_.bindAll( this, 'onChange', 'handleAddition', 'handleRemoval', 'handleReset' );
+			this.instance.bind( 'relational:change:' + this.key, this.onChange );
+			
+			// Handle a custom 'collectionType'
+			this.collectionType = this.options.collectionType;
+			if ( _.isString( this.collectionType ) ) {
+				this.collectionType = Backbone.Relational.store.getObjectByName( this.collectionType );
+			}
+			if ( !this.collectionType.prototype instanceof Backbone.Collection ){
+				throw new Error( 'collectionType must inherit from Backbone.Collection' );
+			}
+
+			// Handle cases where a model/relation is created with a collection passed straight into 'attributes'
+			if ( this.keyContents instanceof Backbone.Collection ) {
+				this.setRelated( this._prepareCollection( this.keyContents ) );
+			}
+			else {
+				this.setRelated( this._prepareCollection() );
+			}
+
+			this.findRelated( { silent: true } );
+		},
+		
+		_getCollectionOptions: function() {
+			return _.isFunction( this.options.collectionOptions ) ?
+				this.options.collectionOptions( this.instance ) :
+				this.options.collectionOptions;
+		},
+
+		/**
+		 * Bind events and setup collectionKeys for a collection that is to be used as the backing store for a HasMany.
+		 * If no 'collection' is supplied, a new collection will be created of the specified 'collectionType' option.
+		 * @param {Backbone.Collection} [collection]
+		 */
+		_prepareCollection: function( collection ) {
+			if ( this.related ) {
+				this.related
+					.unbind( 'relational:add', this.handleAddition )
+					.unbind( 'relational:remove', this.handleRemoval )
+					.unbind( 'relational:reset', this.handleReset )
+			}
+
+			if ( !collection || !( collection instanceof Backbone.Collection ) ) {
+				collection = new this.collectionType( [], this._getCollectionOptions() );
+			}
+
+			collection.model = this.relatedModel;
+			
+			if ( this.options.collectionKey ) {
+				var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
+				
+				if ( collection[ key ] && collection[ key ] !== this.instance ) {
+					if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
+						console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
+					}
+				}
+				else if ( key ) {
+					collection[ key ] = this.instance;
+				}
+			}
+			
+			collection
+				.bind( 'relational:add', this.handleAddition )
+				.bind( 'relational:remove', this.handleRemoval )
+				.bind( 'relational:reset', this.handleReset );
+			
+			return collection;
+		},
+		
+		findRelated: function( options ) {
+			if ( this.keyContents ) {
+				var models = [];
+
+				if ( this.keyContents instanceof Backbone.Collection ) {
+					models = this.keyContents.models;
+				}
+				else {
+					// Handle cases the an API/user supplies just an Object/id instead of an Array
+					this.keyContents = _.isArray( this.keyContents ) ? this.keyContents : [ this.keyContents ];
+
+					// Try to find instances of the appropriate 'relatedModel' in the store
+					_.each( this.keyContents || [], function( item ) {
+							var model = null;
+							if ( item instanceof this.relatedModel ) {
+								model = item;
+							}
+							else if ( item || item === 0 ) { // since 0 can be a valid `id` as well
+								model = this.relatedModel.findOrCreate( item, { create: this.options.createModels } );
+							}
+
+							if ( model && !this.related.get( model ) ) {
+								models.push( model );
+							}
+						}, this );
+				}
+
+				// Add all found 'models' in on go, so 'add' will only be called once (and thus 'sort', etc.)
+				if ( models.length ) {
+					options = this.unsanitizeOptions( options );
+					this.related.add( models, options );
+				}
+			}
+		},
+		
+		/**
+		 * If the key is changed, notify old & new reverse relations and initialize the new relation
+		 */
+		onChange: function( model, attr, options ) {
+			options = this.sanitizeOptions( options );
+			this.keyContents = attr;
+			
+			// Replace 'this.related' by 'attr' if it is a Backbone.Collection
+			if ( attr instanceof Backbone.Collection ) {
+				this._prepareCollection( attr );
+				this.related = attr;
+			}
+			// Otherwise, 'attr' should be an array of related object ids.
+			// Re-use the current 'this.related' if it is a Backbone.Collection, and remove any current entries.
+			// Otherwise, create a new collection.
+			else {
+				var oldIds = {}, newIds = {};
+
+				if (!_.isArray( attr ) && attr !== undefined) {
+					attr = [ attr ];
+				}
+
+				_.each( attr, function( attributes ) {
+					newIds[ attributes.id ] = true;
+				});
+
+				var coll = this.related;
+				if ( coll instanceof Backbone.Collection ) {
+					// Make sure to operate on a copy since we're removing while iterating
+					_.each( coll.models.slice(0) , function( model ) {
+						// When fetch is called with the 'keepNewModels' option, we don't want to remove
+						// client-created new models when the fetch is completed.
+						if ( !options.keepNewModels || !model.isNew() ) {
+							oldIds[ model.id ] = true;
+							coll.remove( model, { silent: (model.id in newIds) } );
+						}
+					});
+				} else {
+					coll = this._prepareCollection();
+				}
+
+				_.each( attr, function( attributes ) {
+					var model = this.relatedModel.findOrCreate( attributes, { create: this.options.createModels } );
+					if (model) {
+						coll.add( model, { silent: (attributes.id in oldIds)} );
+					}
+				}, this);
+
+				this.setRelated( coll );
+
+			}
+			
+			var dit = this;
+			Backbone.Relational.eventQueue.add( function() {
+				!options.silentChange && dit.instance.trigger( 'update:' + dit.key, dit.instance, dit.related, options );
+			});
+		},
+		
+		tryAddRelated: function( model, options ) {
+			options = this.sanitizeOptions( options );
+			if ( !this.related.get( model ) ) {
+				// Check if this new model was specified in 'this.keyContents'
+				var item = _.any( this.keyContents || [], function( item ) {
+						var id = Backbone.Relational.store.resolveIdForItem( this.relatedModel, item );
+						return !_.isNull( id ) && id === model.id;
+					}, this );
+				
+				if ( item ) {
+					this.related.add( model, options );
+				}
+			}
+		},
+		
+		/**
+		 * When a model is added to a 'HasMany', trigger 'add' on 'this.instance' and notify reverse relations.
+		 * (should be 'HasOne', must set 'this.instance' as their related).
+		 */
+		handleAddition: function( model, coll, options ) {
+			//console.debug('handleAddition called; args=%o', arguments);
+			// Make sure the model is in fact a valid model before continuing.
+			// (it can be invalid as a result of failing validation in Backbone.Collection._prepareModel)
+			if ( !( model instanceof Backbone.Model ) ) {
+				return;
+			}
+			
+			options = this.sanitizeOptions( options );
+			
+			_.each( this.getReverseRelations( model ) || [], function( relation ) {
+					relation.addRelated( this.instance, options );
+				}, this );
+
+			// Only trigger 'add' once the newly added model is initialized (so, has it's relations set up)
+			var dit = this;
+			Backbone.Relational.eventQueue.add( function() {
+				!options.silentChange && dit.instance.trigger( 'add:' + dit.key, model, dit.related, options );
+			});
+		},
+		
+		/**
+		 * When a model is removed from a 'HasMany', trigger 'remove' on 'this.instance' and notify reverse relations.
+		 * (should be 'HasOne', which should be nullified)
+		 */
+		handleRemoval: function( model, coll, options ) {
+			//console.debug('handleRemoval called; args=%o', arguments);
+			if ( !( model instanceof Backbone.Model ) ) {
+				return;
+			}
+
+			options = this.sanitizeOptions( options );
+			
+			_.each( this.getReverseRelations( model ) || [], function( relation ) {
+					relation.removeRelated( this.instance, options );
+				}, this );
+			
+			var dit = this;
+			Backbone.Relational.eventQueue.add( function() {
+				!options.silentChange && dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
+			});
+		},
+
+		handleReset: function( coll, options ) {
+			options = this.sanitizeOptions( options );
+
+			var dit = this;
+			Backbone.Relational.eventQueue.add( function() {
+				!options.silentChange && dit.instance.trigger( 'reset:' + dit.key, dit.related, options );
+			});
+		},
+		
+		addRelated: function( model, options ) {
+			var dit = this;
+			options = this.unsanitizeOptions( options );
+			model.queue( function() { // Queued to avoid errors for adding 'model' to the 'this.related' set twice
+				if ( dit.related && !dit.related.get( model ) ) {
+					dit.related.add( model, options );
+				}
+			});
+		},
+		
+		removeRelated: function( model, options ) {
+			options = this.unsanitizeOptions( options );
+			if ( this.related.get( model ) ) {
+				this.related.remove( model, options );
+			}
+		}
+	});
+	
+	/**
+	 * A type of Backbone.Model that also maintains relations to other models and collections.
+	 * New events when compared to the original:
+	 *  - 'add:<key>' (model, related collection, options)
+	 *  - 'remove:<key>' (model, related collection, options)
+	 *  - 'update:<key>' (model, related model or collection, options)
+	 */
+	Backbone.RelationalModel = Backbone.Model.extend({
+		relations: null, // Relation descriptions on the prototype
+		_relations: null, // Relation instances
+		_isInitialized: false,
+		_deferProcessing: false,
+		_queue: null,
+		
+		subModelTypeAttribute: 'type',
+		subModelTypes: null,
+		
+		constructor: function( attributes, options ) {
+			// Nasty hack, for cases like 'model.get( <HasMany key> ).add( item )'.
+			// Defer 'processQueue', so that when 'Relation.createModels' is used we:
+			// a) Survive 'Backbone.Collection.add'; this takes care we won't error on "can't add model to a set twice"
+			//    (by creating a model from properties, having the model add itself to the collection via one of
+			//    it's relations, then trying to add it to the collection).
+			// b) Trigger 'HasMany' collection events only after the model is really fully set up.
+			// Example that triggers both a and b: "p.get('jobs').add( { company: c, person: p } )".
+			var dit = this;
+			if ( options && options.collection ) {
+				this._deferProcessing = true;
+				
+				var processQueue = function( model ) {
+					if ( model === dit ) {
+						dit._deferProcessing = false;
+						dit.processQueue();
+						options.collection.unbind( 'relational:add', processQueue );
+					}
+				};
+				options.collection.bind( 'relational:add', processQueue );
+				
+				// So we do process the queue eventually, regardless of whether this model really gets added to 'options.collection'.
+				_.defer( function() {
+					processQueue( dit );
+				});
+			}
+			
+			this._queue = new Backbone.BlockingQueue();
+			this._queue.block();
+			Backbone.Relational.eventQueue.block();
+			
+			Backbone.Model.apply( this, arguments );
+			
+			// Try to run the global queue holding external events
+			Backbone.Relational.eventQueue.unblock();
+		},
+		
+		/**
+		 * Override 'trigger' to queue 'change' and 'change:*' events
+		 */
+		trigger: function( eventName ) {
+			if ( eventName.length > 5 && 'change' === eventName.substr( 0, 6 ) ) {
+				var dit = this, args = arguments;
+				Backbone.Relational.eventQueue.add( function() {
+						Backbone.Model.prototype.trigger.apply( dit, args );
+					});
+			}
+			else {
+				Backbone.Model.prototype.trigger.apply( this, arguments );
+			}
+			
+			return this;
+		},
+		
+		/**
+		 * Initialize Relations present in this.relations; determine the type (HasOne/HasMany), then creates a new instance.
+		 * Invoked in the first call so 'set' (which is made from the Backbone.Model constructor).
+		 */
+		initializeRelations: function() {
+			this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
+			this._relations = [];
+			
+			_.each( this.relations || [], function( rel ) {
+					var type = !_.isString( rel.type ) ? rel.type :	Backbone[ rel.type ] || Backbone.Relational.store.getObjectByName( rel.type );
+					if ( type && type.prototype instanceof Backbone.Relation ) {
+						new type( this, rel ); // Also pushes the new Relation into _relations
+					}
+					else {
+						Backbone.Relational.showWarnings && typeof console !== 'undefined' && console.warn( 'Relation=%o; missing or invalid type!', rel );
+					}
+				}, this );
+			
+			this._isInitialized = true;
+			this.release();
+			this.processQueue();
+		},
+
+		/**
+		 * When new values are set, notify this model's relations (also if options.silent is set).
+		 * (Relation.setRelated locks this model before calling 'set' on it to prevent loops)
+		 */
+		updateRelations: function( options ) {
+			if ( this._isInitialized && !this.isLocked() ) {
+				_.each( this._relations || [], function( rel ) {
+					// Update from data in `rel.keySource` if set, or `rel.key` otherwise
+					var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
+					if ( rel.related !== val ) {
+						this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
+					}
+				}, this );
+			}
+		},
+		
+		/**
+		 * Either add to the queue (if we're not initialized yet), or execute right away.
+		 */
+		queue: function( func ) {
+			this._queue.add( func );
+		},
+		
+		/**
+		 * Process _queue
+		 */
+		processQueue: function() {
+			if ( this._isInitialized && !this._deferProcessing && this._queue.isBlocked() ) {
+				this._queue.unblock();
+			}
+		},
+		
+		/**
+		 * Get a specific relation.
+		 * @param key {string} The relation key to look for.
+		 * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
+		 */
+		getRelation: function( key ) {
+			return _.detect( this._relations, function( rel ) {
+				if ( rel.key === key ) {
+					return true;
+				}
+			}, this );
+		},
+		
+		/**
+		 * Get all of the created relations.
+		 * @return {Backbone.Relation[]}
+		 */
+		getRelations: function() {
+			return this._relations;
+		},
+		
+		/**
+		 * Retrieve related objects.
+		 * @param key {string} The relation key to fetch models for.
+		 * @param [options] {Object} Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
+		 * @param [update=false] {boolean} Whether to force a fetch from the server (updating existing models).
+		 * @return {jQuery.when[]} An array of request objects
+		 */
+		fetchRelated: function( key, options, update ) {
+			options || ( options = {} );
+			var setUrl,
+				requests = [],
+				rel = this.getRelation( key ),
+				keyContents = rel && rel.keyContents,
+				toFetch = keyContents && _.select( _.isArray( keyContents ) ? keyContents : [ keyContents ], function( item ) {
+					var id = Backbone.Relational.store.resolveIdForItem( rel.relatedModel, item );
+					return !_.isNull( id ) && ( update || !Backbone.Relational.store.find( rel.relatedModel, id ) );
+				}, this );
+			
+			if ( toFetch && toFetch.length ) {
+				// Create a model for each entry in 'keyContents' that is to be fetched
+				var models = _.map( toFetch, function( item ) {
+					var model;
+
+					if ( _.isObject( item ) ) {
+						model = rel.relatedModel.findOrCreate( item );
+					}
+					else {
+						var attrs = {};
+						attrs[ rel.relatedModel.prototype.idAttribute ] = item;
+						model = rel.relatedModel.findOrCreate( attrs );
+					}
+
+					return model;
+				}, this );
+				
+				// Try if the 'collection' can provide a url to fetch a set of models in one request.
+				if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
+					setUrl = rel.related.url( models );
+				}
+				
+				// An assumption is that when 'Backbone.Collection.url' is a function, it can handle building of set urls.
+				// To make sure it can, test if the url we got by supplying a list of models to fetch is different from
+				// the one supplied for the default fetch action (without args to 'url').
+				if ( setUrl && setUrl !== rel.related.url() ) {
+					var opts = _.defaults(
+						{
+							error: function() {
+								var args = arguments;
+								_.each( models || [], function( model ) {
+										model.trigger( 'destroy', model, model.collection, options );
+										options.error && options.error.apply( model, args );
+									});
+							},
+							url: setUrl
+						},
+						options,
+						{ add: true }
+					);
+
+					requests = [ rel.related.fetch( opts ) ];
+				}
+				else {
+					requests = _.map( models || [], function( model ) {
+						var opts = _.defaults(
+							{
+								error: function() {
+									model.trigger( 'destroy', model, model.collection, options );
+									options.error && options.error.apply( model, arguments );
+								}
+							},
+							options
+						);
+						return model.fetch( opts );
+					}, this );
+				}
+			}
+			
+			return requests;
+		},
+		
+		set: function( key, value, options ) {
+			Backbone.Relational.eventQueue.block();
+			
+			// Duplicate backbone's behavior to allow separate key/value parameters, instead of a single 'attributes' object
+			var attributes;
+			if ( _.isObject( key ) || key == null ) {
+				attributes = key;
+				options = value;
+			}
+			else {
+				attributes = {};
+				attributes[ key ] = value;
+			}
+			
+			var result = Backbone.Model.prototype.set.apply( this, arguments );
+			
+			// Ideal place to set up relations :)
+			if ( !this._isInitialized && !this.isLocked() ) {
+				this.constructor.initializeModelHierarchy();
+
+				Backbone.Relational.store.register( this );
+
+				this.initializeRelations();
+			}
+			// Update the 'idAttribute' in Backbone.store if; we don't want it to miss an 'id' update due to {silent:true}
+			else if ( attributes && this.idAttribute in attributes ) {
+				Backbone.Relational.store.update( this );
+			}
+			
+			if ( attributes ) {
+				this.updateRelations( options );
+			}
+			
+			// Try to run the global queue holding external events
+			Backbone.Relational.eventQueue.unblock();
+			
+			return result;
+		},
+		
+		unset: function( attribute, options ) {
+			Backbone.Relational.eventQueue.block();
+			
+			var result = Backbone.Model.prototype.unset.apply( this, arguments );
+			this.updateRelations( options );
+			
+			// Try to run the global queue holding external events
+			Backbone.Relational.eventQueue.unblock();
+			
+			return result;
+		},
+		
+		clear: function( options ) {
+			Backbone.Relational.eventQueue.block();
+			
+			var result = Backbone.Model.prototype.clear.apply( this, arguments );
+			this.updateRelations( options );
+			
+			// Try to run the global queue holding external events
+			Backbone.Relational.eventQueue.unblock();
+			
+			return result;
+		},
+
+		clone: function() {
+			var attributes = _.clone( this.attributes );
+			if ( !_.isUndefined( attributes[ this.idAttribute ] ) ) {
+				attributes[ this.idAttribute ] = null;
+			}
+
+			_.each( this.getRelations() || [], function( rel ) {
+					delete attributes[ rel.key ];
+				});
+
+			return new this.constructor( attributes );
+		},
+		
+		/**
+		 * Convert relations to JSON, omits them when required
+		 */
+		toJSON: function(options) {
+			// If this Model has already been fully serialized in this branch once, return to avoid loops
+			if ( this.isLocked() ) {
+				return this.id;
+			}
+			
+			this.acquire();
+			var json = Backbone.Model.prototype.toJSON.call( this, options );
+			
+			if ( this.constructor._superModel && !( this.constructor._subModelTypeAttribute in json ) ) {
+				json[ this.constructor._subModelTypeAttribute ] = this.constructor._subModelTypeValue;
+			}
+			
+			_.each( this._relations || [], function( rel ) {
+					var value = json[ rel.key ];
+
+					if ( rel.options.includeInJSON === true) {
+						if ( value && _.isFunction( value.toJSON ) ) {
+							json[ rel.keyDestination ] = value.toJSON( options );
+						}
+						else {
+							json[ rel.keyDestination ] = null;
+						}
+					}
+					else if ( _.isString( rel.options.includeInJSON ) ) {
+						if ( value instanceof Backbone.Collection ) {
+							json[ rel.keyDestination ] = value.pluck( rel.options.includeInJSON );
+						}
+						else if ( value instanceof Backbone.Model ) {
+							json[ rel.keyDestination ] = value.get( rel.options.includeInJSON );
+						}	
+						else {
+							json[ rel.keyDestination ] = null;
+						}
+					}
+					else if ( _.isArray( rel.options.includeInJSON ) ) {
+						if ( value instanceof Backbone.Collection ) {
+							var valueSub = [];
+							value.each( function( model ) {
+								var curJson = {};
+								_.each( rel.options.includeInJSON, function( key ) {
+									curJson[ key ] = model.get( key );
+								});
+								valueSub.push( curJson );
+							});
+							json[ rel.keyDestination ] = valueSub;
+						}
+						else if ( value instanceof Backbone.Model ) {
+							var valueSub = {};
+							_.each( rel.options.includeInJSON, function( key ) {
+								valueSub[ key ] = value.get( key );
+							});
+							json[ rel.keyDestination ] = valueSub;
+						}
+						else {
+							json[ rel.keyDestination ] = null;
+						}
+					}
+					else {
+						delete json[ rel.key ];
+					}
+
+					if ( rel.keyDestination !== rel.key ) {
+						delete json[ rel.key ];
+					}
+				});
+			
+			this.release();
+			return json;
+		}
+	},
+	{
+		setup: function( superModel ) {
+			// We don't want to share a relations array with a parent, as this will cause problems with
+			// reverse relations.
+			this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
+
+			this._subModels = {};
+			this._superModel = null;
+
+			// If this model has 'subModelTypes' itself, remember them in the store
+			if ( this.prototype.hasOwnProperty( 'subModelTypes' ) ) {
+				Backbone.Relational.store.addSubModels( this.prototype.subModelTypes, this );
+			}
+			// The 'subModelTypes' property should not be inherited, so reset it.
+			else {
+				this.prototype.subModelTypes = null;
+			}
+
+			// Initialize all reverseRelations that belong to this new model.
+			_.each( this.prototype.relations || [], function( rel ) {
+					if ( !rel.model ) {
+						rel.model = this;
+					}
+
+					if ( rel.reverseRelation && rel.model === this ) {				
+						var preInitialize = true;
+						if ( _.isString( rel.relatedModel ) ) {
+							/**
+							 * The related model might not be defined for two reasons
+							 *  1. it never gets defined, e.g. a typo
+							 *  2. it is related to itself
+							 * In neither of these cases do we need to pre-initialize reverse relations.
+							 */
+							var relatedModel = Backbone.Relational.store.getObjectByName( rel.relatedModel );
+							preInitialize = relatedModel && ( relatedModel.prototype instanceof Backbone.RelationalModel );
+						}
+
+						var type = !_.isString( rel.type ) ? rel.type : Backbone[ rel.type ] || Backbone.Relational.store.getObjectByName( rel.type );
+						if ( preInitialize && type && type.prototype instanceof Backbone.Relation ) {
+							new type( null, rel );
+						}
+					}
+				}, this );
+			
+			return this;
+		},
+
+		/**
+		 * Create a 'Backbone.Model' instance based on 'attributes'.
+		 * @param {Object} attributes
+		 * @param {Object} [options]
+		 * @return {Backbone.Model}
+		 */
+		build: function( attributes, options ) {
+			var model = this;
+
+			// 'build' is a possible entrypoint; it's possible no model hierarchy has been determined yet.
+			this.initializeModelHierarchy();
+
+			// Determine what type of (sub)model should be built if applicable.
+			// Lookup the proper subModelType in 'this._subModels'.
+			if ( this._subModels && this.prototype.subModelTypeAttribute in attributes ) {
+				var subModelTypeAttribute = attributes[ this.prototype.subModelTypeAttribute ];
+				var subModelType = this._subModels[ subModelTypeAttribute ];
+				if ( subModelType ) {
+					model = subModelType;
+				}
+			}
+			
+			return new model( attributes, options );
+		},
+
+		initializeModelHierarchy: function() {
+			// If we're here for the first time, try to determine if this modelType has a 'superModel'.
+			if ( _.isUndefined( this._superModel ) || _.isNull( this._superModel ) ) {
+				Backbone.Relational.store.setupSuperModel( this );
+
+				// If a superModel has been found, copy relations from the _superModel if they haven't been
+				// inherited automatically (due to a redefinition of 'relations').
+				// Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail
+				// the isUndefined/isNull check next time.
+				if ( this._superModel ) {
+					//
+					if ( this._superModel.prototype.relations ) {
+						var supermodelRelationsExist = _.any( this.prototype.relations || [], function( rel ) {
+							return rel.model && rel.model !== this;
+						}, this );
+
+						if ( !supermodelRelationsExist ) {
+							this.prototype.relations = this._superModel.prototype.relations.concat( this.prototype.relations );
+						}
+					}
+				}
+				else {
+					this._superModel = false;
+				}
+			}
+
+			// If we came here through 'build' for a model that has 'subModelTypes', and not all of them have been resolved yet, try to resolve each.
+			if ( this.prototype.subModelTypes && _.keys( this.prototype.subModelTypes ).length !== _.keys( this._subModels ).length ) {
+				_.each( this.prototype.subModelTypes || [], function( subModelTypeName ) {
+					var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
+					subModelType && subModelType.initializeModelHierarchy();
+				});
+			}
+		},
+
+		/**
+		 * Find an instance of `this` type in 'Backbone.Relational.store'.
+		 * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
+		 * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`. 
+		 *   Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
+		 * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
+		 * @param {Object} [options]
+		 * @param {Boolean} [options.create=true]
+		 * @param {Boolean} [options.update=true]
+		 * @return {Backbone.RelationalModel}
+		 */
+		findOrCreate: function( attributes, options ) {
+			options || ( options = {} );
+			var parsedAttributes = (_.isObject( attributes ) && this.prototype.parse) ? this.prototype.parse( attributes ) : attributes;
+			// Try to find an instance of 'this' model type in the store
+			var model = Backbone.Relational.store.find( this, parsedAttributes );
+
+			// If we found an instance, update it with the data in 'item' (unless 'options.update' is false).
+			// If not, create an instance (unless 'options.create' is false).
+			if ( _.isObject( attributes ) ) {
+				if ( model && options.update !== false ) {
+					model.set( parsedAttributes, options );
+				}
+				else if ( !model && options.create !== false ) {
+					model = this.build( attributes, options );
+				}
+			}
+
+			return model;
+		}
+	});
+	_.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
+	
+	/**
+	 * Override Backbone.Collection._prepareModel, so objects will be built using the correct type
+	 * if the collection.model has subModels.
+	 */
+	Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
+	Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
+		var model;
+		
+		if ( attrs instanceof Backbone.Model ) {
+			if ( !attrs.collection ) {
+				attrs.collection = this;
+			}
+			model = attrs;
+		}
+		else {
+			options || (options = {});
+			options.collection = this;
+			
+			if ( typeof this.model.findOrCreate !== 'undefined' ) {
+				model = this.model.findOrCreate( attrs, options );
+			}
+			else {
+				model = new this.model( attrs, options );
+			}
+			
+			if ( !model._validate( attrs, options ) ) {
+				model = false;
+			}
+		}
+		
+		return model;
+	};
+
+	
+	/**
+	 * Override Backbone.Collection.add, so objects fetched from the server multiple times will
+	 * update the existing Model. Also, trigger 'relational:add'.
+	 */
+	var add = Backbone.Collection.prototype.__add = Backbone.Collection.prototype.add;
+	Backbone.Collection.prototype.add = function( models, options ) {
+		options || (options = {});
+		if ( !_.isArray( models ) ) {
+			models = [ models ];
+		}
+
+		var modelsToAdd = [];
+
+		//console.debug( 'calling add on coll=%o; model=%o, options=%o', this, models, options );
+		_.each( models || [], function( model ) {
+			if ( !( model instanceof Backbone.Model ) ) {
+				// `_prepareModel` attempts to find `model` in Backbone.store through `findOrCreate`,
+				// and sets the new properties on it if is found. Otherwise, a new model is instantiated.
+				model = Backbone.Collection.prototype._prepareModel.call( this, model, options );
+			}
+
+				if ( model instanceof Backbone.Model && !this.get( model ) ) {
+					modelsToAdd.push( model );
+				}
+			}, this );
+
+		// Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
+		if ( modelsToAdd.length ) {
+			add.call( this, modelsToAdd, options );
+
+			_.each( modelsToAdd || [], function( model ) {
+				this.trigger( 'relational:add', model, this, options );
+			}, this );
+		}
+		
+		return this;
+	};
+	
+	/**
+	 * Override 'Backbone.Collection.remove' to trigger 'relational:remove'.
+	 */
+	var remove = Backbone.Collection.prototype.__remove = Backbone.Collection.prototype.remove;
+	Backbone.Collection.prototype.remove = function( models, options ) {
+		options || (options = {});
+		if ( !_.isArray( models ) ) {
+			models = [ models ];
+		}
+		else {
+			models = models.slice( 0 );
+		}
+
+		//console.debug('calling remove on coll=%o; models=%o, options=%o', this, models, options );
+		_.each( models || [], function( model ) {
+				model = this.get( model );
+
+				if ( model instanceof Backbone.Model ) {
+					remove.call( this, model, options );
+					this.trigger('relational:remove', model, this, options);
+				}
+			}, this );
+		
+		return this;
+	};
+
+	/**
+	 * Override 'Backbone.Collection.reset' to trigger 'relational:reset'.
+	 */
+	var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
+	Backbone.Collection.prototype.reset = function( models, options ) {
+		reset.call( this, models, options );
+		this.trigger( 'relational:reset', this, options );
+
+		return this;
+	};
+
+	/**
+	 * Override 'Backbone.Collection.sort' to trigger 'relational:reset'.
+	 */
+	var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
+	Backbone.Collection.prototype.sort = function( options ) {
+		sort.call( this, options );
+		this.trigger( 'relational:reset', this, options );
+
+		return this;
+	};
+	
+	/**
+	 * Override 'Backbone.Collection.trigger' so 'add', 'remove' and 'reset' events are queued until relations
+	 * are ready.
+	 */
+	var trigger = Backbone.Collection.prototype.__trigger = Backbone.Collection.prototype.trigger;
+	Backbone.Collection.prototype.trigger = function( eventName ) {
+		if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' ) {
+			var dit = this, args = arguments;
+			
+			if (eventName === 'add') {
+				args = _.toArray( args );
+				// the fourth argument in case of a regular add is the option object.
+				// we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
+				if (_.isObject( args[3] ) ) {
+					args[3] = _.clone( args[3] );
+				}
+			}
+			
+			Backbone.Relational.eventQueue.add( function() {
+					trigger.apply( dit, args );
+				});
+		}
+		else {
+			trigger.apply( this, arguments );
+		}
+		
+		return this;
+	};
+
+	// Override .extend() to automatically call .setup()
+	Backbone.RelationalModel.extend = function( protoProps, classProps ) {
+		var child = Backbone.Model.extend.apply( this, arguments );
+		
+		child.setup( this );
+
+		return child;
+	};
+})();
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore-min.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore-min.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore-min.js	(revision 9305)
@@ -0,0 +1,1 @@
+(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,d=e.filter,g=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.4";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:d&&n.filter===d?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:g&&n.every===g?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2),e=w.isFunction(t);return w.map(n,function(n){return(e?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t,r){return w.isEmpty(t)?r?null:[]:w[r?"find":"filter"](n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.findWhere=function(n,t){return w.where(n,t,!0)},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var k=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=k(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index<t.index?-1:1}),"value")};var F=function(n,t,r,e){var u={},i=k(t||w.identity);return A(n,function(t,a){var o=i.call(r,t,a,n);e(u,o,t)}),u};w.groupBy=function(n,t,r){return F(n,t,r,function(n,t,r){(w.has(n,t)?n[t]:n[t]=[]).push(r)})},w.countBy=function(n,t,r){return F(n,t,r,function(n,t){w.has(n,t)||(n[t]=0),n[t]++})},w.sortedIndex=function(n,t,r,e){r=null==r?w.identity:k(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i},w.bind=function(n,t){if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));var r=o.call(arguments,2);return function(){return n.apply(t,r.concat(o.call(arguments)))}},w.partial=function(n){var t=o.call(arguments,1);return function(){return n.apply(this,t.concat(o.call(arguments)))}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var I=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=I(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&I(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return I(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var M={escape:{"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","/":"&#x2F;"}};M.unescape=w.invert(M.escape);var S={escape:RegExp("["+w.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(M.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(S[n],function(t){return M[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),D.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,q={"'":"'","\\":"\\","\r":"r","\n":"n","	":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||T).source,(r.interpolate||T).source,(r.evaluate||T).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(B,function(n){return"\\"+q[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var D=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],D.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return D.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this);
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/backbone/underscore.js	(revision 9305)
@@ -0,0 +1,1226 @@
+//     Underscore.js 1.4.4
+//     http://underscorejs.org
+//     (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
+//     Underscore may be freely distributed under the MIT license.
+
+(function() {
+
+  // Baseline setup
+  // --------------
+
+  // Establish the root object, `window` in the browser, or `global` on the server.
+  var root = this;
+
+  // Save the previous value of the `_` variable.
+  var previousUnderscore = root._;
+
+  // Establish the object that gets returned to break out of a loop iteration.
+  var breaker = {};
+
+  // Save bytes in the minified (but not gzipped) version:
+  var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+  // Create quick reference variables for speed access to core prototypes.
+  var push             = ArrayProto.push,
+      slice            = ArrayProto.slice,
+      concat           = ArrayProto.concat,
+      toString         = ObjProto.toString,
+      hasOwnProperty   = ObjProto.hasOwnProperty;
+
+  // All **ECMAScript 5** native function implementations that we hope to use
+  // are declared here.
+  var
+    nativeForEach      = ArrayProto.forEach,
+    nativeMap          = ArrayProto.map,
+    nativeReduce       = ArrayProto.reduce,
+    nativeReduceRight  = ArrayProto.reduceRight,
+    nativeFilter       = ArrayProto.filter,
+    nativeEvery        = ArrayProto.every,
+    nativeSome         = ArrayProto.some,
+    nativeIndexOf      = ArrayProto.indexOf,
+    nativeLastIndexOf  = ArrayProto.lastIndexOf,
+    nativeIsArray      = Array.isArray,
+    nativeKeys         = Object.keys,
+    nativeBind         = FuncProto.bind;
+
+  // Create a safe reference to the Underscore object for use below.
+  var _ = function(obj) {
+    if (obj instanceof _) return obj;
+    if (!(this instanceof _)) return new _(obj);
+    this._wrapped = obj;
+  };
+
+  // Export the Underscore object for **Node.js**, with
+  // backwards-compatibility for the old `require()` API. If we're in
+  // the browser, add `_` as a global object via a string identifier,
+  // for Closure Compiler "advanced" mode.
+  if (typeof exports !== 'undefined') {
+    if (typeof module !== 'undefined' && module.exports) {
+      exports = module.exports = _;
+    }
+    exports._ = _;
+  } else {
+    root._ = _;
+  }
+
+  // Current version.
+  _.VERSION = '1.4.4';
+
+  // Collection Functions
+  // --------------------
+
+  // The cornerstone, an `each` implementation, aka `forEach`.
+  // Handles objects with the built-in `forEach`, arrays, and raw objects.
+  // Delegates to **ECMAScript 5**'s native `forEach` if available.
+  var each = _.each = _.forEach = function(obj, iterator, context) {
+    if (obj == null) return;
+    if (nativeForEach && obj.forEach === nativeForEach) {
+      obj.forEach(iterator, context);
+    } else if (obj.length === +obj.length) {
+      for (var i = 0, l = obj.length; i < l; i++) {
+        if (iterator.call(context, obj[i], i, obj) === breaker) return;
+      }
+    } else {
+      for (var key in obj) {
+        if (_.has(obj, key)) {
+          if (iterator.call(context, obj[key], key, obj) === breaker) return;
+        }
+      }
+    }
+  };
+
+  // Return the results of applying the iterator to each element.
+  // Delegates to **ECMAScript 5**'s native `map` if available.
+  _.map = _.collect = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+    each(obj, function(value, index, list) {
+      results[results.length] = iterator.call(context, value, index, list);
+    });
+    return results;
+  };
+
+  var reduceError = 'Reduce of empty array with no initial value';
+
+  // **Reduce** builds up a single result from a list of values, aka `inject`,
+  // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduce && obj.reduce === nativeReduce) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+    }
+    each(obj, function(value, index, list) {
+      if (!initial) {
+        memo = value;
+        initial = true;
+      } else {
+        memo = iterator.call(context, memo, value, index, list);
+      }
+    });
+    if (!initial) throw new TypeError(reduceError);
+    return memo;
+  };
+
+  // The right-associative version of reduce, also known as `foldr`.
+  // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+    var initial = arguments.length > 2;
+    if (obj == null) obj = [];
+    if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+      if (context) iterator = _.bind(iterator, context);
+      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+    }
+    var length = obj.length;
+    if (length !== +length) {
+      var keys = _.keys(obj);
+      length = keys.length;
+    }
+    each(obj, function(value, index, list) {
+      index = keys ? keys[--length] : --length;
+      if (!initial) {
+        memo = obj[index];
+        initial = true;
+      } else {
+        memo = iterator.call(context, memo, obj[index], index, list);
+      }
+    });
+    if (!initial) throw new TypeError(reduceError);
+    return memo;
+  };
+
+  // Return the first value which passes a truth test. Aliased as `detect`.
+  _.find = _.detect = function(obj, iterator, context) {
+    var result;
+    any(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) {
+        result = value;
+        return true;
+      }
+    });
+    return result;
+  };
+
+  // Return all the elements that pass a truth test.
+  // Delegates to **ECMAScript 5**'s native `filter` if available.
+  // Aliased as `select`.
+  _.filter = _.select = function(obj, iterator, context) {
+    var results = [];
+    if (obj == null) return results;
+    if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+    each(obj, function(value, index, list) {
+      if (iterator.call(context, value, index, list)) results[results.length] = value;
+    });
+    return results;
+  };
+
+  // Return all the elements for which a truth test fails.
+  _.reject = function(obj, iterator, context) {
+    return _.filter(obj, function(value, index, list) {
+      return !iterator.call(context, value, index, list);
+    }, context);
+  };
+
+  // Determine whether all of the elements match a truth test.
+  // Delegates to **ECMAScript 5**'s native `every` if available.
+  // Aliased as `all`.
+  _.every = _.all = function(obj, iterator, context) {
+    iterator || (iterator = _.identity);
+    var result = true;
+    if (obj == null) return result;
+    if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+    each(obj, function(value, index, list) {
+      if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+    });
+    return !!result;
+  };
+
+  // Determine if at least one element in the object matches a truth test.
+  // Delegates to **ECMAScript 5**'s native `some` if available.
+  // Aliased as `any`.
+  var any = _.some = _.any = function(obj, iterator, context) {
+    iterator || (iterator = _.identity);
+    var result = false;
+    if (obj == null) return result;
+    if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+    each(obj, function(value, index, list) {
+      if (result || (result = iterator.call(context, value, index, list))) return breaker;
+    });
+    return !!result;
+  };
+
+  // Determine if the array or object contains a given value (using `===`).
+  // Aliased as `include`.
+  _.contains = _.include = function(obj, target) {
+    if (obj == null) return false;
+    if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+    return any(obj, function(value) {
+      return value === target;
+    });
+  };
+
+  // Invoke a method (with arguments) on every item in a collection.
+  _.invoke = function(obj, method) {
+    var args = slice.call(arguments, 2);
+    var isFunc = _.isFunction(method);
+    return _.map(obj, function(value) {
+      return (isFunc ? method : value[method]).apply(value, args);
+    });
+  };
+
+  // Convenience version of a common use case of `map`: fetching a property.
+  _.pluck = function(obj, key) {
+    return _.map(obj, function(value){ return value[key]; });
+  };
+
+  // Convenience version of a common use case of `filter`: selecting only objects
+  // containing specific `key:value` pairs.
+  _.where = function(obj, attrs, first) {
+    if (_.isEmpty(attrs)) return first ? null : [];
+    return _[first ? 'find' : 'filter'](obj, function(value) {
+      for (var key in attrs) {
+        if (attrs[key] !== value[key]) return false;
+      }
+      return true;
+    });
+  };
+
+  // Convenience version of a common use case of `find`: getting the first object
+  // containing specific `key:value` pairs.
+  _.findWhere = function(obj, attrs) {
+    return _.where(obj, attrs, true);
+  };
+
+  // Return the maximum element or (element-based computation).
+  // Can't optimize arrays of integers longer than 65,535 elements.
+  // See: https://bugs.webkit.org/show_bug.cgi?id=80797
+  _.max = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+      return Math.max.apply(Math, obj);
+    }
+    if (!iterator && _.isEmpty(obj)) return -Infinity;
+    var result = {computed : -Infinity, value: -Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed >= result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Return the minimum element (or element-based computation).
+  _.min = function(obj, iterator, context) {
+    if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
+      return Math.min.apply(Math, obj);
+    }
+    if (!iterator && _.isEmpty(obj)) return Infinity;
+    var result = {computed : Infinity, value: Infinity};
+    each(obj, function(value, index, list) {
+      var computed = iterator ? iterator.call(context, value, index, list) : value;
+      computed < result.computed && (result = {value : value, computed : computed});
+    });
+    return result.value;
+  };
+
+  // Shuffle an array.
+  _.shuffle = function(obj) {
+    var rand;
+    var index = 0;
+    var shuffled = [];
+    each(obj, function(value) {
+      rand = _.random(index++);
+      shuffled[index - 1] = shuffled[rand];
+      shuffled[rand] = value;
+    });
+    return shuffled;
+  };
+
+  // An internal function to generate lookup iterators.
+  var lookupIterator = function(value) {
+    return _.isFunction(value) ? value : function(obj){ return obj[value]; };
+  };
+
+  // Sort the object's values by a criterion produced by an iterator.
+  _.sortBy = function(obj, value, context) {
+    var iterator = lookupIterator(value);
+    return _.pluck(_.map(obj, function(value, index, list) {
+      return {
+        value : value,
+        index : index,
+        criteria : iterator.call(context, value, index, list)
+      };
+    }).sort(function(left, right) {
+      var a = left.criteria;
+      var b = right.criteria;
+      if (a !== b) {
+        if (a > b || a === void 0) return 1;
+        if (a < b || b === void 0) return -1;
+      }
+      return left.index < right.index ? -1 : 1;
+    }), 'value');
+  };
+
+  // An internal function used for aggregate "group by" operations.
+  var group = function(obj, value, context, behavior) {
+    var result = {};
+    var iterator = lookupIterator(value || _.identity);
+    each(obj, function(value, index) {
+      var key = iterator.call(context, value, index, obj);
+      behavior(result, key, value);
+    });
+    return result;
+  };
+
+  // Groups the object's values by a criterion. Pass either a string attribute
+  // to group by, or a function that returns the criterion.
+  _.groupBy = function(obj, value, context) {
+    return group(obj, value, context, function(result, key, value) {
+      (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
+    });
+  };
+
+  // Counts instances of an object that group by a certain criterion. Pass
+  // either a string attribute to count by, or a function that returns the
+  // criterion.
+  _.countBy = function(obj, value, context) {
+    return group(obj, value, context, function(result, key) {
+      if (!_.has(result, key)) result[key] = 0;
+      result[key]++;
+    });
+  };
+
+  // Use a comparator function to figure out the smallest index at which
+  // an object should be inserted so as to maintain order. Uses binary search.
+  _.sortedIndex = function(array, obj, iterator, context) {
+    iterator = iterator == null ? _.identity : lookupIterator(iterator);
+    var value = iterator.call(context, obj);
+    var low = 0, high = array.length;
+    while (low < high) {
+      var mid = (low + high) >>> 1;
+      iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
+    }
+    return low;
+  };
+
+  // Safely convert anything iterable into a real, live array.
+  _.toArray = function(obj) {
+    if (!obj) return [];
+    if (_.isArray(obj)) return slice.call(obj);
+    if (obj.length === +obj.length) return _.map(obj, _.identity);
+    return _.values(obj);
+  };
+
+  // Return the number of elements in an object.
+  _.size = function(obj) {
+    if (obj == null) return 0;
+    return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
+  };
+
+  // Array Functions
+  // ---------------
+
+  // Get the first element of an array. Passing **n** will return the first N
+  // values in the array. Aliased as `head` and `take`. The **guard** check
+  // allows it to work with `_.map`.
+  _.first = _.head = _.take = function(array, n, guard) {
+    if (array == null) return void 0;
+    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+  };
+
+  // Returns everything but the last entry of the array. Especially useful on
+  // the arguments object. Passing **n** will return all the values in
+  // the array, excluding the last N. The **guard** check allows it to work with
+  // `_.map`.
+  _.initial = function(array, n, guard) {
+    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+  };
+
+  // Get the last element of an array. Passing **n** will return the last N
+  // values in the array. The **guard** check allows it to work with `_.map`.
+  _.last = function(array, n, guard) {
+    if (array == null) return void 0;
+    if ((n != null) && !guard) {
+      return slice.call(array, Math.max(array.length - n, 0));
+    } else {
+      return array[array.length - 1];
+    }
+  };
+
+  // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+  // Especially useful on the arguments object. Passing an **n** will return
+  // the rest N values in the array. The **guard**
+  // check allows it to work with `_.map`.
+  _.rest = _.tail = _.drop = function(array, n, guard) {
+    return slice.call(array, (n == null) || guard ? 1 : n);
+  };
+
+  // Trim out all falsy values from an array.
+  _.compact = function(array) {
+    return _.filter(array, _.identity);
+  };
+
+  // Internal implementation of a recursive `flatten` function.
+  var flatten = function(input, shallow, output) {
+    each(input, function(value) {
+      if (_.isArray(value)) {
+        shallow ? push.apply(output, value) : flatten(value, shallow, output);
+      } else {
+        output.push(value);
+      }
+    });
+    return output;
+  };
+
+  // Return a completely flattened version of an array.
+  _.flatten = function(array, shallow) {
+    return flatten(array, shallow, []);
+  };
+
+  // Return a version of the array that does not contain the specified value(s).
+  _.without = function(array) {
+    return _.difference(array, slice.call(arguments, 1));
+  };
+
+  // Produce a duplicate-free version of the array. If the array has already
+  // been sorted, you have the option of using a faster algorithm.
+  // Aliased as `unique`.
+  _.uniq = _.unique = function(array, isSorted, iterator, context) {
+    if (_.isFunction(isSorted)) {
+      context = iterator;
+      iterator = isSorted;
+      isSorted = false;
+    }
+    var initial = iterator ? _.map(array, iterator, context) : array;
+    var results = [];
+    var seen = [];
+    each(initial, function(value, index) {
+      if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
+        seen.push(value);
+        results.push(array[index]);
+      }
+    });
+    return results;
+  };
+
+  // Produce an array that contains the union: each distinct element from all of
+  // the passed-in arrays.
+  _.union = function() {
+    return _.uniq(concat.apply(ArrayProto, arguments));
+  };
+
+  // Produce an array that contains every item shared between all the
+  // passed-in arrays.
+  _.intersection = function(array) {
+    var rest = slice.call(arguments, 1);
+    return _.filter(_.uniq(array), function(item) {
+      return _.every(rest, function(other) {
+        return _.indexOf(other, item) >= 0;
+      });
+    });
+  };
+
+  // Take the difference between one array and a number of other arrays.
+  // Only the elements present in just the first array will remain.
+  _.difference = function(array) {
+    var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
+    return _.filter(array, function(value){ return !_.contains(rest, value); });
+  };
+
+  // Zip together multiple lists into a single array -- elements that share
+  // an index go together.
+  _.zip = function() {
+    var args = slice.call(arguments);
+    var length = _.max(_.pluck(args, 'length'));
+    var results = new Array(length);
+    for (var i = 0; i < length; i++) {
+      results[i] = _.pluck(args, "" + i);
+    }
+    return results;
+  };
+
+  // Converts lists into objects. Pass either a single array of `[key, value]`
+  // pairs, or two parallel arrays of the same length -- one of keys, and one of
+  // the corresponding values.
+  _.object = function(list, values) {
+    if (list == null) return {};
+    var result = {};
+    for (var i = 0, l = list.length; i < l; i++) {
+      if (values) {
+        result[list[i]] = values[i];
+      } else {
+        result[list[i][0]] = list[i][1];
+      }
+    }
+    return result;
+  };
+
+  // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+  // we need this function. Return the position of the first occurrence of an
+  // item in an array, or -1 if the item is not included in the array.
+  // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+  // If the array is large and already in sort order, pass `true`
+  // for **isSorted** to use binary search.
+  _.indexOf = function(array, item, isSorted) {
+    if (array == null) return -1;
+    var i = 0, l = array.length;
+    if (isSorted) {
+      if (typeof isSorted == 'number') {
+        i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
+      } else {
+        i = _.sortedIndex(array, item);
+        return array[i] === item ? i : -1;
+      }
+    }
+    if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
+    for (; i < l; i++) if (array[i] === item) return i;
+    return -1;
+  };
+
+  // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+  _.lastIndexOf = function(array, item, from) {
+    if (array == null) return -1;
+    var hasIndex = from != null;
+    if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
+      return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
+    }
+    var i = (hasIndex ? from : array.length);
+    while (i--) if (array[i] === item) return i;
+    return -1;
+  };
+
+  // Generate an integer Array containing an arithmetic progression. A port of
+  // the native Python `range()` function. See
+  // [the Python documentation](http://docs.python.org/library/functions.html#range).
+  _.range = function(start, stop, step) {
+    if (arguments.length <= 1) {
+      stop = start || 0;
+      start = 0;
+    }
+    step = arguments[2] || 1;
+
+    var len = Math.max(Math.ceil((stop - start) / step), 0);
+    var idx = 0;
+    var range = new Array(len);
+
+    while(idx < len) {
+      range[idx++] = start;
+      start += step;
+    }
+
+    return range;
+  };
+
+  // Function (ahem) Functions
+  // ------------------
+
+  // Create a function bound to a given object (assigning `this`, and arguments,
+  // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+  // available.
+  _.bind = function(func, context) {
+    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+    var args = slice.call(arguments, 2);
+    return function() {
+      return func.apply(context, args.concat(slice.call(arguments)));
+    };
+  };
+
+  // Partially apply a function by creating a version that has had some of its
+  // arguments pre-filled, without changing its dynamic `this` context.
+  _.partial = function(func) {
+    var args = slice.call(arguments, 1);
+    return function() {
+      return func.apply(this, args.concat(slice.call(arguments)));
+    };
+  };
+
+  // Bind all of an object's methods to that object. Useful for ensuring that
+  // all callbacks defined on an object belong to it.
+  _.bindAll = function(obj) {
+    var funcs = slice.call(arguments, 1);
+    if (funcs.length === 0) funcs = _.functions(obj);
+    each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+    return obj;
+  };
+
+  // Memoize an expensive function by storing its results.
+  _.memoize = function(func, hasher) {
+    var memo = {};
+    hasher || (hasher = _.identity);
+    return function() {
+      var key = hasher.apply(this, arguments);
+      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+    };
+  };
+
+  // Delays a function for the given number of milliseconds, and then calls
+  // it with the arguments supplied.
+  _.delay = function(func, wait) {
+    var args = slice.call(arguments, 2);
+    return setTimeout(function(){ return func.apply(null, args); }, wait);
+  };
+
+  // Defers a function, scheduling it to run after the current call stack has
+  // cleared.
+  _.defer = function(func) {
+    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+  };
+
+  // Returns a function, that, when invoked, will only be triggered at most once
+  // during a given window of time.
+  _.throttle = function(func, wait) {
+    var context, args, timeout, result;
+    var previous = 0;
+    var later = function() {
+      previous = new Date;
+      timeout = null;
+      result = func.apply(context, args);
+    };
+    return function() {
+      var now = new Date;
+      var remaining = wait - (now - previous);
+      context = this;
+      args = arguments;
+      if (remaining <= 0) {
+        clearTimeout(timeout);
+        timeout = null;
+        previous = now;
+        result = func.apply(context, args);
+      } else if (!timeout) {
+        timeout = setTimeout(later, remaining);
+      }
+      return result;
+    };
+  };
+
+  // Returns a function, that, as long as it continues to be invoked, will not
+  // be triggered. The function will be called after it stops being called for
+  // N milliseconds. If `immediate` is passed, trigger the function on the
+  // leading edge, instead of the trailing.
+  _.debounce = function(func, wait, immediate) {
+    var timeout, result;
+    return function() {
+      var context = this, args = arguments;
+      var later = function() {
+        timeout = null;
+        if (!immediate) result = func.apply(context, args);
+      };
+      var callNow = immediate && !timeout;
+      clearTimeout(timeout);
+      timeout = setTimeout(later, wait);
+      if (callNow) result = func.apply(context, args);
+      return result;
+    };
+  };
+
+  // Returns a function that will be executed at most one time, no matter how
+  // often you call it. Useful for lazy initialization.
+  _.once = function(func) {
+    var ran = false, memo;
+    return function() {
+      if (ran) return memo;
+      ran = true;
+      memo = func.apply(this, arguments);
+      func = null;
+      return memo;
+    };
+  };
+
+  // Returns the first function passed as an argument to the second,
+  // allowing you to adjust arguments, run code before and after, and
+  // conditionally execute the original function.
+  _.wrap = function(func, wrapper) {
+    return function() {
+      var args = [func];
+      push.apply(args, arguments);
+      return wrapper.apply(this, args);
+    };
+  };
+
+  // Returns a function that is the composition of a list of functions, each
+  // consuming the return value of the function that follows.
+  _.compose = function() {
+    var funcs = arguments;
+    return function() {
+      var args = arguments;
+      for (var i = funcs.length - 1; i >= 0; i--) {
+        args = [funcs[i].apply(this, args)];
+      }
+      return args[0];
+    };
+  };
+
+  // Returns a function that will only be executed after being called N times.
+  _.after = function(times, func) {
+    if (times <= 0) return func();
+    return function() {
+      if (--times < 1) {
+        return func.apply(this, arguments);
+      }
+    };
+  };
+
+  // Object Functions
+  // ----------------
+
+  // Retrieve the names of an object's properties.
+  // Delegates to **ECMAScript 5**'s native `Object.keys`
+  _.keys = nativeKeys || function(obj) {
+    if (obj !== Object(obj)) throw new TypeError('Invalid object');
+    var keys = [];
+    for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+    return keys;
+  };
+
+  // Retrieve the values of an object's properties.
+  _.values = function(obj) {
+    var values = [];
+    for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
+    return values;
+  };
+
+  // Convert an object into a list of `[key, value]` pairs.
+  _.pairs = function(obj) {
+    var pairs = [];
+    for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
+    return pairs;
+  };
+
+  // Invert the keys and values of an object. The values must be serializable.
+  _.invert = function(obj) {
+    var result = {};
+    for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
+    return result;
+  };
+
+  // Return a sorted list of the function names available on the object.
+  // Aliased as `methods`
+  _.functions = _.methods = function(obj) {
+    var names = [];
+    for (var key in obj) {
+      if (_.isFunction(obj[key])) names.push(key);
+    }
+    return names.sort();
+  };
+
+  // Extend a given object with all the properties in passed-in object(s).
+  _.extend = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      if (source) {
+        for (var prop in source) {
+          obj[prop] = source[prop];
+        }
+      }
+    });
+    return obj;
+  };
+
+  // Return a copy of the object only containing the whitelisted properties.
+  _.pick = function(obj) {
+    var copy = {};
+    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+    each(keys, function(key) {
+      if (key in obj) copy[key] = obj[key];
+    });
+    return copy;
+  };
+
+   // Return a copy of the object without the blacklisted properties.
+  _.omit = function(obj) {
+    var copy = {};
+    var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
+    for (var key in obj) {
+      if (!_.contains(keys, key)) copy[key] = obj[key];
+    }
+    return copy;
+  };
+
+  // Fill in a given object with default properties.
+  _.defaults = function(obj) {
+    each(slice.call(arguments, 1), function(source) {
+      if (source) {
+        for (var prop in source) {
+          if (obj[prop] == null) obj[prop] = source[prop];
+        }
+      }
+    });
+    return obj;
+  };
+
+  // Create a (shallow-cloned) duplicate of an object.
+  _.clone = function(obj) {
+    if (!_.isObject(obj)) return obj;
+    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+  };
+
+  // Invokes interceptor with the obj, and then returns obj.
+  // The primary purpose of this method is to "tap into" a method chain, in
+  // order to perform operations on intermediate results within the chain.
+  _.tap = function(obj, interceptor) {
+    interceptor(obj);
+    return obj;
+  };
+
+  // Internal recursive comparison function for `isEqual`.
+  var eq = function(a, b, aStack, bStack) {
+    // Identical objects are equal. `0 === -0`, but they aren't identical.
+    // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+    if (a === b) return a !== 0 || 1 / a == 1 / b;
+    // A strict comparison is necessary because `null == undefined`.
+    if (a == null || b == null) return a === b;
+    // Unwrap any wrapped objects.
+    if (a instanceof _) a = a._wrapped;
+    if (b instanceof _) b = b._wrapped;
+    // Compare `[[Class]]` names.
+    var className = toString.call(a);
+    if (className != toString.call(b)) return false;
+    switch (className) {
+      // Strings, numbers, dates, and booleans are compared by value.
+      case '[object String]':
+        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+        // equivalent to `new String("5")`.
+        return a == String(b);
+      case '[object Number]':
+        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+        // other numeric values.
+        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+      case '[object Date]':
+      case '[object Boolean]':
+        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+        // millisecond representations. Note that invalid dates with millisecond representations
+        // of `NaN` are not equivalent.
+        return +a == +b;
+      // RegExps are compared by their source patterns and flags.
+      case '[object RegExp]':
+        return a.source == b.source &&
+               a.global == b.global &&
+               a.multiline == b.multiline &&
+               a.ignoreCase == b.ignoreCase;
+    }
+    if (typeof a != 'object' || typeof b != 'object') return false;
+    // Assume equality for cyclic structures. The algorithm for detecting cyclic
+    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+    var length = aStack.length;
+    while (length--) {
+      // Linear search. Performance is inversely proportional to the number of
+      // unique nested structures.
+      if (aStack[length] == a) return bStack[length] == b;
+    }
+    // Add the first object to the stack of traversed objects.
+    aStack.push(a);
+    bStack.push(b);
+    var size = 0, result = true;
+    // Recursively compare objects and arrays.
+    if (className == '[object Array]') {
+      // Compare array lengths to determine if a deep comparison is necessary.
+      size = a.length;
+      result = size == b.length;
+      if (result) {
+        // Deep compare the contents, ignoring non-numeric properties.
+        while (size--) {
+          if (!(result = eq(a[size], b[size], aStack, bStack))) break;
+        }
+      }
+    } else {
+      // Objects with different constructors are not equivalent, but `Object`s
+      // from different frames are.
+      var aCtor = a.constructor, bCtor = b.constructor;
+      if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
+                               _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
+        return false;
+      }
+      // Deep compare objects.
+      for (var key in a) {
+        if (_.has(a, key)) {
+          // Count the expected number of properties.
+          size++;
+          // Deep compare each member.
+          if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
+        }
+      }
+      // Ensure that both objects contain the same number of properties.
+      if (result) {
+        for (key in b) {
+          if (_.has(b, key) && !(size--)) break;
+        }
+        result = !size;
+      }
+    }
+    // Remove the first object from the stack of traversed objects.
+    aStack.pop();
+    bStack.pop();
+    return result;
+  };
+
+  // Perform a deep comparison to check if two objects are equal.
+  _.isEqual = function(a, b) {
+    return eq(a, b, [], []);
+  };
+
+  // Is a given array, string, or object empty?
+  // An "empty" object has no enumerable own-properties.
+  _.isEmpty = function(obj) {
+    if (obj == null) return true;
+    if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+    for (var key in obj) if (_.has(obj, key)) return false;
+    return true;
+  };
+
+  // Is a given value a DOM element?
+  _.isElement = function(obj) {
+    return !!(obj && obj.nodeType === 1);
+  };
+
+  // Is a given value an array?
+  // Delegates to ECMA5's native Array.isArray
+  _.isArray = nativeIsArray || function(obj) {
+    return toString.call(obj) == '[object Array]';
+  };
+
+  // Is a given variable an object?
+  _.isObject = function(obj) {
+    return obj === Object(obj);
+  };
+
+  // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
+  each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
+    _['is' + name] = function(obj) {
+      return toString.call(obj) == '[object ' + name + ']';
+    };
+  });
+
+  // Define a fallback version of the method in browsers (ahem, IE), where
+  // there isn't any inspectable "Arguments" type.
+  if (!_.isArguments(arguments)) {
+    _.isArguments = function(obj) {
+      return !!(obj && _.has(obj, 'callee'));
+    };
+  }
+
+  // Optimize `isFunction` if appropriate.
+  if (typeof (/./) !== 'function') {
+    _.isFunction = function(obj) {
+      return typeof obj === 'function';
+    };
+  }
+
+  // Is a given object a finite number?
+  _.isFinite = function(obj) {
+    return isFinite(obj) && !isNaN(parseFloat(obj));
+  };
+
+  // Is the given value `NaN`? (NaN is the only number which does not equal itself).
+  _.isNaN = function(obj) {
+    return _.isNumber(obj) && obj != +obj;
+  };
+
+  // Is a given value a boolean?
+  _.isBoolean = function(obj) {
+    return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+  };
+
+  // Is a given value equal to null?
+  _.isNull = function(obj) {
+    return obj === null;
+  };
+
+  // Is a given variable undefined?
+  _.isUndefined = function(obj) {
+    return obj === void 0;
+  };
+
+  // Shortcut function for checking if an object has a given property directly
+  // on itself (in other words, not on a prototype).
+  _.has = function(obj, key) {
+    return hasOwnProperty.call(obj, key);
+  };
+
+  // Utility Functions
+  // -----------------
+
+  // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+  // previous owner. Returns a reference to the Underscore object.
+  _.noConflict = function() {
+    root._ = previousUnderscore;
+    return this;
+  };
+
+  // Keep the identity function around for default iterators.
+  _.identity = function(value) {
+    return value;
+  };
+
+  // Run a function **n** times.
+  _.times = function(n, iterator, context) {
+    var accum = Array(n);
+    for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
+    return accum;
+  };
+
+  // Return a random integer between min and max (inclusive).
+  _.random = function(min, max) {
+    if (max == null) {
+      max = min;
+      min = 0;
+    }
+    return min + Math.floor(Math.random() * (max - min + 1));
+  };
+
+  // List of HTML entities for escaping.
+  var entityMap = {
+    escape: {
+      '&': '&amp;',
+      '<': '&lt;',
+      '>': '&gt;',
+      '"': '&quot;',
+      "'": '&#x27;',
+      '/': '&#x2F;'
+    }
+  };
+  entityMap.unescape = _.invert(entityMap.escape);
+
+  // Regexes containing the keys and values listed immediately above.
+  var entityRegexes = {
+    escape:   new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
+    unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
+  };
+
+  // Functions for escaping and unescaping strings to/from HTML interpolation.
+  _.each(['escape', 'unescape'], function(method) {
+    _[method] = function(string) {
+      if (string == null) return '';
+      return ('' + string).replace(entityRegexes[method], function(match) {
+        return entityMap[method][match];
+      });
+    };
+  });
+
+  // If the value of the named property is a function then invoke it;
+  // otherwise, return it.
+  _.result = function(object, property) {
+    if (object == null) return null;
+    var value = object[property];
+    return _.isFunction(value) ? value.call(object) : value;
+  };
+
+  // Add your own custom functions to the Underscore object.
+  _.mixin = function(obj) {
+    each(_.functions(obj), function(name){
+      var func = _[name] = obj[name];
+      _.prototype[name] = function() {
+        var args = [this._wrapped];
+        push.apply(args, arguments);
+        return result.call(this, func.apply(_, args));
+      };
+    });
+  };
+
+  // Generate a unique integer id (unique within the entire client session).
+  // Useful for temporary DOM ids.
+  var idCounter = 0;
+  _.uniqueId = function(prefix) {
+    var id = ++idCounter + '';
+    return prefix ? prefix + id : id;
+  };
+
+  // By default, Underscore uses ERB-style template delimiters, change the
+  // following template settings to use alternative delimiters.
+  _.templateSettings = {
+    evaluate    : /<%([\s\S]+?)%>/g,
+    interpolate : /<%=([\s\S]+?)%>/g,
+    escape      : /<%-([\s\S]+?)%>/g
+  };
+
+  // When customizing `templateSettings`, if you don't want to define an
+  // interpolation, evaluation or escaping regex, we need one that is
+  // guaranteed not to match.
+  var noMatch = /(.)^/;
+
+  // Certain characters need to be escaped so that they can be put into a
+  // string literal.
+  var escapes = {
+    "'":      "'",
+    '\\':     '\\',
+    '\r':     'r',
+    '\n':     'n',
+    '\t':     't',
+    '\u2028': 'u2028',
+    '\u2029': 'u2029'
+  };
+
+  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
+
+  // JavaScript micro-templating, similar to John Resig's implementation.
+  // Underscore templating handles arbitrary delimiters, preserves whitespace,
+  // and correctly escapes quotes within interpolated code.
+  _.template = function(text, data, settings) {
+    var render;
+    settings = _.defaults({}, settings, _.templateSettings);
+
+    // Combine delimiters into one regular expression via alternation.
+    var matcher = new RegExp([
+      (settings.escape || noMatch).source,
+      (settings.interpolate || noMatch).source,
+      (settings.evaluate || noMatch).source
+    ].join('|') + '|$', 'g');
+
+    // Compile the template source, escaping string literals appropriately.
+    var index = 0;
+    var source = "__p+='";
+    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+      source += text.slice(index, offset)
+        .replace(escaper, function(match) { return '\\' + escapes[match]; });
+
+      if (escape) {
+        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
+      }
+      if (interpolate) {
+        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
+      }
+      if (evaluate) {
+        source += "';\n" + evaluate + "\n__p+='";
+      }
+      index = offset + match.length;
+      return match;
+    });
+    source += "';\n";
+
+    // If a variable is not specified, place data values in local scope.
+    if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+    source = "var __t,__p='',__j=Array.prototype.join," +
+      "print=function(){__p+=__j.call(arguments,'');};\n" +
+      source + "return __p;\n";
+
+    try {
+      render = new Function(settings.variable || 'obj', '_', source);
+    } catch (e) {
+      e.source = source;
+      throw e;
+    }
+
+    if (data) return render(data, _);
+    var template = function(data) {
+      return render.call(this, data, _);
+    };
+
+    // Provide the compiled function source as a convenience for precompilation.
+    template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
+
+    return template;
+  };
+
+  // Add a "chain" function, which will delegate to the wrapper.
+  _.chain = function(obj) {
+    return _(obj).chain();
+  };
+
+  // OOP
+  // ---------------
+  // If Underscore is called as a function, it returns a wrapped object that
+  // can be used OO-style. This wrapper holds altered versions of all the
+  // underscore functions. Wrapped objects may be chained.
+
+  // Helper function to continue chaining intermediate results.
+  var result = function(obj) {
+    return this._chain ? _(obj).chain() : obj;
+  };
+
+  // Add all of the Underscore functions to the wrapper object.
+  _.mixin(_);
+
+  // Add all mutator Array functions to the wrapper.
+  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+    var method = ArrayProto[name];
+    _.prototype[name] = function() {
+      var obj = this._wrapped;
+      method.apply(obj, arguments);
+      if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
+      return result.call(this, obj);
+    };
+  });
+
+  // Add all accessor Array functions to the wrapper.
+  each(['concat', 'join', 'slice'], function(name) {
+    var method = ArrayProto[name];
+    _.prototype[name] = function() {
+      return result.call(this, method.apply(this._wrapped, arguments));
+    };
+  });
+
+  _.extend(_.prototype, {
+
+    // Start chaining a wrapped Underscore object.
+    chain: function() {
+      this._chain = true;
+      return this;
+    },
+
+    // Extracts the result from a wrapped and chained object.
+    value: function() {
+      return this._wrapped;
+    }
+
+  });
+
+}).call(this);
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.controller.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.controller.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.controller.js	(revision 9305)
@@ -0,0 +1,299 @@
+﻿function GeneralController() {
+    this.variationRequestHandler = function (evt) {
+        var self = this;
+        var dialog = new VariationDialog({ model: evt.model });
+        var dialogController = {};
+        _.extend(dialogController, Backbone.Events);
+        dialogController.listenTo(dialog, 'variations-generated', function (variations) {
+            var parent = evt.model.get('parent');
+            for (var i = 0; i < variations.length; i++) {
+                var title = evt.model.get('title');
+                var offset = title.indexOf('(')
+                if (offset != -1) {
+                    title = title.substring(0, offset);
+                }
+                title += '(';
+                for (var j = 0; j < variations[i].length; j++) {
+                    title += variations[i][j].Name + " = " + variations[i][j].Value + "; ";
+                }
+                title += ')';
+                var data = { AlgorithmParameters: variations[i] };
+                if (evt.model.attributes.data.ProblemParameters) {
+                    data.ProblemParameters = evt.model.attributes.data.ProblemParameters;
+                }
+                parent.addNode({ title: title, nodeId: evt.model.get('nodeId'), data: data });
+            }
+            self.model.trigger('change');
+        });
+        dialog.render();
+    },
+    this.experimentFinishedHandler = function (experimentModel, availableExperiments, selectableGroup) {
+        var loadingDialog = new LoadingDialog({ el: $('#loadingDialog') });
+        // start the loading dialog
+        loadingDialog.setLoading(true);
+        loadingDialog.text('Saving experiment ' + experimentModel.get('title') + '...');
+        loadingDialog.render();
+        // send experiment to server
+        experimentModel.save({}, { success: function () {
+            loadingDialog.setLoading(false);
+            loadingDialog.text('Experiment successfully saved!');
+            loadingDialog.render();
+            if (availableExperiments)
+                availableExperiments.fetch({ cache: false, success: function (itm) {
+                    selectableGroup.render();
+                }
+                });
+        }
+        });
+        //loadingDialog.close();
+    }
+}
+
+function ExperimentPageController() {
+    this.refreshExperiments = function () {
+        var availableExperiments = new ExperimentList();
+        availableExperiments.fetch({ cache: false, success: function (itm) {
+            $('#experiments').empty();
+            for (var i = 0; i < availableExperiments.length; i++) {
+                new DraggableView({ model: availableExperiments.models[i], el: $('#experiments') }).render();
+            }
+        }
+        });
+    },
+    this.refreshScenarios = function () {
+        var availableScenarios = new ScenarioList();
+        availableScenarios.fetch({ cache: false, success: function (itm) {
+            var group = new DraggableGroup({ model: availableScenarios, el: $('#algorithms') });
+            group.render();
+        }
+        });
+    },
+    this.create = function () {
+        var self = this;
+        var generalController = new GeneralController();
+        // create all models + required view for the experiment page
+        var model = new ExperimentNode({
+            title: 'Experiment',
+            key: 0,
+            isExperiment: true
+        }, { parse: true });
+        generalController.model = model;
+        var experimentModel = new Experiment({ experiment: model });
+
+        var view = new ExperimentTreeView({ model: model, el: $('#container') });
+        view.render();
+
+        this.refreshScenarios();
+
+        this.refreshExperiments();
+
+        var wizard = new StepWizardView({ el: $('#stepWizard') });
+        wizard.render();
+
+        var detailsView = new ExperimentDetailsTreeView({ model: model, el: $('#container2') });
+        var validationView = new ValidationHintsView({ model: { header: "Validation failed!", text: ""} });
+        var parameterDialog = new ParameterDialog({ model: model, el: $('#parameterDialog') });
+
+        var wizardController = {};
+        _.extend(wizardController, Backbone.Events);
+
+        wizardController.listenTo(model, 'change', function () {
+            // update the details view, when the model changes
+            view.render();
+            detailsView.render();
+        });
+
+
+        wizardController.listenTo(wizard, 'experiment-finished', function () {
+            generalController.experimentFinishedHandler(experimentModel, null, null);
+            self.refreshExperiments();
+            /*// start the loading dialog
+            loadingDialog.setLoading(true);
+            loadingDialog.text('Saving experiment ' + experimentModel.get('title') + '...');
+            loadingDialog.render();
+            // send experiment to server
+            experimentModel.save({}, { success: function () {
+            loadingDialog.setLoading(false);
+            loadingDialog.text('Experiment successfully saved!');
+            loadingDialog.render();
+            self.refreshExperiments();
+            }
+            });*/
+            //loadingDialog.close();
+        });
+
+        wizardController.listenTo(view, 'structure-changed', function (node) {
+            view.render();
+            detailsView.render();
+        });
+
+        wizardController.listenTo(view, 'node-added', function (node) {
+            // get details about the node
+            node.fetch({ success: function (itm) {
+                model.trigger('change', {});
+            }
+            });
+        });
+
+        wizardController.listenTo(detailsView, 'node-clicked', function (node) {
+            // get details about the node
+            var modelNode = ExperimentNode.lookup(node.data.key);
+            if (!modelNode.get('isExperiment')) {
+                parameterDialog.model = modelNode;
+                parameterDialog.render();
+            }
+        });
+
+        wizardController.listenTo(parameterDialog, 'parameters-finished', function (node) {
+            parameterDialog.close();
+        });
+
+        wizardController.listenTo(view, 'variation-request', function (evt) {
+            generalController.variationRequestHandler(evt);
+        });
+
+        var experimentDetailsView = new ExperimentDetailsView({ el: $('#step3'), model: experimentModel });
+    }
+}
+
+
+
+/// ==================== EDIT PAGE =========================
+/// ==================== EDIT PAGE =========================
+/// ==================== EDIT PAGE =========================
+
+function ExperimentEditPageController() {
+    this.refreshScenarios = function () {
+        var availableScenarios = new ScenarioList();
+        availableScenarios.fetch({ cache: false, success: function (itm) {
+            var group = new DraggableGroup({ model: availableScenarios, el: $('#draggableAlgorithms') });
+            group.render();
+            /*$('#algorithms').empty();
+            for (var i = 0; i < availableScenarios.length; i++) {
+            new DraggableView({ model: availableScenarios.models[i], el: $('#algorithms') }).render();
+            }*/
+        }
+        });
+    },
+    this.create = function () {
+        var generalController = new GeneralController();
+
+        var self = this;
+        // create all models + required view for the experiment page
+        var controllerModel = new ControllerModel();
+        var model = new ExperimentNode({
+            title: 'Experiment',
+            key: 0,
+            isExperiment: true
+        }, { parse: true });
+
+        generalController.model = model;
+        var experimentModel = new Experiment({ experiment: model });
+        var experimentDetailsView = new ExperimentDetailsView({ el: $('#step4'), model: experimentModel });
+        experimentDetailsView.render();
+
+        var availableExperiments = new ExperimentList();
+        var selectableGroup = null;
+        var wizardController = {};
+        _.extend(wizardController, Backbone.Events);
+
+        this.refreshScenarios();
+
+        availableExperiments.fetch({ cache: false, success: function (itm) {
+            selectableGroup = new SelectableGroup({ model: availableExperiments, el: $('#experiments') });
+            selectableGroup.render();
+            var draggableExperiments = new DraggableGroup({ model: availableExperiments, el: $('#draggableExperiments') });
+            draggableExperiments.render();
+
+            wizardController.listenTo(selectableGroup, 'node-selected', function (nodeId) {
+                var node = availableExperiments.find(function (itm) { return itm.get('nodeId') == nodeId; });
+                var newNode = new ExperimentNode(node.attributes);
+                experimentModel.set({
+                    experiment: newNode,
+                    title: node.get('title'),
+                    nodeId: node.get('nodeId'),
+                    run: node.get('run'),
+                    repititions: node.get('repititions'),
+                    group: node.get('group')
+                });
+                controllerModel.set({ selectedNode: newNode });
+                detailsView.model = newNode;
+                detailsView.render();
+                view.model = newNode;
+                view.render();
+                experimentDetailsView.render();
+            }
+            );
+        }
+        });
+
+        // create wizard with 4 steps
+        var wizard = new StepWizardView({ el: $('#stepWizard') });
+        wizard.render();
+
+        var view = new ExperimentTreeView({ model: model, el: $('#container') });
+        view.render();
+        var validationView = new ValidationHintsView({ model: { header: "Validation failed!", text: ""} });
+
+        var detailsView = new ExperimentDetailsTreeView({ model: model, el: $('#container2') });
+
+        var parameterDialog = new ParameterDialog({ model: model, el: $('#parameterDialog') });
+        var loadingDialog = new LoadingDialog({ el: $('#loadingDialog') });
+
+        wizardController.listenTo(wizard, 'step-validated', function (validationModel) {
+            // update the details view
+            if (validationModel.stepNumber == 1) {
+                if (controllerModel.get('selectedNode') == null) {
+                    validationView.model["text"] = 'Please select an experiment to continue!';
+                    validationView.render();
+                    validationModel.succeeded = false;
+                }
+            }
+            detailsView.render();
+        });
+
+        wizardController.listenTo(model, 'change', function () {
+            // update the details view, when the model changes
+            view.render();
+            detailsView.render();
+        });
+
+        wizardController.listenTo(view, 'structure-changed', function (node) {
+            view.render();
+        });
+
+        wizardController.listenTo(view, 'node-added', function (node) {
+            // get details about the node
+            //if (!node.get('isExperiment'))
+            node.fetch({ success: function (itm) {
+                model.trigger('change', {});
+            }
+            });
+        });
+
+        wizardController.listenTo(wizard, 'experiment-finished', function () {
+            generalController.experimentFinishedHandler(experimentModel, availableExperiments, selectableGroup);          
+        });
+
+        wizardController.listenTo(detailsView, 'node-clicked', function (node) {
+            // get details about the node
+            var modelNode = ExperimentNode.lookup(node.data.key);
+            if (!modelNode.get('isExperiment')) {
+                parameterDialog.model = modelNode;
+                parameterDialog.render();
+            }
+        });
+
+        wizardController.listenTo(view, 'variation-request', function (evt) {
+            generalController.variationRequestHandler(evt);
+        });
+
+        wizardController.listenTo(parameterDialog, 'parameters-finished', function (node) {
+            parameterDialog.close();
+        });
+    }
+}
+
+
+ 
+ 
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.model.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.model.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.model.js	(revision 9305)
@@ -0,0 +1,119 @@
+﻿//================== MODELS =====================
+var ControllerModel = Backbone.Model.extend({
+  defaults: {
+    selectedNode: null  
+  }
+});
+
+var Experiment = Backbone.RelationalModel.extend({   
+  url: function () {
+    return '/Experiment/Experiment?nodeId=' + this.get('nodeId');
+  },
+  defaults: {
+    title: 'My Experiment',
+    isExperiment: true,
+    run: false,
+    nodeId: null,
+    repititions: 1,
+    group: 'TESTAZURE'
+  },
+  isNew: function() { return this.get('nodeId') == null; },
+  relations: [{
+    key: 'experiment',
+    relatedModel: 'ExperimentNode'
+  }]
+});
+
+var ExperimentNode = Backbone.RelationalModel.extend({
+    defaults: {
+        title: 'Experiment',
+        key: -1,
+        data: {},
+        parent: null,
+        isExperiment: false,
+        nodeId: null
+    },
+    relations: [{
+        type: Backbone.HasMany,
+        key: 'children',
+        relatedModel: 'ExperimentNode',
+        collectionType: 'ScenarioList',
+        reverseRelation: {
+            key: 'parent'
+        }
+    }],
+    url: function () {
+        return this.get('isExperiment') ? '/Experiment/Experiment?nodeId=' + this.get('nodeId') : '/Experiment/Scenario?nodeId=' + this.get('nodeId');
+    },
+    initialize: function (spec) {
+        if (typeof spec.key === 'undefined' || spec.key == -1) {
+            spec.key = ExperimentNode.globalKeyCount;
+            this.set({ key: spec.key });
+            ExperimentNode.globalKeyCount++;
+        }
+        else if (spec.key >= ExperimentNode.globalKeyCount)
+            ExperimentNode.globalKeyCount = spec.key + 1;
+
+        ExperimentNode.globalNodeMap[spec.key] = this;
+    },
+    // adds a new experimentNode to this node
+    addNode: function (experimentNode) {
+        if (!(experimentNode instanceof ExperimentNode)) {
+            experimentNode = new ExperimentNode(experimentNode);
+        }
+        this.get('children').add(experimentNode);
+        experimentNode.set({ parent: this });
+        this.trigger('node-added', experimentNode);
+        return experimentNode;
+    },
+    // moves an experimentNode into this node
+    moveNode: function (experimentNode) {
+        var parent = experimentNode.get('parent');
+        if (parent != null) {
+            // remove from parent
+            parent.get('children').remove(experimentNode);
+        }
+        this.addNode(experimentNode);
+    },
+    // delete this node
+    remove: function () {
+        var parent = this.get('parent');
+        if (parent != null) {
+            parent.get('children').remove(this);
+            this.set({ parent: null });
+            // simply replace it with 'undefined'
+            delete ExperimentNode.globalNodeMap[this.get('key')];
+        }
+        this.trigger('node-removed', this);
+    },
+    lookup: function (key) {
+        return ExperimentNode.globalNodeMap[key];
+    }
+},
+{
+    globalKeyCount: 0,
+    globalNodeMap: {},
+    lookup: function (key) {
+        return ExperimentNode.globalNodeMap[key];
+    },
+    nextGlobalKey: function () {
+        var key = ExperimentNode.globalKeyCount;
+        ExperimentNode.globalKeyCount++;
+        return key;
+    }
+}
+);
+
+var ExperimentList = Backbone.Collection.extend({
+  url: function () {
+    return "/Experiment/ExperimentList";
+  },
+  model: Experiment
+});
+
+var ScenarioList = Backbone.Collection.extend({
+  url: function () {
+    return "/Experiment/ScenarioList";
+  },
+  model: ExperimentNode
+});
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.view.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.view.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/experiment.view.js	(revision 9305)
@@ -0,0 +1,711 @@
+﻿// ================ VIEWS =======================
+var ExperimentTreeView = Backbone.View.extend({
+    renderTree: function (model) {
+        var jsonModel = model.toJSON();
+        var self = this;
+        this.tree = $('<div />').dynatree({
+            children: [jsonModel],
+            autoExpandMS: 750,
+            onActivate: function (node) {
+                // if we are able to remove this node from the tree, enable the remove button.
+                if (node && node.parent.parent != null) {
+                    $('.remove-button', this.$el).removeAttr("disabled");
+                } else if (node) {
+                    $('.remove-button', this.$el).attr("disabled", "disabled");
+                }
+
+                var modelNode = ExperimentNode.lookup(node.data.key);
+                if (modelNode && !modelNode.get('isExperiment')) {
+                    $('.variate-button', this.$el).removeAttr("disabled");
+                } else {
+                    $('.variate-button', this.$el).attr("disabled", "disabled");
+                }
+            },
+            dnd: {
+                onDragStart: function (node) {
+                    return node.parent.parent != null;
+                },
+                onDragEnter: function (node, sourceNode) {
+                    return true;
+                },
+                onDragOver: function (node, sourceNode, hitMode) {
+                    return node.data.title !== "Experiment" || hitMode === "over";
+                },
+                onDrop: function (node, sourceNode, hitMode, ui, draggable) {
+                    if (hitMode !== 'over')
+                        return;
+                    var modelNode = ExperimentNode.lookup(node.data.key);
+                    if (sourceNode) {
+                        var sourceModelNode = ExperimentNode.lookup(sourceNode.data.key);
+                        sourceNode.move(node, hitMode);
+                        // move node into the modelNode
+                        sourceModelNode.moveNode(modelNode);
+                    }
+                    else {
+                        var elem = $(draggable.element[0]);
+                        var newNode = { nodeId: elem.attr('id'), isExperiment: elem.attr('data-isExperiment') === 'true', title: elem.attr('data-name'), key: ExperimentNode.nextGlobalKey() };
+                        if (hitMode == "over") {
+                            node.addChild(newNode);
+                            node.expand(true);
+                            // update the data model
+                            newNode = modelNode.addNode(newNode);
+                            self.trigger('node-added', newNode);
+                        }
+                    }
+                    self.trigger('structure-changed', modelNode);
+                }
+            }
+        });
+        this.tree.dynatree("getRoot").visit(function (node) {
+            node.expand(true);
+        });
+    },
+    initialize: function (spec) {
+        this.renderTree(spec.model);
+    },
+    events: {
+        'click .remove-button': 'doRemove',
+        'click .variate-button': 'doVariate'
+    },
+    render: function () {
+        this.$el.empty();
+        this.renderTree(this.model);
+        var content = $(_.template($('#stepwizard_template').html(), {}));
+        this.tree.appendTo(this.$el);
+        content.appendTo(this.$el);
+    },
+    doRemove: function () {
+        var node = this.tree.dynatree('getActiveNode');
+        if (node && node.parent.parent != null) {
+            var nxt = node.getNextSibling();
+            if (nxt == null)
+                nxt = node.getPrevSibling();
+            if (nxt == null)
+                nxt = node.getParent();
+            node.remove();
+            ExperimentNode.lookup(node.data.key).remove();
+            if (nxt) {
+                this.tree.dynatree("getTree").activateKey(nxt.data.key);
+            }
+        }
+        this.trigger('structure-changed', node);
+    },
+    doVariate: function () {
+        var node = this.tree.dynatree('getActiveNode');
+        var modelNode = ExperimentNode.lookup(node.data.key);
+        this.trigger('variation-request', {
+            parentNode: node.parent,
+            model: modelNode
+        });
+    }
+});
+
+var ExperimentDetailsTreeView = Backbone.View.extend({
+  render: function () {
+    var jsonModel = this.model.toJSON();
+    var self = this;
+    var tree = $('<div />').dynatree({
+      children: [jsonModel],
+      autoExpandMS: 750,
+      onActivate: function (node) {
+        // if we click a node, simply open a dialog to enter it's parameters
+        self.trigger('node-clicked', node);
+      }
+    });
+
+    this.$el.empty();
+    tree.appendTo(this.$el);
+    tree.dynatree("getRoot").visit(function (node) {
+        node.expand(true);
+    });
+  }
+});
+
+var DraggableGroup = Backbone.View.extend({
+    render: function () {
+        var self = this;
+        var div = $('<div />');
+
+        for (var i = 0; i < this.model.length; i++) {
+            new DraggableView({ model: this.model.models[i], el: div }).render();
+        }
+
+        this.$el.empty();
+        div.appendTo(this.$el);
+    }
+});
+
+
+var DraggableView = Backbone.View.extend({
+  render: function () {
+    var late = _.template($("#draggable_template").html(), this.model.attributes);    
+    $(late).draggable({
+      revert: true,
+      connectToDynatree: true,
+      cursorAt: { top: -5, left: -5 },
+      helper: "clone"
+    }).appendTo(this.$el);
+  }
+});
+
+var SelectableGroup = Backbone.View.extend({
+    render: function () {
+        var self = this;
+        var div = $('<div />');
+        for (var i = 0; i < this.model.length; i++) {
+            new SelectableView({ model: this.model.models[i], el: div }).render();
+        }
+        this.$el.empty();
+        div.selectable({
+            filter: 'div',
+            selected: function (event, ui) {
+                self.selected(event, ui);
+            }
+        }).appendTo(this.$el);
+    },
+    selected: function (event, ui) {
+        var nodeId = $(ui.selected).attr('id');
+        this.trigger('node-selected', nodeId);
+    }
+});
+
+var SelectableView = Backbone.View.extend({
+    render: function () {
+        var late = _.template($("#draggable_template").html(), this.model.attributes);
+        $(late).appendTo(this.$el);
+    }
+});
+
+var StepWizardView = Backbone.View.extend({
+    render: function () {
+        var self = this;
+        this.$el.smartWizard({
+            onLeaveStep: function (step) {
+                var stepNum = parseInt(step.attr("rel"));
+                return self.validateStep(stepNum);
+            },
+            onShowStep: function (step) {
+                var stepNum = parseInt(step.attr("rel"));
+                return self.showStep(stepNum);
+            },
+            onFinish: function () {
+                self.trigger('experiment-finished');
+            }
+        });
+    },
+    validateStep: function (stepNumber) {
+        var validationModel = {
+            stepNumber: stepNumber,
+            succeeded: true
+        };
+        this.trigger('step-validated', validationModel);
+        return validationModel.succeeded;
+    },
+    showStep: function (stepNumber) {
+        this.trigger('step-shown', stepNumber);
+        return true;
+    }
+});
+
+var ParameterWizard = Backbone.View.extend({
+  createWizard: function (steps) {
+    // use template instead of jquery?!
+    var newWizard = $('<div />').addClass('wizard swMain');
+    var stepMenu = $('<ul />');
+    var prefix = name + '_';
+    for (var i = 0; i < steps.length; i++) {
+      var step = steps[i];
+      var a = $('<a></a>', { href: '#' + prefix + (i + 1) });
+      $('<label />').addClass("stepNumber").text((i + 1)).appendTo(a);
+      $('<span />').addClass("stepDesc").html(step.heading + '<br /><small>' + step.description + '</small>').appendTo(a);
+      var li = $('<li />').append(a);
+      li.appendTo(stepMenu);
+    }
+    stepMenu.appendTo(newWizard);
+    for (var i = 0; i < steps.length; i++) {
+      var div = $('<div />', { id: prefix + (i + 1) });
+      if (steps[i].content && steps[i].content != null)
+        div.append(steps[i].content);
+      div.appendTo(newWizard);
+    }
+    return newWizard;
+  },
+  render: function () {
+    var self = this;
+    this.$el.empty();
+    var data = this.model.get('data');
+
+    var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
+    var dialog = $('<div />');
+    var algoDiv = $('<div />');
+    var problemDiv = $('<div />');
+
+    for (var i = 0; i < data.AlgorithmParameters.length; i++) {
+      var parameterView = new ParameterEditableView({ model: data.AlgorithmParameters[i] });
+      // render to dialog
+      parameterView.render();
+      parameterView.$el.appendTo(algoDiv);
+    }
+
+    if (hasProblemParameters) {
+      var problemParameters = data.ProblemParameters;
+      for (var i = 0; i < problemParameters.length; i++) {
+        var parameterView = new ParameterEditableView({ model: problemParameters[i] });
+        // render to dialog
+        parameterView.render();
+        parameterView.$el.appendTo(problemDiv);
+      }
+    }
+
+    var newWizard =
+        hasProblemParameters ?
+          this.createWizard([
+            { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv },
+            { heading: "Problem Parameters", description: "Adjust problem parameters", content: problemDiv }
+          ]) :
+          this.createWizard([
+            { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoDiv }
+          ]);
+
+    newWizard.appendTo(this.$el);
+    newWizard.smartWizard({
+      onFinish: function () {
+        self.trigger('parameters-finished');
+      }
+    });
+  }
+});
+
+var ParameterEditableView = Backbone.View.extend({
+    events: {
+        'change': 'valueChanged'
+    },
+    render: function () {
+        var mapper = new DatatypeMapper();
+        var content = $(_.template($('#parameter_template').html(), this.model));
+        mapper.mapHtml(this.model).appendTo($('.rightEntry', content));
+        content.appendTo(this.$el);
+    },
+    valueChanged: function (param) {
+        var value = $(param.target).val();
+        if (!isNaN(value))
+            value = parseFloat(value);
+        var name = $(param.target).attr("name");
+        // normally a controller would do this, just for once, let the view update the model
+        var splitted = name.split("_");
+        if (splitted.length == 1) {
+            this.model.Value = value;
+        } else if (splitted.length == 2) {
+            this.model.Value[parseInt(splitted[1])] = value;
+        } else if (splitted.length == 3) {
+            this.model.Value[parseInt(splitted[1])][parseInt(splitted[2])] = value;
+        }
+    }
+});
+
+
+var LoadingDialog = Backbone.View.extend({
+    initialize: function () {
+        this.reset();
+    },
+    render: function () {
+        var self = this;
+        this.$el.empty();
+        var dialog = $('<div />');
+        this.content.appendTo(dialog);
+        dialog.appendTo(this.$el);
+        this.$el.dialog({
+            autoOpen: true
+        });
+    },
+    text: function (txt) {
+        $('p', this.$el).text(txt);
+    },
+    setLoading: function(isLoading) {
+      if (isLoading)
+        $('img', this.$el).show();
+      else
+        $('img', this.$el).hide();
+    },
+    reset: function () {
+        this.content = $(_.template($('#loading_template').html(), {}));
+    },
+    setContent: function (el) {
+        this.content = el;
+    },
+    close: function () {
+        this.$el.dialog("close");
+    }
+});
+
+
+var ParameterDialog = Backbone.View.extend({
+  render: function () {
+    var self = this;
+
+    this.$el.empty();
+    var data = this.model.get('data');
+
+    var hasProblemParameters = typeof data.ProblemParameters !== 'undefined';
+    var dialog = $('<div />');
+
+    var parameterWizard = new ParameterWizard({ model: this.model, el: dialog });
+    parameterWizard.render();
+    this.listenTo(parameterWizard, 'parameters-finished', function () {
+      self.trigger('parameters-finished');
+    });
+    dialog.appendTo(this.$el);
+    this.$el.dialog({
+      autoOpen: true,
+      width: 1024,
+      height: 768
+    });
+  },
+  close: function () {
+    this.$el.dialog("close");
+  }
+});
+
+var ExperimentDetailsView = Backbone.View.extend({
+    events: {
+        'change': 'valueChanged'
+    },
+    valueChanged: function (param) {
+        var value = $(param.target).val();
+        if (!isNaN(value))
+            value = parseFloat(value);
+        var name = $(param.target).attr("name");
+        if (name == 'Name')
+            this.model.set({ title: value });
+        else if (name == 'RunImmediately')
+            this.model.set({ run: $(param.target).is(':checked') });
+        else if (name == 'Repititions')
+            this.model.set({ repititions: value });
+        else if (name == 'Group')
+            this.model.set({ group: value });
+
+        if (this.model.get('run')) {
+            $('input[name="Repititions"]', this.$el).removeAttr('disabled');
+            $('input[name="Group"]', this.$el).removeAttr('disabled');
+        } else {
+            $('input[name="Repititions"]', this.$el).attr('disabled', 'disabled');
+            $('input[name="Group"]', this.$el).attr('disabled', 'disabled');
+        }
+    },
+    render: function () {
+        $('input[name="Name"]', this.$el).val(this.model.get('title'));
+        $('input[name="RunImmediately"]', this.$el).attr('checked', this.model.get('run'));
+        $('input[name="Repititions"]', this.$el).val(this.model.get('repititions'));
+        $('input[name="Group"]', this.$el).val(this.model.get('group'));
+    }
+});
+
+var ValidationHintsView = Backbone.View.extend({
+    render: function () {
+        var template = _.template($('#validationhints_template').html(), this.model);
+        $(template).dialog({
+            resizable: false,
+            modal: true,
+            buttons: {
+                "OK": function () {
+                    $(this).dialog("close");
+                }
+            }
+        });
+    }
+});
+
+// ========== Variation Dialog Views ============
+
+var VariationDialog = Backbone.View.extend({
+    initialize: function (spec) {
+
+    },
+    events: {
+        'change': 'entrySelected'
+    },
+    render: function () {
+        var self = this;
+        var dialog = $(_.template($('#variationdialog_template').html(), {}));
+
+        var content = $('select[class="variationDialogContent"]', dialog);
+        var data = this.model.get('data');
+        for (var i = 0; i < data.AlgorithmParameters.length; i++) {
+            data.AlgorithmParameters[i].Index = i;
+            var entry = new VariationEntryView({ model: data.AlgorithmParameters[i], el: content });
+            entry.render();
+        }
+
+        this.details = $('div[class="variationDetails"]', dialog);
+        this.activateButton = $('input[name="active"]', dialog);
+        this.activateButton.change(function (evt) {
+            var checked = $(evt.target).is(':checked');
+            if (self.selectedModel && self.selectedModel != null) {
+                self.selectedModel.Active = checked;
+            }
+        });
+        dialog.dialog({
+            height: 300,
+            buttons: {
+                "Abort": function () {
+                    $(this).dialog("close");
+                },
+                "OK": function () {
+                    self.generateVariations();
+                    $(this).dialog("close");
+                }
+            }
+        });
+
+        content.change(function (evt) {
+            var optionSelected = $("option:selected", this);
+            var model = self.model.get('data').AlgorithmParameters[parseInt($(optionSelected).attr('data-index'))]
+            self.entrySelected(model);
+        });
+
+        this.variationDetails = new VariationContentView({ el: this.details });
+        var model = this.model.get('data').AlgorithmParameters[0];
+        this.entrySelected(model);
+    },
+    entrySelected: function (model) {
+        this.selectedModel = model;
+        if (model.Active)
+            this.activateButton.attr('checked', 'checked');
+        else
+            this.activateButton.removeAttr('checked');
+        this.variationDetails.model = model;
+        this.details.empty();
+        this.variationDetails.render();
+    },
+    generateVariations: function () {
+        this.solutions = [];
+        this.exhaust(0, []);
+        this.trigger('variations-generated', this.solutions);
+    },
+    exhaust: function (index, element) {
+        if (index == this.model.get('data').AlgorithmParameters.length) {
+            // we found a solution! store it by creating a deep copy
+            this.solutions.push($.extend(true, [], element));
+            return;
+        }
+
+        var currentParameter = this.model.get('data').AlgorithmParameters[index];
+        if (currentParameter.Active && currentParameter.Generated) {
+            for (var i = 0; i < currentParameter.Generated.length; i++) {
+                element[index] = {
+                    Name: currentParameter.Name,
+                    Value: currentParameter.Generated[i]
+                };
+                if (currentParameter.Options) {
+                    element[index].Options = currentParameter.Options;
+                }
+                this.exhaust(index + 1, element);
+            }
+        } else {
+            element[index] = {
+                Name: currentParameter.Name,
+                Value: currentParameter.Value
+            };
+            if (currentParameter.Options) {
+                element[index].Options = currentParameter.Options;
+            }
+            this.exhaust(index + 1, element);
+        }
+    }
+});
+
+var TableEditView = Backbone.View.extend({
+    events: {
+        'change': 'valueChanged',
+        'click button[data-operation="Add"]': 'addClicked',
+        'click button[data-operation="Remove"]': 'removeClicked'
+    },
+    render: function () {
+        this.$el.empty();
+        // http: //stackoverflow.com/questions/8749236/create-table-with-jquery-append
+        var table = $('<table></table>').addClass('editableTable');
+
+        // determine dimensions
+        var rows = this.model.length;
+        var columns = 1;
+        if ($.isArray(this.model[0])) {
+            columns = this.model[0].length;
+        }
+
+        // create head elements
+        var head = $('<thead></thead>');
+        var headerRow = $('<tr></tr>');
+        headerRow.appendTo(head);
+        for (var i = 0; i < columns; i++) {
+            $('<td />').text((i + 1) + '. Column').appendTo(headerRow);
+        }
+        head.appendTo(table);
+
+        // create body
+        var body = $('<tbody></tbody>');
+
+        for (var i = 0; i < rows; i++) {
+            var row = $('<tr></tr>');
+            for (var j = 0; j < columns; j++) {
+                var rowContent = this.model[i];
+                var columnEntry = $.isArray(rowContent) ? rowContent[j] : rowContent;
+                var col = $('<td></td>');
+                $('<input type="text" name="' + i + '_' + j + '" />').val(columnEntry).appendTo(col);
+                col.appendTo(row);
+            }
+            if (this.options.editable) {
+                $('<button data-operation="Add" name="' + i + '" />').val(columnEntry).text('+').appendTo(col);
+                $('<button data-operation="Remove" name="' + i + '" />').val(columnEntry).text('-').appendTo(col);
+            }
+            row.appendTo(body);
+        }
+        body.appendTo(table);
+        table.appendTo(this.$el);
+    },
+    valueChanged: function (evt) {
+        var target = $(evt.target);
+        var value = target.val();
+        var index = target.getattr('name');
+        var splittedName = index.split('_');
+        var i = parseInt(splittedName[0]);
+        var j = parseInt(splittedName[1]);
+        if ($.isArray(this.model[i])) {
+            this.model[i][j] = parseFloat(value);
+        } else {
+            this.model[i] = parseFloat(value);
+        }
+    },
+    addClicked: function (evt) {
+        var target = $(evt.target);
+        var index = target.attr('name');
+        var i = parseInt(index);
+        if ($.isArray(this.model[i])) {
+            var cpy = [];
+            for (var j = 0; j < this.model[i].length; j++) {
+                cpy.push(this.model[i][j]);
+            }
+            this.model.splice(i, 0, cpy);
+        } else {
+            this.model.splice(i, 0, this.model[i]);
+        }
+        // render after model changes
+        this.render();
+    },
+    removeClicked: function (evt) {
+        var target = $(evt.target);
+        var index = target.attr('name');
+        var i = parseInt(index);
+        this.model.splice(i, 1);
+        // render after model changes
+        this.render();
+    }
+});
+
+var OptionSelectionView = Backbone.View.extend({
+    events: {
+        'change': 'checked'
+    },
+    render: function () {
+        this.$el.empty();
+
+        for (var i = 0; i < this.model.length; i++) {
+            $(_.template($('#checkbox_template').html(), { name: this.model[i], text: this.model[i] })).appendTo(this.$el);
+        }
+    },
+    checked: function (evt) {
+        var checked = $(evt.target).is(':checked');
+        var name = $(evt.target).attr('name')
+        this.trigger('changed', { name: name, checked: checked });
+    }
+});
+
+var VariationContentView = Backbone.View.extend({
+    render: function () {
+        var self = this;
+        if (this.model.Value === 'false' || this.model.Value === 'true' ||
+            this.model.Value === true || this.model.Value === false) {
+            $(_.template($('#variation_boolean_template').html(), {})).appendTo(this.$el);
+            this.model.Generated = [false, true];
+        }
+        else if (!isNaN(this.model.Value)) {
+            var elem = $(_.template($('#variation_number_template').html(), {}));
+            elem.appendTo(this.$el)
+            $('input[name="generate"]', elem).click(function (evt) { self.openGenerator(evt); });
+            if (!this.model.Generated) {
+                this.model.Generated = [0, 1, 2];
+            }
+            var tev = new TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
+            tev.render();
+        }
+        else if (this.model.Options) {
+            var osv = new OptionSelectionView({ model: this.model.Options, el: this.$el });
+            this.listenTo(osv, 'changed', function (evt) {
+                self.selectionChanged(evt);
+            });
+            if (!this.model.Generated)
+                this.model.Generated = [];
+            osv.render();
+            for (var i = 0; i < this.model.Generated.length; i++) {
+                $('input[name="' + this.model.Generated[i] + '"]', this.$el).attr('checked', 'checked');
+            }
+        }
+    },
+    openGenerator: function (evt) {
+        var self = this;
+        var generator = new GeneratorDialog();
+        this.listenTo(generator, 'success', function (data) {
+            self.doGenerate(data);
+        });
+        generator.render();
+    },
+    doGenerate: function (data) {
+        if (data.minimum > data.maximum) {
+            var tmp = data.minimum;
+            data.minimum = data.maximum;
+            data.maximum = tmp;
+        }
+        if (data.step < 0)
+            data.step *= -1;
+        if (data.step == 0)
+            data.step = 1;
+        this.model.Generated = _.range(data.minimum, data.maximum + 1, data.step);
+        var tev = new TableEditView({ model: this.model.Generated, el: $('div div', this.$el), editable: true });
+        tev.render();
+    },
+    selectionChanged: function (evt) {
+        if (!evt.checked) {
+            this.model.Generated = _.without(this.model.Generated, evt.name);
+        } else if (_.indexOf(this.model.Generated, evt.name) == -1) {
+            this.model.Generated.push(evt.name);
+        }
+    }
+});
+
+var GeneratorDialog = Backbone.View.extend({
+    render: function() {
+        var self = this;
+        var generator = $(_.template($('#variation_generator_template').html(),{}));
+        generator.dialog({
+            buttons: {
+                "Abort": function () {
+                    $(this).dialog("close");
+                },
+                "OK": function () {
+                    $(this).dialog("close");
+                    self.trigger("success", {
+                        minimum: parseInt($('input[name="minimum"]', generator).val()),
+                        maximum: parseInt($('input[name="maximum"]', generator).val()),
+                        step: parseFloat($('input[name="step"]', generator).val())
+                    });
+                }
+            }
+        });
+    }
+});
+
+var VariationEntryView = Backbone.View.extend({
+    render: function () {
+        var entryTemplate = _.template($('#variationentry_template').html(), this.model);
+        $(entryTemplate).appendTo(this.$el);        
+    }
+});
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/hl/Site.css
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/hl/Site.css	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/hl/Site.css	(revision 9305)
@@ -433,2 +433,16 @@
     display: none;
 }
+
+.leftEntry {
+    float: left;
+    width: 30%;
+}
+
+.rightEntry{
+    margin-left: 30%;
+}
+
+.ui-selected
+{
+    background: #EA8511;  
+}
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/js/jquery.smartWizard-2.0.js
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/js/jquery.smartWizard-2.0.js	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/js/jquery.smartWizard-2.0.js	(revision 9305)
@@ -0,0 +1,346 @@
+/*
+ * SmartWizard 2.0 plugin
+ * jQuery Wizard control Plugin
+ * by Dipu 
+ * 
+ * http://www.techlaboratory.net 
+ * http://tech-laboratory.blogspot.com
+ */
+ 
+(function($){
+    $.fn.smartWizard = function(action) {
+        var options = $.extend({}, $.fn.smartWizard.defaults, action);
+        var args = arguments;
+        
+        return this.each(function(){
+                var obj = $(this);
+                var curStepIdx = options.selected;
+                var steps = $("ul > li > a[href^='#step-']", obj); // Get all anchors in this array
+                var contentWidth = 0;
+                var loader,msgBox,elmActionBar,elmStepContainer,btNext,btPrevious,btFinish;
+                
+                elmActionBar = $('.actionBar',obj);
+                if(elmActionBar.length == 0){
+                  elmActionBar = $('<div></div>').addClass("actionBar");                
+                }
+
+                msgBox = $('.msgBox',obj);
+                if(msgBox.length == 0){
+                  msgBox = $('<div class="msgBox"><div class="content"></div><a href="#" class="close">X</a></div>');
+                  elmActionBar.append(msgBox);                
+                }
+                
+                $('.close',msgBox).click(function() {
+                    msgBox.fadeOut("normal");
+                    return false;
+                });
+                
+
+                // Method calling logic
+                if (!action || action === 'init' || typeof action === 'object') {
+                  init();
+                }else if (action === 'showMessage') {
+                  //showMessage(Array.prototype.slice.call( args, 1 ));
+                  var ar =  Array.prototype.slice.call( args, 1 );
+                  showMessage(ar[0]);
+                  return true;
+                }else if (action === 'setError') {
+                  var ar =  Array.prototype.slice.call( args, 1 );
+                  setError(ar[0].stepnum,ar[0].iserror);
+                  return true;
+                } else {
+                  $.error( 'Method ' +  action + ' does not exist' );
+                }
+                
+                function init(){
+                  var allDivs =obj.children('div'); //$("div", obj);                
+                  obj.children('ul').addClass("anchor");
+                  allDivs.addClass("content");
+                  // Create Elements
+                  loader = $('<div>Loading</div>').addClass("loader");
+                  elmActionBar = $('<div></div>').addClass("actionBar");
+                  elmStepContainer = $('<div></div>').addClass("stepContainer");
+                  btNext = $('<a>'+options.labelNext+'</a>').attr("href","#").addClass("buttonNext");
+                  btPrevious = $('<a>'+options.labelPrevious+'</a>').attr("href","#").addClass("buttonPrevious");
+                  btFinish = $('<a>'+options.labelFinish+'</a>').attr("href","#").addClass("buttonFinish");
+                  
+                  // highlight steps with errors
+                  if(options.errorSteps && options.errorSteps.length>0){
+                    $.each(options.errorSteps, function(i, n){
+                      setError(n,true);
+                    });
+                  }
+
+
+                  elmStepContainer.append(allDivs);
+                  elmActionBar.append(loader);
+                  obj.append(elmStepContainer);
+                  obj.append(elmActionBar); 
+                  if (options.includeFinishButton) {
+                    elmActionBar.append(btFinish);
+                  }
+                  elmActionBar.append(btNext).append(btPrevious); 
+                  contentWidth = elmStepContainer.width();
+
+                  $(btNext).click(function() {
+                      if($(this).hasClass('buttonDisabled')){
+                        return false;
+                      }
+                      doForwardProgress();
+                      return false;
+                  }); 
+                  $(btPrevious).click(function() {
+                      if($(this).hasClass('buttonDisabled')){
+                        return false;
+                      }
+                      doBackwardProgress();
+                      return false;
+                  }); 
+                  $(btFinish).click(function() {
+                      if(!$(this).hasClass('buttonDisabled')){
+                         if($.isFunction(options.onFinish)) {
+                            if(!options.onFinish.call(this,$(steps))){
+                              return false;
+                            }
+                         }else{
+                           var frm = obj.parents('form');
+                           if(frm && frm.length){
+                             frm.submit();
+                           }                         
+                         }                      
+                      }
+
+                      return false;
+                  }); 
+                  
+                  $(steps).bind("click", function(e){
+                      if(steps.index(this) == curStepIdx){
+                        return false;                    
+                      }
+                      var nextStepIdx = steps.index(this);
+                      var isDone = steps.eq(nextStepIdx).attr("isDone") - 0;
+                      if(isDone == 1){
+                        LoadContent(nextStepIdx);                    
+                      }
+                      return false;
+                  }); 
+                  
+                  // Enable keyboard navigation                 
+                  if(options.keyNavigation){
+                      $(document).keyup(function(e){
+                          if(e.which==39){ // Right Arrow
+                            doForwardProgress();
+                          }else if(e.which==37){ // Left Arrow
+                            doBackwardProgress();
+                          }
+                      });
+                  }
+                  //  Prepare the steps
+                  prepareSteps();
+                  // Show the first slected step
+                  LoadContent(curStepIdx);                  
+                }
+
+                function prepareSteps(){
+                  if(!options.enableAllSteps){
+                    $(steps, obj).removeClass("selected").removeClass("done").addClass("disabled"); 
+                    $(steps, obj).attr("isDone",0);                 
+                  }else{
+                    $(steps, obj).removeClass("selected").removeClass("disabled").addClass("done"); 
+                    $(steps, obj).attr("isDone",1); 
+                  }
+
+            	    $(steps, obj).each(function(i){
+                        $($(this).attr("href"), obj).hide();
+                        $(this).attr("rel",i+1);
+                  });
+                }
+                
+                function LoadContent(stepIdx){
+                    var selStep = steps.eq(stepIdx);
+                    var ajaxurl = options.contentURL;
+                    var hasContent =  selStep.data('hasContent');
+                    stepNum = stepIdx+1;
+                    if(ajaxurl && ajaxurl.length>0){
+                       if(options.contentCache && hasContent){
+                           showStep(stepIdx);                          
+                       }else{
+                           $.ajax({
+                            url: ajaxurl,
+                            type: "POST",
+                            data: ({step_number : stepNum}),
+                            dataType: "text",
+                            beforeSend: function(){ loader.show(); },
+                            error: function(){loader.hide();},
+                            success: function(res){ 
+                              loader.hide();       
+                              if(res && res.length>0){  
+                                 selStep.data('hasContent',true);            
+                                 $($(selStep, obj).attr("href"), obj).html(res);
+                                 showStep(stepIdx);
+                              }
+                            }
+                          }); 
+                      }
+                    }else{
+                      showStep(stepIdx);
+                    }
+                }
+                
+                function showStep(stepIdx){
+                    var selStep = steps.eq(stepIdx); 
+                    var curStep = steps.eq(curStepIdx);
+                    if(stepIdx != curStepIdx){
+                      if($.isFunction(options.onLeaveStep)) {
+                        if(!options.onLeaveStep.call(this,$(curStep))){
+                          return false;
+                        }
+                      }
+                    }     
+                    if (options.updateHeight)
+                        elmStepContainer.height($($(selStep, obj).attr("href"), obj).outerHeight());               
+                    if(options.transitionEffect == 'slide'){
+                      $($(curStep, obj).attr("href"), obj).slideUp("fast",function(e){
+                            $($(selStep, obj).attr("href"), obj).slideDown("fast");
+                            curStepIdx =  stepIdx;                        
+                            SetupStep(curStep,selStep);
+                          });
+                    } else if(options.transitionEffect == 'fade'){                      
+                      $($(curStep, obj).attr("href"), obj).fadeOut("fast",function(e){
+                            $($(selStep, obj).attr("href"), obj).fadeIn("fast");
+                            curStepIdx =  stepIdx;                        
+                            SetupStep(curStep,selStep);                           
+                          });                    
+                    } else if(options.transitionEffect == 'slideleft'){
+                        var nextElmLeft = 0;
+                        var curElementLeft = 0;
+                        if(stepIdx > curStepIdx){
+                            nextElmLeft1 = contentWidth + 10;
+                            nextElmLeft2 = 0;
+                            curElementLeft = 0 - $($(curStep, obj).attr("href"), obj).outerWidth();
+                        } else {
+                            nextElmLeft1 = 0 - $($(selStep, obj).attr("href"), obj).outerWidth() + 20;
+                            nextElmLeft2 = 0;
+                            curElementLeft = 10 + $($(curStep, obj).attr("href"), obj).outerWidth();
+                        }
+                        if(stepIdx == curStepIdx){
+                            nextElmLeft1 = $($(selStep, obj).attr("href"), obj).outerWidth() + 20;
+                            nextElmLeft2 = 0;
+                            curElementLeft = 0 - $($(curStep, obj).attr("href"), obj).outerWidth();                           
+                        }else{
+                            $($(curStep, obj).attr("href"), obj).animate({left:curElementLeft},"fast",function(e){
+                              $($(curStep, obj).attr("href"), obj).hide();
+                            });                       
+                        }
+
+                        $($(selStep, obj).attr("href"), obj).css("left",nextElmLeft1);
+                        $($(selStep, obj).attr("href"), obj).show();
+                        $($(selStep, obj).attr("href"), obj).animate({left:nextElmLeft2},"fast",function(e){
+                          curStepIdx =  stepIdx;                        
+                          SetupStep(curStep,selStep);                      
+                        });
+                    } else{
+                        $($(curStep, obj).attr("href"), obj).hide(); 
+                        $($(selStep, obj).attr("href"), obj).show();
+                        curStepIdx =  stepIdx;                        
+                        SetupStep(curStep,selStep);
+                    }
+                    return true;
+                }
+                
+                function SetupStep(curStep,selStep){
+                   $(curStep, obj).removeClass("selected");
+                   $(curStep, obj).addClass("done");
+                   
+                   $(selStep, obj).removeClass("disabled");
+                   $(selStep, obj).removeClass("done");
+                   $(selStep, obj).addClass("selected");
+                   $(selStep, obj).attr("isDone",1);
+                   adjustButton();
+                   if($.isFunction(options.onShowStep)) {
+                      if(!options.onShowStep.call(this,$(selStep))){
+                        return false;
+                      }
+                   } 
+                }                
+                
+                function doForwardProgress(){
+                  var nextStepIdx = curStepIdx + 1;
+                  if(steps.length <= nextStepIdx){
+                    if(!options.cycleSteps){
+                      return false;
+                    }                  
+                    nextStepIdx = 0;
+                  }
+                  LoadContent(nextStepIdx);
+                }
+                
+                function doBackwardProgress(){
+                  var nextStepIdx = curStepIdx-1;
+                  if(0 > nextStepIdx){
+                    if(!options.cycleSteps){
+                      return false;
+                    }
+                    nextStepIdx = steps.length - 1;
+                  }
+                  LoadContent(nextStepIdx);
+                }  
+                
+                function adjustButton(){
+                  if(!options.cycleSteps){                
+                    if(0 >= curStepIdx){
+                      $(btPrevious).addClass("buttonDisabled");
+                    }else{
+                      $(btPrevious).removeClass("buttonDisabled");
+                    }
+                    if((steps.length-1) <= curStepIdx){
+                      $(btNext).addClass("buttonDisabled");
+                    }else{
+                      $(btNext).removeClass("buttonDisabled");
+                    }
+                  }
+                  // Finish Button 
+                  if(!steps.hasClass('disabled') || options.enableFinishButton){
+                    $(btFinish).removeClass("buttonDisabled");
+                  }else{
+                    $(btFinish).addClass("buttonDisabled");
+                  }                  
+                }
+                
+                function showMessage(msg){
+                  $('.content',msgBox).html(msg);
+              		msgBox.show();
+                }
+                
+                function setError(stepnum,iserror){
+                  if(iserror){                    
+                    $(steps.eq(stepnum-1), obj).addClass('error')
+                  }else{
+                    $(steps.eq(stepnum-1), obj).removeClass("error");
+                  }                                   
+                }                        
+        });  
+    };  
+ 
+    // Default Properties and Events
+    $.fn.smartWizard.defaults = {
+          selected: 0,  // Selected Step, 0 = first step   
+          keyNavigation: true, // Enable/Disable key navigation(left and right keys are used if enabled)
+          enableAllSteps: false,
+          updateHeight: true,
+          transitionEffect: 'fade', // Effect on navigation, none/fade/slide/slideleft
+          contentURL:null, // content url, Enables Ajax content loading
+          contentCache:true, // cache step contents, if false content is fetched always from ajax url
+          cycleSteps: false, // cycle step navigation
+          includeFinishButton: true, // whether to show a Finish button
+          enableFinishButton: false, // make finish button enabled always
+          errorSteps:[],    // Array Steps with errors
+          labelNext:'Next',
+          labelPrevious:'Previous',
+          labelFinish:'Finish',          
+          onLeaveStep: null, // triggers when leaving a step
+          onShowStep: null,  // triggers when showing a step
+          onFinish: null  // triggers when Finish button is clicked
+    };    
+    
+})(jQuery);
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/styles/smart_wizard.css
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/styles/smart_wizard.css	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Content/smartwizard2.0/styles/smart_wizard.css	(revision 9305)
@@ -92,5 +92,5 @@
   list-style: none;
   padding: 0px;  
-  margin: 10px 0;      
+  /*margin: 10px 0;*/        
   clear: both;
   border: 0px solid #CCCCCC;    
@@ -112,5 +112,5 @@
   position:relative;
   float:left;
-  margin:0;
+  margin: 10px 0;
   padding:3px;
   height:60px;
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Controllers/ExperimentController.cs
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Controllers/ExperimentController.cs	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Controllers/ExperimentController.cs	(revision 9305)
@@ -31,5 +31,6 @@
         public ActionResult Index()
         {
-          var model = new ExperimentViewModel();
+          return RedirectToAction("New");
+          /*var model = new ExperimentViewModel();
           var scenarios = ControllerService.withControllerService<IEnumerable<string>>((service) => {
             return service.GetOptimizationScenarioNames();
@@ -37,5 +38,5 @@
           var experiments = ControllerService.withControllerService<IEnumerable<string>>((service) => {
             User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-            return service.GetExperiments(u);
+            return service.GetExperimentNames(u);
           });
           foreach (var experiment in experiments)
@@ -44,45 +45,13 @@
             model.Scenarios.Add(scenario);
 
-          Session["experiment"] = new Experiment();
-
-          return View(model);
+          return View(model);*/
         }
 
         public ActionResult New() {
-          return Index();
+          return View();
         }
 
-        public Experiment Experiment { get { return Session["experiment"] as Experiment; } set { Session["experiment"] = value;} }
-
-        [HttpPost]
-        public JsonResult Save(ExperimentModel model) {
-          var experiment = new Experiment() { Name = model.Name };
-          var stack = new Stack<Node>();
-
-          foreach (var node in model.Experiment.children)
-            stack.Push(node);
-
-          while (stack.Count > 0) {
-            var currentNode = stack.Pop();
-            // add children to stack
-            if (currentNode.children != null)
-              foreach (var node in currentNode.children)
-                stack.Push(node);
-
-            // TODO: Do we really need a tree of scenarios? Or is it a simple list?
-            // push currentNode to experiment
-            if (currentNode.title == "Experiment")
-              continue;
-            experiment.Algorithm.Add(new Algorithm() { Name = currentNode.title });
-          }
-
-          var ok = ControllerService.withControllerService<bool>((service) => {
-            User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-            return service.SaveExperiment(u, experiment);
-          });
-
-          if (ok)
-            return Json(new { success = true, data = model.Name });
-          return Json(new { success = false, error = "Experiment already exists!" });
+        public ActionResult NewEdit() {
+          return View();
         }
 
@@ -91,5 +60,5 @@
           var experiments = ControllerService.withControllerService<IEnumerable<string>>((service) => {
             User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-            return service.GetExperiments(u);
+            return service.GetExperimentNames(u);
           });
           foreach (var experiment in experiments)
@@ -106,89 +75,64 @@
         }
 
-        [HttpPost]
-        public JsonResult SaveExperiment([ExperimentJson] Experiment experiment) {
-          return Json(
-            new { 
-              Success = ControllerService.withControllerService<bool>(service => {
+      #region new experiment methdos
+      [HttpGet]
+      public ActionResult Scenario(string nodeId) {
+        var optimizationScenario = ControllerService.withControllerService<OptimizationScenario>(service => {
+          return service.GetOptimizationScenarioByName(nodeId);
+        });
+        return Content(AlgorithmConverter.ConvertScenarioToJson(optimizationScenario).ToString(), "application/json", System.Text.Encoding.UTF8);
+      }
+
+      [HttpGet]
+      public ActionResult ExperimentList() {
+        var experiments = ControllerService.withControllerService<IEnumerable<Experiment>>(service => {
+          User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
+          return service.GetExperiments(u);
+        });
+        return Content(AlgorithmConverter.ConvertExperimentsToJson(experiments).ToString(), "application/json", System.Text.Encoding.UTF8);
+      }
+
+      [HttpGet]
+      public ActionResult ScenarioList() {
+        var scenarios = ControllerService.withControllerService<IEnumerable<OptimizationScenario>>(service => {
+          User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
+          return service.GetOptimizationScenarios();
+        });
+        return Content(AlgorithmConverter.ConvertScenariosToJson(scenarios).ToString(), "application/json", System.Text.Encoding.UTF8);
+      }
+
+      [HttpPut]
+      public JsonResult Experiment(string nodeId, [ExperimentJson] Experiment exp) {
+        return Json(
+            new {
+              nodeId = ControllerService.withControllerService<string>(service => {
                 User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-                return service.SaveExperiment(u, experiment);
+                return service.SaveExperiment(u, exp);
               })
             });
-        }
+      }
 
-        private JsonResult ComplexExperimentCase(string scenario) {
-          // complex case, find children of experiment
-          var model = new GetParametersModel() { Type = "Complex" };
-          model.Subnames = new List<string>();
+      [HttpPost]
+      public JsonResult Experiment([ExperimentJson] Experiment exp) {
+        return Json(
+            new {
+              nodeId = ControllerService.withControllerService<string>(service => {
+                User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
+                return service.SaveExperiment(u, exp);
+              })
+            });
+      }
 
-          var experiment = ControllerService.withControllerService<Experiment>(service => {
-            User user = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-            return service.GetExperimentByName(user, scenario);
-          });
 
-          // TODO: Make a tree out of subnames!!
-          if (experiment != null)
-            foreach (var algo in experiment.Algorithm)
-              model.Subnames.Add(algo.Name);
-
-          return Json(model, JsonRequestBehavior.AllowGet);
-        }
-
-        [HttpGet]
-        public JsonResult GetParameters(string scenario, string context, int index) {
-          if (context == "Experiment" || context == null) {
-            var optimizationScenario = ControllerService.withControllerService<OptimizationScenario>(service => {
-              return service.GetOptimizationScenarioByName(scenario);
-            });
-
-            if (optimizationScenario != null) {
-              var model = new GetParametersModel() { Type = "Basic" };
-              model.Algorithm = optimizationScenario.FirstAlgorithm;
-              model.Algorithm.Mapper = optimizationScenario.Id;
-              var json = Json(model, JsonRequestBehavior.AllowGet);
-              return json;
-            }
-            else {
-              // complex case, find children of experiment
-              var model = new GetParametersModel() { Type = "Complex" };
-              model.Subnames = new List<string>();
-
-              var experiment = ControllerService.withControllerService<Experiment>(service => {
-                User user = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-                return service.GetExperimentByName(user, scenario);
-              });
-
-              // TODO: Make a tree out of subnames!!
-              if (experiment != null)
-                foreach (var algo in experiment.Algorithm)
-                  model.Subnames.Add(algo.Name);
-
-              return Json(model, JsonRequestBehavior.AllowGet);
-            }
-          }
-          else {
-            var experiment = ControllerService.withControllerService<Experiment>(service => {
-              User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
-              return service.GetExperimentByName(u, context);
-            });
-
-            // if child algorithm is a complex algorithm, return as such
-            if (experiment.Algorithm[index].ChildAlgorithms.Count > 0) {
-              var model = new GetParametersModel() { Type = "Complex" };
-              model.Subnames = new List<string>();
-              foreach (var algo in experiment.Algorithm[index].ChildAlgorithms)
-                model.Subnames.Add(algo.Name);
-              return Json(model, JsonRequestBehavior.AllowGet);
-            }
-            else {
-              // otherwise we have the algorithms just below it
-              var model = new GetParametersModel() { Type = "Basic" };
-              model.Algorithm = experiment.Algorithm[index];
-              model.Algorithm.Mapper = experiment.Algorithm[index].Name;
-              var json = Json(model, JsonRequestBehavior.AllowGet);
-              return json;
-            }
-          }
-        }
+      [HttpGet]
+      public ActionResult Experiment(string nodeId) {
+        var experiment = ControllerService.withControllerService<Experiment>(service => {
+          User u = new User() { Username = Membership.GetUser().UserName, Password = Session["pw"] as string };
+          return service.GetExperimentById(u, nodeId);
+        });
+        var str = AlgorithmConverter.ConvertExperimentToJson(experiment).ToString();
+        return Content(str, "application/json", System.Text.Encoding.UTF8);
+      }
+      #endregion
     }
 }
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/HeuristicLab.Services.Optimization.Web.csproj
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/HeuristicLab.Services.Optimization.Web.csproj	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/HeuristicLab.Services.Optimization.Web.csproj	(revision 9305)
@@ -51,4 +51,7 @@
       <HintPath>..\packages\WindowsAzure.Storage.1.7.0.0\lib\net35-full\Microsoft.WindowsAzure.StorageClient.dll</HintPath>
     </Reference>
+    <Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
+    </Reference>
     <Reference Include="System.Data.Entity" />
     <Reference Include="System.Data.Services.Client" />
@@ -98,4 +101,8 @@
   <ItemGroup>
     <Content Include="Content\ajax-loader.gif" />
+    <Content Include="Content\backbone\backbone-min.js" />
+    <Content Include="Content\backbone\backbone-relational.js" />
+    <Content Include="Content\backbone\underscore-min.js" />
+    <Content Include="Content\backbone\underscore.js" />
     <Content Include="Content\Datatypemapping.js" />
     <Content Include="Content\dynatree\dist\jquery.dynatree.min.js" />
@@ -106,4 +113,7 @@
     <Content Include="Content\dynatree\src\skin\vline-rtl.gif" />
     <Content Include="Content\dynatree\src\skin\vline.gif" />
+    <Content Include="Content\experiment.controller.js" />
+    <Content Include="Content\experiment.model.js" />
+    <Content Include="Content\experiment.view.js" />
     <Content Include="Content\ExperimentSupport.js" />
     <Compile Include="Binders\AlgorithmJsonAttribute.cs" />
@@ -373,4 +383,5 @@
     <Content Include="Views\Status\Index.cshtml" />
     <Content Include="Views\Experiment\New.cshtml" />
+    <Content Include="Views\Experiment\NewEdit.cshtml" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/Index.cshtml
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/Index.cshtml	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/Index.cshtml	(revision 9305)
@@ -60,6 +60,6 @@
 @section submenu {
     <ul>
-        <li class="selected">@Html.ActionLink("Build", "Index")</li>    
-        <li>@Html.ActionLink("Edit", "Edit")</li>
+        <li class="selected">@Html.ActionLink("Build", "New")</li>    
+        <li>@Html.ActionLink("Edit", "NewEdit")</li>
         <li>@Html.ActionLink("Status", "Index", "Status")</li>
     </ul>
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/New.cshtml
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/New.cshtml	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/New.cshtml	(revision 9305)
@@ -1,423 +1,108 @@
 ﻿@model HeuristicLab.Services.Optimization.Web.Models.ExperimentViewModel
-@{
-    ViewBag.Title = "Index.cshtml";
-}
-
+<script type="text/template" id="node_template">
+  <li><%= title%></li>  
+</script>
+
+<script type="text/template" id="tree_template">
+  <div id="<%= id %>" class="treeStyle">
+    <ul>      
+    </ul>
+  </div>
+</script>
+
+<script type="text/template" id="draggable_template">
+  <div id="<%= nodeId %>" data-isExperiment="<%= isExperiment%>" data-name="<%= title%>" class="dragables"><p><%= title %></p></div>
+</script>
+
+<script type="text/template" id="stepwizard_template">
+  <input style="margin-top:5px;" class="remove-button" name="RemoveNode" type="button" value="Remove" disabled="disabled" />
+  <input style="margin-top:5px;" class="variate-button" name="VariateNode" type="button" value="Variate" disabled="disabled" />
+</script>           
+
+<script type="text/template" id="parameter_template">
+<div>
+  <div class="leftEntry"><%= Name %>:</div>
+  <div class="rightEntry"></div>  
+</div>
+</script>       
+
+<script type="text/template" id="validationhints_template">
+<div class="validator" title="<%= header %>">
+  <p><span class="ui-icon ui-icon-alert" style="float: left; margin: 0 7px 20px 0;"></span>
+  <%= text %>
+  </p>
+</div>
+</script>       
+
+<script type="text/template" id="variationdialog_template">
+<div class="variationDialog" title="Variate algorithm">
+  <h1>Select parameter</h1>
+  <select class="variationDialogContent">  
+  </select>
+  <input type="checkbox" name="active" /> Active
+  <div class="variationDetails">          
+  </div>  
+</div>
+</script>       
+
+<script type="text/template" id="variationentry_template">
+<option data-index='<%= Index %>'><%= Name %></option>
+</script>       
+
+<script type="text/template" id="variationcontent_template">
+<div>
+  
+  
+</div>
+</script>       
+
+<script type="text/template" id="variation_boolean_template">
+<p>Boolean paramter: True / False</p>
+</script>       
+
+<script type="text/template" id="variation_number_template">
+<div>
+    <input type="button" name="generate" value="Generate..."/>    
+    <div></div>
+</div>
+</script>       
+
+<script type="text/template" id="checkbox_template">
+<div>
+    <input type="checkbox" name="<%=name%>"/><%=text%><br/>
+</div>
+</script>       
+
+<script type="text/template" id="variation_generator_template">
+<div title="Generate Values">
+ <div class="leftEntry">Minimum:</div>
+ <div class="rightEntry"><input type="text" name="minimum" value="0"/></div>  
+ <div class="leftEntry">Maximum:</div>
+ <div class="rightEntry"><input type="text" name="maximum" value="2"/></div>  
+ <div class="leftEntry">Step:</div>
+ <div class="rightEntry"><input type="text" name="step" value="1"/></div>  
+</div>
+</script>       
+
+<script type="text/template" id="loading_template">
+<div>
+  <p>Loading ... </p>
+  <img src="@Url.Content("~/Content/ajax-loader.gif")" alt="Loading animation" class="loader" />  
+</div>
+</script>    
 
 <script type="text/javascript">
-  function Experiment() {
-    this.algorithms = [];
-    this.nodeMapping = {};
-    this.name = "My Experiment"
-  }
-    
-  Experiment.prototype.buildAndStore = function(node, index, tree, ctx) {
-    var result = this.build(node, index, tree, 'Experiment');
-    this.algorithms = result.algorithms;
-    this.nodeMapping = result.mapping;
-  }
-
-  Experiment.prototype.build = function(node, index, tree, ctx) {
-    var stack = new Array();
-    var root = {name : node.title, children: [], context: ctx};
-    var nodeMapping = {};
-    this.fetchNode(root, node, index, tree);
-    if (node.title != 'Experiment')
-      nodeMapping[node.key] = root;
-      
-    if (node.children)
-        for (var i=0; i < node.children.length; i++) { 
-          stack.push({parent: root, child: node.children[node.children.length - 1 - i], index: node.children.length - 1 - i});
-        }
-    while(stack.length > 0) {
-      var tmp = stack.pop();
-      var parent = tmp.parent;
-      var child = tmp.child;
-      var newChild = { name : child.title, children: [], context: ctx}
-      // fetch the nodes from the server
-      this.fetchNode(newChild, child, tmp.index, tree);
-      nodeMapping[tmp.child.key] = newChild;      
-      parent.children.push(newChild);
-      if (child.children)
-          for (var i=0; i < child.children.length; i++) {
-            stack.push({parent: newChild, child: child.children[child.children.length - 1 - i], index: child.children.length - 1 - i});
-          }
-    }
-    return {algorithms: root, mapping: nodeMapping};
-  }
-
-  Experiment.prototype.fetchNode = function(modelNode, dynaNode, index, tree) {
-    if (modelNode.name == 'Experiment')
-      return;
-    var self = this;
-    var node = tree.getNodeByKey(dynaNode.key);
-    $.ajax({
-        type: "GET",
-        url: '/Experiment/GetParameters',
-        data: { scenario: modelNode.name, context: modelNode.context, index: index },
-        contentType: "application/json; charset=utf-8",
-        dataType: "json",
-        success: function (result) {
-            if (result.Type == "Complex") {
-                // extend the tree element
-                node.removeChildren();
-                var parent = self.nodeMapping[node.data.key];
-                for (var i=0; i < result.Subnames.length; i++) {
-                    // add node to dynatree
-                    var childNode = node.addChild({title: result.Subnames[i]});
-                    var internalResult = self.build(childNode.toDict(true), i, tree, modelNode.name);  
-                    parent.children.push(internalResult.algorithms);
-                    for (var attrname in internalResult.mapping) { self.nodeMapping[attrname] = internalResult.mapping[attrname]; }                  
-                }
-            } else if (result.Type == "Basic") {      
-                // replace the wizard    
-                previousWizards[node.data.key] = replaceWizard(result);
-                saveParameters(previousWizards[node.data.key], node);
-            }
-        }
+    var controller = new ExperimentPageController();
+    $(document).ready(function () {
+        controller.create();
     });
-  }
-
-  Experiment.prototype.pushNode = function(parentKey, nodeKey, entryName, ctx) {
-    var entry = {name: entryName, children: [], context: ctx};
-    this.nodeMapping[parentKey].children.push(entry);
-    this.nodeMapping[nodeKey] = entry;
-  }
-
-  var experiment = new Experiment();
-  var previousWizards = {};
-
-  function saveExperiment() {
-    $('.buttonFinish').attr('disabled', 'disabled');
-    experiment["name"] = $('#name').val();
-    experiment["run"] = $('#runImmediately').attr('checked') == 'checked';
-    experiment["repititions"] = $('#repititions').val();
-    experiment["group"] = $('#group').val();
-
-     $.ajax({ type: "POST",
-      url: '/Experiment/SaveExperiment',
-      data: JSON.stringify(experiment),
-      contentType: "application/json; charset=utf-8",
-      dataType: "json",
-      success: function (result) {
-        $('#status-dialog').dialog("open");
-      },
-      error: function (result) {
-      }
-     });
-  }
-
-  function treeIndexOf(sibling) {
-    var i = 0;
-    var prev = sibling.getPrevSibling()
-    while (prev != null) {
-        i++;
-        prev = prev.getPrevSibling()
-    }
-    return i;
-  }
-
-  function saveParameters(wiz, node) {
-    var wiz = typeof wiz !== 'undefined' ? $(wiz) : $('.wizard', '#parameter-dialog-content');
-    var id = wiz.attr('id');
-    var node = typeof node !== 'undefined' ? node : $('#container2').dynatree("getActiveNode");
-    var data = {};   
-
-    for (var i = 1; i < 3; i++) {
-      var stepId = '#' + id + '_' + i;
-      var step = $(stepId);
-      if (step.length == 0) {
-        break;
-      }
-      var stepDesc = $('a[href="' + stepId + '"] span', wiz).contents()[0].data;
-      var dataEntries = $('input, select', step);
-      var entry = { };
-      var mapper = new DatatypeMapper();
-      for (var j = 0; j < dataEntries.length; j++) {
-        entry[dataEntries[j].name] = mapper.mapString(dataEntries[j].value);
-      }
-      data[stepDesc] = entry;
-    }
-    
-    // aggregate tables:
-    var mapper = new DatatypeMapper();
-    mapper.aggregateTables(data);
-
-    var experimentEntry = experiment.nodeMapping[node.data.key];
-    // we are at the node, update data
-    experimentEntry["data"] = data;
-    // Close dialog
-    $('#params').dialog("close");
-  }
-
-  function createParamterDiv(name, parameters) {
-    var mapper = new DatatypeMapper();
-    // get algorithm parameters
-    var paramsDiv = $('<div />').data("name", name);
-    for (var i=0; i < parameters.length; i++) {
-      var property = parameters[i];
-      var content = mapper.mapHtml(property.Value);
-
-      // add to canvas              
-      $('<h2/>').text(property.Value.Name).appendTo(paramsDiv);
-      content.appendTo(paramsDiv);
-      $('<div />').addClass('clearer').appendTo(paramsDiv);
-    }
-    return paramsDiv;
-  }
-
-  function createWizard(name, steps) {
-    var wizard = $('<div />', { id: name }).addClass('wizard swMain');
-    var stepMenu = $('<ul />');
-    var prefix = name + '_';
-    for (var i = 0; i < steps.length; i++) {
-      var step = steps[i];
-      var a = $('<a></a>', {href: '#' + prefix + (i + 1)});
-      $('<label />').addClass("stepNumber").text((i + 1)).appendTo(a);
-      $('<span />').addClass("stepDesc").html(step.heading + '<br /><small>' + step.description + '</small>').appendTo(a);
-      var li = $('<li />').append(a);
-      li.appendTo(stepMenu);
-    }
-    stepMenu.appendTo(wizard);
-    for (var i = 0; i < steps.length; i++) {
-      var div = $('<div />', { id: prefix + (i + 1) });
-      if (steps[i].content && steps[i].content != null)
-        div.append(steps[i].content);
-      div.appendTo(wizard);
-    }
-    return wizard;
-  }
-
-
-  function makeTree() {
-    var tree = $('#container').dynatree({
-      onActivate: function (node) {
-        if (node && node.data.title !== "Experiment") {
-          $('input[name="RemoveNode"]').removeAttr("disabled");
-        } else if (node) {
-          $('input[name="RemoveNode"]').attr("disabled", "disabled");
-        }
-      },
-      onDeactivate: function (node) {
-      },
-      dnd: {
-        autoExpandMS: 750,
-
-        onDragStart: function (node) {
-          return node.data.title !== "Experiment";
-        },
-        onDragEnter: function (node, sourceNode) {
-          return true;
-        },
-        onDragOver: function (node, sourceNode, hitMode) {
-          return node.data.title !== "Experiment" || hitMode === "over";
-        },
-        onDrop: function (node, sourceNode, hitMode, ui, draggable) {
-          if (sourceNode) {
-            sourceNode.move(node, hitMode);
-          }
-          else {
-            newNode = { title: draggable.element[0].id };
-            if (hitMode == "over") {
-              node.addChild(newNode);
-              node.expand(true);
-            }
-            else if (hitMode == "before") {
-              node.parent.addChild(newNode, node);
-            }
-            else {
-              node.parent.addChild(newNode, node.getNextSibling());
-            }
-          }
-        },
-        onDragLeave: function (node, sourceNode) {
-        }
-      }
-    });
-
-    $('.dragables').each(function () {
-      $(this).draggable({
-        revert: true,
-        connectToDynatree: true,
-        cursorAt: { top: -5, left: -5 },
-        helper: "clone"
-      });
-    });
-
-    $('input[name="RemoveNode"]').click(function () {
-      var node = $("#container").dynatree("getActiveNode");
-      if (node && node.data.title !== "Experiment")
-        node.remove();
-      $(this).attr("disabled", "disabled");
-    });
-  }
-
-  function leaveStep(obj) {
-    var stepNum = obj.attr("rel");
-    return validateStep(stepNum);
-  }
-
-  function validateStep(stepNum) {
-    var isStepValid = true;
-    if (stepNum == 1) {
-      previousWizards = {}
-      experiment = new Experiment();
-      var data = $("#container").dynatree("getTree").toDict().children;
-      $("#container2").dynatree({
-        onActivate: editElement
-      });
-      var rootNode = $("#container2").dynatree("getRoot");
-      rootNode.removeChildren();
-      rootNode.addChild(data);
-      // based on the data rebuild the experiment
-      experiment.buildAndStore(data[0], 0, $("#container2").dynatree("getTree"));
-    }
-    return isStepValid;
-  }
-
-  function resetDialog() {
-    var canvas = $('#parameter-dialog-content');
-    canvas.empty();
-    $('<img />').attr('alt', 'Loading animation').attr('src', '@Url.Content("~/Content/ajax-loader.gif")').attr('class', 'loader').appendTo(canvas);
-    $('<div />').text("Loading...").appendTo(canvas);        
-  }
-
-  globalCounter = 0;
-
-  function replaceWizard(result) {
-    var canvas = $('#parameter-dialog-content');
-    canvas.empty();
-
-    // get parameters as div
-    var algoParamsDiv = null;
-
-    if (result.Algorithm.Parameters) {
-        algoParamsDiv = createParamterDiv("Algorithm Parameters", result.Algorithm.Parameters.Items);
-    }
-
-    var problemParamsDiv = null;
-    if (result.Algorithm.Problem && result.Algorithm.Problem.Parameters) {
-     problemParamsDiv = createParamterDiv("Problem Parameters", result.Algorithm.Problem.Parameters.Items);
-    }
-
-    var wizard = null;
-    if (result.Algorithm.Problem)
-     wizard = createWizard("TestWizard", [
-        { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoParamsDiv },
-        { heading: "Problem Parameters", description: "Adjust problem parameters", content: problemParamsDiv }
-    ]);
-    else
-     wizard = createWizard("TestWizard", [
-        { heading: "Algorithm Parameters", description: "Adjust algorithm parameters", content: algoParamsDiv }
-    ]);
-
-    wizard.appendTo(canvas);
-    $(".wizard", canvas).smartWizard({
-        onFinish: saveParameters,
-        labelFinish: 'Save'
-    });
-    return wizard;
-  }
-
-  function editElement(node) {
-    resetDialog();
-    $("#params").dialog("open");
-    // if we opened this windows before, use the previously created wizard
-    if (node.data.key in previousWizards) {
-      var canvas = $('#parameter-dialog-content');
-      canvas.empty();  
-      $(previousWizards[node.data.key]).appendTo(canvas);
-      var wiz = $(".wizard", canvas).smartWizard({
-        onFinish: saveParameters,
-        labelFinish: 'Save'
-      });
-      // we have to recreate the wizard, otherwise it is not responsive -> remove some multiple occuring wizard elements.
-      var parent = $('.stepContainer.content', wiz).parent('.stepContainer:not(.content)');
-      var elem = $('.stepContainer.content .content', wiz).detach();
-      elem.appendTo(parent);
-      $('.stepContainer.content', wiz).detach();
-      $('.actionBar.content', wiz).detach();
-    }
-    else {
-        $.ajax({
-          type: "GET",
-          url: '/Experiment/GetParameters',
-          data: { scenario: node.data.title, context: experiment.nodeMapping[node.data.key].context, index: treeIndexOf(node) },
-          contentType: "application/json; charset=utf-8",
-          dataType: "json",
-          success: function (result) {
-            if (result.Type == "Complex") $("#params").dialog("close");
-            /*if (result.Type == "Complex") {
-              // extend the tree element     
-              $("#params").dialog("close");     
-              node.removeChildren();
-              for (var i=0; i < result.Subnames.length; i++) {
-                // add node to dynatree
-                var childNode = node.addChild({title: result.Subnames[i]});
-                // add node to model
-                experiment.pushNode(node.data.key, childNode.data.key, result.Subnames[i], node.data.title);
-              }
-            } else*/
-            if (result.Type == "Basic") {      
-              // replace the wizard    
-              previousWizards[node.data.key] = replaceWizard(result);
-              saveParameters(previousWizards[node.data.key], node);
-            }
-          }
-        });
-     }
-  }
-
-  $(document).ready(function () {
-    var wiz = $(".wizard").smartWizard({
-      /*updateHeight: false*/
-      onLeaveStep: leaveStep,
-      onFinish: saveExperiment
-    });
-    makeTree();
-
-    $("#params").dialog({
-      autoOpen: false,
-      width: 1024,
-      height: 768,
-      /*modal: true,
-      buttons: {
-        "Save parameters": function () {
-          
-        }
-        ,
-        "Cancel": function () {
-          $(this).dialog("close");
-        }
-      },
-      close: function () {
-      }*/
-    });
-
-    $("#status-dialog").dialog({
-      autoOpen: false,
-      resizable: false,
-      height: 100,
-      modal: true,
-      buttons: { "OK" : function() { $(this).dialog('close'); $('.buttonFinish').removeAttr('disabled'); } }
-    });
-
-    $('#runImmediately').change(
-        function() {
-            if ($(this).is(':checked')) {
-                $('#repititions').removeAttr('disabled');
-                $('#group').removeAttr('disabled');
-            }
-            else {
-                $('#repititions').attr('disabled', 'disabled');
-                $('#group').attr('disabled', 'disabled');
-            }
-        });
-  });
-</script>
+</script>
+<noscript>
+    <p>You need JavaScript to view this page! Please activate it in your browser settings.</p>
+</noscript>
 
 <h2>Experiment</h2>
 
-<div class="wizard swMain">
+<div id="stepWizard" class="wizard swMain">
   <ul>
     <li>
@@ -450,45 +135,19 @@
   </ul>
   <div id="step1">
-  @using (Html.BeginForm("TreeData", "Tree"))
-  {
   <fieldset>    
     <div>
     <p>Experiment Tree:</p>
     </div>
-
-    <div id="container" class="treeStyle">
-      <ul>
-        <li id="root">Experiment</li>
-      </ul>
-    </div>
-    <input style="margin-top:5px;"  name="RemoveNode" type="button" value="Remove" disabled="disabled" />
-
-    
-     @if (Model.Scenarios.Count > 0) {
-        <p>Choose an algorithm:</p>
-        foreach (var scen in Model.Scenarios) {
-            <div class="dragables" id="@scen"><p>@scen</p></div>
-        }        
-        <div class="clearer"/>    
-     }
-    
-    @if (Model.Scenarios.Count > 0) {
-        <p>Choose from your stored experiments:</p>
-        foreach (var scen in Model.Experiments) {
-            <div class="dragables" id="@scen"><p>@scen</p></div>
-        }
-        <div class="clearer"/>
-    }   
-    
+    <div id="container" class="treeStyle"></div>    
+    <p>Choose an algorithm:</p>
+    <div id="algorithms"></div>
+    <div class="clearer"></div>
+    <p>Choose from your stored experiments:</p>
+    <div id="experiments"></div>
   </fieldset>
-  }
   </div>
   <div id="step2">
-  <h2>Click on a experiment to adjust its parameters:</h2>
-  <div id="container2" class="treeStyle">
-      <ul>
-        <li>Experiment</li>
-      </ul>
-    </div>
+    <h2>Click on a experiment to adjust its parameters:</h2>
+    <div id="container2" class="treeStyle"></div>
   </div>
   <div id="step3">
@@ -524,19 +183,20 @@
       <input id="group" name="Group" type="text" value="TESTAZURE" disabled="disabled" />
     </div>
-  </div>  
-</div>
-
-<div id="status-dialog">
-    <div>
-      Experiment has been saved!
-    </div>            
-</div>
-
-<div id="params" title="Update parameters">
-    <!-- Note: The content must be loaded asynchronously -->
-    <div id="parameter-dialog-content">
-      <img src="@Url.Content("~/Content/ajax-loader.gif")" alt="Loading animation" class="loader" />
-      Loading ...      
-    </div>
-            
-  </div>
+  </div>    
+</div>
+
+<div id="parameterDialog">
+  <div id="parameterDialog-content">
+  </div>
+</div>
+
+<div id="loadingDialog">
+</div>
+
+@section submenu {
+    <ul>
+        <li class="selected">@Html.ActionLink("Build", "New")</li>    
+        <li>@Html.ActionLink("Edit", "NewEdit")</li>
+        <li>@Html.ActionLink("Status", "Index", "Status")</li>
+    </ul>
+}
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/NewEdit.cshtml
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/NewEdit.cshtml	(revision 9305)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Experiment/NewEdit.cshtml	(revision 9305)
@@ -0,0 +1,216 @@
+﻿@model HeuristicLab.Services.Optimization.Web.Models.ExperimentViewModel
+<script type="text/template" id="node_template">
+  <li><%= title%></li>  
+</script>
+
+<script type="text/template" id="tree_template">
+  <div id="<%= id %>" class="treeStyle">
+    <ul>      
+    </ul>
+  </div>
+</script>
+
+<script type="text/template" id="draggable_template">
+  <div id="<%= nodeId %>" data-isExperiment="<%= isExperiment%>" data-name="<%= title%>" class="dragables"><p><%= title %></p></div>
+</script>
+
+<script type="text/template" id="stepwizard_template">
+  <input style="margin-top:5px;" class="remove-button" name="RemoveNode" type="button" value="Remove" disabled="disabled" />
+  <input style="margin-top:5px;" class="variate-button" name="VariateNode" type="button" value="Variate" disabled="disabled" />
+</script>           
+
+<script type="text/template" id="parameter_template">
+<div>
+  <div class="leftEntry"><%= Name %>:</div>
+  <div class="rightEntry"></div>  
+</div>
+</script>       
+
+<script type="text/template" id="validationhints_template">
+<div class="validator" title="<%= header %>">
+  <p><span class="ui-icon ui-icon-alert" style="float: left; margin: 0 7px 20px 0;"></span>
+  <%= text %>
+  </p>
+</div>
+</script>       
+
+<script type="text/template" id="variationdialog_template">
+<div class="variationDialog" title="Variate algorithm">
+  <h1>Select parameter</h1>
+  <select class="variationDialogContent">  
+  </select>
+  <input type="checkbox" name="active" /> Active
+  <div class="variationDetails">          
+  </div>  
+</div>
+</script>       
+
+<script type="text/template" id="variationentry_template">
+<option data-index='<%= Index %>'><%= Name %></option>
+</script>       
+
+<script type="text/template" id="variationcontent_template">
+<div>
+  
+  
+</div>
+</script>       
+
+<script type="text/template" id="variation_boolean_template">
+<p>Boolean paramter: True / False</p>
+</script>       
+
+<script type="text/template" id="variation_number_template">
+<div>
+    <input type="button" name="generate" value="Generate..."/>    
+    <div></div>
+</div>
+</script>       
+
+<script type="text/template" id="checkbox_template">
+<div>
+    <input type="checkbox" name="<%=name%>"/><%=text%><br/>
+</div>
+</script>       
+
+<script type="text/template" id="variation_generator_template">
+<div title="Generate Values">
+ <div class="leftEntry">Minimum:</div>
+ <div class="rightEntry"><input type="text" name="minimum" value="0"/></div>  
+ <div class="leftEntry">Maximum:</div>
+ <div class="rightEntry"><input type="text" name="maximum" value="2"/></div>  
+ <div class="leftEntry">Step:</div>
+ <div class="rightEntry"><input type="text" name="step" value="1"/></div>  
+</div>
+</script>       
+
+<script type="text/template" id="loading_template">
+<div>
+  <p>Loading ... </p>
+  <img src="@Url.Content("~/Content/ajax-loader.gif")" alt="Loading animation" class="loader" />  
+</div>
+</script>    
+
+<script type="text/javascript">
+    var controller = new ExperimentEditPageController();
+    $(document).ready(function () {
+        controller.create();
+    });
+</script>
+<noscript>
+    <p>You need JavaScript to view this page! Please activate it in your browser settings.</p>
+</noscript>
+
+<h2>Experiment</h2>
+
+<div id="stepWizard" class="wizard swMain">
+  <ul>
+    <li>
+      <a href="#step1">
+        <label class="stepNumber">1</label>
+        <span class="stepDesc">
+          Select experiment<br />
+          <small>Edit one of your experiments</small>
+        </span>
+      </a>
+    </li>
+    <li>
+      <a href="#step2">
+        <label class="stepNumber">2</label>
+        <span class="stepDesc">
+          Structure<br />
+          <small>Modify experiment structure</small>
+        </span>
+      </a>
+    </li>
+    <li>
+      <a href="#step3">
+        <label class="stepNumber">3</label>
+        <span class="stepDesc">
+          Customization<br />
+          <small>Change experiment parameters</small>
+        </span>
+      </a>
+    </li>
+    <li>
+      <a href="#step4">
+        <label class="stepNumber">4</label>
+        <span class="stepDesc">
+          Experiment Details<br />
+          <small>Set name of experiment/Run experiment</small>
+        </span>
+      </a>
+    </li>
+  </ul>
+  <div id="step1">
+  <fieldset>        
+    <div id="experiments"></div>
+  </fieldset>
+  </div>
+  <div id="step2">
+  <fieldset>    
+    <div>
+    <p>Experiment Tree:</p>
+    </div>
+    <div id="container" class="treeStyle"></div>    
+    <p>Choose an algorithm:</p>
+    <div id="draggableAlgorithms"></div>
+    <div class="clearer"></div>
+    <p>Choose from your stored experiments:</p>
+    <div id="draggableExperiments"></div>
+  </fieldset>
+  </div>
+  <div id="step3">
+    <h2>Click on a experiment to adjust its parameters:</h2>
+    <div id="container2" class="treeStyle"></div>
+  </div>
+  <div id="step4">
+    <div class="editor-label">
+      <label for="name">Experiment Template Name:</label>
+    </div>
+
+    <div class="editor-field">
+      <input id="name" name="Name" type="text" value="My Experiment" />
+    </div>
+
+    <div class="editor-label">
+      <label for="name">Run experiment immediately?</label>
+    </div>
+
+    <div class="editor-field">
+      <input id="runImmediately" name="RunImmediately" type="checkbox" />
+    </div>
+
+    <div class="editor-label">
+      <label for="name">Repititions</label>
+    </div>
+
+    <div class="editor-field">
+      <input id="repititions" name="Repititions" type="text" value="1" disabled="disabled" />
+    </div>
+
+    <div class="editor-label">
+      <label for="name">Group</label>
+    </div>
+
+    <div class="editor-field">
+      <input id="group" name="Group" type="text" value="TESTAZURE" disabled="disabled" />
+    </div>
+  </div>    
+</div>
+
+<div id="parameterDialog">
+  <div id="parameterDialog-content">
+  </div>
+</div>
+
+<div id="loadingDialog">
+</div>
+
+@section submenu {
+    <ul>
+        <li>@Html.ActionLink("Build", "New")</li>    
+        <li class="selected">@Html.ActionLink("Edit", "NewEdit")</li>
+        <li>@Html.ActionLink("Status", "Index", "Status")</li>
+    </ul>
+}
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Shared/_Layout.cshtml
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Shared/_Layout.cshtml	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/Views/Shared/_Layout.cshtml	(revision 9305)
@@ -35,4 +35,12 @@
     <link href="@Url.Content("~/Content/smartwizard2.0/styles/smart_wizard.css")" rel="stylesheet" type="text/css" />
     <script type="text/javascript" src="@Url.Content("~/Content/smartwizard2.0/js/jquery.smartWizard-2.0.min.js")"></script>
+
+    <script type='text/javascript' src='@Url.Content("~/Content/backbone/underscore.js")'></script>
+    <script type='text/javascript' src='@Url.Content("~/Content/backbone/backbone-min.js")'></script>
+    <script type='text/javascript' src='@Url.Content("~/Content/backbone/backbone-relational.js")'></script>
+
+    <script type='text/javascript' src='@Url.Content("~/Content/experiment.model.js")'></script>
+    <script type='text/javascript' src='@Url.Content("~/Content/experiment.view.js")'></script>
+    <script type='text/javascript' src='@Url.Content("~/Content/experiment.controller.js")'></script>
 </head>
 <body>
Index: /branches/OaaS/HeuristicLab.Services.Optimization.Web/packages.config
===================================================================
--- /branches/OaaS/HeuristicLab.Services.Optimization.Web/packages.config	(revision 9304)
+++ /branches/OaaS/HeuristicLab.Services.Optimization.Web/packages.config	(revision 9305)
@@ -11,3 +11,4 @@
   <package id="System.Web.Providers.Core" version="1.0" />
   <package id="System.Web.Providers" version="1.1" />
+  <package id="Newtonsoft.Json" version="4.5.11" />
 </packages>
