xoslib wip
authorScott Baker <smbaker@gmail.com>
Tue, 8 Jul 2014 04:54:35 +0000 (21:54 -0700)
committerScott Baker <smbaker@gmail.com>
Tue, 8 Jul 2014 04:54:35 +0000 (21:54 -0700)
planetstack/core/xoslib/dashboards/sliverListTest.html
planetstack/core/xoslib/dashboards/xsh.html [new file with mode: 0644]
planetstack/core/xoslib/static/js/sliverListTest.js
planetstack/core/xoslib/static/js/xos-backbone.js
planetstack/core/xoslib/static/xsh/constants.js [new file with mode: 0644]
planetstack/core/xoslib/static/xsh/object_id.js [new file with mode: 0644]
planetstack/core/xoslib/static/xsh/shell_utils.js [new file with mode: 0644]
planetstack/core/xoslib/static/xsh/tokens.js [new file with mode: 0644]
planetstack/core/xoslib/static/xsh/utils.js [new file with mode: 0644]
planetstack/core/xoslib/static/xsh/xsh.js [new file with mode: 0644]
planetstack/core/xoslib/up.sh

index 7a5b2d4..61c54cf 100644 (file)
@@ -3,7 +3,6 @@
 
 <script src="{{ STATIC_URL }}/js/underscore-min.js"></script>
 <script src="{{ STATIC_URL }}/js/backbone-min.js"></script>
-<script src="{{ STATIC_URL }}/js/xxbackbone-tastypie.js"></script>
 <script src="{{ STATIC_URL }}/js/ICanHaz.min.js"></script>
 
 <script src="{{ STATIC_URL }}/js/xos-backbone.js"></script>
