Commits

Sean Wilkinson committed db4514b

Second step ...

  • Participants
  • Parent commits d0da9b8

Comments (0)

Files changed (7)

 #   control -- always execute your commits manually!
 #
 #                                                       ~~ (c) SRW, 08 Sep 2012
+#                                                   ~~ last updated 17 Nov 2012
 
 #-  CouchApp configuration files -- these may contain passwords!
 .couchapprc
 #-  Mercurial repository itself (if present)
 .hg/
 
-#-  Node Package Manager logs
+#-  Node.js stuff
+node_modules/
 npm-debug.log
 
 #-  Vim swap files
 #
 #   This contains live instructions for development on the Quanah library.
 #
-#                                                       ~~ (c) SRW, 05 Oct 2012
+#                                                       ~~ (c) SRW, 17 Nov 2012
 
 PROJECT_ROOT    :=  $(realpath $(dir $(firstword $(MAKEFILE_LIST))))
 
 include $(PROJECT_ROOT)/tools/macros.make
 
-ENGINES :=  js jsc v8 d8 v8cgi node nodejs narwhal-jsc rhino ringo narwhal  \
-                couchjs phantomjs # avmshell
-JS      :=  $(strip $(call contingent, $(ENGINES)))
-JSLIBS  :=  libs.js
-QUANAH  :=  src/quanah.js
-SRCJS   :=  $(JSLIBS) $(QUANAH) tools/chubby-checker.js $(wildcard tests/*.js)
-EXEJS   :=  main.js
-MINJS   :=  $(EXEJS:%.js=%-min.js)
-HTML    :=  index.html
-
-CAT     :=  $(call contingent, gcat cat)
-CLOSURE :=  $(call contingent, closure-compiler closure)
-CP      :=  $(call contingent, rsync gcp cp)
-CURL    :=  $(call contingent, curl) #-sS
-OPEN    :=  $(call contingent, x-www-browser gnome-open open)
+NPM     :=  $(call contingent, npm)
 RM      :=  $(call contingent, grm rm) -rf
-TIME    :=  $(call contingent, time)
-TOUCH   :=  $(call contingent, gtouch touch)
-WEBPAGE :=  $(call contingent, ruby jruby) ./tools/webpage.rb
-YUICOMP :=  $(call contingent, yui-compressor yuicompressor)
-
-define compile-with-google-closure
-    $(CLOSURE) --compilation_level SIMPLE_OPTIMIZATIONS \
-        $(1:%=--js %) --js_output_file $(2)
-endef
-
-define compile-with-yuicompressor
-    JS_TEMP_FILE="$(strip $(call random-prefix, $(2)))"                 ;   \
-    $(CAT) $(1) > $${JS_TEMP_FILE}                                      ;   \
-    $(YUICOMP) --type js $${JS_TEMP_FILE} -o $(2)                       ;   \
-    $(RM) $${JS_TEMP_FILE}
-endef
-
-define compile-js
-    $(call aside, "Optimizing scripts: $(1) --> $(2)")                  ;   \
-    $(call compile-with-yuicompressor, $(1), $(2))
-endef
-
-define fetch-url
-    $(call hilite, 'Fetching "$(strip $(2))" ...')                      ;   \
-    $(CURL) -o $(2) $(1)
-endef
-
-define random-prefix
-    $${RANDOM:=`hexdump -n 2 -e '/2 "%u"' /dev/urandom`}-$(strip $(1))
-endef
 
 .PHONY: all clean clobber distclean reset run
-.INTERMEDIATE: json2.js
 .SILENT: ;
 
 all: run
 
 clean: reset
-	@   $(RM) $(EXEJS) results1.out results2.out time-data.out
 
 clobber: clean
-	@   $(RM) $(abspath $(filter-out $(SRCJS), $(wildcard *.js)))
 
 distclean: clobber
-	@   $(RM) .d8_history .v8_history $(HTML) libs.js npm-debug.log *.tgz
+	@   $(RM) $(addprefix $(PROJECT_ROOT)/, \
+                .d8_history .v8_history ./node_modules/ npm-debug.log *.tgz)
 
 reset:
 	@   $(call contingent, clear)
 
-run: quick
+run: test
 
 ###
 
-.PHONY: benchmark browse browser check check-jasmine check-old quick test
-
-benchmark: $(EXEJS)
-	@   $(RM) time-data.out                                         ;   \
-            for each in $(call available, $(ENGINES)); do                   \
-                $(call aside, "$${each}")                               ;   \
-                for i in 1 2 3; do                                          \
-                    echo $${each} >>time-data.out 2>&1                  ;   \
-                    ($(TIME) $${each} $(EXEJS) >/dev/null 2>&1)             \
-                                                >>time-data.out 2>&1    ;   \
-                done                                                    ;   \
-            done                                                        ;   \
-            $(call hilite, '(analysis placeholder)')
-
-browse: browser
-	@   if [ -f $(HTML) ]; then $(OPEN) $(HTML); fi
-
-browser: $(EXEJS) $(HTML)
-
-check: check-old check-jasmine
-
-check-jasmine: $(QUANAH)
-	@   if [ -f test/test.html ]; then $(OPEN) test/test.html; fi
-
-check-old: $(EXEJS)
-	@   $(RM) results1.out results2.out                             ;   \
-            for each in $(call available, $(ENGINES)); do                   \
-                $(call aside, "$${each}")                               ;   \
-                if [ ! -f results1.out ]; then                              \
-                    $${each} $(EXEJS) 2>&1 >results1.out                ;   \
-                    if [ $$? -eq 0 ]; then                                  \
-                        $(call hilite, 'Success.')                      ;   \
-                    else                                                    \
-                        $(call alert, 'Failure (execution).')           ;   \
-                    fi                                                  ;   \
-                    sort results1.out -o results1.out                   ;   \
-                else                                                        \
-                    $${each} $(EXEJS) 2>&1 >results2.out                ;   \
-                    if [ $$? -eq 0 ]; then                                  \
-                        sort results2.out -o results2.out               ;   \
-                        diff results1.out results2.out 2>&1 >/dev/null  ;   \
-                        if [ $$? -eq 0 ]; then                              \
-                            $(call hilite, 'Success.')                  ;   \
-                        else                                                \
-                            $(call alert, 'Failure (different output).');   \
-                        fi                                              ;   \
-                    else                                                    \
-                        $(call alert, 'Failure (execution).')           ;   \
-                    fi                                                  ;   \
-                fi                                                      ;   \
-            done
-
-fast: $(EXEJS)
-	@   QUICK_JS_FILE="$(strip $(call random-prefix, $(EXEJS)))"    ;   \
-            $(call compile-js, $(EXEJS), $${QUICK_JS_FILE})             ;   \
-            $(call aside, "$(JS) $${QUICK_JS_FILE}")                    ;   \
-            $(TIME) $(JS) $${QUICK_JS_FILE}                             ;   \
-            if [ $$? -eq 0 ]; then                                          \
-                $(call hilite, 'Success.')                              ;   \
-            else                                                            \
-                $(call alert, 'Failure.')                               ;   \
-            fi                                                          ;   \
-            $(RM) $${QUICK_JS_FILE}
-
-faster: $(MINJS)
-	@   $(call aside, "$(JS) $<")                                   ;   \
-            $(TIME) $(JS) $<                                            ;   \
-            if [ $$? -eq 0 ]; then                                          \
-                $(call hilite, 'Success.')                              ;   \
-            else                                                            \
-                $(call alert, 'Failure.')                               ;   \
-            fi
-
-quick:
-	@   QUICK_JS_FILE="$(strip $(call random-prefix, $(EXEJS)))"    ;   \
-            $(CAT) $(filter-out $(JSLIBS), $(SRCJS)) > $${QUICK_JS_FILE};   \
-            $(call aside, "$(JS)")                                      ;   \
-            $(TIME) $(JS) $${QUICK_JS_FILE}                             ;   \
-            if [ $$? -eq 0 ]; then                                          \
-                $(call hilite, 'Success.')                              ;   \
-            else                                                            \
-                $(call alert, 'Failure.')                               ;   \
-            fi                                                          ;   \
-            $(RM) $${QUICK_JS_FILE}
-
-test: check
+.PHONY: test
 
-###
-
-$(EXEJS): $(SRCJS)
-	@   $(CAT) $^ > $@
-
-$(HTML): | $(EXEJS)
-	@   $(WEBPAGE) -o $@ $(EXEJS)
-
-$(JSLIBS): json2.js
-	@   $(CAT) $^ > $@
-
-$(MINJS): $(EXEJS)
-	@   $(call compile-js, $<, $@)
+test: $(PROJECT_ROOT)/node_modules/
+	@   $(NPM) test
 
 ###
 
-#-  NOTE: I cache a few JavaScript frameworks and libraries in a directory on
-#   my personal machine, and I highly recommend it for rapid development. If
-#   you don't have the same personal settings, though, these directions will
-#   still fall back to our trusty friend 'curl' :-)
-
-json2.js:
-	@   CROCKHUB="https://raw.github.com/douglascrockford"          ;   \
-            if [ -f $(CODEBANK)/lib/JavaScript/json2.js ]; then             \
-                $(CP) $(CODEBANK)/lib/JavaScript/json2.js $@            ;   \
-            else                                                            \
-                $(CURL) -o $@ $${CROCKHUB}/JSON-js/master/json2.js      ;   \
-            fi 
+$(PROJECT_ROOT)/node_modules/: $(PROJECT_ROOT)/package.json
+	@   $(NPM) install
 
 ###
 

examples/hello-full.js

+//- JavaScript source code
+
+//- hello-full.js ~~
+//
+//  This program is _not_ a simple "Hello world" program. Instead, it serves as
+//  a minimal example of the rigor with which Quanah has been designed. I don't
+//  expect anyone else to code this way, but I still want to provide resources
+//  for those who are or aspire to become expert-level JavaScript programmers.
+//
+//                                                      ~~ (c) SRW, 17 Nov 2012
+
+(function () {
+    'use strict';
+
+ // Pragmas
+
+    /*global puts: false */
+
+    /*jslint indent: 4, maxlen: 80 */
+
+ // Demonstration
+
+    ('Hello world!').Q(function (evt) {
+     // This function needs documentation.
+        puts(this.val);
+        return evt.exit();
+    });
+
+ // That's all, folks!
+
+    return;
+
+}());
+
+//- vim:set syntax=javascript:

