Sean Wilkinson avatar Sean Wilkinson committed aeeee13 Merge

Merge branch 'rainman+webchassis'

Comments (0)

Files changed (9)

couchdb-app/_attachments/developer.js

+//- JavaScript source code
+
+//- developer.js ~~
+//                                                      ~~ (c) SRW, 21 Oct 2011
+
+chassis(function (q, global) {
+    'use strict';
+
+    q.puts('--- Developer mode ---');
+
+    var x, y;
+
+    x = [1, 2, 3, 4, 5];
+
+    y = x.Q(function (x) {
+        return x.map(function (each) {
+            return 3 * each;
+        });
+    });
+
+    y.onready = function (val, exit) {
+        console.log(val);
+        exit.success(val);
+    };
+
+});
+
+//- vim:set syntax=javascript:

couchdb-app/_attachments/fs.js

+//- JavaScript source code
+
+//- fs.js ~~
+//
+//  This Web Chassis module provide a filesystem abstraction for Rainman that
+//  utilizes AJAX for transfer, JSON for serialization, and CouchDB for remote
+//  storage.
+//
+//                                                      ~~ (c) SRW, 19 Oct 2011
+
+chassis(function (q, global) {
+    'use strict';
+
+ // Module definition
+
+    q.fs = function () {
+
+     // Prerequisites
+
+        if (global.hasOwnProperty('RAINMAN') === false) {
+            q.load('rainman.js');
+            q.die('Awaiting RAINMAN ...');
+            return;
+        }
+
+     // Declarations
+
+        var deserialize, doc, isFunction, read, serialize;
+
+     // Definitions
+
+        deserialize = function (obj) {
+            return JSON.parse(obj, function revive(key, val) {
+                var isFxn;
+                if (val instanceof Object) {
+                    isFxn = val.hasOwnProperty('type')  &&
+                            val.hasOwnProperty('val')   &&
+                            val.type === 'true-function';
+                    if (isFxn === true) {
+                        return eval(val.val);
+                    }
+                }
+                return val;
+            });
+        };
+
+        doc = function (id) {
+            return 'http://' + global.location.host + '/db/' + id;
+        };
+
+        isFunction = function (f) {
+            return ((typeof f === 'function') && (f instanceof Function));
+        };
+
+        read = function (url, callback) {
+            var request = new XMLHttpRequest();
+            request.onreadystatechange = function () {
+                var error, response;
+                if (request.readyState === 4) {
+                    if (request.status === 200) {
+                        error = null;
+                        response = deserialize(request.responseText);
+                    } else {
+                        error = new Error(request.status);
+                        response = request.statusText;
+                    }
+                    callback(error, response);
+                }
+            };
+            request.open('GET', url, true);
+            request.send(null);
+        };
+
+        serialize = function (x) {
+            return JSON.stringify(x, function replacer(key, val) {
+                var left, obj, right;
+                obj = {};
+                if (isFunction(val)) {
+                    left = '(function () {\nreturn ';
+                    right = ';\n}());';
+                    obj.type = 'true-function';
+                    if (isFunction(val.toJSON)) {
+                        obj.val = left + val.toJSON() + right;
+                    } else if (isFunction(val.toSource)) {
+                        obj.val = left + val.toSource() + right;
+                    } else if (isFunction(val.toString)) {
+                        obj.val = left + val.toString() + right;
+                    } else {
+                        obj.val = left + val + right;
+                    }
+                    return obj;
+                } else {
+                    return val;
+                }
+            });
+        };
+
+     // Rainman initialization
+
+        global.RAINMAN.init({
+
+            read: function (key, exit) {
+                read(doc(key), function (err, res) {
+                    if (err === null) {
+                        exit.success(res.val);
+                    } else {
+                        exit.failure(err.message);
+                    }
+                });
+            },
+
+            remove: function (key, exit) {
+                read(doc(key), function (err, res) {
+                    var req;
+                    if (err === null) {
+                        req = new XMLHttpRequest();
+                        req.onreadystatechange = function () {
+                            if (req.readyState === 4) {
+                                if (req.status === 200) {
+                                    exit.success(undefined);
+                                } else {
+                                    exit.failure(req.statusText);
+                                }
+                            }
+                        };
+                        req.open('DELETE', doc(key), true);
+                        req.setRequestHeader('If-Match', res._rev);
+                        req.send(null);
+                    } else if (err.message === '404') {
+                        exit.success(undefined);
+                    } else {
+                        exit.failure(res);
+                    }
+                });
+            },
+
+            write: function (key, val, exit) {
+                read(doc(key), function (err, res) {
+                    var obj, req;
+                    if (err === null) {
+                        obj = res;
+                        obj.val = val;
+                    } else if (err.message === '404') {
+                        obj = {
+                            '_id':  key,
+                            'val':  val
+                        };
+                    } else {
+                        exit.failure(res);
+                        return;
+                    }
+                    req = new XMLHttpRequest();
+                    req.onreadystatechange = function () {
+                        if (req.readyState === 4) {
+                            if (req.status === 201) {
+                                exit.success(obj.val);
+                            } else {
+                                exit.failure(req.statusText);
+                            }
+                        }
+                    };
+                    req.open('PUT', doc(key), true);
+                    req.send(serialize(obj));
+                });
+            }
+
+        });
+
+     // Web Chassis "exports"
+
+        q.fs$read = function (id, callback) {
+            read(doc(id), callback);
+        };
+
+        q.fs$sync = function (obj) {
+         // Ideally, I'll be able to write "y.onready = sync;" eventually ...
+            return global.RAINMAN(obj);
+        };
+
+    };
+
+});
+
+//- vim:set syntax=javascript:

couchdb-app/_attachments/index.html

     <title>Quanah: Home Page</title>
     <link rel="stylesheet" href="style.css"/>
     <link rel="shortcut icon" href="favicon.ico"/>
+    <script src="web-chassis.js"></script>
   </head>
   <body>
     <noscript>Please enable JavaScript in your browser.</noscript>
     <ul>
       <li>
-        <a href="javascript:BUTTON.dev();">Developers</a>
-      </li>
-      <li>
-        <a href="javascript:BUTTON.vol();">Volunteers</a>
+        <a href="http://quanah.couchone.com:5984/_utils">Admin</a>
       </li>
       <li>
         <a href="http://goo.gl/tRxfM">Source code</a>
         <a href="_view/tasks">Queue</a>
       </li>
     </ul>
