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 [2/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/

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commands.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commands.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commands.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/commands.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,1049 @@
+/* ***** 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.cmd.commands");
+
+// = Commands =
+//
+// This array stores all of the default commands.
+
+// ** {{{bespin.cmd.commands.store}}} **
+//
+// The core store to hold commands that others share.
+bespin.cmd.commands.store = {};
+
+// ** {{{bespin.cmd.commands.add}}} **
+//
+// Add the command to the store which has a name -> command hash
+bespin.cmd.commands.add = function(command) {
+    bespin.cmd.commands.store[command.name] = command;
+};
+
+// ** {{{bespin.cmd.commands.get}}} **
+//
+// Return a command from the store
+bespin.cmd.commands.get = function(commandname) {
+    return bespin.cmd.commands.store[commandname];
+};
+
+// ** {{{Command: bespin.cmd.commands.toArgArray}}} **
+// Helper for when you have a command that needs to get a hold of it's params
+// as an array for processing
+bespin.cmd.commands.toArgArray = function(args) {
+    if (args == null) {
+        return [];
+    }
+    else {
+        var spliten = args.split(" ");
+        if (spliten.length == 1 && spliten[0] == "") {
+            return [];
+        }
+        else {
+            return spliten;
+        }
+    }
+};
+
+// == Start adding commands to the store ==
+//
+
+// ** {{{Command: help}}} **
+bespin.cmd.commands.add({
+    name: 'help',
+    takes: ['search'],
+    preview: 'show commands',
+    description: 'The <u>help</u> gives you access to the various commands in the Bespin system.<br/><br/>You can narrow the search of a command by adding an optional search params.<br/><br/>If you pass in the magic <em>hidden</em> parameter, you will find subtle hidden commands.<br/><br/>Finally, pass in the full name of a command and you can get the full description, which you just did to see this!',
+    completeText: 'optionally, narrow down the search',
+    execute: function(self, extra) {
+        var commands = [];
+        var command, name;
+
+        if (self.commands[extra]) { // caught a real command
+            commands.push("<u>Help for the command: <em>" + extra + "</em></u><br/>");
+            command = self.commands[extra];
+            commands.push(command['description'] ? command.description : command.preview);
+        } else {
+            var showHidden = false;
+            commands.push("<u>Commands Available</u><br/>");
+
+            if (extra) {
+                if (extra == "hidden") { // sneaky, sneaky.
+                    extra = "";
+                    showHidden = true;
+                }
+                commands.push("<em>(starting with</em> " + extra + " <em>)</em><br/>");
+            }
+
+            var tobesorted = [];
+            for (name in self.commands) {
+                tobesorted.push(name);
+            }
+
+            var sorted = tobesorted.sort();
+            
+            for (var i = 0; i < sorted.length; i++) {
+                name = sorted[i];
+                command = self.commands[name];
+
+                if (!showHidden && command.hidden) continue;
+                if (extra && name.indexOf(extra) != 0) continue;
+
+                var args = (command.takes) ? ' [' + command.takes.order.join('] [') + ']' : '';
+                commands.push('<b>' + name + args + '</b>: ' + command.preview);
+            }
+        }
+        self.showInfo("<div style='font-size: 0.80em'>" + commands.join("<br/>") + "</div>");
+    }
+}); 
+
+// ** {{{Command: set}}} **
+bespin.cmd.commands.add({
+        name: 'set',
+        takes: ['key', 'value'],
+        preview: 'define and show settings',
+        completeText: 'optionally, add a key and/or a value, else you will see all settings',
+        // complete: function(self, value) {
+        //     console.log(self);
+        //     console.log(value);
+        //     return value;
+        // },
+        execute: function(self, setting) {
+            var output;
+
+            if (!setting.key) { // -- show all
+                var settings = self.settings.list();
+
+                output = "<u>Your Settings</u><br/><br/>";
+                for (var x = 0; x < settings.length; x++) {
+                    if (settings[x].key[0] != '_') {
+                        output += settings[x].key + ": " + settings[x].value + "<br/>";
+                    }
+                }
+            } else {
+                var key = setting.key;
+                if (setting.value === undefined) { // show it
+                    var value = self.settings.get(key);
+                    if (value) {
+                        output = "<u>Your setting</u><br/><br/>";
+                        output += key + ": " + value;                        
+                    } else {
+                        output = "You do not have a setting for <em>" + key + "</em>";
+                    }
+                } else {
+                    output = "<u>Saving setting</u><br/><br/>";
+                    output += key + ": " + setting.value;
+                    self.settings.set(key, setting.value);
+                }
+            }
+            self.showInfo(output);
+        }
+});
+
+// ** {{{Command: files (ls, list)}}} **
+bespin.cmd.commands.add({
+    name: 'files',
+    aliases: ['ls', 'list'],
+    takes: ['project'],
+    preview: 'show files',
+    completeText: 'optionally, add the project name of your choice',
+    execute: function(self, project) {
+        if (!project) {
+            bespin.withComponent('editSession', function(editSession) {
+                project = editSession.project;
+            });
+        }
+
+        if (!project) {
+            self.showInfo("You need to pass in a project");
+            return;
+        }
+
+        self.files.fileNames(project, function(fileNames) {
+            var files = "<u>Files in project: " + project + "</u><br/><br/>";
+            for (var x = 0; x < fileNames.length; x++) {
+                files += fileNames[x].name + "<br/>";
+            }
+            self.showInfo(files);
+        });
+    }
+});
+
+// ** {{{Command: status}}} **
+bespin.cmd.commands.add({
+    name: 'status',
+    preview: 'get info on the current project and file',
+    execute: function(self) {
+        bespin.publish("bespin:session:status");
+    }
+});
+
+// ** {{{Command: project}}} **
+bespin.cmd.commands.add({
+    name: 'project',
+    takes: ['projectname'],
+    preview: 'show the current project, or set to a new one',
+    completeText: 'optionally, add the project name to change to that project',
+    execute: function(self, projectname) {
+        if (projectname) {
+            bespin.publish("bespin:project:set", { project: projectname });
+        } else {
+            self.executeCommand('status');
+        }
+    }
+});
+
+// ** {{{Command: projects}}} **
+bespin.cmd.commands.add({
+    name: 'projects',
+    preview: 'show projects',
+    execute: function(self, extra) {
+      self.files.projects(function(projectNames) {
+          var projects = "<u>Your projects</u><br/><br/>";
+          for (var x = 0; x < projectNames.length; x++) {
+            projects += projectNames[x].name + "<br/>";
+          }
+          self.showInfo(projects);
+      });
+    }
+});
+
+// ** {{{Command: createproject}}} **
+bespin.cmd.commands.add({
+    name: 'createproject',
+    takes: ['projectname'],
+    preview: 'create a new project',
+    usage: '[newprojectname]',
+    execute: function(self, projectname) {
+        if (!projectname) {
+            self.showUsage(this);
+            return;
+        }
+        bespin.publish("bespin:project:create", { project: projectname });
+    }
+});
+
+// ** {{{Command: createproject}}} **
+bespin.cmd.commands.add({
+    name: 'deleteproject',
+    takes: ['projectname'],
+    preview: 'delete a new project',
+    usage: '[newprojectname]',
+    execute: function(self, projectname) {
+        if (!projectname) {
+            self.showUsage(this);
+            return;
+        }
+        bespin.publish("bespin:project:delete", { project: projectname });
+    }
+});
+
+// ** {{{Command: renameproject}}} **
+bespin.cmd.commands.add({
+    name: 'renameproject',
+    takes: ['currentProject', 'newProject'],
+    preview: 'rename a project',
+    usage: '[currentProject], [newProject]',
+    execute: function(self, args) {
+        if (!args.currentProject || !args.newProject) {
+            self.showUsage(this);
+            return;
+        }
+        bespin.publish("bespin:project:rename", { currentProject: args.currentProject, newProject: args.newProject });
+    }
+});
+
+// ** {{{Command: mkdir}}} **
+bespin.cmd.commands.add({
+    name: 'mkdir',
+    takes: ['path', 'projectname'],
+    preview: 'create a new directory in the given project',
+    usage: '[path] [projectname]',
+    execute: function(self, args) {
+        if (!args.path) {
+            self.showUsage(this);
+            return;
+        }
+        
+        var opts = { path: args.path };
+        if (args.projectname) opts.project = args.projectname;
+        
+        bespin.publish("bespin:directory:create", opts);
+    }
+});
+
+// ** {{{Command: save}}} **
+bespin.cmd.commands.add({
+    name: 'save',
+    takes: ['filename'],
+    preview: 'save the current contents',
+    completeText: 'add the filename to save as, or use the current file',
+    withKey: "CMD S",
+    execute: function(self, filename) {
+        bespin.publish("bespin:editor:savefile", {
+            filename: filename
+        });
+    }
+});
+
+// ** {{{Command: load (open)}}} **
+bespin.cmd.commands.add({
+    name: 'load',
+    aliases: ['open'],
+    takes: ['filename'],
+    preview: 'load up the contents of the file',
+    completeText: 'add the filename to open',
+    execute: function(self, filename) {
+        bespin.publish("bespin:editor:openfile", {
+            filename: filename
+        });
+    }
+});
+
+// ** {{{Command: preview}}} **
+bespin.cmd.commands.add({
+    name: 'preview',
+    takes: ['filename'],
+    preview: 'view the file in a new browser window',
+    completeText: 'add the filename to view or use the current file',
+    execute: function(self, filename) {
+        bespin.publish("bespin:editor:preview", {
+            filename: filename
+        });
+    }
+});
+
+// ** {{{Command: editconfig}}} **
+bespin.cmd.commands.add({
+    name: 'editconfig',
+    aliases: ['config'],
+    preview: 'load up the config file',
+    execute: function(self) {
+        bespin.publish("bespin:editor:config:edit");
+    }
+});
+
+// ** {{{Command: runconfig}}} **
+bespin.cmd.commands.add({
+    name: 'runconfig',
+    preview: 'run your config file',
+    execute: function(self) {
+        bespin.publish("bespin:editor:config:run");
+    }
+});
+
+// ** {{{Command: cmdload}}} **
+bespin.cmd.commands.add({
+    name: 'cmdload',
+    takes: ['commandname'],
+    preview: 'load up a new command',
+    completeText: 'command name to load (required)',
+    usage: '[commandname]: Command name required.',
+    execute: function(self, commandname) {
+        if (!commandname) {
+            self.showUsage(this);
+            return;
+        }
+        bespin.publish("bespin:commands:load", { commandname: commandname });
+    }
+});
+
+// ** {{{Command: cmdedit}}} **
+bespin.cmd.commands.add({
+    name: 'cmdedit',
+    takes: ['commandname'],
+    aliases: ['cmdadd'],
+    preview: 'edit the given command (force if doesn\'t exist',
+    completeText: 'command name to edit (required)',
+    usage: '[commandname]: Command name required.',
+    execute: function(self, commandname) {
+        if (!commandname) {
+            self.showUsage(this);
+            return;
+        }
+        
+        bespin.publish("bespin:commands:edit", { commandname: commandname });
+    }
+});
+
+// ** {{{Command: cmdlist}}} **
+bespin.cmd.commands.add({
+    name: 'cmdlist',
+    preview: 'list my custom commands',
+    execute: function(self) {
+        bespin.publish("bespin:commands:list");
+    }
+});
+
+// ** {{{Command: cmdrm}}} **
+bespin.cmd.commands.add({
+    name: 'cmdrm',
+    takes: ['commandname'],
+    preview: 'delete a custom command',
+    completeText: 'command name to delete (required)',
+    usage: '[commandname]: Command name required.',
+    execute: function(self, commandname) {
+        if (!commandname) {
+            self.showUsage(this);
+            return;
+        }
+        
+        bespin.publish("bespin:commands:delete", { commandname: commandname });
+    }
+});
+
+// ** {{{Command: newfile}}} **
+bespin.cmd.commands.add({
+    name: 'newfile',
+    //aliases: ['new'],
+    takes: ['filename', 'project'],
+    preview: 'create a new buffer for file',
+    completeText: 'optionally, name the new filename first, and then the name of the project second',
+    withKey: "CTRL SHIFT N",
+    execute: function(self, args) {
+        if (args.filename) {
+            args.newfilename = args.filename;
+            delete args.filename;
+        }
+        bespin.publish("bespin:editor:newfile", args || {});
+    }
+});
+
+// ** {{{Command: rm (remove, del)}}} **
+bespin.cmd.commands.add({
+    name: 'rm',
+    aliases: ['remove', 'del'],
+    takes: ['filename'],
+    preview: 'remove the file',
+    completeText: 'add the filename to remove',
+    execute: function(self, filename) {
+        var project = bespin.get('editSession').project;
+        if (!project) {
+            self.showInfo("rm only works with the project is set.");
+            return;            
+        }
+
+        if (!filename) {
+            self.showInfo("give me a filename or directory to delete");
+            return;
+        }
+
+        self.files.removeFile(project, filename, function() {
+            if (bespin.get('editSession').checkSameFile(project, filename)) self.editor.model.clear(); // only clear if deleting the same file
+
+            self.showInfo('Removed file: ' + filename, true);
+        }, function(xhr) {
+            self.showInfo("Wasn't able to remove the file <b>" + filename + "</b><br/><em>Error</em> (probably doesn't exist): " + xhr.responseText);
+        });
+    }
+});
+
+// ** {{{Command: closefile}}} **
+bespin.cmd.commands.add({
+    name: 'closefile',
+    takes: ['filename', 'project'],
+    preview: 'close the file (may lose edits)',
+    completeText: 'add the filename to close (defaults to this file).<br>also, optional project name.',
+    execute: function(self, args) {
+        bespin.publish("bespin:editor:closefile", args);
+    }
+});
+
+// ** {{{Command: dashboard}}} **
+bespin.cmd.commands.add({
+    name: 'dashboard',
+    preview: 'navigate to the file',
+    execute: function(self) {
+        bespin.util.navigate.dashboard();
+    }
+});
+
+// ** {{{Command: version}}} **
+bespin.cmd.commands.add({
+    name: 'version',
+    takes: ['command'],
+    preview: 'show the version for Bespin or a command',
+    completeText: 'optionally, a command name',
+    execute: function(self, command) {
+        var bespinVersion = 'Your Bespin is at version ' + bespin.versionNumber + ', Code name: "' + bespin.versionCodename + '"';
+        var version;
+        if (command) {
+            var theCommand = self.commands[command];
+            if (!theCommand) {
+                version = "It appears that there is no command named '" + command + "', but " + bespinVersion;
+            } else {
+                version = (theCommand.version)
+                    ? "The command named '" + command + "' is at version " + theCommand.version 
+                    : "The command named '" + command + "' is a core command in Bespin version " + bespin.versionNumber;
+            }
+        }
+        else {
+            version = bespinVersion;
+        }
+        self.showInfo(version);
+    }
+});
+
+// ** {{{Command: clear}}} **
+bespin.cmd.commands.add({
+    name: 'clear',
+    aliases: ['cls'],
+    preview: 'clear the file',
+    execute: function(self) {
+        self.editor.model.clear();
+    }
+});
+
+// ** {{{Command: goto}}} **
+bespin.cmd.commands.add({
+    name: 'goto',
+    takes: ['linenumber'],
+    preview: 'move it! make the editor head to your line number.',
+    completeText: 'add the line number to move to',
+    execute: function(self, linenumber) {
+        if (linenumber) {
+            var linenumAsInt = parseInt(linenumber, 10) - 1; // parse the line number as a decimal
+            
+            self.editor.moveCursor({ row: linenumAsInt, col: 0 });
+            
+            // If the line that we are moving to is off screen, center it, else just move in place
+            if ( (linenumAsInt < self.editor.ui.firstVisibleRow) || (linenumAsInt >= self.editor.ui.firstVisibleRow+self.editor.ui.visibleRows) ) {
+                bespin.publish("bespin:editor:doaction", {
+                    action: 'moveCursorRowToCenter'
+                });
+            }
+        }
+    }
+});
+
+// ** {{{Command: replace}}} **
+bespin.cmd.commands.add({
+    name: 'replace',
+    takes: ['search', 'replace'],
+    preview: 's/foo/bar/g',
+    completeText: 'add the search regex, and then the replacement text',
+    execute: function(self, args) {
+        self.editor.model.replace(args.search, args.replace);
+    }
+});
+
+// ** {{{Command: login}}} **
+bespin.cmd.commands.add({
+    name: 'login',
+    // aliases: ['user'],
+    //            takes: ['username', 'password'],
+    hidden: true,
+    takes: {
+        order: ['username', 'password'],
+        username: {
+            "short": 'u'
+        },
+        password: {
+            "short": 'p',
+            optional: true
+        }
+    },
+    preview: 'login to the service',
+    completeText: 'pass in your username and password',
+    execute: function(self, args) {
+        if (!args) { // short circuit if no username
+            self.executeCommand("status");
+            return;
+        }
+        bespin.get('editSession').username = args.user; // TODO: normalize syncing
+        bespin.get('server').login(args.user, args.pass);
+    }
+});
+
+// ** {{{Command: logout}}} **
+bespin.cmd.commands.add({
+    name: 'logout',
+    preview: 'log out',
+    execute: function(self) {
+        delete bespin.get('editSession').username;
+        bespin.get('server').logout(function() {
+			window.location.href="/";
+		});
+    }
+});
+
+// ** {{{Command: bespin}}} **
+bespin.cmd.commands.add({
+    name: 'bespin',
+    preview: 'has',
+    hidden: true,
+    messages: [
+        "really wants you to trick it out in some way.",
+        "is your Web editor.",
+        "would love to be like Emacs on the Web.",
+        "is written on the Web platform, so you can tweak it."
+    ],
+    execute: function(self) {
+        self.showInfo("Bespin " + this.messages[Math.floor(Math.random() * this.messages.length)]);
+    }
+});
+
+// ** {{{Command: action}}} **
+bespin.cmd.commands.add({
+    name: 'action',
+    takes: ['actionname'],
+    preview: 'execute any editor action',
+    hidden: true,
+    execute: function(self, actionname) {
+        bespin.publish("bespin:editor:doaction", {
+            action: actionname
+        });
+    }
+});
+
+// ** {{{Command: sort}}} **
+bespin.cmd.commands.add({
+    name: 'sort',
+    takes: ['direction'],
+    preview: 'sort the current buffer',
+    completeText: 'optionally, sort descending',
+    execute: function(self, direction) {
+        var buffer = self.editor.model.getDocument().split(/\n/);
+        buffer.sort();
+        if (direction && /^desc/.test(direction.toLowerCase())) buffer.reverse(); 
+        self.editor.model.insertDocument(buffer.join("\n"));
+    }
+});
+
+// ** {{{Command: quota}}} **
+bespin.cmd.commands.add({
+    name: 'quota',
+    preview: 'show your quota info',
+    megabytes: function(bytes) {
+        return (bytes / 1024 / 1024).toFixed(2);
+    },
+    execute: function(self) {
+        var editSession = bespin.get('editSession');
+        self.showInfo("You have " + this.megabytes(editSession.quota - editSession.amountUsed) + " MB free space to put some great code!<br><br> <em style='font-size: smaller'>Used " + this.megabytes(editSession.amountUsed) + " MB out of your " + this.megabytes(editSession.quota) + " MB quota</em>");
+    }
+});
+
+// ** {{{Command: export}}} **
+bespin.cmd.commands.add({
+    name: 'export',
+    takes: ['project', 'archivetype'],
+    preview: 'export the given project with an archivetype of zip or tgz',
+    completeText: 'project name, archivetype (zip | tgz, defaults to zip)',
+    execute: function(self, args) {
+        var project = args.project || bespin.get('editSession').project;
+        
+        var type = args.archivetype;
+        if (!bespin.util.include(['zip','tgz','tar.gz'], type)) {
+            type = 'zip';
+        }
+
+        bespin.get('server').exportProject(project, type); // try to do it via the iframe
+    }
+});
+
+// ** {{{Command: import}}} **
+bespin.cmd.commands.add({
+    name: 'import',
+    takes: ['url', 'project'],
+    preview: 'import the given url as a project.<br>If a project name isn\'t given it will use the filename',
+    completeText: 'url (to an archive zip | tgz), optional project name',
+    usage: "[url of archive] [projectname]<br><br><em>(projectname optional. Will be taken from the URL if not provided)</em>",
+    // ** {{{calculateProjectName}}}
+    //
+    // Given a URL, work out the project name as a default
+    // For example, given http://foo.com/path/to/myproject.zip
+    // return "myproject"
+    calculateProjectName: function(url) {
+        var split = url.split('/');
+        var projectMaker = split[split.length - 1].split(".");
+        projectMaker.pop();
+        return projectMaker.join("_");
+    },
+    // ** {{{isURL}}}
+    //
+    // Test the given string to return if it is a URL.
+    // In this context it has to be http(s) only
+    isURL: function(url) {
+        return (url && (/^http(:|s:)/.test(url))); 
+    },
+    // ** {{{execute}}}
+    //
+    // Can be called in three ways:
+    //
+    // * import http://foo.com/path/to/archive.zip
+    // * import http://foo.com/path/to/archive.zip projectName
+    // * import projectName http://foo.com/path/to/archive.zip
+    execute: function(self, args) {
+        var project, url;
+
+        // Fail fast. Nothing given?
+        if (!args.url) {
+            self.showUsage(this);
+            return;
+        // * checking - import http://foo.com/path/to/archive.zip
+        } else if (!args.project && this.isURL(args.url)) {
+            args.project = this.calculateProjectName(args.url);
+        // * Oops, project and url are the wrong way around. That's fine
+        } else if (this.isURL(args.project)) {
+            project = args.project;
+            url = args.url;
+            args.project = url;
+            args.url = project;
+        // * Make sure that a URL came along at some point
+        } else if (!this.isURL(args.url)) {
+            self.showUsage(this);
+            return;            
+        }
+        
+        project = args.project;
+        url = args.url;
+
+        self.showInfo("About to import " + project + " from:<br><br>" + url + "<br><br><em>It can take awhile to download the project, so be patient!</em>");
+
+        bespin.publish("bespin:project:import", { project: project, url: url });
+    }
+});
+
+// ** {{{Command: trim}}} **
+bespin.cmd.commands.add({
+    name: 'trim',
+    takes: ['side'], // left, right, both
+    preview: 'trim trailing or leading whitespace',
+    completeText: 'optionally, give a side of left, right, or both (defaults to right)',
+    execute: function(self, side) {
+        self.editor.model.changeEachRow(function(row) {
+            if (!side) side = "right";
+            
+            if (bespin.util.include(["left", "both"], side)) {
+                while (row[0] == ' ') {
+                    row.shift();
+                }
+            }
+            
+            if (bespin.util.include(["right", "both"], side)) {
+                var i = row.length - 1;
+
+                while (row[i] == ' ') {
+                    delete row[i];
+                    i--;
+                }
+            }
+            return bespin.util.shrinkArray(row);
+        });
+    }
+});
+
+// ** {{{Command: bindkey}}} **
+bespin.cmd.commands.add({
+    name: 'bindkey',
+    takes: ['modifiers', 'key', 'action'],
+    preview: 'Bind a key to an action',
+    completeText: 'give modifier(s), key, and action name',
+    hidden: true,
+    execute: function(self, args) {
+        if (args.modifiers == "none") args.modifiers = '';
+
+        bespin.publish("bespin:editor:bindkey", args);
+    }
+});
+
+// ** {{{Command: insert}}} **
+bespin.cmd.commands.add({
+    name: 'insert',
+    takes: ['text'],
+    preview: 'insert the given text at this point.',
+    hidden: true,
+    execute: function(self, text) {
+        self.editor.model.insertChunk(self.editor.cursorPosition, text);
+    }
+});
+
+// ** {{{Command: typingtest}}} **
+bespin.cmd.commands.add({
+    name: 'typingtest',
+    preview: 'type in the alphabet a few times',
+    hidden: true,
+    execute: function(self) {
+        var start = Date.now();
+
+        for (var i = 0; i < 3; i++) {
+            dojo.forEach(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'], function(c) {
+                var args = { pos: bespin.editor.utils.copyPos(self.editor.cursorPosition) };
+                args.newchar = c;
+                self.editor.ui.actions.insertCharacter(args);            
+            });
+        }
+        
+        var stop = Date.now();
+        
+        self.showInfo("It took " + (stop - start) + " milliseconds to do this");        
+    }
+});
+
+// ** {{{Command: template}}} **
+bespin.cmd.commands.add({
+    name: 'template',
+    takes: ['type'],
+    preview: 'insert templates',
+    completeText: 'pass in the template name',
+    templates: { 'in': "for (var key in object) {\n\n}"},
+    execute: function(cmdline, type) {
+        cmdline.editor.model.insertChunk(cmdline.editor.cursorPosition, this.templates[type]);
+    }
+});
+
+// ** {{{Command: alias}}} **
+bespin.cmd.commands.add({
+    name: 'alias',
+    takes: ['alias', 'command'],
+    preview: 'define and show aliases for commands',
+    completeText: 'optionally, add your alias name, and then the command name',
+    execute: function(self, args) {
+      var output;
+      if (!args.alias) { // -- show all
+        output = "<u>Your Aliases</u><br/><br/>";
+        for (var x in self.aliases) {
+          output += x + ": " + self.aliases[x] + "<br/>";
+        }
+      } else {
+        if (args.command === undefined) { // show it
+          output = "<u>Your alias</u><br/><br/>";
+          var alias = self.aliases[args.alias];
+          if (alias) {
+              output += args.alias + ": " + self.aliases[args.alias];
+          } else {
+              output += "No alias set for " + args.alias;
+          }
+        } else { // save a new alias
+          var key = args.alias;
+          var value = args.command;
+          var aliascmd = value.split(' ')[0];
+          
+          output = "<u>Saving setting</u><br/><br/>";
+          if (self.commands[key]) {
+              output += "Sorry, there is already a command with the name: " + key;
+          } else if (self.commands[aliascmd]) {
+              output += key + ": " + value;
+              self.aliases[key] = value;
+          } else if (self.aliases[aliascmd]) { // TODO: have the symlink to the alias not the end point
+              output += key + ": " + self.aliases[value] + " (" + value + " was an alias itself)";
+              self.aliases[key] = value;
+          } else {
+              output += "Sorry, no command or alias with that name.";
+          }
+        }
+      }
+      self.showInfo(output);
+    }
+});
+
+// ** {{{Command: history}}} **
+bespin.cmd.commands.add({
+    name: 'history',
+    preview: 'show history of the commands',
+    execute: function(self) {
+        self.showInfo('<u>Command History</u><br/><br/>' + self.commandLineHistory.history.join('<br/>'));
+    }
+});
+
+// ** {{{Command: use}}} **
+bespin.cmd.commands.add({
+    name: 'use',
+    takes: ['type'],
+    preview: 'use patterns to bring in code',
+    completeText: '"sound" will add sound support',
+    libnames: {
+        'jquery': 'jquery.min.js'
+    },
+    execute: function(self, type) {
+        if (type == 'sound') {
+            self.editor.model.insertChunk({ row: 3, col: 0 },
+                '  <script type="text/javascript" src="soundmanager2.js"></script>\n');
+            self.editor.model.insertChunk({ row: 4, col: 0 },
+                "  <script>\n  var sound; \n  soundManager.onload = function() {\n    sound =  soundManager.createSound('mySound','/path/to/mysoundfile.mp3');\n  }\n  </script>\n");
+        } else if (type == 'js') {
+            var jslib = 'http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js';
+            var script = '<script type="text/javascript" src="' + jslib + '"></script>\n';
+            self.editor.model.insertChunk({ row: 3, col: 0 }, script);
+        }
+    }
+});
+
+// ** {{{Command: follow}}} **
+bespin.cmd.commands.add({
+    name: 'follow',
+    takes: ['username ...'],
+    preview: 'add to the list of users we are following, or (with no args) list the current set',
+    completeText: 'username(s) of person(s) to follow',
+    usage: "[username] ...<br><br><em>(username optional. Will list current followed users if not provided)</em>",
+    // ** {{{execute}}}
+    execute: function(self, args) {
+        var usernames = bespin.cmd.commands.toArgArray(args);
+
+        if (usernames.length == 0) {
+            bespin.publish("bespin:network:followers");
+        }
+        else {
+            bespin.publish("bespin:network:follow", [ usernames ]);
+        }
+    }
+});
+
+// ** {{{Command: unfollow}}} **
+bespin.cmd.commands.add({
+    name: 'unfollow',
+    takes: ['username ...'],
+    preview: 'remove from the list of users we are following',
+    completeText: 'username(s) of person(s) to stop following',
+    usage: "[username] ...<br><br><em>The username(s) to stop following</em>",
+    // ** {{{execute}}}
+    execute: function(self, args) {
+        var usernames = bespin.cmd.commands.toArgArray(args);
+
+        if (usernames.length == 0) {
+            self.showInfo('Please specify the users to cease following');
+        }
+        else {
+            bespin.publish("bespin:network:unfollow", [ usernames ]);
+        }
+    }
+});
+
+// ** {{{Command: group}}} **
+bespin.cmd.commands.add({
+    name: 'group',
+    preview: 'Collect the people you follow into groups, and display the existing groups',
+    // ** {{{execute}}}
+    execute: function(self, args) {
+        args = bespin.cmd.commands.toArgArray(args);
+
+        if (args.length == 0) {
+            bespin.publish("bespin:groups:list:all");
+        }
+        else if (args.length == 1) {
+            bespin.publish("bespin:groups:list", [ args[0] ]);
+        }
+        else if (args.length == 2) {
+            if (args[1] == "-r" || args[1] == "--remove") {
+                bespin.publish("bespin:groups:remove:all", [ args[0] ]);
+            }
+            else {
+                self.showInfo('Syntax error - You must specify what you want to do with your group.');
+            }
+        }
+        else if (args.length > 2) {
+            var group = args.shift();
+            var command = args.shift();
+            if (command == "-a" || command == "--add") {
+                bespin.publish("bespin:groups:add", [ group, args ]);
+            }
+            else if (command == "-r" || command == "--remove") {
+                args.shift();
+                bespin.publish("bespin:groups:remove", [ group, args ]);
+            }
+            else {
+                self.showInfo('Syntax error - To manipulate a group you must use add/remove');
+            }
+        }
+    }
+});
+
+// ** {{{Command: test}}} **
+bespin.cmd.commands.add({
+    name: 'test',
+    preview: 'Run some automated end to end tests',
+    script: [
+        { send:"echo Starting", expect:/^Starting$/ },
+        { send:"follow", expect:/sds/ },
+        { send:"echo Finished", expect:/^Finished$/ }
+    ],
+    // ** {{{_setup}}}
+    _setup: function(self, onComplete) {
+        this.originalShowInfo = self.showInfo;
+        var that = this;
+        bespin.get('server').request('POST', '/test/setup/', null, {
+            call:onComplete,
+            onFailure:function(xhr) {
+                that._cleanup(self, "_setup() failed. Maybe due to: " + xhr.responseText);
+            }
+        });
+    },
+    // ** {{{_cleanup}}}
+    _cleanup: function(self, reason) {
+        self.showInfo = this.originalShowInfo;
+        self.showInfo(reason);
+        bespin.get('server').request('POST', '/test/cleanup/', null, {
+            call:function() {
+                console.log("Server cleanup completed")
+            },
+            onFailure:function(xhr) {
+                self.showInfo("_setup() failed. Maybe due to: " + xhr.responseText);
+            }
+        });
+    },
+    // ** {{{_runNextElement}}}
+    _runNextElement: function(self, script, index) {
+        console.log("_runNextElement", index);
+        if (index >= script.length) {
+            this._cleanup(self, "Finished running tests");
+            return;
+        }
+        var element = script[index];
+        var that = this;
+        self.showInfo = function(html, autohide) {
+            var info = dojo.byId('info')
+            info.innerHTML = html;
+            var text = info.textContent;
+            if (element.expect.test(text)) {
+                that._runNextElement(self, script, index + 1);
+            }
+            else {
+                console.error("Test failure at index:", index);
+                console.log("Command: ", element.send);
+                console.log("Expected: ", element.expect.source);
+                console.log("Received:", text);
+                that._cleanup(self, "Test failure at index: " + index + "<br/>Command: '" + element.send + "'<br/>Expected: /" + element.expect.source + "/<br/>Received: '" + text + "'");
+            }
+        };
+        self.executeCommand(element.send);
+    },
+    // ** {{{execute}}}
+    execute: function(self) {
+        var that = this;
+        this._setup(self, function() {
+            that._runNextElement(self, that.script, 0);
+        });
+    }
+});
+
+// ** {{{Command: echo}}} **
+bespin.cmd.commands.add({
+    name: 'echo',
+    takes: ['message ...'],
+    preview: 'A test echo command',
+    // ** {{{execute}}}
+    execute: function(self, args) {
+        self.showInfo(args);
+    }
+});
+

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

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/dashboardcommands.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/dashboardcommands.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/dashboardcommands.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/dashboardcommands.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,36 @@
+/* ***** 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.cmd.dashboardcommands");
+
+// = Dashboard Commands =
+//
+// This array stores all of the dashboard commands.
+
+bespin.cmd.dashboardcommands.Commands = [
+    'help', 'files', 'set', 'project', 'projects', 'version', 'bespin',
+    'import', 'export', 'alias', 'history', 'mkdir', 'newfile',
+    'createproject', 'deleteproject', 'renameproject', 'status',
+    'follow', 'unfollow', 'group', 'test', 'echo'
+];
\ No newline at end of file

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

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/editorcommands.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/editorcommands.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/editorcommands.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/cmd/editorcommands.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,37 @@
+/* ***** 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.cmd.editorcommands");
+
+// = Editor Commands =
+//
+// This array stores all of the editor commands. 
+
+(function() {
+    var keys = []; 
+    for (var i in bespin.cmd.commands.store){
+        keys.push(i);
+    }  
+    bespin.cmd.editorcommands.Commands = keys;  
+})();
\ No newline at end of file

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

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/Component.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/Component.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/Component.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/Component.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,94 @@
+dojo.provide("bespin.editor.Component");
+
+dojo.require("bespin.bespin");
+
+dojo.require("bespin.util.canvas");
+dojo.require("bespin.util.keys");
+dojo.require("bespin.util.navigate");
+dojo.require("bespin.util.path");
+dojo.require("bespin.util.tokenobject");
+dojo.require("bespin.util.util");
+dojo.require("bespin.util.mousewheelevent");
+dojo.require("bespin.util.urlbar");
+
+dojo.require("bespin.client.filesystem");
+dojo.require("bespin.client.settings");
+dojo.require("bespin.client.status");
+dojo.require("bespin.client.server");
+dojo.require("bespin.client.session");
+
+dojo.require("bespin.editor.actions");
+dojo.require("bespin.editor.clipboard");
+dojo.require("bespin.editor.cursor");
+dojo.require("bespin.editor.editor");
+dojo.require("bespin.editor.events");
+dojo.require("bespin.editor.model");
+dojo.require("bespin.editor.toolbar");
+dojo.require("bespin.editor.themes");
+dojo.require("bespin.editor.undo");
+
+dojo.require("bespin.syntax.syntax");
+dojo.require("bespin.syntax.javascript");
+dojo.require("bespin.syntax.css");
+dojo.require("bespin.syntax.html");
+dojo.require("bespin.syntax.php");
+
+dojo.require("bespin.cmd.commandline");
+dojo.require("bespin.cmd.commands");
+dojo.require("bespin.cmd.editorcommands");
+
+dojo.require("th.helpers"); // -- Thunderhead... hooooo
+dojo.require("th.css");
+dojo.require("th.th");
+dojo.require("th.models");
+dojo.require("th.borders");
+dojo.require("th.components");
+
+dojo.declare("bespin.editor.Component", null, {
+    constructor: function(container, opts) {
+        opts.actsAsComponent = true;
+        
+        var initialcontent;
+        if (opts.loadfromdiv) {
+            initialcontent = dojo.byId(container).innerHTML
+        }
+
+        this.editor = bespin.register('editor', opts.editor || new bespin.editor.API(container, opts));
+        this.editSession = bespin.register('editSession', opts.editSession || new bespin.client.session.EditSession(this.editor));
+        this.server = bespin.register('server', opts.server || new bespin.client.Server());
+        this.files = bespin.register('files', opts.files || new bespin.client.FileSystem());
+        bespin.register('settings', opts.settings || new bespin.client.settings.Core(bespin.client.settings.InMemory));
+        
+        dojo.connect(window, 'resize', opts.resize || dojo.hitch(this, function() {
+            this.editor.paint();
+        }));
+
+        if (opts.loadfromdiv && initialcontent) {
+            this.setContent(initialcontent);
+        }
+
+        if (opts.syntax) { // -- turn on syntax highlighting
+            bespin.publish("bespin:settings:syntax", { language: opts.syntax });
+        }
+        
+        if (!opts.dontstealfocus) {
+            this.editor.canvas.focus();
+        }
+    },
+    
+    getContent: function() {
+        return this.editor.model.getDocument();
+    },
+
+    setContent: function(content) {
+        return this.editor.model.insertDocument(content);
+    },
+    
+    set: function(key, value) {
+        bespin.publish("bespin:settings:set", {
+           key: key,
+           value: value 
+        });
+    }
+});
+

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

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/actions.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/actions.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/actions.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/actions.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,639 @@
+/* ***** 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.editor.actions");  
+
+// = Actions =
+//
+// The editor can run various actions. They are defined here and you can add or change them dynamically. Cool huh?
+//
+// An action mutates the model or editor state in some way. The only way the editor state or model should be manipulated is via
+// the execution of actions.
+//
+// Actions integrate with the undo manager by including instructions for how to undo (and redo) the action. These instructions
+// take the form of a hash containing the necessary state for undo/redo. A key "action" corresponds to the function name of the
+// action that should be executed to undo or redo the operation and the remaining keys correspond to state necessary to perform
+// the action. See below for various examples.
+
+dojo.declare("bespin.editor.Actions", null, { 
+    constructor: function(editor) {
+        this.editor = editor;
+        this.ignoreRepaints = false;
+    },
+
+    // this is a generic helper method used by various cursor-moving methods
+    handleCursorSelection: function(args) {
+        if (args.event.shiftKey) {
+            if (!this.editor.selection) this.editor.setSelection({ startPos: bespin.editor.utils.copyPos(args.pos) });
+            this.editor.setSelection({ startPos: this.editor.selection.startPos, endPos: bespin.editor.utils.copyPos(this.editor.cursorManager.getScreenPosition())});
+        } else {
+            this.editor.setSelection(undefined);
+        }
+    },
+
+    moveCursor: function(moveType, args) {
+        var posData = this.editor.cursorManager[moveType]();
+        this.handleCursorSelection(args);
+        this.repaint();
+        args.pos = posData.newPos;
+        return args;
+    },
+
+    moveCursorLeft: function(args) {
+        return this.moveCursor("moveLeft", args);
+    },
+
+    moveCursorRight: function(args) {
+        return this.moveCursor("moveRight", args);
+    },
+
+    moveCursorUp: function(args) {
+        return this.moveCursor("moveUp", args);
+    },
+
+    moveCursorDown: function(args) {
+        return this.moveCursor("moveDown", args);
+    },
+
+    moveToLineStart: function(args) {
+        return this.moveCursor("moveToLineStart", args);
+    },
+
+    moveToLineEnd: function(args) {
+        return this.moveCursor("moveToLineEnd", args);
+    },
+
+    moveToFileTop: function(args) {
+        return this.moveCursor("moveToTop", args);
+    },
+
+    moveToFileBottom: function(args) {
+        return this.moveCursor("moveToBottom", args);
+    },
+
+    movePageUp: function(args) {
+        return this.moveCursor("movePageUp", args);
+    },
+
+    movePageDown: function(args) {
+        return this.moveCursor("movePageDown", args);
+    },
+
+    moveWordLeft: function(args) {
+        return this.moveCursor("smartMoveLeft", args);
+    },
+
+    moveWordRight: function(args) {
+        return this.moveCursor("smartMoveRight", args);
+    },
+
+    deleteWordLeft: function (args) {
+        this.deleteChunk({
+            endPos: args.pos,
+            pos: this.moveCursor("smartMoveLeft", args).pos
+        });
+        return args;
+    },
+
+    deleteWordRight: function (args) {
+        this.deleteChunk({
+            pos: args.pos,
+            endPos: this.moveCursor("smartMoveRight", args).pos
+        });
+        return args;
+    },
+
+    undo: function() {
+        this.editor.undoManager.undo();
+    },
+
+    redo: function() {
+        this.editor.undoManager.redo();
+    },
+
+    selectAll: function(args) {
+        // do nothing with an empty doc
+        if (this.editor.model.getMaxCols == 0) return;
+
+        args.startPos = { col: 0, row: 0 };
+        args.endPos = { col: this.editor.model.getRowLength(this.editor.model.getRowCount() - 1), row: this.editor.model.getRowCount() - 1 };
+
+        this.select(args);
+    },
+
+    select: function(args) {
+        if (args.startPos) {
+            this.editor.setSelection({ startPos: args.startPos, endPos: args.endPos });
+            this.editor.cursorManager.moveCursor(args.endPos);
+        } else {
+            this.editor.setSelection(undefined);
+        }
+    },
+
+    insertTab: function(args) {
+        var settings = bespin.get('settings');
+        
+        if (this.editor.getSelection() && !args.undoInsertTab) {
+            this.indent(args);
+            return;
+        }
+
+        var tabWidth = args.tabWidth;
+        var tab = args.tab;
+
+        if (!tab || !tabWidth) {
+            var realTabs = (settings.get('tabsize') == 'tabs');
+            if (realTabs) {
+                // do something tabby
+                tab = "\t";
+                tabWidth = 1;
+            } else {
+                var tabWidth = parseInt(settings.get('tabsize') || bespin.defaultTabSize);   // TODO: global needs fixing
+                var tabWidthCount = tabWidth;
+                var tab = "";
+                while (tabWidthCount-- > 0) {
+                    tab += " ";
+                }
+            }
+        }
+
+        this.editor.model.insertCharacters({row: args.modelPos.row, col: args.modelPos.col}, tab);
+        this.editor.cursorManager.moveCursor({row: args.modelPos.row, col: args.modelPos.col + tabWidth});
+
+        var linetext = this.editor.model.getRowArray(args.modelPos.row).join("");
+        // linetext = linetext.replace(/\t/g, "TAB");
+        // console.log(linetext);
+
+        this.repaint();
+        
+        // undo/redo
+        args.action = "insertTab";
+        var redoOperation = args;
+        var undoArgs = { action: "removeTab", queued: args.queued, modelPos: bespin.editor.utils.copyPos(args.modelPos),
+                         pos: bespin.editor.utils.copyPos(args.pos), tab: tab, tabWidth: tabWidth };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+    
+    // this function can only be called by editor.undoManager for undo insertTab in the case of beeing nothing selected
+    removeTab: function(args) {
+        var tabWidth = args.tabWidth;
+        
+        this.editor.model.deleteCharacters({row: args.pos.row, col: args.pos.col}, tabWidth);
+        this.editor.cursorManager.moveCursor({row: args.pos.row, col: args.pos.col});
+        
+        this.repaint();
+        
+        args.action = "removeTab";
+        var redoOperation = args;
+        var undoArgs = { action: "insertTab", undoInsertTab: true, queued: args.queued, pos: bespin.editor.utils.copyPos(args.pos),
+                         modelPos: bespin.editor.utils.copyPos(args.modelPos), tab: args.tab, tabWidth: args.tabWidth };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    // TODO: this is likely now broken
+    indent: function(args) {
+        var historyIndent = args.historyIndent || false;    
+        if (!historyIndent) {
+            var newHistoryIndent = [];
+        }
+        var selection = args.selection || this.editor.getSelection();
+        var fakeSelection = args.fakeSelection || false;
+        var startRow = selection.startPos.row;
+        var endRow = selection.endPos.row;
+        var tabWidth = parseInt(_settings.get('tabsize') || bespin.defaultTabSize);   // TODO: global needs fixing
+        var tabWidthCount = tabWidth;
+        var tab = "";
+        while (tabWidthCount-- > 0) {
+            tab += " ";
+        }
+
+        for (var y = startRow; y <= endRow; y++) {
+            if (!historyIndent) {
+                var row = this.editor.model.getRowArray(y).join("");
+                var match = /^(\s+).*/.exec(row);
+                var leadingWhitespaceLength = 0;
+                if (match && match.length == 2) {
+                    leadingWhitespaceLength = match[1].length;
+                }
+                var charsToInsert = (leadingWhitespaceLength % tabWidth ? tabWidth - (leadingWhitespaceLength % tabWidth) : tabWidth);
+                this.editor.model.insertCharacters({row: y, col: 0}, tab.substring(0, charsToInsert));
+                newHistoryIndent.push(charsToInsert);
+            } else {
+                this.editor.model.insertCharacters({row: y, col: 0}, tab.substring(0, historyIndent[y - startRow]));                
+            } 
+        }
+
+        if (!fakeSelection) {
+            selection.startPos.col += (historyIndent ? historyIndent[0] : tab.length);
+            selection.endPos.col += (historyIndent ? historyIndent[historyIndent.length-1] : tab.length);
+            this.editor.setSelection(selection);
+        }
+        args.pos.col += (historyIndent ? historyIndent[historyIndent.length-1] : tab.length);
+        this.editor.cursorManager.moveCursor({ col: args.pos.col });
+        historyIndent = historyIndent ? historyIndent : newHistoryIndent;
+        this.repaint();
+
+        // undo/redo
+        args.action = "indent";
+        args.selection = selection;
+        var redoOperation = args;
+        var undoArgs = { action: "unindent", queued: args.queued, selection: selection, fakeSelection: fakeSelection, historyIndent: historyIndent, pos: bespin.editor.utils.copyPos(args.pos) };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));        
+    },
+
+    unindent: function(args) {
+        var historyIndent = args.historyIndent || false;
+        if (!historyIndent) {
+            var newHistoryIndent = [];
+        }
+        var selection = args.selection || this.editor.getSelection();
+        var fakeSelection = args.fakeSelection || false;
+        if (!selection) {
+            fakeSelection = true;
+            selection = {startPos: {row: args.pos.row, col: args.pos.col}, endPos: {row: args.pos.row, col: args.pos.col}};
+        }
+        var startRow = selection.startPos.row;
+        var endRow = selection.endPos.row;
+        var tabWidth = parseInt(_settings.get('tabsize') || bespin.defaultTabSize);   // TODO: global needs fixing
+
+        for (var y = startRow; y <= endRow; y++) {
+            if (historyIndent) {
+                var charsToDelete = historyIndent[y - startRow];
+            } else {
+                var row = this.editor.model.getRowArray(y).join("");
+                var match = /^(\s+).*/.exec(row);
+                var leadingWhitespaceLength = 0;
+                if (match && match.length == 2) {
+                    leadingWhitespaceLength = match[1].length;
+                }
+                var charsToDelete = leadingWhitespaceLength >= tabWidth ? (leadingWhitespaceLength % tabWidth ? leadingWhitespaceLength % tabWidth : tabWidth) : leadingWhitespaceLength;               
+                newHistoryIndent.push(charsToDelete);
+            }
+
+            if (charsToDelete) {
+                this.editor.model.deleteCharacters({row: y, col: 0}, charsToDelete);
+            }
+            if (y == startRow) {
+                selection.startPos.col = Math.max(0, selection.startPos.col - charsToDelete);
+            }
+            if (y == endRow) {
+                selection.endPos.col = Math.max(0, selection.endPos.col - charsToDelete);
+            }
+            if (y == args.pos.row) {
+                args.pos.col = Math.max(0, args.pos.col - charsToDelete);
+            }
+        }
+        this.editor.cursorManager.moveCursor({ col: args.pos.col });
+
+        if (!fakeSelection) {
+            this.editor.setSelection(selection);
+        }
+        historyIndent = historyIndent ? historyIndent : newHistoryIndent;
+        this.repaint();
+        
+        // undo/redo
+        args.action = "unindent";
+        args.selection = selection;
+        var redoOperation = args;
+        var undoArgs = { action: "indent", queued: args.queued, selection: selection, fakeSelection: fakeSelection, historyIndent: historyIndent, pos: bespin.editor.utils.copyPos(args.pos) };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    // NOTE: Actually, clipboard.js is taking care of this unless EditorOnly mode is set
+    cutSelection: function(args) {
+        this.copySelection(args);
+        this.deleteSelection(args);
+    },
+    
+    // NOTE: Actually, clipboard.js is taking care of this unless EditorOnly mode is set
+    copySelection: function(args) {
+        var selectionObject = this.editor.getSelection();
+        if (selectionObject) {
+            var selectionText = this.editor.model.getChunk(selectionObject);
+            if (selectionText) {
+                bespin.editor.clipboard.Manual.copy(selectionText);
+            }
+        }
+    },
+
+    deleteSelectionAndInsertChunk: function(args) {
+        var oldqueued = args.queued;
+
+        args.queued = true;
+        var selection = this.editor.getSelection();
+        var chunk = this.deleteSelection(args);
+        args.pos = bespin.editor.utils.copyPos(this.editor.getCursorPos());
+        var endPos = this.insertChunk(args);
+
+        args.queued = oldqueued;
+
+        // undo/redo
+        args.action = "deleteSelectionAndInsertChunk";
+        args.selection = selection;
+        var redoOperation = args;
+        var undoArgs = {
+            action: "deleteChunkAndInsertChunkAndSelect",
+            pos: bespin.editor.utils.copyPos(args.pos),
+            endPos: endPos,
+            queued: args.queued,
+            chunk: chunk
+        };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    deleteChunkAndInsertChunkAndSelect: function(args) {
+        var oldqueued = args.queued;
+
+        args.queued = true;
+        this.deleteChunk(args);
+        this.insertChunkAndSelect(args);
+
+        args.queued = oldqueued;
+
+        // undo/redo
+        args.action = "deleteChunkAndInsertChunkAndSelect";
+        var redoOperation = args;
+        var undoArgs = {
+            action: "deleteSelectionAndInsertChunk",
+            pos: bespin.editor.utils.copyPos(args.pos),
+            queued: args.queued,
+            selection: args.selection
+        };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    // NOTE: Actually, clipboard.js is taking care of this unless EditorOnly mode is set
+    pasteFromClipboard: function(args) {
+        var clipboard = (args.clipboard) ? args.clipboard : bespin.editor.clipboard.Manual.data();
+        if (clipboard === undefined) return; // darn it clipboard!
+        args.chunk = clipboard;
+        this.insertChunk(args);
+    },
+
+    insertChunk: function(args) {
+        if (this.editor.selection) {
+            this.deleteSelectionAndInsertChunk(args);
+        } else {
+            var pos = this.editor.model.insertChunk(bespin.editor.utils.copyPos(this.editor.cursorManager.getScreenPosition()), args.chunk);
+            this.editor.cursorManager.moveCursor(pos);
+            this.repaint();
+
+            // undo/redo
+            args.action = "insertChunk";
+            var redoOperation = args;
+            var undoArgs = { action: "deleteChunk", pos: bespin.editor.utils.copyPos(args.pos), queued: args.queued, endPos: pos };
+            var undoOperation = undoArgs;
+            this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+
+            return pos;
+        }
+    },
+
+    deleteChunk: function(args) {
+        var chunk = this.editor.model.deleteChunk({ startPos: args.pos, endPos: args.endPos });
+        this.editor.cursorManager.moveCursor(args.pos);
+        this.repaint();
+
+        // undo/redo
+        args.action = "deleteChunk";
+        var redoOperation = args;
+        var undoArgs = { action: "insertChunk", pos: bespin.editor.utils.copyPos(args.pos), queued: args.queued, chunk: chunk };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    //deleteLine: function(args) {
+    //    this.editor.lines.splice(args.pos.row);
+    //    if (args.pos.row >= this.editor.lines.length) this.editor.cursorManager.moveCursor({ row: args.pos.row - 1, col: args.pos.col });
+    //    this.repaint();
+    //},
+
+    joinLine: function(args) {
+        if (args.joinDirection == "up") {
+            if (args.pos.row == 0) return;
+
+            var newcol = this.editor.ui.getRowScreenLength(args.pos.row - 1);
+            this.editor.model.joinRow(args.pos.row - 1);
+            this.editor.cursorManager.moveCursor({ row: args.pos.row - 1, col: newcol });
+        } else {
+            if (args.pos.row >= this.editor.model.getRowCount() - 1) return;
+
+            this.editor.model.joinRow(args.pos.row);
+        }
+
+        // undo/redo
+        args.action = "joinLine";
+        var redoOperation = args;
+        var undoArgs = { action: "newline", pos: bespin.editor.utils.copyPos(this.editor.getCursorPos()), queued: args.queued };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+
+        this.repaint();
+    },
+
+    killLine: function(args) {
+        // select the current row
+        this.editor.setSelection({ startPos: { row: args.pos.row, col: 0 }, endPos: { row: args.pos.row + 1, col: 0 } });
+        this.cutSelection(args); // cut (will save and redo will work)
+    },
+    
+    deleteSelection: function(args) {
+        if (!this.editor.selection) return;
+        var selection = this.editor.getSelection();
+        var startPos = bespin.editor.utils.copyPos(selection.startPos);
+        var chunk = this.editor.model.getChunk(selection);
+        this.editor.model.deleteChunk(selection);
+
+        // undo/redo
+        args.action = "deleteSelection";
+        var redoOperation = args;
+        var undoArgs = { action: "insertChunkAndSelect", pos: bespin.editor.utils.copyPos(startPos), queued: args.queued, chunk: chunk };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+
+        // setting the selection to undefined has to happen *after* we enqueue the undoOp otherwise replay breaks
+        this.editor.setSelection(undefined);
+        this.editor.cursorManager.moveCursor(startPos);
+        this.repaint();
+
+        return chunk;
+    },
+
+    insertChunkAndSelect: function(args) {
+        var endPos = this.editor.model.insertChunk(args.pos, args.chunk);
+
+        args.action = "insertChunkAndSelect";
+        var redoOperation = args;
+        var undoArgs = { action: "deleteSelection", pos: bespin.editor.utils.copyPos(endPos), queued: args.queued };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+
+        // setting the selection to undefined has to happen *after* we enqueue the undoOp otherwise replay breaks
+        this.editor.setSelection({ startPos: args.pos, endPos: endPos });
+        this.editor.cursorManager.moveCursor(endPos);
+        this.repaint();
+    },
+
+    backspace: function(args) {
+        if (this.editor.selection) {
+            this.deleteSelection(args);
+        } else {
+            if (args.pos.col > 0) {
+                this.editor.cursorManager.moveCursor({ col:  Math.max(0, args.pos.col - 1) });
+                args.pos.col -= 1;
+                this.deleteCharacter(args);
+            } else {
+                args.joinDirection = "up";
+                this.joinLine(args);
+            }
+        }
+    },
+
+    deleteKey: function(args) {
+        if (this.editor.selection) {
+            this.deleteSelection(args);
+        } else {
+            if (args.pos.col < this.editor.model.getRowLength(args.pos.row)) {
+                this.deleteCharacter(args);
+            } else {
+                args.joinDirection = "down";
+                this.joinLine(args);
+            }
+        }
+    },
+
+    deleteCharacter: function(args) {
+        if (args.pos.col < this.editor.ui.getRowScreenLength(args.pos.row)) {
+            var deleted = this.editor.model.deleteCharacters(args.pos, 1);
+            this.repaint();
+
+            // undo/redo
+            args.action = "deleteCharacter";
+            var redoOperation = args;
+            var undoArgs = { action: "insertCharacter", pos: bespin.editor.utils.copyPos(args.pos), queued: args.queued, newchar: deleted };
+            var undoOperation = undoArgs;
+            this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+        }
+    },
+
+    newline: function(args) {
+        var autoindentAmount = bespin.get('settings').get('autoindent') ? bespin.util.leadingSpaces(this.editor.model.getRowArray(args.pos.row)) : 0;
+        this.editor.model.splitRow(args.pos, autoindentAmount);
+        this.editor.cursorManager.moveCursor({ row: this.editor.cursorManager.getScreenPosition().row + 1, col: autoindentAmount });
+
+        // undo/redo
+        args.action = "newline";
+        var redoOperation = args;
+        var undoArgs = { action: "joinLine", joinDirection: "up", pos: bespin.editor.utils.copyPos(this.editor.cursorManager.getScreenPosition()), queued: args.queued };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+
+        this.repaint();
+    },
+
+    // it seems kinda silly, but when you have a region selected and you insert a character, I have a separate action that is invoked.
+    // this is because it's really two operations: deleting the selected region and then inserting a character. Each of these two
+    // actions adds an operation to the undo queue. So I have two choices for
+    deleteSelectionAndInsertCharacter: function(args) {
+        var oldqueued = args.queued;
+
+        args.queued = true;
+        var chunk = this.deleteSelection(args);
+        args.pos = bespin.editor.utils.copyPos(this.editor.getCursorPos());
+        this.insertCharacter(args);
+
+        args.queued = oldqueued;
+
+        // undo/redo
+        args.action = "deleteSelectionAndInsertCharacter";
+        var redoOperation = args;
+        var undoArgs = {
+            action: "deleteCharacterAndInsertChunkAndSelect",
+            pos: bespin.editor.utils.copyPos(args.pos),
+            queued: args.queued,
+            chunk: chunk
+        };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    deleteCharacterAndInsertChunkAndSelect: function(args) {
+        var oldqueued = args.queued;
+
+        args.queued = true;
+        this.deleteCharacter(args);
+        this.insertChunkAndSelect(args);
+
+        args.queued = oldqueued;
+
+        // undo/redo
+        args.action = "deleteCharacterAndInsertChunkAndSelect";
+        var redoOperation = args;
+        var undoArgs = { action: "deleteSelectionAndInsertCharacter", pos: bespin.editor.utils.copyPos(args.pos), queued: args.queued };
+        var undoOperation = undoArgs;
+        this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+    },
+
+    insertCharacter: function(args) {
+        if (this.editor.selection) {
+            this.deleteSelectionAndInsertCharacter(args);
+        } else {
+            this.editor.model.insertCharacters(args.pos, args.newchar);
+            this.editor.cursorManager.moveRight();
+            this.repaint();
+
+            // undo/redo
+            args.action = "insertCharacter";
+            var redoOperation = args;
+            var undoArgs = { action: "deleteCharacter", pos: bespin.editor.utils.copyPos(args.pos), queued: args.queued };
+            var undoOperation = undoArgs;
+            this.editor.undoManager.addUndoOperation(new bespin.editor.UndoItem(undoOperation, redoOperation));
+        }
+    },
+    
+    moveCursorRowToCenter: function(args) {
+        var saveCursorRow = this.editor.getCursorPos().row;
+        var halfRows = Math.floor(this.editor.ui.visibleRows / 2);
+        if (saveCursorRow > (this.editor.ui.firstVisibleRow + halfRows)) { // bottom half, so move down
+            this.editor.cursorManager.moveCursor({ row: this.editor.getCursorPos().row + halfRows });
+        } else { // top half, so move up
+            this.editor.cursorManager.moveCursor({ row: this.editor.getCursorPos().row - halfRows });
+        }
+        this.editor.ui.ensureCursorVisible();
+        this.editor.cursorManager.moveCursor({ row: saveCursorRow });
+    },
+
+    repaint: function() {
+        if (!this.ignoreRepaints) {
+            this.editor.ui.ensureCursorVisible();
+            this.editor.paint();
+        }
+    }
+});

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

Added: camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/clipboard.js
URL: http://svn.apache.org/viewvc/camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/clipboard.js?rev=755931&view=auto
==============================================================================
--- camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/clipboard.js (added)
+++ camel/trunk/components/camel-web/src/main/webapp/js/bespin/editor/clipboard.js Thu Mar 19 11:24:18 2009
@@ -0,0 +1,337 @@
+/* ***** 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.editor.clipboard");
+
+// = Clipboard =
+//
+// Handle clipboard operations. 
+// If using WebKit (I know, feature detection would be nicer, but e.clipboardData is deep) use DOMEvents
+// Else try the bad tricks.
+
+// ** {{{ bespin.editor.clipboard }}} **
+//
+// The factory that is used to install, and setup the adapter that does the work
+
+dojo.mixin(bespin.editor.clipboard, {
+    // ** {{{ install }}} **
+    //
+    // Given a clipboard adapter implementation, save it, an call install() on it
+    install: function(editor, newImpl) {
+        if (this.uses && typeof this.uses['uninstall'] == "function") this.uses.uninstall();
+        this.uses = newImpl;
+        this.uses.install(editor);
+    },
+
+    // ** {{{ setup }}} **
+    //
+    // Do the first setup. Right now checks for WebKit and inits a DOMEvents solution if that is true
+    // else install the default.
+    setup: function(editor) {
+        if (dojo.isWebKit) {
+            this.install(editor, new bespin.editor.clipboard.DOMEvents());
+        } else {
+            this.install(editor, new bespin.editor.clipboard.HiddenWorld());
+        }
+    }
+});
+
+// ** {{{ bespin.editor.clipboard.DOMEvents }}} **
+//
+// This adapter configures the DOMEvents that only WebKit seems to do well right now.
+// There is trickery involved here. The before event changes focus to the hidden
+// copynpaster text input, and then the real event does its thing and we focus back
+
+dojo.declare("bespin.editor.clipboard.DOMEvents", null, {
+    install: function(editor) {
+        
+        // * Configure the hidden copynpaster element
+        var copynpaster = dojo.create("input", {
+            type: 'text',
+            id: 'copynpaster',
+            style: "position: absolute; z-index: -400; top: -100px; left: -100px; width: 0; height: 0; border: none;"
+        }, dojo.body());
+        
+        // Copy
+        this.beforecopyHandle = dojo.connect(document, "beforecopy", function(e) {
+            e.preventDefault();
+            copynpaster.focus();
+        });
+
+        this.copyHandle = dojo.connect(document, "copy", function(e) {
+            var selectionText = editor.getSelectionAsText();
+            
+            if (selectionText && selectionText != '') {
+                e.preventDefault();
+                e.clipboardData.setData('text/plain', selectionText);
+            }
+            
+            dojo.byId('canvas').focus();
+        });
+
+        // Cut
+        this.beforecutHandle = dojo.connect(document, "beforecut", function(e) {
+            e.preventDefault();
+            copynpaster.focus();
+        });
+
+        this.cutHandle = dojo.connect(document, "cut", function(e) {
+            var selectionObject = editor.getSelection();
+
+            if (selectionObject) {
+                var selectionText = editor.model.getChunk(selectionObject);
+
+                if (selectionText && selectionText != '') {
+                    e.preventDefault();
+                    e.clipboardData.setData('text/plain', selectionText);
+                    editor.ui.actions.deleteSelection(selectionObject);
+                }
+            }
+
+            dojo.byId('canvas').focus();
+        });
+
+        // Paste
+        this.beforepasteHandle = dojo.connect(document, "beforepaste", function(e) {
+            e.preventDefault();
+            copynpaster.focus();
+        });
+
+        this.pasteHandle = dojo.connect(document, "paste", function(e) {
+            e.preventDefault();
+            var args = bespin.editor.utils.buildArgs();    
+            args.chunk = e.clipboardData.getData('text/plain');
+            if (args.chunk) editor.ui.actions.insertChunk(args);
+
+            dojo.byId('canvas').focus();
+            copynpaster.value = '';
+        });
+
+        dojo.connect(document, "dom:loaded", dojo.hitch(this, function() {
+            this.keydownHandle = dojo.connect(copynpaster, "keydown", function(e) {
+                e.stopPropagation();
+            });
+
+            this.keypressHandle = dojo.connect(copynpaster, "keypress", function(e) {
+                e.stopPropagation();
+            });
+        }));        
+    },
+    
+    uninstall: function() {
+        dojo.disconnect(this.keypressHandle);
+        dojo.disconnect(this.keydownHandle);
+        dojo.disconnect(this.beforepasteHandle);
+        dojo.disconnect(this.pasteHandle);
+        dojo.disconnect(this.beforecutHandle);
+        dojo.disconnect(this.cutHandle);
+        dojo.disconnect(this.beforecopyHandle);
+        dojo.disconnect(this.copyHandle);
+    }
+});
+
+// ** {{{ bespin.editor.clipboard.HiddenWorld }}} **
+//
+// Exclusively grab the C, X, and V key combos and use a hidden textarea to move data around
+
+dojo.declare("bespin.editor.clipboard.HiddenWorld", null, {
+    install: function(editor) {
+
+        // * Configure the hidden copynpaster element
+        var copynpaster = dojo.create("textarea", {
+            tabIndex: '-1',
+            autocomplete: 'off',
+            id: 'copynpaster',
+            style: "position: absolute; z-index: -400; top: -100px; left: -100px; width: 0; height: 0; border: none;"
+        }, dojo.body());
+        
+        var grabAndGo = function(text) {
+            copynpaster.value = text;
+            focusSelectAndGo();
+        };
+        
+        var focusSelectAndGo = function() {
+            copynpaster.focus();
+            copynpaster.select();
+            setTimeout(function() {
+                dojo.byId('canvas').focus();
+            }, 0);
+        };
+        
+        this.keyDown = dojo.connect(document, "keydown", function(e) {
+            if ((bespin.util.isMac() && e.metaKey) || e.ctrlKey) {
+                // Copy
+                if (e.keyCode == 67 /*c*/) {
+                    // place the selection into the textarea
+                    var selectionText = editor.getSelectionAsText();
+
+                    if (selectionText && selectionText != '') {
+                        grabAndGo(selectionText);
+                    }
+
+                // Cut
+                } else if (e.keyCode == 88 /*x*/) {
+                    // place the selection into the textarea
+                    var selectionObject = editor.getSelection();
+
+                    if (selectionObject) {
+                        var selectionText = editor.model.getChunk(selectionObject);
+
+                        if (selectionText && selectionText != '') {
+                            grabAndGo(selectionText);
+                            editor.ui.actions.deleteSelection(selectionObject);
+                        }
+                    }
+
+                // Paste
+                } else if (e.keyCode == 86 /*v*/) {
+                    if (e.target == dojo.byId("command")) return; // let the paste happen in the command
+
+                    focusSelectAndGo();
+
+                    setTimeout(function() { // wait just a TOUCH to make sure that it is selected
+                        var args = bespin.editor.utils.buildArgs();    
+                        args.chunk = copynpaster.value;
+                        if (args.chunk) editor.ui.actions.insertChunk(args);
+                    }, 1);
+                }
+            }
+        });
+    },
+    
+    uninstall: function() {
+        dojo.disconnect(this.keyDown);
+    }
+});
+
+// ** {{{ bespin.editor.clipboard.EditorOnly }}} **
+//
+// Turn on the key combinations to access the Bespin.Clipboard.Manual class which basically only works
+// with the editor only. Not in favour.
+
+dojo.declare("bespin.editor.clipboard.EditorOnly", null, {
+    install: function() {
+        var copyArgs = bespin.util.keys.fillArguments("CMD C");
+        copyArgs.action = "copySelection";
+        bespin.publish("bespin:editor:bindkey", copyArgs);
+
+        var pasteArgs = bespin.util.keys.fillArguments("CMD V");
+        pasteArgs.action = "pasteFromClipboard";
+        bespin.publish("bespin:editor:bindkey", pasteArgs);
+
+        var cutArgs = bespin.util.keys.fillArguments("CMD X");
+        cutArgs.action = "cutSelection";
+        bespin.publish("bespin:editor:bindkey", cutArgs);
+    }
+});
+
+// ** {{{ Bespin.Clipboard.Manual }}} **
+//
+// The ugly hack that tries to use XUL to get work done, but will probably fall through to in-app copy/paste only        
+bespin.editor.clipboard.Manual = function() {
+    var clipdata;
+    
+    return {
+        copy: function(copytext) {
+            try {
+                if (netscape.security.PrivilegeManager.enablePrivilege) {
+                    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                } else {
+                    clipdata = copytext;
+                    return;
+                }
+            } catch (ex) {
+                clipdata = copytext;
+                return;
+            }
+
+            var str = Components.classes["@mozilla.org/supports-string;1"].
+                                      createInstance(Components.interfaces.nsISupportsString);
+            str.data = copytext;
+
+            var trans = Components.classes["@mozilla.org/widget/transferable;1"].
+                                   createInstance(Components.interfaces.nsITransferable);
+            if (!trans) return false;
+
+            trans.addDataFlavor("text/unicode");
+            trans.setTransferData("text/unicode", str, copytext.length * 2);
+
+            var clipid = Components.interfaces.nsIClipboard;
+            var clip   = Components.classes["@mozilla.org/widget/clipboard;1"].getService(clipid);
+            if (!clip) return false;
+
+            clip.setData(trans, null, clipid.kGlobalClipboard);
+
+            /*
+            // Flash doesn't work anymore :(
+            if (inElement.createTextRange) {
+                var range = inElement.createTextRange();
+                if (range && BodyLoaded==1)
+                    range.execCommand('Copy');
+            } else {
+                var flashcopier = 'flashcopier';
+                if (!document.getElementById(flashcopier)) {
+                    var divholder = document.createElement('div');
+                    divholder.id = flashcopier;
+                    document.body.appendChild(divholder);
+                }
+                document.getElementById(flashcopier).innerHTML = '';
+
+                var divinfo = '<embed src="_clipboard.swf" FlashVars="clipboard='+escape(inElement.value)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>';
+                document.getElementById(flashcopier).innerHTML = divinfo;
+            }
+            */
+        },
+
+        data: function() {
+            try {
+                if (netscape.security.PrivilegeManager.enablePrivilege) {
+                    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                } else {
+                    return clipdata;
+                }
+            } catch (ex) {
+                return clipdata;
+            }
+
+            var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
+            if (!clip) return false;
+
+            var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
+            if (!trans) return false;
+            trans.addDataFlavor("text/unicode");
+
+            clip.getData(trans, clip.kGlobalClipboard);
+
+            var str       = {};
+            var strLength = {};
+            var pastetext = "";
+
+            trans.getTransferData("text/unicode", str, strLength);
+            if (str) str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
+            if (str) pastetext = str.data.substring(0, strLength.value / 2);
+            return pastetext;
+        }
+    };
+}();

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