[Pkg-mozext-commits] [ubiquity-extension] 06/08: Add missing sources.

Gabriele Giacone gg0-guest at moszumanska.debian.org
Wed Jul 30 00:24:15 UTC 2014


This is an automated email from the git hooks/post-receive script.

gg0-guest pushed a commit to branch master
in repository ubiquity-extension.

commit e134a7a9ed77fe62119e6b9e97e0f67ac9cf9417
Author: Gabriele Giacone <1o5g4r8o at gmail.com>
Date:   Wed Jul 30 02:02:17 2014 +0200

    Add missing sources.
---
 debian/changelog                                   |    2 +
 debian/copyright                                   |   14 +-
 debian/missing-sources/README                      |   19 +
 debian/missing-sources/scripts/core.js             |  865 ++++++++++
 debian/missing-sources/scripts/date.js             |   21 +
 debian/missing-sources/scripts/extras.js           |  332 ++++
 .../missing-sources/scripts/globalization/en-US.js |  195 +++
 .../scripts/html-sanitizer-minified.js             |    1 +
 debian/missing-sources/scripts/html-sanitizer.js   | 1065 +++++++++++++
 debian/missing-sources/scripts/parser.js           | 1116 +++++++++++++
 debian/missing-sources/scripts/prettify.js         | 1655 ++++++++++++++++++++
 debian/missing-sources/scripts/sugarpak.js         |  475 ++++++
 debian/missing-sources/scripts/time.js             |  269 ++++
 13 files changed, 6028 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index 626c552..9ba0d53 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,6 +2,8 @@ ubiquity-extension (0.6.4~pre20140729-1) UNRELEASED; urgency=medium
 
   * Hg snapshot.
   * Switch PTS to new Package Tracker.
+  * Missing sources: copied under d/missing-sources, added copyright
+    entries.
   * Bump Standards-Version to 3.9.5 (no changes).
 
  -- Gabriele Giacone <1o5g4r8o at gmail.com>  Tue, 29 Jul 2014 16:53:16 +0200
diff --git a/debian/copyright b/debian/copyright
index 5e6a674..f0e09a9 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -29,10 +29,22 @@ Files: scripts/pixastic.js
 Copyright: 2008 Jacob Seidelin <cupboy at gmail.com>
 License: MIT
 
-Files: scripts/date.js
+Files: scripts/date.js debian/missing-sources/scripts/date.js
+ debian/missing-sources/scripts/core.js
+ debian/missing-sources/scripts/extras.js
+ debian/missing-sources/scripts/parser.js
+ debian/missing-sources/scripts/sugarpak.js
+ debian/missing-sources/scripts/time.js
+ debian/missing-sources/scripts/globalization/en-US.js
 Copyright: 2006-2008, Coolite Inc.
 License: MIT
 
+Files: scripts/html-sanitizer-minified.js scripts/prettify.js
+ debian/missing-sources/scripts/html-sanitizer.js
+ debian/missing-sources/scripts/prettify.js
+Copyright: 2006, Google Inc.
+License: Apache-2
+
 Files: scripts/wikicreole.js
 Copyright: 2008, Ivan Fomichev
            2007, Chris Purcell