-    <script>
-        var BUTTON;
-        BUTTON = {
-            dev: function () {
-                alert('(coming soon)');
-            },
-            vol: function () {
-                alert('(coming soon)');
-            }
-        };
-    </script>
     <script src="quanah.js"></script>
+    <script src="fs.js"></script>
   </body>
 </html>

couchdb-app/_attachments/quanah.js

 
 //- quanah.js ~~
 //
-//  Browser features that are required:
-//  -   XMLHttpRequest object
-//  -   [Web] Worker object (although I'll be able to remove this part soon)
-//  -   getters and setters (ES5 recommended!)
+//  This program defines Quanah in terms of Web Chassis. It depends on global
+//  variables "chassis" and "RAINMAN" (the latter indirectly), but ultimately
+//  I plan to hand-roll an optimized, self-contained version some day :-)
 //
-//  TO-DO:
-//  -   argument parsing
-//  -   error message "bubbling" from remote machines
-//
-//                                                      ~~ (c) SRW, 11 Oct 2011
+//                                                      ~~ (c) SRW, 19 Oct 2011
 
-(function (global) {
+chassis(function (q, global) {
     'use strict';
 
- // Assertions
+ // Prerequisites
+
+    q.lib('fs');
 
     if (typeof Object.prototype.Q === 'function') {
      // Avoid unnecessary work if Method Q already exists.
 
  // Private declarations
 
-    var argv, bookmarks, countdown, define, isFunction, key2meta, read, token,
-        uuid, write;
+    var countdown, sync, token;
 
  // Private definitions
 
-    argv = {
-        developer:  true,
-        volunteer:  true
-    };
-
-    bookmarks = {
-        doc: function (id) {
-            return 'http://' + global.location.host + '/db/' + id;
-        },
-        queue: function (key) {
-            return 'http://' + global.location.host + '/_view/tasks?key="' +
-                key + '"';
-        },
-        uuids: function (n) {
-            return 'http://' + global.location.host + '/_uuids?count=' + n;
-        }
-    };
-
-    token = null;                       //- TODO: retrieve this using parseURI
-
     countdown = function (n, callback) {
-        var t = n;
+        var total = parseInt(Math.abs(n));
         return function () {
-            t -= 1;
-            if (t === 0) {
+            total -= 1;
+            if (total === 0) {
                 callback.apply(this, arguments);
             }
         };
     };
 
-    define = function (obj, name, params) {
-        if (typeof Object.defineProperty === 'function') {
-            define = function (obj, name, params) {
-                return Object.defineProperty(obj, name, params);
-            };
-        } else {
-            define = function (obj, name, params) {
-                var key;
-                for (key in params) {
-                    if (params.hasOwnProperty(key) === true) {
-                        switch (key) {
-                        case 'get':
-                            obj.__defineGetter__(name, params[key]);
-                            break;
-                        case 'set':
-                            obj.__defineSetter__(name, params[key]);
-                            break;
-                        case 'value':
-                            delete obj[name];
-                            obj[name] = params[key];
-                            break;
-                        default:
-                         // (placeholder)
-                        }
-                    }
-                }
-                return obj;
-            };
-        }
-        return define(obj, name, params);
-    };
-
-    isFunction = function (f) {
-        return ((typeof f === 'function') && (f instanceof Function));
-    };
+    sync = q.fs$sync;
 
-    key2meta = {};                      //- a cache for CouchDB metadata
+    token = Math.random();              //- TODO: load value from environment
 
-    read = function (url, callback) {
-        var request = new XMLHttpRequest();
-        request.onreadystatechange = function () {
-            var response;
-            if (request.readyState === 4) {
-                response = JSON.parse(request.responseText);
-                callback(request, response);
-            }
-        };
-        request.open('GET', url, true);
-        request.send(null);
-    };
-
-    uuid = function () {
-     // This function generates hexadecimal UUIDs of length 32.
-        var x = '';
-        while (x.length < 32) {
-            x += Math.random().toString(16).slice(2, (32 + 2 - x.length));
-        }
-        return x;
-    };
-
-    write = function (url, data, callback) {
-        var request = new XMLHttpRequest();
-        request.onreadystatechange = function () {
-            var response;
-            if (request.readyState === 4) {
-                response = JSON.parse(request.responseText);
-                callback(request, response);
-            }
-        };
-        request.open('PUT', url, true);
-        request.setRequestHeader('Content-type', 'application/json');
-        request.send(JSON.stringify(data));
-    };
-
- // Private constructors
+ // Global definitions
 
-    function QuanahVar(obj) {
-        obj = ((typeof obj === 'object') && (obj !== null)) ? obj : {};
-        var egress, ready, revive, stack, that;
-        egress = function () {
-            return {
-                failure: function (x) {
-                    that.status = 'failed';
-                    that.val = x;
-                    stack.splice(0, stack.length);
-                    ready = true;
-                 // I'm not sure if this works correctly yet ...
-                    global.setTimeout(that.sync, 1000);
-                },
-                success: function (x) {
-                    that.val = x;
-                    ready = true;
-                    revive();
-                }
-            };
-        }; 
-        ready = true;
-        revive = function () {
-            var f;
-            if (ready === true) {
-                ready = false;
-                f = stack.shift();
-                if (f === undefined) {
-                    ready = true;
-                } else {
-                    f.call(that, that.val, egress());
-                }
-            }
-        };
-        stack = [];
-        that = this;
-        define(that, 'onready', {
-            configurable: false,
-            enumerable: true,
-            get: function () {
-                return (stack.length > 0) ? stack[0] : null;
-            },
-            set: function (f) {
-                if (isFunction(f) === false) {
-                    throw new Error('"onready" method expects a function.');
+    Object.prototype.Q = function (func) {
+        var count, f, x, y, task;
+        if ((typeof func === 'function') && (func instanceof Function)) {
+            count = countdown(3, function () {
+                task.onready = function (val, exit) {
+                    val.f = f.key;
+                    val.x = x.key;
+                    val.y = y.key;
+                    val.status = 'waiting';
+                    val.token = token;
+                    exit.success(val);
+                };
+                sync(task);
+            });
+            f = sync({val: func});
+            x = sync((this instanceof f.constructor) ? this : {val: this});
+            y = sync({val: null});
+            task = sync({
+                val: {
+                    f:      null,
+                    x:      null,
+                    y:      null,
+                    status: 'initializing'
                 }
-                stack.push(f);
-                revive();
-            }
-        });
-     // Here, we add user-specified properties directly to the new object; we
-     // don't worry if either is missing, though, because the "sync" method
-     // handles that. NOTE: "sync" won't be defined till the very end!
-        if (obj.hasOwnProperty('key')) {
-            that.key = obj.key;
-        }
-        if (obj.hasOwnProperty('val')) {
-            that.val = obj.val;
-        }
-        return that.sync();
-    }
-
-    QuanahVar.prototype.sync = function () {
-        var that = this;
-        that.onready = function (x, exit) {
-            var count, flags, meta, pull, push, y;
-            count = countdown(2, function () {
-                exit.failure(x);
             });
-            flags = {
-                pull:   false,
-                push:   false
-            };
-            pull = function () {
-                read(meta.url, function (request, response) {
-                    if (request.status === 200) {
-                        meta.rev = response._rev;
-                        if (flags.push === true) {
-                            that.val = response.val;
-                            push();
-                        } else {
-                            exit.success(response.val);
-                        }
-                    } else {
-                        count();
-                    }
-                });
+            f.onready = x.onready = y.onready = function (val, exit) {
+                count();
+                exit.success(val);
             };
-            push = function () {
-                write(meta.url, y, function (request, response) {
-                    if (request.status === 201) {
-                        if (response.ok === true) {
-                            meta.rev = response.rev;
-                            exit.success(x);
+            y.onready = function (val, exit) {
+             // NOTE: See polling mechanism at http://goo.gl/TwYXA .
+                var check, timer;
+                check = function () {
+                    q.fs$read(task.key, function (err, res) {
+                        var temp;
+                        if (err === null) {
+                            if (res.val.status === 'done' ||
+                                res.val.status === 'failed') {
+                                global.clearInterval(timer);
+                                console.log('Cleared timer :-)');
+                                temp = sync({key: y.key});
+                                temp.onready = function (val, exit_temp) {
+                                    exit.success(val);
+                                    exit_temp.success(val);
+                                };
+                            }
                         } else {
-                            count();
+                            exit.failure(res);
                         }
-                    } else {
-                        count();
-                    }
-                });
-            };
-            if (that.hasOwnProperty('key')) {
-             // Instance may be initializing from key ==> pull.
-                flags.pull = true;
-                if (that.hasOwnProperty('val')) {
-                 // Instance needs to be synced ==> push _also_ :-)
-                    flags.push = true;
-                }
-            } else {
-             // Instance is initializing from value ==> push.
-                flags.push = true;
-                that.key = uuid();
-                that.val = (that.hasOwnProperty('val')) ? that.val : null;
-            }
-            if (key2meta.hasOwnProperty(that.key) === false) {
-                key2meta[that.key] = {
-                    id:     that.key,
-                    rev:    undefined,
-                    url:    bookmarks.doc(that.key)
+                    });
+                    console.log('Checking ...' + task.key, new Date());
+                    //exit.success(val);
                 };
-            }
-            meta = key2meta[that.key];
-            y = {
-                '_id':  meta.id,
-                '_rev': meta.rev,
-                'type': (typeof that.val),
-                'val':  (function (val) {
-                    var left, right;
-                    left = '(function () {\nreturn ';
-                    right = ';\n}());';
-                    if (isFunction(val)) {
-                        if (isFunction(val.toJSON)) {
-                            return left + val.toJSON() + right;
-                        } else if (isFunction(val.toSource)) {
-                            return left + val.toSource() + right;
-                        } else if (isFunction(val.toString)) {
-                            return left + val.toString() + right;
-                        } else {
-                            return left + val + right;
-                        }
-                    } else {
-                        return val;
-                    }
-                }(that.val))
+                console.log('Starting timer ...');
+                timer = global.setInterval(check, 1000);    //- 1 Hz polling
             };
-            if ((flags.pull === false) && (flags.push === true)) {
-                push();
-            } else {
-                pull();
-            }
-        };
-        return that;
-    };
-
- // Global definitions
-
-    Object.prototype.Q = function (func) {  //  y = x.Q(f);
-        var count, f, task, x, y;
-        count = countdown(3, function () {
-            task.onready = function (val, exit) {
-                val.f = f.key;
-                val.x = x.key;
-                val.y = y.key;
-                val.status = 'waiting';
-                exit.success(val);
-            };
-            task.sync();
-        });
-        f = new QuanahVar({val: func});
-        x = (this instanceof QuanahVar) ? this : new QuanahVar({val: this});
-        y = new QuanahVar();
-        task = new QuanahVar({val: {f: null, x: null, y: null, status: null}});
-        f.onready = x.onready = y.onready = function (val, exit) {
-            count();
-            exit.success(val);
-        };
-        y.onready = function (val, exit) {
-            var check, timer;
-            check = function () {
-                read(bookmarks.doc(task.key), function (request, response) {
-                    if (request.status === 200) {
-                        switch (response.val.status) {
-                        case 'done':
-                         // Exit with current value so the "sync" can occur.
-                            global.clearInterval(timer);
-                            read(bookmarks.doc(y.key), function (req, res) {
-                                if (req.status === 200) {
-                                    exit.success(res.val);
-                                } else {
-                                    exit.failure(res.val);
-                                }
-                            });
-                            break;
-                        case 'failed':
-                            global.clearInterval(timer);
-                            read(bookmarks.doc(y.key), function (req, res) {
-                                exit.failure(res.val);
-                            });
-                            break;
-                        default:
-                         // (placeholder)
-                        }
-                    }
-                });
-            };
-            timer = global.setInterval(check, 1000); //- check every 1000 ms
-        };
-        return y;
+            return y;
+        } else {
+            throw new Error('Method Q expects a function as its argument.');
+        }
     };
 
  // Invocations
 
-    if (argv.developer === true) {
-        (function developer() {
-            if (global.hasOwnProperty('console')) {
-                console.log('--- Developer Mode enabled ---');
-            }
-        }());
+    q.puts('Welcome to Quanah.');
+
+    if (q.argv.developer === true) {
+        q.load('developer.js');
     }
 
-    if (argv.volunteer === true) {
-        (function volunteer() {
-            var bee;
-            if (global.hasOwnProperty('console')) {
-                console.log('--- Volunteer Mode enabled ---');
-            }
-            if (global.hasOwnProperty('window')) {
-             // This part runs in a web browser.
-                bee = new Worker('quanah.js');
-                bee.onmessage = function (evt) {
-                    console.log(evt.data);
-                };
-                bee.onerror = function (evt) {
-                    console.error(evt);
-                };
-            } else {
-             // This part runs in a Web Worker.
-                read(bookmarks.queue('waiting'), function (request, response) {
-                    var f, n, obj, queue, task, x, y;
-                    if (request.status === 200) {
-                        queue = response;
-                        n = queue.rows.length;
-                        if (n === 0) {
-                            global.postMessage('Nothing to do ...');
-                        } else {
-                            obj = queue.rows[0];
-                            f = new QuanahVar({key: obj.value.f});
-                            x = new QuanahVar({key: obj.value.x});
-                            y = new QuanahVar({key: obj.value.y});
-                            task = new QuanahVar({key: obj.id});
-                            task.onready = function (val, exit) {
-                                val.status = 'running';
-                                exit.success(val);
-                            };
-                            task.sync();
-                            task.onready = function (tval, exit) {
-                                var count, func;
-                                count = countdown(3, function () {
-                                 // Yes, I know this is begging for a malicious
-                                 // code injection -- that's precisely why I've
-                                 // suggested using JSLINT as a preprocessor.
-                                    try {
-                                        y.val = (eval(f.val))(x.val);
-                                        task.val.status = 'done';
-                                    } catch (err) {
-                                        exit.failure(err);
-                                    } finally {
-                                        y.sync();
-                                        task.sync();
-                                        exit.success(tval);
-                                    }
-                                });
-                                func = function (val, exit) {
-                                    exit.success(val);
-                                    count();
-                                };
-                                f.onready = x.onready = y.onready = func;
-                            };
-                        }
-                    } else {
-                        throw new Error('Badness?');
-                    }
-                });
-            }
-        }());
+    if (q.argv.volunteer === true) {
+        q.load('volunteer.js');
     }
 
-}(function (outer) {
-    'use strict';
-    return (this === null) ? outer : this;
-}.call(null, this)));
+});
 
 //- vim:set syntax=javascript:

couchdb-app/_attachments/rainman.js

+//- JavaScript source code
+
+//- rainman.js ~~
+//                                                      ~~ (c) SRW, 17 Oct 2011
+
+(function (global) {
+    'use strict';
+
+ // Assertions
+
+    if (global.hasOwnProperty('RAINMAN')) {
+     // If RAINMAN is already present, avoid extra setup cost.
+        return;
+    }
+
+ // Declarations
+
+    var define, isFunction, read, remove, uuid, write;
+
+ // Definitions
+
+    define = function (obj, name, params) {
+        if (typeof Object.defineProperty === 'function') {
+            define = function (obj, name, params) {
+                return Object.defineProperty(obj, name, params);
+            };
+        } else {
+            define = function (obj, name, params) {
+                var key;
+                for (key in params) {
+                    if (params.hasOwnProperty(key) === true) {
+                        switch (key) {
+                        case 'get':
+                            obj.__defineGetter__(name, params[key]);
+                            break;
+                        case 'set':
+                            obj.__defineSetter__(name, params[key]);
+                            break;
+                        case 'value':
+                            delete obj[name];
+                            obj[name] = params[key];
+                            break;
+                        default:
+                         // (placeholder)
+                        }
+                    }
+                }
+                return obj;
+            };
+        }
+        return define(obj, name, params);
+    };
+
+    isFunction = function (f) {
+        return ((typeof f === 'function') && (f instanceof Function));
+    };
+
+    read = function (key) {
+        throw new Error('RAINMAN needs a definition for "read".');
+    };
+
+    remove = function (key) {
+        throw new Error('RAINMAN needs a definition for "remove".');
+    };
+
+    uuid = function () {
+     // This function generates hexadecimal UUIDs of length 32.
+        var x = '';
+        while (x.length < 32) {
+            x += Math.random().toString(16).slice(2, (32 + 2 - x.length));
+        }
+        return x;
+    };
+
+    write = function (key, val) {
+        throw new Error('RAINMAN needs a definition for "write".');
+    };
+
+ // Constructors
+
+    function AVar(obj) {
+        obj = (obj instanceof Object) ? obj : {};
+        var estack, rstack, ready, revive, that;
+        if (obj instanceof AVar) {
+            return obj;
+        } else {
+            estack = [];
+            rstack = [];
+            ready  = true;
+            revive = function (stack) {
+                var f;
+                if (ready === true) {
+                    ready = false;
+                    f = stack.shift();
+                    if (f === undefined) {
+                        ready = true;
+                    } else {
+                        try {
+                            f.call(that, that.val, {
+                                failure: function (x) {
+                                    that.val = x;
+                                    //ready = true;
+                                    revive(estack);
+                                },
+                                success: function (x) {
+                                    that.val = x;
+                                    ready = true;
+                                    revive(rstack);
+                                }
+                            });
+                        } catch (err) {
+                            that.val = err;
+                            ready = true;
+                            revive(estack);
+                        }
+                    }
+                }
+            };
+            that = this;
+            define(that, 'onerror', {
+                configurable: false,
+                enumerable: true,
+                get: function () {
+                    return (estack.length > 0) ? estack[0] : null;
+                },
+                set: function (f) {
+                    if (isFunction(f)) {
+                        estack.push(f);
+                        //revive(estack);
+                    } else {
+                        throw new Error('"onerror" expects a function.');
+                    }
+                }
+            });
+            define(that, 'onready', {
+                configurable: false,
+                enumerable: true,
+                get: function () {
+                    return (rstack.length > 0) ? rstack[0] : null;
+                },
+                set: function (f) {
+                    if (isFunction(f)) {
+                        rstack.push(f);
+                        revive(rstack);
+                    } else {
+                        throw new Error('"onready" expects a function.');
+                    }
+                }
+            });
+            that.key = (obj.hasOwnProperty('key')) ? obj.key : uuid();
+            that.val = (obj.hasOwnProperty('val')) ? obj.val : null;
+            return that;
+        }
+    }
+
+ // Global definitions
+
+    global.RAINMAN = function (x) {
+     // NOTE: This function requires initialization before use!
+        var y = new AVar(x);
+        switch ((x.hasOwnProperty('key') ? 2 : 0) +
+                (x.hasOwnProperty('val') ? 1 : 0)) {
+        case 1:
+         // Only 'val' was specified.
+            y.onready = function (val, exit) {
+                write(y.key, val, exit);
+            };
+            break;
+        case 2:
+         // Only 'key' was specified.
+            y.onready = function (val, exit) {
+                read(y.key, exit);
+            };
+            break;
+        case 3:
+         // Both 'key' and 'val' were specified.
+            y.onready = function (val, exit) {
+                if (val === undefined) {
+                    remove(y.key, exit);
+                } else {
+                    write(y.key, val, exit);
+                }
+            };
+            break;
+        default:
+         // Neither 'key' nor 'val' was specified -- assume a preallocation?
+            y.onready = function (val, exit) {
+                write(y.key, val, exit);
+            };
+        }
+        return y;
+    };
+
+    global.RAINMAN.init = function (obj) {
+        obj = (obj instanceof Object) ? obj : {};
+        read = (isFunction(obj.read)) ? obj.read : read;
+        remove = (isFunction(obj.remove)) ? obj.remove : remove;
+        write = (isFunction(obj.write)) ? obj.write : write;
+        delete global.RAINMAN.init;
+    };
+
+ // That's all, folks!
+
+    return;
+
+}(function (outer_scope) {
+    'use strict';
+ // This strict anonymous closure is taken from my Web Chassis project.
+    /*global global: true */
+    if (this === null) {
+        return (typeof global === 'object') ? global : outer_scope;
+    } else {
+        return (typeof this.global === 'object') ? this.global : this;
+    }
+}.call(null, this)));
+
+//- vim:set syntax=javascript:

couchdb-app/_attachments/volunteer.js

+//- JavaScript source code
+
+//- volunteer.js ~~
+//                                                      ~~ (c) SRW, 21 Oct 2011
+
+chassis(function (q, global) {
+    'use strict';
+
+ // Prerequisites
+
+    q.lib('fs');
+
+ // Declarations
+
+    var countdown, sync, task;
+
+ // Definitions (n/a)
+
+    countdown = function (n, callback) {
+        var total = parseInt(Math.abs(n));
+        return function () {
+            total -= 1;
+            if (total === 0) {
+                callback.apply(this, arguments);
+            }
+        };
+    };
+
+    sync = q.fs$sync;
+
+ // Invocations
+
+    q.fs$read('_design/quanah/_view/tasks?key="waiting"', function (err, res) {
+
+        if (err !== null) {
+         // This is sloppy but very helpful for debugging right now ...
+            console.error(err, res);
+        }
+
+        if (res.rows.length === 0) {
+            console.log('Nothing to do ...');
+            return;
+        }
+
+     // For now, take the first entry, run it, and upload its results.
+
+        task = sync({key: res.rows[0].id});
+
+        task.onready = function (val, exit) {
+            val.status = 'running';
+            exit.success(val);
+        };
+
+        sync(task);
+
+        task.onready = function (val_task, exit_task) {
+
+            var count, f, x, y;
+
+            count = countdown(3, function () {
+                y.onready = function (val_y, exit_y) {
+                    var results;
+                    try {
+                        results = f.val(x.val);
+                        val_task.status = 'done';
+                        exit_y.success(results);
+                    } catch (err) {
+                        val_task.status = 'failed';
+                        exit_y.success(err);
+                    }
+                };
+                sync(y);
+                y.onready = function (val_y, exit_y) {
+                    exit_task.success(val_task);
+                    console.log(val_y);
+                    exit_y.success(val_y);
+                };
+            });
+
+            f = sync({key: val_task.f});
+            x = sync({key: val_task.x});
+            y = sync({key: val_task.y});
+
+            f.onready = x.onready = y.onready = function (val, exit) {
+                count();
+                exit.success(val);
+            };
+
+        };
+
+        sync(task);
+
+    });
+
+});
+
+//- vim:set syntax=javascript:

couchdb-app/_attachments/web-chassis.js

+/** Web Chassis vQ.1 ********* BEGIN SHELL SCRIPT ********* >/dev/null 2>&1; #*\
+
+#-  web-chassis.js ~~
+#
+#   This is a custom version of Web Chassis (web-chassis.googlecode.com) that
+#   has been adapted for use with Quanah and Rainman (rainman.googlecode.com).
+#
+#                                                       ~~ (c) SRW, 19 Oct 2011
+
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+    emulate sh;
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+    set -o posix;
+fi
+
+alert() {
+  # This shell function outputs red text to stderr using ANSI C-strings.
+    printf '\033[1;31mERROR: %s\033[1;0m\n' "$*" >&2;
+}
+
+available() {
+  # This shell function accepts a variable number of program names and prints
+  # the absolute path to the first one available by watching for a successful
+  # exit code. Looping isn't ideal, but it behaves consistently across shells.
+    for each in $@; do
+        command -v ${each} >/dev/null 2>&1;
+        if [ $? -eq 0 ]; then
+            command -v ${each};
+            break;
+        fi
+    done
+    return $?;
+}
+
+usage() {
+  # This shell function prints a subset of the full documentation and exits.
+    printf '%s\n\n' "Usage: [SHELL] $0 [OPTION]... [FILE]...";
+    printf '%s\n\n' "Options:
+    -h, --help                          print this message and exit
+    -v, --version                       print version number and exit
+    -*, --*                             all other options pass directly to JS";
+    printf '%s %s.\n' 'Full documentation is available online at' \
+        'https://web-chassis.googlecode.com';
+}
+
+version() {
+  # This shell function prints the version number and exits. 
+    printf '%s\n' '0.2';
+}
+
+while getopts ":-:hv" option; do
+    case "${option}" in
+        h)
+            usage && exit 0;
+            ;;
+        v)
+            version && exit 0;
+            ;;
+        *)
+          # This is a hack so I can fake support for "long options" even
+          # though 'getopts' doesn't support them. It deliberately omits
+          # a catch for unknown flags; those will pass directly to JS :-)
+            [ ${OPTARG} = help ] && usage && exit 0;
+            [ ${OPTARG} = version ] && version && exit 0;
+            ;;
+    esac
+done
+
+ENVJS=${JS};
+JS=$(available ${JS} v8 js jsc d8 node rhino ringo narwhal);
+SHORTJS="${JS##*\/}";
+Q=$0;
+ARGV="$0 $*";
+
+if [ -n "${ENVJS}" ] && [ -n "${JS}" ]; then
+    if [ "${ENVJS}" != "${JS}" ] && [ "${ENVJS}" != "${SHORTJS}" ]; then
+        alert "Could not find '${ENVJS}'; using '${SHORTJS}' instead ...";
+    fi
+fi
+
+case ${SHORTJS:=:} in
+  # Explicit rules to enable features that should have been on by default ...
+    d8)                                 #-  Google V8 debugging shell
+        exec ${JS} --strict_mode ${Q} -- ${ARGV};
+        ;;
+    js)                                 #-  Mozilla SpiderMonkey 1.8 or later
+        exec ${JS} ${Q} ${ARGV};        #   For 1.8.5+, "-m -j -U" --> JIT+UTF8
+        ;;
+    jsc)                                #-  JavaScriptCore developer shell
+        exec ${JS} ${Q} -- ${ARGV};
+        ;;
+    node)                               #-  Node.js
+        exec ${JS} ${ARGV} --v8-options --strict_mode;
+        ;;
+    rhino)                              #-  Mozilla Rhino 1.7 release 3 2011
+        #exec java -Dfile.encoding=UTF-8 -jar <?> -encoding utf8 ${Q} ${ARGV};
+        exec ${JS} -encoding utf8 ${Q} ${ARGV};
+        ;;
+    v8)                                 #-  Google V8 sample shell
+        exec ${JS} --strict_mode -e "arguments = ('${ARGV}'.split(' '))" ${Q};
+        ;;
+  # Last resort rules
+    :)                                  #-  No [known] ${JS} available
+        alert 'No known JS engine found on ${PATH}.';
+        JS="`printf '%q%q' \
+            '/System/Library/Frameworks/JavaScriptCore.framework' \
+            '/Versions/Current/Resources/jsc'`";
+        if [ `available ${JS}` ]; then
+            alert "Using ${JS} ..." && exec ${JS} ${Q} -- ${ARGV};
+        fi
+        alert 'No JS engine found.' && exit 1;
+        ;;
+    *)                                  #-  No explicit rule for ${JS}
+        exec ${JS} ${ARGV};
+        ;;
+esac
+
+#****************************** END SHELL SCRIPT ******************************/
+/****************************** BEGIN JAVASCRIPT ******************************\
+ *                                                                            *
+ *  NOTE: Web Chassis is undergoing renovations -- pardon our mess ;-)        *
+ *                                                                            *
+\******************************************************************************/
+
+/*jslint indent: 4, maxlen: 80, nomen: true                                   */
+/*global chassis: true, global: true, module: true, require: false            */
+
+/******************************************************************************\
+ *                                                                            *
+ *  (Brief explanation will go here ...)                                      *
+ *                                                                            *
+\******************************************************************************/
+
+(function (global) {                    //- This strict anonymous closure can
+    "use strict";                       //  still access the global object :-)
+
+ // Constructors (these would get lifted to the top of the scope anyway ...)
+
+    function TryAgainLater(message) {
+        this.message = message || "Dying ...";
+    }
+
+    TryAgainLater.prototype = new Error();
+
+ // Private declarations
+
+    var q, revive, stack1, stack2;
+
+ // Private definitions
+
+    q = global.chassis = function chassis(f) {
+        if ((typeof f === 'function') && (f instanceof Function)) {
+            stack1.unshift(f);
+            revive();
+        } else {
+            throw new Error("Web Chassis expects functions as arguments.");
+        }
+    };
+
+    stack1 = [];
+    stack2 = [];
+
+    revive = function () {
+        var func;
+        while ((func = stack1.shift()) !== undefined) {
+            try {
+                func.call(this, q, global);
+            } catch (err) {
+                if (err instanceof TryAgainLater) {
+                    stack2.push(func);
+                    if (q.flags.debug) {
+                        q.puts(err.message);
+                    }
+                } else {
+                    q.puts(err);
+                }
+            }
+        }
+        Array.prototype.push.apply(stack1, stack2.splice(0, stack2.length));
+    };
+
+ // Because JS functions are also objects, we can use Chassis itself as an
+ // object in which we may store related properties, methods, and data :-)
+
+    q.detects = function (property) {
+     // This function detects properties available on the global object as a
+     // means to configure Web Chassis to the current environment. I wrote it
+     // as a lazy-loader to allow for memoization on a private variable instead
+     // of on the function itself -- I hit a really nasty bug once where the
+     // program needed to memoize an "arguments" property onto the function
+     // itself, and that fails silently in strict mode.
+        var cache = {
+         // This is a special local-scope-only value in CommonJS ...
+            module: (typeof module === 'object')
+        };
+        q.detects = function (property) {
+            if (cache.hasOwnProperty(property) === false) {
+                cache[property] = (global[property]) ? true : false;
+            }
+            return cache[property];
+        };
+        return q.detects(property);
+    };
+
+    q.die = function (message) {
+        throw new TryAgainLater(message);
+    };
+
+    q.flags = {};
+
+    q.include = function (libname) {    //- see also: http://goo.gl/2h4m
+        var loaded = {};
+        q.include = function (libname) {
+            var key, re;
+            if (loaded[libname] !== true) {
+                re = new RegExp("^" + libname + "\\$(.+)$");
+                for (key in q) {
+                    if (q.hasOwnProperty(key) && re.test(key)) {
+                        q[key.match(re)[1]] = q[key];
+                    }
+                }
+                loaded[libname] = true;
+            }
+        };
+        q.include(libname);
+    };
+
+    q.lib = function (libname) {
+        var defineProperty, loaded;
+        if (typeof Object.defineProperty === 'function') {
+            defineProperty = Object.defineProperty;
+        } else {
+            defineProperty = function (obj, key, params) {
+                var each;
+                for (each in params) {
+                    if (params.hasOwnProperty(each)) {
+                        switch (each) {
+                        case "get":
+                            obj.__defineGetter__(key, params[each]);
+                            break;
+                        case "set":
+                            obj.__defineSetter__(key, params[each]);
+                            break;
+                        case "value":
+                            delete obj[key];
+                            obj[key] = params[each];
+                            break;
+                        default:
+                         // (placeholder)
+                        }
+                    }
+                }
+                return obj;
+            };
+        }
+        loaded = {};
+        q.lib = function (libname) {
+            switch (loaded[libname]) {
+            case true:
+             // The module definition has already loaded ==> exit early :-)
+                break;
+            case false:
+             // A setter exists, but the module itself is still forthcoming.
+                q.die('Awaiting "' + libname + '" ...');
+                break;
+            default:
+             // This is the first time we've tried to lazy-load the module,
+             // which means we need to check if it exists or if we should wait.
+                loaded[libname] = false;
+                if (q.hasOwnProperty(libname)) {
+                 // If the module already exists, use Web Chassis to run it,
+                 // just in case it has its own internal dependencies. We bind
+                 // to 'q' to provide some support for a CommonJS-ish API.
+                    q(function (q) {
+                        q[libname].call(q, q);
+                        loaded[libname] = true;
+                        if (q.flags.debug) {
+                            q.puts('Loaded "' + libname + '" :-)');
+                        }
+                    });
+                } else {
+                 // The module doesn't exist yet, so we'll create a setter
+                 // function that will lazy-load it upon assignment so that
+                 // we'll have it available as soon as possible.
+                    defineProperty(q, libname, {
+                        configurable: true,
+                        set: function (f) {
+                         // Remove the setter and assign the module as the
+                         // user expects has already been done.
+                            delete q[libname];
+                            q[libname] = f;
+                         // We use the same simple mechanism as above because
+                         // recursing just to DRY out the code sometimes might
+                         // lead to a race condition in this particular case.
+                            q(function (q) {
+                                q[libname].call(q, q);
+                                loaded[libname] = true;
+                                if (q.flags.debug) {
+                                    q.puts('Loaded "' + libname + '" :-)');
+                                }
+                            });
+                        }
+                    });
+                    q.die('Created setter for "' + libname + '" ...');
+                }
+            }
+        };
+        q.lib(libname);
+    };
+
+    if (q.detects("location")) {
+        q.argv = (function () {
+         // This anonymous closure is based in part on parseUri 1.2.2 by
+         // Steven Levithan (stevenlevithan.com, MIT License). It treats the
+         // 'location.search' value as a set of ampersand-separated Boolean
+         // key=value parameters whose keys are valid JS identifiers and whose
+         // values are either "true" or "false" (without quotes). The function
+         // accepts an object whose own properties will be used to override
+         // flags that are already present.
+            var argv, i, key, m, opts, uri;
+            opts = {
+                key: [
+                    'source', 'protocol', 'authority', 'userInfo', 'user',
+                    'password', 'host', 'port', 'relative', 'path',
+                    'directory', 'file', 'query', 'anchor'
+                ],
+                parser: new RegExp('^(?:([^:\\/?#]+):)?(?:\\/\\/((?:(([^:@' +
+                    ']*)(?::([^:@]*))?)?@)?([^:\\/?#]*)(?::(\\d*))?))?((('  +
+                    '(?:[^?#\\/]*\\/)*)([^?#]*))(?:\\?([^#]*))?(?:#(.*))?)'),
+                q: {
+                    name:   'flags',
+                    parser: /(?:^|&)([^&=]*)=?([^&]*)/g
+                }
+            };
+            m = opts.parser.exec(global.location.href);
+            uri = {};
+            for (i = 14; i > 0; i -= 1) {
+                uri[opts.key[i]] = m[i] || '';
+            }
+            uri[opts.q.name] = {};
+            uri[opts.key[12]].replace(opts.q.parser, function ($0, $1, $2) {
+                if ($1) {
+                    uri[opts.q.name][$1] = ($2 !== 'false') ? true : false;
+                }
+            });
+         // First, let's compute the "command-line arguments" :-)
+            argv = {};
+            for (key in uri.flags) {
+                if (uri.flags.hasOwnProperty(key)) {
+                    argv[key] = uri.flags[key];
+                }
+            }
+            return argv;
+        }());
+        if (q.detects("window")) {
+         // Web Chassis is running inside a web browser -- hooray!
+            q.load = function (uri) {
+                var loaded = {};
+                q.load = function (uri) {
+                    if (loaded[uri] === true) {
+                     // It has already been loaded.
+                        return;
+                    } else if (loaded[uri] === false) {
+                     // It is being loaded.
+                        return;
+                    } else {
+                        loaded[uri] = false;
+                        var script = global.document.createElement("script");
+                        script.src = uri;
+                        script.onload = function () {
+                            loaded[uri] = true;
+                            revive();
+                        };
+                        global.document.body.appendChild(script);
+                    }
+                };
+                q.load(uri);
+            };
+            q.puts = function () {
+                var join = Array.prototype.join;
+                if (q.detects("console")) {
+                    q.puts = function () {
+                        global.console.log(join.call(arguments, " "));
+                    };
+                } else {
+                    q.puts = function () {
+                        var d, f, p;
+                        d = global.document;
+                        p = d.body.appendChild(d.createElement("pre"));
+                        p.innerHTML += join.call(arguments, " ");
+                        d = p = null;
+                    };
+                }
+                q.puts.apply(this, arguments);
+            };
+            global.window.onunload = function () {
+                delete global.chassis;
+            };
+        } else {
+         // Web Chassis is running inside a Web Worker -- hooray!
+            q.load = function (uri) {
+                var loaded = {};
+                q.load = function (uri) {
+                    if (loaded[uri] === true) {
+                     // It has already been loaded.
+                        return;
+                    } else if (loaded[uri] === false) {
+                     // It is being loaded. This is kind of overkill, because
+                     // Web Workers use a blocking script loader anyway ...
+                        return;
+                    } else {
+                        loaded[uri] = false;
+                        global.importScripts(uri);
+                        loaded[uri] = true;
+                        revive();
+                    }
+                };
+                q.load(uri);
+            };
+            q.puts = function () {
+                global.postMessage(Array.prototype.slice.call(arguments));
+            };
+        }
+    } else if (q.detects("load") && q.detects("print")) {
+     // You're running this in a developer shell, not a browser -- good luck!
+        if (q.detects("scriptArgs")) {
+            q.argv = Array.prototype.slice.call(global.scriptArgs, 1);
+        } else if (q.detects("arguments")) {
+            q.argv = Array.prototype.slice.call(global["arguments"], 1);
+        } else {
+            q.argv = [];
+        }
+        q.load = function (uri) {
+            var loaded = {};
+            q.load = function (uri) {
+                if (loaded[uri] === true) {
+                 // It has already been loaded.
+                    return;
+                } else if (loaded[uri] === false) {
+                 // It is being loaded. This is probably overkill, because the
+                 // developer shells all use blocking script loaders ...
+                    return;
+                } else {
+                    loaded[uri] = false;
+                    global.load(uri);
+                    loaded[uri] = true;
+                    revive();
+                }
+            };
+            q.load(uri);
+        };
+        q.puts = function () {
+            global.print(Array.prototype.join.call(arguments, " "));
+        };
+    } else if (q.detects("process")) {
+     // You're running this in Node.js, not a browser -- good luck!
+        global.fs = require("fs");
+        global.vm = require("vm");
+        q.argv = global.process.argv.slice(2);
+        q.load = function (uri) {
+            var loaded = {};
+            q.load = function (uri) {
+                if (loaded[uri] === true) {
+                 // It has already loaded.
+                    return;
+                } else if (loaded[uri] === false) {
+                 // It is being loaded. This is NOT overkill in Node.js, since
+                 // all kinds of crazy things can happen concurrently here.
+                    return;
+                } else {
+                    loaded[uri] = false;
+                    global.fs.readFile(uri, "utf8", function (err, data) {
+                        if (err) {
+                            throw err;
+                        }
+                        global.vm.createScript(data, uri).runInThisContext();
+                        loaded[uri] = true;
+                        revive();
+                    });
+                }
+            };
+            q.load(uri);
+        };
+        q.puts = function () {
+            global.console.log(Array.prototype.join.call(arguments, " "));
+        };
+    } else {
+     // Seriously, do contact me so I can add support for your platform, ok?
+        throw new Error("Platform detection failed -- please file a report!");
+    }
+
+ // The formatting conventions here for valid arguments are as follows:
+ // -   an argument looks like "[-][-]key[=value]"                      ;
+ // -   keys and values are made of typical filesystem characters like
+ //         (A-Za-z_./0-9)                                              ;
+ // -   keys without values map to "key=true"                           ;
+ // -   only values whose stringified numeric forms match their initial
+ //     string forms will be treated as numbers                         ; and
+ // -   repeated keys' values will be stored as an array.
+
+    (function () {
+        var f, flag, i, n;
+        f = function (x, pattern) {
+            x.replace(pattern, function (matches, key, val) {
+                switch (val) {
+                case "true":
+                    val = true;
+                    break;
+                case "false":
+                    val = false;
+                    break;
+                case "":
+                    val = true;
+                    break;
+                default:
+                    val = (isNaN(parseFloat(val))) ? val : parseFloat(val);
+                }
+                if (q.flags.hasOwnProperty(key)) {
+                    if (q.flags[key].hasOwnProperty("length")) {
+                        Array.prototype.push.call(q.flags[key], val);
+                    } else {
+                        q.flags[key] = [q.flags[key], val];
+                    }
+                } else {
+                    q.flags[key] = val;
+                }
+            });
+        };
+        flag = /^[\-]{0,2}([\w\.\-\/]+)[=]?([\w\.\-\/]*)$/;
+        n = q.argv.length;
+        for (i = 0; i < n; i += 1) {
+            f(q.argv[i], flag);
+        }
+    }());
+
+ // We may only load external scripts after processing _all_ input arguments!
+
+    (function () {
+        var key, re;
+        re = /\.js$/;
+        for (key in q.flags) {
+            if (q.flags.hasOwnProperty(key) && re.test(key)) {
+                q.load(key);
+            }
+        }
+    }());
+
+ // Finally, we'll add the ability to load Web Chassis as a CommonJS module.
+ // To load it interactively, use
+ //     > var chassis = require("./bin/web-chassis.js").init();
+
+    if (typeof module === 'object') {
+     // NOTE: This is deliberately written "module" instead of "global.module"
+     // because the 'module' object is often restricted to local scope.
+        module.exports.init = function () {
+            return q;
+        };
+    }
+
+}(function (outer_scope) {
+    "use strict";
+
+ // This strict anonymous closure encapsulates the logic for detecting which
+ // object in the environment should be treated as _the_ global object. It's
+ // not as easy as you might think -- strict mode disables the 'call' method's
+ // default behavior of replacing "null" with the global object. Luckily, we
+ // can work around that by passing a reference to the enclosing scope as an
+ // argument at the same time and testing to see if strict mode has done its
+ // deed. This task is not hard in the usual browser context because we know
+ // that the global object is 'window', but CommonJS implementations such as
+ // RingoJS confound the issue by modifying the scope chain, running scripts
+ // in sandboxed contexts, and using identifiers like "global" carelessly ...
+
+    if (this === null) {
+
+     // Strict mode has captured us, but we already passed a reference :-)
+
+        return (typeof global === 'object') ? global : outer_scope;
+
+    } else {
+
+     // Strict mode isn't supported in this environment, but we still need to
+     // make sure we don't get fooled by Rhino's 'global' function.
+
+        return (typeof this.global === 'object') ? this.global : this;
+
+    }
+
+}.call(null, this)));
+
+//- vim:set syntax=javascript:

couchdb-app/views/tasks/map.js

     var v, y;
     if (doc.hasOwnProperty('val') === true) {
         v = doc.val;
-        y = v.hasOwnProperty('f') && v.hasOwnProperty('x') &&
-            v.hasOwnProperty('y') && v.hasOwnProperty('status');
+        y = v.hasOwnProperty('f')       &&
+            v.hasOwnProperty('x')       &&
+            v.hasOwnProperty('y')       &&
+            v.hasOwnProperty('status')  &&
+            v.hasOwnProperty('token');
         if (y === true) {
-            if (v.hasOwnProperty('token')) {
-             // Here is preliminary token support, per Jonas's request.
-                emit([v.status, v.token], {f: v.f, x: v.x, y: v.y});
-            } else {
-                emit(v.status, {f: v.f, x: v.x, y: v.y});
-            }
+            emit(v.status, v.token);
         }
     }
 };
 COLOR       :=  "\#CCCCCC"
 IMGOPTS_REF  =  $(IMGOPTS) -extent '$(1)x$(1)' -bordercolor $(COLOR)        \
                 -border "1x1" -matte -fill none -fuzz "20%"                 \
-                -draw "matte 0,0 floodfill" -shave "1x1" -background $(COLOR)
+                -draw "matte 0,0 floodfill" -shave "1x1" -background "\#FFFFFF"
 
 .PHONY: all clean clobber reset run
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.