Commits

Sean Wilkinson committed 1aa9bda Merge

Merge branch 'master' into rainman+webchassis

Comments (0)

Files changed (29)

 #   is to script and monitor every step of my workflow except for version
 #   control -- always execute your commits manually!
 #
-#                                                       ~~ (c) SRW, 19 Oct 2011
+#                                                       ~~ (c) SRW, 31 Oct 2011
 
 #-  CouchApp configuration files -- these may contain passwords!
 .couchapprc
 .d8_history
 .v8_history
 
+#-  Mercurial repository itself (if present)
+.hg/
+
 #-  Vim swap files
 *.swp
 
 #
 #   For more information, see hgignore(5).
 #
-#                                                       ~~ (c) SRW, 22 Aug 2011
+#                                                       ~~ (c) SRW, 31 Oct 2011
 
 #-  Vim swap files
 #   -   files that end with '.swp'
 
 ^\.couchapprc$
 
+#-  Git repository itself (if present)
+
+.git/
+
 #-  Google V8 debugging shell ("d8") interactive history files
 #   -   files named exactly '.d8_history'
 
+^\.v8_history$
 ^\.d8_history$
 
 #-  vim:set syntax=python:

README

-Please see https://quanah.googlecode.com for more information.
+Quanah
+======
+
+Please see https://quanah.googlecode.com for more information.

chrome-app/Makefile

 SRC     :=  $(wildcard *.js)
 
 SC_SHOT :=  1280x800.jpg
-SM_TILE :=  480x240.png
+SM_TILE :=  440x280.png
 
 ZIPFILE :=  quanah.zip
 
 ###
 
 $(SC_SHOT):
-	@   $(PHANTOM) $(SRC) 1280x800 $@
+	@   $(PHANTOM) $(SRC) $(@:%.jpg=%) $@
 
 $(SM_TILE):
-	@   $(PHANTOM) $(SRC) 480x240 $@
+	@   $(PHANTOM) $(SRC) $(@:%.png=%) $@
 
 ###
 

chrome-app/quanah/128_icon.png

Old
Old image
New
New image

