-// 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)) {
- xos = new OpenCloud();
- 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. List some objects') +
- PTAG('Try these:') +
- PTAG(' xos.slices.all();') +
- PTAG(' xos.slivers.all();') +
- PTAG(' xos.sites.all();') +
- PTAG(' xos.nodes.all();');
-
- },
-
- _t3: function() {
- this._tutorialPtr = 3;
- return PTAG('3. Filter some objects') +
- PTAG('Try these:') +
- PTAG(' xos.slices.get({name: "HyperCache"});');
- PTAG(' xos.nodes.filter({site_id: xos.sites.get({name: "Princeton"})["id"]});');
-
- },
-
- _t4: function() {
- this._tutorialPtr = 4;
- return PTAG('4. Available xos objects and methods') +
- PTAG('Try these:') +
- PTAG(' xos;') +
- PTAG(' xos.nodes;');
-
- },
-
- _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, '&').replace(/</g, '<').replace(/>/g, '>');\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"," ",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() {
- updateOpenCloud(onLoaded = startTerminal);
-});