examples/hello.js

+//- JavaScript source code
+
+//- hello.js ~~
+//
+//  NOTE: You should load "tools/puts.js" first or you won't see any output.
+//  I would apologize, but it really isn't my fault that "ECMAScript-262" lacks
+//  a standardized output function.
+//
+//                                                      ~~ (c) SRW, 17 Nov 2012
+
+('Hello world!').Q(function () { puts(this.val); });
+
+//- vim:set syntax=javascript:

test/quanah-spec.js

 
 //- quanah-spec.js ~~
 //
-//  Jasmine (http://pivotal.github.com/jasmine/) BDD Specs for Quanah, written
-//  in an effort to:
-//    * Provide a degree of documentation
-//    * Learn the darn thing
-//    * Provide a way to catch changes made in Quanah's API
+//  These tests were originally contributed by David Robbins on 06 April 2012
+//  for use with Jasmine (http://pivotal.github.com/jasmine/). The current form
+//  is designed for use with Mocha (https://github.com/visionmedia/mocha) as
+//  part of an NPM-based workflow.
 //
-//  The actually interesting, i.e. expressive of Quanah's use, code takes place
-//  in the describe(...) blocks; everything prior is just helpers.
-//
-//                                                      ~~ (c) DER, 06 Apr 2012
-//                                                      update SRW, 17 Nov 2012
+//                                                      ~~ (c) SRW, 17 Nov 2012
 
 (function () {
     'use strict';
 
  // Pragmas
 
-    /*global
-        beforeEach: false,
-        console: false,
-        describe: false,
-        expect: false,
-        it: false,
-        jasmine: false,
-        runs: false,
-        waitsFor: false,
-        xit: false
-    */
-
-    /*jshint */
-
-    /*jslint indent: 4, maxlen: 80 */
-
- // Prerequisites
+    /*global beforeEach: false, describe: false, it: false */
 
-    if (Object.prototype.hasOwnProperty('Q') === false) {
-        throw new Error('Method Q is missing.');
-    }
+    /*jslint indent: 4, maxlen: 80, node: true */
 
  // Declarations
 
-    var Q, getAvars, toType, onerror;
+    var expect;
 
  // Definitions
 
-    Q = Object.prototype.Q;
-
-    getAvars = function (x) {
-     // Returns an avar whose val is an array of avars, with one for each
-     // of the arguments (or each element of the first argument if it is
-     // an array.
-        var arg, args, avars, i, temp;
-        avars = [];
-        args = (toType(x) === 'array') ? x : arguments;
-        for (i in args) {
-            if (args.hasOwnProperty(i)) {
-                arg = args[i];
-                temp = Q.avar({val: arg});
-                temp.onerror = onerror;
-                avars.push(temp);
-            }
-        }
-        temp = Q.avar({val: avars});
-        temp.onerror = onerror;
-        return temp;
-    };
-
-    toType = function (obj) {
-     // This function needs documentation.
-        return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
-    };
-
-    onerror = function (msg) {
-     // This function needs documentation.
-        console.error(msg);
-        return;
-    };
-
- // Invocations
-
-    beforeEach(function () {
-     // This function needs documentation.
-        var customMatchers, setMatcherMessage;
-        customMatchers = {};
-        setMatcherMessage = function (message, matcher_context) {
-         // This function needs documentation.
-            matcher_context.message = function () {
-             // This function needs documentation.
-                return message;
-            };
-            return;
-        };
-        customMatchers.toBeA = function (expected_type) {
-         // This function needs documentation.
-            return toType(this.actual) === expected_type;
-        };
-        customMatchers.toBeAFunction = function () {
-         // This function needs documentation.
-            return toType(this.actual) === 'function';
-        };
-        customMatchers.toBeAObject = function () {
-         // This function needs documentation.
-            return toType(this.actual) === 'object';
-        };
-        customMatchers.toBeAUuid = function () {
-         // This function needs documentation.
-            return (this.actual.match(/[0-9abcdef]{32}/)) !== null;
-        };
-        customMatchers.toContainPrefixes = function (expected) {
-         // This function needs documentation.
-            var key;
-            for (key in expected) {
-                if (expected.hasOwnProperty(key) &&
-                        (this.actual[key] !== expected[key])) {
-                    setMatcherMessage('Expected ' + this.actual[key] +
-                            ' to be ' + expected[key] + ', with prefix ' +
-                            key + '.', this);
-                    return false;
-                }
-            }
-            return true;
-        };
-        this.addMatchers(customMatchers);
-        return;
-    });
+    expect = require('expect.js');
 
-    describe('Quanah', function () {
+ // Tests
 
+    describe('Quanah', function () {
      // This is the actual specification :-)
+        var avar;
+        beforeEach(function () {
+         // This function needs documentation.
+            Object.prototype.Q = require('../src/quanah');
+            avar = Object.prototype.Q.avar;
+            return;
+        });
 
         it('should be awesome', function () {
-            expect(true).toBeTruthy();
-            return;
+            expect(true).to.equal(true);
         });
 
         it('should place function Q on the Object prototype', function () {
             expect(Object.prototype.hasOwnProperty('Q')).toBeTruthy();
-            return;
         });
 
         it('should provide the Quanah API', function () {
                     expect(typeof Q[thing]).toBe(api[thing]);
                 }
             }
-            return;
         });
 
-        return;
-
     });
 
     describe('Quanah AVars', function () {
+//- JavaScript source code
+
+//- puts.js ~~
+//                                                      ~~ (c) SRW, 17 Nov 2012
+
+(function (global) {
+    'use strict';
+
+ // Pragmas
+
+    /*jslint indent: 4, maxlen: 80 */
+
+ // Out-of-scope definitions
+
+    global.puts = function () {
+     // This function is my own self-contained output logging utility.
+        var hOP, isFunction, join;
+        hOP = function (obj, name) {
+         // This function simply abbreviates the `hasOwnProperty` method
+         // lookup. To perform the check correctly, you have to check if `obj`
+         // would even have the method `hasOwnProperty`, and full rigor gets
+         // old really fast.
+            return ((obj !== null)      &&
+                    (obj !== undefined) &&
+                    (obj.hasOwnProperty(name)));
+        };
+        isFunction = function (f) {
+         // This function returns `true` if and only if input argument `f` is a
+         // function. The second condition is necessary to avoid a false
+         // positive in a pre-ES5 environment when `f` is a regular expression.
+            return ((typeof f === 'function') && (f instanceof Function));
+        };
+        join = Array.prototype.join;
+        if (hOP(global, 'system') && isFunction(global.system.print)) {
+            global.puts = function () {
+             // This function is typically used by Narwhal and RingoJS. Since
+             // Narwhal can be powered by any conforming engine, I should warn
+             // you that I've only tested it on JSC and Rhino engines.
+                global.system.print(join.call(arguments, ' '));
+                return;
+            };
+        } else if (hOP(global, 'system') && isFunction(global.system.stdout)) {
+            global.puts = function () {
+             // This function is typically used by v8cgi. Its support here is
+             // experimental; see http://code.google.com/p/v8cgi/ for info.
+                global.system.stdout(join.call(arguments, ' ') + '\n');
+                return;
+            };
+        } else if (hOP(global, 'console') && isFunction(global.console.log)) {
+            global.puts = function () {
+             // This function is typically used by Node.js and by modern web
+             // browsers that have a developer's console.
+                global.console.log(join.call(arguments, ' '));
+                return;
+            };
+        } else if (isFunction(global.alert)) {
+            global.puts = function () {
+             // This function is a fallback definition used by "crusty old web
+             // browsers" that lack a developer's console. Falling back to the
+             // 'alert' function is possibly the most obnoxious thing I could
+             // have done, but I'll try and find some other alternatives soon.
+                global.alert(join.call(arguments, ' '));
+                return;
+            };
+        } else if (hOP(global, 'println') && isFunction(global.println)) {
+            global.puts = function () {
+             // This function is typically used by 'jrunscript', which is a
+             // JS environment that comes bundled with Java itself.
+                global.println(join.call(arguments, ' '));
+                return;
+            };
+        } else if (hOP(global, 'print') && isFunction(global.print)) {
+            global.puts = function () {
+             // This function is typically used by server-side developers'
+             // shells like JavaScriptCore, Rhino, Spidermonkey, and V8. A
+             // few variations include 'couchjs', 'd8', and 'mongo'.
+                global.print(join.call(arguments, ' '));
+                return;
+            };
+        } else if (isFunction(global.postMessage)) {
+            global.puts = function () {
+             // This function is typically used by a Web Worker, but it isn't
+             // a standalone definition. It must be tied through in the main
+             // browser context to some 'bee.onmessage' handler ...
+                global.postMessage(join.call(arguments, ' '));
+                return;
+            };
+        } else {
+         // This is the place where only the naughtiest of implementations
+         // will land. Unfortunately, Adobe/Mozilla Tamarin is one of them.
+            global.puts = function () {
+             // This function definition is a last resort, trust me. Only the
+             // naughtiest of implementations will land here. Unfortunately,
+             // Adobe/Mozilla Tamarin is one of them. Although it's mainly an
+             // ActionScript engine at the moment, major ECMAScript upgrades
+             // are planned to roll out here later in the year ...
+                /*global print: false */
+                if (isFunction(print)) {
+                    print(join.call(arguments, ' '));
+                    return;
+                }
+                throw new Error('The `puts` definition fell through.');
+            };
+        }
+        global.puts.apply(this, arguments);
+        return;
+    };
+
+ // That's all, folks!
+
+    return;
+
+}(Function.prototype.call.call(function (that) {
+    'use strict';
+ // See the bottom of "quanah.js" for documentation.
+    /*jslint indent: 4, maxlen: 80 */
+    /*global global: true */
+    if (this === null) {
+        return (typeof global === 'object') ? global : that;
+    }
+    return (typeof this.global === 'object') ? this.global : this;
+}, null, this)));
+
+//- vim:set syntax=javascript:

tools/webpage.rb

-#-  Ruby source code
-
-#-  webpage.rb ~~
-#
-#   This script generates an HTML document in the current working directory.
-#   It will embed any stylesheets, images, or otherwise that are specified as
-#   command-line arguments, based on file extension. It does not attempt to
-#   create optimized, ready-to-deploy webpages, but they are valid HTML 5. I
-#   wrote this program because I find myself using the web browser increasingly
-#   as a development tool for tasks not traditionally reserved for the browser,
-#   such as viewing image files output by Gnuplot or R. When those situations
-#   arise, I end up hacking together a project-specific PHP script or something
-#   when I really should just have a dedicated script that I can tune for all
-#   problems in order to maintain some level of consistency, at the very least.
-#   It also keeps me from having to go hunt for small advances over the usual
-#   scripts, because all the advances will be contained herein :-)
-#
-#   NOTE: I would like to add the ability not only to embed HTML pages in other
-#   HTML pages using iframes, but also to allow the user to click on the iframe
-#   to load the source URL as a standalone webpage.
-#
-#   NOTE: This implementation of Ruby will be deprecated soon in favor of a JS
-#   version that uses the Web Chassis framework directly. This version is kind
-#   of flaky anyway due to name changes and such.
-#
-#                                                       ~~ (c) SRW, 18 Aug 2011
-
-require "optparse"
-
-def main
-
-    options = {
-        :files      =>  [],
-        :output     =>  "index.html",
-        :verbose    =>  false
-    }
-
-    optparse = OptionParser.new do |opts|
-
-        opts.banner = "Usage: webpage [options] [files]"
-
-        opts.on_tail("-h", "--help", "display this message ;-)") do
-            puts opts
-            exit
-        end
-
-        opts.on("-o", "--output WEBPAGE", "specify the output file") do |f|
-            options[:output] = f
-        end
-
-        opts.on("-q", "--silent", "silence stdout logging") do
-            options[:verbose] = false
-        end
-
-        opts.on("-v", "--verbose", "print lots of garbage to stdout") do
-            options[:verbose] = true
-        end
-
-    end
-
-  # Destructively parse the input arguments.
-
-    optparse.parse!
-
-  # Assume that all remaining uncaught arguments should be interpreted as paths
-  # to files that should be embedded; ensure that these exist before storing.
-
-    ARGV.delete_if { |x| options[:files] << x if FileTest.file?(x); true }
-
-  # Print stuff out so I can double-check it while I'm still debugging ...
-
-    content = webpage(options)
-    puts content if options[:verbose]
-
-  # Write the generated contents to file :-)
-
-    File::open(options[:output], "w") { |f| f.write(content) }
-
-end
-
-def today
-    months = [
-        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-    ]
-    time = Time.now
-    day = (time.day < 10) ? "0#{time.day}" : time.day
-    month = months[time.month - 1]
-    year = time.year
-    return [day, month, year].join(' ');
-end
-
-def webpage(options)
-
-    f = {
-        :favicon    =>  nil,
-        :css        =>  [],
-        :imgs       =>  {},             #-  NOTE: This one is a hash.
-        :js         =>  [],
-        :swf        =>  [],
-        :other      =>  []
-    }
-
-    options[:files].each do |filename|
-        extension = /([^.]*)$/.match(filename)[0]
-        name = File.basename(filename, File.extname(filename))
-        case extension
-            when "css"
-                f[:css] << %{<link rel="stylesheet" href="#{filename}"/>}
-            when "gif", "jpg", "jpeg", "pdf", "png", "svg"
-                if f[:imgs].has_key?(name) then
-                    f[:imgs][name] << filename
-                else
-                    f[:imgs][name] = [filename]
-                end
-            when "html"
-                if filename != options[:output] then
-                    if f[:imgs].has_key?(name) then
-                        f[:imgs][name] << filename
-                    else
-                        f[:imgs][name] = [filename]
-                    end
-                end
-            when "ico"
-                f[:favicon] = %{<link rel="shortcut icon" href="#{filename}"/>}
-            when "js"
-                if (File.basename(filename) == "web-chassis.js") then
-                    f[:chassis] = filename
-                    #f[:elevate] = %{<script src="#{filename}"></script>}
-                else
-                    f[:js] << filename
-                    #f[:js] << %{<script src="#{filename}"></script>}
-                end
-            when "swf"
-                f[:swf] << [
-                    %{<object width="800" height="600">},
-                    %{<param name="movie" value="#{filename}">},
-                    %{<embed src="#{filename}" width="800" height="600">},
-                    %{</embed></object>}
-                ].join("")
-            else #:other
-                f[:other] << filename
-        end
-    end
-
-  # Now, we need to filter the images for fallbacks -- if the same basename has
-  # been given for SVG and PNG, for example, choose SVG and set up a fallback.
-
-    f[:imgs].each_pair do |key, val|
-        val.sort! do |a, b|
-            order = [".html", ".svg", ".pdf", ".png", ".gif", ".jpeg", ".jpg"]
-            metric = lambda { |x| order.index(File.extname(x)) }
-            metric.call(a) <=> metric.call(b)
-        end
-    end
-
-    f[:imgs] = f[:imgs].values.map do |filename, fallback|
-        fallback = fallback || "n/a"
-        caption = filename
-        puts "Fallback: #{fallback}" if fallback != "n/a"
-        extension = /([^.]*)$/.match(filename)[0].to_sym
-      # Please note that none of these rules actually _use_ the fallback yet!
-        case extension
-            when :gif
-                img = %{<img src="#{filename}" alt="(#{filename})"/>}
-            when :html
-                img = %{<iframe src="#{filename}"></iframe>}
-            when :jpg
-                img = %{<img src="#{filename}" alt="(#{filename})"/>}
-            when :pdf
-                img = %{<object data="#{filename}" type="application/pdf" } +
-                    'width="700" height="700" alt="(embedded PDF)"></object>'
-            when :png
-                img = %{<img src="#{filename}" alt="(#{filename})"/>}
-            when :svg
-                img = %{<img src="#{filename}" alt="(#{filename})"}
-            else
-                img = %{<a href="#{filename}">(click to view)</a>}
-        end
-        "<figure><figcaption>#{caption}</figcaption>#{img}</figure>"
-    end
-
-  # Then, we'll need a default layout if no stylesheets were specified.
-
-    if f[:css].length == 0 then
-        f[:css] = [(<<-EOF
-    <style>
-        body {
-            background-color: #CCCCCC;
-            color: black;
-            font-family: Georgia, serif;
-            font-size: 12pt;
-            margin: 1in;
-            text-align: center;
-        }
-        a {
-            border-bottom: 1px dotted;
-            color: black;
-            text-decoration: none;
-        }
-        a:hover {
-            color: blue;
-        }
-        canvas {
-            border: 1px solid black;
-        }
-        div {
-            margin-bottom: 1em;
-            margin-top: 1em;
-            text-align: justify;
-        }
-        figcaption {
-            font-weight: bold;
-        }
-        figure {
-            text-align: center;
-        }
-        figure img[src$=".png"] {
-            background-color: white;
-        }
-        figure img[src$=".svg"] {
-            background-color: white;
-        }
-        footer {}
-        header {}
-        iframe {
-            height: 75%;
-            width: 100%;
-        }
-        noscript {
-            color: red;
-            font-size: 200%;
-            text-align: center;
-        }
-        pre {
-            text-align: left;
-        }
-        textarea {
-            font-family: Courier, monospace;
-            font-size: 12pt;
-            text-align: left;
-        }
-        .mono {
-            font-family: Courier, monospace;
-        }
-    </style>
-    EOF
-        ).strip()]
-    end
-
-    manana = <<-EOF
-<script id="manana">
-        this.manana = function () {
-            "use strict";
-            if (typeof window.console !== 'object') {
-                (function (form) {
-                    var runButton, textarea;
-                    runButton = document.createElement("input");
-                    textarea  = document.createElement("textarea");
-                    runButton.onclick = function () {
-                        try {
-                            eval(textarea.value);
-                        } catch (err) {
-                            alert(err);
-                        } finally {
-                         // Uncomment this to refresh the page automatically.
-                         //location.reload(true);
-                        }
-                    };
-                    runButton.type = "button";
-                    runButton.value = "Run";
-                    textarea.autofocus = true;
-                    textarea.cols = 80;
-                    textarea.rows = 24;
-                    form.appendChild(textarea);
-                    form.appendChild(document.createElement("br"));
-                    form.appendChild(document.createElement("br"));
-                    form.appendChild(runButton);
-                    document.body.appendChild(form);
-                }(document.createElement("form")));
-            } else {
-                (function (div) {
-                    div.innerHTML += "Check the developer console for output.";
-                    document.body.appendChild(div);
-                }(document.createElement("div")));
-            }
-            chassis(function (q, global) {
-                "use strict";
-                var scripts, me;
-                me = document.getElementById("manana");
-                scripts = [
-                #{
-                    padding = '",' + "\n" + (" " * 20) + '"'
-                    ('    "' + f[:js].join(padding) + '"') if f[:js].length > 0
-                }
-                ];
-                while (scripts.length > 0) {
-                    q.load(scripts.shift());
-                }
-                delete global.manana;                   //- deletes function
-                me.parentNode.removeChild(me);          //- deletes script
-            });
-        }
-    </script>
-    EOF
-
-  # Finally, we generate some HTML -- yay!
-
-    html = <<-EOF
-<!DOCTYPE html>
-<!--
-    #{options[:output]} ~~
-
-    This static page was generated dynamically by a Ruby script prior to
-    deployment. All dynamic interactions are performed in client-side JS :-)
-
-                                                        ~~ (c) SRW, #{today}
--->
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-    <meta name="author" content="Sean Wilkinson"/>
-    <title>#{options[:output]}</title>
-    #{f[:css].join("\n    ")}
-    #{(f[:favicon] == nil) ? "<!-- (no favicon) -->" : f[:favicon]}
-  </head>
-  <body>
-    <noscript>This page requires JavaScript.</noscript>
-    #{f[:imgs].length > 0 ? f[:imgs].join("\n    ") : "<!-- (no images) -->"}
-  <!--
-    <script>
-        (function () {
-            'use strict';
-            var bee = new Worker('main.js');
-            bee.onmessage = function (evt) {
-                console.log(evt.data);
-                return false;
-            };
-            bee.onerror = function (evt) {
-                console.error(evt.message);
-                return false;
-            };
-        }());
-    </script>
-  -->
-    #{# Web Chassis should be included?
-        if f[:chassis] then
-            if f[:js].length > 0 then
-                manana + "    " + '<script src="' + f[:chassis] +
-                    '" onload="manana();"></script>'
-            else
-                '<script src="' + f[:chassis] + '"></script>'
-            end
-        else
-            if f[:js].length > 0 then
-                (f[:js].map do |each|
-                    %{<script src="#{each}"></script>}
-                end).join("\n    ")
-            else
-                "<!-- (no scripts) -->"
-            end
-        end
-    }
-    #{f[:swf].length > 0 ? f[:swf].join("\n    ") : "<!-- (no flash) -->"}
-  </body>
-</html>
-    EOF
-
-    html
-
-end
-
-main
-
-#-  vim:set syntax=ruby: