You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by js...@apache.org on 2009/03/19 12:24:20 UTC

svn commit: r755931 [1/9] - in /camel/trunk/components/camel-web/src/main/webapp/js/bespin: ./ client/ cmd/ editor/ mobwrite/ page/ page/dashboard/ page/editor/ page/index/ syntax/ user/ util/

Author: jstrachan
Date: Thu Mar 19 11:24:18 2009
New Revision: 755931

URL: http://svn.apache.org/viewvc?rev=755931&view=rev
Log:
added Bespin support for editing routes for CAMEL-1355 - which includes adding Bespin & Dojo JS libraries. Sorry for the big commit :)

Added:
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commands.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/dashboardcommands.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/editorcommands.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/Component.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/actions.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/clipboard.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/cursor.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/editor.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/embed.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/events.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/filelist.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/model.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/quickopen.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/themes.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/toolbar.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/undo.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/events.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/mobwrite/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/mobwrite/core.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/mobwrite/diff.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/mobwrite/form.js
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/mobwrite/integrate.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/dashboard/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/dashboard/components.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/dashboard/dependencies.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/dashboard/events.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/dashboard/init.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/editor/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/editor/dependencies.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/editor/init.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/index/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/index/dependencies.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/page/index/init.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/arduino.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/css.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/html.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/javascript.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/php.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/syntax/syntax.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/user/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/user/dependencies.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/user/register.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/user/utils.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/canvas.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/keys.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/mousewheelevent.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/navigate.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/path.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/tokenobject.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/urlbar.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/util.js   (with props)
    camel/trunk/components/camel-web/src/main/webapp/js/bespin/util/webpieces.js   (with props)

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,113 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// = Bespin =
+//
+// This is the root of it all. The {{{ bespin }}} namespace.
+// All of the JavaScript for Bespin will be placed in this namespace later.
+//
+// {{{ bespin.versionNumber }}} is the core version of the Bespin system
+// {{{ bespin.apiVersion }}} is the version number of the API (to ensure that the
+//                          client and server are talking the same language)
+// {{{ bespin.displayVersion }}} is a function that sets innerHTML on the element given, with the Bespin version info
+//
+// {{{ bespin.publish }}} maps onto dojo.publish but lets us abstract away for the future
+// {{{ bespin.subscribe }}} maps onto dojo.subscribe but lets us abstract away for the future
+// {{{ bespin.unsubscribe }}} maps onto dojo.unsubscribe but lets us abstract away for the future
+//
+// {{{ bespin.register }}} is the way to attach components into the bespin system for others to get out
+// {{{ bespin.get }}} allows you to get registered components out
+// {{{ bespin.withComponent }}} maps onto dojo.subscribe but lets us abstract away for the future
+
+dojo.provide("bespin.bespin");
+    
+dojo.mixin(bespin, {
+    // BEGIN VERSION BLOCK
+    versionNumber: 'tip',
+    versionCodename: 'DEVELOPMENT MODE',
+    apiVersion: 'dev',
+    // END VERSION BLOCK
+    
+    defaultTabSize: 4,
+    userSettingsProject: "BespinSettings",
+
+    // == Methods for tying to the event bus
+
+    // ** {{{ publish }}} **
+    //
+    // Given a topic and a set of parameters, publish onto the bus
+    publish: function(topic, args) {
+        dojo.publish(topic, dojo.isArray(args) ? args : [ args || {} ]);
+    },
+
+    // ** {{{ subscribe }}} **
+    //
+    // Given a topic and a function, subscribe to the event
+    subscribe: dojo.subscribe,
+
+    // ** {{{ unsubscribe }}} **
+    //
+    // Unsubscribe the functions from the topic
+    unsubscribe: dojo.unsubscribe,
+
+    // == Methods for registering components with the main system
+    registeredComponents: {},
+
+    // ** {{{ register }}} **
+    //
+    // Given an id and an object, register it inside of Bespin
+    register: function(id, object) {
+        bespin.publish("bespin:component:register", { id: id, object: object });
+
+        this.registeredComponents[id] = object;
+        
+        return object;
+    },
+
+    // ** {{{ get }}} **
+    //
+    // Given an id return the component
+    get: function(id) {
+        return this.registeredComponents[id];
+    },
+
+    // ** {{{ withComponent }}} **
+    //
+    // Given an id, and function to run, execute it if the component is available
+    withComponent: function(id, func) {
+        var component = this.get(id);
+        if (component) {
+            return func(component);
+        }
+    },
+
+    // ** {{{ displayVersion }}} **
+    //
+    // Given an HTML element
+    displayVersion: function(el) {
+        var el = dojo.byId(el) || dojo.byId("version");
+        if (!el) return;
+        el.innerHTML = '<a href="https://wiki.mozilla.org/Labs/Bespin/ReleaseNotes" title="Read the release notes">Version <span class="versionnumber">' + this.versionNumber + '</span> "' + this.versionCodename + '"</a>';
+    }
+});

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/bespin.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,225 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+ 
+// = FileSystem =
+//
+// This abstracts the remote Web Service file system, and in the future
+// local file systems too.
+// 
+// It ties into the {{{bespin.client.Server}}} object for remote access
+
+dojo.provide("bespin.client.filesystem"); 
+
+dojo.declare("bespin.client.FileSystem", null, {
+    constructor: function(server) {
+        this.server = server || bespin.get('server');
+    },
+
+    // ** {{{ bespin.client.FileSystem.newFile(project, path, callback) }}}
+    //
+    // Create a new file in the file system using:
+    //
+    // * {{{project}}} is the name of the project to create the file in
+    // * {{{path}}} is the full path to save the file into
+    // * {{{onSuccess}}} is a callback to fire if the file is created
+    newFile: function(project, path, onSuccess) {
+        this.whenFileDoesNotExist(project, path, {
+            execute: function() {
+                bespin.get('editSession').startSession(project, path || "new.txt");
+                onSuccess();
+            },
+            elseFailed: function() {
+                bespin.publish("bespin:cmdline:showinfo", { msg: 'The file ' + path + ' already exists my friend.'});
+            }
+        });
+    },
+
+    // ** {{{ bespin.client.FileSystem.loadFile(project, path, callback) }}}
+    //
+    // Load the file in the given project and path and get going
+    //
+    // * {{{project}}} is the name of the project that houses the file
+    // * {{{path}}} is the full path to load the file into
+    // * {{{onSuccess}}} is a callback to fire if the file is loaded
+    // * {{{dontStartSession}}} is a flag to turn off starting a session. Used in the config loading for example
+    loadFile: function(project, path, onSuccess, dontStartSession) {
+        if (!dontStartSession) bespin.get('editSession').startSession(project, path);
+
+        this.server.loadFile(project, path, function(content) {
+            if (/\n$/.test(content)) content = content.substr(0, content.length - 1);
+
+            onSuccess({
+                name: path,
+                content: content,
+                timestamp: new Date().getTime()
+            });
+        });
+    },
+
+    // ** {{{ bespin.client.FileSystem.forceOpenFile(project, path, content) }}}
+    //
+    // With the given project and path, open a file if existing, else create a new one
+    //
+    // * {{{project}}} is the name of the project to create the file in
+    // * {{{path}}} is the full path to save the file into
+    // * {{{content}}} is the template initial content to put in the new file (if new)
+    forceOpenFile: function(project, path, content) {
+        this.whenFileDoesNotExist(project, path, {
+            execute: function() {
+                if (!content) content = " ";
+                bespin.publish("bespin:editor:newfile", {
+                    project: project,
+                    newfilename: path,
+                    content: content
+                });
+            },
+            elseFailed: function() {
+                bespin.publish("bespin:editor:openfile", {
+                    project: project,
+                    filename: path
+                });
+            }
+        });
+    },
+
+    // ** {{{ FileSystem.projects(callback) }}}
+    //
+    // Return a JSON representation of the projects that the user has access too
+    //
+    // * {{{callback}}} is a callback that fires given the project list
+    projects: function(callback) {
+        this.server.projects(callback);
+    },
+
+    // ** {{{ bespin.client.FileSystem.fileNames(callback) }}}
+    //
+    // Return a JSON representation of the files at the root of the given project
+    //
+    // * {{{callback}}} is a callback that fires given the files
+    fileNames: function(project, callback) {
+        this.server.list(project, '', callback);
+    },
+
+    // ** {{{ bespin.client.FileSystem.saveFile(project, file) }}}
+    //
+    // Save a file to the given project
+    //
+    // * {{{project}}} is the name of the project to save into
+    // * {{{file}}} is the file object that contains the path and content to save
+    saveFile: function(project, file) {
+        // Unix files should always have a trailing new-line; add if not present
+        if (/\n$/.test(file.content)) file.content += "\n";
+
+        this.server.saveFile(project, file.name, file.content, file.lastOp);
+    },
+
+    // ** {{{ bespin.client.FileSystem.removeFile(project, path, onSuccess, onFailure) }}}
+    //
+    // Create a directory
+    //
+    // * {{{project}}} is the name of the directory to create
+    // * {{{path}}} is the full path to the directory to create
+    // * {{{onSuccess}}} is the callback to fire if the make works
+    // * {{{onFailure}}} is the callback to fire if the make fails
+    makeDirectory: function(project, path, onSuccess, onFailure) {
+        this.server.makeDirectory(project, path, onSuccess, onFailure);
+    },
+
+    // ** {{{ bespin.client.FileSystem.makeDirectory(project, path, onSuccess, onFailure) }}}
+    //
+    // Remove a directory
+    //
+    // * {{{project}}} is the name of the directory to remove
+    // * {{{path}}} is the full path to the directory to delete
+    // * {{{onSuccess}}} is the callback to fire if the remove works
+    // * {{{onFailure}}} is the callback to fire if the remove fails
+    removeDirectory: function(project, path, onSuccess, onFailure) {
+        this.server.removeFile(project, path, onSuccess, onFailure);
+    },
+
+    // ** {{{ bespin.client.FileSystem.removeFile(project, path, onSuccess, onFailure) }}}
+    //
+    // Remove the file from the file system
+    //
+    // * {{{project}}} is the name of the project to delete the file from
+    // * {{{path}}} is the full path to the file to delete
+    // * {{{onSuccess}}} is the callback to fire if the remove works
+    // * {{{onFailure}}} is the callback to fire if the remove fails
+    removeFile: function(project, path, onSuccess, onFailure) {
+        this.server.removeFile(project, path, onSuccess, onFailure);
+    },
+
+    // ** {{{ bespin.client.FileSystem.removeFile(project, path, onSuccess, onFailure) }}}
+    //
+    // Close the open session for the file
+    //
+    // * {{{project}}} is the name of the project to close the file from
+    // * {{{path}}} is the full path to the file to close
+    // * {{{callback}}} is the callback to fire when closed
+    closeFile: function(project, path, callback) {
+        this.server.closeFile(project, path, callback);
+    },
+
+    // ** {{{ bespin.client.FileSystem.whenFileExists(project, path, callbacks) }}}
+    //
+    // Check to see if the file exists and then run the appropriate callback
+    //
+    // * {{{project}}} is the name of the project
+    // * {{{path}}} is the full path to the file
+    // * {{{callbacks}}} is the pair of callbacks:
+    //   execute (file exists)
+    //   elseFailed (file does not exist)
+    whenFileExists: function(project, path, callbacks) {
+        this.server.list(project, bespin.util.path.directory(path), function(files) {
+            if (files && dojo.some(files, function(file){ return (file.name == path); })) {   
+                callbacks['execute']();
+            } else {
+                if (callbacks['elseFailed']) callbacks['elseFailed']();
+            }
+        }, function(xhr) {
+            if (callbacks['elseFailed']) callbacks['elseFailed'](xhr);
+        });
+    },
+
+    // ** {{{ bespin.client.FileSystem.whenFileDoesNotExist(project, path, callbacks) }}}
+    //
+    // The opposite of {{{ bespin.client.FileSystem.whenFileExists() }}}
+    //
+    // * {{{project}}} is the name of the project
+    // * {{{path}}} is the full path to the file
+    // * {{{callbacks}}} is the pair of callbacks:
+    //   execute (file does not exist)
+    //   elseFailed (file exists)
+    whenFileDoesNotExist: function(project, path, callbacks) {
+        this.server.list(project, bespin.util.path.directory(path), function(files) {
+            if (!files || !dojo.some(files, function(file){ return (file.name == path); })) {
+                callbacks['execute']();
+            } else {
+                if (callbacks['elseFailed']) callbacks['elseFailed']();
+            }
+        }, function(xhr) {
+            callbacks['execute'](); // the list failed which means it didn't exist
+        });
+    }
+});
\ No newline at end of file

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/filesystem.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,522 @@
+dojo.provide("bespin.client.server");
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// = Server =
+//
+// The Server object implements the [[https://wiki.mozilla.org/BespinServerAPI|Bespin Server API]]
+// giving the client access to the backend store. The {{{FileSystem}}} object uses this to talk back.
+
+dojo.declare("bespin.client.Server", null, {
+    // ** {{{ initialize(base) }}}
+    //
+    // Object creation initialization
+    //
+    // * {{{base}}} is the base server URL to access
+    constructor: function(base) {
+        this.SERVER_BASE_URL = base || '';
+    },
+
+    // == Helpers ==
+
+    // ** {{{ request(method, url, payload, callbackOptions) }}}
+    //
+    // The core way to access the backend system.
+    // Similar to the Prototype Ajax.Request wrapper 
+    //
+    // * {{{method}}} is the HTTP method (GET|POST|PUT|DELETE)
+    // * {{{url}}} is the sub url to hit (after the base url)
+    // * {{{payload}}} is what to send up for POST requests
+    // * {{{options}}} is how you pass in various callbacks.
+    //   options['evalJSON'] = true or false to auto eval
+    //   options['call'] = the main callback
+    //   options['log'] = just log the following
+    //   options['onFailure'] = call for general failures
+    //   options['on' + STATUS CODE] = call for specific failures
+    request: function(method, url, payload, options) {
+        var xhr = new XMLHttpRequest();
+
+        if (location.href.indexOf("file:") == 0){ // if developing and using this locally only!
+           try {
+               if (netscape.security.PrivilegeManager.enablePrivilege) {
+                   netscape.security.PrivilegeManager.enablePrivilege('UniversalBrowserRead');
+               }
+           } catch (ex) {
+           }
+        }
+
+        if (options) { // do it async (best)
+            xhr.onreadystatechange = function() {
+                if (xhr.readyState == 4) {
+                    if (xhr.status && xhr.status != 0 && (xhr.status >= 200 && xhr.status < 300)) {
+                        var response = xhr.responseText;
+                        
+                        if (options['evalJSON'] && response) {
+                            try {
+                                response = dojo.fromJson(response);
+                            } catch (syntaxException) {
+                                console.log("Couldn't eval the JSON: " + response + " (SyntaxError: " + syntaxException + ")");
+                            }
+                        }
+                        
+                        if (dojo.isFunction(options['call'])) {
+                            options['call'](response, xhr);
+                        } else if (options['log']) {
+                            console.log(options['log']);
+                        }
+                    } else {                        
+                        var onStatus = 'on' + xhr.status;
+                        if (options[onStatus]) {
+                            options[onStatus](xhr);
+                        } else if (options['onFailure']) {
+                            options['onFailure'](xhr);
+                        }
+                    }
+                }
+            };
+            xhr.open(method, this.SERVER_BASE_URL + url, true); // url must have leading /
+            xhr.setRequestHeader("Content-Type", 'application/x-www-form-urlencoded');
+            if (options.headers) {
+                for (var key in options.headers) {
+                    if (options.headers.hasOwnProperty(key)) {
+                        xhr.setRequestHeader(key, options.headers[key]);
+                    }
+                }
+            }
+            xhr.send(payload);
+        } else {
+            var fullUrl = this.SERVER_BASE_URL + url;
+            console.log("Are you sure you want to do a synchronous Ajax call? Really? " + fullUrl);
+            xhr.open(method, fullUrl, false);
+            xhr.send(payload);
+            return xhr.responseText;
+        }
+    },
+
+    // == USER ==
+
+    // ** {{{ login(user, pass, token, callback, notloggedin) }}}
+    //
+    // Try to login to the backend system.
+    // 
+    // * {{{user}}} is the username
+    // * {{{pass}}} is the password
+    // * {{{onSuccess}}} fires when the user is logged in
+    // * {{{onFailure}}} fires when the user failed to login
+    login: function(user, pass, token, onSuccess, onFailure) {
+        var url = "/register/login/" + user;
+        this.request('POST', url, "password=" + escape(pass), { 
+            call: onSuccess,
+            on401: onFailure,
+            log: 'Login complete.',
+            headers: { 'DoubleSubmitCookie':token }
+        });
+    },
+
+    // ** {{{ signup(user, pass, email, callback, notloggedin, userconflict) }}}
+    //
+    // Signup / Register the user to the backend system
+    // 
+    // * {{{user}}} is the username
+    // * {{{pass}}} is the password
+    // * {{{email}}} is the email
+    // * {{{onSuccess}}} fires when the user is logged in
+    // * {{{notloggedin}}} fires when not logged in
+    // * {{{userconflict}}} fires when the username exists
+	signup: function(user, pass, email, onSuccess, notloggedin, userconflict) {
+        var url = "/register/new/" + user;
+        this.request('POST', url, 
+			"password=" + escape(pass) + "&email=" + escape(email), { 
+			call: onSuccess, on401: notloggedin, on409: userconflict,
+			log: 'Login complete.' 
+		});
+	},
+
+    // ** {{{ logout(callback) }}}
+    //
+    // Logout from the backend
+    // 
+    // * {{{callback}}} fires after the logout attempt
+    logout: function(callback) {
+        var url = "/register/logout/";
+        this.request('POST', url, null, { log: 'Logout complete.', call: callback });
+    },
+
+    // ** {{{ currentuser(callback, notloggedin) }}}
+    //
+    // Return info on the current logged in user
+    // 
+    // * {{{callback}}} fires after the user attempt
+    // * {{{notloggedin}}} fires if the user isn't logged in
+    currentuser: function(callback, notloggedin) {
+        var url = "/register/userinfo/";
+        return this.request('GET', url, null, 
+                { call: callback, on401: notloggedin, evalJSON: true });
+    },
+
+    // == FILES ==
+
+    // ** {{{ list(project, path, onSuccess, onFailure) }}}
+    //
+    // List the path in the given project
+    // 
+    // * {{{project}}} is the project to list
+    // * {{{path}}} is the path to list out
+    // * {{{onSuccess}}} fires if the list returns something
+    // * {{{onFailure}}} fires if there is an error getting a list from the server
+    list: function(project, path, onSuccess, onFailure) {
+        var project = project || '';
+        var url = bespin.util.path.combine('/file/list/', project, path || '/');
+        var opts = { call: onSuccess, evalJSON: true, log: "Listing files in: " + url };
+        if (dojo.isFunction(onFailure)) opts.onFailure = onFailure;
+
+        this.request('GET', url, null, opts);
+    },
+
+    // ** {{{ projects(callback) }}}
+    //
+    // Return the list of projects that you have access too
+    // 
+    // * {{{callback}}} gets fired with the project list
+    projects: function(callback) {
+        this.request('GET', '/file/list/', null, { call: callback, evalJSON: true });
+    },
+
+    // ** {{{ saveFile(project, path, contents, lastOp) }}}
+    //
+    // Save the given file
+    // 
+    // * {{{project}}} is the project to save
+    // * {{{path}}} is the path to save to
+    // * {{{callback}}} fires after the save returns
+    // * {{{lastOp}}} contains the last edit operation
+    saveFile: function(project, path, contents, lastOp) {
+        if (!project || !path) return;
+
+        var url = bespin.util.path.combine('/file/at', project, (path || ''));
+        if (lastOp) url += "?lastEdit=" + lastOp;
+
+        this.request('PUT', url, contents, { log: 'Saved file.' });
+    },
+
+    // ** {{{ loadFile(project, path, contents) }}}
+    //
+    // Load the given file
+    // 
+    // * {{{project}}} is the project to load from
+    // * {{{path}}} is the path to load
+    // * {{{callback}}} fires after the file is loaded
+    loadFile: function(project, path, callback) {
+        var project = project || '';
+        var path = path || '';
+        var url = bespin.util.path.combine('/file/at', project, path);
+
+        this.request('GET', url, null, { call: callback });
+    },
+
+    // ** {{{ removeFile(project, path, onSuccess, onFailure) }}}
+    //
+    // Remove the given file
+    // 
+    // * {{{project}}} is the project to remove from
+    // * {{{path}}} is the path to remove
+    // * {{{onSuccess}}} fires if the deletion works
+    // * {{{onFailure}}} fires if the deletion failed
+    removeFile: function(project, path, onSuccess, onFailure) {
+        var project = project || '';
+        var path = path || '';
+        var url = bespin.util.path.combine('/file/at', project, path);
+        var opts = { call: onSuccess };
+        if (dojo.isFunction(onFailure)) opts.onFailure = onFailure;
+        
+        this.request('DELETE', url, null, opts);
+    },
+
+    // ** {{{ makeDirectory(project, path, onSuccess, onFailure) }}}
+    //
+    // Create a new directory
+    // 
+    // * {{{project}}} is the project to save
+    // * {{{path}}} is the path to save to
+    // * {{{onSuccess}}} fires if the deletion works
+    // * {{{onFailure}}} fires if the deletion failed
+    makeDirectory: function(project, path, onSuccess, onFailure) {
+        if (!project) return;
+
+        var url = bespin.util.path.combineAsDirectory('/file/at', project, (path || ''));
+        var opts = {};
+        if (dojo.isFunction(onSuccess)) {
+            opts.call = onSuccess;
+        } else {
+            opts['log'] = "Made a directory: [project=" + project + ", path=" + path + "]";
+        }
+        if (dojo.isFunction(onFailure)) opts.onFailure = onFailure;
+
+        this.request('PUT', url, null, opts);
+    },
+    
+    // ** {{{ removeDirectory(project, path, onSuccess, onFailure) }}}
+    //
+    // Removed a directory
+    // 
+    // * {{{project}}} is the project to save
+    // * {{{path}}} is the path to save to
+    // * {{{onSuccess}}} fires if the deletion works
+    // * {{{onFailure}}} fires if the deletion failed
+    removeDirectory: function(project, path, onSuccess, onFailure) {
+        if (!project) return;
+        if (!path) path = '';
+        
+        var url = bespin.util.path.combineAsDirectory('/file/at', project, path);
+        var opts = {};
+        if (dojo.isFunction(onSuccess)) {
+            opts.call = onSuccess;
+        } else {
+            opts['log'] = "Removed directory: [project=" + project + ", path=" + path + "]";
+        }
+        if (dojo.isFunction(onFailure)) opts.onFailure = onFailure;
+
+        this.request('DELETE', url, null, opts);
+    },
+
+     // ** {{{ listOpen(callback) }}}
+     //
+     // Returns JSON with the key of filename, and the value of an array of usernames:
+     // { "foo.txt": ["ben"], "SomeAjaxApp/foo.txt": ["dion"] }
+     // 
+     // * {{{callback}}} fires after listing the open files
+    listOpen: function(callback) {
+        this.request('GET', '/file/listopen/', null, {
+            call: callback, evalJSON: true, log: 'List open files.' 
+        });
+    },
+
+    // ** {{{ closeFile(project, path, callback) }}}
+    //
+    // Close the given file (remove from open sessions)
+    // 
+    // * {{{project}}} is the project to close from
+    // * {{{path}}} is the path to close
+    // * {{{callback}}} fires after the file is closed
+    closeFile: function(project, path, callback) {
+        var path = path || '';
+        var url = bespin.util.path.combine('/file/close', project, path);
+        this.request('POST', url, null, { call: callback });
+    },
+    
+    // ** {{{ searchFiles(project, searchstring, callback) }}}
+    //
+    // Search for files within the given project
+    // 
+    // * {{{project}}} is the project to look from
+    // * {{{searchstring}}} to compare files with
+    // * {{{callback}}} fires after the file is closed
+    searchFiles: function(project, searchkey, callback) {
+        var url = bespin.util.path.combine('/file/search', project+'?q='+escape(searchkey));
+        var opts = { call: callback, evalJSON: true, log: "Listing searchfiles for: " + project + ", searchkey: " + searchkey};
+        this.request('GET', url, null, opts);
+    },
+
+    // == EDIT ==
+
+    // ** {{{ editActions(project, path, callback) }}}
+    //
+    // Get the list of edit actions
+    // 
+    // * {{{project}}} is the project to edit from
+    // * {{{path}}} is the path to edit
+    // * {{{callback}}} fires after the edit is done
+    editActions: function(project, path, callback) {
+        var path = path || '';
+        var url = bespin.util.path.combine('/edit/list', project, path);
+        this.request('GET', url, null, { call: callback, log: "Edit Actions Complete." });
+    },
+
+    // ** {{{ editAfterActions(project, path, callback) }}}
+    //
+    // Get the list of edit after actions
+    // 
+    // * {{{project}}} is the project to edit from
+    // * {{{path}}} is the path to edit
+    // * {{{callback}}} fires after the edit is done
+    editAfterActions: function(project, path, index, callback) {
+        var path = path || '';
+        var url = bespin.util.path.combine('/edit/recent', index, project, path);
+        this.request('GET', url, null, { call: callback, log: "Edit After Actions Complete." });
+    },
+
+    // ** {{{ doAction(project, path, actions) }}}
+    //
+    // Store actions to the edit queue
+    // 
+    // * {{{project}}} is the project
+    // * {{{path}}} is the path
+    // * {{{actions}}} contain the actions to store
+    doAction: function(project, path, actions) {
+        var path = path || '';
+        var url = bespin.util.path.combine('/edit', project, path);
+
+        var sp = "[" + actions.join(",") + "]";
+
+        this.request('PUT', url, sp, { call: function(){} });
+    },
+
+    // == PROJECTS ==
+    //
+    // still needed: owners, authorize, deauthorize
+    
+    // ** {{{ exportProject(project, archivetype) }}}
+    //
+    // Export the project as either a zip file or tar + gz
+    // 
+    // * {{{project}}} is the project to export
+    // * {{{archivetype}}} is either zip | tgz
+    exportProject: function(project, archivetype) {
+        if (bespin.util.include(['zip','tgz','tar.gz'], archivetype)) {
+//            console.log('/project/export', project + "." + archivetype);
+            var iframe = document.createElement("iframe");
+            iframe.src = bespin.util.path.combine('/project/export', project + "." + archivetype);
+            iframe.style.display = 'none';
+            iframe.style.height = iframe.style.width = "0";
+            document.getElementsByTagName("body")[0].appendChild(iframe);
+        }
+    },
+
+    // ** {{{ importProject(project, url, opts) }}}
+    //
+    // Import the given file into the given project
+    // 
+    // * {{{project}}} is the project to export
+    // * {{{url}}} is the URL to the file to import
+    // * {{{archivetype}}} is either zip | tgz
+    importProject: function(project, url, opts) {
+        if (opts) { // wrap the import success call in an event to say that the import is complete
+            var userCall = opts.call;
+            opts.call = function(text, xhr) {
+                userCall(text, xhr);
+                bespin.publish("bespin:project:imported", {
+                    project: project,
+                    url: url
+                });
+            };
+        }
+        
+        this.request('POST', '/project/fromurl/' + project, url, opts || {});
+    },
+    
+    // ** {{{ renameProject(currentProject, newProject) }}}
+    //
+    // Import the given file into the given project
+    // 
+    // * {{{currentProject}}} is the current name of the project
+    // * {{{newProject}}} is the new name
+    renameProject: function(currentProject, newProject, opts) {
+        if (!opts) opts = { log: "Renaming project from " + currentProject + " to " + newProject };
+        if (currentProject && newProject) {
+            this.request('POST', '/project/rename/' + currentProject + "/", newProject, opts);
+        }
+    },
+
+    // == SETTINGS ==
+    //
+    //
+    // * GET /settings/ to list all settings for currently logged in user as json dict
+    // * GET /settings/[setting] to get the value for a single setting as json string
+    // * POST /settings/ with HTTP POST DATA (in standard form post syntax) to set the value for a collection of settings (all values are strings)
+    // * DELETE /settings/[setting] to delete a single setting
+
+    listSettings: function(callback) {
+        if (typeof callback == "function") {
+            this.request('GET', '/settings/', null, { call: callback, evalJSON: true });
+        }
+    },
+
+    getSetting: function(name, callback) {
+        if (typeof callback == "function") {
+            this.request('GET', '/settings/' + name, null, { call: callback });
+        }
+    },
+    
+    setSetting: function(name, value, callback) {
+        var settings = {};
+        settings[name] = value;
+        this.setSettings(settings, (callback || function(){}));
+    },
+    
+    setSettings: function(settings, callback) {
+        this.request('POST', '/settings/', dojo.objectToQuery(settings), { call: (callback || function(){}) });
+    },
+    
+    unsetSetting: function(name, callback) {
+        this.request('DELETE', '/settings/' + name, null, { call: (callback || function(){}) });
+    },
+
+    // ** {{{ follows(opts) }}}
+    // Get a list of the users the current user is following
+    followers: function(opts) {
+        this.request('GET', '/network/followers/', null, opts || {});
+    },
+
+    // ** {{{ follows(opts) }}}
+    // Get a list of the users the current user is following
+    follow: function(users, opts) {
+        this.request('POST', '/network/follow/', dojo.toJson(users), opts || {});
+    },
+
+    // ** {{{ follows(opts) }}}
+    // Get a list of the users the current user is following
+    unfollow: function(users, opts) {
+        this.request('POST', '/network/unfollow/', dojo.toJson(users), opts || {});
+    },
+
+    // ** {{{ groupListAll() }}}
+    // Get a list of the users the current user is following
+    groupListAll: function(opts) {
+        this.request('GET', '/group/list/all/', null, opts || {});
+    },
+
+    // ** {{{ groupList() }}}
+    // Get a list of the users the current user is following
+    groupList: function(group, opts) {
+        this.request('GET', '/group/list/' + group + '/', null, opts || {});
+    },
+
+    // ** {{{ groupRemoveAll() }}}
+    // Get a list of the users the current user is following
+    groupRemoveAll: function(group, opts) {
+        this.request('POST', '/group/remove/all/' + group + '/', null, opts || {});
+    },
+
+    // ** {{{ groupAdd() }}}
+    // Get a list of the users the current user is following
+    groupAdd: function(group, users, opts) {
+        this.request('POST', '/group/remove/' + group + '/', dojo.toJson(users), opts || {});
+    },
+
+    // ** {{{ groupRemove() }}}
+    // Get a list of the users the current user is following
+    groupRemove: function(group, users, opts) {
+        this.request('POST', '/group/add/' + group + '/', dojo.toJson(users), opts || {});
+    }
+});

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/server.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// = Session =
+//
+// This session module provides functionality that both stores session information
+// and handle collaboration.
+//
+// This module includes:
+//
+// * {{{ bespin.client.session.EditSession }}}: Wraps a file edit session
+// * {{{ bespin.client.session.SyncHelper }}}: Deals with syncing edits back to the server
+
+dojo.provide("bespin.client.session");
+
+// ** {{{ bespin.client.session.EditSession }}} **
+//
+// EditSession represents a file edit session with the Bespin back-end server. It is responsible for
+// sending changes to the server as well as receiving changes from the server and mutating the document
+// model with received changes.
+
+dojo.declare("bespin.client.session.EditSession", null, {
+    constructor: function(editor) {        
+        this.editor = editor;
+        this.collaborate = false;
+    },
+
+    setUserinfo: function(userinfo) {
+        this.username = userinfo.username;
+        this.amountUsed = userinfo.amountUsed;
+        this.quota = userinfo.quota;
+    },
+
+    checkSameFile: function(project, path) {
+        return ((this.project == project) && (this.path == path));
+    },
+
+    startSession: function(project, path, username) {
+        this.project = project;
+        this.path = path;
+        if (!this.username) this.username = username;
+
+        if (this.collaborate) {
+            // Extreme suckage: This starts a sync session with the server.
+            // All it's really doing is talking to a sync service which is totally
+            // separate from the file storage API. They need merging, and then we
+            // should be able to ditch whatever loads the text into the editor
+            mobwrite.share(bespin.get('editSession'));
+        }
+    },
+
+    stopSession: function() {
+        this.project = undefined;
+        this.path = undefined;
+    }
+});
\ No newline at end of file

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/session.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,561 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+dojo.provide("bespin.client.settings");
+
+// = Settings =
+//
+// This settings module provides a base implementation to store settings for users.
+// It also contains various "stores" to save that data, including:
+//
+// * {{{bespin.client.settings.Core}}} : Core interface to settings. User code always goes through here.
+// * {{{bespin.client.settings.Server}}} : The main store. Saves back to the Bespin Server API
+// * {{{bespin.client.settings.InMemory}}} : In memory settings that are used primarily for debugging
+// * {{{bespin.client.settings.Cookie}}} : Store in a cookie using cookie-jar
+// * {{{bespin.client.settings.URL}}} : Intercept settings in the URL. Often used to override
+// * {{{bespin.client.settings.DB}}} : Commented out for now, but a way to store settings locally
+// * {{{bespin.client.settings.Events}}} : Custom events that the settings store can intercept and send
+
+// ** {{{ bespin.client.settings.Core }}} **
+//
+// Handles load/save of user settings.
+// TODO: tie into the sessions servlet; eliminate Gears dependency
+
+dojo.declare("bespin.client.settings.Core", null, {
+    constructor: function(store) {
+        this.browserOverrides = {};
+        this.fromURL = new bespin.client.settings.URL();
+        this.customEvents = new bespin.client.settings.Events(this);
+
+        this.loadStore(store);    // Load up the correct settings store
+    },
+
+    loadSession: function() {
+        var path    = this.fromURL.get('path') || this.get('_path');
+        var project = this.fromURL.get('project') || this.get('_project');
+        
+        bespin.publish("bespin:settings:init", { // -- time to init my friends
+            path: path,
+            project: project
+        });
+    },
+
+    defaultSettings: function() {
+        return {
+            'tabsize': '2',
+            'fontsize': '10',
+            'autocomplete': 'off',
+            'collaborate': 'off',
+            'syntax': 'auto'
+        };
+    },
+
+    isOn: function(value) {
+        return value == 'on' || value == 'true';
+    },
+
+    isOff: function(value) {
+        return value == 'off' || value == 'false';
+    },
+    
+    isSettingOn: function(key) {
+        return this.isOn(this.get(key));
+    },
+
+    isSettingOff: function(key) {
+        return this.isOff(this.get(key));
+    },
+
+    // ** {{{ Settings.loadStore() }}} **
+    //
+    // This is where we choose which store to load
+    loadStore: function(store) {
+        this.store = new (store || bespin.client.settings.Server)(this);
+
+//        this.store = new Bespin.Settings.Cookie(this);
+
+// TODO: ignore gears for now:
+// this.store = (window['google']) ? new Bespin.Settings.DB : new Bespin.Settings.InMemory;
+// this.store = new Bespin.Settings.InMemory;
+    },
+
+    toList: function() {
+        var settings = [];
+        var storeSettings = this.store.settings;
+        for (var prop in storeSettings) {
+            if (storeSettings.hasOwnProperty(prop)) {
+                settings.push({ 'key': prop, 'value': storeSettings[prop] });
+            }
+        }
+        return settings;
+    },
+
+    set: function(key, value) {
+        this.store.set(key, value);
+
+        bespin.publish("bespin:settings:set:" + key, { value: value });
+    },
+
+    get: function(key) {
+        var fromURL = this.fromURL.get(key); // short circuit
+        if (fromURL) return fromURL;
+
+        return this.store.get(key);
+    },
+
+    unset: function(key) {
+        this.store.unset(key);
+    },
+
+    list: function() {
+        if (typeof this.store['list'] == "function") {
+            return this.store.list();
+        } else {
+            return this.toList();
+        }
+    }
+
+});
+
+// ** {{{ bespin.client.settings.InMemory }}} **
+//
+// Debugging in memory settings (die when browser is closed)
+
+dojo.declare("bespin.client.settings.InMemory", null, {
+    constructor: function(parent) {
+        this.parent = parent;
+
+        this.settings = this.parent.defaultSettings();
+
+        bespin.publish("bespin:settings:loaded");
+    },
+
+    set: function(key, value) {
+        this.settings[key] = value;
+    },
+
+    get: function(key) {
+        return this.settings[key];
+    },
+
+    unset: function(key) {
+        delete this.settings[key];
+    }
+});
+
+// ** {{{ bespin.client.settings.Cookie }}} **
+//
+// Save the settings in a cookie
+
+dojo.declare("bespin.client.settings.Cookie", null, {
+    constructor: function(parent) {
+        var expirationInHours = 1;
+        this.cookieSettings = {
+            expires: expirationInHours / 24,
+            path: '/'
+        };
+
+        var settings = dojo.fromJson(dojo.cookie("settings"));
+
+        if (settings) {
+            this.settings = settings;
+        } else {
+            this.settings = {
+                'tabsize': '2',
+                'fontsize': '10',
+                'autocomplete': 'off',
+                'collaborate': 'off',
+                '_username': 'dion'
+            };
+            dojo.cookie("settings", dojo.toJson(this.settings), this.cookieSettings);
+        }
+        bespin.publish("bespin:settings:loaded");
+    },
+
+    set: function(key, value) {
+        this.settings[key] = value;
+        dojo.cookie("settings", dojo.toJson(this.settings), this.cookieSettings);
+    },
+
+    get: function(key) {
+        return this.settings[key];
+    },
+
+    unset: function(key) {
+        delete this.settings[key];
+        dojo.cookie("settings", dojo.toJson(this.settings), this.cookieSettings);
+    }
+});    
+
+// ** {{{ bespin.client.settings.Server }}} **
+//
+// The real grand-daddy that implements uses {{{Server}}} to access the backend
+
+dojo.declare("bespin.client.settings.Server", null, {
+    constructor: function(parent) {
+        this.parent = parent;
+        this.server = bespin.get('server');
+
+        // TODO: seed the settings  
+        this.server.listSettings(dojo.hitch(this, function(settings) {
+            this.settings = settings;
+            if (settings['tabsize'] === undefined) {
+                this.settings = this.parent.defaultSettings();
+                this.server.setSettings(this.settings);
+            }
+            bespin.publish("bespin:settings:loaded");
+        }));
+    },
+
+    set: function(key, value) {
+        this.settings[key] = value;
+        this.server.setSetting(key, value);
+    },
+
+    get: function(key) {
+        return this.settings[key];
+    },
+
+    unset: function(key) {
+        delete this.settings[key];
+        this.unsetSetting(key);
+    }
+});
+
+
+// ** {{{ bespin.client.settings.DB }}} **
+//
+// Taken out for now to allow us to not require gears_db.js (and Gears itself).
+// Experimental ability to save locally in the SQLite database.
+// The plan is to migrate to ActiveRecord.js or something like it to abstract on top
+// of various stores (HTML5, Gears, globalStorage, etc.)
+
+/*
+// turn off for now so we can take gears_db.js out
+
+Bespin.Settings.DB = Class.create({
+    initialize: function(parent) {
+        this.parent = parent;
+        this.db = new GearsDB('wideboy');
+
+        //this.db.run('drop table settings');
+        this.db.run('create table if not exists settings (' +
+               'id integer primary key,' +
+               'key varchar(255) unique not null,' +
+               'value varchar(255) not null,' +
+               'timestamp int not null)');
+
+        this.db.run('CREATE INDEX IF NOT EXISTS settings_id_index ON settings (id)');
+        bespin.publish("bespin:settings:loaded");
+    },
+
+    set: function(key, value) {
+        this.db.forceRow('settings', { 'key': key, 'value': value, timestamp: new Date().getTime() }, 'key');
+    },
+
+    get: function(key) {
+        var rs = this.db.run('select distinct value from settings where key = ?', [ key ]);
+        try {
+            if (rs && rs.isValidRow()) {
+              return rs.field(0);
+            }
+        } catch (e) {
+            console.log(e.message);
+        } finally {
+            rs.close();
+        }
+    },
+
+    unset: function(key) {
+        this.db.run('delete from settings where key = ?', [ key ]);
+    },
+
+    list: function() {
+        // TODO: Need to override with browser settings
+        return this.db.selectRows('settings', '1=1');
+    },
+
+    // -- Private-y
+    seed: function() {
+        this.db.run('delete from settings');
+
+        // TODO: loop through the settings
+        this.db.run('insert into settings (key, value, timestamp) values (?, ?, ?)', ['keybindings', 'emacs', 1183878000000]);
+        this.db.run('insert into settings (key, value, timestamp) values (?, ?, ?)', ['tabsize', '2', 1183878000000]);
+        this.db.run('insert into settings (key, value, timestamp) values (?, ?, ?)', ['fontsize', '10', 1183878000000]);
+        this.db.run('insert into settings (key, value, timestamp) values (?, ?, ?)', ['autocomplete', 'off', 1183878000000]);
+    }
+});
+*/
+
+// ** {{{ bespin.client.settings.URL }}} **
+//
+// Grab the setting from the URL, either via # or ?   
+
+dojo.declare("bespin.client.settings.URL", null, {
+    constructor: function(queryString) {            
+        this.results = dojo.queryToObject(this.stripHash(queryString || window.location.hash));
+    },
+
+    get: function(key) {
+        return this.results[key];
+    },
+
+    set: function(key, value) {
+        this.results[key] = value;
+    },
+    
+    stripHash: function(url) {
+        var tobe = url.split('');
+        tobe.shift();
+        return tobe.join('');
+    }
+});
+
+// ** {{{ bespin.client.settings.Events }}} **
+//
+// Custom Event holder for the Settings work. 
+// It deals with both settings themselves, and other events that
+// settings need to watch and look for
+
+dojo.declare("bespin.client.settings.Events", null, {
+    constructor: function(settings) {
+        var editSession = bespin.get('editSession');
+        var editor = bespin.get('editor');
+
+        // ** {{{ Event: bespin:settings:set }}} **
+        // 
+        // Watch for someone wanting to do a set operation
+        bespin.subscribe("bespin:settings:set", function(event) {
+            var key = event.key;
+            var value = event.value;
+
+            settings.set(key, value);
+        });
+
+        // ** {{{ Event: bespin:editor:openfile:opensuccess }}} **
+        // 
+        // Change the session settings when a new file is opened
+        bespin.subscribe("bespin:editor:openfile:opensuccess", function(event) {
+            var file = event.file;
+
+            editSession.path = file.name;
+
+            settings.set('_project',  editSession.project);
+            settings.set('_path',     editSession.path);
+            settings.set('_username', editSession.username);
+
+            if (editSession.syncHelper) editSession.syncHelper.syncWithServer();
+        });
+
+        // ** {{{ Event: bespin:editor:openfile:opensuccess }}} **
+        // 
+        // Change the syntax highlighter when a new file is opened
+        bespin.subscribe("bespin:editor:openfile:opensuccess", function(event) {
+            var file = event.file;
+            var split = file.name.split('.');
+            var type = split[split.length - 1]; 
+
+            if (type) {
+                bespin.publish("bespin:settings:syntax", { language: type });
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:set:syntax }}} **
+        // 
+        // When the syntax setting is changed, tell the syntax system to change
+        bespin.subscribe("bespin:settings:set:syntax", function(event) {
+            bespin.publish("bespin:settings:syntax", { language: event.value, fromCommand: true });
+        });
+
+        // ** {{{ Event: bespin:settings:syntax }}} **
+        // 
+        // Given a new syntax command, change the editor.language        
+        bespin.subscribe("bespin:settings:syntax", function(event) {
+            var language = event.language;
+            var fromCommand = event.fromCommand;
+            var syntaxSetting = settings.get('syntax') || "off";
+
+            if (language == editor.language) return; // already set to be that language
+
+            if (bespin.util.include(['auto', 'on'], language)) {
+                var split = window.location.hash.split('.');
+                var type = split[split.length - 1];                
+                if (type) editor.language = type;
+            } else if (bespin.util.include(['auto', 'on'], syntaxSetting) || fromCommand) {
+                editor.language = language;
+            } else if (syntaxSetting == 'off') {
+                editor.language = 'off';
+            } 
+        });
+
+        // ** {{{ Event: bespin:settings:set:collaborate }}} **
+        // 
+        // Turn on the collaboration system if set to be on
+        bespin.subscribe("bespin:settings:set:collaborate", function(event) {
+            editSession.collaborate = settings.isOn(event.value);
+        });
+
+        // ** {{{ Event: bespin:settings:set:fontsize }}} **
+        // 
+        // Change the font size for the editor
+        bespin.subscribe("bespin:settings:set:fontsize", function(event) {
+            var fontsize = parseInt(event.value);
+            editor.theme.lineNumberFont = fontsize + "pt Monaco, Lucida Console, monospace";
+        });
+
+        // ** {{{ Event: bespin:settings:set:theme }}} **
+        // 
+        // Change the Theme object used by the editor
+        bespin.subscribe("bespin:settings:set:theme", function(event) {
+            var theme = event.value;
+
+            if (theme) {
+                var themeSettings = bespin.themes[theme];
+
+                if (themeSettings) {
+                    if (themeSettings != editor.theme) {
+                        editor.theme = themeSettings;
+                    }
+                } else {
+                    bespin.publish("bespin:cmdline:showinfo", {
+                        msg: "Sorry old chap. No theme called '" + theme + "'. Fancy making it?"
+                    });
+                }
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:set:keybindings }}} **
+        // 
+        // Add in emacs key bindings
+        bespin.subscribe("bespin:settings:set:keybindings", function(event) {
+            var value = event.value;
+
+            if (value == "emacs") {
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "b",
+                    action: "moveCursorLeft"
+                });
+
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "f",
+                    action: "moveCursorRight"
+                });
+
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "p",
+                    action: "moveCursorUp"
+                });
+
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "n",
+                    action: "moveCursorDown"
+                });
+
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "a",
+                    action: "moveToLineStart"
+                });
+
+                bespin.publish("bespin:editor:bindkey", {
+                    modifiers: "ctrl",
+                    key: "e",
+                    action: "moveToLineEnd"
+                });
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:set:cursorblink }}} **
+        // 
+        // The frequency of the cursor blink in milliseconds (defaults to 250)
+        bespin.subscribe("bespin:settings:set:cursorblink", function(event) {
+            var ms = parseInt(event.value); // get the number of milliseconds
+
+            if (ms) {
+                editor.ui.toggleCursorFrequency = ms;
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:init }}} **
+        // 
+        // If we are opening up a new file
+        bespin.subscribe("bespin:settings:init", function(event) {
+            var path    = event.path;
+            var project = event.project;
+
+            // TODO: use the action and don't run a command itself
+            var newfile = settings.fromURL.get('new');
+            if (!newfile) { // scratch file
+                if (project && (editSession.project != project)) {
+                    bespin.publish("bespin:project:set", { project: project });
+                }
+
+                if (path) {
+                    bespin.publish("bespin:editor:openfile", { filename: path });
+                }
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:init }}} **
+        // 
+        // Setup the theme
+        bespin.subscribe("bespin:settings:init", function(event) {
+            bespin.publish("bespin:settings:set:theme", {
+                value: settings.get('theme')
+            });
+        });
+
+        // ** {{{ Event: bespin:settings:init }}} **
+        // 
+        // Setup the special keybindings
+        bespin.subscribe("bespin:settings:init", function(event) {
+            bespin.publish("bespin:settings:set:keybindings", {
+                value: settings.get('keybindings')
+            });
+        });
+
+        // ** {{{ Event: bespin:settings:init }}} **
+        // 
+        // Check for auto load
+        bespin.subscribe("bespin:settings:init", function(event) {
+            if (settings.isOn(settings.get('autoconfig'))) {
+                bespin.publish("bespin:editor:config:run");
+            }
+        });
+
+        // ** {{{ Event: bespin:settings:init }}} **
+        // 
+        // Setup the font size that the user has configured
+        bespin.subscribe("bespin:settings:init", function(event) {
+            bespin.publish("bespin:settings:set:fontsize", {
+                value: settings.get('fontsize')
+            });
+        });        
+    }
+});

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/settings.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,68 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */  
+ 
+dojo.provide("bespin.client.status");
+
+// = StatusChecker =
+//
+// This is currently just a mock to randomly share status messages.
+// In the future the server will serve up interesting data.
+// For now, it is turned off.
+
+dojo.declare("bespin.client.settings.StatusChecker", null, {
+    constructor: function() {
+        this.interval = 0;
+        this.statusMessages = [
+            "Bob is editing the file brick.html",
+            "Emily is creating a new tag called 'v3.4'",
+            "Jessica is saving the file kidly.html",
+            "John is idle. Lazy git!",
+            "Mickey has checked in a set of 4 files to project 'Bank'",
+            "Don has created the function 'doCalculation' in class 'Bank'",
+            "Benji is deleting the function 'doCalculation' in class 'Bank'"
+        ];
+    },
+
+    start: function() {
+        this.interval = setInterval(dojo.hitch(this, "updateStatus"), 12000);
+    },
+
+    stop: function() {
+        clearInterval(this.interval);
+    },
+
+    updateStatus: function() {
+        var randomMessage = this.randomStatus();
+        this.setStatus(randomMessage);
+    },
+
+    randomStatus: function() {
+        var random = Math.floor(Math.random() * this.statusMessages.length);
+        return this.statusMessages[random];
+    },
+
+    setStatus: function(message) {
+        dojo.byId('message').innerHTML = message;
+    }
+});

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/client/status.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,512 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * The Original Code is Bespin.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bespin Team (bespin@mozilla.com)
+ *
+ * ***** END LICENSE BLOCK ***** */
+    
+// = Command Line =
+//
+// This command line module provides everything that the command line interface needs:
+//
+// * {{{bespin.cmd.commandline.Interface}}} : The base class itself. The actually interface.
+// * {{{bespin.cmd.commandline.KeyBindings}}} : Handling the special key handling in the command line
+// * {{{bespin.cmd.commandline.History}}} : Handle command line history
+// * {{{bespin.cmd.commandline.SimpleHistoryStore}}} : Simple one session storage of history
+// * {{{bespin.cmd.commandline.Events}}} : The custom events that the command line needs to handle
+
+dojo.provide("bespin.cmd.commandline");
+
+// ** {{{ bespin.cmd.commandline.Interface }}} **
+//
+// The core command line driver. It executes commands, stores them, and handles completion
+
+dojo.declare("bespin.cmd.commandline.Interface", null, {
+    constructor: function(commandLine, initCommands) {
+        this.commandLine = dojo.byId(commandLine);
+
+        if (bespin.get('files')) this.files = bespin.get('files');
+        if (bespin.get('settings')) this.settings = bespin.get('settings');
+        if (bespin.get('editor')) this.editor = bespin.get('editor');
+
+        this.inCommandLine = false;
+        this.suppressInfo = false; // When true, info bar popups will not be shown
+        this.commands = {};
+        this.aliases = {};
+
+        this.commandLineKeyBindings = new bespin.cmd.commandline.KeyBindings(this);
+        this.commandLineHistory = new bespin.cmd.commandline.History(this);
+        this.customEvents = new bespin.cmd.commandline.Events(this);
+
+        if (initCommands) this.addCommands(initCommands); // initialize the commands for the cli
+    },
+
+    executeCommand: function(value) {
+        var data = value.split(/\s+/);
+        var commandname = data.shift();
+
+        var command;
+        var argstr = data.join(' ');
+
+        if (this.commands[commandname]) {
+            command = this.commands[commandname];
+        } else if (this.aliases[commandname]) {
+            var alias = this.aliases[commandname].split(' ');
+            var aliascmd = alias.shift();
+            if (alias.length > 0) {
+                argstr = alias.join(' ') + ' ' + argstr;
+            }
+            command = this.commands[aliascmd];
+        } else {
+            this.showInfo("Sorry, no command '" + commandname + "'. Maybe try to run &raquo; help", true);
+            return;
+        }
+
+        bespin.publish("bespin:cmdline:executed", { command: command, args: argstr });
+
+        command.execute(this, this.getArgs(argstr.split(' '), command));
+        this.commandLine.value = ''; // clear after the command
+    },
+      
+    addCommand: function(command) {
+        // -- Allow for the default [ ] takes style by expanding it to something bigger
+        if (command.takes && dojo.isArray(command.takes)) {
+            command = this.normalizeTakes(command); 
+        }  
+
+        // -- Add bindings
+        if (command.withKey) {             
+            var args = bespin.util.keys.fillArguments(command.withKey);
+
+            args.action = "bespin:cmdline:execute;name=" + command.name;
+            bespin.publish("bespin:editor:bindkey", args);
+        }   
+
+        this.commands[command.name] = command;
+
+        if (command['aliases']) {
+            dojo.forEach(command['aliases'], function(alias) {
+                this.aliases[alias] = command.name;
+            }, this);
+        }
+    },
+    
+    addCommands: function(commands) {   
+        dojo.forEach(commands, dojo.hitch(this, function(command) {
+            if (dojo.isString(command)) command = bespin.cmd.commands.get(command);
+            this.addCommand(command);
+        }));
+        
+    },
+    
+    hasCommand: function(commandname) {
+        if (this.commands[commandname]) { // yup, there she blows. shortcut
+            return true;
+        }
+
+        for (command in this.commands) { // try the aliases
+            if (this.commands[command]['aliases']) {
+                if (bespin.util.include(this.commands[command]['aliases'], commandname)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    },
+
+    showUsage: function(command, autohide) {
+        var usage = command.usage || "no usage information found for " + command.name;
+        this.showInfo("Usage: " + command.name + " " + usage, autohide);
+    },
+
+    showInfo: function(html, autohide) {
+        if (this.suppressInfo) return; // bypass
+
+        this.hideInfo();
+
+        dojo.byId('info').innerHTML = html;
+        dojo.style('info', 'display', 'block'); 
+        dojo.connect(dojo.byId('info'), "onclick", this, "hideInfo");
+
+        if (autohide) {
+            this.infoTimeout = setTimeout(dojo.hitch(this, function() {
+                this.hideInfo();
+            }), 4600);
+        }
+    },
+
+    hideInfo: function() {
+        dojo.style('info', 'display', 'none');
+        if (this.infoTimeout) clearTimeout(this.infoTimeout);
+    },
+
+    findCompletions: function(value) {
+        var matches = [];
+
+        if (value.length > 0) {
+            for (var command in this.commands) {
+                if (command.indexOf(value) == 0) {
+                  matches.push(command);
+                }
+            }
+            
+            for (var alias in this.aliases) {
+                if (alias.indexOf(value) == 0) {
+                  matches.push(alias);
+                }
+            }
+        }
+
+        return matches;
+    },
+
+    complete: function(value) {
+        var matches = this.findCompletions(value);
+        if (matches.length == 1) {
+            var commandLineValue = matches[0];
+            
+            var command = this.commands[matches[0]];
+
+            if (command) {
+                if (this.commandTakesArgs(command)) {
+                    commandLineValue += ' ';
+                }
+
+                if (command['completeText']) {
+                    this.showInfo(command['completeText']);
+                }
+
+                if (command['complete']) {
+                    this.showInfo(command.complete(this, value));
+                }
+            } else { // an alias
+                this.showInfo(commandLineValue + " is an alias for: " + this.aliases[commandLineValue]);
+                commandLineValue += ' ';
+            }
+            this.commandLine.value = commandLineValue;
+        }
+    },
+
+    commandTakesArgs: function(command) {
+        return command.takes != undefined;
+    },
+
+    // ** {{{ getArgs }}} **
+    //
+    // Calculate the args object to be passed into the command. 
+    // If it only takes one argument just send in that data, but if it wants more, split it all up for the command and send in an object.
+
+    getArgs: function(fromUser, command) {
+        if (!command.takes) return undefined;
+
+        var args;
+        var userString = fromUser.join(' ');
+
+        if (command.takes && command.takes.order.length < 2) { // One argument, so just return that
+            args = userString;
+        } else {           
+            args = new bespin.util.TokenObject(userString, { params: command.takes.order.join(' ') });
+            args.rawinput = userString;
+        }
+        return args;
+    },
+
+    normalizeTakes: function(command) {
+        // TODO: handle shorts that are the same! :)
+        var takes = command.takes;
+        command.takes = {
+            order: takes
+        };
+        
+        dojo.forEach(takes, function(item) {
+            command.takes[item] = {
+                "short": item[0]
+            };
+        });
+
+        return command;
+    },
+    
+    handleCommandLineFocus: function(e) {
+        if (this.inCommandLine) return true; // in the command line!
+
+        if (e.keyChar == 'j' && e.ctrlKey) { // send to command line
+            this.commandLine.focus();
+
+            dojo.stopEvent(e);
+            return true;
+        }
+    }
+
+});
+
+// ** {{{ bespin.cmd.commandline.KeyBindings }}} **
+//
+// Handle key bindings for the command line
+
+dojo.declare("bespin.cmd.commandline.KeyBindings", null, {
+    constructor: function(cl) {        
+        // -- Tie to the commandLine element itself     
+        dojo.connect(cl.commandLine, "onfocus", cl, function() {
+            bespin.publish("bespin:cmdline:focus");
+            
+            this.inCommandLine = true;
+            dojo.byId('promptimg').src = 'images/icn_command_on.png';
+        });
+        dojo.connect(cl.commandLine, "onblur", cl, function() {
+            this.inCommandLine = false;
+            dojo.byId('promptimg').src = 'images/icn_command.png';
+        });
+        
+        dojo.connect(cl.commandLine, "onkeyup", cl, function(e) {
+            var command;
+            if (e.keyChar >= "A".charCodeAt() && e.keyChar < "Z".charCodeAt()) { // only real letters
+                var completions = this.findCompletions(dojo.byId('command').value);
+                var commandString = completions[0];
+                if (completions.length > 0) {
+                    var isAutoComplete = bespin.get('settings').isSettingOn('autocomplete');
+                    if (isAutoComplete && completions.length == 1) { // if only one just set the value
+                        command = this.commands[commandString] || this.commands[this.aliases[commandString]];
+
+                        var spacing = (this.commandTakesArgs(command)) ? ' ' : '';
+                        dojo.byId('command').value = commandString + spacing;
+                
+                        if (command['completeText']) {
+                            this.showInfo(command['completeText']);
+                        } else {
+                            this.hideInfo();
+                        }
+                    } else if (completions.length == 1) {
+                        if (completions[0] != dojo.byId('command').value) {
+                            this.showInfo(completions.join(', '));
+                        } else {
+                            command = this.commands[completions[0]] || this.commands[this.aliases[completions[0]]];
+
+                            if (this.commandTakesArgs(command)) {
+                                this.complete(dojo.byId('command').value); // make it complete
+                            } else {
+                                this.hideInfo();
+                            }
+                        }
+                    } else {
+                        this.showInfo(completions.join(', '));
+                    }
+                }
+            }
+        });             
+        
+        dojo.connect(cl.commandLine, "onkeypress", cl, function(e) {
+            if (e.keyChar == 'j' && e.ctrlKey) { // send back
+                dojo.stopEvent(e);
+
+                dojo.byId('command').blur();
+
+                bespin.publish("bespin:cmdline:blur");
+
+                return false;
+            } else if ((e.keyChar == 'n' && e.ctrlKey) || e.keyCode == dojo.keys.DOWN_ARROW) {
+                this.commandLineHistory.setNext();
+                return false;
+            } else if ((e.keyChar == 'p' && e.ctrlKey) || e.keyCode == dojo.keys.UP_ARROW) {
+                this.commandLineHistory.setPrevious();
+                return false;
+            } else if (e.keyCode == dojo.keys.ENTER) {
+                this.executeCommand(dojo.byId('command').value);
+
+                return false;
+            } else if (e.keyCode == dojo.keys.TAB) { 
+                dojo.stopEvent(e);
+                
+                this.complete(dojo.byId('command').value);
+                return false;
+            } else if (e.keyCode == dojo.keys.ESCAPE) {
+                this.hideInfo();
+            }
+        });   
+    }
+});
+
+// ** {{{ bespin.cmd.commandline.History }}} **
+//
+// Store command line history so you can go back and forth
+
+dojo.declare("bespin.cmd.commandline.History", null, {
+    constructor: function(cl) {
+        this.commandLine = cl;
+        this.history = [];
+        this.pointer = 0;
+        this.store = new bespin.cmd.commandline.SimpleHistoryStore();
+        this.seed();
+    },
+
+    // TODO: get from the database
+    seed: function() {
+        this.history = this.store.seed();
+    },
+
+    add: function(command) {
+        command = dojo.trim(command);
+        if (this.last() != command) {
+            this.store.add(command);
+            this.history.push(command);
+            this.pointer = this.history.length - 1;
+        }
+    },
+
+    next: function() {
+        if (this.pointer < this.history.length) {
+            return this.history[this.pointer++];
+        }
+    },
+
+    previous: function() {
+        if (this.pointer > 0) {
+           return this.history[this.pointer--];
+        }
+    },
+
+    last: function() {
+        return this.history[this.history.length - 1];
+    },
+
+    first: function() {
+        return this.history[0];
+    },
+
+    set: function(command) {
+        this.commandLine.commandLine.value = command;
+    },
+
+    setNext: function() {
+        var next = this.next();
+        if (next) {
+            this.set(next);
+        }
+    },
+
+    setPrevious: function() {
+        var prev = this.previous();
+        if (prev) {
+            this.set(prev);
+        }
+    }
+});
+
+// ** {{{ bespin.cmd.commandline.SimpleHistoryStore }}} **
+//
+// A simple store that keeps the commands in memory.
+// In the future we would want to store the history cross session.
+
+dojo.declare("bespin.cmd.commandline.SimpleHistoryStore", null, {
+    constructor: function() {
+        this.commands = [];
+    },
+
+    seed: function() {
+        this.add('ls');
+        this.add('clear');
+        this.add('status');
+
+        return dojo.clone(this.commands); 
+    },
+
+    add: function(command) {
+        this.commands.push(command);
+    }
+});
+
+// ** {{{ bespin.cmd.commandline.Events }}} **
+//
+// The custom events that the commandline participates in
+
+dojo.declare("bespin.cmd.commandline.Events", null, {
+    constructor: function(commandline) {
+        // ** {{{ Event: bespin:cmdline:showinfo }}} **
+        // 
+        // Observe when others want to show the info bar for the command line
+        bespin.subscribe("bespin:cmdline:showinfo", function(event) {
+            var msg = event.msg;
+            var autohide = event.autohide; 
+            if (msg) commandline.showInfo(msg, autohide);
+        });
+
+        // ** {{{ Event: bespin:cmdline:suppressinfo }}} **
+        // 
+        // Turn on info bar suppression
+        bespin.subscribe("bespin:cmdline:suppressinfo", function(event) {
+            commandline.suppressInfo = true;
+        });
+
+        // ** {{{ Event: bespin:cmdline:unsuppressinfo }}} **
+        // 
+        // Turn off info bar suppression
+        bespin.subscribe("bespin:cmdline:unsuppressinfo", function(event) {
+            commandline.suppressInfo = false;
+        });
+
+        // ** {{{ Event: bespin:cmdline:executed }}} **
+        // 
+        // Once the command has been executed, do something.
+        // In this case, save it for the history
+        bespin.subscribe("bespin:cmdline:executed", function(event) {
+            commandline.commandLineHistory.add(event.command.name + " " + event.args); // only add to the history when a valid command
+        });
+        
+        // ** {{{ Event: bespin:cmdline:executed }}} **
+        // 
+        // Once the command has been executed, do something.        
+        bespin.subscribe("bespin:cmdline:execute", function(event) {
+            var command = event.name;
+            var args    = event.args;
+            if (command && args) { // if we have a command and some args
+                command += " " + args;
+            }
+
+            if (command) commandline.executeCommand(command);
+        });
+        
+        // -- Files
+        // ** {{{ Event: bespin:editor:openfile:openfail }}} **
+        // 
+        // If an open file action failed, tell the user.
+        bespin.subscribe("bespin:editor:openfile:openfail", function(event) {
+            commandline.showInfo('Could not open file: ' + event.filename + "<br/><br/><em>(maybe try &raquo; list)</em>");
+        });
+
+        // ** {{{ Event: bespin:editor:openfile:opensuccess }}} **
+        // 
+        // The open file action worked, so tell the user
+        bespin.subscribe("bespin:editor:openfile:opensuccess", function(event) {
+            commandline.showInfo('Loaded file: ' + event.file.name, true);
+        });
+
+        // -- Projects
+        // ** {{{ Event: bespin:project:set }}} **
+        // 
+        // When the project changes, alert the user
+        bespin.subscribe("bespin:project:set", function(event) {
+            var project = event.project;
+
+            bespin.get('editSession').project = project;
+            if (!event.suppressPopup) commandline.showInfo('Changed project to ' + project, true);
+        });
+
+    }
+});

Propchange: camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commandline.js
------------------------------------------------------------------------------
    svn:eol-style = native