xoslib wip
diff --git a/planetstack/core/xoslib/README b/planetstack/core/xoslib/README
new file mode 100644
index 0000000..a5af121
--- /dev/null
+++ b/planetstack/core/xoslib/README
@@ -0,0 +1,7 @@
+Add to the following in settings.py
+
+ STATICFILES_DIRS=
+ "/opt/planetstack/core/xoslib/static/",
+
+ TEMPLATE_DIRS=
+ "/opt/planetstack/xoslib/templates",
\ No newline at end of file
diff --git a/planetstack/core/xoslib/dashboards/sliverListTest.html b/planetstack/core/xoslib/dashboards/sliverListTest.html
new file mode 100644
index 0000000..2238181
--- /dev/null
+++ b/planetstack/core/xoslib/dashboards/sliverListTest.html
@@ -0,0 +1,26 @@
+{% load mustache %}
+{% load straight_include %}
+
+<script src="{{ STATIC_URL }}/js/underscore-min.js"></script>
+<script src="{{ STATIC_URL }}/js/backbone-min.js"></script>
+<script src="{{ STATIC_URL }}/js/backbone-tastypie.js"></script>
+<script src="{{ STATIC_URL }}/js/ICanHaz.min.js"></script>
+
+<script src="{{ STATIC_URL }}/js/xos-backbone.js"></script>
+<script src="{{ STATIC_URL }}/js/sliverListTest.js"></script>
+
+<script type="text/html" id="sliverTemplate">
+ {% straight_include "mustache/sliverTemplate.mustache" %}
+</script>
+
+<script type="text/html" id="listApp">
+ {% straight_include "mustache/listApp.mustache" %}
+</script>
+
+<script type="text/html" id="detailApp">
+ {% straight_include "mustache/detailApp.mustache" %}
+</script>
+
+<div id="app">
+ {% mustache "mustache/listApp" %}
+</div>
diff --git a/planetstack/core/xoslib/static/js/sliverListTest.js b/planetstack/core/xoslib/static/js/sliverListTest.js
new file mode 100644
index 0000000..294ffd3
--- /dev/null
+++ b/planetstack/core/xoslib/static/js/sliverListTest.js
@@ -0,0 +1,177 @@
+(function(){
+
+window.SliverView = Backbone.View.extend({
+ tagName: 'li',
+ className: 'XOSLib.sliver',
+
+ events: {
+ 'click .permalink': 'navigate'
+ },
+
+ initialize: function(){
+ this.model.bind('change', this.render, this);
+ },
+
+ navigate: function(e){
+ this.trigger('navigate', this.model);
+ e.preventDefault();
+ },
+
+ render: function(){
+ $(this.el).html(ich.sliverTemplate(this.model.toJSON()));
+ return this;
+ }
+});
+
+
+window.DetailApp = Backbone.View.extend({
+ events: {
+ 'click .home': 'home'
+ },
+
+ home: function(e){
+ this.trigger('home');
+ e.preventDefault();
+ },
+
+ render: function(){
+ $(this.el).html(ich.detailApp(this.model.toJSON()));
+ return this;
+ }
+});
+
+window.InputView = Backbone.View.extend({
+ events: {
+ 'click .sliver': 'createSliver',
+ 'keypress #message': 'createOnEnter'
+ },
+
+ createOnEnter: function(e){
+ if((e.keyCode || e.which) == 13){
+ this.createSliver();
+ e.preventDefault();
+ }
+
+ },
+
+ createSliver: function(){
+ var message = this.$('#message').val();
+ if(message){
+ this.collection.create({
+ message: message
+ });
+ this.$('#message').val('');
+ }
+ }
+
+});
+
+window.ListView = Backbone.View.extend({
+ initialize: function(){
+ _.bindAll(this, 'addOne', 'addAll');
+
+ this.collection.bind('add', this.addOne);
+ this.collection.bind('reset', this.addAll, this);
+ this.views = [];
+ },
+
+ addAll: function(){
+ this.views = [];
+ this.collection.each(this.addOne);
+ },
+
+ addOne: function(sliver){
+ var view = new SliverView({
+ model: XOSLib.sliver
+ });
+ $(this.el).prepend(view.render().el);
+ this.views.push(view);
+ view.bind('all', this.rethrow, this);
+ },
+
+ rethrow: function(){
+ this.trigger.apply(this, arguments);
+ }
+
+});
+
+window.ListApp = Backbone.View.extend({
+ el: "#app",
+
+ rethrow: function(){
+ this.trigger.apply(this, arguments);
+ },
+
+ render: function(){
+ console.log("listApp.render");
+ $(this.el).html(ich.listApp({}));
+ var list = new ListView({
+ collection: this.collection,
+ el: this.$('#slivers')
+ });
+ list.addAll();
+ list.bind('all', this.rethrow, this);
+ new InputView({
+ collection: this.collection,
+ el: this.$('#input')
+ });
+ }
+});
+
+
+window.Router = Backbone.Router.extend({
+ routes: {
+ '': 'list',
+ ':id/': 'detail'
+ },
+
+ navigate_to: function(model){
+ var path = (model && model.get('id') + '/') || '';
+ console.log("Router.navigate_to");
+ this.navigate(path, true);
+ },
+
+ detail: function(){ console.log("Router.detail"); },
+
+ list: function(){ console.log("Router.list"); }
+});
+
+$(function(){
+ window.app = window.app || {};
+ app.router = new Router();
+ app.slivers = new XOSLib.slivers();
+ app.list = new ListApp({
+ el: $("#app"),
+ collection: app.slivers
+ });
+ app.detail = new DetailApp({
+ el: $("#app")
+ });
+ app.router.bind('route:list', function(){
+ console.log("Router:list2");
+ app.slivers.maybeFetch({
+ success: _.bind(app.list.render, app.list)
+ });
+ });
+ app.router.bind('route:detail', function(id){
+ console.log("Router:detail2");
+ app.slivers.getOrFetch(app.slivers.urlRoot + id + '/', {
+ success: function(model){
+ app.detail.model = model;
+ app.detail.render();
+ }
+ });
+ });
+
+ app.slivers.maybeFetch({
+ success: _.bind(app.list.render, app.list)
+ });
+
+ app.list.bind('navigate', app.router.navigate_to, app.router);
+ app.detail.bind('home', app.router.navigate_to, app.router);
+ Backbone.history.start({
+ pushState: true,
+ silent: app.loaded
+ });
+});
+})();
diff --git a/planetstack/core/xoslib/static/js/xos-backbone.js b/planetstack/core/xoslib/static/js/xos-backbone.js
new file mode 100644
index 0000000..99f0784
--- /dev/null
+++ b/planetstack/core/xoslib/static/js/xos-backbone.js
@@ -0,0 +1,49 @@
+SLIVER_API = "/plstackapi/slivers/";
+
+XOSCollection = Backbone.Collection.extend({
+ maybeFetch: function(options){
+ // Helper function to fetch only if this collection has not been fetched before.
+ if(this._fetched){
+ // If this has already been fetched, call the success, if it exists
+ options.success && options.success();
+ console.log("alreadyFetched");
+ return;
+ }
+
+ // when the original success function completes mark this collection as fetched
+ var self = this,
+ successWrapper = function(success){
+ return function(){
+ self._fetched = true;
+ success && success.apply(this, arguments);
+ };
+ };
+ options.success = successWrapper(options.success);
+ console.log("call fetch");
+ this.fetch(options);
+ },
+
+ getOrFetch: function(id, options){
+ // Helper function to use this collection as a cache for models on the server
+ var model = this.get(id);
+
+ if(model){
+ options.success && options.success(model);
+ return;
+ }
+
+ model = new Sliver({
+ resource_uri: id
+ });
+
+ model.fetch(options);
+ }
+});
+
+function xoslib() {
+ this.sliver = Backbone.Model.extend({ urlRoot: SLIVER_API });
+ this.slivers = XOSCollection.extend({ urlRoot: SLIVER_API,
+ model: this.sliver});
+};
+
+XOSLib = new xoslib();
diff --git a/planetstack/core/xoslib/templates/mustache/detailApp.mustache b/planetstack/core/xoslib/templates/mustache/detailApp.mustache
new file mode 100644
index 0000000..e415945
--- /dev/null
+++ b/planetstack/core/xoslib/templates/mustache/detailApp.mustache
@@ -0,0 +1,8 @@
+<h2>
+ <a class="home" href="/">All Slivers</a>
+</h2>
+<ul id="slivers">
+ <li class="sliver"
+ {{>sliverTemplate}}
+ </li>
+</ul>
diff --git a/planetstack/core/xoslib/templates/mustache/listApp.mustache b/planetstack/core/xoslib/templates/mustache/listApp.mustache
new file mode 100644
index 0000000..3af7ebe
--- /dev/null
+++ b/planetstack/core/xoslib/templates/mustache/listApp.mustache
@@ -0,0 +1,3 @@
+<h2>All Slivers</h2>
+<ul id="slivers">
+</ul>
diff --git a/planetstack/core/xoslib/templates/mustache/sliverTemplate.mustache b/planetstack/core/xoslib/templates/mustache/sliverTemplate.mustache
new file mode 100644
index 0000000..9959a19
--- /dev/null
+++ b/planetstack/core/xoslib/templates/mustache/sliverTemplate.mustache
@@ -0,0 +1 @@
+<a class="permalink" href="/{{id}}/">{{ name }}</a>
diff --git a/planetstack/core/xoslib/templatetags/mustache.py b/planetstack/core/xoslib/templatetags/mustache.py
new file mode 100644
index 0000000..a3b3b2a
--- /dev/null
+++ b/planetstack/core/xoslib/templatetags/mustache.py
@@ -0,0 +1,51 @@
+from django import template
+from django.conf import settings
+import pystache
+import os
+
+register = template.Library()
+
+class View(pystache.View):
+ template_path = settings.TEMPLATE_DIRS[0]
+
+ def __init__(self, template_dir, template_name, context):
+ self.template_path = template_dir
+ self.template_name = template_name
+ return super(View, self).__init__(context=context)
+
+class MustacheNode(template.Node):
+ def __init__(self, template_name, attr=None):
+ for template_dir in settings.TEMPLATE_DIRS:
+ if os.path.exists(os.path.join(template_dir, template_name) + ".mustache"):
+ break
+ else:
+ raise IOError("failed to find %s in %s" % (template_name, str(settings.TEMPLATE_DIRS)))
+
+ self.template_path = template_dir
+ self.template = template_name
+ self.attr = attr
+
+ def render(self, context):
+ mcontext = context[self.attr] if self.attr else {}
+ view = View(self.template_path, self.template, context=mcontext)
+ return view.render()
+
+def do_mustache(parser, token):
+ """
+ Loads a mustache template and render it inline
+
+ Example::
+
+ {% mustache "foo/bar" data %}
+
+ """
+ bits = token.split_contents()
+ if len(bits) not in [2,3]:
+ raise template.TemplateSyntaxError("%r tag takes two arguments: the location of the template file, and the template context" % bits[0])
+ path = bits[1]
+ path = path[1:-1]
+ attrs = bits[2:]
+ return MustacheNode(path, *attrs)
+
+
+register.tag("mustache", do_mustache)
diff --git a/planetstack/core/xoslib/templatetags/straight_include.py b/planetstack/core/xoslib/templatetags/straight_include.py
new file mode 100644
index 0000000..54710f3
--- /dev/null
+++ b/planetstack/core/xoslib/templatetags/straight_include.py
@@ -0,0 +1,62 @@
+"""
+Straight Include template tag by @HenrikJoreteg
+
+Django templates don't give us any way to escape template tags.
+
+So if you ever need to include client side templates for ICanHaz.js (or anything else that
+may confuse django's templating engine) You can is this little snippet.
+
+Just use it as you would a normal {% include %} tag. It just won't process the included text.
+
+It assumes your included templates are in you django templates directory.
+
+Usage:
+
+{% load straight_include %}
+
+{% straight_include "my_icanhaz_templates.html" %}
+
+"""
+
+import os
+from django import template
+from django.conf import settings
+
+
+register = template.Library()
+
+
+class StraightIncludeNode(template.Node):
+ def __init__(self, template_path):
+ for template_dir in settings.TEMPLATE_DIRS:
+ self.filepath = '%s/%s' % (template_dir, template_path)
+ if os.path.exists(self.filepath):
+ break
+ else:
+ raise IOError("cannot find %s in %s" % (template_path, str(TEMPLATE_DIRS)))
+
+ def render(self, context):
+ fp = open(self.filepath, 'r')
+ output = fp.read()
+ fp.close()
+ return output
+
+
+def do_straight_include(parser, token):
+ """
+ Loads a template and includes it without processing it
+
+ Example::
+
+ {% straight_include "foo/some_include" %}
+
+ """
+ bits = token.split_contents()
+ if len(bits) != 2:
+ raise template.TemplateSyntaxError("%r tag takes one argument: the location of the file within the template folder" % bits[0])
+ path = bits[1][1:-1]
+
+ return StraightIncludeNode(path)
+
+
+register.tag("straight_include", do_straight_include)
diff --git a/planetstack/core/xoslib/up.sh b/planetstack/core/xoslib/up.sh
new file mode 100755
index 0000000..1121892
--- /dev/null
+++ b/planetstack/core/xoslib/up.sh
@@ -0,0 +1,3 @@
+scp static/js/*.js princeton_planetstack@node43.princeton.vicci.org:/opt/planetstack/core/xoslib/static/js/
+scp templates/mustache/*.mustache princeton_planetstack@node43.princeton.vicci.org:/opt/planetstack/core/xoslib/templates/mustache/
+scp dashboards/*.html princeton_planetstack@node43.princeton.vicci.org:/opt/planetstack/templates/admin/dashboard/