chrome-app/quanah/manifest.json

 {
     "name": "Quanah",
     "description": "The Web Computer as a hosted web app for Chrome",
-    "version": "3",
+    "version": "3.1.3",
     "app": {
         "urls": [
-            "http://quanah.couchone.com/"
+            "*://quanah.couchone.com/"
         ],
         "launch": {
-            "web_url": "http://quanah.couchone.com"
+            "web_url": "https://quanah.couchone.com"
         }
     },
     "icons": {
         "128": "128_icon.png"
     },
+    "offline_enabled": true,
     "permissions": [
         "unlimitedStorage",
         "notifications"

couchdb-app/Makefile

 #   not been met. The Python-based "couchapp" program is also required, but I
 #   may add directions at some point for using the Ruby-based "soca" instead.
 #
-#                                                       ~~ (c) SRW, 11 Sep 2011
+#                                                       ~~ (c) SRW, 31 Oct 2011
 
 include ../tools/macros.make
 
 APP         :=  quanah
 #URL         :=  http://localhost
 #DB          :=  localhost:5984/app
-URL         :=  http://quanah.couchone.com
-DB          :=  quanah.couchone.com:5984/app
+URL         :=  https://quanah.couchone.com
+DB          :=  quanah.ic.ht:5984/app
+#DB          :=  quanah.ic.ht:5984/app   #-  quanah.couchone.com:5984/app ... ?
 APPRC       :=  $(APP)/.couchapprc
 
 COUCHAPP    :=  $(call contingent, couchapp)
             $(CP) .couchappignore $(APP)/                               ;   \
             $(CP) filters $(APP)/                                       ;   \
             $(CP) rewrites.json $(APP)/                                 ;   \
+            echo "# $${RANDOM}" >> $(APP)/_attachments/cache.manifest   ;   \
             cd $(APP) && $(COUCHAPP) push -q                            ;   \
             if [ $$? -eq 0 ]; then                                          \
                 $(OPEN) $(URL)                                          ;   \

couchdb-app/_attachments/cache.manifest

+CACHE MANIFEST
+
+#-  cache.manifest ~~
+#                                                       ~~ (c) SRW, 02 Nov 2011
+
+developer.js
+favicon.ico
+fs.js
+gui.js
+icon-ipad.png
+icon-iphone.png
+icon-iphone4.png
+index.html
+quanah.js
+rainman.js
+startup-image.png
+style.css
+volunteer.js
+web-chassis.js
+
+NETWORK:
+*
+
+# EOF

couchdb-app/_attachments/debug.html

+<!DOCTYPE html>
+<!--
+    debug.html ~~
+
+    This page is for debugging (obviously), but specifically it contains lots
+    of useful links that would annoy users, adds a "debug" query parameter,
+    and foregoes the application cache to help shows _exactly_ what is wrong.
+
+                                                        ~~ (c) SRW, 02 Nov 2011
+-->
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <meta name="author" content="Sean Wilkinson"/>
+    <title>Quanah: Debug</title>
+    <link rel="stylesheet" href="./style.css"/>
+    <link rel="shortcut icon" href="./favicon.ico"/>
+  <!--
+    Apple stuff
+  -->
+    <meta name="apple-mobile-web-app-capable" content="yes"/>
+    <meta name="apple-mobile-web-app-status-bar-style"
+        content="black-translucent"/>
+    <link rel="apple-touch-icon" href="./icon-iphone.png"/>
+    <link rel="apple-touch-icon" sizes="72x72" href="./icon-ipad.png"/>
+    <link rel="apple-touch-icon" sizes="114x114" href="./icon-iphone4.png"/>
+    <link rel="apple-touch-startup-image" href="./startup-image.png"/>
+  <!--
+    JavaScript
+  -->
+    <script src="./web-chassis.js"></script>
+  </head>
+  <body>
+    <noscript>Please enable JavaScript in your browser.</noscript>
+    <ul>
+      <li>
+        <a href="./index.html?debug=true">Home</a>
+      </li>
+      <li>
+        <a href="http://quanah.ic.ht/_log">Log</a>
+      </li>
+      <li>
+        <a href="http://goo.gl/tRxfM">Source</a>
+      </li>
+      <li>
+        <a href="./db/_changes?filter=quanah/queue&limit=20">Queue</a>
+      </li>
+    </ul>
+    <script src="./quanah.js"></script>
+    <script src="./fs.js"></script>
+  </body>
+</html>

couchdb-app/_attachments/demo.js

-//- JavaScript source code
-
-//- demo.js ~~
-//
-//  I write this short demonstration in lieu of actual documentation :-P
-//
-//                                                      ~~ (c) SRW, 11 Oct 2011
-
-(function () {
-    'use strict';
-
-    var x, y, z;
-
-    x = [1, 2, 3, 4, 5];
-
-    y = x.Q(function (x) {
-        return x.map(function (each) {
-            return 3 * each;
-        });
-    });
-
-    y.onready = function (y, exit) {
-        console.log(y);                 //> [3, 6, 9, 12, 15]
-        exit.success(y);
-    };
-
-    z = y.Q(function (x) {
-        return x.reduce(function (a, b) {
-            return a + b;
-        });
-    });
-
-    z.onready = function (z, exit) {
-        console.log(z);                 //> 45
-        exit.success(z);
-    };
-
-}());
-
-(function () {
-    'use strict';
-
- // Here is the "same" program as above, implemented with a monadic style :-)
-
-    [1, 2, 3, 4, 5].
-        Q(function (x) {
-            return x.map(function (each) {
-                return 3 * each;
-            });
-        }).
-        Q(function (x) {
-            return x.reduce(function (a, b) {
-                return a + b;
-            });
-        }).
-        onready = function (x, exit) {
-            console.log(x);
-            exit.success(x);
-        };
-
-}());
-
-//- vim:set syntax=javascript:

couchdb-app/_attachments/developer.js

 //- JavaScript source code
 
 //- developer.js ~~
-//                                                      ~~ (c) SRW, 21 Oct 2011
+//                                                      ~~ (c) SRW, 26 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);
-    };
+ // This demonstrates a computation in the monadic style. Output should be 45.
+
+    [1, 2, 3, 4, 5].
+        Q(function (x) {
+            return x.map(function (each) {
+                return 3 * each;
+            });
+        }).
+        Q(function (x) {
+            return x.reduce(function (a, b) {
+                return a + b;
+            });
+        }).
+        onready = function (val, exit) {
+            q.puts(val);
+            exit.success(val);            
+        };
 
 });
 