diff --git a/debian/missing-sources/README b/debian/missing-sources/README
new file mode 100644
index 0000000..7ace0b7
--- /dev/null
+++ b/debian/missing-sources/README
@@ -0,0 +1,19 @@
+Missing sources
+---------------
+[ source-is-missing lintian tag ]
+
+Pointers to missing sources, copied in this directory:
+
+ * scripts/date.js
+
+     https://code.google.com/p/datejs/source/browse/
+
+ * scripts/html-sanitizer.js
+
+     https://code.google.com/p/google-caja/source/browse/
+     https://google-caja.googlecode.com/svn/trunk/src/com/google/caja/plugin/html-sanitizer.js
+
+ * scripts/prettify.js
+
+     https://code.google.com/p/google-code-prettify/source/browse/
+     https://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js
diff --git a/debian/missing-sources/scripts/core.js b/debian/missing-sources/scripts/core.js
new file mode 100644
index 0000000..f7e05d6
--- /dev/null
+++ b/debian/missing-sources/scripts/core.js
@@ -0,0 +1,865 @@
+/**
+ * @version: 1.0 Alpha-1
+ * @author: Coolite Inc. http://www.coolite.com/
+ * @date: 2008-04-13
+ * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * @website: http://www.datejs.com/
+ */
+ 
+(function () {
+    var $D = Date, 
+        $P = $D.prototype, 
+        $C = $D.CultureInfo,
+        p = function (s, l) {
+            if (!l) {
+                l = 2;
+            }
+            return ("000" + s).slice(l * -1);
+        };
+            
+    /**
+     * Resets the time of this Date object to 12:00 AM (00:00), which is the start of the day.
+     * @param {Boolean}  .clone() this date instance before clearing Time
+     * @return {Date}    this
+     */
+    $P.clearTime = function () {
+        this.setHours(0);
+        this.setMinutes(0);
+        this.setSeconds(0);
+        this.setMilliseconds(0);
+        return this;
+    };
+
+    /**
+     * Resets the time of this Date object to the current time ('now').
+     * @return {Date}    this
+     */
+    $P.setTimeToNow = function () {
+        var n = new Date();
+        this.setHours(n.getHours());
+        this.setMinutes(n.getMinutes());
+        this.setSeconds(n.getSeconds());
+        this.setMilliseconds(n.getMilliseconds());
+        return this;
+    };
+
+    /** 
+     * Gets a date that is set to the current date. The time is set to the start of the day (00:00 or 12:00 AM).
+     * @return {Date}    The current date.
+     */
+    $D.today = function () {
+        return new Date().clearTime();
+    };
+
+    /**
+     * Compares the first date to the second date and returns an number indication of their relative values.  
+     * @param {Date}     First Date object to compare [Required].
+     * @param {Date}     Second Date object to compare to [Required].
+     * @return {Number}  -1 = date1 is lessthan date2. 0 = values are equal. 1 = date1 is greaterthan date2.
+     */
+    $D.compare = function (date1, date2) {
+        if (isNaN(date1) || isNaN(date2)) { 
+            throw new Error(date1 + " - " + date2); 
+        } else if (date1 instanceof Date && date2 instanceof Date) {
+            return (date1 < date2) ? -1 : (date1 > date2) ? 1 : 0;
+        } else { 
+            throw new TypeError(date1 + " - " + date2); 
+        }
+    };
+    
+    /**
+     * Compares the first Date object to the second Date object and returns true if they are equal.  
+     * @param {Date}     First Date object to compare [Required]
+     * @param {Date}     Second Date object to compare to [Required]
+     * @return {Boolean} true if dates are equal. false if they are not equal.
+     */
+    $D.equals = function (date1, date2) { 
+        return (date1.compareTo(date2) === 0); 
+    };
+
+    /**
+     * Gets the day number (0-6) if given a CultureInfo specific string which is a valid dayName, abbreviatedDayName or shortestDayName (two char).
+     * @param {String}   The name of the day (eg. "Monday, "Mon", "tuesday", "tue", "We", "we").
+     * @return {Number}  The day number
+     */
+    $D.getDayNumberFromName = function (name) {
+        var n = $C.dayNames, m = $C.abbreviatedDayNames, o = $C.shortestDayNames, s = name.toLowerCase();
+        for (var i = 0; i < n.length; i++) { 
+            if (n[i].toLowerCase() == s || m[i].toLowerCase() == s || o[i].toLowerCase() == s) { 
+                return i; 
+            }
+        }
+        return -1;  
+    };
+    
+    /**
+     * Gets the month number (0-11) if given a Culture Info specific string which is a valid monthName or abbreviatedMonthName.
+     * @param {String}   The name of the month (eg. "February, "Feb", "october", "oct").
+     * @return {Number}  The day number
+     */
+    $D.getMonthNumberFromName = function (name) {
+        var n = $C.monthNames, m = $C.abbreviatedMonthNames, s = name.toLowerCase();
+        for (var i = 0; i < n.length; i++) {
+            if (n[i].toLowerCase() == s || m[i].toLowerCase() == s) { 
+                return i; 
+            }
+        }
+        return -1;
+    };
+
+    /**
+     * Determines if the current date instance is within a LeapYear.
+     * @param {Number}   The year.
+     * @return {Boolean} true if date is within a LeapYear, otherwise false.
+     */
+    $D.isLeapYear = function (year) { 
+        return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); 
+    };
+
+    /**
+     * Gets the number of days in the month, given a year and month value. Automatically corrects for LeapYear.
+     * @param {Number}   The year.
+     * @param {Number}   The month (0-11).
+     * @return {Number}  The number of days in the month.
+     */
+    $D.getDaysInMonth = function (year, month) {
+        return [31, ($D.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+    };
+ 
+    $D.getTimezoneAbbreviation = function (offset) {
+        var z = $C.timezones, p;
+        for (var i = 0; i < z.length; i++) {
+            if (z[i].offset === offset) {
+                return z[i].name;
+            }
+        }
+        return null;
+    };
+    
+    $D.getTimezoneOffset = function (name) {
+        var z = $C.timezones, p;
+        for (var i = 0; i < z.length; i++) {
+            if (z[i].name === name.toUpperCase()) {
+                return z[i].offset;
+            }
+        }
+        return null;
+    };
+
+    /**
+     * Returns a new Date object that is an exact date and time copy of the original instance.
+     * @return {Date}    A new Date instance
+     */
+    $P.clone = function () {
+        return new Date(this.getTime()); 
+    };
+
+    /**
+     * Compares this instance to a Date object and returns an number indication of their relative values.  
+     * @param {Date}     Date object to compare [Required]
+     * @return {Number}  -1 = this is lessthan date. 0 = values are equal. 1 = this is greaterthan date.
+     */
+    $P.compareTo = function (date) {
+        return Date.compare(this, date);
+    };
+
+    /**
+     * Compares this instance to another Date object and returns true if they are equal.  
+     * @param {Date}     Date object to compare. If no date to compare, new Date() [now] is used.
+     * @return {Boolean} true if dates are equal. false if they are not equal.
+     */
+    $P.equals = function (date) {
+        return Date.equals(this, date || new Date());
+    };
+
+    /**
+     * Determines if this instance is between a range of two dates or equal to either the start or end dates.
+     * @param {Date}     Start of range [Required]
+     * @param {Date}     End of range [Required]
+     * @return {Boolean} true is this is between or equal to the start and end dates, else false
+     */
+    $P.between = function (start, end) {
+        return this.getTime() >= start.getTime() && this.getTime() <= end.getTime();
+    };
+
+    /**
+     * Determines if this date occurs after the date to compare to.
+     * @param {Date}     Date object to compare. If no date to compare, new Date() ("now") is used.
+     * @return {Boolean} true if this date instance is greater than the date to compare to (or "now"), otherwise false.
+     */
+    $P.isAfter = function (date) {
+        return this.compareTo(date || new Date()) === 1;
+    };
+
+    /**
+     * Determines if this date occurs before the date to compare to.
+     * @param {Date}     Date object to compare. If no date to compare, new Date() ("now") is used.
+     * @return {Boolean} true if this date instance is less than the date to compare to (or "now").
+     */
+    $P.isBefore = function (date) {
+        return (this.compareTo(date || new Date()) === -1);
+    };
+
+    /**
+     * Determines if the current Date instance occurs today.
+     * @return {Boolean} true if this date instance is 'today', otherwise false.
+     */
+    
+    /**
+     * Determines if the current Date instance occurs on the same Date as the supplied 'date'. 
+     * If no 'date' to compare to is provided, the current Date instance is compared to 'today'. 
+     * @param {date}     Date object to compare. If no date to compare, the current Date ("now") is used.
+     * @return {Boolean} true if this Date instance occurs on the same Day as the supplied 'date'.
+     */
+    $P.isToday = $P.isSameDay = function (date) {
+        return this.clone().clearTime().equals((date || new Date()).clone().clearTime());
+    };
+    
+    /**
+     * Adds the specified number of milliseconds to this instance. 
+     * @param {Number}   The number of milliseconds to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addMilliseconds = function (value) {
+        this.setMilliseconds(this.getMilliseconds() + value * 1);
+        return this;
+    };
+
+    /**
+     * Adds the specified number of seconds to this instance. 
+     * @param {Number}   The number of seconds to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addSeconds = function (value) { 
+        return this.addMilliseconds(value * 1000); 
+    };
+
+    /**
+     * Adds the specified number of seconds to this instance. 
+     * @param {Number}   The number of seconds to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addMinutes = function (value) { 
+        return this.addMilliseconds(value * 60000); /* 60*1000 */
+    };
+
+    /**
+     * Adds the specified number of hours to this instance. 
+     * @param {Number}   The number of hours to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addHours = function (value) { 
+        return this.addMilliseconds(value * 3600000); /* 60*60*1000 */
+    };
+
+    /**
+     * Adds the specified number of days to this instance. 
+     * @param {Number}   The number of days to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addDays = function (value) {
+        this.setDate(this.getDate() + value * 1);
+        return this;
+    };
+
+    /**
+     * Adds the specified number of weeks to this instance. 
+     * @param {Number}   The number of weeks to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addWeeks = function (value) { 
+        return this.addDays(value * 7);
+    };
+
+    /**
+     * Adds the specified number of months to this instance. 
+     * @param {Number}   The number of months to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addMonths = function (value) {
+        var n = this.getDate();
+        this.setDate(1);
+        this.setMonth(this.getMonth() + value * 1);
+        this.setDate(Math.min(n, $D.getDaysInMonth(this.getFullYear(), this.getMonth())));
+        return this;
+    };
+
+    /**
+     * Adds the specified number of years to this instance. 
+     * @param {Number}   The number of years to add. The number can be positive or negative [Required]
+     * @return {Date}    this
+     */
+    $P.addYears = function (value) {
+        return this.addMonths(value * 12);
+    };
+
+    /**
+     * Adds (or subtracts) to the value of the years, months, weeks, days, hours, minutes, seconds, milliseconds of the date instance using given configuration object. Positive and Negative values allowed.
+     * Example
+    <pre><code>
+    Date.today().add( { days: 1, months: 1 } )
+     
+    new Date().add( { years: -1 } )
+    </code></pre> 
+     * @param {Object}   Configuration object containing attributes (months, days, etc.)
+     * @return {Date}    this
+     */
+    $P.add = function (config) {
+        if (typeof config == "number") {
+            this._orient = config;
+            return this;    
+        }
+        
+        var x = config;
+        
+        if (x.milliseconds) { 
+            this.addMilliseconds(x.milliseconds); 
+        }
+        if (x.seconds) { 
+            this.addSeconds(x.seconds); 
+        }
+        if (x.minutes) { 
+            this.addMinutes(x.minutes); 
+        }
+        if (x.hours) { 
+            this.addHours(x.hours); 
+        }
+        if (x.weeks) { 
+            this.addWeeks(x.weeks); 
+        }    
+        if (x.months) { 
+            this.addMonths(x.months); 
+        }
+        if (x.years) { 
+            this.addYears(x.years); 
+        }
+        if (x.days) {
+            this.addDays(x.days); 
+        }
+        return this;
+    };
+    
+    var $y, $m, $d;
+    
+    /**
+     * Get the week number. Week one (1) is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
+     * This algorithm is a JavaScript port of the work presented by Claus T�ndering at http://www.tondering.dk/claus/cal/node8.html#SECTION00880000000000000000
+     * .getWeek() Algorithm Copyright (c) 2008 Claus Tondering.
+     * The .getWeek() function does NOT convert the date to UTC. The local datetime is used. Please use .getISOWeek() to get the week of the UTC converted date.
+     * @return {Number}  1 to 53
+     */
+    $P.getWeek = function () {
+        var a, b, c, d, e, f, g, n, s, w;
+        
+        $y = (!$y) ? this.getFullYear() : $y;
+        $m = (!$m) ? this.getMonth() + 1 : $m;
+        $d = (!$d) ? this.getDate() : $d;
+
+        if ($m <= 2) {
+            a = $y - 1;
+            b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
+            c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
+            s = b - c;
+            e = 0;
+            f = $d - 1 + (31 * ($m - 1));
+        } else {
+            a = $y;
+            b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
+            c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
+            s = b - c;
+            e = s + 1;
+            f = $d + ((153 * ($m - 3) + 2) / 5) + 58 + s;
+        }
+        
+        g = (a + b) % 7;
+        d = (f + g - e) % 7;
+        n = (f + 3 - d) | 0;
+
+        if (n < 0) {
+            w = 53 - ((g - s) / 5 | 0);
+        } else if (n > 364 + s) {
+            w = 1;
+        } else {
+            w = (n / 7 | 0) + 1;
+        }
+        
+        $y = $m = $d = null;
+        
+        return w;
+    };
+    
+    /**
+     * Get the ISO 8601 week number. Week one ("01") is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
+     * The .getISOWeek() function does convert the date to it's UTC value. Please use .getWeek() to get the week of the local date.
+     * @return {String}  "01" to "53"
+     */
+    $P.getISOWeek = function () {
+        $y = this.getUTCFullYear();
+        $m = this.getUTCMonth() + 1;
+        $d = this.getUTCDate();
+        return p(this.getWeek());
+    };
+
+    /**
+     * Moves the date to Monday of the week set. Week one (1) is the week which contains the first Thursday of the year.
+     * @param {Number}   A Number (1 to 53) that represents the week of the year.
+     * @return {Date}    this
+     */    
+    $P.setWeek = function (n) {
+        return this.moveToDayOfWeek(1).addWeeks(n - this.getWeek());
+    };
+
+    // private
+    var validate = function (n, min, max, name) {
+        if (typeof n == "undefined") {
+            return false;
+        } else if (typeof n != "number") {
+            throw new TypeError(n + " is not a Number."); 
+        } else if (n < min || n > max) {
+            throw new RangeError(n + " is not a valid value for " + name + "."); 
+        }
+        return true;
+    };
+
+    /**
+     * Validates the number is within an acceptable range for milliseconds [0-999].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateMillisecond = function (value) {
+        return validate(value, 0, 999, "millisecond");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for seconds [0-59].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateSecond = function (value) {
+        return validate(value, 0, 59, "second");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for minutes [0-59].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateMinute = function (value) {
+        return validate(value, 0, 59, "minute");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for hours [0-23].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateHour = function (value) {
+        return validate(value, 0, 23, "hour");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for the days in a month [0-MaxDaysInMonth].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateDay = function (value, year, month) {
+        return validate(value, 1, $D.getDaysInMonth(year, month), "day");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for months [0-11].
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateMonth = function (value) {
+        return validate(value, 0, 11, "month");
+    };
+
+    /**
+     * Validates the number is within an acceptable range for years.
+     * @param {Number}   The number to check if within range.
+     * @return {Boolean} true if within range, otherwise false.
+     */
+    $D.validateYear = function (value) {
+        return validate(value, 0, 9999, "year");
+    };
+
+    /**
+     * Set the value of year, month, day, hour, minute, second, millisecond of date instance using given configuration object.
+     * Example
+    <pre><code>
+    Date.today().set( { day: 20, month: 1 } )
+
+    new Date().set( { millisecond: 0 } )
+    </code></pre>
+     * 
+     * @param {Object}   Configuration object containing attributes (month, day, etc.)
+     * @return {Date}    this
+     */
+    $P.set = function (config) {
+        if ($D.validateMillisecond(config.millisecond)) {
+            this.addMilliseconds(config.millisecond - this.getMilliseconds()); 
+        }
+        
+        if ($D.validateSecond(config.second)) {
+            this.addSeconds(config.second - this.getSeconds()); 
+        }
+        
+        if ($D.validateMinute(config.minute)) {
+            this.addMinutes(config.minute - this.getMinutes()); 
+        }
+        
+        if ($D.validateHour(config.hour)) {
+            this.addHours(config.hour - this.getHours()); 
+        }
+        
+        if ($D.validateMonth(config.month)) {
+            this.addMonths(config.month - this.getMonth()); 
+        }
+
+        if ($D.validateYear(config.year)) {
+            this.addYears(config.year - this.getFullYear()); 
+        }
+        
+	    /* day has to go last because you can't validate the day without first knowing the month */
+        if ($D.validateDay(config.day, this.getFullYear(), this.getMonth())) {
+            this.addDays(config.day - this.getDate()); 
+        }
+        
+        if (config.timezone) { 
+            this.setTimezone(config.timezone); 
+        }
+        
+        if (config.timezoneOffset) { 
+            this.setTimezoneOffset(config.timezoneOffset); 
+        }
+
+        if (config.week && validate(config.week, 0, 53, "week")) {
+            this.setWeek(config.week);
+        }
+        
+        return this;   
+    };
+
+    /**
+     * Moves the date to the first day of the month.
+     * @return {Date}    this
+     */
+    $P.moveToFirstDayOfMonth = function () {
+        return this.set({ day: 1 });
+    };
+
+    /**
+     * Moves the date to the last day of the month.
+     * @return {Date}    this
+     */
+    $P.moveToLastDayOfMonth = function () { 
+        return this.set({ day: $D.getDaysInMonth(this.getFullYear(), this.getMonth())});
+    };
+
+    /**
+     * Moves the date to the next n'th occurrence of the dayOfWeek starting from the beginning of the month. The number (-1) is a magic number and will return the last occurrence of the dayOfWeek in the month.
+     * @param {Number}   The dayOfWeek to move to
+     * @param {Number}   The n'th occurrence to move to. Use (-1) to return the last occurrence in the month
+     * @return {Date}    this
+     */
+    $P.moveToNthOccurrence = function (dayOfWeek, occurrence) {
+        var shift = 0;
+        if (occurrence > 0) {
+            shift = occurrence - 1;
+        }
+        else if (occurrence === -1) {
+            this.moveToLastDayOfMonth();
+            if (this.getDay() !== dayOfWeek) {
+                this.moveToDayOfWeek(dayOfWeek, -1);
+            }
+            return this;
+        }
+        return this.moveToFirstDayOfMonth().addDays(-1).moveToDayOfWeek(dayOfWeek, +1).addWeeks(shift);
+    };
+
+    /**
+     * Move to the next or last dayOfWeek based on the orient value.
+     * @param {Number}   The dayOfWeek to move to
+     * @param {Number}   Forward (+1) or Back (-1). Defaults to +1. [Optional]
+     * @return {Date}    this
+     */
+    $P.moveToDayOfWeek = function (dayOfWeek, orient) {
+        var diff = (dayOfWeek - this.getDay() + 7 * (orient || +1)) % 7;
+        return this.addDays((diff === 0) ? diff += 7 * (orient || +1) : diff);
+    };
+
+    /**
+     * Move to the next or last month based on the orient value.
+     * @param {Number}   The month to move to. 0 = January, 11 = December
+     * @param {Number}   Forward (+1) or Back (-1). Defaults to +1. [Optional]
+     * @return {Date}    this
+     */
+    $P.moveToMonth = function (month, orient) {
+        var diff = (month - this.getMonth() + 12 * (orient || +1)) % 12;
+        return this.addMonths((diff === 0) ? diff += 12 * (orient || +1) : diff);
+    };
+
+    /**
+     * Get the Ordinal day (numeric day number) of the year, adjusted for leap year.
+     * @return {Number} 1 through 365 (366 in leap years)
+     */
+    $P.getOrdinalNumber = function () {
+        return Math.ceil((this.clone().clearTime() - new Date(this.getFullYear(), 0, 1)) / 86400000) + 1;
+    };
+
+    /**
+     * Get the time zone abbreviation of the current date.
+     * @return {String} The abbreviated time zone name (e.g. "EST")
+     */
+    $P.getTimezone = function () {
+        return $D.getTimezoneAbbreviation(this.getUTCOffset());
+    };
+
+    $P.setTimezoneOffset = function (offset) {
+        var here = this.getTimezoneOffset(), there = Number(offset) * -6 / 10;
+        return this.addMinutes(there - here); 
+    };
+
+    $P.setTimezone = function (offset) { 
+        return this.setTimezoneOffset($D.getTimezoneOffset(offset)); 
+    };
+
+    /**
+     * Indicates whether Daylight Saving Time is observed in the current time zone.
+     * @return {Boolean} true|false
+     */
+    $P.hasDaylightSavingTime = function () { 
+        return (Date.today().set({month: 0, day: 1}).getTimezoneOffset() !== Date.today().set({month: 6, day: 1}).getTimezoneOffset());
+    };
+    
+    /**
+     * Indicates whether this Date instance is within the Daylight Saving Time range for the current time zone.
+     * @return {Boolean} true|false
+     */
+    $P.isDaylightSavingTime = function () {
+        return Date.today().set({month: 0, day: 1}).getTimezoneOffset() != this.getTimezoneOffset();
+    };
+
+    /**
+     * Get the offset from UTC of the current date.
+     * @return {String} The 4-character offset string prefixed with + or - (e.g. "-0500")
+     */
+    $P.getUTCOffset = function () {
+        var n = this.getTimezoneOffset() * -10 / 6, r;
+        if (n < 0) { 
+            r = (n - 10000).toString(); 
+            return r.charAt(0) + r.substr(2); 
+        } else { 
+            r = (n + 10000).toString();  
+            return "+" + r.substr(1); 
+        }
+    };
+
+    /**
+     * Returns the number of milliseconds between this date and date.
+     * @param {Date} Defaults to now
+     * @return {Number} The diff in milliseconds
+     */
+    $P.getElapsed = function (date) {
+        return (date || new Date()) - this;
+    };
+
+    if (!$P.toISOString) {
+        /**
+         * Converts the current date instance into a string with an ISO 8601 format. The date is converted to it's UTC value.
+         * @return {String}  ISO 8601 string of date
+         */
+        $P.toISOString = function () {
+            // From http://www.json.org/json.js. Public Domain. 
+            function f(n) {
+                return n < 10 ? '0' + n : n;
+            }
+
+            return '"' + this.getUTCFullYear()   + '-' +
+                f(this.getUTCMonth() + 1) + '-' +
+                f(this.getUTCDate())      + 'T' +
+                f(this.getUTCHours())     + ':' +
+                f(this.getUTCMinutes())   + ':' +
+                f(this.getUTCSeconds())   + 'Z"';
+        };
+    }
+    
+    // private
+    $P._toString = $P.toString;
+
+    /**
+     * Converts the value of the current Date object to its equivalent string representation.
+     * Format Specifiers
+    <pre>
+    CUSTOM DATE AND TIME FORMAT STRINGS
+    Format  Description                                                                  Example
+    ------  ---------------------------------------------------------------------------  -----------------------
+     s      The seconds of the minute between 0-59.                                      "0" to "59"
+     ss     The seconds of the minute with leading zero if required.                     "00" to "59"
+     
+     m      The minute of the hour between 0-59.                                         "0"  or "59"
+     mm     The minute of the hour with leading zero if required.                        "00" or "59"
+     
+     h      The hour of the day between 1-12.                                            "1"  to "12"
+     hh     The hour of the day with leading zero if required.                           "01" to "12"
+     
+     H      The hour of the day between 0-23.                                            "0"  to "23"
+     HH     The hour of the day with leading zero if required.                           "00" to "23"
+     
+     d      The day of the month between 1 and 31.                                       "1"  to "31"
+     dd     The day of the month with leading zero if required.                          "01" to "31"
+     ddd    Abbreviated day name. $C.abbreviatedDayNames.                                "Mon" to "Sun" 
+     dddd   The full day name. $C.dayNames.                                              "Monday" to "Sunday"
+     
+     M      The month of the year between 1-12.                                          "1" to "12"
+     MM     The month of the year with leading zero if required.                         "01" to "12"
+     MMM    Abbreviated month name. $C.abbreviatedMonthNames.                            "Jan" to "Dec"
+     MMMM   The full month name. $C.monthNames.                                          "January" to "December"
+
+     yy     The year as a two-digit number.                                              "99" or "08"
+     yyyy   The full four digit year.                                                    "1999" or "2008"
+     
+     t      Displays the first character of the A.M./P.M. designator.                    "A" or "P"
+            $C.amDesignator or $C.pmDesignator
+     tt     Displays the A.M./P.M. designator.                                           "AM" or "PM"
+            $C.amDesignator or $C.pmDesignator
+     
+     S      The ordinal suffix ("st, "nd", "rd" or "th") of the current day.            "st, "nd", "rd" or "th"
+
+|| *Format* || *Description* || *Example* ||
+|| d      || The CultureInfo shortDate Format Pattern                                     || "M/d/yyyy" ||
+|| D      || The CultureInfo longDate Format Pattern                                      || "dddd, MMMM dd, yyyy" ||
+|| F      || The CultureInfo fullDateTime Format Pattern                                  || "dddd, MMMM dd, yyyy h:mm:ss tt" ||
+|| m      || The CultureInfo monthDay Format Pattern                                      || "MMMM dd" ||
+|| r      || The CultureInfo rfc1123 Format Pattern                                       || "ddd, dd MMM yyyy HH:mm:ss GMT" ||
+|| s      || The CultureInfo sortableDateTime Format Pattern                              || "yyyy-MM-ddTHH:mm:ss" ||
+|| t      || The CultureInfo shortTime Format Pattern                                     || "h:mm tt" ||
+|| T      || The CultureInfo longTime Format Pattern                                      || "h:mm:ss tt" ||
+|| u      || The CultureInfo universalSortableDateTime Format Pattern                     || "yyyy-MM-dd HH:mm:ssZ" ||
+|| y      || The CultureInfo yearMonth Format Pattern                                     || "MMMM, yyyy" ||
+     
+
+    STANDARD DATE AND TIME FORMAT STRINGS
+    Format  Description                                                                  Example ("en-US")
+    ------  ---------------------------------------------------------------------------  -----------------------
+     d      The CultureInfo shortDate Format Pattern                                     "M/d/yyyy"
+     D      The CultureInfo longDate Format Pattern                                      "dddd, MMMM dd, yyyy"
+     F      The CultureInfo fullDateTime Format Pattern                                  "dddd, MMMM dd, yyyy h:mm:ss tt"
+     m      The CultureInfo monthDay Format Pattern                                      "MMMM dd"
+     r      The CultureInfo rfc1123 Format Pattern                                       "ddd, dd MMM yyyy HH:mm:ss GMT"
+     s      The CultureInfo sortableDateTime Format Pattern                              "yyyy-MM-ddTHH:mm:ss"
+     t      The CultureInfo shortTime Format Pattern                                     "h:mm tt"
+     T      The CultureInfo longTime Format Pattern                                      "h:mm:ss tt"
+     u      The CultureInfo universalSortableDateTime Format Pattern                     "yyyy-MM-dd HH:mm:ssZ"
+     y      The CultureInfo yearMonth Format Pattern                                     "MMMM, yyyy"
+    </pre>
+     * @param {String}   A format string consisting of one or more format spcifiers [Optional].
+     * @return {String}  A string representation of the current Date object.
+     */
+    $P.toString = function (format) {
+        var x = this;
+        
+        // Standard Date and Time Format Strings. Formats pulled from CultureInfo file and
+        // may vary by culture. 
+        if (format && format.length == 1) {
+            var c = $C.formatPatterns;
+            x.t = x.toString;
+            switch (format) {
+            case "d": 
+                return x.t(c.shortDate);
+            case "D":
+                return x.t(c.longDate);
+            case "F":
+                return x.t(c.fullDateTime);
+            case "m":
+                return x.t(c.monthDay);
+            case "r":
+                return x.t(c.rfc1123);
+            case "s":
+                return x.t(c.sortableDateTime);
+            case "t":
+                return x.t(c.shortTime);
+            case "T":
+                return x.t(c.longTime);
+            case "u":
+                return x.t(c.universalSortableDateTime);
+            case "y":
+                return x.t(c.yearMonth);
+            }    
+        }
+        
+        var ord = function (n) {
+                switch (n * 1) {
+                case 1: 
+                case 21: 
+                case 31: 
+                    return "st";
+                case 2: 
+                case 22: 
+                    return "nd";
+                case 3: 
+                case 23: 
+                    return "rd";
+                default: 
+                    return "th";
+                }
+            };
+        
+        return format ? format.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S)/g, 
+        function (m) {
+            if (m.charAt(0) === "\\") {
+                return m.replace("\\", "");
+            }
+            x.h = x.getHours;
+            switch (m) {
+            case "hh":
+                return p(x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12));
+            case "h":
+                return x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : (x.h() - 12);
+            case "HH":
+                return p(x.h());
+            case "H":
+                return x.h();
+            case "mm":
+                return p(x.getMinutes());
+            case "m":
+                return x.getMinutes();
+            case "ss":
+                return p(x.getSeconds());
+            case "s":
+                return x.getSeconds();
+            case "yyyy":
+                return p(x.getFullYear(), 4);
+            case "yy":
+                return p(x.getFullYear());
+            case "dddd":
+                return $C.dayNames[x.getDay()];
+            case "ddd":
+                return $C.abbreviatedDayNames[x.getDay()];
+            case "dd":
+                return p(x.getDate());
+            case "d":
+                return x.getDate();
+            case "MMMM":
+                return $C.monthNames[x.getMonth()];
+            case "MMM":
+                return $C.abbreviatedMonthNames[x.getMonth()];
+            case "MM":
+                return p((x.getMonth() + 1));
+            case "M":
+                return x.getMonth() + 1;
+            case "t":
+                return x.h() < 12 ? $C.amDesignator.substring(0, 1) : $C.pmDesignator.substring(0, 1);
+            case "tt":
+                return x.h() < 12 ? $C.amDesignator : $C.pmDesignator;
+            case "S":
+                return ord(x.getDate());
+            default: 
+                return m;
+            }
+        }
+        ) : this._toString();
+    };
+}());    
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/date.js b/debian/missing-sources/scripts/date.js
new file mode 100644
index 0000000..eda4501
--- /dev/null
+++ b/debian/missing-sources/scripts/date.js
@@ -0,0 +1,21 @@
+/**
+ * Please include a date.js file from the /build/ folder.
+ * 
+ * Individual date.js files have been compiled for each of the 150+ supported Cultures.
+ * 
+ * Example
+ *     date.js       // English (United States)
+ *     date-en-US.js // English (United States)
+ *     date-de-DE.js // Deutsch (Deutschland)
+ *     date-es-MX.js // fran�ais (France)
+ */
+
+alert(
+    "Please include a date.js file from the /build/ folder.\n\n" +
+    "Individual date.js files have been compiled for each of the 150+ supported Cultures.\n\n" + 
+    "Example\n" + 
+    "    date.js       // English (United States)\n" + 
+    "    date-en-US.js // English (United States)\n" + 
+    "    date-de-DE.js // Deutsch (Deutschland)\n" + 
+    "    date-es-MX.js // fran�ais (France)\n"
+    );
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/extras.js b/debian/missing-sources/scripts/extras.js
new file mode 100644
index 0000000..595e488
--- /dev/null
+++ b/debian/missing-sources/scripts/extras.js
@@ -0,0 +1,332 @@
+/**
+ * @version: 1.0 Alpha-1
+ * @author: Coolite Inc. http://www.coolite.com/
+ * @date: 2008-04-13
+ * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * @website: http://www.datejs.com/
+ */
+ 
+(function () {
+    var $D = Date, 
+        $P = $D.prototype, 
+        $C = $D.CultureInfo,
+        $f = [],
+        p = function (s, l) {
+            if (!l) {
+                l = 2;
+            }
+            return ("000" + s).slice(l * -1);
+        };
+
+    /**
+     * Converts a PHP format string to Java/.NET format string. 
+     * A PHP format string can be used with .$format or .format.
+     * A Java/.NET format string can be used with .toString().
+     * The .parseExact function will only accept a Java/.NET format string
+     *
+     * Example
+     <pre>
+     var f1 = "%m/%d/%y"
+     var f2 = Date.normalizeFormat(f1); // "MM/dd/yy"
+     
+     new Date().format(f1);    // "04/13/08"
+     new Date().$format(f1);   // "04/13/08"
+     new Date().toString(f2);  // "04/13/08"
+     
+     var date = Date.parseExact("04/13/08", f2); // Sun Apr 13 2008
+     </pre>
+     * @param {String}   A PHP format string consisting of one or more format spcifiers.
+     * @return {String}  The PHP format converted to a Java/.NET format string.
+     */        
+    $D.normalizeFormat = function (format) {
+        $f = [];
+        var t = new Date().$format(format);
+        return $f.join("");
+    };
+
+    /**
+     * Format a local Unix timestamp according to locale settings
+     * 
+     * Example
+     <pre>
+     Date.strftime("%m/%d/%y", new Date());       // "04/13/08"
+     Date.strftime("c", "2008-04-13T17:52:03Z");  // "04/13/08"
+     </pre>
+     * @param {String}   A format string consisting of one or more format spcifiers [Optional].
+     * @param {Number}   The number representing the number of seconds that have elapsed since January 1, 1970 (local time). 
+     * @return {String}  A string representation of the current Date object.
+     */
+    $D.strftime = function (format, time) {
+        return new Date(time * 1000).$format(format);
+    };
+
+    /**
+     * Parse any textual datetime description into a Unix timestamp. 
+     * A Unix timestamp is the number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT).
+     * 
+     * Example
+     <pre>
+     Date.strtotime("04/13/08");              // 1208044800
+     Date.strtotime("1970-01-01T00:00:00Z");  // 0
+     </pre>
+     * @param {String}   A format string consisting of one or more format spcifiers [Optional].
+     * @param {Object}   A string or date object.
+     * @return {String}  A string representation of the current Date object.
+     */
+    $D.strtotime = function (time) {
+        var d = $D.parse(time);
+        d.addMinutes(d.getTimezoneOffset() * -1);
+        return Math.round($D.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds()) / 1000);
+    };
+
+    /**
+     * Converts the value of the current Date object to its equivalent string representation using a PHP/Unix style of date format specifiers.
+     *
+     * The following descriptions are from http://www.php.net/strftime and http://www.php.net/manual/en/function.date.php. 
+     * Copyright � 2001-2008 The PHP Group
+     * 
+     * Format Specifiers
+     <pre>
+    Format  Description                                                                  Example
+    ------  ---------------------------------------------------------------------------  -----------------------
+     %a     abbreviated weekday name according to the current localed                    "Mon" through "Sun"
+     %A     full weekday name according to the current locale                            "Sunday" through "Saturday"
+     %b     abbreviated month name according to the current locale                       "Jan" through "Dec"
+     %B     full month name according to the current locale                              "January" through "December"
+     %c     preferred date and time representation for the current locale                "4/13/2008 12:33 PM"
+     %C     century number (the year divided by 100 and truncated to an integer)         "00" to "99"
+     %d     day of the month as a decimal number                                         "01" to "31"
+     %D     same as %m/%d/%y                                                             "04/13/08"
+     %e     day of the month as a decimal number, a single digit is preceded by a space  "1" to "31"
+     %g     like %G, but without the century                                             "08"
+     %G     The 4-digit year corresponding to the ISO week number (see %V).              "2008"
+            This has the same format and value as %Y, except that if the ISO week number 
+            belongs to the previous or next year, that year is used instead.
+     %h     same as %b                                                                   "Jan" through "Dec"
+     %H     hour as a decimal number using a 24-hour clock                               "00" to "23"
+     %I     hour as a decimal number using a 12-hour clock                               "01" to "12"
+     %j     day of the year as a decimal number                                          "001" to "366"
+     %m     month as a decimal number                                                    "01" to "12"
+     %M     minute as a decimal number                                                   "00" to "59"
+     %n     newline character                                                            "\n"
+     %p     either "am" or "pm" according to the given time value, or the                "am" or "pm"
+            corresponding strings for the current locale
+     %r     time in a.m. and p.m. notation                                               "8:44 PM"
+     %R     time in 24 hour notation                                                     "20:44"
+     %S     second as a decimal number                                                   "00" to "59"
+     %t     tab character                                                                "\t"
+     %T     current time, equal to %H:%M:%S                                              "12:49:11"
+     %u     weekday as a decimal number ["1", "7"], with "1" representing Monday         "1" to "7"
+     %U     week number of the current year as a decimal number, starting with the       "0" to ("52" or "53")
+            first Sunday as the first day of the first week
+     %V     The ISO 8601:1988 week number of the current year as a decimal number,       "00" to ("52" or "53")
+            range 01 to 53, where week 1 is the first week that has at least 4 days 
+            in the current year, and with Monday as the first day of the week. 
+            (Use %G or %g for the year component that corresponds to the week number 
+            for the specified timestamp.)
+     %W     week number of the current year as a decimal number, starting with the       "00" to ("52" or "53")
+            first Monday as the first day of the first week
+     %w     day of the week as a decimal, Sunday being "0"                               "0" to "6"
+     %x     preferred date representation for the current locale without the time        "4/13/2008"
+     %X     preferred time representation for the current locale without the date        "12:53:05"
+     %y     year as a decimal number without a century                                   "00" "99"
+     %Y     year as a decimal number including the century                               "2008"
+     %Z     time zone or name or abbreviation                                            "UTC", "EST", "PST"
+     %z     same as %Z 
+     %%     a literal "%" character                                                      "%"
+      
+     d      Day of the month, 2 digits with leading zeros                                "01" to "31"
+     D      A textual representation of a day, three letters                             "Mon" through "Sun"
+     j      Day of the month without leading zeros                                       "1" to "31"
+     l      A full textual representation of the day of the week (lowercase "L")         "Sunday" through "Saturday"
+     N      ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)  "1" (for Monday) through "7" (for Sunday)
+     S      English ordinal suffix for the day of the month, 2 characters                "st", "nd", "rd" or "th". Works well with j
+     w      Numeric representation of the day of the week                                "0" (for Sunday) through "6" (for Saturday)
+     z      The day of the year (starting from "0")                                      "0" through "365"      
+     W      ISO-8601 week number of year, weeks starting on Monday                       "00" to ("52" or "53")
+     F      A full textual representation of a month, such as January or March           "January" through "December"
+     m      Numeric representation of a month, with leading zeros                        "01" through "12"
+     M      A short textual representation of a month, three letters                     "Jan" through "Dec"
+     n      Numeric representation of a month, without leading zeros                     "1" through "12"
+     t      Number of days in the given month                                            "28" through "31"
+     L      Whether it's a leap year                                                     "1" if it is a leap year, "0" otherwise
+     o      ISO-8601 year number. This has the same value as Y, except that if the       "2008"
+            ISO week number (W) belongs to the previous or next year, that year 
+            is used instead.
+     Y      A full numeric representation of a year, 4 digits                            "2008"
+     y      A two digit representation of a year                                         "08"
+     a      Lowercase Ante meridiem and Post meridiem                                    "am" or "pm"
+     A      Uppercase Ante meridiem and Post meridiem                                    "AM" or "PM"
+     B      Swatch Internet time                                                         "000" through "999"
+     g      12-hour format of an hour without leading zeros                              "1" through "12"
+     G      24-hour format of an hour without leading zeros                              "0" through "23"
+     h      12-hour format of an hour with leading zeros                                 "01" through "12"
+     H      24-hour format of an hour with leading zeros                                 "00" through "23"
+     i      Minutes with leading zeros                                                   "00" to "59"
+     s      Seconds, with leading zeros                                                  "00" through "59"
+     u      Milliseconds                                                                 "54321"
+     e      Timezone identifier                                                          "UTC", "EST", "PST"
+     I      Whether or not the date is in daylight saving time (uppercase i)             "1" if Daylight Saving Time, "0" otherwise
+     O      Difference to Greenwich time (GMT) in hours                                  "+0200", "-0600"
+     P      Difference to Greenwich time (GMT) with colon between hours and minutes      "+02:00", "-06:00"
+     T      Timezone abbreviation                                                        "UTC", "EST", "PST"
+     Z      Timezone offset in seconds. The offset for timezones west of UTC is          "-43200" through "50400"
+            always negative, and for those east of UTC is always positive.
+     c      ISO 8601 date                                                                "2004-02-12T15:19:21+00:00"
+     r      RFC 2822 formatted date                                                      "Thu, 21 Dec 2000 16:01:07 +0200"
+     U      Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)                   "0"     
+     </pre>
+     * @param {String}   A format string consisting of one or more format spcifiers [Optional].
+     * @return {String}  A string representation of the current Date object.
+     */
+    $P.$format = function (format) { 
+        var x = this, 
+            y,
+            t = function (v) {
+                $f.push(v);
+                return x.toString(v);
+            };
+
+        return format ? format.replace(/(%|\\)?.|%%/g, 
+        function (m) {
+            if (m.charAt(0) === "\\" || m.substring(0, 2) === "%%") {
+                return m.replace("\\", "").replace("%%", "%");
+            }
+            switch (m) {
+            case "d":
+            case "%d":
+                return t("dd");
+            case "D":
+            case "%a":
+                return t("ddd");
+            case "j":
+            case "%e":
+                return t("d");
+            case "l":
+            case "%A":
+                return t("dddd");
+            case "N":
+            case "%u":
+                return x.getDay() + 1;
+            case "S":
+                return t("S");
+            case "w":
+            case "%w":
+                return x.getDay();
+            case "z":
+                return x.getOrdinalNumber();
+            case "%j":
+                return p(x.getOrdinalNumber(), 3);
+            case "%U":
+                var d1 = x.clone().set({month: 0, day: 1}).addDays(-1).moveToDayOfWeek(0),
+                    d2 = x.clone().addDays(1).moveToDayOfWeek(0, -1);
+                return (d2 < d1) ? "00" : p((d2.getOrdinalNumber() - d1.getOrdinalNumber()) / 7 + 1);                
+            case "W":
+            case "%V":
+                return x.getISOWeek();
+            case "%W":
+                return p(x.getWeek());
+            case "F":
+            case "%B":
+                return t("MMMM");
+            case "m":
+            case "%m":
+                return t("MM");
+            case "M":
+            case "%b":
+            case "%h":
+                return t("MMM");
+            case "n":
+                return t("M");
+            case "t":
+                return $D.getDaysInMonth(x.getFullYear(), x.getMonth());
+            case "L":
+                return ($D.isLeapYear(x.getFullYear())) ? 1 : 0;
+            case "o":
+            case "%G":
+                return x.setWeek(x.getISOWeek()).toString("yyyy");
+            case "%g":
+                return x.$format("%G").slice(-2);
+            case "Y":
+            case "%Y":
+                return t("yyyy");
+            case "y":
+            case "%y":
+                return t("yy");
+            case "a":
+            case "%p":
+                return t("tt").toLowerCase();
+            case "A":
+                return t("tt").toUpperCase();
+            case "g":
+            case "%I":
+                return t("h");
+            case "G":
+                return t("H");
+            case "h":
+                return t("hh");
+            case "H":
+            case "%H":
+                return t("HH");
+            case "i":
+            case "%M":
+                return t("mm");
+            case "s":
+            case "%S":
+                return t("ss");
+            case "u":
+                return p(x.getMilliseconds(), 3);
+            case "I":
+                return (x.isDaylightSavingTime()) ? 1 : 0;
+            case "O":
+                return x.getUTCOffset();
+            case "P":
+                y = x.getUTCOffset();
+                return y.substring(0, y.length - 2) + ":" + y.substring(y.length - 2);
+            case "e":
+            case "T":
+            case "%z":
+            case "%Z":            
+                return x.getTimezone();
+            case "Z":
+                return x.getTimezoneOffset() * -60;
+            case "B":
+                var now = new Date();
+                return Math.floor(((now.getHours() * 3600) + (now.getMinutes() * 60) + now.getSeconds() + (now.getTimezoneOffset() + 60) * 60) / 86.4);
+            case "c":
+                return x.toISOString().replace(/\"/g, "");
+            case "U":
+                return $D.strtotime("now");
+            case "%c":
+                return t("d") + " " + t("t");
+            case "%C":
+                return Math.floor(x.getFullYear() / 100 + 1);
+            case "%D":
+                return t("MM/dd/yy");
+            case "%n":
+                return "\\n";
+            case "%t":
+                return "\\t";
+            case "%r":
+                return t("hh:mm tt");                
+            case "%R":
+                return t("H:mm");
+            case "%T":
+                return t("H:mm:ss");
+            case "%x":
+                return t("d");
+            case "%X":
+                return t("t");
+            default:
+                $f.push(m);
+			    return m;
+            }
+        }
+        ) : this._toString();
+    };
+    
+    if (!$P.format) {
+        $P.format = $P.$format;
+    }
+}());    
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/globalization/en-US.js b/debian/missing-sources/scripts/globalization/en-US.js
new file mode 100644
index 0000000..190edc9
--- /dev/null
+++ b/debian/missing-sources/scripts/globalization/en-US.js
@@ -0,0 +1,195 @@
+Date.CultureInfo = {
+	/* Culture Name */
+    name: "en-US",
+    englishName: "English (United States)",
+    nativeName: "English (United States)",
+    
+    /* Day Name Strings */
+    dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+    abbreviatedDayNames: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+    shortestDayNames: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
+    firstLetterDayNames: ["S", "M", "T", "W", "T", "F", "S"],
+    
+    /* Month Name Strings */
+    monthNames: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+    abbreviatedMonthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+
+	/* AM/PM Designators */
+    amDesignator: "AM",
+    pmDesignator: "PM",
+
+    firstDayOfWeek: 0,
+    twoDigitYearMax: 2029,
+    
+    /**
+     * The dateElementOrder is based on the order of the 
+     * format specifiers in the formatPatterns.DatePattern. 
+     *
+     * Example:
+     <pre>
+     shortDatePattern    dateElementOrder
+     ------------------  ---------------- 
+     "M/d/yyyy"          "mdy"
+     "dd/MM/yyyy"        "dmy"
+     "yyyy-MM-dd"        "ymd"
+     </pre>
+     *
+     * The correct dateElementOrder is required by the parser to
+     * determine the expected order of the date elements in the
+     * string being parsed.
+     */
+    dateElementOrder: "mdy",
+    
+    /* Standard date and time format patterns */
+    formatPatterns: {
+        shortDate: "M/d/yyyy",
+        longDate: "dddd, MMMM dd, yyyy",
+        shortTime: "h:mm tt",
+        longTime: "h:mm:ss tt",
+        fullDateTime: "dddd, MMMM dd, yyyy h:mm:ss tt",
+        sortableDateTime: "yyyy-MM-ddTHH:mm:ss",
+        universalSortableDateTime: "yyyy-MM-dd HH:mm:ssZ",
+        rfc1123: "ddd, dd MMM yyyy HH:mm:ss GMT",
+        monthDay: "MMMM dd",
+        yearMonth: "MMMM, yyyy"
+    },
+
+    /**
+     * NOTE: If a string format is not parsing correctly, but
+     * you would expect it parse, the problem likely lies below. 
+     * 
+     * The following regex patterns control most of the string matching
+     * within the parser.
+     * 
+     * The Month name and Day name patterns were automatically generated
+     * and in general should be (mostly) correct. 
+     *
+     * Beyond the month and day name patterns are natural language strings.
+     * Example: "next", "today", "months"
+     *
+     * These natural language string may NOT be correct for this culture. 
+     * If they are not correct, please translate and edit this file
+     * providing the correct regular expression pattern. 
+     *
+     * If you modify this file, please post your revised CultureInfo file
+     * to the Datejs Forum located at http://www.datejs.com/forums/.
+     *
+     * Please mark the subject of the post with [CultureInfo]. Example:
+     *    Subject: [CultureInfo] Translated "da-DK" Danish(Denmark)
+     * 
+     * We will add the modified patterns to the master source files.
+     *
+     * As well, please review the list of "Future Strings" section below. 
+     */	
+    regexPatterns: {
+        jan: /^jan(uary)?/i,
+        feb: /^feb(ruary)?/i,
+        mar: /^mar(ch)?/i,
+        apr: /^apr(il)?/i,
+        may: /^may/i,
+        jun: /^jun(e)?/i,
+        jul: /^jul(y)?/i,
+        aug: /^aug(ust)?/i,
+        sep: /^sep(t(ember)?)?/i,
+        oct: /^oct(ober)?/i,
+        nov: /^nov(ember)?/i,
+        dec: /^dec(ember)?/i,
+
+        sun: /^su(n(day)?)?/i,
+        mon: /^mo(n(day)?)?/i,
+        tue: /^tu(e(s(day)?)?)?/i,
+        wed: /^we(d(nesday)?)?/i,
+        thu: /^th(u(r(s(day)?)?)?)?/i,
+        fri: /^fr(i(day)?)?/i,
+        sat: /^sa(t(urday)?)?/i,
+
+        future: /^next/i,
+        past: /^last|past|prev(ious)?/i,
+        add: /^(\+|aft(er)?|from|hence)/i,
+        subtract: /^(\-|bef(ore)?|ago)/i,
+        
+        yesterday: /^yes(terday)?/i,
+        today: /^t(od(ay)?)?/i,
+        tomorrow: /^tom(orrow)?/i,
+        now: /^n(ow)?/i,
+        
+        millisecond: /^ms|milli(second)?s?/i,
+        second: /^sec(ond)?s?/i,
+        minute: /^mn|min(ute)?s?/i,
+		hour: /^h(our)?s?/i,
+		week: /^w(eek)?s?/i,
+        month: /^m(onth)?s?/i,
+        day: /^d(ay)?s?/i,
+        year: /^y(ear)?s?/i,
+		
+        shortMeridian: /^(a|p)/i,
+        longMeridian: /^(a\.?m?\.?|p\.?m?\.?)/i,
+        timezone: /^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt|utc)/i,
+        ordinalSuffix: /^\s*(st|nd|rd|th)/i,
+        timeContext: /^\s*(\:|a(?!u|p)|p)/i
+    },
+
+	timezones: [{name:"UTC", offset:"-000"}, {name:"GMT", offset:"-000"}, {name:"EST", offset:"-0500"}, {name:"EDT", offset:"-0400"}, {name:"CST", offset:"-0600"}, {name:"CDT", offset:"-0500"}, {name:"MST", offset:"-0700"}, {name:"MDT", offset:"-0600"}, {name:"PST", offset:"-0800"}, {name:"PDT", offset:"-0700"}]
+};
+
+/********************
+ ** Future Strings **
+ ********************
+ * 
+ * The following list of strings may not be currently being used, but 
+ * may be incorporated into the Datejs library later. 
+ *
+ * We would appreciate any help translating the strings below.
+ * 
+ * If you modify this file, please post your revised CultureInfo file
+ * to the Datejs Forum located at http://www.datejs.com/forums/.
+ *
+ * Please mark the subject of the post with [CultureInfo]. Example:
+ *    Subject: [CultureInfo] Translated "da-DK" Danish(Denmark)b
+ *
+ * English Name        Translated
+ * ------------------  -----------------
+ * about               about
+ * ago                 ago
+ * date                date
+ * time                time
+ * calendar            calendar
+ * show                show
+ * hourly              hourly
+ * daily               daily
+ * weekly              weekly
+ * bi-weekly           bi-weekly
+ * fortnight           fortnight
+ * monthly             monthly
+ * bi-monthly          bi-monthly
+ * quarter             quarter
+ * quarterly           quarterly
+ * yearly              yearly
+ * annual              annual
+ * annually            annually
+ * annum               annum
+ * again               again
+ * between             between
+ * after               after
+ * from now            from now
+ * repeat              repeat
+ * times               times
+ * per                 per
+ * min (abbrev minute) min
+ * morning             morning
+ * noon                noon
+ * night               night
+ * midnight            midnight
+ * mid-night           mid-night
+ * evening             evening
+ * final               final
+ * future              future
+ * spring              spring
+ * summer              summer
+ * fall                fall
+ * winter              winter
+ * end of              end of
+ * end                 end
+ * long                long
+ * short               short
+ */
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/html-sanitizer-minified.js b/debian/missing-sources/scripts/html-sanitizer-minified.js
new file mode 120000
index 0000000..10e049f
--- /dev/null
+++ b/debian/missing-sources/scripts/html-sanitizer-minified.js
@@ -0,0 +1 @@
+html-sanitizer.js
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/html-sanitizer.js b/debian/missing-sources/scripts/html-sanitizer.js
new file mode 100644
index 0000000..e54053f
--- /dev/null
+++ b/debian/missing-sources/scripts/html-sanitizer.js
@@ -0,0 +1,1065 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview
+ * An HTML sanitizer that can satisfy a variety of security policies.
+ *
+ * <p>
+ * The HTML sanitizer is built around a SAX parser and HTML element and
+ * attributes schemas.
+ *
+ * If the cssparser is loaded, inline styles are sanitized using the
+ * css property and value schemas.  Else they are remove during
+ * sanitization.
+ *
+ * If it exists, uses parseCssDeclarations, sanitizeCssProperty,  cssSchema
+ *
+ * @author mikesamuel at gmail.com
+ * @author jasvir at gmail.com
+ * \@requires html4, URI
+ * \@overrides window
+ * \@provides html, html_sanitize
+ */
+
+// The Turkish i seems to be a non-issue, but abort in case it is.
+if ('I'.toLowerCase() !== 'i') { throw 'I/i problem'; }
+
+/**
+ * \@namespace
+ */
+var html = (function(html4) {
+
+  // For closure compiler
+  var parseCssDeclarations, sanitizeCssProperty, cssSchema;
+  if ('undefined' !== typeof window) {
+    parseCssDeclarations = window['parseCssDeclarations'];
+    sanitizeCssProperty = window['sanitizeCssProperty'];
+    cssSchema = window['cssSchema'];
+  }
+
+  // The keys of this object must be 'quoted' or JSCompiler will mangle them!
+  // This is a partial list -- lookupEntity() uses the host browser's parser
+  // (when available) to implement full entity lookup.
+  // Note that entities are in general case-sensitive; the uppercase ones are
+  // explicitly defined by HTML5 (presumably as compatibility).
+  var ENTITIES = {
+    'lt': '<',
+    'LT': '<',
+    'gt': '>',
+    'GT': '>',
+    'amp': '&',
+    'AMP': '&',
+    'quot': '"',
+    'apos': '\'',
+    'nbsp': '\240'
+  };
+
+  // Patterns for types of entity/character reference names.
+  var decimalEscapeRe = /^#(\d+)$/;
+  var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/;
+  // contains every entity per http://www.w3.org/TR/2011/WD-html5-20110113/named-character-references.html
+  var safeEntityNameRe = /^[A-Za-z][A-za-z0-9]+$/;
+  // Used as a hook to invoke the browser's entity parsing. <textarea> is used
+  // because its content is parsed for entities but not tags.
+  // TODO(kpreid): This retrieval is a kludge and leads to silent loss of
+  // functionality if the document isn't available.
+  var entityLookupElement =
+      ('undefined' !== typeof window && window['document'])
+          ? window['document'].createElement('textarea') : null;
+  /**
+   * Decodes an HTML entity.
+   *
+   * {\@updoc
+   * $ lookupEntity('lt')
+   * # '<'
+   * $ lookupEntity('GT')
+   * # '>'
+   * $ lookupEntity('amp')
+   * # '&'
+   * $ lookupEntity('nbsp')
+   * # '\xA0'
+   * $ lookupEntity('apos')
+   * # "'"
+   * $ lookupEntity('quot')
+   * # '"'
+   * $ lookupEntity('#xa')
+   * # '\n'
+   * $ lookupEntity('#10')
+   * # '\n'
+   * $ lookupEntity('#x0a')
+   * # '\n'
+   * $ lookupEntity('#010')
+   * # '\n'
+   * $ lookupEntity('#x00A')
+   * # '\n'
+   * $ lookupEntity('Pi')      // Known failure
+   * # '\u03A0'
+   * $ lookupEntity('pi')      // Known failure
+   * # '\u03C0'
+   * }
+   *
+   * @param {string} name the content between the '&' and the ';'.
+   * @return {string} a single unicode code-point as a string.
+   */
+  function lookupEntity(name) {
+    // TODO: entity lookup as specified by HTML5 actually depends on the
+    // presence of the ";".
+    if (ENTITIES.hasOwnProperty(name)) { return ENTITIES[name]; }
+    var m = name.match(decimalEscapeRe);
+    if (m) {
+      return String.fromCharCode(parseInt(m[1], 10));
+    } else if (!!(m = name.match(hexEscapeRe))) {
+      return String.fromCharCode(parseInt(m[1], 16));
+    } else if (entityLookupElement && safeEntityNameRe.test(name)) {
+      entityLookupElement.innerHTML = '&' + name + ';';
+      var text = entityLookupElement.textContent;
+      ENTITIES[name] = text;
+      return text;
+    } else {
+      return '&' + name + ';';
+    }
+  }
+
+  function decodeOneEntity(_, name) {
+    return lookupEntity(name);
+  }
+
+  var nulRe = /\0/g;
+  function stripNULs(s) {
+    return s.replace(nulRe, '');
+  }
+
+  var ENTITY_RE_1 = /&(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/g;
+  var ENTITY_RE_2 = /^(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/;
+  /**
+   * The plain text of a chunk of HTML CDATA which possibly containing.
+   *
+   * {\@updoc
+   * $ unescapeEntities('')
+   * # ''
+   * $ unescapeEntities('hello World!')
+   * # 'hello World!'
+   * $ unescapeEntities('1 < 2 && 4 > 3
')
+   * # '1 < 2 && 4 > 3\n'
+   * $ unescapeEntities('<&lt <- unfinished entity>')
+   * # '<&lt <- unfinished entity>'
+   * $ unescapeEntities('/foo?bar=baz&copy=true')  // & often unescaped in URLS
+   * # '/foo?bar=baz&copy=true'
+   * $ unescapeEntities('pi=π&#x3c0;, Pi=Π\u03A0') // FIXME: known failure
+   * # 'pi=\u03C0\u03c0, Pi=\u03A0\u03A0'
+   * }
+   *
+   * @param {string} s a chunk of HTML CDATA.  It must not start or end inside
+   *     an HTML entity.
+   */
+  function unescapeEntities(s) {
+    return s.replace(ENTITY_RE_1, decodeOneEntity);
+  }
+
+  var ampRe = /&/g;
+  var looseAmpRe = /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi;
+  var ltRe = /[<]/g;
+  var gtRe = />/g;
+  var quotRe = /\"/g;
+
+  /**
+   * Escapes HTML special characters in attribute values.
+   *
+   * {\@updoc
+   * $ escapeAttrib('')
+   * # ''
+   * $ escapeAttrib('"<<&==&>>"')  // Do not just escape the first occurrence.
+   * # '"<<&==&>>"'
+   * $ escapeAttrib('Hello <World>!')
+   * # 'Hello <World>!'
+   * }
+   */
+  function escapeAttrib(s) {
+    return ('' + s).replace(ampRe, '&').replace(ltRe, '<')
+        .replace(gtRe, '>').replace(quotRe, '"');
+  }
+
+  /**
+   * Escape entities in RCDATA that can be escaped without changing the meaning.
+   * {\@updoc
+   * $ normalizeRCData('1 < 2 && 3 > 4 && 5 < 7&8')
+   * # '1 < 2 && 3 > 4 && 5 < 7&8'
+   * }
+   */
+  function normalizeRCData(rcdata) {
+    return rcdata
+        .replace(looseAmpRe, '&$1')
+        .replace(ltRe, '<')
+        .replace(gtRe, '>');
+  }
+
+  // TODO(felix8a): validate sanitizer regexs against the HTML5 grammar at
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
+  // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html
+
+  // We initially split input so that potentially meaningful characters
+  // like '<' and '>' are separate tokens, using a fast dumb process that
+  // ignores quoting.  Then we walk that token stream, and when we see a
+  // '<' that's the start of a tag, we use ATTR_RE to extract tag
+  // attributes from the next token.  That token will never have a '>'
+  // character.  However, it might have an unbalanced quote character, and
+  // when we see that, we combine additional tokens to balance the quote.
+
+  var ATTR_RE = new RegExp(
+    '^\\s*' +
+    '([-.:\\w]+)' +             // 1 = Attribute name
+    '(?:' + (
+      '\\s*(=)\\s*' +           // 2 = Is there a value?
+      '(' + (                   // 3 = Attribute value
+        // TODO(felix8a): maybe use backref to match quotes
+        '(\")[^\"]*(\"|$)' +    // 4, 5 = Double-quoted string
+        '|' +
+        '(\')[^\']*(\'|$)' +    // 6, 7 = Single-quoted string
+        '|' +
+        // Positive lookahead to prevent interpretation of
+        // <foo a= b=c> as <foo a='b=c'>
+        // TODO(felix8a): might be able to drop this case
+        '(?=[a-z][-\\w]*\\s*=)' +
+        '|' +
+        // Unquoted value that isn't an attribute name
+        // (since we didn't match the positive lookahead above)
+        '[^\"\'\\s]*' ) +
+      ')' ) +
+    ')?',
+    'i');
+
+  // false on IE<=8, true on most other browsers
+  var splitWillCapture = ('a,b'.split(/(,)/).length === 3);
+
+  // bitmask for tags with special parsing, like <script> and <textarea>
+  var EFLAGS_TEXT = html4.eflags['CDATA'] | html4.eflags['RCDATA'];
+
+  /**
+   * Given a SAX-like event handler, produce a function that feeds those
+   * events and a parameter to the event handler.
+   *
+   * The event handler has the form:{@code
+   * {
+   *   // Name is an upper-case HTML tag name.  Attribs is an array of
+   *   // alternating upper-case attribute names, and attribute values.  The
+   *   // attribs array is reused by the parser.  Param is the value passed to
+   *   // the saxParser.
+   *   startTag: function (name, attribs, param) { ... },
+   *   endTag:   function (name, param) { ... },
+   *   pcdata:   function (text, param) { ... },
+   *   rcdata:   function (text, param) { ... },
+   *   cdata:    function (text, param) { ... },
+   *   startDoc: function (param) { ... },
+   *   endDoc:   function (param) { ... }
+   * }}
+   *
+   * @param {Object} handler a record containing event handlers.
+   * @return {function(string, Object)} A function that takes a chunk of HTML
+   *     and a parameter.  The parameter is passed on to the handler methods.
+   */
+  function makeSaxParser(handler) {
+    // Accept quoted or unquoted keys (Closure compat)
+    var hcopy = {
+      cdata: handler.cdata || handler['cdata'],
+      comment: handler.comment || handler['comment'],
+      endDoc: handler.endDoc || handler['endDoc'],
+      endTag: handler.endTag || handler['endTag'],
+      pcdata: handler.pcdata || handler['pcdata'],
+      rcdata: handler.rcdata || handler['rcdata'],
+      startDoc: handler.startDoc || handler['startDoc'],
+      startTag: handler.startTag || handler['startTag']
+    };
+    return function(htmlText, param) {
+      return parse(htmlText, hcopy, param);
+    };
+  }
+
+  // Parsing strategy is to split input into parts that might be lexically
+  // meaningful (every ">" becomes a separate part), and then recombine
+  // parts if we discover they're in a different context.
+
+  // TODO(felix8a): Significant performance regressions from -legacy,
+  // tested on
+  //    Chrome 18.0
+  //    Firefox 11.0
+  //    IE 6, 7, 8, 9
+  //    Opera 11.61
+  //    Safari 5.1.3
+  // Many of these are unusual patterns that are linearly slower and still
+  // pretty fast (eg 1ms to 5ms), so not necessarily worth fixing.
+
+  // TODO(felix8a): "<script> && && && ... <\/script>" is slower on all
+  // browsers.  The hotspot is htmlSplit.
+
+  // TODO(felix8a): "<p title='>>>>...'><\/p>" is slower on all browsers.
+  // This is partly htmlSplit, but the hotspot is parseTagAndAttrs.
+
+  // TODO(felix8a): "<a><\/a><a><\/a>..." is slower on IE9.
+  // "<a>1<\/a><a>1<\/a>..." is faster, "<a><\/a>2<a><\/a>2..." is faster.
+
+  // TODO(felix8a): "<p<p<p..." is slower on IE[6-8]
+
+  var continuationMarker = {};
+  function parse(htmlText, handler, param) {
+    var m, p, tagName;
+    var parts = htmlSplit(htmlText);
+    var state = {
+      noMoreGT: false,
+      noMoreEndComments: false
+    };
+    parseCPS(handler, parts, 0, state, param);
+  }
+
+  function continuationMaker(h, parts, initial, state, param) {
+    return function () {
+      parseCPS(h, parts, initial, state, param);
+    };
+  }
+
+  function parseCPS(h, parts, initial, state, param) {
+    try {
+      if (h.startDoc && initial == 0) { h.startDoc(param); }
+      var m, p, tagName;
+      for (var pos = initial, end = parts.length; pos < end;) {
+        var current = parts[pos++];
+        var next = parts[pos];
+        switch (current) {
+        case '&':
+          if (ENTITY_RE_2.test(next)) {
+            if (h.pcdata) {
+              h.pcdata('&' + next, param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+            pos++;
+          } else {
+            if (h.pcdata) { h.pcdata("&", param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          }
+          break;
+        case '<\/':
+          if ((m = /^([-\w:]+)[^\'\"]*/.exec(next))) {
+            if (m[0].length === next.length && parts[pos + 1] === '>') {
+              // fast case, no attribute parsing needed
+              pos += 2;
+              tagName = m[1].toLowerCase();
+              if (h.endTag) {
+                h.endTag(tagName, param, continuationMarker,
+                  continuationMaker(h, parts, pos, state, param));
+              }
+            } else {
+              // slow case, need to parse attributes
+              // TODO(felix8a): do we really care about misparsing this?
+              pos = parseEndTag(
+                parts, pos, h, param, continuationMarker, state);
+            }
+          } else {
+            if (h.pcdata) {
+              h.pcdata('</', param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          }
+          break;
+        case '<':
+          if (m = /^([-\w:]+)\s*\/?/.exec(next)) {
+            if (m[0].length === next.length && parts[pos + 1] === '>') {
+              // fast case, no attribute parsing needed
+              pos += 2;
+              tagName = m[1].toLowerCase();
+              if (h.startTag) {
+                h.startTag(tagName, [], param, continuationMarker,
+                  continuationMaker(h, parts, pos, state, param));
+              }
+              // tags like <script> and <textarea> have special parsing
+              var eflags = html4.ELEMENTS[tagName];
+              if (eflags & EFLAGS_TEXT) {
+                var tag = { name: tagName, next: pos, eflags: eflags };
+                pos = parseText(
+                  parts, tag, h, param, continuationMarker, state);
+              }
+            } else {
+              // slow case, need to parse attributes
+              pos = parseStartTag(
+                parts, pos, h, param, continuationMarker, state);
+            }
+          } else {
+            if (h.pcdata) {
+              h.pcdata('<', param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          }
+          break;
+        case '<\!--':
+          // The pathological case is n copies of '<\!--' without '-->', and
+          // repeated failure to find '-->' is quadratic.  We avoid that by
+          // remembering when search for '-->' fails.
+          if (!state.noMoreEndComments) {
+            // A comment <\!--x--> is split into three tokens:
+            //   '<\!--', 'x--', '>'
+            // We want to find the next '>' token that has a preceding '--'.
+            // pos is at the 'x--'.
+            for (p = pos + 1; p < end; p++) {
+              if (parts[p] === '>' && /--$/.test(parts[p - 1])) { break; }
+            }
+            if (p < end) {
+              if (h.comment) {
+                var comment = parts.slice(pos, p).join('');
+                h.comment(
+                  comment.substr(0, comment.length - 2), param,
+                  continuationMarker,
+                  continuationMaker(h, parts, p + 1, state, param));
+              }
+              pos = p + 1;
+            } else {
+              state.noMoreEndComments = true;
+            }
+          }
+          if (state.noMoreEndComments) {
+            if (h.pcdata) {
+              h.pcdata('<!--', param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          }
+          break;
+        case '<\!':
+          if (!/^\w/.test(next)) {
+            if (h.pcdata) {
+              h.pcdata('<!', param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          } else {
+            // similar to noMoreEndComment logic
+            if (!state.noMoreGT) {
+              for (p = pos + 1; p < end; p++) {
+                if (parts[p] === '>') { break; }
+              }
+              if (p < end) {
+                pos = p + 1;
+              } else {
+                state.noMoreGT = true;
+              }
+            }
+            if (state.noMoreGT) {
+              if (h.pcdata) {
+                h.pcdata('<!', param, continuationMarker,
+                  continuationMaker(h, parts, pos, state, param));
+              }
+            }
+          }
+          break;
+        case '<?':
+          // similar to noMoreEndComment logic
+          if (!state.noMoreGT) {
+            for (p = pos + 1; p < end; p++) {
+              if (parts[p] === '>') { break; }
+            }
+            if (p < end) {
+              pos = p + 1;
+            } else {
+              state.noMoreGT = true;
+            }
+          }
+          if (state.noMoreGT) {
+            if (h.pcdata) {
+              h.pcdata('<?', param, continuationMarker,
+                continuationMaker(h, parts, pos, state, param));
+            }
+          }
+          break;
+        case '>':
+          if (h.pcdata) {
+            h.pcdata(">", param, continuationMarker,
+              continuationMaker(h, parts, pos, state, param));
+          }
+          break;
+        case '':
+          break;
+        default:
+          if (h.pcdata) {
+            h.pcdata(current, param, continuationMarker,
+              continuationMaker(h, parts, pos, state, param));
+          }
+          break;
+        }
+      }
+      if (h.endDoc) { h.endDoc(param); }
+    } catch (e) {
+      if (e !== continuationMarker) { throw e; }
+    }
+  }
+
+  // Split str into parts for the html parser.
+  function htmlSplit(str) {
+    // can't hoist this out of the function because of the re.exec loop.
+    var re = /(<\/|<\!--|<[!?]|[&<>])/g;
+    str += '';
+    if (splitWillCapture) {
+      return str.split(re);
+    } else {
+      var parts = [];
+      var lastPos = 0;
+      var m;
+      while ((m = re.exec(str)) !== null) {
+        parts.push(str.substring(lastPos, m.index));
+        parts.push(m[0]);
+        lastPos = m.index + m[0].length;
+      }
+      parts.push(str.substring(lastPos));
+      return parts;
+    }
+  }
+
+  function parseEndTag(parts, pos, h, param, continuationMarker, state) {
+    var tag = parseTagAndAttrs(parts, pos);
+    // drop unclosed tags
+    if (!tag) { return parts.length; }
+    if (h.endTag) {
+      h.endTag(tag.name, param, continuationMarker,
+        continuationMaker(h, parts, pos, state, param));
+    }
+    return tag.next;
+  }
+
+  function parseStartTag(parts, pos, h, param, continuationMarker, state) {
+    var tag = parseTagAndAttrs(parts, pos);
+    // drop unclosed tags
+    if (!tag) { return parts.length; }
+    if (h.startTag) {
+      h.startTag(tag.name, tag.attrs, param, continuationMarker,
+        continuationMaker(h, parts, tag.next, state, param));
+    }
+    // tags like <script> and <textarea> have special parsing
+    if (tag.eflags & EFLAGS_TEXT) {
+      return parseText(parts, tag, h, param, continuationMarker, state);
+    } else {
+      return tag.next;
+    }
+  }
+
+  var endTagRe = {};
+
+  // Tags like <script> and <textarea> are flagged as CDATA or RCDATA,
+  // which means everything is text until we see the correct closing tag.
+  function parseText(parts, tag, h, param, continuationMarker, state) {
+    var end = parts.length;
+    if (!endTagRe.hasOwnProperty(tag.name)) {
+      endTagRe[tag.name] = new RegExp('^' + tag.name + '(?:[\\s\\/]|$)', 'i');
+    }
+    var re = endTagRe[tag.name];
+    var first = tag.next;
+    var p = tag.next + 1;
+    for (; p < end; p++) {
+      if (parts[p - 1] === '<\/' && re.test(parts[p])) { break; }
+    }
+    if (p < end) { p -= 1; }
+    var buf = parts.slice(first, p).join('');
+    if (tag.eflags & html4.eflags['CDATA']) {
+      if (h.cdata) {
+        h.cdata(buf, param, continuationMarker,
+          continuationMaker(h, parts, p, state, param));
+      }
+    } else if (tag.eflags & html4.eflags['RCDATA']) {
+      if (h.rcdata) {
+        h.rcdata(normalizeRCData(buf), param, continuationMarker,
+          continuationMaker(h, parts, p, state, param));
+      }
+    } else {
+      throw new Error('bug');
+    }
+    return p;
+  }
+
+  // at this point, parts[pos-1] is either "<" or "<\/".
+  function parseTagAndAttrs(parts, pos) {
+    var m = /^([-\w:]+)/.exec(parts[pos]);
+    var tag = {};
+    tag.name = m[1].toLowerCase();
+    tag.eflags = html4.ELEMENTS[tag.name];
+    var buf = parts[pos].substr(m[0].length);
+    // Find the next '>'.  We optimistically assume this '>' is not in a
+    // quoted context, and further down we fix things up if it turns out to
+    // be quoted.
+    var p = pos + 1;
+    var end = parts.length;
+    for (; p < end; p++) {
+      if (parts[p] === '>') { break; }
+      buf += parts[p];
+    }
+    if (end <= p) { return void 0; }
+    var attrs = [];
+    while (buf !== '') {
+      m = ATTR_RE.exec(buf);
+      if (!m) {
+        // No attribute found: skip garbage
+        buf = buf.replace(/^[\s\S][^a-z\s]*/, '');
+
+      } else if ((m[4] && !m[5]) || (m[6] && !m[7])) {
+        // Unterminated quote: slurp to the next unquoted '>'
+        var quote = m[4] || m[6];
+        var sawQuote = false;
+        var abuf = [buf, parts[p++]];
+        for (; p < end; p++) {
+          if (sawQuote) {
+            if (parts[p] === '>') { break; }
+          } else if (0 <= parts[p].indexOf(quote)) {
+            sawQuote = true;
+          }
+          abuf.push(parts[p]);
+        }
+        // Slurp failed: lose the garbage
+        if (end <= p) { break; }
+        // Otherwise retry attribute parsing
+        buf = abuf.join('');
+        continue;
+
+      } else {
+        // We have an attribute
+        var aName = m[1].toLowerCase();
+        var aValue = m[2] ? decodeValue(m[3]) : '';
+        attrs.push(aName, aValue);
+        buf = buf.substr(m[0].length);
+      }
+    }
+    tag.attrs = attrs;
+    tag.next = p + 1;
+    return tag;
+  }
+
+  function decodeValue(v) {
+    var q = v.charCodeAt(0);
+    if (q === 0x22 || q === 0x27) { // " or '
+      v = v.substr(1, v.length - 2);
+    }
+    return unescapeEntities(stripNULs(v));
+  }
+
+  /**
+   * Returns a function that strips unsafe tags and attributes from html.
+   * @param {function(string, Array.<string>): ?Array.<string>} tagPolicy
+   *     A function that takes (tagName, attribs[]), where tagName is a key in
+   *     html4.ELEMENTS and attribs is an array of alternating attribute names
+   *     and values.  It should return a record (as follows), or null to delete
+   *     the element.  It's okay for tagPolicy to modify the attribs array,
+   *     but the same array is reused, so it should not be held between calls.
+   *     Record keys:
+   *        attribs: (required) Sanitized attributes array.
+   *        tagName: Replacement tag name.
+   * @return {function(string, Array)} A function that sanitizes a string of
+   *     HTML and appends result strings to the second argument, an array.
+   */
+  function makeHtmlSanitizer(tagPolicy) {
+    var stack;
+    var ignoring;
+    var emit = function (text, out) {
+      if (!ignoring) { out.push(text); }
+    };
+    return makeSaxParser({
+      'startDoc': function(_) {
+        stack = [];
+        ignoring = false;
+      },
+      'startTag': function(tagNameOrig, attribs, out) {
+        if (ignoring) { return; }
+        if (!html4.ELEMENTS.hasOwnProperty(tagNameOrig)) { return; }
+        var eflagsOrig = html4.ELEMENTS[tagNameOrig];
+        if (eflagsOrig & html4.eflags['FOLDABLE']) {
+          return;
+        }
+
+        var decision = tagPolicy(tagNameOrig, attribs);
+        if (!decision) {
+          ignoring = !(eflagsOrig & html4.eflags['EMPTY']);
+          return;
+        } else if (typeof decision !== 'object') {
+          throw new Error('tagPolicy did not return object (old API?)');
+        }
+        if ('attribs' in decision) {
+          attribs = decision['attribs'];
+        } else {
+          throw new Error('tagPolicy gave no attribs');
+        }
+        var eflagsRep;
+        var tagNameRep;
+        if ('tagName' in decision) {
+          tagNameRep = decision['tagName'];
+          eflagsRep = html4.ELEMENTS[tagNameRep];
+        } else {
+          tagNameRep = tagNameOrig;
+          eflagsRep = eflagsOrig;
+        }
+        // TODO(mikesamuel): relying on tagPolicy not to insert unsafe
+        // attribute names.
+
+        // If this is an optional-end-tag element and either this element or its
+        // previous like sibling was rewritten, then insert a close tag to
+        // preserve structure.
+        if (eflagsOrig & html4.eflags['OPTIONAL_ENDTAG']) {
+          var onStack = stack[stack.length - 1];
+          if (onStack && onStack.orig === tagNameOrig &&
+              (onStack.rep !== tagNameRep || tagNameOrig !== tagNameRep)) {
+                out.push('<\/', onStack.rep, '>');
+          }
+        }
+
+        if (!(eflagsOrig & html4.eflags['EMPTY'])) {
+          stack.push({orig: tagNameOrig, rep: tagNameRep});
+        }
+
+        out.push('<', tagNameRep);
+        for (var i = 0, n = attribs.length; i < n; i += 2) {
+          var attribName = attribs[i],
+              value = attribs[i + 1];
+          if (value !== null && value !== void 0) {
+            out.push(' ', attribName, '="', escapeAttrib(value), '"');
+          }
+        }
+        out.push('>');
+
+        if ((eflagsOrig & html4.eflags['EMPTY'])
+            && !(eflagsRep & html4.eflags['EMPTY'])) {
+          // replacement is non-empty, synthesize end tag
+          out.push('<\/', tagNameRep, '>');
+        }
+      },
+      'endTag': function(tagName, out) {
+        if (ignoring) {
+          ignoring = false;
+          return;
+        }
+        if (!html4.ELEMENTS.hasOwnProperty(tagName)) { return; }
+        var eflags = html4.ELEMENTS[tagName];
+        if (!(eflags & (html4.eflags['EMPTY'] | html4.eflags['FOLDABLE']))) {
+          var index;
+          if (eflags & html4.eflags['OPTIONAL_ENDTAG']) {
+            for (index = stack.length; --index >= 0;) {
+              var stackElOrigTag = stack[index].orig;
+              if (stackElOrigTag === tagName) { break; }
+              if (!(html4.ELEMENTS[stackElOrigTag] &
+                    html4.eflags['OPTIONAL_ENDTAG'])) {
+                // Don't pop non optional end tags looking for a match.
+                return;
+              }
+            }
+          } else {
+            for (index = stack.length; --index >= 0;) {
+              if (stack[index].orig === tagName) { break; }
+            }
+          }
+          if (index < 0) { return; }  // Not opened.
+          for (var i = stack.length; --i > index;) {
+            var stackElRepTag = stack[i].rep;
+            if (!(html4.ELEMENTS[stackElRepTag] &
+                  html4.eflags['OPTIONAL_ENDTAG'])) {
+              out.push('<\/', stackElRepTag, '>');
+            }
+          }
+          if (index < stack.length) {
+            tagName = stack[index].rep;
+          }
+          stack.length = index;
+          out.push('<\/', tagName, '>');
+        }
+      },
+      'pcdata': emit,
+      'rcdata': emit,
+      'cdata': emit,
+      'endDoc': function(out) {
+        for (; stack.length; stack.length--) {
+          out.push('<\/', stack[stack.length - 1].rep, '>');
+        }
+      }
+    });
+  }
+
+  var ALLOWED_URI_SCHEMES = /^(?:https?|mailto)$/i;
+
+  function safeUri(uri, effect, ltype, hints, naiveUriRewriter) {
+    if (!naiveUriRewriter) { return null; }
+    try {
+      var parsed = URI.parse('' + uri);
+      if (parsed) {
+        if (!parsed.hasScheme() ||
+            ALLOWED_URI_SCHEMES.test(parsed.getScheme())) {
+          var safe = naiveUriRewriter(parsed, effect, ltype, hints);
+          return safe ? safe.toString() : null;
+        }
+      }
+    } catch (e) {
+      return null;
+    }
+    return null;
+  }
+
+  function log(logger, tagName, attribName, oldValue, newValue) {
+    if (!attribName) {
+      logger(tagName + " removed", {
+        change: "removed",
+        tagName: tagName
+      });
+    }
+    if (oldValue !== newValue) {
+      var changed = "changed";
+      if (oldValue && !newValue) {
+        changed = "removed";
+      } else if (!oldValue && newValue)  {
+        changed = "added";
+      }
+      logger(tagName + "." + attribName + " " + changed, {
+        change: changed,
+        tagName: tagName,
+        attribName: attribName,
+        oldValue: oldValue,
+        newValue: newValue
+      });
+    }
+  }
+
+  function lookupAttribute(map, tagName, attribName) {
+    var attribKey;
+    attribKey = tagName + '::' + attribName;
+    if (map.hasOwnProperty(attribKey)) {
+      return map[attribKey];
+    }
+    attribKey = '*::' + attribName;
+    if (map.hasOwnProperty(attribKey)) {
+      return map[attribKey];
+    }
+    return void 0;
+  }
+  function getAttributeType(tagName, attribName) {
+    return lookupAttribute(html4.ATTRIBS, tagName, attribName);
+  }
+  function getLoaderType(tagName, attribName) {
+    return lookupAttribute(html4.LOADERTYPES, tagName, attribName);
+  }
+  function getUriEffect(tagName, attribName) {
+    return lookupAttribute(html4.URIEFFECTS, tagName, attribName);
+  }
+
+  /**
+   * Sanitizes attributes on an HTML tag.
+   * @param {string} tagName An HTML tag name in lowercase.
+   * @param {Array.<?string>} attribs An array of alternating names and values.
+   * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
+   *     apply to URI attributes; it can return a new string value, or null to
+   *     delete the attribute.  If unspecified, URI attributes are deleted.
+   * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
+   *     to attributes containing HTML names, element IDs, and space-separated
+   *     lists of classes; it can return a new string value, or null to delete
+   *     the attribute.  If unspecified, these attributes are kept unchanged.
+   * @return {Array.<?string>} The sanitized attributes as a list of alternating
+   *     names and values, where a null value means to omit the attribute.
+   */
+  function sanitizeAttribs(tagName, attribs,
+    opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
+    // TODO(felix8a): it's obnoxious that domado duplicates much of this
+    // TODO(felix8a): maybe consistently enforce constraints like target=
+    for (var i = 0; i < attribs.length; i += 2) {
+      var attribName = attribs[i];
+      var value = attribs[i + 1];
+      var oldValue = value;
+      var atype = null, attribKey;
+      if ((attribKey = tagName + '::' + attribName,
+           html4.ATTRIBS.hasOwnProperty(attribKey)) ||
+          (attribKey = '*::' + attribName,
+           html4.ATTRIBS.hasOwnProperty(attribKey))) {
+        atype = html4.ATTRIBS[attribKey];
+      }
+      if (atype !== null) {
+        switch (atype) {
+          case html4.atype['NONE']: break;
+          case html4.atype['SCRIPT']:
+            value = null;
+            if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+          case html4.atype['STYLE']:
+            if ('undefined' === typeof parseCssDeclarations) {
+              value = null;
+              if (opt_logger) {
+                log(opt_logger, tagName, attribName, oldValue, value);
+	      }
+              break;
+            }
+            var sanitizedDeclarations = [];
+            parseCssDeclarations(
+                value,
+                {
+                  'declaration': function (property, tokens) {
+                    var normProp = property.toLowerCase();
+                    sanitizeCssProperty(
+                        normProp, tokens,
+                        opt_naiveUriRewriter
+                        ? function (url) {
+                            return safeUri(
+                                url, html4.ueffects.SAME_DOCUMENT,
+                                html4.ltypes.SANDBOXED,
+                                {
+                                  "TYPE": "CSS",
+                                  "CSS_PROP": normProp
+                                }, opt_naiveUriRewriter);
+                          }
+                        : null);
+                    if (tokens.length) {
+                      sanitizedDeclarations.push(
+                          normProp + ': ' + tokens.join(' '));
+                    }
+                  }
+                });
+            value = sanitizedDeclarations.length > 0 ?
+              sanitizedDeclarations.join(' ; ') : null;
+            if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+          case html4.atype['ID']:
+          case html4.atype['IDREF']:
+          case html4.atype['IDREFS']:
+          case html4.atype['GLOBAL_NAME']:
+          case html4.atype['LOCAL_NAME']:
+          case html4.atype['CLASSES']:
+            value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
+            if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+          case html4.atype['URI']:
+            value = safeUri(value,
+              getUriEffect(tagName, attribName),
+              getLoaderType(tagName, attribName),
+              {
+                "TYPE": "MARKUP",
+                "XML_ATTR": attribName,
+                "XML_TAG": tagName
+              }, opt_naiveUriRewriter);
+              if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+          case html4.atype['URI_FRAGMENT']:
+            if (value && '#' === value.charAt(0)) {
+              value = value.substring(1);  // remove the leading '#'
+              value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value;
+              if (value !== null && value !== void 0) {
+                value = '#' + value;  // restore the leading '#'
+              }
+            } else {
+              value = null;
+            }
+            if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+          default:
+            value = null;
+            if (opt_logger) {
+              log(opt_logger, tagName, attribName, oldValue, value);
+            }
+            break;
+        }
+      } else {
+        value = null;
+        if (opt_logger) {
+          log(opt_logger, tagName, attribName, oldValue, value);
+        }
+      }
+      attribs[i + 1] = value;
+    }
+    return attribs;
+  }
+
+  /**
+   * Creates a tag policy that omits all tags marked UNSAFE in html4-defs.js
+   * and applies the default attribute sanitizer with the supplied policy for
+   * URI attributes and NMTOKEN attributes.
+   * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
+   *     apply to URI attributes.  If not given, URI attributes are deleted.
+   * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
+   *     to attributes containing HTML names, element IDs, and space-separated
+   *     lists of classes.  If not given, such attributes are left unchanged.
+   * @return {function(string, Array.<?string>)} A tagPolicy suitable for
+   *     passing to html.sanitize.
+   */
+  function makeTagPolicy(
+    opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
+    return function(tagName, attribs) {
+      if (!(html4.ELEMENTS[tagName] & html4.eflags['UNSAFE'])) {
+        return {
+          'attribs': sanitizeAttribs(tagName, attribs,
+            opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger)
+        };
+      } else {
+        if (opt_logger) {
+          log(opt_logger, tagName, undefined, undefined, undefined);
+        }
+      }
+    };
+  }
+
+  /**
+   * Sanitizes HTML tags and attributes according to a given policy.
+   * @param {string} inputHtml The HTML to sanitize.
+   * @param {function(string, Array.<?string>)} tagPolicy A function that
+   *     decides which tags to accept and sanitizes their attributes (see
+   *     makeHtmlSanitizer above for details).
+   * @return {string} The sanitized HTML.
+   */
+  function sanitizeWithPolicy(inputHtml, tagPolicy) {
+    var outputArray = [];
+    makeHtmlSanitizer(tagPolicy)(inputHtml, outputArray);
+    return outputArray.join('');
+  }
+
+  /**
+   * Strips unsafe tags and attributes from HTML.
+   * @param {string} inputHtml The HTML to sanitize.
+   * @param {?function(?string): ?string} opt_naiveUriRewriter A transform to
+   *     apply to URI attributes.  If not given, URI attributes are deleted.
+   * @param {function(?string): ?string} opt_nmTokenPolicy A transform to apply
+   *     to attributes containing HTML names, element IDs, and space-separated
+   *     lists of classes.  If not given, such attributes are left unchanged.
+   */
+  function sanitize(inputHtml,
+    opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger) {
+    var tagPolicy = makeTagPolicy(
+      opt_naiveUriRewriter, opt_nmTokenPolicy, opt_logger);
+    return sanitizeWithPolicy(inputHtml, tagPolicy);
+  }
+
+  // Export both quoted and unquoted names for Closure linkage.
+  var html = {};
+  html.escapeAttrib = html['escapeAttrib'] = escapeAttrib;
+  html.makeHtmlSanitizer = html['makeHtmlSanitizer'] = makeHtmlSanitizer;
+  html.makeSaxParser = html['makeSaxParser'] = makeSaxParser;
+  html.makeTagPolicy = html['makeTagPolicy'] = makeTagPolicy;
+  html.normalizeRCData = html['normalizeRCData'] = normalizeRCData;
+  html.sanitize = html['sanitize'] = sanitize;
+  html.sanitizeAttribs = html['sanitizeAttribs'] = sanitizeAttribs;
+  html.sanitizeWithPolicy = html['sanitizeWithPolicy'] = sanitizeWithPolicy;
+  html.unescapeEntities = html['unescapeEntities'] = unescapeEntities;
+  return html;
+})(html4);
+
+var html_sanitize = html['sanitize'];
+
+// Exports for Closure compiler.  Note this file is also cajoled
+// for domado and run in an environment without 'window'
+if (typeof window !== 'undefined') {
+  window['html'] = html;
+  window['html_sanitize'] = html_sanitize;
+}
diff --git a/debian/missing-sources/scripts/parser.js b/debian/missing-sources/scripts/parser.js
new file mode 100644
index 0000000..eb173c2
--- /dev/null
+++ b/debian/missing-sources/scripts/parser.js
@@ -0,0 +1,1116 @@
+/**
+ * @version: 1.0 Alpha-1
+ * @author: Coolite Inc. http://www.coolite.com/
+ * @date: 2008-04-13
+ * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * @website: http://www.datejs.com/
+ */
+ 
+(function () {
+    Date.Parsing = {
+        Exception: function (s) {
+            this.message = "Parse error at '" + s.substring(0, 10) + " ...'"; 
+        }
+    };
+    
+    var $P = Date.Parsing; 
+    var _ = $P.Operators = {
+        //
+        // Tokenizers
+        //
+        rtoken: function (r) { // regex token
+            return function (s) {
+                var mx = s.match(r);
+                if (mx) { 
+                    return ([ mx[0], s.substring(mx[0].length) ]); 
+                } else { 
+                    throw new $P.Exception(s); 
+                }
+            };
+        },
+        token: function (s) { // whitespace-eating token
+            return function (s) {
+                return _.rtoken(new RegExp("^\s*" + s + "\s*"))(s);
+                // Removed .strip()
+                // return _.rtoken(new RegExp("^\s*" + s + "\s*"))(s).strip();
+            };
+        },
+        stoken: function (s) { // string token
+            return _.rtoken(new RegExp("^" + s)); 
+        },
+
+        //
+        // Atomic Operators
+        // 
+
+        until: function (p) {
+            return function (s) {
+                var qx = [], rx = null;
+                while (s.length) { 
+                    try { 
+                        rx = p.call(this, s); 
+                    } catch (e) { 
+                        qx.push(rx[0]); 
+                        s = rx[1]; 
+                        continue; 
+                    }
+                    break;
+                }
+                return [ qx, s ];
+            };
+        },
+        many: function (p) {
+            return function (s) {
+                var rx = [], r = null; 
+                while (s.length) { 
+                    try { 
+                        r = p.call(this, s); 
+                    } catch (e) { 
+                        return [ rx, s ]; 
+                    }
+                    rx.push(r[0]); 
+                    s = r[1];
+                }
+                return [ rx, s ];
+            };
+        },
+
+        // generator operators -- see below
+        optional: function (p) {
+            return function (s) {
+                var r = null; 
+                try { 
+                    r = p.call(this, s); 
+                } catch (e) { 
+                    return [ null, s ]; 
+                }
+                return [ r[0], r[1] ];
+            };
+        },
+        not: function (p) {
+            return function (s) {
+                try { 
+                    p.call(this, s); 
+                } catch (e) { 
+                    return [null, s]; 
+                }
+                throw new $P.Exception(s);
+            };
+        },
+        ignore: function (p) {
+            return p ? 
+            function (s) { 
+                var r = null; 
+                r = p.call(this, s); 
+                return [null, r[1]]; 
+            } : null;
+        },
+        product: function () {
+            var px = arguments[0], 
+            qx = Array.prototype.slice.call(arguments, 1), rx = [];
+            for (var i = 0 ; i < px.length ; i++) {
+                rx.push(_.each(px[i], qx));
+            }
+            return rx;
+        },
+        cache: function (rule) { 
+            var cache = {}, r = null; 
+            return function (s) {
+                try { 
+                    r = cache[s] = (cache[s] || rule.call(this, s)); 
+                } catch (e) { 
+                    r = cache[s] = e; 
+                }
+                if (r instanceof $P.Exception) { 
+                    throw r; 
+                } else { 
+                    return r; 
+                }
+            };
+        },
+    	  
+        // vector operators -- see below
+        any: function () {
+            var px = arguments;
+            return function (s) { 
+                var r = null;
+                for (var i = 0; i < px.length; i++) { 
+                    if (px[i] == null) { 
+                        continue; 
+                    }
+                    try { 
+                        r = (px[i].call(this, s)); 
+                    } catch (e) { 
+                        r = null; 
+                    }
+                    if (r) { 
+                        return r; 
+                    }
+                } 
+                throw new $P.Exception(s);
+            };
+        },
+        each: function () { 
+            var px = arguments;
+            return function (s) { 
+                var rx = [], r = null;
+                for (var i = 0; i < px.length ; i++) { 
+                    if (px[i] == null) { 
+                        continue; 
+                    }
+                    try { 
+                        r = (px[i].call(this, s)); 
+                    } catch (e) { 
+                        throw new $P.Exception(s); 
+                    }
+                    rx.push(r[0]); 
+                    s = r[1];
+                }
+                return [ rx, s]; 
+            };
+        },
+        all: function () { 
+            var px = arguments, _ = _; 
+            return _.each(_.optional(px)); 
+        },
+
+        // delimited operators
+        sequence: function (px, d, c) {
+            d = d || _.rtoken(/^\s*/);  
+            c = c || null;
+            
+            if (px.length == 1) { 
+                return px[0]; 
+            }
+            return function (s) {
+                var r = null, q = null;
+                var rx = []; 
+                for (var i = 0; i < px.length ; i++) {
+                    try { 
+                        r = px[i].call(this, s); 
+                    } catch (e) { 
+                        break; 
+                    }
+                    rx.push(r[0]);
+                    try { 
+                        q = d.call(this, r[1]); 
+                    } catch (ex) { 
+                        q = null; 
+                        break; 
+                    }
+                    s = q[1];
+                }
+                if (!r) { 
+                    throw new $P.Exception(s); 
+                }
+                if (q) { 
+                    throw new $P.Exception(q[1]); 
+                }
+                if (c) {
+                    try { 
+                        r = c.call(this, r[1]);
+                    } catch (ey) { 
+                        throw new $P.Exception(r[1]); 
+                    }
+                }
+                return [ rx, (r?r[1]:s) ];
+            };
+        },
+    		
+	    //
+	    // Composite Operators
+	    //
+    		
+        between: function (d1, p, d2) { 
+            d2 = d2 || d1; 
+            var _fn = _.each(_.ignore(d1), p, _.ignore(d2));
+            return function (s) { 
+                var rx = _fn.call(this, s); 
+                return [[rx[0][0], r[0][2]], rx[1]]; 
+            };
+        },
+        list: function (p, d, c) {
+            d = d || _.rtoken(/^\s*/);  
+            c = c || null;
+            return (p instanceof Array ?
+                _.each(_.product(p.slice(0, -1), _.ignore(d)), p.slice(-1), _.ignore(c)) :
+                _.each(_.many(_.each(p, _.ignore(d))), px, _.ignore(c)));
+        },
+        set: function (px, d, c) {
+            d = d || _.rtoken(/^\s*/); 
+            c = c || null;
+            return function (s) {
+                // r is the current match, best the current 'best' match
+                // which means it parsed the most amount of input
+                var r = null, p = null, q = null, rx = null, best = [[], s], last = false;
+
+                // go through the rules in the given set
+                for (var i = 0; i < px.length ; i++) {
+
+                    // last is a flag indicating whether this must be the last element
+                    // if there is only 1 element, then it MUST be the last one
+                    q = null; 
+                    p = null; 
+                    r = null; 
+                    last = (px.length == 1); 
+
+                    // first, we try simply to match the current pattern
+                    // if not, try the next pattern
+                    try { 
+                        r = px[i].call(this, s);
+                    } catch (e) { 
+                        continue; 
+                    }
+
+                    // since we are matching against a set of elements, the first
+                    // thing to do is to add r[0] to matched elements
+                    rx = [[r[0]], r[1]];
+
+                    // if we matched and there is still input to parse and 
+                    // we don't already know this is the last element,
+                    // we're going to next check for the delimiter ...
+                    // if there's none, or if there's no input left to parse
+                    // than this must be the last element after all ...
+                    if (r[1].length > 0 && ! last) {
+                        try { 
+                            q = d.call(this, r[1]); 
+                        } catch (ex) { 
+                            last = true; 
+                        }
+                    } else { 
+                        last = true; 
+                    }
+
+				    // if we parsed the delimiter and now there's no more input,
+				    // that means we shouldn't have parsed the delimiter at all
+				    // so don't update r and mark this as the last element ...
+                    if (!last && q[1].length === 0) { 
+                        last = true; 
+                    }
+
+
+				    // so, if this isn't the last element, we're going to see if
+				    // we can get any more matches from the remaining (unmatched)
+				    // elements ...
+                    if (!last) {
+
+                        // build a list of the remaining rules we can match against,
+                        // i.e., all but the one we just matched against
+                        var qx = []; 
+                        for (var j = 0; j < px.length ; j++) { 
+                            if (i != j) { 
+                                qx.push(px[j]); 
+                            }
+                        }
+
+                        // now invoke recursively set with the remaining input
+                        // note that we don't include the closing delimiter ...
+                        // we'll check for that ourselves at the end
+                        p = _.set(qx, d).call(this, q[1]);
+
+                        // if we got a non-empty set as a result ...
+                        // (otw rx already contains everything we want to match)
+                        if (p[0].length > 0) {
+                            // update current result, which is stored in rx ...
+                            // basically, pick up the remaining text from p[1]
+                            // and concat the result from p[0] so that we don't
+                            // get endless nesting ...
+                            rx[0] = rx[0].concat(p[0]); 
+                            rx[1] = p[1]; 
+                        }
+                    }
+
+				    // at this point, rx either contains the last matched element
+				    // or the entire matched set that starts with this element.
+
+				    // now we just check to see if this variation is better than
+				    // our best so far, in terms of how much of the input is parsed
+                    if (rx[1].length < best[1].length) { 
+                        best = rx; 
+                    }
+
+				    // if we've parsed all the input, then we're finished
+                    if (best[1].length === 0) { 
+                        break; 
+                    }
+                }
+
+			    // so now we've either gone through all the patterns trying them
+			    // as the initial match; or we found one that parsed the entire
+			    // input string ...
+
+			    // if best has no matches, just return empty set ...
+                if (best[0].length === 0) { 
+                    return best; 
+                }
+
+			    // if a closing delimiter is provided, then we have to check it also
+                if (c) {
+                    // we try this even if there is no remaining input because the pattern
+                    // may well be optional or match empty input ...
+                    try { 
+                        q = c.call(this, best[1]); 
+                    } catch (ey) { 
+                        throw new $P.Exception(best[1]); 
+                    }
+
+                    // it parsed ... be sure to update the best match remaining input
+                    best[1] = q[1];
+                }
+
+			    // if we're here, either there was no closing delimiter or we parsed it
+			    // so now we have the best match; just return it!
+                return best;
+            };
+        },
+        forward: function (gr, fname) {
+            return function (s) { 
+                return gr[fname].call(this, s); 
+            };
+        },
+
+        //
+        // Translation Operators
+        //
+        replace: function (rule, repl) {
+            return function (s) { 
+                var r = rule.call(this, s); 
+                return [repl, r[1]]; 
+            };
+        },
+        process: function (rule, fn) {
+            return function (s) {  
+                var r = rule.call(this, s); 
+                return [fn.call(this, r[0]), r[1]]; 
+            };
+        },
+        min: function (min, rule) {
+            return function (s) {
+                var rx = rule.call(this, s); 
+                if (rx[0].length < min) { 
+                    throw new $P.Exception(s); 
+                }
+                return rx;
+            };
+        }
+    };
+	
+
+	// Generator Operators And Vector Operators
+
+	// Generators are operators that have a signature of F(R) => R,
+	// taking a given rule and returning another rule, such as 
+	// ignore, which parses a given rule and throws away the result.
+
+	// Vector operators are those that have a signature of F(R1,R2,...) => R,
+	// take a list of rules and returning a new rule, such as each.
+
+	// Generator operators are converted (via the following _generator
+	// function) into functions that can also take a list or array of rules
+	// and return an array of new rules as though the function had been
+	// called on each rule in turn (which is what actually happens).
+
+	// This allows generators to be used with vector operators more easily.
+	// Example:
+	// each(ignore(foo, bar)) instead of each(ignore(foo), ignore(bar))
+
+	// This also turns generators into vector operators, which allows
+	// constructs like:
+	// not(cache(foo, bar))
+	
+    var _generator = function (op) {
+        return function () {
+            var args = null, rx = [];
+            if (arguments.length > 1) {
+                args = Array.prototype.slice.call(arguments);
+            } else if (arguments[0] instanceof Array) {
+                args = arguments[0];
+            }
+            if (args) { 
+                for (var i = 0, px = args.shift() ; i < px.length ; i++) {
+                    args.unshift(px[i]); 
+                    rx.push(op.apply(null, args)); 
+                    args.shift();
+                    return rx;
+                } 
+            } else { 
+                return op.apply(null, arguments); 
+            }
+        };
+    };
+    
+    var gx = "optional not ignore cache".split(/\s/);
+    
+    for (var i = 0 ; i < gx.length ; i++) { 
+        _[gx[i]] = _generator(_[gx[i]]); 
+    }
+
+    var _vector = function (op) {
+        return function () {
+            if (arguments[0] instanceof Array) { 
+                return op.apply(null, arguments[0]); 
+            } else { 
+                return op.apply(null, arguments); 
+            }
+        };
+    };
+    
+    var vx = "each any all".split(/\s/);
+    
+    for (var j = 0 ; j < vx.length ; j++) { 
+        _[vx[j]] = _vector(_[vx[j]]); 
+    }
+	
+}());
+
+(function () {
+    var $D = Date, $P = $D.prototype, $C = $D.CultureInfo;
+
+    var flattenAndCompact = function (ax) { 
+        var rx = []; 
+        for (var i = 0; i < ax.length; i++) {
+            if (ax[i] instanceof Array) {
+                rx = rx.concat(flattenAndCompact(ax[i]));
+            } else { 
+                if (ax[i]) { 
+                    rx.push(ax[i]); 
+                }
+            }
+        }
+        return rx;
+    };
+    
+    $D.Grammar = {};
+	
+    $D.Translator = {
+        hour: function (s) { 
+            return function () { 
+                this.hour = Number(s); 
+            }; 
+        },
+        minute: function (s) { 
+            return function () { 
+                this.minute = Number(s); 
+            }; 
+        },
+        second: function (s) { 
+            return function () { 
+                this.second = Number(s); 
+            }; 
+        },
+        meridian: function (s) { 
+            return function () { 
+                this.meridian = s.slice(0, 1).toLowerCase(); 
+            }; 
+        },
+        timezone: function (s) {
+            return function () {
+                var n = s.replace(/[^\d\+\-]/g, "");
+                if (n.length) { 
+                    this.timezoneOffset = Number(n); 
+                } else { 
+                    this.timezone = s.toLowerCase(); 
+                }
+            };
+        },
+        day: function (x) { 
+            var s = x[0];
+            return function () { 
+                this.day = Number(s.match(/\d+/)[0]); 
+            };
+        }, 
+        month: function (s) {
+            return function () {
+                this.month = (s.length == 3) ? "jan feb mar apr may jun jul aug sep oct nov dec".indexOf(s)/4 : Number(s) - 1;
+            };
+        },
+        year: function (s) {
+            return function () {
+                var n = Number(s);
+                this.year = ((s.length > 2) ? n : 
+                    (n + (((n + 2000) < $C.twoDigitYearMax) ? 2000 : 1900))); 
+            };
+        },
+        rday: function (s) { 
+            return function () {
+                switch (s) {
+                case "yesterday": 
+                    this.days = -1;
+                    break;
+                case "tomorrow":  
+                    this.days = 1;
+                    break;
+                case "today": 
+                    this.days = 0;
+                    break;
+                case "now": 
+                    this.days = 0; 
+                    this.now = true; 
+                    break;
+                }
+            };
+        },
+        finishExact: function (x) {  
+            x = (x instanceof Array) ? x : [ x ]; 
+
+            for (var i = 0 ; i < x.length ; i++) { 
+                if (x[i]) { 
+                    x[i].call(this); 
+                }
+            }
+            
+            var now = new Date();
+            
+            if ((this.hour || this.minute) && (!this.month && !this.year && !this.day)) {
+                this.day = now.getDate();
+            }
+
+            if (!this.year) {
+                this.year = now.getFullYear();
+            }
+            
+            if (!this.month && this.month !== 0) {
+                this.month = now.getMonth();
+            }
+            
+            if (!this.day) {
+                this.day = 1;
+            }
+            
+            if (!this.hour) {
+                this.hour = 0;
+            }
+            
+            if (!this.minute) {
+                this.minute = 0;
+            }
+
+            if (!this.second) {
+                this.second = 0;
+            }
+
+            if (this.meridian && this.hour) {
+                if (this.meridian == "p" && this.hour < 12) {
+                    this.hour = this.hour + 12;
+                } else if (this.meridian == "a" && this.hour == 12) {
+                    this.hour = 0;
+                }
+            }
+            
+            if (this.day > $D.getDaysInMonth(this.year, this.month)) {
+                throw new RangeError(this.day + " is not a valid value for days.");
+            }
+
+            var r = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second);
+
+            if (this.timezone) { 
+                r.set({ timezone: this.timezone }); 
+            } else if (this.timezoneOffset) { 
+                r.set({ timezoneOffset: this.timezoneOffset }); 
+            }
+            
+            return r;
+        },			
+        finish: function (x) {
+            x = (x instanceof Array) ? flattenAndCompact(x) : [ x ];
+
+            if (x.length === 0) { 
+                return null; 
+            }
+
+            for (var i = 0 ; i < x.length ; i++) { 
+                if (typeof x[i] == "function") {
+                    x[i].call(this); 
+                }
+            }
+            
+            var today = $D.today();
+            
+            if (this.now && !this.unit && !this.operator) { 
+                return new Date(); 
+            } else if (this.now) {
+                today = new Date();
+            }
+            
+            var expression = !!(this.days && this.days !== null || this.orient || this.operator);
+            
+            var gap, mod, orient;
+            orient = ((this.orient == "past" || this.operator == "subtract") ? -1 : 1);
+            
+            if(!this.now && "hour minute second".indexOf(this.unit) != -1) {
+                today.setTimeToNow();
+            }
+
+            if (this.month || this.month === 0) {
+                if ("year day hour minute second".indexOf(this.unit) != -1) {
+                    this.value = this.month + 1;
+                    this.month = null;
+                    expression = true;
+                }
+            }
+            
+            if (!expression && this.weekday && !this.day && !this.days) {
+                var temp = Date[this.weekday]();
+                this.day = temp.getDate();
+                if (!this.month) {
+                    this.month = temp.getMonth();
+                }
+                this.year = temp.getFullYear();
+            }
+            
+            if (expression && this.weekday && this.unit != "month") {
+                this.unit = "day";
+                gap = ($D.getDayNumberFromName(this.weekday) - today.getDay());
+                mod = 7;
+                this.days = gap ? ((gap + (orient * mod)) % mod) : (orient * mod);
+            }
+            
+            if (this.month && this.unit == "day" && this.operator) {
+                this.value = (this.month + 1);
+                this.month = null;
+            }
+       
+            if (this.value != null && this.month != null && this.year != null) {
+                this.day = this.value * 1;
+            }
+     
+            if (this.month && !this.day && this.value) {
+                today.set({ day: this.value * 1 });
+                if (!expression) {
+                    this.day = this.value * 1;
+                }
+            }
+
+            if (!this.month && this.value && this.unit == "month" && !this.now) {
+                this.month = this.value;
+                expression = true;
+            }
+
+            if (expression && (this.month || this.month === 0) && this.unit != "year") {
+                this.unit = "month";
+                gap = (this.month - today.getMonth());
+                mod = 12;
+                this.months = gap ? ((gap + (orient * mod)) % mod) : (orient * mod);
+                this.month = null;
+            }
+
+            if (!this.unit) { 
+                this.unit = "day"; 
+            }
+            
+            if (!this.value && this.operator && this.operator !== null && this[this.unit + "s"] && this[this.unit + "s"] !== null) {
+                this[this.unit + "s"] = this[this.unit + "s"] + ((this.operator == "add") ? 1 : -1) + (this.value||0) * orient;
+            } else if (this[this.unit + "s"] == null || this.operator != null) {
+                if (!this.value) {
+                    this.value = 1;
+                }
+                this[this.unit + "s"] = this.value * orient;
+            }
+
+            if (this.meridian && this.hour) {
+                if (this.meridian == "p" && this.hour < 12) {
+                    this.hour = this.hour + 12;
+                } else if (this.meridian == "a" && this.hour == 12) {
+                    this.hour = 0;
+                }
+            }
+            
+            if (this.weekday && !this.day && !this.days) {
+                var temp = Date[this.weekday]();
+                this.day = temp.getDate();
+                if (temp.getMonth() !== today.getMonth()) {
+                    this.month = temp.getMonth();
+                }
+            }
+            
+            if ((this.month || this.month === 0) && !this.day) { 
+                this.day = 1; 
+            }
+            
+            if (!this.orient && !this.operator && this.unit == "week" && this.value && !this.day && !this.month) {
+                return Date.today().setWeek(this.value);
+            }
+
+            if (expression && this.timezone && this.day && this.days) {
+                this.day = this.days;
+            }
+            
+            return (expression) ? today.add(this) : today.set(this);
+        }
+    };
+
+    var _ = $D.Parsing.Operators, g = $D.Grammar, t = $D.Translator, _fn;
+
+    g.datePartDelimiter = _.rtoken(/^([\s\-\.\,\/\x27]+)/); 
+    g.timePartDelimiter = _.stoken(":");
+    g.whiteSpace = _.rtoken(/^\s*/);
+    g.generalDelimiter = _.rtoken(/^(([\s\,]|at|@|on)+)/);
+  
+    var _C = {};
+    g.ctoken = function (keys) {
+        var fn = _C[keys];
+        if (! fn) {
+            var c = $C.regexPatterns;
+            var kx = keys.split(/\s+/), px = []; 
+            for (var i = 0; i < kx.length ; i++) {
+                px.push(_.replace(_.rtoken(c[kx[i]]), kx[i]));
+            }
+            fn = _C[keys] = _.any.apply(null, px);
+        }
+        return fn;
+    };
+    g.ctoken2 = function (key) { 
+        return _.rtoken($C.regexPatterns[key]);
+    };
+
+    // hour, minute, second, meridian, timezone
+    g.h = _.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/), t.hour));
+    g.hh = _.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/), t.hour));
+    g.H = _.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/), t.hour));
+    g.HH = _.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/), t.hour));
+    g.m = _.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/), t.minute));
+    g.mm = _.cache(_.process(_.rtoken(/^[0-5][0-9]/), t.minute));
+    g.s = _.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/), t.second));
+    g.ss = _.cache(_.process(_.rtoken(/^[0-5][0-9]/), t.second));
+    g.hms = _.cache(_.sequence([g.H, g.m, g.s], g.timePartDelimiter));
+  
+    // _.min(1, _.set([ g.H, g.m, g.s ], g._t));
+    g.t = _.cache(_.process(g.ctoken2("shortMeridian"), t.meridian));
+    g.tt = _.cache(_.process(g.ctoken2("longMeridian"), t.meridian));
+    g.z = _.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/), t.timezone));
+    g.zz = _.cache(_.process(_.rtoken(/^((\+|\-)\s*\d\d\d\d)|((\+|\-)\d\d\:?\d\d)/), t.timezone));
+    
+    g.zzz = _.cache(_.process(g.ctoken2("timezone"), t.timezone));
+    g.timeSuffix = _.each(_.ignore(g.whiteSpace), _.set([ g.tt, g.zzz ]));
+    g.time = _.each(_.optional(_.ignore(_.stoken("T"))), g.hms, g.timeSuffix);
+    	  
+    // days, months, years
+    g.d = _.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/), 
+        _.optional(g.ctoken2("ordinalSuffix"))), t.day));
+    g.dd = _.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/), 
+        _.optional(g.ctoken2("ordinalSuffix"))), t.day));
+    g.ddd = g.dddd = _.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"), 
+        function (s) { 
+            return function () { 
+                this.weekday = s; 
+            }; 
+        }
+    ));
+    g.M = _.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/), t.month));
+    g.MM = _.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/), t.month));
+    g.MMM = g.MMMM = _.cache(_.process(
+        g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"), t.month));
+    g.y = _.cache(_.process(_.rtoken(/^(\d\d?)/), t.year));
+    g.yy = _.cache(_.process(_.rtoken(/^(\d\d)/), t.year));
+    g.yyy = _.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/), t.year));
+    g.yyyy = _.cache(_.process(_.rtoken(/^(\d\d\d\d)/), t.year));
+	
+	// rolling these up into general purpose rules
+    _fn = function () { 
+        return _.each(_.any.apply(null, arguments), _.not(g.ctoken2("timeContext")));
+    };
+    
+    g.day = _fn(g.d, g.dd); 
+    g.month = _fn(g.M, g.MMM); 
+    g.year = _fn(g.yyyy, g.yy);
+
+    // relative date / time expressions
+    g.orientation = _.process(g.ctoken("past future"), 
+        function (s) { 
+            return function () { 
+                this.orient = s; 
+            }; 
+        }
+    );
+    g.operator = _.process(g.ctoken("add subtract"), 
+        function (s) { 
+            return function () { 
+                this.operator = s; 
+            }; 
+        }
+    );  
+    g.rday = _.process(g.ctoken("yesterday tomorrow today now"), t.rday);
+    g.unit = _.process(g.ctoken("second minute hour day week month year"), 
+        function (s) { 
+            return function () { 
+                this.unit = s; 
+            }; 
+        }
+    );
+    g.value = _.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/), 
+        function (s) { 
+            return function () { 
+                this.value = s.replace(/\D/g, ""); 
+            }; 
+        }
+    );
+    g.expression = _.set([ g.rday, g.operator, g.value, g.unit, g.orientation, g.ddd, g.MMM ]);
+
+    // pre-loaded rules for different date part order preferences
+    _fn = function () { 
+        return  _.set(arguments, g.datePartDelimiter); 
+    };
+    g.mdy = _fn(g.ddd, g.month, g.day, g.year);
+    g.ymd = _fn(g.ddd, g.year, g.month, g.day);
+    g.dmy = _fn(g.ddd, g.day, g.month, g.year);
+    g.date = function (s) { 
+        return ((g[$C.dateElementOrder] || g.mdy).call(this, s));
+    }; 
+
+    // parsing date format specifiers - ex: "h:m:s tt" 
+    // this little guy will generate a custom parser based
+    // on the format string, ex: g.format("h:m:s tt")
+    g.format = _.process(_.many(
+        _.any(
+        // translate format specifiers into grammar rules
+        _.process(
+        _.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/), 
+        function (fmt) { 
+        if (g[fmt]) { 
+            return g[fmt]; 
+        } else { 
+            throw $D.Parsing.Exception(fmt); 
+        }
+    }
+    ),
+    // translate separator tokens into token rules
+    _.process(
+    _.rtoken(/^[^dMyhHmstz]+/), // all legal separators 
+        function (s) { 
+            return _.ignore(_.stoken(s)); 
+        } 
+    )
+    )), 
+        // construct the parser ...
+        function (rules) { 
+            return _.process(_.each.apply(null, rules), t.finishExact); 
+        }
+    );
+    
+    var _F = {
+		//"M/d/yyyy": function (s) { 
+		//	var m = s.match(/^([0-2]\d|3[0-1]|\d)\/(1[0-2]|0\d|\d)\/(\d\d\d\d)/);
+		//	if (m!=null) { 
+		//		var r =  [ t.month.call(this,m[1]), t.day.call(this,m[2]), t.year.call(this,m[3]) ];
+		//		r = t.finishExact.call(this,r);
+		//		return [ r, "" ];
+		//	} else {
+		//		throw new Date.Parsing.Exception(s);
+		//	}
+		//}
+		//"M/d/yyyy": function (s) { return [ new Date(Date._parse(s)), ""]; }
+	}; 
+    var _get = function (f) { 
+        return _F[f] = (_F[f] || g.format(f)[0]);      
+    };
+  
+    g.formats = function (fx) {
+        if (fx instanceof Array) {
+            var rx = []; 
+            for (var i = 0 ; i < fx.length ; i++) {
+                rx.push(_get(fx[i])); 
+            }
+            return _.any.apply(null, rx);
+        } else { 
+            return _get(fx); 
+        }
+    };
+
+	// check for these formats first
+    g._formats = g.formats([
+        "\"yyyy-MM-ddTHH:mm:ssZ\"",
+        "yyyy-MM-ddTHH:mm:ssZ",
+        "yyyy-MM-ddTHH:mm:ssz",
+        "yyyy-MM-ddTHH:mm:ss",
+        "yyyy-MM-ddTHH:mmZ",
+        "yyyy-MM-ddTHH:mmz",
+        "yyyy-MM-ddTHH:mm",
+        "ddd, MMM dd, yyyy H:mm:ss tt",
+        "ddd MMM d yyyy HH:mm:ss zzz",
+        "MMddyyyy",
+        "ddMMyyyy",
+        "Mddyyyy",
+        "ddMyyyy",
+        "Mdyyyy",
+        "dMyyyy",
+        "yyyy",
+        "Mdyy",
+        "dMyy",
+        "d"
+    ]);
+
+	// starting rule for general purpose grammar
+    g._start = _.process(_.set([ g.date, g.time, g.expression ], 
+        g.generalDelimiter, g.whiteSpace), t.finish);
+	
+	// real starting rule: tries selected formats first, 
+	// then general purpose rule
+    g.start = function (s) {
+        try { 
+            var r = g._formats.call({}, s); 
+            if (r[1].length === 0) {
+                return r; 
+            }
+        } catch (e) {}
+        return g._start.call({}, s);
+    };
+	
+	$D._parse = $D.parse;
+
+    /**
+     * Converts the specified string value into its JavaScript Date equivalent using CultureInfo specific format information.
+     * 
+     * Example
+    <pre><code>
+    ///////////
+    // Dates //
+    ///////////
+
+    // 15-Oct-2004
+    var d1 = Date.parse("10/15/2004");
+
+    // 15-Oct-2004
+    var d1 = Date.parse("15-Oct-2004");
+
+    // 15-Oct-2004
+    var d1 = Date.parse("2004.10.15");
+
+    //Fri Oct 15, 2004
+    var d1 = Date.parse("Fri Oct 15, 2004");
+
+    ///////////
+    // Times //
+    ///////////
+
+    // Today at 10 PM.
+    var d1 = Date.parse("10 PM");
+
+    // Today at 10:30 PM.
+    var d1 = Date.parse("10:30 P.M.");
+
+    // Today at 6 AM.
+    var d1 = Date.parse("06am");
+
+    /////////////////////
+    // Dates and Times //
+    /////////////////////
+
+    // 8-July-2004 @ 10:30 PM
+    var d1 = Date.parse("July 8th, 2004, 10:30 PM");
+
+    // 1-July-2004 @ 10:30 PM
+    var d1 = Date.parse("2004-07-01T22:30:00");
+
+    ////////////////////
+    // Relative Dates //
+    ////////////////////
+
+    // Returns today's date. The string "today" is culture specific.
+    var d1 = Date.parse("today");
+
+    // Returns yesterday's date. The string "yesterday" is culture specific.
+    var d1 = Date.parse("yesterday");
+
+    // Returns the date of the next thursday.
+    var d1 = Date.parse("Next thursday");
+
+    // Returns the date of the most previous monday.
+    var d1 = Date.parse("last monday");
+
+    // Returns today's day + one year.
+    var d1 = Date.parse("next year");
+
+    ///////////////
+    // Date Math //
+    ///////////////
+
+    // Today + 2 days
+    var d1 = Date.parse("t+2");
+
+    // Today + 2 days
+    var d1 = Date.parse("today + 2 days");
+
+    // Today + 3 months
+    var d1 = Date.parse("t+3m");
+
+    // Today - 1 year
+    var d1 = Date.parse("today - 1 year");
+
+    // Today - 1 year
+    var d1 = Date.parse("t-1y"); 
+
+
+    /////////////////////////////
+    // Partial Dates and Times //
+    /////////////////////////////
+
+    // July 15th of this year.
+    var d1 = Date.parse("July 15");
+
+    // 15th day of current day and year.
+    var d1 = Date.parse("15");
+
+    // July 1st of current year at 10pm.
+    var d1 = Date.parse("7/1 10pm");
+    </code></pre>
+     *
+     * @param {String}   The string value to convert into a Date object [Required]
+     * @return {Date}    A Date object or null if the string cannot be converted into a Date.
+     */
+    $D.parse = function (s) {
+        var r = null; 
+        if (!s) { 
+            return null; 
+        }
+        if (s instanceof Date) {
+            return s;
+        }
+        try { 
+            r = $D.Grammar.start.call({}, s.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1")); 
+        } catch (e) { 
+            return null; 
+        }
+        return ((r[1].length === 0) ? r[0] : null);
+    };
+
+    $D.getParseFunction = function (fx) {
+        var fn = $D.Grammar.formats(fx);
+        return function (s) {
+            var r = null;
+            try { 
+                r = fn.call({}, s); 
+            } catch (e) { 
+                return null; 
+            }
+            return ((r[1].length === 0) ? r[0] : null);
+        };
+    };
+    
+    /**
+     * Converts the specified string value into its JavaScript Date equivalent using the specified format {String} or formats {Array} and the CultureInfo specific format information.
+     * The format of the string value must match one of the supplied formats exactly.
+     * 
+     * Example
+    <pre><code>
+    // 15-Oct-2004
+    var d1 = Date.parseExact("10/15/2004", "M/d/yyyy");
+
+    // 15-Oct-2004
+    var d1 = Date.parse("15-Oct-2004", "M-ddd-yyyy");
+
+    // 15-Oct-2004
+    var d1 = Date.parse("2004.10.15", "yyyy.MM.dd");
+
+    // Multiple formats
+    var d1 = Date.parseExact("10/15/2004", ["M/d/yyyy", "MMMM d, yyyy"]);
+    </code></pre>
+     *
+     * @param {String}   The string value to convert into a Date object [Required].
+     * @param {Object}   The expected format {String} or an array of expected formats {Array} of the date string [Required].
+     * @return {Date}    A Date object or null if the string cannot be converted into a Date.
+     */
+    $D.parseExact = function (s, fx) { 
+        return $D.getParseFunction(fx)(s); 
+    };	
+}());
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/prettify.js b/debian/missing-sources/scripts/prettify.js
new file mode 100644
index 0000000..fccf4f5
--- /dev/null
+++ b/debian/missing-sources/scripts/prettify.js
@@ -0,0 +1,1655 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ *
+ * <p>
+ * For a fairly comprehensive set of languages see the
+ * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
+ * file that came with this source.  At a minimum, the lexer should work on a
+ * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
+ * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
+ * and a subset of Perl, but, because of commenting conventions, doesn't work on
+ * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
+ * <p>
+ * Usage: <ol>
+ * <li> include this source file in an html page via
+ *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
+ * <li> define style rules.  See the example page for examples.
+ * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
+ *    {@code class=prettyprint.}
+ *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
+ *    printer needs to do more substantial DOM manipulations to support that, so
+ *    some css styles may not be preserved.
+ * </ol>
+ * That's it.  I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in, but if you wish, you can add
+ * another class to the {@code <pre>} or {@code <code>} element to specify the
+ * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
+ * starts with "lang-" followed by a file extension, specifies the file type.
+ * See the "lang-*.js" files in this directory for code that implements
+ * per-language file handlers.
+ * <p>
+ * Change log:<br>
+ * cbeust, 2006/08/22
+ * <blockquote>
+ *   Java annotations (start with "@") are now captured as literals ("lit")
+ * </blockquote>
+ * @requires console
+ */
+
+// JSLint declarations
+/*global console, document, navigator, setTimeout, window, define */
+
+/** @define {boolean} */
+var IN_GLOBAL_SCOPE = true;
+
+/**
+ * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
+ * UI events.
+ * If set to {@code false}, {@code prettyPrint()} is synchronous.
+ */
+window['PR_SHOULD_USE_CONTINUATION'] = true;
+
+/**
+ * Pretty print a chunk of code.
+ * @param {string} sourceCodeHtml The HTML to pretty print.
+ * @param {string} opt_langExtension The language name to use.
+ *     Typically, a filename extension like 'cpp' or 'java'.
+ * @param {number|boolean} opt_numberLines True to number lines,
+ *     or the 1-indexed number of the first line in sourceCodeHtml.
+ * @return {string} code as html, but prettier
+ */
+var prettyPrintOne;
+/**
+ * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
+ * {@code class=prettyprint} and prettify them.
+ *
+ * @param {Function} opt_whenDone called when prettifying is done.
+ * @param {HTMLElement|HTMLDocument} opt_root an element or document
+ *   containing all the elements to pretty print.
+ *   Defaults to {@code document.body}.
+ */
+var prettyPrint;
+
+
+(function () {
+  var win = window;
+  // Keyword lists for various languages.
+  // We use things that coerce to strings to make them compact when minified
+  // and to defeat aggressive optimizers that fold large string constants.
+  var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
+  var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
+      "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
+      "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
+  var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
+      "new,operator,private,protected,public,this,throw,true,try,typeof"];
+  var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
+      "concept,concept_map,const_cast,constexpr,decltype,delegate," +
+      "dynamic_cast,explicit,export,friend,generic,late_check," +
+      "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
+      "static_cast,template,typeid,typename,using,virtual,where"];
+  var JAVA_KEYWORDS = [COMMON_KEYWORDS,
+      "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
+      "instanceof,interface,null,native,package,strictfp,super,synchronized," +
+      "throws,transient"];
+  var CSHARP_KEYWORDS = [COMMON_KEYWORDS,
+      "abstract,as,base,bool,by,byte,checked,decimal,delegate,descending," +
+      "dynamic,event,finally,fixed,foreach,from,group,implicit,in,interface," +
+      "internal,into,is,let,lock,null,object,out,override,orderby,params," +
+      "partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong," +
+      "unchecked,unsafe,ushort,var,virtual,where"];
+  var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
+      "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
+      "throw,true,try,unless,until,when,while,yes";
+  var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
+      "debugger,eval,export,function,get,null,set,undefined,var,with," +
+      "Infinity,NaN"];
+  var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
+      "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
+      "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
+  var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
+      "elif,except,exec,finally,from,global,import,in,is,lambda," +
+      "nonlocal,not,or,pass,print,raise,try,with,yield," +
+      "False,True,None"];
+  var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
+      "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
+      "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
+      "BEGIN,END"];
+   var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
+      "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
+      "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
+  var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
+      "function,in,local,set,then,until"];
+  var ALL_KEYWORDS = [
+      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
+      PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
+  var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
+
+  // token style names.  correspond to css classes
+  /**
+   * token style for a string literal
+   * @const
+   */
+  var PR_STRING = 'str';
+  /**
+   * token style for a keyword
+   * @const
+   */
+  var PR_KEYWORD = 'kwd';
+  /**
+   * token style for a comment
+   * @const
+   */
+  var PR_COMMENT = 'com';
+  /**
+   * token style for a type
+   * @const
+   */
+  var PR_TYPE = 'typ';
+  /**
+   * token style for a literal value.  e.g. 1, null, true.
+   * @const
+   */
+  var PR_LITERAL = 'lit';
+  /**
+   * token style for a punctuation string.
+   * @const
+   */
+  var PR_PUNCTUATION = 'pun';
+  /**
+   * token style for plain text.
+   * @const
+   */
+  var PR_PLAIN = 'pln';
+
+  /**
+   * token style for an sgml tag.
+   * @const
+   */
+  var PR_TAG = 'tag';
+  /**
+   * token style for a markup declaration such as a DOCTYPE.
+   * @const
+   */
+  var PR_DECLARATION = 'dec';
+  /**
+   * token style for embedded source.
+   * @const
+   */
+  var PR_SOURCE = 'src';
+  /**
+   * token style for an sgml attribute name.
+   * @const
+   */
+  var PR_ATTRIB_NAME = 'atn';
+  /**
+   * token style for an sgml attribute value.
+   * @const
+   */
+  var PR_ATTRIB_VALUE = 'atv';
+
+  /**
+   * A class that indicates a section of markup that is not code, e.g. to allow
+   * embedding of line numbers within code listings.
+   * @const
+   */
+  var PR_NOCODE = 'nocode';
+
+  
+  
+  /**
+   * A set of tokens that can precede a regular expression literal in
+   * javascript
+   * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
+   * has the full list, but I've removed ones that might be problematic when
+   * seen in languages that don't support regular expression literals.
+   *
+   * <p>Specifically, I've removed any keywords that can't precede a regexp
+   * literal in a syntactically legal javascript program, and I've removed the
+   * "in" keyword since it's not a keyword in many languages, and might be used
+   * as a count of inches.
+   *
+   * <p>The link above does not accurately describe EcmaScript rules since
+   * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
+   * very well in practice.
+   *
+   * @private
+   * @const
+   */
+  var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
+  
+  // CAVEAT: this does not properly handle the case where a regular
+  // expression immediately follows another since a regular expression may
+  // have flags for case-sensitivity and the like.  Having regexp tokens
+  // adjacent is not valid in any language I'm aware of, so I'm punting.
+  // TODO: maybe style special characters inside a regexp as punctuation.
+
+  /**
+   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
+   * matches the union of the sets of strings matched by the input RegExp.
+   * Since it matches globally, if the input strings have a start-of-input
+   * anchor (/^.../), it is ignored for the purposes of unioning.
+   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
+   * @return {RegExp} a global regex.
+   */
+  function combinePrefixPatterns(regexs) {
+    var capturedGroupIndex = 0;
+  
+    var needToFoldCase = false;
+    var ignoreCase = false;
+    for (var i = 0, n = regexs.length; i < n; ++i) {
+      var regex = regexs[i];
+      if (regex.ignoreCase) {
+        ignoreCase = true;
+      } else if (/[a-z]/i.test(regex.source.replace(
+                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
+        needToFoldCase = true;
+        ignoreCase = false;
+        break;
+      }
+    }
+  
+    var escapeCharToCodeUnit = {
+      'b': 8,
+      't': 9,
+      'n': 0xa,
+      'v': 0xb,
+      'f': 0xc,
+      'r': 0xd
+    };
+  
+    function decodeEscape(charsetPart) {
+      var cc0 = charsetPart.charCodeAt(0);
+      if (cc0 !== 92 /* \\ */) {
+        return cc0;
+      }
+      var c1 = charsetPart.charAt(1);
+      cc0 = escapeCharToCodeUnit[c1];
+      if (cc0) {
+        return cc0;
+      } else if ('0' <= c1 && c1 <= '7') {
+        return parseInt(charsetPart.substring(1), 8);
+      } else if (c1 === 'u' || c1 === 'x') {
+        return parseInt(charsetPart.substring(2), 16);
+      } else {
+        return charsetPart.charCodeAt(1);
+      }
+    }
+  
+    function encodeEscape(charCode) {
+      if (charCode < 0x20) {
+        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
+      }
+      var ch = String.fromCharCode(charCode);
+      return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
+          ? "\\" + ch : ch;
+    }
+  
+    function caseFoldCharset(charSet) {
+      var charsetParts = charSet.substring(1, charSet.length - 1).match(
+          new RegExp(
+              '\\\\u[0-9A-Fa-f]{4}'
+              + '|\\\\x[0-9A-Fa-f]{2}'
+              + '|\\\\[0-3][0-7]{0,2}'
+              + '|\\\\[0-7]{1,2}'
+              + '|\\\\[\\s\\S]'
+              + '|-'
+              + '|[^-\\\\]',
+              'g'));
+      var ranges = [];
+      var inverse = charsetParts[0] === '^';
+  
+      var out = ['['];
+      if (inverse) { out.push('^'); }
+  
+      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
+        var p = charsetParts[i];
+        if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
+          out.push(p);
+        } else {
+          var start = decodeEscape(p);
+          var end;
+          if (i + 2 < n && '-' === charsetParts[i + 1]) {
+            end = decodeEscape(charsetParts[i + 2]);
+            i += 2;
+          } else {
+            end = start;
+          }
+          ranges.push([start, end]);
+          // If the range might intersect letters, then expand it.
+          // This case handling is too simplistic.
+          // It does not deal with non-latin case folding.
+          // It works for latin source code identifiers though.
+          if (!(end < 65 || start > 122)) {
+            if (!(end < 65 || start > 90)) {
+              ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
+            }
+            if (!(end < 97 || start > 122)) {
+              ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
+            }
+          }
+        }
+      }
+  
+      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
+      // -> [[1, 12], [14, 14], [16, 17]]
+      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
+      var consolidatedRanges = [];
+      var lastRange = [];
+      for (var i = 0; i < ranges.length; ++i) {
+        var range = ranges[i];
+        if (range[0] <= lastRange[1] + 1) {
+          lastRange[1] = Math.max(lastRange[1], range[1]);
+        } else {
+          consolidatedRanges.push(lastRange = range);
+        }
+      }
+  
+      for (var i = 0; i < consolidatedRanges.length; ++i) {
+        var range = consolidatedRanges[i];
+        out.push(encodeEscape(range[0]));
+        if (range[1] > range[0]) {
+          if (range[1] + 1 > range[0]) { out.push('-'); }
+          out.push(encodeEscape(range[1]));
+        }
+      }
+      out.push(']');
+      return out.join('');
+    }
+  
+    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
+      // Split into character sets, escape sequences, punctuation strings
+      // like ('(', '(?:', ')', '^'), and runs of characters that do not
+      // include any of the above.
+      var parts = regex.source.match(
+          new RegExp(
+              '(?:'
+              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
+              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
+              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
+              + '|\\\\[0-9]+'  // a back-reference or octal escape
+              + '|\\\\[^ux0-9]'  // other escape sequence
+              + '|\\(\\?[:!=]'  // start of a non-capturing group
+              + '|[\\(\\)\\^]'  // start/end of a group, or line start
+              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
+              + ')',
+              'g'));
+      var n = parts.length;
+  
+      // Maps captured group numbers to the number they will occupy in
+      // the output or to -1 if that has not been determined, or to
+      // undefined if they need not be capturing in the output.
+      var capturedGroups = [];
+  
+      // Walk over and identify back references to build the capturedGroups
+      // mapping.
+      for (var i = 0, groupIndex = 0; i < n; ++i) {
+        var p = parts[i];
+        if (p === '(') {
+          // groups are 1-indexed, so max group index is count of '('
+          ++groupIndex;
+        } else if ('\\' === p.charAt(0)) {
+          var decimalValue = +p.substring(1);
+          if (decimalValue) {
+            if (decimalValue <= groupIndex) {
+              capturedGroups[decimalValue] = -1;
+            } else {
+              // Replace with an unambiguous escape sequence so that
+              // an octal escape sequence does not turn into a backreference
+              // to a capturing group from an earlier regex.
+              parts[i] = encodeEscape(decimalValue);
+            }
+          }
+        }
+      }
+  
+      // Renumber groups and reduce capturing groups to non-capturing groups
+      // where possible.
+      for (var i = 1; i < capturedGroups.length; ++i) {
+        if (-1 === capturedGroups[i]) {
+          capturedGroups[i] = ++capturedGroupIndex;
+        }
+      }
+      for (var i = 0, groupIndex = 0; i < n; ++i) {
+        var p = parts[i];
+        if (p === '(') {
+          ++groupIndex;
+          if (!capturedGroups[groupIndex]) {
+            parts[i] = '(?:';
+          }
+        } else if ('\\' === p.charAt(0)) {
+          var decimalValue = +p.substring(1);
+          if (decimalValue && decimalValue <= groupIndex) {
+            parts[i] = '\\' + capturedGroups[decimalValue];
+          }
+        }
+      }
+  
+      // Remove any prefix anchors so that the output will match anywhere.
+      // ^^ really does mean an anchored match though.
+      for (var i = 0; i < n; ++i) {
+        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
+      }
+  
+      // Expand letters to groups to handle mixing of case-sensitive and
+      // case-insensitive patterns if necessary.
+      if (regex.ignoreCase && needToFoldCase) {
+        for (var i = 0; i < n; ++i) {
+          var p = parts[i];
+          var ch0 = p.charAt(0);
+          if (p.length >= 2 && ch0 === '[') {
+            parts[i] = caseFoldCharset(p);
+          } else if (ch0 !== '\\') {
+            // TODO: handle letters in numeric escapes.
+            parts[i] = p.replace(
+                /[a-zA-Z]/g,
+                function (ch) {
+                  var cc = ch.charCodeAt(0);
+                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
+                });
+          }
+        }
+      }
+  
+      return parts.join('');
+    }
+  
+    var rewritten = [];
+    for (var i = 0, n = regexs.length; i < n; ++i) {
+      var regex = regexs[i];
+      if (regex.global || regex.multiline) { throw new Error('' + regex); }
+      rewritten.push(
+          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
+    }
+  
+    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
+  }
+
+  /**
+   * Split markup into a string of source code and an array mapping ranges in
+   * that string to the text nodes in which they appear.
+   *
+   * <p>
+   * The HTML DOM structure:</p>
+   * <pre>
+   * (Element   "p"
+   *   (Element "b"
+   *     (Text  "print "))       ; #1
+   *   (Text    "'Hello '")      ; #2
+   *   (Element "br")            ; #3
+   *   (Text    "  + 'World';")) ; #4
+   * </pre>
+   * <p>
+   * corresponds to the HTML
+   * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
+   *
+   * <p>
+   * It will produce the output:</p>
+   * <pre>
+   * {
+   *   sourceCode: "print 'Hello '\n  + 'World';",
+   *   //                     1          2
+   *   //           012345678901234 5678901234567
+   *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
+   * }
+   * </pre>
+   * <p>
+   * where #1 is a reference to the {@code "print "} text node above, and so
+   * on for the other text nodes.
+   * </p>
+   *
+   * <p>
+   * The {@code} spans array is an array of pairs.  Even elements are the start
+   * indices of substrings, and odd elements are the text nodes (or BR elements)
+   * that contain the text for those substrings.
+   * Substrings continue until the next index or the end of the source.
+   * </p>
+   *
+   * @param {Node} node an HTML DOM subtree containing source-code.
+   * @param {boolean} isPreformatted true if white-space in text nodes should
+   *    be considered significant.
+   * @return {Object} source code and the text nodes in which they occur.
+   */
+  function extractSourceSpans(node, isPreformatted) {
+    var nocode = /(?:^|\s)nocode(?:\s|$)/;
+  
+    var chunks = [];
+    var length = 0;
+    var spans = [];
+    var k = 0;
+  
+    function walk(node) {
+      var type = node.nodeType;
+      if (type == 1) {  // Element
+        if (nocode.test(node.className)) { return; }
+        for (var child = node.firstChild; child; child = child.nextSibling) {
+          walk(child);
+        }
+        var nodeName = node.nodeName.toLowerCase();
+        if ('br' === nodeName || 'li' === nodeName) {
+          chunks[k] = '\n';
+          spans[k << 1] = length++;
+          spans[(k++ << 1) | 1] = node;
+        }
+      } else if (type == 3 || type == 4) {  // Text
+        var text = node.nodeValue;
+        if (text.length) {
+          if (!isPreformatted) {
+            text = text.replace(/[ \t\r\n]+/g, ' ');
+          } else {
+            text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
+          }
+          // TODO: handle tabs here?
+          chunks[k] = text;
+          spans[k << 1] = length;
+          length += text.length;
+          spans[(k++ << 1) | 1] = node;
+        }
+      }
+    }
+  
+    walk(node);
+  
+    return {
+      sourceCode: chunks.join('').replace(/\n$/, ''),
+      spans: spans
+    };
+  }
+
+  /**
+   * Apply the given language handler to sourceCode and add the resulting
+   * decorations to out.
+   * @param {number} basePos the index of sourceCode within the chunk of source
+   *    whose decorations are already present on out.
+   */
+  function appendDecorations(basePos, sourceCode, langHandler, out) {
+    if (!sourceCode) { return; }
+    var job = {
+      sourceCode: sourceCode,
+      basePos: basePos
+    };
+    langHandler(job);
+    out.push.apply(out, job.decorations);
+  }
+
+  var notWs = /\S/;
+
+  /**
+   * Given an element, if it contains only one child element and any text nodes
+   * it contains contain only space characters, return the sole child element.
+   * Otherwise returns undefined.
+   * <p>
+   * This is meant to return the CODE element in {@code <pre><code ...>} when
+   * there is a single child element that contains all the non-space textual
+   * content, but not to return anything where there are multiple child elements
+   * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
+   * is textual content.
+   */
+  function childContentWrapper(element) {
+    var wrapper = undefined;
+    for (var c = element.firstChild; c; c = c.nextSibling) {
+      var type = c.nodeType;
+      wrapper = (type === 1)  // Element Node
+          ? (wrapper ? element : c)
+          : (type === 3)  // Text Node
+          ? (notWs.test(c.nodeValue) ? element : wrapper)
+          : wrapper;
+    }
+    return wrapper === element ? undefined : wrapper;
+  }
+
+  /** Given triples of [style, pattern, context] returns a lexing function,
+    * The lexing function interprets the patterns to find token boundaries and
+    * returns a decoration list of the form
+    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+    * where index_n is an index into the sourceCode, and style_n is a style
+    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
+    * all characters in sourceCode[index_n-1:index_n].
+    *
+    * The stylePatterns is a list whose elements have the form
+    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
+    *
+    * Style is a style constant like PR_PLAIN, or can be a string of the
+    * form 'lang-FOO', where FOO is a language extension describing the
+    * language of the portion of the token in $1 after pattern executes.
+    * E.g., if style is 'lang-lisp', and group 1 contains the text
+    * '(hello (world))', then that portion of the token will be passed to the
+    * registered lisp handler for formatting.
+    * The text before and after group 1 will be restyled using this decorator
+    * so decorators should take care that this doesn't result in infinite
+    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
+    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
+    * '<script>foo()<\/script>', which would cause the current decorator to
+    * be called with '<script>' which would not match the same rule since
+    * group 1 must not be empty, so it would be instead styled as PR_TAG by
+    * the generic tag rule.  The handler registered for the 'js' extension would
+    * then be called with 'foo()', and finally, the current decorator would
+    * be called with '<\/script>' which would not match the original rule and
+    * so the generic tag rule would identify it as a tag.
+    *
+    * Pattern must only match prefixes, and if it matches a prefix, then that
+    * match is considered a token with the same style.
+    *
+    * Context is applied to the last non-whitespace, non-comment token
+    * recognized.
+    *
+    * Shortcut is an optional string of characters, any of which, if the first
+    * character, gurantee that this pattern and only this pattern matches.
+    *
+    * @param {Array} shortcutStylePatterns patterns that always start with
+    *   a known character.  Must have a shortcut string.
+    * @param {Array} fallthroughStylePatterns patterns that will be tried in
+    *   order if the shortcut ones fail.  May have shortcuts.
+    *
+    * @return {function (Object)} a
+    *   function that takes source code and returns a list of decorations.
+    */
+  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
+    var shortcuts = {};
+    var tokenizer;
+    (function () {
+      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
+      var allRegexs = [];
+      var regexKeys = {};
+      for (var i = 0, n = allPatterns.length; i < n; ++i) {
+        var patternParts = allPatterns[i];
+        var shortcutChars = patternParts[3];
+        if (shortcutChars) {
+          for (var c = shortcutChars.length; --c >= 0;) {
+            shortcuts[shortcutChars.charAt(c)] = patternParts;
+          }
+        }
+        var regex = patternParts[1];
+        var k = '' + regex;
+        if (!regexKeys.hasOwnProperty(k)) {
+          allRegexs.push(regex);
+          regexKeys[k] = null;
+        }
+      }
+      allRegexs.push(/[\0-\uffff]/);
+      tokenizer = combinePrefixPatterns(allRegexs);
+    })();
+
+    var nPatterns = fallthroughStylePatterns.length;
+
+    /**
+     * Lexes job.sourceCode and produces an output array job.decorations of
+     * style classes preceded by the position at which they start in
+     * job.sourceCode in order.
+     *
+     * @param {Object} job an object like <pre>{
+     *    sourceCode: {string} sourceText plain text,
+     *    basePos: {int} position of job.sourceCode in the larger chunk of
+     *        sourceCode.
+     * }</pre>
+     */
+    var decorate = function (job) {
+      var sourceCode = job.sourceCode, basePos = job.basePos;
+      /** Even entries are positions in source in ascending order.  Odd enties
+        * are style markers (e.g., PR_COMMENT) that run from that position until
+        * the end.
+        * @type {Array.<number|string>}
+        */
+      var decorations = [basePos, PR_PLAIN];
+      var pos = 0;  // index into sourceCode
+      var tokens = sourceCode.match(tokenizer) || [];
+      var styleCache = {};
+
+      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
+        var token = tokens[ti];
+        var style = styleCache[token];
+        var match = void 0;
+
+        var isEmbedded;
+        if (typeof style === 'string') {
+          isEmbedded = false;
+        } else {
+          var patternParts = shortcuts[token.charAt(0)];
+          if (patternParts) {
+            match = token.match(patternParts[1]);
+            style = patternParts[0];
+          } else {
+            for (var i = 0; i < nPatterns; ++i) {
+              patternParts = fallthroughStylePatterns[i];
+              match = token.match(patternParts[1]);
+              if (match) {
+                style = patternParts[0];
+                break;
+              }
+            }
+
+            if (!match) {  // make sure that we make progress
+              style = PR_PLAIN;
+            }
+          }
+
+          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
+          if (isEmbedded && !(match && typeof match[1] === 'string')) {
+            isEmbedded = false;
+            style = PR_SOURCE;
+          }
+
+          if (!isEmbedded) { styleCache[token] = style; }
+        }
+
+        var tokenStart = pos;
+        pos += token.length;
+
+        if (!isEmbedded) {
+          decorations.push(basePos + tokenStart, style);
+        } else {  // Treat group 1 as an embedded block of source code.
+          var embeddedSource = match[1];
+          var embeddedSourceStart = token.indexOf(embeddedSource);
+          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
+          if (match[2]) {
+            // If embeddedSource can be blank, then it would match at the
+            // beginning which would cause us to infinitely recurse on the
+            // entire token, so we catch the right context in match[2].
+            embeddedSourceEnd = token.length - match[2].length;
+            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
+          }
+          var lang = style.substring(5);
+          // Decorate the left of the embedded source
+          appendDecorations(
+              basePos + tokenStart,
+              token.substring(0, embeddedSourceStart),
+              decorate, decorations);
+          // Decorate the embedded source
+          appendDecorations(
+              basePos + tokenStart + embeddedSourceStart,
+              embeddedSource,
+              langHandlerForExtension(lang, embeddedSource),
+              decorations);
+          // Decorate the right of the embedded section
+          appendDecorations(
+              basePos + tokenStart + embeddedSourceEnd,
+              token.substring(embeddedSourceEnd),
+              decorate, decorations);
+        }
+      }
+      job.decorations = decorations;
+    };
+    return decorate;
+  }
+
+  /** returns a function that produces a list of decorations from source text.
+    *
+    * This code treats ", ', and ` as string delimiters, and \ as a string
+    * escape.  It does not recognize perl's qq() style strings.
+    * It has no special handling for double delimiter escapes as in basic, or
+    * the tripled delimiters used in python, but should work on those regardless
+    * although in those cases a single string literal may be broken up into
+    * multiple adjacent string literals.
+    *
+    * It recognizes C, C++, and shell style comments.
+    *
+    * @param {Object} options a set of optional parameters.
+    * @return {function (Object)} a function that examines the source code
+    *     in the input job and builds the decoration list.
+    */
+  function sourceDecorator(options) {
+    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
+    if (options['tripleQuotedStrings']) {
+      // '''multi-line-string''', 'single-line-string', and double-quoted
+      shortcutStylePatterns.push(
+          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
+           null, '\'"']);
+    } else if (options['multiLineStrings']) {
+      // 'multi-line-string', "multi-line-string"
+      shortcutStylePatterns.push(
+          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
+           null, '\'"`']);
+    } else {
+      // 'single-line-string', "single-line-string"
+      shortcutStylePatterns.push(
+          [PR_STRING,
+           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
+           null, '"\'']);
+    }
+    if (options['verbatimStrings']) {
+      // verbatim-string-literal production from the C# grammar.  See issue 93.
+      fallthroughStylePatterns.push(
+          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
+    }
+    var hc = options['hashComments'];
+    if (hc) {
+      if (options['cStyleComments']) {
+        if (hc > 1) {  // multiline hash comments
+          shortcutStylePatterns.push(
+              [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
+        } else {
+          // Stop C preprocessor declarations at an unclosed open comment
+          shortcutStylePatterns.push(
+              [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
+               null, '#']);
+        }
+        // #include <stdio.h>
+        fallthroughStylePatterns.push(
+            [PR_STRING,
+             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
+             null]);
+      } else {
+        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
+      }
+    }
+    if (options['cStyleComments']) {
+      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
+      fallthroughStylePatterns.push(
+          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
+    }
+    var regexLiterals = options['regexLiterals'];
+    if (regexLiterals) {
+      /**
+       * @const
+       */
+      var regexExcls = regexLiterals > 1
+        ? ''  // Multiline regex literals
+        : '\n\r';
+      /**
+       * @const
+       */
+      var regexAny = regexExcls ? '.' : '[\\S\\s]';
+      /**
+       * @const
+       */
+      var REGEX_LITERAL = (
+          // A regular expression literal starts with a slash that is
+          // not followed by * or / so that it is not confused with
+          // comments.
+          '/(?=[^/*' + regexExcls + '])'
+          // and then contains any number of raw characters,
+          + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
+          // escape sequences (\x5C),
+          +    '|\\x5C' + regexAny
+          // or non-nesting character sets (\x5B\x5D);
+          +    '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
+          +             '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
+          // finally closed by a /.
+          + '/');
+      fallthroughStylePatterns.push(
+          ['lang-regex',
+           RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
+           ]);
+    }
+
+    var types = options['types'];
+    if (types) {
+      fallthroughStylePatterns.push([PR_TYPE, types]);
+    }
+
+    var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
+    if (keywords.length) {
+      fallthroughStylePatterns.push(
+          [PR_KEYWORD,
+           new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
+           null]);
+    }
+
+    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
+
+    var punctuation =
+      // The Bash man page says
+
+      // A word is a sequence of characters considered as a single
+      // unit by GRUB. Words are separated by metacharacters,
+      // which are the following plus space, tab, and newline: { }
+      // | & $ ; < >
+      // ...
+      
+      // A word beginning with # causes that word and all remaining
+      // characters on that line to be ignored.
+
+      // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
+      // comment but empirically
+      // $ echo {#}
+      // {#}
+      // $ echo \$#
+      // $#
+      // $ echo }#
+      // }#
+
+      // so /(?:^|[|&;<>\s])/ is more appropriate.
+
+      // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
+      // suggests that this definition is compatible with a
+      // default mode that tries to use a single token definition
+      // to recognize both bash/python style comments and C
+      // preprocessor directives.
+
+      // This definition of punctuation does not include # in the list of
+      // follow-on exclusions, so # will not be broken before if preceeded
+      // by a punctuation character.  We could try to exclude # after
+      // [|&;<>] but that doesn't seem to cause many major problems.
+      // If that does turn out to be a problem, we should change the below
+      // when hc is truthy to include # in the run of punctuation characters
+      // only when not followint [|&;<>].
+      '^.[^\\s\\w.$@\'"`/\\\\]*';
+    if (options['regexLiterals']) {
+      punctuation += '(?!\s*\/)';
+    }
+
+    fallthroughStylePatterns.push(
+        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
+        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
+        [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
+        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
+        [PR_LITERAL,
+         new RegExp(
+             '^(?:'
+             // A hex number
+             + '0x[a-f0-9]+'
+             // or an octal or decimal number,
+             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
+             // possibly in scientific notation
+             + '(?:e[+\\-]?\\d+)?'
+             + ')'
+             // with an optional modifier like UL for unsigned long
+             + '[a-z]*', 'i'),
+         null, '0123456789'],
+        // Don't treat escaped quotes in bash as starting strings.
+        // See issue 144.
+        [PR_PLAIN,       /^\\[\s\S]?/, null],
+        [PR_PUNCTUATION, new RegExp(punctuation), null]);
+
+    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
+  }
+
+  var decorateSource = sourceDecorator({
+        'keywords': ALL_KEYWORDS,
+        'hashComments': true,
+        'cStyleComments': true,
+        'multiLineStrings': true,
+        'regexLiterals': true
+      });
+
+  /**
+   * Given a DOM subtree, wraps it in a list, and puts each line into its own
+   * list item.
+   *
+   * @param {Node} node modified in place.  Its content is pulled into an
+   *     HTMLOListElement, and each line is moved into a separate list item.
+   *     This requires cloning elements, so the input might not have unique
+   *     IDs after numbering.
+   * @param {boolean} isPreformatted true iff white-space in text nodes should
+   *     be treated as significant.
+   */
+  function numberLines(node, opt_startLineNum, isPreformatted) {
+    var nocode = /(?:^|\s)nocode(?:\s|$)/;
+    var lineBreak = /\r\n?|\n/;
+  
+    var document = node.ownerDocument;
+  
+    var li = document.createElement('li');
+    while (node.firstChild) {
+      li.appendChild(node.firstChild);
+    }
+    // An array of lines.  We split below, so this is initialized to one
+    // un-split line.
+    var listItems = [li];
+  
+    function walk(node) {
+      var type = node.nodeType;
+      if (type == 1 && !nocode.test(node.className)) {  // Element
+        if ('br' === node.nodeName) {
+          breakAfter(node);
+          // Discard the <BR> since it is now flush against a </LI>.
+          if (node.parentNode) {
+            node.parentNode.removeChild(node);
+          }
+        } else {
+          for (var child = node.firstChild; child; child = child.nextSibling) {
+            walk(child);
+          }
+        }
+      } else if ((type == 3 || type == 4) && isPreformatted) {  // Text
+        var text = node.nodeValue;
+        var match = text.match(lineBreak);
+        if (match) {
+          var firstLine = text.substring(0, match.index);
+          node.nodeValue = firstLine;
+          var tail = text.substring(match.index + match[0].length);
+          if (tail) {
+            var parent = node.parentNode;
+            parent.insertBefore(
+              document.createTextNode(tail), node.nextSibling);
+          }
+          breakAfter(node);
+          if (!firstLine) {
+            // Don't leave blank text nodes in the DOM.
+            node.parentNode.removeChild(node);
+          }
+        }
+      }
+    }
+  
+    // Split a line after the given node.
+    function breakAfter(lineEndNode) {
+      // If there's nothing to the right, then we can skip ending the line
+      // here, and move root-wards since splitting just before an end-tag
+      // would require us to create a bunch of empty copies.
+      while (!lineEndNode.nextSibling) {
+        lineEndNode = lineEndNode.parentNode;
+        if (!lineEndNode) { return; }
+      }
+  
+      function breakLeftOf(limit, copy) {
+        // Clone shallowly if this node needs to be on both sides of the break.
+        var rightSide = copy ? limit.cloneNode(false) : limit;
+        var parent = limit.parentNode;
+        if (parent) {
+          // We clone the parent chain.
+          // This helps us resurrect important styling elements that cross lines.
+          // E.g. in <i>Foo<br>Bar</i>
+          // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
+          var parentClone = breakLeftOf(parent, 1);
+          // Move the clone and everything to the right of the original
+          // onto the cloned parent.
+          var next = limit.nextSibling;
+          parentClone.appendChild(rightSide);
+          for (var sibling = next; sibling; sibling = next) {
+            next = sibling.nextSibling;
+            parentClone.appendChild(sibling);
+          }
+        }
+        return rightSide;
+      }
+  
+      var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
+  
+      // Walk the parent chain until we reach an unattached LI.
+      for (var parent;
+           // Check nodeType since IE invents document fragments.
+           (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
+        copiedListItem = parent;
+      }
+      // Put it on the list of lines for later processing.
+      listItems.push(copiedListItem);
+    }
+  
+    // Split lines while there are lines left to split.
+    for (var i = 0;  // Number of lines that have been split so far.
+         i < listItems.length;  // length updated by breakAfter calls.
+         ++i) {
+      walk(listItems[i]);
+    }
+  
+    // Make sure numeric indices show correctly.
+    if (opt_startLineNum === (opt_startLineNum|0)) {
+      listItems[0].setAttribute('value', opt_startLineNum);
+    }
+  
+    var ol = document.createElement('ol');
+    ol.className = 'linenums';
+    var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
+    for (var i = 0, n = listItems.length; i < n; ++i) {
+      li = listItems[i];
+      // Stick a class on the LIs so that stylesheets can
+      // color odd/even rows, or any other row pattern that
+      // is co-prime with 10.
+      li.className = 'L' + ((i + offset) % 10);
+      if (!li.firstChild) {
+        li.appendChild(document.createTextNode('\xA0'));
+      }
+      ol.appendChild(li);
+    }
+  
+    node.appendChild(ol);
+  }
+  /**
+   * Breaks {@code job.sourceCode} around style boundaries in
+   * {@code job.decorations} and modifies {@code job.sourceNode} in place.
+   * @param {Object} job like <pre>{
+   *    sourceCode: {string} source as plain text,
+   *    sourceNode: {HTMLElement} the element containing the source,
+   *    spans: {Array.<number|Node>} alternating span start indices into source
+   *       and the text node or element (e.g. {@code <BR>}) corresponding to that
+   *       span.
+   *    decorations: {Array.<number|string} an array of style classes preceded
+   *       by the position at which they start in job.sourceCode in order
+   * }</pre>
+   * @private
+   */
+  function recombineTagsAndDecorations(job) {
+    var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
+    isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
+    var newlineRe = /\n/g;
+  
+    var source = job.sourceCode;
+    var sourceLength = source.length;
+    // Index into source after the last code-unit recombined.
+    var sourceIndex = 0;
+  
+    var spans = job.spans;
+    var nSpans = spans.length;
+    // Index into spans after the last span which ends at or before sourceIndex.
+    var spanIndex = 0;
+  
+    var decorations = job.decorations;
+    var nDecorations = decorations.length;
+    // Index into decorations after the last decoration which ends at or before
+    // sourceIndex.
+    var decorationIndex = 0;
+  
+    // Remove all zero-length decorations.
+    decorations[nDecorations] = sourceLength;
+    var decPos, i;
+    for (i = decPos = 0; i < nDecorations;) {
+      if (decorations[i] !== decorations[i + 2]) {
+        decorations[decPos++] = decorations[i++];
+        decorations[decPos++] = decorations[i++];
+      } else {
+        i += 2;
+      }
+    }
+    nDecorations = decPos;
+  
+    // Simplify decorations.
+    for (i = decPos = 0; i < nDecorations;) {
+      var startPos = decorations[i];
+      // Conflate all adjacent decorations that use the same style.
+      var startDec = decorations[i + 1];
+      var end = i + 2;
+      while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
+        end += 2;
+      }
+      decorations[decPos++] = startPos;
+      decorations[decPos++] = startDec;
+      i = end;
+    }
+  
+    nDecorations = decorations.length = decPos;
+  
+    var sourceNode = job.sourceNode;
+    var oldDisplay;
+    if (sourceNode) {
+      oldDisplay = sourceNode.style.display;
+      sourceNode.style.display = 'none';
+    }
+    try {
+      var decoration = null;
+      while (spanIndex < nSpans) {
+        var spanStart = spans[spanIndex];
+        var spanEnd = spans[spanIndex + 2] || sourceLength;
+  
+        var decEnd = decorations[decorationIndex + 2] || sourceLength;
+  
+        var end = Math.min(spanEnd, decEnd);
+  
+        var textNode = spans[spanIndex + 1];
+        var styledText;
+        if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
+            // Don't introduce spans around empty text nodes.
+            && (styledText = source.substring(sourceIndex, end))) {
+          // This may seem bizarre, and it is.  Emitting LF on IE causes the
+          // code to display with spaces instead of line breaks.
+          // Emitting Windows standard issue linebreaks (CRLF) causes a blank
+          // space to appear at the beginning of every line but the first.
+          // Emitting an old Mac OS 9 line separator makes everything spiffy.
+          if (isIE8OrEarlier) {
+            styledText = styledText.replace(newlineRe, '\r');
+          }
+          textNode.nodeValue = styledText;
+          var document = textNode.ownerDocument;
+          var span = document.createElement('span');
+          span.className = decorations[decorationIndex + 1];
+          var parentNode = textNode.parentNode;
+          parentNode.replaceChild(span, textNode);
+          span.appendChild(textNode);
+          if (sourceIndex < spanEnd) {  // Split off a text node.
+            spans[spanIndex + 1] = textNode
+                // TODO: Possibly optimize by using '' if there's no flicker.
+                = document.createTextNode(source.substring(end, spanEnd));
+            parentNode.insertBefore(textNode, span.nextSibling);
+          }
+        }
+  
+        sourceIndex = end;
+  
+        if (sourceIndex >= spanEnd) {
+          spanIndex += 2;
+        }
+        if (sourceIndex >= decEnd) {
+          decorationIndex += 2;
+        }
+      }
+    } finally {
+      if (sourceNode) {
+        sourceNode.style.display = oldDisplay;
+      }
+    }
+  }
+
+  /** Maps language-specific file extensions to handlers. */
+  var langHandlerRegistry = {};
+  /** Register a language handler for the given file extensions.
+    * @param {function (Object)} handler a function from source code to a list
+    *      of decorations.  Takes a single argument job which describes the
+    *      state of the computation.   The single parameter has the form
+    *      {@code {
+    *        sourceCode: {string} as plain text.
+    *        decorations: {Array.<number|string>} an array of style classes
+    *                     preceded by the position at which they start in
+    *                     job.sourceCode in order.
+    *                     The language handler should assigned this field.
+    *        basePos: {int} the position of source in the larger source chunk.
+    *                 All positions in the output decorations array are relative
+    *                 to the larger source chunk.
+    *      } }
+    * @param {Array.<string>} fileExtensions
+    */
+  function registerLangHandler(handler, fileExtensions) {
+    for (var i = fileExtensions.length; --i >= 0;) {
+      var ext = fileExtensions[i];
+      if (!langHandlerRegistry.hasOwnProperty(ext)) {
+        langHandlerRegistry[ext] = handler;
+      } else if (win['console']) {
+        console['warn']('cannot override language handler %s', ext);
+      }
+    }
+  }
+  function langHandlerForExtension(extension, source) {
+    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
+      // Treat it as markup if the first non whitespace character is a < and
+      // the last non-whitespace character is a >.
+      extension = /^\s*</.test(source)
+          ? 'default-markup'
+          : 'default-code';
+    }
+    return langHandlerRegistry[extension];
+  }
+  registerLangHandler(decorateSource, ['default-code']);
+  registerLangHandler(
+      createSimpleLexer(
+          [],
+          [
+           [PR_PLAIN,       /^[^<?]+/],
+           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
+           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
+           // Unescaped content in an unknown language
+           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
+           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
+           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
+           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
+           // Unescaped content in javascript.  (Or possibly vbscript).
+           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
+           // Contains unescaped stylesheet content
+           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
+           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
+          ]),
+      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
+  registerLangHandler(
+      createSimpleLexer(
+          [
+           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
+           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
+           ],
+          [
+           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
+           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
+           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
+           [PR_PUNCTUATION,  /^[=<>\/]+/],
+           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
+           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
+           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
+           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
+           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
+           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
+           ]),
+      ['in.tag']);
+  registerLangHandler(
+      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
+  registerLangHandler(sourceDecorator({
+          'keywords': CPP_KEYWORDS,
+          'hashComments': true,
+          'cStyleComments': true,
+          'types': C_TYPES
+        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
+  registerLangHandler(sourceDecorator({
+          'keywords': 'null,true,false'
+        }), ['json']);
+  registerLangHandler(sourceDecorator({
+          'keywords': CSHARP_KEYWORDS,
+          'hashComments': true,
+          'cStyleComments': true,
+          'verbatimStrings': true,
+          'types': C_TYPES
+        }), ['cs']);
+  registerLangHandler(sourceDecorator({
+          'keywords': JAVA_KEYWORDS,
+          'cStyleComments': true
+        }), ['java']);
+  registerLangHandler(sourceDecorator({
+          'keywords': SH_KEYWORDS,
+          'hashComments': true,
+          'multiLineStrings': true
+        }), ['bash', 'bsh', 'csh', 'sh']);
+  registerLangHandler(sourceDecorator({
+          'keywords': PYTHON_KEYWORDS,
+          'hashComments': true,
+          'multiLineStrings': true,
+          'tripleQuotedStrings': true
+        }), ['cv', 'py', 'python']);
+  registerLangHandler(sourceDecorator({
+          'keywords': PERL_KEYWORDS,
+          'hashComments': true,
+          'multiLineStrings': true,
+          'regexLiterals': 2  // multiline regex literals
+        }), ['perl', 'pl', 'pm']);
+  registerLangHandler(sourceDecorator({
+          'keywords': RUBY_KEYWORDS,
+          'hashComments': true,
+          'multiLineStrings': true,
+          'regexLiterals': true
+        }), ['rb', 'ruby']);
+  registerLangHandler(sourceDecorator({
+          'keywords': JSCRIPT_KEYWORDS,
+          'cStyleComments': true,
+          'regexLiterals': true
+        }), ['javascript', 'js']);
+  registerLangHandler(sourceDecorator({
+          'keywords': COFFEE_KEYWORDS,
+          'hashComments': 3,  // ### style block comments
+          'cStyleComments': true,
+          'multilineStrings': true,
+          'tripleQuotedStrings': true,
+          'regexLiterals': true
+        }), ['coffee']);
+  registerLangHandler(sourceDecorator({
+          'keywords': RUST_KEYWORDS,
+          'cStyleComments': true,
+          'multilineStrings': true
+        }), ['rc', 'rs', 'rust']);
+  registerLangHandler(
+      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
+
+  function applyDecorator(job) {
+    var opt_langExtension = job.langExtension;
+
+    try {
+      // Extract tags, and convert the source code to plain text.
+      var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
+      /** Plain text. @type {string} */
+      var source = sourceAndSpans.sourceCode;
+      job.sourceCode = source;
+      job.spans = sourceAndSpans.spans;
+      job.basePos = 0;
+
+      // Apply the appropriate language handler
+      langHandlerForExtension(opt_langExtension, source)(job);
+
+      // Integrate the decorations and tags back into the source code,
+      // modifying the sourceNode in place.
+      recombineTagsAndDecorations(job);
+    } catch (e) {
+      if (win['console']) {
+        console['log'](e && e['stack'] || e);
+      }
+    }
+  }
+
+  /**
+   * Pretty print a chunk of code.
+   * @param sourceCodeHtml {string} The HTML to pretty print.
+   * @param opt_langExtension {string} The language name to use.
+   *     Typically, a filename extension like 'cpp' or 'java'.
+   * @param opt_numberLines {number|boolean} True to number lines,
+   *     or the 1-indexed number of the first line in sourceCodeHtml.
+   */
+  function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
+    var container = document.createElement('div');
+    // This could cause images to load and onload listeners to fire.
+    // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
+    // We assume that the inner HTML is from a trusted source.
+    // The pre-tag is required for IE8 which strips newlines from innerHTML
+    // when it is injected into a <pre> tag.
+    // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
+    // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
+    container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
+    container = container.firstChild;
+    if (opt_numberLines) {
+      numberLines(container, opt_numberLines, true);
+    }
+
+    var job = {
+      langExtension: opt_langExtension,
+      numberLines: opt_numberLines,
+      sourceNode: container,
+      pre: 1
+    };
+    applyDecorator(job);
+    return container.innerHTML;
+  }
+
+   /**
+    * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
+    * {@code class=prettyprint} and prettify them.
+    *
+    * @param {Function} opt_whenDone called when prettifying is done.
+    * @param {HTMLElement|HTMLDocument} opt_root an element or document
+    *   containing all the elements to pretty print.
+    *   Defaults to {@code document.body}.
+    */
+  function $prettyPrint(opt_whenDone, opt_root) {
+    var root = opt_root || document.body;
+    var doc = root.ownerDocument || document;
+    function byTagName(tn) { return root.getElementsByTagName(tn); }
+    // fetch a list of nodes to rewrite
+    var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
+    var elements = [];
+    for (var i = 0; i < codeSegments.length; ++i) {
+      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
+        elements.push(codeSegments[i][j]);
+      }
+    }
+    codeSegments = null;
+
+    var clock = Date;
+    if (!clock['now']) {
+      clock = { 'now': function () { return +(new Date); } };
+    }
+
+    // The loop is broken into a series of continuations to make sure that we
+    // don't make the browser unresponsive when rewriting a large page.
+    var k = 0;
+    var prettyPrintingJob;
+
+    var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
+    var prettyPrintRe = /\bprettyprint\b/;
+    var prettyPrintedRe = /\bprettyprinted\b/;
+    var preformattedTagNameRe = /pre|xmp/i;
+    var codeRe = /^code$/i;
+    var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
+    var EMPTY = {};
+
+    function doWork() {
+      var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
+                     clock['now']() + 250 /* ms */ :
+                     Infinity);
+      for (; k < elements.length && clock['now']() < endTime; k++) {
+        var cs = elements[k];
+
+        // Look for a preceding comment like
+        // <?prettify lang="..." linenums="..."?>
+        var attrs = EMPTY;
+        {
+          for (var preceder = cs; (preceder = preceder.previousSibling);) {
+            var nt = preceder.nodeType;
+            // <?foo?> is parsed by HTML 5 to a comment node (8)
+            // like <!--?foo?-->, but in XML is a processing instruction
+            var value = (nt === 7 || nt === 8) && preceder.nodeValue;
+            if (value
+                ? !/^\??prettify\b/.test(value)
+                : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
+              // Skip over white-space text nodes but not others.
+              break;
+            }
+            if (value) {
+              attrs = {};
+              value.replace(
+                  /\b(\w+)=([\w:.%+-]+)/g,
+                function (_, name, value) { attrs[name] = value; });
+              break;
+            }
+          }
+        }
+
+        var className = cs.className;
+        if ((attrs !== EMPTY || prettyPrintRe.test(className))
+            // Don't redo this if we've already done it.
+            // This allows recalling pretty print to just prettyprint elements
+            // that have been added to the page since last call.
+            && !prettyPrintedRe.test(className)) {
+
+          // make sure this is not nested in an already prettified element
+          var nested = false;
+          for (var p = cs.parentNode; p; p = p.parentNode) {
+            var tn = p.tagName;
+            if (preCodeXmpRe.test(tn)
+                && p.className && prettyPrintRe.test(p.className)) {
+              nested = true;
+              break;
+            }
+          }
+          if (!nested) {
+            // Mark done.  If we fail to prettyprint for whatever reason,
+            // we shouldn't try again.
+            cs.className += ' prettyprinted';
+
+            // If the classes includes a language extensions, use it.
+            // Language extensions can be specified like
+            //     <pre class="prettyprint lang-cpp">
+            // the language extension "cpp" is used to find a language handler
+            // as passed to PR.registerLangHandler.
+            // HTML5 recommends that a language be specified using "language-"
+            // as the prefix instead.  Google Code Prettify supports both.
+            // http://dev.w3.org/html5/spec-author-view/the-code-element.html
+            var langExtension = attrs['lang'];
+            if (!langExtension) {
+              langExtension = className.match(langExtensionRe);
+              // Support <pre class="prettyprint"><code class="language-c">
+              var wrapper;
+              if (!langExtension && (wrapper = childContentWrapper(cs))
+                  && codeRe.test(wrapper.tagName)) {
+                langExtension = wrapper.className.match(langExtensionRe);
+              }
+
+              if (langExtension) { langExtension = langExtension[1]; }
+            }
+
+            var preformatted;
+            if (preformattedTagNameRe.test(cs.tagName)) {
+              preformatted = 1;
+            } else {
+              var currentStyle = cs['currentStyle'];
+              var defaultView = doc.defaultView;
+              var whitespace = (
+                  currentStyle
+                  ? currentStyle['whiteSpace']
+                  : (defaultView
+                     && defaultView.getComputedStyle)
+                  ? defaultView.getComputedStyle(cs, null)
+                  .getPropertyValue('white-space')
+                  : 0);
+              preformatted = whitespace
+                  && 'pre' === whitespace.substring(0, 3);
+            }
+
+            // Look for a class like linenums or linenums:<n> where <n> is the
+            // 1-indexed number of the first line.
+            var lineNums = attrs['linenums'];
+            if (!(lineNums = lineNums === 'true' || +lineNums)) {
+              lineNums = className.match(/\blinenums\b(?::(\d+))?/);
+              lineNums =
+                lineNums
+                ? lineNums[1] && lineNums[1].length
+                  ? +lineNums[1] : true
+                : false;
+            }
+            if (lineNums) { numberLines(cs, lineNums, preformatted); }
+
+            // do the pretty printing
+            prettyPrintingJob = {
+              langExtension: langExtension,
+              sourceNode: cs,
+              numberLines: lineNums,
+              pre: preformatted
+            };
+            applyDecorator(prettyPrintingJob);
+          }
+        }
+      }
+      if (k < elements.length) {
+        // finish up in a continuation
+        setTimeout(doWork, 250);
+      } else if ('function' === typeof opt_whenDone) {
+        opt_whenDone();
+      }
+    }
+
+    doWork();
+  }
+
+  /**
+   * Contains functions for creating and registering new language handlers.
+   * @type {Object}
+   */
+  var PR = win['PR'] = {
+        'createSimpleLexer': createSimpleLexer,
+        'registerLangHandler': registerLangHandler,
+        'sourceDecorator': sourceDecorator,
+        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
+        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
+        'PR_COMMENT': PR_COMMENT,
+        'PR_DECLARATION': PR_DECLARATION,
+        'PR_KEYWORD': PR_KEYWORD,
+        'PR_LITERAL': PR_LITERAL,
+        'PR_NOCODE': PR_NOCODE,
+        'PR_PLAIN': PR_PLAIN,
+        'PR_PUNCTUATION': PR_PUNCTUATION,
+        'PR_SOURCE': PR_SOURCE,
+        'PR_STRING': PR_STRING,
+        'PR_TAG': PR_TAG,
+        'PR_TYPE': PR_TYPE,
+        'prettyPrintOne':
+           IN_GLOBAL_SCOPE
+             ? (win['prettyPrintOne'] = $prettyPrintOne)
+             : (prettyPrintOne = $prettyPrintOne),
+        'prettyPrint': prettyPrint =
+           IN_GLOBAL_SCOPE
+             ? (win['prettyPrint'] = $prettyPrint)
+             : (prettyPrint = $prettyPrint)
+      };
+
+  // Make PR available via the Asynchronous Module Definition (AMD) API.
+  // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
+  // The Asynchronous Module Definition (AMD) API specifies a
+  // mechanism for defining modules such that the module and its
+  // dependencies can be asynchronously loaded.
+  // ...
+  // To allow a clear indicator that a global define function (as
+  // needed for script src browser loading) conforms to the AMD API,
+  // any global define function SHOULD have a property called "amd"
+  // whose value is an object. This helps avoid conflict with any
+  // other existing JavaScript code that could have defined a define()
+  // function that does not conform to the AMD API.
+  if (typeof define === "function" && define['amd']) {
+    define("google-code-prettify", [], function () {
+      return PR; 
+    });
+  }
+})();
diff --git a/debian/missing-sources/scripts/sugarpak.js b/debian/missing-sources/scripts/sugarpak.js
new file mode 100644
index 0000000..3374f33
--- /dev/null
+++ b/debian/missing-sources/scripts/sugarpak.js
@@ -0,0 +1,475 @@
+/**
+ * @version: 1.0 Alpha-1
+ * @author: Coolite Inc. http://www.coolite.com/
+ * @date: 2008-04-13
+ * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * @website: http://www.datejs.com/
+ */
+
+/**
+ **************************************************************
+ ** SugarPak - Domain Specific Language -  Syntactical Sugar **
+ **************************************************************
+ */
+ 
+(function () {
+    var $D = Date, $P = $D.prototype, $C = $D.CultureInfo, $N = Number.prototype;
+
+    // private
+    $P._orient = +1;
+
+    // private
+    $P._nth = null;
+
+    // private
+    $P._is = false;
+
+    // private
+    $P._same = false;
+    
+    // private
+    $P._isSecond = false;
+
+    // private
+    $N._dateElement = "day";
+
+    /** 
+     * Moves the date to the next instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
+     * Example
+    <pre><code>
+    Date.today().next().friday();
+    Date.today().next().fri();
+    Date.today().next().march();
+    Date.today().next().mar();
+    Date.today().next().week();
+    </code></pre>
+     * 
+     * @return {Date}    date
+     */
+    $P.next = function () {
+        this._orient = +1;
+        return this;
+    };
+
+    /** 
+     * Creates a new Date (Date.today()) and moves the date to the next instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
+     * Example
+    <pre><code>
+    Date.next().friday();
+    Date.next().fri();
+    Date.next().march();
+    Date.next().mar();
+    Date.next().week();
+    </code></pre>
+     * 
+     * @return {Date}    date
+     */    
+    $D.next = function () {
+        return $D.today().next();
+    };
+
+    /** 
+     * Moves the date to the previous instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
+     * Example
+    <pre><code>
+    Date.today().last().friday();
+    Date.today().last().fri();
+    Date.today().last().march();
+    Date.today().last().mar();
+    Date.today().last().week();
+    </code></pre>
+     *  
+     * @return {Date}    date
+     */
+    $P.last = $P.prev = $P.previous = function () {
+        this._orient = -1;
+        return this;
+    };
+
+    /** 
+     * Creates a new Date (Date.today()) and moves the date to the previous instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
+     * Example
+    <pre><code>
+    Date.last().friday();
+    Date.last().fri();
+    Date.previous().march();
+    Date.prev().mar();
+    Date.last().week();
+    </code></pre>
+     *  
+     * @return {Date}    date
+     */
+    $D.last = $D.prev = $D.previous = function () {
+        return $D.today().last();
+    };    
+
+    /** 
+     * Performs a equality check when followed by either a month name, day name or .weekday() function.
+     * Example
+    <pre><code>
+    Date.today().is().friday(); // true|false
+    Date.today().is().fri();
+    Date.today().is().march();
+    Date.today().is().mar();
+    </code></pre>
+     *  
+     * @return {Boolean}    true|false
+     */
+    $P.is = function () { 
+        this._is = true; 
+        return this; 
+    };
+
+    /** 
+     * Determines if two date objects occur on/in exactly the same instance of the subsequent date part function.
+     * The function .same() must be followed by a date part function (example: .day(), .month(), .year(), etc).
+     *
+     * An optional Date can be passed in the date part function. If now date is passed as a parameter, 'Now' is used. 
+     *
+     * The following example demonstrates how to determine if two dates fall on the exact same day.
+     *
+     * Example
+    <pre><code>
+    var d1 = Date.today(); // today at 00:00
+    var d2 = new Date();   // exactly now.
+
+    // Do they occur on the same day?
+    d1.same().day(d2); // true
+    
+     // Do they occur on the same hour?
+    d1.same().hour(d2); // false, unless d2 hour is '00' (midnight).
+    
+    // What if it's the same day, but one year apart?
+    var nextYear = Date.today().add(1).year();
+
+    d1.same().day(nextYear); // false, because the dates must occur on the exact same day. 
+    </code></pre>
+     *
+     * Scenario: Determine if a given date occurs during some week period 2 months from now. 
+     *
+     * Example
+    <pre><code>
+    var future = Date.today().add(2).months();
+    return someDate.same().week(future); // true|false;
+    </code></pre>
+     *  
+     * @return {Boolean}    true|false
+     */    
+    $P.same = function () { 
+        this._same = true;
+        this._isSecond = false;
+        return this; 
+    };
+
+    /** 
+     * Determines if the current date/time occurs during Today. Must be preceded by the .is() function.
+     * Example
+    <pre><code>
+    someDate.is().today();    // true|false
+    new Date().is().today();  // true
+    Date.today().is().today();// true
+    Date.today().add(-1).day().is().today(); // false
+    </code></pre>
+     *  
+     * @return {Boolean}    true|false
+     */    
+    $P.today = function () {
+        return this.same().day();
+    };
+
+    /** 
+     * Determines if the current date is a weekday. This function must be preceded by the .is() function.
+     * Example
+    <pre><code>
+    Date.today().is().weekday(); // true|false
+    </code></pre>
+     *  
+     * @return {Boolean}    true|false
+     */
+    $P.weekday = function () {
+        if (this._is) { 
+            this._is = false;
+            return (!this.is().sat() && !this.is().sun());
+        }
+        return false;
+    };
+
+    /** 
+     * Sets the Time of the current Date instance. A string "6:15 pm" or config object {hour:18, minute:15} are accepted.
+     * Example
+    <pre><code>
+    // Set time to 6:15pm with a String
+    Date.today().at("6:15pm");
+
+    // Set time to 6:15pm with a config object
+    Date.today().at({hour:18, minute:15});
+    </code></pre>
+     *  
+     * @return {Date}    date
+     */
+    $P.at = function (time) {
+        return (typeof time === "string") ? $D.parse(this.toString("d") + " " + time) : this.set(time);
+    }; 
+        
+    /** 
+     * Creates a new Date() and adds this (Number) to the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
+     * Example
+    <pre><code>
+    // Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
+    (3).days().fromNow();
+    (6).months().fromNow();
+
+    // Declared Number variables do not require parentheses. 
+    var n = 6;
+    n.months().fromNow();
+    </code></pre>
+     *  
+     * @return {Date}    A new Date instance
+     */
+    $N.fromNow = $N.after = function (date) {
+        var c = {};
+        c[this._dateElement] = this;
+        return ((!date) ? new Date() : date.clone()).add(c);
+    };
+
+    /** 
+     * Creates a new Date() and subtract this (Number) from the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
+     * Example
+    <pre><code>
+    // Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
+    (3).days().ago();
+    (6).months().ago();
+
+    // Declared Number variables do not require parentheses. 
+    var n = 6;
+    n.months().ago();
+    </code></pre>
+     *  
+     * @return {Date}    A new Date instance
+     */
+    $N.ago = $N.before = function (date) {
+        var c = {};
+        c[this._dateElement] = this * -1;
+        return ((!date) ? new Date() : date.clone()).add(c);
+    };
+
+    // Do NOT modify the following string tokens. These tokens are used to build dynamic functions.
+    // All culture-specific strings can be found in the CultureInfo files. See /trunk/src/globalization/.
+    var dx = ("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),
+        mx = ("january february march april may june july august september october november december").split(/\s/),
+        px = ("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),
+        pxf = ("Milliseconds Seconds Minutes Hours Date Week Month FullYear").split(/\s/),
+		nth = ("final first second third fourth fifth").split(/\s/),
+        de;
+
+   /** 
+     * Returns an object literal of all the date parts.
+     * Example
+    <pre><code>
+	var o = new Date().toObject();
+	
+	// { year: 2008, month: 4, week: 20, day: 13, hour: 18, minute: 9, second: 32, millisecond: 812 }
+	
+	// The object properties can be referenced directly from the object.
+	
+	alert(o.day);  // alerts "13"
+	alert(o.year); // alerts "2008"
+    </code></pre>
+     *  
+     * @return {Date}    An object literal representing the original date object.
+     */
+    $P.toObject = function () {
+        var o = {};
+        for (var i = 0; i < px.length; i++) {
+            o[px[i].toLowerCase()] = this["get" + pxf[i]]();
+        }
+        return o;
+    }; 
+   
+   /** 
+     * Returns a date created from an object literal. Ignores the .week property if set in the config. 
+     * Example
+    <pre><code>
+	var o = new Date().toObject();
+	
+	return Date.fromObject(o); // will return the same date. 
+
+    var o2 = {month: 1, day: 20, hour: 18}; // birthday party!
+    Date.fromObject(o2);
+    </code></pre>
+     *  
+     * @return {Date}    An object literal representing the original date object.
+     */    
+    $D.fromObject = function(config) {
+        config.week = null;
+        return Date.today().set(config);
+    };
+        
+    // Create day name functions and abbreviated day name functions (eg. monday(), friday(), fri()).
+    var df = function (n) {
+        return function () { 
+            if (this._is) { 
+                this._is = false; 
+                return this.getDay() == n; 
+            }
+            if (this._nth !== null) {
+                // If the .second() function was called earlier, remove the _orient 
+                // from the date, and then continue.
+                // This is required because 'second' can be used in two different context.
+                // 
+                // Example
+                //
+                //   Date.today().add(1).second();
+                //   Date.march().second().monday();
+                // 
+                // Things get crazy with the following...
+                //   Date.march().add(1).second().second().monday(); // but it works!!
+                //  
+                if (this._isSecond) {
+                    this.addSeconds(this._orient * -1);
+                }
+                // make sure we reset _isSecond
+                this._isSecond = false;
+
+                var ntemp = this._nth;
+                this._nth = null;
+                var temp = this.clone().moveToLastDayOfMonth();
+                this.moveToNthOccurrence(n, ntemp);
+                if (this > temp) {
+                    throw new RangeError($D.getDayName(n) + " does not occur " + ntemp + " times in the month of " + $D.getMonthName(temp.getMonth()) + " " + temp.getFullYear() + ".");
+                }
+                return this;
+            }			
+            return this.moveToDayOfWeek(n, this._orient);
+        };
+    };
+    
+    var sdf = function (n) {
+        return function () {
+            var t = $D.today(), shift = n - t.getDay();
+            if (n === 0 && $C.firstDayOfWeek === 1 && t.getDay() !== 0) {
+                shift = shift + 7;
+            }
+            return t.addDays(shift);
+        };
+    };
+	
+    for (var i = 0; i < dx.length; i++) {
+        // Create constant static Day Name variables. Example: Date.MONDAY or Date.MON
+        $D[dx[i].toUpperCase()] = $D[dx[i].toUpperCase().substring(0, 3)] = i;
+
+        // Create Day Name functions. Example: Date.monday() or Date.mon()
+        $D[dx[i]] = $D[dx[i].substring(0, 3)] = sdf(i);
+
+        // Create Day Name instance functions. Example: Date.today().next().monday()
+        $P[dx[i]] = $P[dx[i].substring(0, 3)] = df(i);
+    }
+    
+    // Create month name functions and abbreviated month name functions (eg. january(), march(), mar()).
+    var mf = function (n) { 
+        return function () {
+            if (this._is) { 
+                this._is = false; 
+                return this.getMonth() === n; 
+            }
+            return this.moveToMonth(n, this._orient); 
+        };
+    };
+    
+    var smf = function (n) {
+        return function () {
+            return $D.today().set({ month: n, day: 1 });
+        };
+    };
+    
+    for (var j = 0; j < mx.length; j++) {
+        // Create constant static Month Name variables. Example: Date.MARCH or Date.MAR
+        $D[mx[j].toUpperCase()] = $D[mx[j].toUpperCase().substring(0, 3)] = j;
+
+        // Create Month Name functions. Example: Date.march() or Date.mar()
+        $D[mx[j]] = $D[mx[j].substring(0, 3)] = smf(j);
+
+        // Create Month Name instance functions. Example: Date.today().next().march()
+        $P[mx[j]] = $P[mx[j].substring(0, 3)] = mf(j);
+    }
+    
+    // Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
+    var ef = function (j) {
+        return function () {
+            // if the .second() function was called earlier, the _orient 
+            // has alread been added. Just return this and reset _isSecond.
+            if (this._isSecond) {
+                this._isSecond = false;
+                return this;
+            }
+
+            if (this._same) {
+                this._same = this._is = false; 
+                var o1 = this.toObject(),
+                    o2 = (arguments[0] || new Date()).toObject(),
+                    v = "",
+                    k = j.toLowerCase();
+                    
+                for (var m = (px.length - 1); m > -1; m--) {
+                    v = px[m].toLowerCase();
+                    if (o1[v] != o2[v]) {
+                        return false;
+                    }
+                    if (k == v) {
+                        break;
+                    }
+                }
+                return true;
+            }
+            
+            if (j.substring(j.length - 1) != "s") {
+                j += "s"; 
+            }
+            return this["add" + j](this._orient);
+        };
+    };
+    
+    
+    var nf = function (n) {
+        return function () {
+            this._dateElement = n;
+            return this;
+        };
+    };
+   
+    for (var k = 0; k < px.length; k++) {
+        de = px[k].toLowerCase();
+    
+        // Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
+        $P[de] = $P[de + "s"] = ef(px[k]);
+        
+        // Create date element functions and plural date element functions used with Number (eg. day(), days(), months()).
+        $N[de] = $N[de + "s"] = nf(de);
+    }
+    
+    $P._ss = ef("Second");
+	
+    var nthfn = function (n) {
+        return function (dayOfWeek) {
+            if (this._same) {
+                return this._ss(arguments[0]);
+            }
+            if (dayOfWeek || dayOfWeek === 0) {
+                return this.moveToNthOccurrence(dayOfWeek, n);
+            }
+            this._nth = n;
+
+            // if the operator is 'second' add the _orient, then deal with it later...
+            if (n === 2 && (dayOfWeek === undefined || dayOfWeek === null)) {
+                this._isSecond = true;
+                return this.addSeconds(this._orient);
+            }
+            return this;
+        };
+    };
+
+    for (var l = 0; l < nth.length; l++) {
+        $P[nth[l]] = (l === 0) ? nthfn(-1) : nthfn(l);
+    }
+}());
\ No newline at end of file
diff --git a/debian/missing-sources/scripts/time.js b/debian/missing-sources/scripts/time.js
new file mode 100644
index 0000000..c3ac11f
--- /dev/null
+++ b/debian/missing-sources/scripts/time.js
@@ -0,0 +1,269 @@
+/**
+ * @version: 1.0 Alpha-1
+ * @author: Coolite Inc. http://www.coolite.com/
+ * @date: 2008-04-13
+ * @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
+ * @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. 
+ * @website: http://www.datejs.com/
+ */
+ 
+/* 
+ * TimeSpan(milliseconds);
+ * TimeSpan(days, hours, minutes, seconds);
+ * TimeSpan(days, hours, minutes, seconds, milliseconds);
+ */
+var TimeSpan = function (days, hours, minutes, seconds, milliseconds) {
+    var attrs = "days hours minutes seconds milliseconds".split(/\s+/);
+    
+    var gFn = function (attr) { 
+        return function () { 
+            return this[attr]; 
+        }; 
+    };
+	
+    var sFn = function (attr) { 
+        return function (val) { 
+            this[attr] = val; 
+            return this; 
+        }; 
+    };
+	
+    for (var i = 0; i < attrs.length ; i++) {
+        var $a = attrs[i], $b = $a.slice(0, 1).toUpperCase() + $a.slice(1);
+        TimeSpan.prototype[$a] = 0;
+        TimeSpan.prototype["get" + $b] = gFn($a);
+        TimeSpan.prototype["set" + $b] = sFn($a);
+    }
+
+    if (arguments.length == 4) { 
+        this.setDays(days); 
+        this.setHours(hours); 
+        this.setMinutes(minutes); 
+        this.setSeconds(seconds); 
+    } else if (arguments.length == 5) { 
+        this.setDays(days); 
+        this.setHours(hours); 
+        this.setMinutes(minutes); 
+        this.setSeconds(seconds); 
+        this.setMilliseconds(milliseconds); 
+    } else if (arguments.length == 1 && typeof days == "number") {
+        var orient = (days < 0) ? -1 : +1;
+        this.setMilliseconds(Math.abs(days));
+        
+        this.setDays(Math.floor(this.getMilliseconds() / 86400000) * orient);
+        this.setMilliseconds(this.getMilliseconds() % 86400000);
+
+        this.setHours(Math.floor(this.getMilliseconds() / 3600000) * orient);
+        this.setMilliseconds(this.getMilliseconds() % 3600000);
+
+        this.setMinutes(Math.floor(this.getMilliseconds() / 60000) * orient);
+        this.setMilliseconds(this.getMilliseconds() % 60000);
+
+        this.setSeconds(Math.floor(this.getMilliseconds() / 1000) * orient);
+        this.setMilliseconds(this.getMilliseconds() % 1000);
+
+        this.setMilliseconds(this.getMilliseconds() * orient);
+    }
+
+    this.getTotalMilliseconds = function () {
+        return (this.getDays() * 86400000) + (this.getHours() * 3600000) + (this.getMinutes() * 60000) + (this.getSeconds() * 1000); 
+    };
+    
+    this.compareTo = function (time) {
+        var t1 = new Date(1970, 1, 1, this.getHours(), this.getMinutes(), this.getSeconds()), t2;
+        if (time === null) { 
+            t2 = new Date(1970, 1, 1, 0, 0, 0); 
+        }
+        else {
+            t2 = new Date(1970, 1, 1, time.getHours(), time.getMinutes(), time.getSeconds());
+        }
+        return (t1 < t2) ? -1 : (t1 > t2) ? 1 : 0;
+    };
+
+    this.equals = function (time) {
+        return (this.compareTo(time) === 0);
+    };    
+
+    this.add = function (time) { 
+        return (time === null) ? this : this.addSeconds(time.getTotalMilliseconds() / 1000); 
+    };
+
+    this.subtract = function (time) { 
+        return (time === null) ? this : this.addSeconds(-time.getTotalMilliseconds() / 1000); 
+    };
+
+    this.addDays = function (n) { 
+        return new TimeSpan(this.getTotalMilliseconds() + (n * 86400000)); 
+    };
+
+    this.addHours = function (n) { 
+        return new TimeSpan(this.getTotalMilliseconds() + (n * 3600000)); 
+    };
+
+    this.addMinutes = function (n) { 
+        return new TimeSpan(this.getTotalMilliseconds() + (n * 60000)); 
+    };
+
+    this.addSeconds = function (n) {
+        return new TimeSpan(this.getTotalMilliseconds() + (n * 1000)); 
+    };
+
+    this.addMilliseconds = function (n) {
+        return new TimeSpan(this.getTotalMilliseconds() + n); 
+    };
+
+    this.get12HourHour = function () {
+        return (this.getHours() > 12) ? this.getHours() - 12 : (this.getHours() === 0) ? 12 : this.getHours();
+    };
+
+    this.getDesignator = function () { 
+        return (this.getHours() < 12) ? Date.CultureInfo.amDesignator : Date.CultureInfo.pmDesignator;
+    };
+
+    this.toString = function (format) {
+        this._toString = function () {
+            if (this.getDays() !== null && this.getDays() > 0) {
+                return this.getDays() + "." + this.getHours() + ":" + this.p(this.getMinutes()) + ":" + this.p(this.getSeconds());
+            }
+            else { 
+                return this.getHours() + ":" + this.p(this.getMinutes()) + ":" + this.p(this.getSeconds());
+            }
+        };
+        
+        this.p = function (s) {
+            return (s.toString().length < 2) ? "0" + s : s;
+        };
+        
+        var me = this;
+        
+        return format ? format.replace(/dd?|HH?|hh?|mm?|ss?|tt?/g, 
+        function (format) {
+            switch (format) {
+            case "d":	
+                return me.getDays();
+            case "dd":	
+                return me.p(me.getDays());
+            case "H":	
+                return me.getHours();
+            case "HH":	
+                return me.p(me.getHours());
+            case "h":	
+                return me.get12HourHour();
+            case "hh":	
+                return me.p(me.get12HourHour());
+            case "m":	
+                return me.getMinutes();
+            case "mm":	
+                return me.p(me.getMinutes());
+            case "s":	
+                return me.getSeconds();
+            case "ss":	
+                return me.p(me.getSeconds());
+            case "t":	
+                return ((me.getHours() < 12) ? Date.CultureInfo.amDesignator : Date.CultureInfo.pmDesignator).substring(0, 1);
+            case "tt":	
+                return (me.getHours() < 12) ? Date.CultureInfo.amDesignator : Date.CultureInfo.pmDesignator;
+            }
+        }
+        ) : this._toString();
+    };
+    return this;
+};    
+
+/**
+ * Gets the time of day for this date instances. 
+ * @return {TimeSpan} TimeSpan
+ */
+Date.prototype.getTimeOfDay = function () {
+    return new TimeSpan(0, this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds());
+};
+
+/* 
+ * TimePeriod(startDate, endDate);
+ * TimePeriod(years, months, days, hours, minutes, seconds, milliseconds);
+ */
+var TimePeriod = function (years, months, days, hours, minutes, seconds, milliseconds) {
+    var attrs = "years months days hours minutes seconds milliseconds".split(/\s+/);
+    
+    var gFn = function (attr) { 
+        return function () { 
+            return this[attr]; 
+        }; 
+    };
+	
+    var sFn = function (attr) { 
+        return function (val) { 
+            this[attr] = val; 
+            return this; 
+        }; 
+    };
+	
+    for (var i = 0; i < attrs.length ; i++) {
+        var $a = attrs[i], $b = $a.slice(0, 1).toUpperCase() + $a.slice(1);
+        TimePeriod.prototype[$a] = 0;
+        TimePeriod.prototype["get" + $b] = gFn($a);
+        TimePeriod.prototype["set" + $b] = sFn($a);
+    }
+    
+    if (arguments.length == 7) { 
+        this.years = years;
+        this.months = months;
+        this.setDays(days);
+        this.setHours(hours); 
+        this.setMinutes(minutes); 
+        this.setSeconds(seconds); 
+        this.setMilliseconds(milliseconds);
+    } else if (arguments.length == 2 && arguments[0] instanceof Date && arguments[1] instanceof Date) {
+        // startDate and endDate as arguments
+    
+        var d1 = years.clone();
+        var d2 = months.clone();
+    
+        var temp = d1.clone();
+        var orient = (d1 > d2) ? -1 : +1;
+        
+        this.years = d2.getFullYear() - d1.getFullYear();
+        temp.addYears(this.years);
+        
+        if (orient == +1) {
+            if (temp > d2) {
+                if (this.years !== 0) {
+                    this.years--;
+                }
+            }
+        } else {
+            if (temp < d2) {
+                if (this.years !== 0) {
+                    this.years++;
+                }
+            }
+        }
+        
+        d1.addYears(this.years);
+
+        if (orient == +1) {
+            while (d1 < d2 && d1.clone().addDays(Date.getDaysInMonth(d1.getYear(), d1.getMonth()) ) < d2) {
+                d1.addMonths(1);
+                this.months++;
+            }
+        }
+        else {
+            while (d1 > d2 && d1.clone().addDays(-d1.getDaysInMonth()) > d2) {
+                d1.addMonths(-1);
+                this.months--;
+            }
+        }
+        
+        var diff = d2 - d1;
+
+        if (diff !== 0) {
+            var ts = new TimeSpan(diff);
+            this.setDays(ts.getDays());
+            this.setHours(ts.getHours());
+            this.setMinutes(ts.getMinutes());
+            this.setSeconds(ts.getSeconds());
+            this.setMilliseconds(ts.getMilliseconds());
+        }
+    }
+    return this;
+};
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/ubiquity-extension.git



More information about the Pkg-mozext-commits mailing list