diff --git a/planetstack/core/xoslib/dashboards/xsh.html b/planetstack/core/xoslib/dashboards/xsh.html
new file mode 100644 (file)
index 0000000..6e5103a
--- /dev/null
@@ -0,0 +1,21 @@
+  <div id="terminal">
+    <p class="response">XSH - The XOS Shell</p>
+    <br />
+    <p id="terminal_help1" style="display: none;">type "help" for help</p>
+    <p id="terminal_help2" style="display: none;">type "tutorial" to start the tutorial</p>
+
+  </div>
+  <link rel="stylesheet" type="text/css" href="{% static 'shell/opencloud_shell.css' %}" media="all">
+  <script src="{{ STATIC_URL }}/js/underscore-min.js"></script>
+  <script src="{{ STATIC_URL }}/js/backbone-min.js"></script>
+  <script src="{{ STATIC_URL }}/js/ICanHaz.min.js"></script>
+  <script src="{{ STATIC_URL }}/js/xos-backbone.js"></script>
+  <script src="{% static 'xsh/xsh.js' %}"></script>
+  <script src="{% static 'xsh/object_id.js' %}"></script>
+  <script src="{% static 'xsh/constants.js' %}"></script>
+  <script src="{% static 'xsh/utils.js' %}"></script>
+  <script src="{% static 'xsh/shell_utils.js' %}"></script>
+  <script src="{% static 'xsh/tokens.js' %}"></script>
+
+
+
index 2365aa2..59970d8 100644 (file)
@@ -110,7 +110,7 @@ window.Router = Backbone.Router.extend({
 $(function(){
     window.app = window.app || {};
     app.router = new Router();
-    app.slivers = XOSLib.slivers; //new XOSLib.slivers();
+    app.slivers = xos.slivers; //new XOSLib.slivers();
     app.list = new ListApp({
         el: $("#app"),
         collection: app.slivers
index 1633768..c4f1a33 100644 (file)
@@ -1,4 +1,5 @@
 SLIVER_API = "/plstackapi/slivers/";
+SLICE_API = "/plstackapi/slices/";
 
 XOSModel = Backbone.Model.extend({
     /* from backbone-tastypie.js */
@@ -93,6 +94,11 @@ function xoslib() {
     this.sliverCollection = XOSCollection.extend({ urlRoot: SLIVER_API,
                                                    model: this.sliver});
     this.slivers = new this.sliverCollection();
+
+    this.slice = XOSModel.extend({ urlRoot: SLICE_API });
+    this.sliceCollection = XOSCollection.extend({ urlRoot: SLICE_API,
+                                                   model: this.slice});
+    this.slices = new this.sliceCollection();
 };
 
-XOSLib = new xoslib();
+xos = new xoslib();
diff --git a/planetstack/core/xoslib/static/xsh/constants.js b/planetstack/core/xoslib/static/xsh/constants.js
new file mode 100644 (file)
index 0000000..97b690a
--- /dev/null
@@ -0,0 +1,39 @@
+// TryMongo\r
+//\r
+// Copyright (c) 2009 Kyle Banker\r
+// Licensed under the MIT Licence.\r
+// http://www.opensource.org/licenses/mit-license.php\r
+\r
+var DefaultInputHtml = function(stack) {\r
+    var linePrompt = "";\r
+    if(stack == 0) {\r
+      linePrompt += "<span class='prompt'> ></span>";\r
+    }\r
+    else {\r
+      for(var i=0; i <= stack; i++) {\r
+        linePrompt += "<span class='prompt'>.</span>";\r
+      }\r
+    }\r
+    return "<div class='terminal_line'>" +\r
+           linePrompt +\r
+           "<input type='text' class='readLine active' />" +\r
+           "</div>";\r
+}\r
+\r
+var EnterKeyCode     = 13;\r
+var UpArrowKeyCode   = 38;\r
+var DownArrowKeyCode = 40;\r
+\r
+var PTAG = function(str) {\r
+  return '<pre class="terminal_help">' + str + '</pre>';\r
+}\r
+\r
+var BR = function() {\r
+  return "<br/>";\r
+}\r
+\r
+var JavascriptKeywords = ['abstract', 'boolean', 'break', 'byte', 'case', 'catch', 'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'double', 'else', 'enum', 'export', 'extends', 'false', 'final', 'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import', 'in', 'instanceof', 'int', 'interface', 'long', 'native', 'new', 'null', 'package', 'private', 'protected', 'public', 'return', 'short', 'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws', 'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while', 'with', 'alert', 'date', 'eval'];\r
+\r
+var JavascriptClassNames = ['Array', 'String', 'Object']\r
+\r
+var MongoKeywords = ['help','tutorial','next','back','t0','t1','t2','t3','t4'];\r
diff --git a/planetstack/core/xoslib/static/xsh/object_id.js b/planetstack/core/xoslib/static/xsh/object_id.js
new file mode 100644 (file)
index 0000000..15cbbb9
--- /dev/null
@@ -0,0 +1,12 @@
+var ObjectIdCounter = 0;
+
+var ObjectId = function() {
+  this.counter = (ObjectIdCounter += 1);
+  this.str     = this.counter;
+  this.initialize();
+  return this.counter;
+};
+
+ObjectId.prototype.initialize = function() {
+  return this.counter;
+}
diff --git a/planetstack/core/xoslib/static/xsh/shell_utils.js b/planetstack/core/xoslib/static/xsh/shell_utils.js
new file mode 100644 (file)
index 0000000..8ed5f4f
--- /dev/null
@@ -0,0 +1,548 @@
+DB = function() {
+}
+
+print = function(msg) {
+  //console.log(msg);
+}
+
+
+friendlyEqual = function( a , b ){
+    if ( a == b )
+        return true;
+
+    if ( tojson( a ) == tojson( b ) )
+        return true;
+
+    return false;
+}
+
+
+doassert = function( msg ){
+    print( "assert: " + msg );
+    throw msg;
+}
+
+assert = function( b , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( b )
+        return;
+    
+    doassert( "assert failed : " + msg );
+}
+
+assert.eq = function( a , b , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( a == b )
+        return;
+
+    if ( ( a != null && b != null ) && friendlyEqual( a , b ) )
+        return;
+
+    doassert( "[" + tojson( a ) + "] != [" + tojson( b ) + "] are not equal : " + msg );
+}
+
+assert.neq = function( a , b , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+    if ( a != b )
+        return;
+
+    doassert( "[" + a + "] != [" + b + "] are equal : " + msg );
+}
+
+assert.soon = function( f, msg, timeout, interval ) {
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    var start = new Date();
+    timeout = timeout || 30000;
+    interval = interval || 200;
+    var last;
+    while( 1 ) {
+        
+        if ( typeof( f ) == "string" ){
+            if ( eval( f ) )
+                return;
+        }
+        else {
+            if ( f() )
+                return;
+        }
+        
+        if ( ( new Date() ).getTime() - start.getTime() > timeout )
+            doassert( "assert.soon failed: " + f + ", msg:" + msg );
+        sleep( interval );
+    }
+}
+
+assert.throws = function( func , params , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+    try {
+        func.apply( null , params );
+    }
+    catch ( e ){
+        return e;
+    }
+
+    doassert( "did not throw exception: " + msg );
+}
+
+assert.commandWorked = function( res , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( res.ok == 1 )
+        return;
+    
+    doassert( "command failed: " + tojson( res ) + " : " + msg );
+}
+
+assert.commandFailed = function( res , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( res.ok == 0 )
+        return;
+    
+    doassert( "command worked when it should have failed: " + tojson( res ) + " : " + msg );
+}
+
+assert.isnull = function( what , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( what == null )
+        return;
+    
+    doassert( "supposed to null (" + ( msg || "" ) + ") was: " + tojson( what ) );
+}
+
+assert.lt = function( a , b , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( a < b )
+        return;
+    doassert( a + " is not less than " + b + " : " + msg );
+}
+
+assert.gt = function( a , b , msg ){
+    if ( assert._debug && msg ) print( "in assert for: " + msg );
+
+    if ( a > b )
+        return;
+    doassert( a + " is not greater than " + b + " : " + msg );
+}
+
+Object.extend = function( dst , src , deep ){
+    for ( var k in src ){
+        var v = src[k];
+        if ( deep && typeof(v) == "object" ){
+            v = Object.extend( typeof ( v.length ) == "number" ? [] : {} , v , true );
+        }
+        dst[k] = v;
+    }
+    return dst;
+}
+
+argumentsToArray = function( a ){
+    var arr = [];
+    for ( var i=0; i<a.length; i++ )
+        arr[i] = a[i];
+    return arr;
+}
+
+isString = function( x ){
+    return typeof( x ) == "string";
+}
+
+isNumber = function(x){
+    return typeof( x ) == "number";
+}
+
+isObject = function( x ){
+    return typeof( x ) == "object";
+}
+
+String.prototype.trim = function() {
+    return this.replace(/^\s+|\s+$/g,"");
+}
+String.prototype.ltrim = function() {
+    return this.replace(/^\s+/,"");
+}
+String.prototype.rtrim = function() {
+    return this.replace(/\s+$/,"");
+}
+
+Date.timeFunc = function( theFunc , numTimes ){
+
+    var start = new Date();
+    
+    numTimes = numTimes || 1;
+    for ( var i=0; i<numTimes; i++ ){
+        theFunc.apply( null , argumentsToArray( arguments ).slice( 2 ) );
+    }
+
+    return (new Date()).getTime() - start.getTime();
+}
+
+Date.prototype.tojson = function(){
+    return "\"" + this.toString() + "\"";
+}
+
+RegExp.prototype.tojson = RegExp.prototype.toString;
+
+Array.contains = function( a  , x ){
+    for ( var i=0; i<a.length; i++ ){
+        if ( a[i] == x )
+            return true;
+    }
+    return false;
+}
+
+Array.unique = function( a ){
+    var u = [];
+    for ( var i=0; i<a.length; i++){
+        var o = a[i];
+        if ( ! Array.contains( u , o ) ){
+            u.push( o );
+        }
+    }
+    return u;
+}
+
+Array.shuffle = function( arr ){
+    for ( var i=0; i<arr.length-1; i++ ){
+        var pos = i+Math.floor(Math.random()*(arr.length-i));
+        var save = arr[i];
+        arr[i] = arr[pos];
+        arr[pos] = save;
+    }
+    return arr;
+}
+
+
+Array.tojson = function( a , indent , x , html){
+    if (!indent) 
+        indent = "";
+    var spacer = "";
+    if(html) {
+      spacer = "<br/>";
+      indent = " &nbsp; "
+    }
+
+    var s = spacer + "[ " + spacer;
+    indent += " ";
+    for ( var i=0; i<a.length; i++){
+        s += indent + tojson( a[i], indent );
+        if ( i < a.length - 1 ){
+            s += "," + spacer;
+        }
+    }
+    if ( a.length == 0 ) {
+        s += indent;
+    }
+
+    indent = indent.substring(1);
+    s += spacer + " "+"]";
+    return s;
+}
+
+Array.fetchRefs = function( arr , coll ){
+    var n = [];
+    for ( var i=0; i<arr.length; i ++){
+        var z = arr[i];
+        if ( coll && coll != z.getCollection() )
+            continue;
+        n.push( z.fetch() );
+    }
+    
+    return n;
+}
+
+Array.sum = function( arr ){
+    if ( arr.length == 0 )
+        return null;
+    var s = arr[0];
+    for ( var i=1; i<arr.length; i++ )
+        s += arr[i];
+    return s;
+}
+
+Array.avg = function( arr ){
+    if ( arr.length == 0 )
+        return null;
+    return Array.sum( arr ) / arr.length;
+}
+
+Array.stdDev = function( arr ){
+    var avg = Array.avg( arr );
+    var sum = 0;
+
+    for ( var i=0; i<arr.length; i++ ){
+        sum += Math.pow( arr[i] - avg , 2 );
+    }
+
+    return Math.sqrt( sum / arr.length );
+}
+
+if ( ! ObjectId.prototype )
+    ObjectId.prototype = {}
+
+ObjectId.prototype.toString = function(){
+    return this.str;
+}
+
+ObjectId.prototype.tojson = function(){
+    return "ObjectId(\"" + this.str + "\")";
+}
+
+ObjectId.prototype.isObjectId = true;
+
+tojson = function( x, indent , nolint , html){
+    if ( x == null )
+        return "null";
+    
+    if ( x == undefined )
+        return "undefined";
+    
+    if (!indent) 
+        indent = "";
+
+    switch ( typeof x ){
+        
+    case "string": {
+        var s = "\"";
+        for ( var i=0; i<x.length; i++ ){
+            if ( x[i] == '"' ){
+                s += "\\\"";
+            }
+            else
+                s += x[i];
+        }
+        return s + "\"";
+    }
+        
+    case "number": 
+    case "boolean":
+        return "" + x;
+            
+    case "object":{
+        var s = tojsonObject( x, indent , nolint , html);
+        if ( ( nolint == null || nolint == true ) && s.length < 80 && ( indent == null || indent.length == 0 ) ){
+            s = s.replace( /[\s\r\n ]+/gm , " " );
+        }
+        return s;
+    }
+        
+    case "function":
+        return x.toString();
+        
+
+    default:
+        throw "tojson can't handle type " + ( typeof x );
+    }
+    
+}
+
+tojsonObject = function( x, indent , nolint , html){
+    if(html) {
+      var lineEnding = "<br/>";
+      var tabSpace   = "&nbsp;";
+    }
+    else {
+      var lineEnding = nolint ? " " : "\n";
+      var tabSpace = nolint ? "" : "\t";
+    }
+
+    assert.eq( ( typeof x ) , "object" , "tojsonObject needs object, not [" + ( typeof x ) + "]" );
+
+    if (!indent)
+        indent = "";
+
+    if ( x.hasOwnProperty("__str__")) {
+        return x.__str__();
+    }
+
+    if ( typeof( x.tojson ) == "function" && x.tojson != tojson ) {
+        return x.tojson(indent,nolint,html);
+    }
+
+    if ( typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) {
+        return x.constructor.tojson( x, indent , nolint, html );
+    }
+
+    if ( x.toString() == "[object MaxKey]" )
+        return "{ $maxKey : 1 }";
+    if ( x.toString() == "[object MinKey]" )
+        return "{ $minKey : 1 }";
+
+    var s = "{" + lineEnding;
+
+    // push one level of indent
+    indent += tabSpace;
+
+    var total = 0;
+    for ( var k in x ) total++;
+    if ( total == 0 ) {
+        s += indent + lineEnding;
+    }
+
+    var keys = x;
+    if ( typeof( x._simpleKeys ) == "function" )
+        keys = x._simpleKeys();
+    var num = 1;
+    for ( var k in keys ){
+        var val = x[k];
+
+        s += indent + "\"" + k + "\" : " + tojson( val, indent , nolint );
+        if (num != total) {
+            s += ",";
+            num++;
+        }
+        s += lineEnding;
+    }
+
+    // pop one level of indent
+    indent = indent.substring(1);
+    return s + indent + "}";
+}
+
+shellPrint = function( x ){
+    it = x;
+    if ( x != undefined )
+        shellPrintHelper( x );
+}
+
+printjson = function(x){
+    print( tojson( x ) );
+}
+
+shellPrintHelper = function( x ){
+
+    if ( typeof( x ) == "undefined" ){
+
+        return;
+    }
+    
+    if ( x == null ){
+        print( "null" );
+        return;
+    }
+
+    if ( typeof x != "object" ) 
+        return print( x );
+    
+    var p = x.shellPrint;
+    if ( typeof p == "function" )
+        return x.shellPrint();
+
+    var p = x.tojson;
+    if ( typeof p == "function" )
+        print( x.tojson() );
+    else
+        print( tojson( x ) );
+}
+
+shellHelper = function( command , rest , shouldPrint ){
+    command = command.trim();
+    var args = rest.trim().replace(/;$/,"").split( "\s+" );
+    
+    if ( ! shellHelper[command] )
+        throw "no command [" + command + "]";
+    
+    var res = shellHelper[command].apply( null , args );
+    if ( shouldPrint ){
+        shellPrintHelper( res );
+    }
+    return res;
+}
+
+help = shellHelper.help = function(){
+    print( "HELP" );
+    print( "\t" + "show dbs                     show database names");
+    print( "\t" + "show collections             show collections in current database");
+    print( "\t" + "show users                   show users in current database");
+    print( "\t" + "show profile                 show most recent system.profile entries with time >= 1ms");
+    print( "\t" + "use <db name>                set curent database to <db name>" );
+    print( "\t" + "db.help()                    help on DB methods");
+    print( "\t" + "db.foo.help()                help on collection methods");
+    print( "\t" + "db.foo.find()                list objects in collection foo" );
+    print( "\t" + "db.foo.find( { a : 1 } )     list objects in foo where a == 1" );
+    print( "\t" + "it                           result of the last line evaluated; use to further iterate");
+}
+
+if ( typeof( Map ) == "undefined" ){
+    Map = function(){
+        this._data = {};
+    }
+}
+
+Map.hash = function( val ){
+    if ( ! val )
+        return val;
+
+    switch ( typeof( val ) ){
+    case 'string':
+    case 'number':
+    case 'date':
+        return val.toString();
+    case 'object':
+    case 'array':
+        var s = "";
+        for ( var k in val ){
+            s += k + val[k];
+        }
+        return s;
+    }
+
+    throw "can't hash : " + typeof( val );
+}
+
+Map.prototype.put = function( key , value ){
+    var o = this._get( key );
+    var old = o.value;
+    o.value = value;
+    return old;
+}
+
+Map.prototype.get = function( key ){
+    return this._get( key ).value;
+}
+
+Map.prototype._get = function( key ){
+    var h = Map.hash( key );
+    var a = this._data[h];
+    if ( ! a ){
+        a = [];
+        this._data[h] = a;
+    }
+    
+    for ( var i=0; i<a.length; i++ ){
+        if ( friendlyEqual( key , a[i].key ) ){
+            return a[i];
+        }
+    }
+    var o = { key : key , value : null };
+    a.push( o );
+    return o;
+}
+
+Map.prototype.values = function(){
+    var all = [];
+    for ( var k in this._data ){
+        this._data[k].forEach( function(z){ all.push( z.value ); } );
+    }
+    return all;
+}
+
+if ( typeof( gc ) == "undefined" ){
+    gc = function(){
+    }
+}
+   
+
+Math.sigFig = function( x , N ){
+    if ( ! N ){
+        N = 3;
+    }
+    var p = Math.pow( 10, N - Math.ceil( Math.log( Math.abs(x) ) / Math.log( 10 )) );
+    return Math.round(x*p)/p;
+}
+
diff --git a/planetstack/core/xoslib/static/xsh/tokens.js b/planetstack/core/xoslib/static/xsh/tokens.js
new file mode 100644 (file)
index 0000000..49c246e
--- /dev/null
@@ -0,0 +1,268 @@
+// tokens.js
+// 2009-05-17
+
+// (c) 2006 Douglas Crockford
+
+// Produce an array of simple token objects from a string.
+// A simple token object contains these members:
+//      type: 'name', 'string', 'number', 'operator'
+//      value: string or number value of the token
+//      from: index of first character of the token
+//      to: index of the last character + 1
+
+// Comments of the // type are ignored.
+
+// Operators are by default single characters. Multicharacter
+// operators can be made by supplying a string of prefix and
+// suffix characters.
+// characters. For example,
+//      '<>+-&', '=>&:'
+// will match any of these:
+//      <=  >>  >>>  <>  >=  +: -: &: &&: &&
+
+
+
+String.prototype.tokens = function (prefix, suffix) {
+    var c;                      // The current character.
+    var from;                   // The index of the start of the token.
+    var i = 0;                  // The index of the current character.
+    var length = this.length;
+    var n;                      // The number value.
+    var q;                      // The quote character.
+    var str;                    // The string value.
+
+    var result = [];            // An array to hold the results.
+
+    var make = function (type, value) {
+
+// Make a token object.
+
+        return {
+            type: type,
+            value: value,
+            from: from,
+            to: i
+        };
+    };
+
+// Begin tokenization. If the source string is empty, return nothing.
+
+    if (!this) {
+        return;
+    }
+
+// If prefix and suffix strings are not provided, supply defaults.
+
+    if (typeof prefix !== 'string') {
+        prefix = '<>+-&';
+    }
+    if (typeof suffix !== 'string') {
+        suffix = '=>&:';
+    }
+
+
+// Loop through this text, one character at a time.
+
+    c = this.charAt(i);
+    while (c) {
+        from = i;
+
+// Ignore whitespace.
+
+        if (c <= ' ') {
+            i += 1;
+            c = this.charAt(i);
+
+// name.
+
+        } else if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z') {
+            str = c;
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+                        (c >= '0' && c <= '9') || c === '_') {
+                    str += c;
+                    i += 1;
+                } else {
+                    break;
+                }
+            }
+            result.push(make('name', str));
+
+// number.
+
+// A number cannot start with a decimal point. It must start with a digit,
+// possibly '0'.
+
+        } else if (c >= '0' && c <= '9') {
+            str = c;
+            i += 1;
+
+// Look for more digits.
+
+            for (;;) {
+                c = this.charAt(i);
+                if (c < '0' || c > '9') {
+                    break;
+                }
+                i += 1;
+                str += c;
+            }
+
+// Look for a decimal fraction part.
+
+            if (c === '.') {
+                i += 1;
+                str += c;
+                for (;;) {
+                    c = this.charAt(i);
+                    if (c < '0' || c > '9') {
+                        break;
+                    }
+                    i += 1;
+                    str += c;
+                }
+            }
+
+// Look for an exponent part.
+
+            if (c === 'e' || c === 'E') {
+                i += 1;
+                str += c;
+                c = this.charAt(i);
+                if (c === '-' || c === '+') {
+                    i += 1;
+                    str += c;
+                    c = this.charAt(i);
+                }
+                if (c < '0' || c > '9') {
+                    make('number', str).error("Bad exponent");
+                }
+                do {
+                    i += 1;
+                    str += c;
+                    c = this.charAt(i);
+                } while (c >= '0' && c <= '9');
+            }
+
+// Make sure the next character is not a letter.
+
+            if (c >= 'a' && c <= 'z') {
+                str += c;
+                i += 1;
+                make('number', str).error("Bad number");
+            }
+
+// Convert the string value to a number. If it is finite, then it is a good
+// token.
+
+            n = +str;
+            if (isFinite(n)) {
+                result.push(make('number', n));
+            } else {
+                make('number', str).error("Bad number");
+            }
+
+// string
+
+        } else if (c === '\'' || c === '"') {
+            str = '';
+            q = c;
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if (c < ' ') {
+                    make('string', str).error(c === '\n' || c === '\r' || c === '' ?
+                        "Unterminated string." :
+                        "Control character in string.", make('', str));
+                }
+
+// Look for the closing quote.
+
+                if (c === q) {
+                    break;
+                }
+
+// Look for escapement.
+
+                if (c === '\\') {
+                    i += 1;
+                    if (i >= length) {
+                        make('string', str).error("Unterminated string");
+                    }
+                    c = this.charAt(i);
+                    switch (c) {
+                    case 'b':
+                        c = '\b';
+                        break;
+                    case 'f':
+                        c = '\f';
+                        break;
+                    case 'n':
+                        c = '\n';
+                        break;
+                    case 'r':
+                        c = '\r';
+                        break;
+                    case 't':
+                        c = '\t';
+                        break;
+                    case 'u':
+                        if (i >= length) {
+                            make('string', str).error("Unterminated string");
+                        }
+                        c = parseInt(this.substr(i + 1, 4), 16);
+                        if (!isFinite(c) || c < 0) {
+                            make('string', str).error("Unterminated string");
+                        }
+                        c = String.fromCharCode(c);
+                        i += 4;
+                        break;
+                    }
+                }
+                str += c;
+                i += 1;
+            }
+            i += 1;
+            result.push(make('string', str));
+            c = this.charAt(i);
+
+// comment.
+
+        } else if (c === '/' && this.charAt(i + 1) === '/') {
+            i += 1;
+            for (;;) {
+                c = this.charAt(i);
+                if (c === '\n' || c === '\r' || c === '') {
+                    break;
+                }
+                i += 1;
+            }
+
+// combining
+
+        } else if (prefix.indexOf(c) >= 0) {
+            str = c;
+            i += 1;
+            while (i < length) {
+                c = this.charAt(i);
+                if (suffix.indexOf(c) < 0) {
+                    break;
+                }
+                str += c;
+                i += 1;
+            }
+            result.push(make('operator', str));
+
+// single-character operator
+
+        } else {
+            i += 1;
+            result.push(make('operator', c));
+            c = this.charAt(i);
+        }
+    }
+    return result;
+};
+
diff --git a/planetstack/core/xoslib/static/xsh/utils.js b/planetstack/core/xoslib/static/xsh/utils.js
new file mode 100644 (file)
index 0000000..1ded27e
--- /dev/null
@@ -0,0 +1,81 @@
+// Try Mongo
+//
+// Copyright (c) 2009 Kyle Banker
+// Licensed under the MIT licence.
+// http://www.opensource.org/licenses/mit-license.php
+
+// extending array like this is breaking datatables
+
+/*Array.prototype.include = function(value) {
+  for(var i=0; i < this.length; i++) {
+    if(this[i] == value) {
+      return this[i];
+    }
+  }
+  return false;
+};
+
+Array.prototype.empty = function() {
+  return (this.length == 0);
+};*/
+
+function ArrayInclude(arr,value) {
+  for(var i=0; i < arr.length; i++) {
+    if(arr[i] == value) {
+      return arr[i];
+    }
+  }
+  return false;
+};
+
+Function.prototype.bind = function() {
+  var __method = this, object = arguments[0], args = [];
+
+  for(i = 1; i < arguments.length; i++) {
+   args.push(arguments[i]);
+  }
+
+ return function() {
+ return __method.apply(object, args);
+ };
+}; 
+
+String.prototype.trim = function() {
+  return this.replace(/^\s+|\s+$/g,"");
+};
+
+// Prints javascript types as readable strings.
+Inspect = function(obj) {
+  if(typeof(obj) != 'object') {
+    return obj;
+  }
+
+  else if (obj instanceof Array) {
+    var objRep = [];
+    for(var prop in obj) { 
+      if(obj.hasOwnProperty(prop)) {
+        objRep.push(obj[prop]); 
+      }
+    }
+    return '[' + objRep.join(', ') + ']';
+  }
+
+  else {
+    var objRep = [];
+    for(var prop in obj) {
+      if(obj.hasOwnProperty(prop)) {
+        objRep.push(prop + ': ' + ((typeof(obj[prop]) == 'object') ? Inspect(obj[prop]) : obj[prop]));
+      }
+    }
+    return '{' + objRep.join(', ') + '}';
+  }
+};
+
+// Prints an array of javascript objects.
+CollectionInspect = function(coll) {
+  var str = '';
+  for(var i=0; i<coll.length; i++) {
+    str += Inspect(coll[i]) + '<br />'; 
+  }
+  return str;
+};
diff --git a/planetstack/core/xoslib/static/xsh/xsh.js b/planetstack/core/xoslib/static/xsh/xsh.js
new file mode 100644 (file)
index 0000000..c2073e4
--- /dev/null
@@ -0,0 +1,405 @@
+// TryMongo
+//
+// Copyright (c) 2009 Kyle Banker
+// Licensed under the MIT Licence.
+// http://www.opensource.org/licenses/mit-license.php
+
+// Readline class to handle line input.
+var ReadLine = function(options) {
+  this.options      = options || {};
+  this.htmlForInput = this.options.htmlForInput;
+  this.inputHandler = this.options.handler || this.mockHandler;
+  this.scoper       = this.options.scoper;
+  this.terminal     = $(this.options.terminalId || "#terminal");
+  this.lineClass    = this.options.lineClass || '.readLine';
+  this.history      = [];
+  this.historyPtr   = 0;
+
+  this.initialize();
+};
+
+ReadLine.prototype = {
+
+  initialize: function() {
+    this.addInputLine();
+  },
+
+  // Enter a new input line with proper behavior.
+  addInputLine: function(stackLevel) {
+    stackLevel = stackLevel || 0;
+    this.terminal.append(this.htmlForInput(stackLevel));
+    var ctx = this;
+    ctx.activeLine = $(this.lineClass + '.active');
+
+    // Bind key events for entering and navigting history.
+    ctx.activeLine.bind("keydown", function(ev) {
+      switch (ev.keyCode) {
+        case EnterKeyCode:
+          ctx.processInput(this.value); 
+          break;
+        case UpArrowKeyCode: 
+          ctx.getCommand('previous');
+          break;
+        case DownArrowKeyCode: 
+          ctx.getCommand('next');
+          break;
+      }
+    });
+
+    this.activeLine.focus();
+  },
+
+  // Returns the 'next' or 'previous' command in this history.
+  getCommand: function(direction) {
+    if(this.history.length === 0) {
+      return;
+    }
+    this.adjustHistoryPointer(direction);
+    this.activeLine[0].value = this.history[this.historyPtr];
+    $(this.activeLine[0]).focus();
+    //this.activeLine[0].value = this.activeLine[0].value;
+  },
+
+  // Moves the history pointer to the 'next' or 'previous' position. 
+  adjustHistoryPointer: function(direction) {
+    if(direction == 'previous') {
+      if(this.historyPtr - 1 >= 0) {
+        this.historyPtr -= 1;
+      }
+    }
+    else {
+      if(this.historyPtr + 1 < this.history.length) {
+        this.historyPtr += 1;
+      }
+    }
+  },
+
+  // Return the handler's response.
+  processInput: function(value) {
+    var response = this.inputHandler.apply(this.scoper, [value]);
+    this.insertResponse(response.result);
+
+    // Save to the command history...
+    if((lineValue = value.trim()) !== "") {
+      this.history.push(lineValue);
+      this.historyPtr = this.history.length;
+    }
+
+    // deactivate the line...
+    this.activeLine.value = "";
+    this.activeLine.attr({disabled: true});
+    this.activeLine.removeClass('active');
+
+    // and add add a new command line.
+    this.addInputLine(response.stack);
+  },
+
+  insertResponse: function(response) {
+    if((response.length < 1) || (response=='"donotprintme"') || (response=='donotprintme')) {
+      this.activeLine.parent().append("<p class='response'></p>");
+    }
+    else {
+      this.activeLine.parent().append("<p class='response'>" + response + "</p>");
+    }
+  },
+
+  // Simply return the entered string if the user hasn't specified a smarter handler.
+  mockHandler: function(inputString) {
+    return function() {
+      this._process = function() { return inputString; };
+    };
+  }
+};
+
+var MongoHandler = function() {
+  this._currentCommand = "";
+  this._rawCommand     = "";
+  this._commandStack   = 0;
+  this._tutorialPtr    = 0;
+  this._tutorialMax    = 4;
+
+  this._mongo          = {};
+  this._mongo.test     = [];
+  this.collections     = [];
+};
+
+MongoHandler.prototype = {
+
+  _process: function(inputString, errorCheck) {
+    this._rawCommand += ' ' + inputString;
+
+    try {
+      inputString += '  '; // fixes certain bugs with the tokenizer.
+      var tokens    = inputString.tokens();
+      var mongoFunc = this._getCommand(tokens);
+      if(this._commandStack === 0 && inputString.match(/^\s*$/)) {
+        return {stack: 0, result: ''};
+      }
+      else if(this._commandStack === 0 && mongoFunc) {
+        this._resetCurrentCommand();
+        return {stack: 0, result: mongoFunc.apply(this, [tokens])};
+      }
+      else {
+        return this._evaluator(tokens);
+      }
+    }
+
+    catch(err) {
+        this._resetCurrentCommand();
+        console.trace();
+        return {stack: 0, result: "JS Error: " + err};
+    }
+  },
+
+  // Calls eval on the input string when ready.
+  _evaluator: function(tokens) {
+    isAssignment = tokens.length>=2 && tokens[0].type=="name" && tokens[1].type=="operator" && tokens[1].value=="=";
+
+    this._currentCommand += " " + this._massageTokens(tokens);
+    if(this._shouldEvaluateCommand(tokens))  {
+        print = this.print;
+
+        // So this eval statement is the heart of the REPL.
+        var result = eval(this._currentCommand.trim());
+        if(result === undefined) {
+          throw('result is undefined');
+        } else if (typeof(result) === 'function') {
+          throw('result is a function. did you mean to call it?');
+        } else {
+          result = $htmlFormat(result);
+        }
+        this._resetCurrentCommand();
+        if (isAssignment) {
+            return {stack: this._commandStack, result: ""};
+        } else {
+            return {stack: this._commandStack, result: result};
+        }
+      }
+
+    else {
+      return {stack: this._commandStack, result: ""};
+    }
+  },
+
+  _resetCurrentCommand: function() {
+    this._currentCommand = '';
+    this._rawCommand     = '';
+  },
+
+  // Evaluate only when we've exited any blocks.
+  _shouldEvaluateCommand: function(tokens) {
+    for(var i=0; i < tokens.length; i++) {
+      var token = tokens[i];
+      if(token.type == 'operator') {
+        if(token.value == '(' || token.value == '{') {
+          this._commandStack += 1;
+        }
+        else if(token.value == ')' || token.value == '}') {
+          this._commandStack -= 1;
+        }
+      }
+    }
+
+    if(this._commandStack === 0) {
+      return true;
+    }
+    else {
+      return false;
+    }
+  },
+
+  _massageTokens: function(tokens) {
+    for(var i=0; i < tokens.length; i++) {
+      if(tokens[i].type == 'name') {
+        if(tokens[i].value == 'var') {
+          tokens[i].value = '';
+        }
+      }
+    }
+    return this._collectTokens(tokens);
+  },
+
+  // Collects tokens into a string, placing spaces between variables.
+  // This methods is called after we scope the vars.
+  _collectTokens: function(tokens) {
+    var result = "";
+    for(var i=0; i < tokens.length; i++) {
+      if(tokens[i].type == "name" && tokens[i+1] && tokens[i+1].type == 'name') {
+        result += tokens[i].value + ' ';
+      }
+      else if (tokens[i].type == 'string') {
+        result += "'" + tokens[i].value + "'";
+      }
+      else {
+        result += tokens[i].value;
+      }
+    }
+    return result;
+  },
+
+  // print output to the screen, e.g., in a loop
+  // TODO: remove dependency here
+  print: function() {
+   $('.readLine.active').parent().append('<p>' + JSON.stringify(arguments[0]) + '</p>');
+   return "donotprintme";
+  },
+
+  /* MongoDB     */
+  /* ________________________________________ */
+
+  // help command
+  _help: function() {
+      return PTAG('HELP') +
+             PTAG('xos                                 list xos API object types') +
+             PTAG('xos.slices                          list methods to can call on slices') +
+             PTAG('xos.slices.all()                    get all slices') +
+             PTAG('xos.slices.filter({key: "value"})   filter using dictionary') +
+             PTAG('xos.slices.get({key: "value"})      get using dictionary')
+
+  },
+
+  _tutorial: function() {
+    this._tutorialPtr = 0;
+    return PTAG("This is a self-guided tutorial on the xos shell.") +
+           PTAG("The tutorial is simple, more or less a few basic commands to try.") +
+           PTAG("To go directly to any part tutorial, enter one of the commands t0, t1, t2...t10") +
+           PTAG("Otherwise, use 'next' and 'back'. Start by typing 'next' and pressing enter.");
+  },
+
+  // go to the next step in the tutorial.
+  _next: function() {
+    if(this._tutorialPtr < this._tutorialMax) {
+      return this['_t' + (this._tutorialPtr + 1)]();
+    }
+    else {
+      return "You've reached the end of the tutorial. To go to the beginning, type 'tutorial'";
+    }
+  },
+
+  // go to the previous step in the tutorial.
+  _back: function() {
+    if(this._tutorialPtr > 1) {
+      return this['_t' + (this._tutorialPtr - 1)]();
+    }
+    else {
+      return this._tutorial();
+    }
+  },
+
+  _t1: function() {
+    this._tutorialPtr = 1;
+    return PTAG('1. JavaScript Shell') +
+           PTAG('The first thing to notice is that the MongoDB shell is JavaScript-based.') +
+           PTAG('So you can do things like:') +
+           PTAG('  a = 5; ') +
+           PTAG('  a * 10; ') +
+           PTAG('  print(a); ') +
+           PTAG("  for(i=0; i<10; i++) { print('hello'); }; ") +
+           PTAG("Try a few JS commands; when you're ready to move on, enter 'next'");
+
+  },
+
+  _t2: function() {
+    this._tutorialPtr = 2;
+    return PTAG('2. The API is asynchronous') +
+           PTAG('Try these:') +
+           PTAG('    xos.slices.fetch();') +
+           PTAG('    // wait a second or two...') +
+           PTAG('    xos.slices.objects();');
+
+  },
+
+  _t3: function() {
+    this._tutorialPtr = 3;
+    return PTAG('3. Filter some objects') +
+           PTAG('Try these:');
+
+  },
+
+  _t4: function() {
+    this._tutorialPtr = 4;
+    return PTAG('4. Available xos objects and methods') +
+           PTAG('Try these:') +
+           PTAG('    xos;') +
+           PTAG('    xos.slices;');
+
+  },
+
+  _getCommand: function(tokens) {
+    if(tokens[0] && ArrayInclude(MongoKeywords,(tokens[0].value + '').toLowerCase())) {
+      switch(tokens[0].value.toLowerCase()) {
+        case 'help':
+          return this._help;
+
+        case 'tutorial':
+          return this._tutorial;
+        case 'next':
+          return this._next;
+        case 'back':
+          return this._back;
+        case 't0':
+          return this._tutorial;
+        case 't1':
+          return this._t1;
+        case 't2':
+          return this._t2;
+        case 't3':
+          return this._t3;
+        case 't4':
+          return this._t4;
+      }
+    }
+  }
+};
+
+function replaceAll(find, replace, str) {
+  return str.replace(new RegExp(find, 'g'), replace);\r
+}
+
+/* stackoverflow: http://stackoverflow.com/questions/4810841/how-can-i-pretty-print-json-using-javascript */
+function syntaxHighlight(json) {
+    if ( json.hasOwnProperty("__str__")) {
+        return syntaxHighlight(json.__str__());
+    }
+    if (typeof json != 'string') {\r
+         json = JSON.stringify(json, undefined, "\t");\r
+    }\r
+    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\r
+    return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {\r
+        var cls = 'terminal_number';\r
+        if (/^"/.test(match)) {\r
+            if (/:$/.test(match)) {\r
+                cls = 'terminal_key';\r
+            } else {\r
+                cls = 'terminal_string';\r
+            }\r
+        } else if (/true|false/.test(match)) {\r
+            cls = 'terminal_boolean';\r
+        } else if (/null/.test(match)) {\r
+            cls = 'terminal_null';\r
+        }\r
+        return '<span class="' + cls + '">' + match + '</span>';\r
+    });\r
+}
+
+$htmlFormat = function(obj) {
+  //JSON.stringify(obj,undefined,2)
+  result=replaceAll("\t","&nbsp;",replaceAll("\n","<br>",syntaxHighlight(obj))); //tojson(obj, ' ', ' ', true);
+  return result;
+}
+
+function startTerminal() {
+  var mongo       = new MongoHandler();
+  var terminal    = new ReadLine({htmlForInput: DefaultInputHtml,
+                                  handler: mongo._process,
+                                  scoper: mongo});
+  $("#terminal_help1").show();
+  $("#terminal_help2").show();
+  $("#terminal_wait").hide();
+
+  $("#terminal").bind('click', function() { $(".readLine.active").focus(); });
+};
+
+$(document).ready(function() {
+    startTerminal();
+});
index 1121892..bc2d469 100755 (executable)
@@ -1,3 +1,4 @@
 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 static/xsh/*.js princeton_planetstack@node43.princeton.vicci.org:/opt/planetstack/core/xoslib/static/xsh/
 scp dashboards/*.html princeton_planetstack@node43.princeton.vicci.org:/opt/planetstack/templates/admin/dashboard/