diff --git a/js/dos/web/pathhandler.js b/js/dos/web/pathhandler.js
new file mode 100644
index 0000000..bf1148f
--- /dev/null
+++ b/js/dos/web/pathhandler.js
@@ -0,0 +1,165 @@
+/* ------------------------------------------------------------------------*
+ * Copyright 2013-2014 Arne F. Claassen
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------*/
+
+var Josh = Josh || {};
+(function(root, $, _) {
+ Josh.PathHandler = function(shell, config) {
+ config = config || {};
+ var _console = config.console || (Josh.Debug && root.console ? root.console : {
+ log: function() {
+ }
+ });
+ var _shell = shell;
+ _shell.templates.not_found = _.template("
<%=cmd%>: <%=path%>: No such file or directory
");
+ _shell.templates.ls = _.template("<% _.each(nodes, function(node) { %><%=node.name%> <% }); %>
");
+ _shell.templates.pwd = _.template("<%=node.path %>
");
+ _shell.templates.prompt = _.template("<%= node.path %> $");
+ var _original_default = _shell.getCommandHandler('_default');
+ var self = {
+ current: null,
+ pathCompletionHandler: pathCompletionHandler,
+ commandAndPathCompletionHandler: commandAndPathCompletionHandler,
+ getNode: function(path, callback) {
+ callback();
+ },
+ getChildNodes: function(node, callback) {
+ callback([]);
+ },
+ getPrompt: function() {
+ return _shell.templates.prompt({node: self.current});
+ }
+ };
+
+ _shell.setCommandHandler("ls", {
+ exec: ls,
+ completion: pathCompletionHandler
+ });
+ _shell.setCommandHandler("pwd", {
+ exec: pwd,
+ completion: pathCompletionHandler
+ });
+ _shell.setCommandHandler("cd", {
+ exec: cd,
+ completion: pathCompletionHandler
+ });
+ _shell.setCommandHandler("_default", {
+ exec: _original_default.exec,
+ completion: commandAndPathCompletionHandler
+ });
+ _shell.onNewPrompt(function(callback) {
+ callback(self.getPrompt());
+ });
+
+ function commandAndPathCompletionHandler(cmd, arg, line, callback) {
+ _console.log("calling command and path completion handler w/ cmd: '"+cmd+"', arg: '"+arg+"'");
+ if(!arg) {
+ arg = cmd;
+ }
+ if(arg[0] == '.' || arg[0] == '/') {
+ return pathCompletionHandler(cmd, arg, line, callback);
+ }
+ return _original_default.completion(cmd, arg, line, callback);
+ }
+
+ function pathCompletionHandler(cmd, arg, line, callback) {
+ _console.log("completing '" + arg + "'");
+ if(!arg) {
+ _console.log("completing on current");
+ return completeChildren(self.current, '', callback);
+ }
+ if(arg[arg.length - 1] == '/') {
+ _console.log("completing children w/o partial");
+ return self.getNode(arg, function(node) {
+ if(!node) {
+ _console.log("no node for path");
+ return callback();
+ }
+ return completeChildren(node, '', callback);
+ });
+ }
+ var partial = "";
+ var lastPathSeparator = arg.lastIndexOf("/");
+ var parent = arg.substr(0, lastPathSeparator + 1);
+ partial = arg.substr(lastPathSeparator + 1);
+ if(partial === '..' || partial === '.') {
+ return callback({
+ completion: '/',
+ suggestions: []
+ });
+ }
+ _console.log("completing children via parent '" + parent+"' w/ partial '"+partial+"'");
+ return self.getNode(parent, function(node) {
+ if(!node) {
+ _console.log("no node for parent path");
+ return callback();
+ }
+ return completeChildren(node, partial, function(completion) {
+ if(completion && completion.completion == '' && completion.suggestions.length == 1) {
+ return callback({
+ completion: '/',
+ suggestions: []
+ });
+ }
+ return callback(completion);
+ });
+ });
+ }
+
+ function completeChildren(node, partial, callback) {
+ self.getChildNodes(node, function(childNodes) {
+ callback(_shell.bestMatch(partial, _.map(childNodes, function(x) {
+ return x.name;
+ })));
+ });
+ }
+
+ function cd(cmd, args, callback) {
+ self.getNode(args[0], function(node) {
+ if(!node) {
+ return callback(_shell.templates.not_found({cmd: 'cd', path: args[0]}));
+ }
+ self.current = node;
+ return callback();
+ });
+ }
+
+ function pwd(cmd, args, callback) {
+ callback(_shell.templates.pwd({node: self.current}));
+ }
+
+ function ls(cmd, args, callback) {
+ _console.log('ls');
+ if(!args || !args[0]) {
+ return render_ls(self.current, self.current.path, callback);
+ }
+ return self.getNode(args[0], function(node) {
+ render_ls(node, args[0], callback);
+ });
+ }
+
+ function render_ls(node, path, callback) {
+ if(!node) {
+ return callback(_shell.templates.not_found({cmd: 'ls', path: path}));
+ }
+ return self.getChildNodes(node, function(children) {
+ _console.log("finish render: " + node.name);
+ callback(_shell.templates.ls({nodes: children}));
+ });
+ }
+
+ return self;
+ };
+})(this, $, _);
\ No newline at end of file