couchdb-app/_attachments/fs.js

         };
 
         doc = function (id) {
-            return 'http://' + global.location.host + '/db/' + id;
+            return global.location.protocol + '//' +
+                global.location.host + '/db/' + id;
         };
 
         isFunction = function (f) {

couchdb-app/_attachments/googleaa6fa1323445baad.html

+google-site-verification: googleaa6fa1323445baad.html

couchdb-app/_attachments/gui.js

+//- JavaScript source code
+
+//- gui.js ~~
+//
+//  This file contains the web app's interactive elements. I may eventually
+//  move to jQuery, but for now this has been sufficient for Quanah's needs.
+//
+//                                                      ~~ (c) SRW, 02 Nov 2011
+
+//- (n/a)
+
+//- vim:set syntax=javascript:

couchdb-app/_attachments/icon-ipad.png

Added
New image

couchdb-app/_attachments/icon-iphone.png

Added
New image

couchdb-app/_attachments/icon-iphone4.png

Added
New image

couchdb-app/_attachments/index.html

 <!DOCTYPE html>
 <!--
     index.html ~~
-                                                        ~~ (c) SRW, 30 Sep 2011
+                                                        ~~ (c) SRW, 31 Oct 2011
 -->
-<html lang="en">
+<html lang="en" manifest="./cache.manifest">
   <head>
     <meta charset="utf-8"/>
     <meta name="author" content="Sean Wilkinson"/>
-    <title>Quanah: Home Page</title>
-    <link rel="stylesheet" href="style.css"/>
-    <link rel="shortcut icon" href="favicon.ico"/>
-    <script src="web-chassis.js"></script>
+    <title>Quanah</title>
+    <link rel="stylesheet" href="./style.css"/>
+    <link rel="shortcut icon" href="./favicon.ico"/>
+  <!--
+    Apple stuff
+  -->
+    <meta name="apple-mobile-web-app-capable" content="yes"/>
+    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
+    <link rel="apple-touch-icon" href="./icon-iphone.png"/>
+    <link rel="apple-touch-icon" sizes="72x72" href="./icon-ipad.png"/>
+    <link rel="apple-touch-icon" sizes="114x114" href="./icon-iphone4.png"/>
+    <link rel="apple-touch-startup-image" href="./startup-image.png"/>
+  <!--
+    Chrome Web Store Link
+  -->
+    <link rel="chrome-webstore-item" href="https://chrome.google.com/webstore/detail/ipbnldbdhfajobjlnoeemdjidnpifcgc">
+  <!--
+    JavaScript
+  -->
+    <script src="./web-chassis.js"></script>
   </head>
   <body>
     <noscript>Please enable JavaScript in your browser.</noscript>
     <ul>
       <li>
-        <a href="http://quanah.couchone.com:5984/_utils">Admin</a>
+        <a href="./?developer=true">Developer</a>
       </li>
       <li>
-        <a href="http://goo.gl/tRxfM">Source code</a>
+        <a href="./?volunteer=true">Volunteer</a>
       </li>
       <li>
-        <a href="db/_changes?filter=quanah/queue">Queue</a>
+        <a href="http://goo.gl/tRxfM">Source</a>
       </li>
     </ul>
-    <script src="quanah.js"></script>
-    <script src="fs.js"></script>
+    <script src="./quanah.js"></script>
+    <script src="./fs.js"></script>
   </body>
 </html>

couchdb-app/_attachments/quanah.js

 //  variables "chassis" and "RAINMAN" (the latter indirectly), but ultimately
 //  I plan to hand-roll an optimized, self-contained version some day :-)
 //
-//                                                      ~~ (c) SRW, 19 Oct 2011
+//                                                      ~~ (c) SRW, 26 Oct 2011
 
 chassis(function (q, global) {
     'use strict';
 
     sync = q.fs$sync;
 
-    token = 'sean';
-    //token = Math.random();            //- TODO: load value from environment
+    token = 'sean';                     //- TODO: load value from environment
 
  // Global definitions
 
                         var temp;
                         if (err === null) {
                             if (res.val.status === 'done' ||
-                                res.val.status === 'failed') {
+                                    res.val.status === 'failed') {
                                 global.clearInterval(timer);
-                                console.log('Cleared timer :-)');
+                                if (q.argv.debug === true) {
+                                    q.puts('Cleared timer :-)');
+                                }
                                 temp = sync({key: y.key});
                                 temp.onready = function (val, exit_temp) {
                                     exit.success(val);
                             exit.failure(res);
                         }
                     });
-                    console.log('Checking ...' + task.key, new Date());
-                    //exit.success(val);
+                    if (q.argv.debug === true) {
+                        q.puts('Checking ' + task.key + ' ...');
+                    }
                 };
-                console.log('Starting timer ...');
+                if (q.argv.debug === true) {
+                    q.puts('Starting timer ...');
+                }
                 timer = global.setInterval(check, 1000);    //- 1 Hz polling
             };
             return y;
 
  // Invocations
 
-    q.puts('Welcome to Quanah.');
-
     if (q.argv.developer === true) {
+        q.puts('Welcome to Quanah.');
         q.load('developer.js');
     }
 
     if (q.argv.volunteer === true) {
+        q.puts('Thanks for helping out!');
         q.load('volunteer.js');
     }
 

couchdb-app/_attachments/rainman.js

 //- JavaScript source code
 
 //- rainman.js ~~
-//                                                      ~~ (c) SRW, 17 Oct 2011
+//                                                      ~~ (c) SRW, 31 Oct 2011
 
 (function (global) {
     'use strict';
  // Definitions
 
     define = function (obj, name, params) {
-        if (typeof Object.defineProperty === 'function') {
+        if (isFunction(Object.defineProperty)) {
             define = function (obj, name, params) {
                 return Object.defineProperty(obj, name, params);
             };
             define = function (obj, name, params) {
                 var key;
                 for (key in params) {
-                    if (params.hasOwnProperty(key) === true) {
+                    if (params.hasOwnProperty(key)) {
                         switch (key) {
                         case 'get':
                             obj.__defineGetter__(name, params[key]);

couchdb-app/_attachments/startup-image.png

Added
New image

couchdb-app/_attachments/style.css

     color: black;
     font-family: Georgia, serif;
     font-size: 12pt;
-    margin-top: 0.5in;
+    margin-top: 20%;
     margin-left: 1in;
     margin-right: 1in;
     margin-bottom: 0.5in;
 }
 
 ul {
-    font-size: 200%;
+    font-size: 150%;
     list-style-type: none;
     margin: 0;
     padding: 0;

couchdb-app/_attachments/volunteer.js

 
  // Declarations
 
-    var countdown, sync, timer, volunteer, waiting;
+    var countdown, limit, sync, timer, volunteer, waiting;
 
  // Definitions (n/a)
 
         };
     };
 
+    limit = 20;
+
     sync = q.fs$sync;
 
     volunteer = function () {
         q.fs$read(waiting, function (err, res) {
-            var queue, task;
+            var queue, index, task;
             if (err !== null) {
              // This is sloppy but very helpful for debugging right now ...
-                console.error(err, res);
+                if (q.argv.debug === true) {
+                    console.error(err, res);
+                }
             }
-            queue = res.results;
+            queue = res.results || [];
             if (queue.length === 0) {
-                console.log('Nothing to do ...');
+                if (q.argv.debug === true) {
+                    q.puts('Nothing to do ...');
+                }
                 return;
             } else {
                 global.clearInterval(timer);                //- quit polling
                     sync(y);
                     y.onready = function (val_y, exit_y) {
                         exit_task.success(val_task);
-                        console.log(val_y);
+                        if (q.argv.debug === true) {
+                            q.puts(val_y);
+                        }
                         exit_y.success(val_y);
                     };
                 });
         });
     };
 
-    waiting = '_changes?filter=quanah/queue&status=waiting';
+    waiting = '_changes?filter=quanah/queue&status=waiting&limit=' + limit;
 
  // Invocations
 

couchdb-app/_attachments/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
+#                                                       ~~ (c) SRW, 31 Oct 2011
 
 if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
     emulate sh;
 \******************************************************************************/
 
 (function (global) {                    //- This strict anonymous closure can
-    "use strict";                       //  still access the global object :-)
+    '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 ...";
+        this.message = message || 'Dying ...';
     }
 
     TryAgainLater.prototype = new Error();
 
  // Private declarations
 
-    var q, revive, stack1, stack2;
+    var isFunction, q, revive, stack1, stack2;
 
  // Private definitions
 
+    isFunction = function (f) {
+        return ((typeof f === 'function') && (f instanceof Function));
+    };
+
     q = global.chassis = function chassis(f) {
-        if ((typeof f === 'function') && (f instanceof Function)) {
+        if (isFunction(f)) {
             stack1.unshift(f);
             revive();
         } else {
-            throw new Error("Web Chassis expects functions as arguments.");
+            throw new Error('Web Chassis expects functions as arguments.');
         }
     };
 
         q.include = function (libname) {
             var key, re;
             if (loaded[libname] !== true) {
-                re = new RegExp("^" + libname + "\\$(.+)$");
+                re = new RegExp('^' + libname + '\\$(.+)$');
                 for (key in q) {
                     if (q.hasOwnProperty(key) && re.test(key)) {
                         q[key.match(re)[1]] = q[key];
 
     q.lib = function (libname) {
         var defineProperty, loaded;
-        if (typeof Object.defineProperty === 'function') {
+        if (isFunction(Object.defineProperty)) {
             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]);
+            defineProperty = function (obj, name, params) {
+                var key;
+                for (key in params) {
+                    if (params.hasOwnProperty(key)) {
+                        switch (key) {
+                        case 'get':
+                            obj.__defineGetter__(name, params[key]);
                             break;
-                        case "set":
-                            obj.__defineSetter__(key, params[each]);
+                        case 'set':
+                            obj.__defineSetter__(name, params[key]);
                             break;
-                        case "value":
-                            delete obj[key];
-                            obj[key] = params[each];
+                        case 'value':
+                            delete obj[name];
+                            obj[name] = params[key];
                             break;
                         default:
                          // (placeholder)
         q.lib(libname);
     };
 
-    if (q.detects("location")) {
+    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
             }
             return argv;
         }());
-        if (q.detects("window")) {
+        if (q.detects('window')) {
          // Web Chassis is running inside a web browser -- hooray!
             q.load = function (uri) {
                 var loaded = {};
                         return;
                     } else {
                         loaded[uri] = false;
-                        var script = global.document.createElement("script");
+                        var script = global.document.createElement('script');
                         script.src = uri;
                         script.onload = function () {
                             loaded[uri] = true;
             };
             q.puts = function () {
                 var join = Array.prototype.join;
-                if (q.detects("console")) {
+                if (q.detects('console')) {
                     q.puts = function () {
-                        global.console.log(join.call(arguments, " "));
+                        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, " ");
+                        p = d.body.appendChild(d.createElement('pre'));
+                        p.innerHTML += join.call(arguments, ' ');
                         d = p = null;
                     };
                 }
                 global.postMessage(Array.prototype.slice.call(arguments));
             };
         }
-    } else if (q.detects("load") && q.detects("print")) {
+    } 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")) {
+        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 if (q.detects('arguments')) {
+            q.argv = Array.prototype.slice.call(global['arguments'], 1);
         } else {
             q.argv = [];
         }
             q.load(uri);
         };
         q.puts = function () {
-            global.print(Array.prototype.join.call(arguments, " "));
+            global.print(Array.prototype.join.call(arguments, ' '));
         };
-    } else if (q.detects("process")) {
+    } 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");
+        global.fs = require('fs');
+        global.vm = require('vm');
         q.argv = global.process.argv.slice(2);
         q.load = function (uri) {
             var loaded = {};
                     return;
                 } else {
                     loaded[uri] = false;
-                    global.fs.readFile(uri, "utf8", function (err, data) {
+                    global.fs.readFile(uri, 'utf8', function (err, data) {
                         if (err) {
                             throw err;
                         }
             q.load(uri);
         };
         q.puts = function () {
-            global.console.log(Array.prototype.join.call(arguments, " "));
+            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!");
+        throw new Error('Platform detection failed -- please file a report!');
     }
 
  // The formatting conventions here for valid arguments are as follows:
         f = function (x, pattern) {
             x.replace(pattern, function (matches, key, val) {
                 switch (val) {
-                case "true":
+                case 'true':
                     val = true;
                     break;
-                case "false":
+                case 'false':
                     val = false;
                     break;
-                case "":
+                case '':
                     val = true;
                     break;
                 default:
                     val = (isNaN(parseFloat(val))) ? val : parseFloat(val);
                 }
                 if (q.flags.hasOwnProperty(key)) {
-                    if (q.flags[key].hasOwnProperty("length")) {
+                    if (q.flags[key].hasOwnProperty('length')) {
                         Array.prototype.push.call(q.flags[key], val);
                     } else {
                         q.flags[key] = [q.flags[key], val];
     }
 
 }(function (outer_scope) {
-    "use strict";
+    '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

couchdb-app/filters/queue.js

 //- JavaScript source code
 
 //- queue.js ~~
-//                                                      ~~ (c) SRW, 21 Oct 2011
+//
+//  This file contains a generalized solution to querying JSON documents'
+//  properties according to known values. Additionally, using the "_changes"
+//  API leaves an easy way room to add authentication on a per-request basis,
+//  if ever such measures should be needed for access control :-)
+//
+//                                                      ~~ (c) SRW, 31 Oct 2011
 
 function (doc, req) {
     'use strict';
 
-    return ((doc.hasOwnProperty('val'))                         &&
-            (doc.val !== null)                                  &&
-            (doc.val !== undefined)                             &&
-            (doc.val.hasOwnProperty('f'))                       &&
-            (doc.val.hasOwnProperty('x'))                       &&
-            (doc.val.hasOwnProperty('y'))                       &&
-            (doc.val.hasOwnProperty('status'))                  &&
-            (doc.val.hasOwnProperty('token'))                   &&
-            (req.hasOwnProperty('query'))                       &&
-            (req.query.hasOwnProperty('status') ?
-                (doc.val.status === req.query.status) : true)   &&
-            (req.query.hasOwnProperty('token') ?
-                (doc.val.token === req.query.token) : true));
-
- // The next part is an attempt to construct a generalized query mechanism for
- // arbitrary terms -- it doesn't work yet ...
- //
- //                                                     ~~ (c) SRW, 26 Oct 2011
-
- /*
-    var isTask, matchesQuery;
-
-    isTask = function (doc) {
-        return ((doc.hasOwnProperty('val'))         &&
-                (doc.val !== null)                  &&
-                (doc.val !== undefined)             &&
-                (doc.val.hasOwnProperty('f'))       &&
-                (doc.val.hasOwnProperty('x'))       &&
-                (doc.val.hasOwnProperty('y'))       &&
-                (doc.val.hasOwnProperty('status'))) ;
-    };
-
-    matchesQuery = function (doc, req) {
-        var key, query;
-        query = (req.hasOwnProperty('query')) ? req.query : {};
-        for (key in query) {
-            if (query.hasOwnProperty(key) && doc.hasOwnProperty(key)) {
-                if (query[key] !== doc[key]) {
-                    return false;
-                }
+    var flag, key, q, v;
+
+    if (doc.hasOwnProperty('val')) {
+        v = ((doc.val !== null) && (doc.val !== undefined)) ? doc.val : {};
+        flag = ((v.hasOwnProperty('f')) && (v.hasOwnProperty('x')) &&
+                (v.hasOwnProperty('y')) && (v.hasOwnProperty('status')));
+        if (flag !== true) {
+            return false;
+        }
+    } else {
+        return false;
+    }
+
+    q = (req.hasOwnProperty('query')) ? req.query : {};
+
+    for (key in q) {
+        if (q.hasOwnProperty(key) && v.hasOwnProperty(key)) {
+         // NOTE: This ignores keys that don't exist on the document so
+         // that CouchDB's special filter keywords won't interfere. Then,
+         // you can still specify "limit=10" or "since=100", for example,
+         // to subset results without blacklisting keywords explicitly :-)
+            if (q[key] !== v[key]) {
+                return false;
             }
         }
-        return true;
-    };
+    }
 
-    return isTask(doc) && matchesQuery(doc, req);
- */
+    return true;
 }
 
 //- vim:set syntax=javascript:
 #
 #   These directions build Quanah's icons from LaTeX source code.
 #
-#                                                           ~~ SRW, 21 Oct 2010
+#                                                           ~~ SRW, 31 Oct 2011
 
-SHELL       :=  bash                        #-  GNU bash        v. 3.2.48(1)
+include ../tools/macros.make
 
-RENDER      :=  pdflatex -no-shell-escape   #-  MacTeX 2010     v. 1.40.11-2.2
-CROP        :=  pdfcrop                     #   "               v. 1.31
-CONVERT     :=  convert                     #-  ImageMagick     v. 6.6.7-1
-CP          :=  rsync -va                   #-  rsync.samba.org v. 3.0.7
-OPEN        :=  open
+COMPOSITE   :=  $(call contingent, composite)                   #-  ImageMagick
+CONVERT     :=  $(call contingent, convert)                     #-  "
+CP          :=  $(call contingent, rsync) -va                   #-  Samba
+CROP        :=  $(call contingent, pdfcrop)                     #-  TeX Live
+OPEN        :=  $(call contingent, gnome-open open)
+RENDER      :=  $(call contingent, pdflatex) -no-shell-escape   #-  TeX Live
 
 BGSRC       :=  background.tex
 BGPDF       :=  $(BGSRC:%.tex=%.pdf)
+DARKBGSRC   :=  dark-background.tex
+DARKBGPDF   :=  $(DARKBGSRC:%.tex=%.pdf)
 SRC         :=  quanah-logo.tex
 PDF         :=  $(SRC:%.tex=%.pdf)
 
-APPLE_TOUCH :=  apple-touch-icon.png
 BITBUCKET   :=  bitbucket.jpg
 FAVICON     :=  favicon.ico
 GOOGLECODE  :=  googlecode.png
 GOOGWEBAPP1 :=  128_icon.png
 GOOGWEBAPP2 :=  112_icon.png
+IOS_LORES   :=  apple-touch-startup-image-lores.png
+IOS_HIRES   :=  apple-touch-startup-image-hires.png
+IOS_SPLASH  :=  apple-touch-startup-image-splash.png
+IPAD        :=  apple-touch-icon-ipad.png
+IPHONE      :=  apple-touch-icon-iphone.png
+IPHONE4     :=  apple-touch-icon-iphone4.png
 LOGO        :=  logo.jpg
 
+DARK_BG     :=  dark-background.png
+DARK_BG_HI  :=  dark-background-hires.png
+DARK_BG_LO  :=  dark-background-lores.png
+PNG128      :=  reference-128.png
+PNG256      :=  reference-256.png
 REF_BG      :=  background.png
 REF_FG      :=  foreground.png
+REF_JPG     :=  reference.jpg
 REF_PNG     :=  reference.png
 
-ICONS       :=  $(APPLE_TOUCH)  \
-                $(BITBUCKET)    \
+ICONS       :=  $(BITBUCKET)    \
                 $(FAVICON)      \
                 $(GOOGLECODE)   \
                 $(GOOGWEBAPP1)  \
                 $(GOOGWEBAPP2)  \
+                $(IOS_LORES)    \
+                $(IOS_HIRES)    \
+                $(IOS_SPLASH)   \
+                $(IPAD)         \
+                $(IPHONE)       \
+                $(IPHONE4)      \
                 $(LOGO)
 
 #-  Since we're converting a vector format (PDF) into raster formats, we will
 IMGOPTS      =  -density 600 -gravity center -quality 100 -resize '$(1)x$(1)'
 
 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 "\#FFFFFF"
+                -draw "matte 0,0 floodfill" -shave "1x1" -background "\#929292"
 
 .PHONY: all clean clobber reset run
 
 all: run
 
 clean: reset
-	@   $(RM) $(wildcard *.aux *.log) $(BGPDF) $(PDF) $(REF_FG) $(REF_BG)
+	@   $(RM) $(wildcard *.aux *.log) $(BGPDF) $(DARKBGPDF) $(PDF)      \
+                $(DARK_BG) $(DARK_BG_HI) $(DARK_BG_LO) $(PNG128) $(PNG256)  \
+                $(REF_FG) $(REF_BG)
 
 clobber: clean
-	@   $(RM) $(ICONS) $(REF_PNG)
+	@   $(RM) $(ICONS) $(REF_JPG) $(REF_PNG)
 
 reset:
-	@   clear
+	@   $(call contingent, clear)
 
 run: $(ICONS)
 	@   for each in $(ICONS); do $(OPEN) $${each}; done
 
 .PHONY: test
 
-test: $(APPLE_TOUCH)
+test: $(IPHONE4)
 	@   $(OPEN) $<
 
 ###
 
-$(APPLE_TOUCH): $(REF_PNG)
-	@   $(CONVERT) $(call IMGOPTS,114) $< $@
+$(BITBUCKET): $(REF_PNG)
+	@   $(CONVERT) \
+                -density 600 -gravity center -quality 100 -resize '35x35'   \
+                -extent '35x35' -bordercolor $(COLOR) -border "1x1" -matte  \
+                -fill none -fuzz "20%" -draw "matte 0,0 floodfill" -shave   \
+                "1x1" -background white $< $@
 
 $(FAVICON): $(REF_PNG)
 	@   $(CONVERT) $(call IMGOPTS,16) $< $@
 
-$(BITBUCKET): $(REF_PNG)
-	@   $(CONVERT) $(call IMGOPTS_REF,35) $< $@
-
 $(GOOGLECODE): $(REF_PNG)
 	@   $(CONVERT) $(call IMGOPTS,55) $< $@
 
 $(GOOGWEBAPP2): $(REF_PNG)
 	@   $(CONVERT) $(call IMGOPTS,112) $< $@
 
+$(IOS_LORES): $(DARK_BG) $(PNG128)
+	@   $(COMPOSITE) -gravity center $(PNG128) $(DARK_BG) $@
+
+$(IOS_HIRES): $(DARK_BG_HI) $(PNG256)
+	@   $(COMPOSITE) -gravity center $(PNG256) $(DARK_BG_HI) $@
+
+$(IOS_SPLASH): $(DARK_BG) $(GOOGWEBAPP1)
+	@   $(COMPOSITE) -gravity center $(GOOGWEBAPP1) $(DARK_BG) $@
+
+$(IPHONE): $(REF_JPG)
+	@   $(CONVERT) $(call IMGOPTS,57) $< $@
+
+$(IPAD): $(REF_JPG)
+	@   $(CONVERT) $(call IMGOPTS,72) $< $@
+
+$(IPHONE4): $(REF_JPG)
+	@   $(CONVERT) $(call IMGOPTS,114) $< $@
+
 $(LOGO): $(REF_PNG)
 	@   $(CONVERT) $(call IMGOPTS_REF,128) $< $@
 
+###
+
+$(PNG128): $(REF_PNG)
+	@   $(CONVERT) $(call IMGOPTS,128) $< $@
+
+$(PNG256): $(REF_PNG)
+	@   $(CONVERT) $(call IMGOPTS,256) $< $@
+
+###
+
+$(DARK_BG): $(DARKBGPDF)
+	@   $(CONVERT) $(call IMGOPTS,512) -extent '320x460' $< $@
+
+$(DARK_BG_LO): $(DARKBGPDF)
+	@   $(CONVERT) $(call IMGOPTS,512) -extent '320x480' $< $@
+
+$(DARK_BG_HI): $(DARKBGPDF)
+	@   $(CONVERT) $(call IMGOPTS,1024) -extent '640x960' $< $@
+
 $(REF_BG): $(BGPDF)
-	@   $(CONVERT) $(call IMGOPTS,512) -extent '512x512' $< $@
+	@   $(CONVERT) $(call IMGOPTS,1024) -extent '1024x1024' $< $@
 
 $(REF_FG): $(PDF)
-	@   $(CONVERT) $(call IMGOPTS,512) -extent '512x512' $< $@
+	@   $(CONVERT) $(call IMGOPTS,1024) -extent '1024x1024' $< $@
+
+$(REF_JPG): $(REF_PNG)
+	@   $(CONVERT) $(call IMGOPTS_REF,1024) $< $@
 
 $(REF_PNG): $(REF_BG) $(REF_FG)
-	@   $(CONVERT) $(call IMGOPTS,512) $(REF_FG) $(REF_BG) \
+	@   $(CONVERT) $(call IMGOPTS,1024) $(REF_FG) $(REF_BG) \
                 -compose ChangeMask -composite $@
 
 ###
 
 $(PDF): $(SRC)
 
+###
+
 %.pdf: %.tex
 	@   $(RENDER) $<                                                ;   \
             $(CROP) --margins "-1 0 -1 -1" $@ $@

icons/background.tex

 
 %-  background.tex ~~
 %
-%   This is the known background that corresponds to "web-logo.tex".
+%   This is the known background that corresponds to "quanah-logo.tex".
 %
 %                                                           ~~ SRW, 21 Oct 2010
 

icons/dark-background.tex

+%-  LaTeX document file
+
+%-  dark-background.tex ~~
+%
+%   This is a dark background for compositing the iOS startup image.
+%
+%                                                           ~~ SRW, 31 Oct 2011
+
+\documentclass{article}
+
+\usepackage{eulervm}                    %-  use Zapf's "Euler" font
+\usepackage{xcolor}                     %-  allow colors to be defined :-)
+    \definecolor{WebSafeNavy}{HTML}{000080}
+    \definecolor{DarkGray}{HTML}{929292}
+
+    \setlength{\fboxsep}{2pt}
+    \setlength{\fboxrule}{1pt}
+
+\pagestyle{empty}                       %-  minimize document's bounding box
+
+\begin{document}
+
+    \Huge
+    \colorbox{DarkGray}{\color{WebSafeNavy}\phantom{$Q$}}%
+
+\end{document}
+
+%-  vim:set syntax=tex: