check in missing js file
diff --git a/planetstack/core/xoslib/static/js/vendor/backbone.syphon.js b/planetstack/core/xoslib/static/js/vendor/backbone.syphon.js
new file mode 100644
index 0000000..3cd1537
--- /dev/null
+++ b/planetstack/core/xoslib/static/js/vendor/backbone.syphon.js
@@ -0,0 +1,469 @@
+// Backbone.Syphon, v0.4.1
+// Copyright (c)2012 Derick Bailey, Muted Solutions, LLC.
+// Distributed under MIT license
+// http://github.com/derickbailey/backbone.syphon
+Backbone.Syphon = (function(Backbone, $, _){
+  var Syphon = {};
+
+  // Ignore Element Types
+  // --------------------
+
+  // Tell Syphon to ignore all elements of these types. You can
+  // push new types to ignore directly in to this array.
+  Syphon.ignoredTypes = ["button", "submit", "reset", "fieldset"];
+
+  // Syphon
+  // ------
+
+  // Get a JSON object that represents
+  // all of the form inputs, in this view.
+  // Alternately, pass a form element directly
+  // in place of the view.
+  Syphon.serialize = function(view, options){
+    var data = {};
+
+    // Build the configuration
+    var config = buildConfig(options);
+
+    // Get all of the elements to process
+    var elements = getInputElements(view, config);
+
+    // Process all of the elements
+    _.each(elements, function(el){
+      var $el = $(el);
+      var type = getElementType($el); 
+
+      // Get the key for the input
+      var keyExtractor = config.keyExtractors.get(type);
+      var key = keyExtractor($el);
+
+      // Get the value for the input
+      var inputReader = config.inputReaders.get(type);
+      var value = inputReader($el);
+
+      // Get the key assignment validator and make sure
+      // it's valid before assigning the value to the key
+      var validKeyAssignment = config.keyAssignmentValidators.get(type);
+      if (validKeyAssignment($el, key, value)){
+        var keychain = config.keySplitter(key);
+        data = assignKeyValue(data, keychain, value);
+      }
+    });
+
+    // Done; send back the results.
+    return data;
+  };
+  
+  // Use the given JSON object to populate
+  // all of the form inputs, in this view.
+  // Alternately, pass a form element directly
+  // in place of the view.
+  Syphon.deserialize = function(view, data, options){
+    // Build the configuration
+    var config = buildConfig(options);
+
+    // Get all of the elements to process
+    var elements = getInputElements(view, config);
+
+    // Flatten the data structure that we are deserializing
+    var flattenedData = flattenData(config, data);
+
+    // Process all of the elements
+    _.each(elements, function(el){
+      var $el = $(el);
+      var type = getElementType($el); 
+
+      // Get the key for the input
+      var keyExtractor = config.keyExtractors.get(type);
+      var key = keyExtractor($el);
+
+      // Get the input writer and the value to write
+      var inputWriter = config.inputWriters.get(type);
+      var value = flattenedData[key];
+
+      // Write the value to the input
+      inputWriter($el, value);
+    });
+  };
+
+  // Helpers
+  // -------
+
+  // Retrieve all of the form inputs
+  // from the form
+  var getInputElements = function(view, config){
+    var form = getForm(view);
+    var elements = form.elements;
+
+    elements = _.reject(elements, function(el){
+      var reject;
+      var type = getElementType(el);
+      var extractor = config.keyExtractors.get(type);
+      var identifier = extractor($(el));
+     
+      var foundInIgnored = _.include(config.ignoredTypes, type);
+      var foundInInclude = _.include(config.include, identifier);
+      var foundInExclude = _.include(config.exclude, identifier);
+
+      if (foundInInclude){
+        reject = false;
+      } else {
+        if (config.include){
+          reject = true;
+        } else {
+          reject = (foundInExclude || foundInIgnored);
+        }
+      }
+
+      return reject;
+    });
+
+    return elements;
+  };
+
+  // Determine what type of element this is. It
+  // will either return the `type` attribute of
+  // an `<input>` element, or the `tagName` of
+  // the element when the element is not an `<input>`.
+  var getElementType = function(el){
+    var typeAttr;
+    var $el = $(el);
+    var tagName = $el[0].tagName;
+    var type = tagName;
+
+    if (tagName.toLowerCase() === "input"){
+      typeAttr = $el.attr("type");
+      if (typeAttr){
+        type = typeAttr;
+      } else {
+        type = "text";
+      }
+    }
+    
+    // Always return the type as lowercase
+    // so it can be matched to lowercase
+    // type registrations.
+    return type.toLowerCase();
+  };
+  
+  // If a form element is given, just return it. 
+  // Otherwise, get the form element from the view.
+  var getForm = function(viewOrForm){
+    if (_.isUndefined(viewOrForm.$el) && viewOrForm.tagName.toLowerCase() === 'form'){
+      return viewOrForm;
+    } else {
+      return viewOrForm.$el.is("form") ? viewOrForm.el : viewOrForm.$("form")[0];
+    }
+  };
+
+  // Build a configuration object and initialize
+  // default values.
+  var buildConfig = function(options){
+    var config = _.clone(options) || {};
+    
+    config.ignoredTypes = _.clone(Syphon.ignoredTypes);
+    config.inputReaders = config.inputReaders || Syphon.InputReaders;
+    config.inputWriters = config.inputWriters || Syphon.InputWriters;
+    config.keyExtractors = config.keyExtractors || Syphon.KeyExtractors;
+    config.keySplitter = config.keySplitter || Syphon.KeySplitter;
+    config.keyJoiner = config.keyJoiner || Syphon.KeyJoiner;
+    config.keyAssignmentValidators = config.keyAssignmentValidators || Syphon.KeyAssignmentValidators;
+    
+    return config;
+  };
+
+  // Assigns `value` to a parsed JSON key. 
+  //
+  // The first parameter is the object which will be
+  // modified to store the key/value pair.
+  //
+  // The second parameter accepts an array of keys as a 
+  // string with an option array containing a 
+  // single string as the last option.
+  //
+  // The third parameter is the value to be assigned.
+  //
+  // Examples:
+  //
+  // `["foo", "bar", "baz"] => {foo: {bar: {baz: "value"}}}`
+  // 
+  // `["foo", "bar", ["baz"]] => {foo: {bar: {baz: ["value"]}}}`
+  // 
+  // When the final value is an array with a string, the key
+  // becomes an array, and values are pushed in to the array,
+  // allowing multiple fields with the same name to be 
+  // assigned to the array.
+  var assignKeyValue = function(obj, keychain, value) {
+    if (!keychain){ return obj; }
+
+    var key = keychain.shift();
+
+    // build the current object we need to store data
+    if (!obj[key]){
+      obj[key] = _.isArray(key) ? [] : {};
+    }
+
+    // if it's the last key in the chain, assign the value directly
+    if (keychain.length === 0){
+      if (_.isArray(obj[key])){
+        obj[key].push(value);
+      } else {
+        obj[key] = value;
+      }
+    }
+
+    // recursive parsing of the array, depth-first
+    if (keychain.length > 0){
+      assignKeyValue(obj[key], keychain, value);
+    }
+    
+    return obj;
+  };
+
+  // Flatten the data structure in to nested strings, using the
+  // provided `KeyJoiner` function.
+  //
+  // Example:
+  //
+  // This input:
+  //
+  // ```js
+  // {
+  //   widget: "wombat",
+  //   foo: {
+  //     bar: "baz",
+  //     baz: {
+  //       quux: "qux"
+  //     },
+  //     quux: ["foo", "bar"]
+  //   }
+  // }
+  // ```
+  //
+  // With a KeyJoiner that uses [ ] square brackets, 
+  // should produce this output:
+  //
+  // ```js
+  // {
+  //  "widget": "wombat",
+  //  "foo[bar]": "baz",
+  //  "foo[baz][quux]": "qux",
+  //  "foo[quux]": ["foo", "bar"]
+  // }
+  // ```
+  var flattenData = function(config, data, parentKey){
+    var flatData = {};
+
+    _.each(data, function(value, keyName){
+      var hash = {};
+
+      // If there is a parent key, join it with
+      // the current, child key.
+      if (parentKey){
+        keyName = config.keyJoiner(parentKey, keyName);
+      }
+
+      if (_.isArray(value)){
+        keyName += "[]";
+        hash[keyName] = value;
+      } else if (_.isObject(value)){
+        hash = flattenData(config, value, keyName);
+      } else {
+        hash[keyName] = value;
+      }
+
+      // Store the resulting key/value pairs in the
+      // final flattened data object
+      _.extend(flatData, hash);
+    });
+
+    return flatData;
+  };
+
+  return Syphon;
+})(Backbone, jQuery, _);
+
+// Type Registry
+// -------------
+
+// Type Registries allow you to register something to
+// an input type, and retrieve either the item registered
+// for a specific type or the default registration
+Backbone.Syphon.TypeRegistry = function(){
+  this.registeredTypes = {};
+};
+
+// Borrow Backbone's `extend` keyword for our TypeRegistry
+Backbone.Syphon.TypeRegistry.extend = Backbone.Model.extend;
+
+_.extend(Backbone.Syphon.TypeRegistry.prototype, {
+
+  // Get the registered item by type. If nothing is
+  // found for the specified type, the default is
+  // returned.
+  get: function(type){
+    var item = this.registeredTypes[type];
+
+    if (!item){
+      item = this.registeredTypes["default"];
+    }
+
+    return item;
+  },
+
+  // Register a new item for a specified type
+  register: function(type, item){
+    this.registeredTypes[type] = item;
+  },
+
+  // Register a default item to be used when no
+  // item for a specified type is found
+  registerDefault: function(item){
+    this.registeredTypes["default"] = item;
+  },
+
+  // Remove an item from a given type registration
+  unregister: function(type){
+    if (this.registeredTypes[type]){
+      delete this.registeredTypes[type];
+    }
+  }
+});
+
+
+
+
+// Key Extractors
+// --------------
+
+// Key extractors produce the "key" in `{key: "value"}`
+// pairs, when serializing.
+Backbone.Syphon.KeyExtractorSet = Backbone.Syphon.TypeRegistry.extend();
+
+// Built-in Key Extractors
+Backbone.Syphon.KeyExtractors = new Backbone.Syphon.KeyExtractorSet();
+
+// The default key extractor, which uses the
+// input element's "id" attribute
+Backbone.Syphon.KeyExtractors.registerDefault(function($el){
+  return $el.prop("name");
+});
+
+
+// Input Readers
+// -------------
+
+// Input Readers are used to extract the value from
+// an input element, for the serialized object result
+Backbone.Syphon.InputReaderSet = Backbone.Syphon.TypeRegistry.extend();
+
+// Built-in Input Readers
+Backbone.Syphon.InputReaders = new Backbone.Syphon.InputReaderSet();
+
+// The default input reader, which uses an input
+// element's "value"
+Backbone.Syphon.InputReaders.registerDefault(function($el){
+  return $el.val();
+});
+
+// Checkbox reader, returning a boolean value for
+// whether or not the checkbox is checked.
+Backbone.Syphon.InputReaders.register("checkbox", function($el){
+  var checked = $el.prop("checked");
+  return checked;
+});
+
+
+// Input Writers
+// -------------
+
+// Input Writers are used to insert a value from an
+// object into an input element.
+Backbone.Syphon.InputWriterSet = Backbone.Syphon.TypeRegistry.extend();
+
+// Built-in Input Writers
+Backbone.Syphon.InputWriters = new Backbone.Syphon.InputWriterSet();
+
+// The default input writer, which sets an input
+// element's "value"
+Backbone.Syphon.InputWriters.registerDefault(function($el, value){
+  $el.val(value);
+});
+
+// Checkbox writer, set whether or not the checkbox is checked
+// depending on the boolean value.
+Backbone.Syphon.InputWriters.register("checkbox", function($el, value){
+  $el.prop("checked", value);
+});
+
+// Radio button writer, set whether or not the radio button is
+// checked.  The button should only be checked if it's value
+// equals the given value.
+Backbone.Syphon.InputWriters.register("radio", function($el, value){
+  $el.prop("checked", $el.val() === value);
+});
+
+// Key Assignment Validators
+// -------------------------
+
+// Key Assignment Validators are used to determine whether or not a
+// key should be assigned to a value, after the key and value have been
+// extracted from the element. This is the last opportunity to prevent
+// bad data from getting serialized to your object.
+
+Backbone.Syphon.KeyAssignmentValidatorSet = Backbone.Syphon.TypeRegistry.extend();
+
+// Build-in Key Assignment Validators
+Backbone.Syphon.KeyAssignmentValidators = new Backbone.Syphon.KeyAssignmentValidatorSet();
+
+// Everything is valid by default
+Backbone.Syphon.KeyAssignmentValidators.registerDefault(function(){ return true; });
+
+// But only the "checked" radio button for a given
+// radio button group is valid
+Backbone.Syphon.KeyAssignmentValidators.register("radio", function($el, key, value){ 
+  return $el.prop("checked");
+});
+
+
+// Backbone.Syphon.KeySplitter
+// ---------------------------
+
+// This function is used to split DOM element keys in to an array
+// of parts, which are then used to create a nested result structure.
+// returning `["foo", "bar"]` results in `{foo: { bar: "value" }}`.
+//
+// Override this method to use a custom key splitter, such as:
+// `<input name="foo.bar.baz">`, `return key.split(".")`
+Backbone.Syphon.KeySplitter = function(key){
+  var matches = key.match(/[^\[\]]+/g);
+
+  if (key.indexOf("[]") === key.length - 2){
+    lastKey = matches.pop();
+    matches.push([lastKey]);
+  }
+
+  return matches;
+}
+
+
+// Backbone.Syphon.KeyJoiner
+// -------------------------
+
+// Take two segments of a key and join them together, to create the
+// de-normalized key name, when deserializing a data structure back
+// in to a form.
+//
+// Example: 
+//
+// With this data strucutre `{foo: { bar: {baz: "value", quux: "another"} } }`,
+// the key joiner will be called with these parameters, and assuming the
+// join happens with "[ ]" square brackets, the specified output:
+// 
+// `KeyJoiner("foo", "bar")` //=> "foo[bar]"
+// `KeyJoiner("foo[bar]", "baz")` //=> "foo[bar][baz]"
+// `KeyJoiner("foo[bar]", "quux")` //=> "foo[bar][quux]"
+
+Backbone.Syphon.KeyJoiner = function(parentKey, childKey){
+  return parentKey + "[" + childKey + "]";
+}