[
  {
    "path": ".gitignore",
    "content": "\\.DS_Store\r\n\\.classpath\r\n\\.project\r\n\\.settings\r\n\\.iml\r\n\\.ipr\r\n\\.iws\r\n*\\.bat\r\n*\\.psd\r\n*\\.ai\r\n*\\.sh\r\nga.js\r\nrobots.txt\r\nantixss\\.js\r\ncountdown\\.demo\\.js"
  },
  {
    "path": "LICENSE.txt",
    "content": "The MIT License\r\n\r\nCopyright (c) 2006-2012 Stephen M. McKamey\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\nTHE SOFTWARE.\r\n"
  },
  {
    "path": "README.md",
    "content": "# [Countdown.js][1]\n\nA simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.\n\n----\n\n## The Motivation\n\nWhile seemingly a trivial problem, the human descriptions for a span of time tend to be fuzzier than a computer naturally computes.\nMore specifically, months are an inherently messed up unit of time.\nFor instance, when a human says \"in 1 month\" how long do they mean? Banks often interpret this as *thirty days* but that is only correct one third of the time.\nPeople casually talk about a month being *four weeks long* but there is only one month in a year which is four weeks long and it is only that long about three quarters of the time.\nEven intuitively defining these terms can be problematic. For instance, what is the date one month after January 31st, 2001?\nJavaScript will happily call this March 3rd, 2001. Humans will typically debate either February 28th, 2001 or March 1st, 2001.\nIt seems there isn't a \"right\" answer, per se.\n\n## The Algorithm\n\n*Countdown.js* emphasizes producing intuitively correct description of timespans which are consistent as time goes on.\nTo do this, *Countdown.js* uses the concept of \"today's date next month\" to mean \"a month from now\".\nAs the days go by, *Countdown.js* produces consecutively increasing or decreasing counts without inconsistent jumps.\nThe range of accuracy is only limited by the underlying system clock.\n\n*Countdown.js* approaches finding the difference between two times like an elementary school subtraction problem.\nEach unit acts like a base-10 place where any overflow is carried to the next highest unit, and any underflow is borrowed from the next highest unit.\nIn base-10 subtraction, every column is worth 10 times the previous column.\nWith time, it is a little more complex since the conversions between the units of time are not the same and months are an inconsistent number of days.\nInternally, *Countdown.js* maintains the concept of a \"reference month\" which determines how many days a given month or year represents.\nIn the final step of the algorithm, *Countdown.js* then prunes the set of time units down to only those requested, forcing larger units down to smaller.\n\n### Time Zones & Daylight Savings Time\n\nAs of v2.4, *Countdown.js* performs all calculations with respect to the **viewer's local time zone**.\nEarlier versions performed difference calculations in UTC, which is generally the correct way to do math on time.\nIn this situation, however, an issue with using UTC happens when either of the two dates being worked with is within one time zone offset of a month boundary.\nIf the UTC interpretation of that instant in time is in a different month than that of the local time zone, then the viewer's perception is that the calculated time span is incorrect.\nThis is the heart of the problem that *Countdown.js* attempts to solve: talking about spans of time can be ambiguous.\nNearly all bugs reported for *Countdown.js* have been because the viewer expects something different due to their particular time zone.\n\nJavaScript ([ECMA-262](http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.7)) only works with dates as UTC or the local time zone, not arbitrary time zones.\nBy design, all JS Date objects represent an instant in time (milliseconds since midnight Jan 1, 1970 **in UTC**) interpreted as the user's local time.\nSince most humans think about local time not UTC, it the most makes sense to perform this time span algorithm in reference to local time.\n\nDaylight Savings Time further complicates things, creating hours which get repeated and hours which cannot exist.\n*Countdown.js* effectively ignores these edge cases and talks about time preferring human intuition about time over surprise exactness.\nExample: A viewer asks for the description from noon the day before a daylight savings begins to noon the day after.\nA computer would answer \"23 hours\" whereas a human would confidently answer \"1 day\" even after being reminded to \"Spring Forward\".\nThe computer is technically more accurate but this is not the value that humans actually expect or desire.\nHumans pretend that time is simple and makes sense. Unfortunately, humans made time far more complex than it needed to be with time zones and daylight savings.\nUTC simplifies time but at the cost of being inconsistent with human experience.\n\n----\n\n## The API\n\nA simple but flexible API is the goal of *Countdown.js*. There is one global function with a set of static constants:\n\n    var timespan = countdown(start|callback, end|callback, units, max, digits);\n\nThe parameters are a starting Date, ending Date, an optional set of units, an optional maximum number of units, and an optional maximum number of decimal places on the smallest unit. `units` defaults to `countdown.DEFAULTS`, `max` defaults to `NaN` (all specified units), `digits` defaults to `0`.\n\n\tcountdown.ALL =\n\t\tcountdown.MILLENNIA |\n\t\tcountdown.CENTURIES |\n\t\tcountdown.DECADES |\n\t\tcountdown.YEARS |\n\t\tcountdown.MONTHS |\n\t\tcountdown.WEEKS |\n\t\tcountdown.DAYS |\n\t\tcountdown.HOURS |\n\t\tcountdown.MINUTES |\n\t\tcountdown.SECONDS |\n\t\tcountdown.MILLISECONDS;\n\n\tcountdown.DEFAULTS =\n\t\tcountdown.YEARS |\n\t\tcountdown.MONTHS |\n\t\tcountdown.DAYS |\n\t\tcountdown.HOURS |\n\t\tcountdown.MINUTES |\n\t\tcountdown.SECONDS;\n\nThis allows a very minimal call to accept the defaults and get the time since/until a single date. For example:\n\n\tcountdown( new Date(2000, 0, 1) ).toString();\n\nThis will produce a human readable description like:\n\n\t11 years, 8 months, 4 days, 10 hours, 12 minutes and 43 seconds\n\n### The `start` / `end` arguments\n\nThe parameters `start` and `end` can be one of several values:\n\n1. `null` which indicates \"now\".\n2. a JavaScript `Date` object.\n3. a `number` specifying the number of milliseconds since midnight Jan 1, 1970 UTC (i.e., the \"UNIX epoch\").\n4. a callback `function` accepting one timespan argument.\n\nTo reference a specific instant in time, either use a `number` offset from the epoch, or a JavaScript `Date` object instantiated with the specific offset from the epoch.\nIn JavaScript, if a `Date` object is instantiated using year/month/date/etc values, then those values are interpreted interpreted in reference to the browser's local time zone and daylight savings settings.\n\nIf `start` and `end` are both specified, then repeated calls to `countdown(...)` will always return the same result.\nIf one date argument is left `null` while the other is provided, then repeated calls will count up if the provided date is in the past, and it will count down if the provided date is in the future.\nFor example,\n\n\tvar daysSinceLastWorkplaceAccident = countdown(507314280000, null, countdown.DAYS);\n\nIf a callback function is supplied, then an interval timer will be started with a frequency based upon the smallest unit (e.g., if `countdown.SECONDS` is the smallest unit, the callback will be invoked once per second). Rather than returning a Timespan object, the timer's ID will be returned to allow canceling by passing into `window.clearInterval(id)`. For example, to show a timer since the page first loaded:\n\n\tvar timerId =\n\t  countdown(\n\t    new Date(),\n\t    function(ts) {\n\t      document.getElementById('pageTimer').innerHTML = ts.toHTML(\"strong\");\n\t    },\n\t    countdown.HOURS|countdown.MINUTES|countdown.SECONDS);\n\t\n\t// later on this timer may be stopped\n\twindow.clearInterval(timerId);\n\n### The `units` argument\n\nThe static units constants can be combined using [standard bitwise operators](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators). For example, to explicitly include \"months or days\" use bitwise-OR:\n\n\tcountdown.MONTHS | countdown.DAYS\n\nTo explicitly exclude units like \"not weeks and not milliseconds\" combine bitwise-NOT and bitwise-AND:\n\n\t~countdown.WEEKS & ~countdown.MILLISECONDS\n\n[Equivalently](http://en.wikipedia.org/wiki/De_Morgan's_laws), to specify everything but \"not weeks or milliseconds\" wrap bitwise-NOT around bitwise-OR:\n\n\t~(countdown.WEEKS | countdown.MILLISECONDS)\n\n### The `max` argument\n\nThe next optional argument `max` specifies a maximum number of unit labels to display. This allows specifying which units are interesting but only displaying the `max` most significant units.\n\n\tcountdown(start, end, units).toString() => \"5 years, 1 month, 19 days, 12 hours and 17 minutes\"\n\nSpecifying `max` as `2` ensures that only the two most significant units are displayed **(note the rounding of the least significant unit)**:\n\n\tcountdown(start, end, units, 2).toString() => \"5 years and 2 months\"\n\nNegative or zero values of `max` are ignored.\n\n----\n#### Breaking change in v2.3.0!\nPreviously, the `max` number of unit labels argument used to be specified when formatting in `timespan.toString(...)` and `timespan.toHTML(...)`. v2.3.0 moves it to `countdown(...)`, which improves efficiency as well as enabling fractional units (see below).\n\n----\n\n### The `digits` argument\n\nThe final optional argument `digits` allows fractional values on the smallest unit.\n\n\tcountdown(start, end, units, max).toString() => \"5 years and 2 months\"\n\nSpecifying `digits` as `2` allows up to 2 digits beyond the decimal point to be displayed **(note the rounding of the least significant unit)**:\n\n\tcountdown(start, end, units, max, 2).toString() => \"5 years and 1.65 months\"\n\n`digits` must be between `0` and `20`, inclusive.\n\n----\n#### Rounding\n\nWith the calculations of fractional units in v2.3.0, the smallest displayed unit now properly rounds. Previously, the equivalent of `\"1.99 years\"` would be truncated to `\"1 year\"`, as of v2.3.0 it will display as `\"2 years\"`.\n\nTypically, this is the intended interpretation but there are a few circumstances where people expect the truncated behavior. For example, people often talk about their age as the lowest possible interpretation. e.g., they claim \"39-years-old\" right up until the morning of their 40th birthday (some people do even for years after!). In these cases, after calling <code>countdown(start,end,units,max,20)</code> with the largest possible number of `digits`, you might want to set `ts.years = Math.floor(ts.years)` before calling `ts.toString()`. The vain might want you to set `ts.years = Math.min(ts.years, 39)`!\n\n----\n\n### Timespan result\n\nThe return value is a Timespan object which always contains the following fields:\n\n- `Date start`: the starting date object used for the calculation\n- `Date end`: the ending date object used for the calculation\n- `Number units`: the units specified\n- `Number value`: total milliseconds difference (i.e., `end` - `start`). If `end < start` then `value` will be negative.\n\nTypically the `end` occurs after `start`, but if the arguments were reversed, the only difference is `Timespan.value` will be negative. The sign of `value` can be used to determine if the event occurs in the future or in the past.\n\nThe following time unit fields are only present if their corresponding units were requested:\n\n- `Number millennia`\n- `Number centuries`\n- `Number decades`\n- `Number years`\n- `Number months`\n- `Number days`\n- `Number hours`\n- `Number minutes`\n- `Number seconds`\n- `Number milliseconds`\n\nFinally, Timespan has two formatting methods each with some optional parameters. If the difference between `start` and `end` is less than the requested granularity of units, then `toString(...)` and `toHTML(...)` will return the empty label (defaults to an empty string).\n\n`String toString(emptyLabel)`: formats the Timespan object as an English sentence. e.g., using the same input:\n\n\tts.toString() => \"5 years, 1 month, 19 days, 12 hours and 17 minutes\"\n\n`String toHTML(tagName, emptyLabel)`: formats the Timespan object as an English sentence, with the specified HTML tag wrapped around each unit. If no tag name is provided, \"`span`\" is used. e.g., using the same input:\n\n\tts.toHTML() => \"<span>5 years</span>, <span>1 month</span>, <span>19 days</span>, <span>12 hours</span> and <span>17 minutes</span>\"\n\n\tts.toHTML(\"em\") => \"<em>5 years</em>, <em>1 month</em>, <em>19 days</em>, <em>12 hours</em> and <em>17 minutes</em>\"\n\n----\n\n### Localization\n\nVery basic localization is supported via the static `setLabels` and `resetLabels` methods. These change the functionality for all timespans on the page.\n\n\tcountdown.resetLabels();\n\n\tcountdown.setLabels(singular, plural, last, delim, empty, formatter);\n\nThe arguments:\n\n- `singular` is a pipe (`'|'`) delimited ascending list of singular unit name overrides\n- `plural` is a pipe (`'|'`) delimited ascending list of plural unit name overrides\n- `last` is a delimiter before the last unit (default: `' and '`)\n- `delim` is a delimiter to use between all other units (default: `', '`),\n- `empty` is a label to use when all units are zero (default: `''`)\n- `formatter` is a function which takes a `number` and returns a `string` (default uses `Number.toString()`),  \n  allowing customization of the way numbers are formatted, e.g., commas every 3 digits or some unique style that is specific to your locale.\n\nNotice that the spacing is part of the labels.\n\nThe following examples would translate the output into Brazilian Portuguese and French, respectively:\n\n\tcountdown.setLabels(\n\t\t' milissegundo| segundo| minuto| hora| dia| semana| mês| ano| década| século| milênio',\n\t\t' milissegundos| segundos| minutos| horas| dias| semanas| meses| anos| décadas| séculos| milênios',\n\t\t' e ',\n\t\t' + ',\n\t\t'agora');\n\n\tcountdown.setLabels(\n\t\t' milliseconde| seconde| minute| heure| jour| semaine| mois| année| décennie| siècle| millénaire',\n\t\t' millisecondes| secondes| minutes| heures| jours| semaines| mois| années| décennies| siècles| millénaires',\n\t\t' et ',\n\t\t', ',\n\t\t'maintenant');\n\nIf you only wanted to override some of the labels just leave the other pipe-delimited places empty. Similarly, leave off any of the delimiter arguments which do not need overriding.\n\n\tcountdown.setLabels(\n\t\t'||| hr| d',\n\t\t'ms| sec|||| wks|| yrs',\n\t\t', and finally ');\n\n\tts.toString() => \"1 millennium, 2 centuries, 5 yrs, 1 month, 7 wks, 19 days, 1 hr, 2 minutes, 17 sec, and finally 1 millisecond\"\n\nIf you only wanted to override the empty label:\n\n\tcountdown.setLabels(\n\t\tnull,\n\t\tnull,\n\t\tnull,\n\t\tnull,\n\t\t'Now.');\n\n\tts.toString() => \"Now.\"\n\nThe following would be effectively the same as calling `countdown.resetLabels()`:\n\n\tcountdown.setLabels(\n\t\t' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium',\n\t\t' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia',\n\t\t' and ',\n\t\t', ',\n\t\t'',\n\t\tfunction(n){ return n.toString(); });\n\n----\n\n## License\n\nDistributed under the terms of [The MIT license][2].\n\n----\n\nCopyright (c) 2006-2014 [Stephen M. McKamey][3]\n\n  [1]: http://countdownjs.org\n  [2]: https://raw.githubusercontent.com/mckamey/countdownjs/master/LICENSE.txt\n  [3]: http://mck.me\n"
  },
  {
    "path": "bower.json",
    "content": "{\n  \"name\": \"countdownjs\",\n  \"version\": \"2.6.1\",\n  \"description\": \"A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.\",\n  \"main\": \"countdown.js\",\n  \"keywords\": [\n    \"countdown\",\n    \"timer\",\n    \"clock\",\n    \"date\",\n    \"time\",\n    \"timespan\",\n    \"year\",\n    \"month\",\n    \"week\",\n    \"day\",\n    \"hour\",\n    \"minute\",\n    \"second\"\n  ],\n  \"authors\": [\n    \"Stephen McKamey (http://mck.me)\"\n  ],\n  \"license\": \"MIT\",\n  \"homepage\": \"http://countdownjs.org\",\n  \"ignore\": [\n  \t\"./!(countdown.js)\"\n  ]\n}\n"
  },
  {
    "path": "build.xml",
    "content": "﻿<?xml version=\"1.0\"?>\r\n<project basedir=\".\" default=\"all\">\r\n\t<taskdef name=\"closure\" classname=\"com.google.javascript.jscomp.ant.CompileTask\" classpath=\"lib/closure/compiler.jar\" />\r\n\r\n\t<target name=\"init\">\r\n\t\t<echo message=\"Building...\" />\r\n\t\t<property name=\"lib.dir\" value=\"${basedir}/lib\" />\r\n\t\t<property name=\"src.dir\" value=\"${basedir}\" />\r\n\t\t<property name=\"test.dir\" value=\"${basedir}/test\" />\r\n\t\t<property name=\"build.dir\" value=\"${basedir}\" />\r\n\t</target>\r\n\r\n\t<target name=\"jslint\" depends=\"init\" description=\"Check JS with JSLint\">\r\n\t\t<echo message=\"Running JSLint static analysis...\" />\r\n\t\t<java jar=\"${lib.dir}/rhino/js.jar\" fork=\"true\">\r\n\t\t\t<arg value=\"${test.dir}/lint.js\" />\r\n\t\t</java>\r\n\t\t<echo message=\"JSLint static analysis done.\" />\r\n\t</target>\r\n\r\n\t<target name=\"compile.js\" depends=\"jslint\" description=\"Compile JS\">\r\n\t\t<echo message=\"Compiling script...\" />\r\n\t\t<closure compilationLevel=\"simple\" warning=\"verbose\" debug=\"false\" output=\"${build.dir}/countdown.min.js\">\r\n\t\t\t<sources dir=\"${build.dir}\">\r\n\t\t\t\t<file name=\"externs.js\"/>\r\n\t\t\t\t<file name=\"countdown.js\"/>\r\n\t\t\t</sources>\r\n\t\t</closure>\r\n\t\t<echo message=\"Script compilation done.\" />\r\n\t</target>\r\n\r\n\t<target name=\"all\" depends=\"compile.js\">\r\n\t\t<echo message=\"Build completed.\" />\r\n\t\t<concat destfile=\"${build.dir}/countdown.demo.js\">\r\n\t\t\t<fileset file=\"${build.dir}/antixss.js\" />\r\n\t\t\t<fileset file=\"${build.dir}/countdown.min.js\" />\r\n\t\t</concat>\r\n\t</target>\r\n\r\n</project>"
  },
  {
    "path": "countdown.js",
    "content": "/*global window module */\n/**\n * @license countdown.js v2.6.1 http://countdownjs.org\n * Copyright (c)2006-2014 Stephen M. McKamey.\n * Licensed under The MIT License.\n */\n/*jshint bitwise:false */\n\n/**\n * API entry\n * @public\n * @param {function(Object)|Date|number} start the starting date\n * @param {function(Object)|Date|number} end the ending date\n * @param {number} units the units to populate\n * @return {Object|number}\n */\nvar countdown = (\n\nfunction() {\n\t/*jshint smarttabs:true */\n\n\t'use strict';\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MILLISECONDS\t= 0x001;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar SECONDS\t\t\t= 0x002;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MINUTES\t\t\t= 0x004;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar HOURS\t\t\t= 0x008;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar DAYS\t\t\t= 0x010;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar WEEKS\t\t\t= 0x020;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MONTHS\t\t\t= 0x040;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar YEARS\t\t\t= 0x080;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar DECADES\t\t\t= 0x100;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar CENTURIES\t\t= 0x200;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MILLENNIA\t\t= 0x400;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar DEFAULTS\t\t= YEARS|MONTHS|DAYS|HOURS|MINUTES|SECONDS;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MILLISECONDS_PER_SECOND = 1000;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar SECONDS_PER_MINUTE = 60;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MINUTES_PER_HOUR = 60;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar HOURS_PER_DAY = 24;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MILLISECONDS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar DAYS_PER_WEEK = 7;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar MONTHS_PER_YEAR = 12;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar YEARS_PER_DECADE = 10;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar DECADES_PER_CENTURY = 10;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar CENTURIES_PER_MILLENNIUM = 10;\n\n\t/**\n\t * @private\n\t * @param {number} x number\n\t * @return {number}\n\t */\n\tvar ceil = Math.ceil;\n\n\t/**\n\t * @private\n\t * @param {number} x number\n\t * @return {number}\n\t */\n\tvar floor = Math.floor;\n\n\t/**\n\t * @private\n\t * @param {Date} ref reference date\n\t * @param {number} shift number of months to shift\n\t * @return {number} number of days shifted\n\t */\n\tfunction borrowMonths(ref, shift) {\n\t\tvar prevTime = ref.getTime();\n\n\t\t// increment month by shift\n\t\tref.setMonth( ref.getMonth() + shift );\n\n\t\t// this is the trickiest since months vary in length\n\t\treturn Math.round( (ref.getTime() - prevTime) / MILLISECONDS_PER_DAY );\n\t}\n\n\t/**\n\t * @private\n\t * @param {Date} ref reference date\n\t * @return {number} number of days\n\t */\n\tfunction daysPerMonth(ref) {\n\t\tvar a = ref.getTime();\n\n\t\t// increment month by 1\n\t\tvar b = new Date(a);\n\t\tb.setMonth( ref.getMonth() + 1 );\n\n\t\t// this is the trickiest since months vary in length\n\t\treturn Math.round( (b.getTime() - a) / MILLISECONDS_PER_DAY );\n\t}\n\n\t/**\n\t * @private\n\t * @param {Date} ref reference date\n\t * @return {number} number of days\n\t */\n\tfunction daysPerYear(ref) {\n\t\tvar a = ref.getTime();\n\n\t\t// increment year by 1\n\t\tvar b = new Date(a);\n\t\tb.setFullYear( ref.getFullYear() + 1 );\n\n\t\t// this is the trickiest since years (periodically) vary in length\n\t\treturn Math.round( (b.getTime() - a) / MILLISECONDS_PER_DAY );\n\t}\n\n\t/**\n\t * Applies the Timespan to the given date.\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @param {Date=} date\n\t * @return {Date}\n\t */\n\tfunction addToDate(ts, date) {\n\t\tdate = (date instanceof Date) || ((date !== null) && isFinite(date)) ? new Date(+date) : new Date();\n\t\tif (!ts) {\n\t\t\treturn date;\n\t\t}\n\n\t\t// if there is a value field, use it directly\n\t\tvar value = +ts.value || 0;\n\t\tif (value) {\n\t\t\tdate.setTime(date.getTime() + value);\n\t\t\treturn date;\n\t\t}\n\n\t\tvalue = +ts.milliseconds || 0;\n\t\tif (value) {\n\t\t\tdate.setMilliseconds(date.getMilliseconds() + value);\n\t\t}\n\n\t\tvalue = +ts.seconds || 0;\n\t\tif (value) {\n\t\t\tdate.setSeconds(date.getSeconds() + value);\n\t\t}\n\n\t\tvalue = +ts.minutes || 0;\n\t\tif (value) {\n\t\t\tdate.setMinutes(date.getMinutes() + value);\n\t\t}\n\n\t\tvalue = +ts.hours || 0;\n\t\tif (value) {\n\t\t\tdate.setHours(date.getHours() + value);\n\t\t}\n\n\t\tvalue = +ts.weeks || 0;\n\t\tif (value) {\n\t\t\tvalue *= DAYS_PER_WEEK;\n\t\t}\n\n\t\tvalue += +ts.days || 0;\n\t\tif (value) {\n\t\t\tdate.setDate(date.getDate() + value);\n\t\t}\n\n\t\tvalue = +ts.months || 0;\n\t\tif (value) {\n\t\t\tdate.setMonth(date.getMonth() + value);\n\t\t}\n\n\t\tvalue = +ts.millennia || 0;\n\t\tif (value) {\n\t\t\tvalue *= CENTURIES_PER_MILLENNIUM;\n\t\t}\n\n\t\tvalue += +ts.centuries || 0;\n\t\tif (value) {\n\t\t\tvalue *= DECADES_PER_CENTURY;\n\t\t}\n\n\t\tvalue += +ts.decades || 0;\n\t\tif (value) {\n\t\t\tvalue *= YEARS_PER_DECADE;\n\t\t}\n\n\t\tvalue += +ts.years || 0;\n\t\tif (value) {\n\t\t\tdate.setFullYear(date.getFullYear() + value);\n\t\t}\n\n\t\treturn date;\n\t}\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_MILLISECONDS\t= 0;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_SECONDS\t\t= 1;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_MINUTES\t\t= 2;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_HOURS\t\t\t= 3;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_DAYS\t\t\t= 4;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_WEEKS\t\t\t= 5;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_MONTHS\t\t= 6;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_YEARS\t\t\t= 7;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_DECADES\t\t= 8;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_CENTURIES\t\t= 9;\n\n\t/**\n\t * @private\n\t * @const\n\t * @type {number}\n\t */\n\tvar LABEL_MILLENNIA\t\t= 10;\n\n\t/**\n\t * @private\n\t * @type {Array}\n\t */\n\tvar LABELS_SINGLUAR;\n\n\t/**\n\t * @private\n\t * @type {Array}\n\t */\n\tvar LABELS_PLURAL;\n\n\t/**\n\t * @private\n\t * @type {string}\n\t */\n\tvar LABEL_LAST;\n\n\t/**\n\t * @private\n\t * @type {string}\n\t */\n\tvar LABEL_DELIM;\n\n\t/**\n\t * @private\n\t * @type {string}\n\t */\n\tvar LABEL_NOW;\n\n\t/**\n\t * Formats a number & unit as a string\n\t * \n\t * @param {number} value\n\t * @param {number} unit\n\t * @return {string}\n\t */\n\tvar formatter;\n\n\t/**\n\t * Formats a number as a string\n\t * \n\t * @private\n\t * @param {number} value\n\t * @return {string}\n\t */\n\tvar formatNumber;\n\n\t/**\n\t * @private\n\t * @param {number} value\n\t * @param {number} unit unit index into label list\n\t * @return {string}\n\t */\n\tfunction plurality(value, unit) {\n\t\treturn formatNumber(value)+((value === 1) ? LABELS_SINGLUAR[unit] : LABELS_PLURAL[unit]);\n\t}\n\n\t/**\n\t * Formats the entries with singular or plural labels\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @return {Array}\n\t */\n\tvar formatList;\n\n\t/**\n\t * Timespan representation of a duration of time\n\t * \n\t * @private\n\t * @this {Timespan}\n\t * @constructor\n\t */\n\tfunction Timespan() {}\n\n\t/**\n\t * Formats the Timespan as a sentence\n\t * \n\t * @param {string=} emptyLabel the string to use when no values returned\n\t * @return {string}\n\t */\n\tTimespan.prototype.toString = function(emptyLabel) {\n\t\tvar label = formatList(this);\n\n\t\tvar count = label.length;\n\t\tif (!count) {\n\t\t\treturn emptyLabel ? ''+emptyLabel : LABEL_NOW;\n\t\t}\n\t\tif (count === 1) {\n\t\t\treturn label[0];\n\t\t}\n\n\t\tvar last = LABEL_LAST+label.pop();\n\t\treturn label.join(LABEL_DELIM)+last;\n\t};\n\n\t/**\n\t * Formats the Timespan as a sentence in HTML\n\t * \n\t * @param {string=} tag HTML tag name to wrap each value\n\t * @param {string=} emptyLabel the string to use when no values returned\n\t * @return {string}\n\t */\n\tTimespan.prototype.toHTML = function(tag, emptyLabel) {\n\t\ttag = tag || 'span';\n\t\tvar label = formatList(this);\n\n\t\tvar count = label.length;\n\t\tif (!count) {\n\t\t\temptyLabel = emptyLabel || LABEL_NOW;\n\t\t\treturn emptyLabel ? '<'+tag+'>'+emptyLabel+'</'+tag+'>' : emptyLabel;\n\t\t}\n\t\tfor (var i=0; i<count; i++) {\n\t\t\t// wrap each unit in tag\n\t\t\tlabel[i] = '<'+tag+'>'+label[i]+'</'+tag+'>';\n\t\t}\n\t\tif (count === 1) {\n\t\t\treturn label[0];\n\t\t}\n\n\t\tvar last = LABEL_LAST+label.pop();\n\t\treturn label.join(LABEL_DELIM)+last;\n\t};\n\n\t/**\n\t * Applies the Timespan to the given date\n\t * \n\t * @param {Date=} date the date to which the timespan is added.\n\t * @return {Date}\n\t */\n\tTimespan.prototype.addTo = function(date) {\n\t\treturn addToDate(this, date);\n\t};\n\n\t/**\n\t * Formats the entries as English labels\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @return {Array}\n\t */\n\tformatList = function(ts) {\n\t\tvar list = [];\n\n\t\tvar value = ts.millennia;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_MILLENNIA));\n\t\t}\n\n\t\tvalue = ts.centuries;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_CENTURIES));\n\t\t}\n\n\t\tvalue = ts.decades;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_DECADES));\n\t\t}\n\n\t\tvalue = ts.years;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_YEARS));\n\t\t}\n\n\t\tvalue = ts.months;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_MONTHS));\n\t\t}\n\n\t\tvalue = ts.weeks;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_WEEKS));\n\t\t}\n\n\t\tvalue = ts.days;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_DAYS));\n\t\t}\n\n\t\tvalue = ts.hours;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_HOURS));\n\t\t}\n\n\t\tvalue = ts.minutes;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_MINUTES));\n\t\t}\n\n\t\tvalue = ts.seconds;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_SECONDS));\n\t\t}\n\n\t\tvalue = ts.milliseconds;\n\t\tif (value) {\n\t\t\tlist.push(formatter(value, LABEL_MILLISECONDS));\n\t\t}\n\n\t\treturn list;\n\t};\n\n\t/**\n\t * Borrow any underflow units, carry any overflow units\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @param {string} toUnit\n\t */\n\tfunction rippleRounded(ts, toUnit) {\n\t\tswitch (toUnit) {\n\t\t\tcase 'seconds':\n\t\t\t\tif (ts.seconds !== SECONDS_PER_MINUTE || isNaN(ts.minutes)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple seconds up to minutes\n\t\t\t\tts.minutes++;\n\t\t\t\tts.seconds = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'minutes':\n\t\t\t\tif (ts.minutes !== MINUTES_PER_HOUR || isNaN(ts.hours)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple minutes up to hours\n\t\t\t\tts.hours++;\n\t\t\t\tts.minutes = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'hours':\n\t\t\t\tif (ts.hours !== HOURS_PER_DAY || isNaN(ts.days)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple hours up to days\n\t\t\t\tts.days++;\n\t\t\t\tts.hours = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'days':\n\t\t\t\tif (ts.days !== DAYS_PER_WEEK || isNaN(ts.weeks)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple days up to weeks\n\t\t\t\tts.weeks++;\n\t\t\t\tts.days = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'weeks':\n\t\t\t\tif (ts.weeks !== daysPerMonth(ts.refMonth)/DAYS_PER_WEEK || isNaN(ts.months)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple weeks up to months\n\t\t\t\tts.months++;\n\t\t\t\tts.weeks = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'months':\n\t\t\t\tif (ts.months !== MONTHS_PER_YEAR || isNaN(ts.years)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple months up to years\n\t\t\t\tts.years++;\n\t\t\t\tts.months = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'years':\n\t\t\t\tif (ts.years !== YEARS_PER_DECADE || isNaN(ts.decades)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple years up to decades\n\t\t\t\tts.decades++;\n\t\t\t\tts.years = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'decades':\n\t\t\t\tif (ts.decades !== DECADES_PER_CENTURY || isNaN(ts.centuries)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple decades up to centuries\n\t\t\t\tts.centuries++;\n\t\t\t\tts.decades = 0;\n\n\t\t\t\t/* falls through */\n\t\t\tcase 'centuries':\n\t\t\t\tif (ts.centuries !== CENTURIES_PER_MILLENNIUM || isNaN(ts.millennia)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// ripple centuries up to millennia\n\t\t\t\tts.millennia++;\n\t\t\t\tts.centuries = 0;\n\t\t\t\t/* falls through */\n\t\t\t}\n\t}\n\n\t/**\n\t * Ripple up partial units one place\n\t * \n\t * @private\n\t * @param {Timespan} ts timespan\n\t * @param {number} frac accumulated fractional value\n\t * @param {string} fromUnit source unit name\n\t * @param {string} toUnit target unit name\n\t * @param {number} conversion multiplier between units\n\t * @param {number} digits max number of decimal digits to output\n\t * @return {number} new fractional value\n\t */\n\tfunction fraction(ts, frac, fromUnit, toUnit, conversion, digits) {\n\t\tif (ts[fromUnit] >= 0) {\n\t\t\tfrac += ts[fromUnit];\n\t\t\tdelete ts[fromUnit];\n\t\t}\n\n\t\tfrac /= conversion;\n\t\tif (frac + 1 <= 1) {\n\t\t\t// drop if below machine epsilon\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (ts[toUnit] >= 0) {\n\t\t\t// ensure does not have more than specified number of digits\n\t\t\tts[toUnit] = +(ts[toUnit] + frac).toFixed(digits);\n\t\t\trippleRounded(ts, toUnit);\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn frac;\n\t}\n\n\t/**\n\t * Ripple up partial units to next existing\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @param {number} digits max number of decimal digits to output\n\t */\n\tfunction fractional(ts, digits) {\n\t\tvar frac = fraction(ts, 0, 'milliseconds', 'seconds', MILLISECONDS_PER_SECOND, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'seconds', 'minutes', SECONDS_PER_MINUTE, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'minutes', 'hours', MINUTES_PER_HOUR, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'hours', 'days', HOURS_PER_DAY, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'days', 'weeks', DAYS_PER_WEEK, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'weeks', 'months', daysPerMonth(ts.refMonth)/DAYS_PER_WEEK, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'months', 'years', daysPerYear(ts.refMonth)/daysPerMonth(ts.refMonth), digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'years', 'decades', YEARS_PER_DECADE, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'decades', 'centuries', DECADES_PER_CENTURY, digits);\n\t\tif (!frac) { return; }\n\n\t\tfrac = fraction(ts, frac, 'centuries', 'millennia', CENTURIES_PER_MILLENNIUM, digits);\n\n\t\t// should never reach this with remaining fractional value\n\t\tif (frac) { throw new Error('Fractional unit overflow'); }\n\t}\n\n\t/**\n\t * Borrow any underflow units, carry any overflow units\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t */\n\tfunction ripple(ts) {\n\t\tvar x;\n\n\t\tif (ts.milliseconds < 0) {\n\t\t\t// ripple seconds down to milliseconds\n\t\t\tx = ceil(-ts.milliseconds / MILLISECONDS_PER_SECOND);\n\t\t\tts.seconds -= x;\n\t\t\tts.milliseconds += x * MILLISECONDS_PER_SECOND;\n\n\t\t} else if (ts.milliseconds >= MILLISECONDS_PER_SECOND) {\n\t\t\t// ripple milliseconds up to seconds\n\t\t\tts.seconds += floor(ts.milliseconds / MILLISECONDS_PER_SECOND);\n\t\t\tts.milliseconds %= MILLISECONDS_PER_SECOND;\n\t\t}\n\n\t\tif (ts.seconds < 0) {\n\t\t\t// ripple minutes down to seconds\n\t\t\tx = ceil(-ts.seconds / SECONDS_PER_MINUTE);\n\t\t\tts.minutes -= x;\n\t\t\tts.seconds += x * SECONDS_PER_MINUTE;\n\n\t\t} else if (ts.seconds >= SECONDS_PER_MINUTE) {\n\t\t\t// ripple seconds up to minutes\n\t\t\tts.minutes += floor(ts.seconds / SECONDS_PER_MINUTE);\n\t\t\tts.seconds %= SECONDS_PER_MINUTE;\n\t\t}\n\n\t\tif (ts.minutes < 0) {\n\t\t\t// ripple hours down to minutes\n\t\t\tx = ceil(-ts.minutes / MINUTES_PER_HOUR);\n\t\t\tts.hours -= x;\n\t\t\tts.minutes += x * MINUTES_PER_HOUR;\n\n\t\t} else if (ts.minutes >= MINUTES_PER_HOUR) {\n\t\t\t// ripple minutes up to hours\n\t\t\tts.hours += floor(ts.minutes / MINUTES_PER_HOUR);\n\t\t\tts.minutes %= MINUTES_PER_HOUR;\n\t\t}\n\n\t\tif (ts.hours < 0) {\n\t\t\t// ripple days down to hours\n\t\t\tx = ceil(-ts.hours / HOURS_PER_DAY);\n\t\t\tts.days -= x;\n\t\t\tts.hours += x * HOURS_PER_DAY;\n\n\t\t} else if (ts.hours >= HOURS_PER_DAY) {\n\t\t\t// ripple hours up to days\n\t\t\tts.days += floor(ts.hours / HOURS_PER_DAY);\n\t\t\tts.hours %= HOURS_PER_DAY;\n\t\t}\n\n\t\twhile (ts.days < 0) {\n\t\t\t// NOTE: never actually seen this loop more than once\n\n\t\t\t// ripple months down to days\n\t\t\tts.months--;\n\t\t\tts.days += borrowMonths(ts.refMonth, 1);\n\t\t}\n\n\t\t// weeks is always zero here\n\n\t\tif (ts.days >= DAYS_PER_WEEK) {\n\t\t\t// ripple days up to weeks\n\t\t\tts.weeks += floor(ts.days / DAYS_PER_WEEK);\n\t\t\tts.days %= DAYS_PER_WEEK;\n\t\t}\n\n\t\tif (ts.months < 0) {\n\t\t\t// ripple years down to months\n\t\t\tx = ceil(-ts.months / MONTHS_PER_YEAR);\n\t\t\tts.years -= x;\n\t\t\tts.months += x * MONTHS_PER_YEAR;\n\n\t\t} else if (ts.months >= MONTHS_PER_YEAR) {\n\t\t\t// ripple months up to years\n\t\t\tts.years += floor(ts.months / MONTHS_PER_YEAR);\n\t\t\tts.months %= MONTHS_PER_YEAR;\n\t\t}\n\n\t\t// years is always non-negative here\n\t\t// decades, centuries and millennia are always zero here\n\n\t\tif (ts.years >= YEARS_PER_DECADE) {\n\t\t\t// ripple years up to decades\n\t\t\tts.decades += floor(ts.years / YEARS_PER_DECADE);\n\t\t\tts.years %= YEARS_PER_DECADE;\n\n\t\t\tif (ts.decades >= DECADES_PER_CENTURY) {\n\t\t\t\t// ripple decades up to centuries\n\t\t\t\tts.centuries += floor(ts.decades / DECADES_PER_CENTURY);\n\t\t\t\tts.decades %= DECADES_PER_CENTURY;\n\n\t\t\t\tif (ts.centuries >= CENTURIES_PER_MILLENNIUM) {\n\t\t\t\t\t// ripple centuries up to millennia\n\t\t\t\t\tts.millennia += floor(ts.centuries / CENTURIES_PER_MILLENNIUM);\n\t\t\t\t\tts.centuries %= CENTURIES_PER_MILLENNIUM;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Remove any units not requested\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @param {number} units the units to populate\n\t * @param {number} max number of labels to output\n\t * @param {number} digits max number of decimal digits to output\n\t */\n\tfunction pruneUnits(ts, units, max, digits) {\n\t\tvar count = 0;\n\n\t\t// Calc from largest unit to smallest to prevent underflow\n\t\tif (!(units & MILLENNIA) || (count >= max)) {\n\t\t\t// ripple millennia down to centuries\n\t\t\tts.centuries += ts.millennia * CENTURIES_PER_MILLENNIUM;\n\t\t\tdelete ts.millennia;\n\n\t\t} else if (ts.millennia) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & CENTURIES) || (count >= max)) {\n\t\t\t// ripple centuries down to decades\n\t\t\tts.decades += ts.centuries * DECADES_PER_CENTURY;\n\t\t\tdelete ts.centuries;\n\n\t\t} else if (ts.centuries) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & DECADES) || (count >= max)) {\n\t\t\t// ripple decades down to years\n\t\t\tts.years += ts.decades * YEARS_PER_DECADE;\n\t\t\tdelete ts.decades;\n\n\t\t} else if (ts.decades) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & YEARS) || (count >= max)) {\n\t\t\t// ripple years down to months\n\t\t\tts.months += ts.years * MONTHS_PER_YEAR;\n\t\t\tdelete ts.years;\n\n\t\t} else if (ts.years) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & MONTHS) || (count >= max)) {\n\t\t\t// ripple months down to days\n\t\t\tif (ts.months) {\n\t\t\t\tts.days += borrowMonths(ts.refMonth, ts.months);\n\t\t\t}\n\t\t\tdelete ts.months;\n\n\t\t\tif (ts.days >= DAYS_PER_WEEK) {\n\t\t\t\t// ripple day overflow back up to weeks\n\t\t\t\tts.weeks += floor(ts.days / DAYS_PER_WEEK);\n\t\t\t\tts.days %= DAYS_PER_WEEK;\n\t\t\t}\n\n\t\t} else if (ts.months) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & WEEKS) || (count >= max)) {\n\t\t\t// ripple weeks down to days\n\t\t\tts.days += ts.weeks * DAYS_PER_WEEK;\n\t\t\tdelete ts.weeks;\n\n\t\t} else if (ts.weeks) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & DAYS) || (count >= max)) {\n\t\t\t//ripple days down to hours\n\t\t\tts.hours += ts.days * HOURS_PER_DAY;\n\t\t\tdelete ts.days;\n\n\t\t} else if (ts.days) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & HOURS) || (count >= max)) {\n\t\t\t// ripple hours down to minutes\n\t\t\tts.minutes += ts.hours * MINUTES_PER_HOUR;\n\t\t\tdelete ts.hours;\n\n\t\t} else if (ts.hours) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & MINUTES) || (count >= max)) {\n\t\t\t// ripple minutes down to seconds\n\t\t\tts.seconds += ts.minutes * SECONDS_PER_MINUTE;\n\t\t\tdelete ts.minutes;\n\n\t\t} else if (ts.minutes) {\n\t\t\tcount++;\n\t\t}\n\n\t\tif (!(units & SECONDS) || (count >= max)) {\n\t\t\t// ripple seconds down to milliseconds\n\t\t\tts.milliseconds += ts.seconds * MILLISECONDS_PER_SECOND;\n\t\t\tdelete ts.seconds;\n\n\t\t} else if (ts.seconds) {\n\t\t\tcount++;\n\t\t}\n\n\t\t// nothing to ripple milliseconds down to\n\t\t// so ripple back up to smallest existing unit as a fractional value\n\t\tif (!(units & MILLISECONDS) || (count >= max)) {\n\t\t\tfractional(ts, digits);\n\t\t}\n\t}\n\n\t/**\n\t * Populates the Timespan object\n\t * \n\t * @private\n\t * @param {Timespan} ts\n\t * @param {?Date} start the starting date\n\t * @param {?Date} end the ending date\n\t * @param {number} units the units to populate\n\t * @param {number} max number of labels to output\n\t * @param {number} digits max number of decimal digits to output\n\t */\n\tfunction populate(ts, start, end, units, max, digits) {\n\t\tvar now = new Date();\n\n\t\tts.start = start = start || now;\n\t\tts.end = end = end || now;\n\t\tts.units = units;\n\n\t\tts.value = end.getTime() - start.getTime();\n\t\tif (ts.value < 0) {\n\t\t\t// swap if reversed\n\t\t\tvar tmp = end;\n\t\t\tend = start;\n\t\t\tstart = tmp;\n\t\t}\n\n\t\t// reference month for determining days in month\n\t\tts.refMonth = new Date(start.getFullYear(), start.getMonth(), 15, 12, 0, 0);\n\t\ttry {\n\t\t\t// reset to initial deltas\n\t\t\tts.millennia = 0;\n\t\t\tts.centuries = 0;\n\t\t\tts.decades = 0;\n\t\t\tts.years = end.getFullYear() - start.getFullYear();\n\t\t\tts.months = end.getMonth() - start.getMonth();\n\t\t\tts.weeks = 0;\n\t\t\tts.days = end.getDate() - start.getDate();\n\t\t\tts.hours = end.getHours() - start.getHours();\n\t\t\tts.minutes = end.getMinutes() - start.getMinutes();\n\t\t\tts.seconds = end.getSeconds() - start.getSeconds();\n\t\t\tts.milliseconds = end.getMilliseconds() - start.getMilliseconds();\n\n\t\t\tripple(ts);\n\t\t\tpruneUnits(ts, units, max, digits);\n\n\t\t} finally {\n\t\t\tdelete ts.refMonth;\n\t\t}\n\n\t\treturn ts;\n\t}\n\n\t/**\n\t * Determine an appropriate refresh rate based upon units\n\t * \n\t * @private\n\t * @param {number} units the units to populate\n\t * @return {number} milliseconds to delay\n\t */\n\tfunction getDelay(units) {\n\t\tif (units & MILLISECONDS) {\n\t\t\t// refresh very quickly\n\t\t\treturn MILLISECONDS_PER_SECOND / 30; //30Hz\n\t\t}\n\n\t\tif (units & SECONDS) {\n\t\t\t// refresh every second\n\t\t\treturn MILLISECONDS_PER_SECOND; //1Hz\n\t\t}\n\n\t\tif (units & MINUTES) {\n\t\t\t// refresh every minute\n\t\t\treturn MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE;\n\t\t}\n\n\t\tif (units & HOURS) {\n\t\t\t// refresh hourly\n\t\t\treturn MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR;\n\t\t}\n\t\t\n\t\tif (units & DAYS) {\n\t\t\t// refresh daily\n\t\t\treturn MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY;\n\t\t}\n\n\t\t// refresh the rest weekly\n\t\treturn MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK;\n\t}\n\n\t/**\n\t * API entry point\n\t * \n\t * @public\n\t * @param {Date|number|Timespan|null|function(Timespan,number)} start the starting date\n\t * @param {Date|number|Timespan|null|function(Timespan,number)} end the ending date\n\t * @param {number=} units the units to populate\n\t * @param {number=} max number of labels to output\n\t * @param {number=} digits max number of decimal digits to output\n\t * @return {Timespan|number}\n\t */\n\tfunction countdown(start, end, units, max, digits) {\n\t\tvar callback;\n\n\t\t// ensure some units or use defaults\n\t\tunits = +units || DEFAULTS;\n\t\t// max must be positive\n\t\tmax = (max > 0) ? max : NaN;\n\t\t// clamp digits to an integer between [0, 20]\n\t\tdigits = (digits > 0) ? (digits < 20) ? Math.round(digits) : 20 : 0;\n\n\t\t// ensure start date\n\t\tvar startTS = null;\n\t\tif ('function' === typeof start) {\n\t\t\tcallback = start;\n\t\t\tstart = null;\n\n\t\t} else if (!(start instanceof Date)) {\n\t\t\tif ((start !== null) && isFinite(start)) {\n\t\t\t\tstart = new Date(+start);\n\t\t\t} else {\n\t\t\t\tif ('object' === typeof startTS) {\n\t\t\t\t\tstartTS = /** @type{Timespan} */(start);\n\t\t\t\t}\n\t\t\t\tstart = null;\n\t\t\t}\n\t\t}\n\n\t\t// ensure end date\n\t\tvar endTS = null;\n\t\tif ('function' === typeof end) {\n\t\t\tcallback = end;\n\t\t\tend = null;\n\n\t\t} else if (!(end instanceof Date)) {\n\t\t\tif ((end !== null) && isFinite(end)) {\n\t\t\t\tend = new Date(+end);\n\t\t\t} else {\n\t\t\t\tif ('object' === typeof end) {\n\t\t\t\t\tendTS = /** @type{Timespan} */(end);\n\t\t\t\t}\n\t\t\t\tend = null;\n\t\t\t}\n\t\t}\n\n\t\t// must wait to interpret timespans until after resolving dates\n\t\tif (startTS) {\n\t\t\tstart = addToDate(startTS, end);\n\t\t}\n\t\tif (endTS) {\n\t\t\tend = addToDate(endTS, start);\n\t\t}\n\n\t\tif (!start && !end) {\n\t\t\t// used for unit testing\n\t\t\treturn new Timespan();\n\t\t}\n\n\t\tif (!callback) {\n\t\t\treturn populate(new Timespan(), /** @type{Date} */(start), /** @type{Date} */(end), /** @type{number} */(units), /** @type{number} */(max), /** @type{number} */(digits));\n\t\t}\n\n\t\t// base delay off units\n\t\tvar delay = getDelay(units),\n\t\t\ttimerId,\n\t\t\tfn = function() {\n\t\t\t\tcallback(\n\t\t\t\t\tpopulate(new Timespan(), /** @type{Date} */(start), /** @type{Date} */(end), /** @type{number} */(units), /** @type{number} */(max), /** @type{number} */(digits)),\n\t\t\t\t\ttimerId\n\t\t\t\t);\n\t\t\t};\n\n\t\tfn();\n\t\treturn (timerId = setInterval(fn, delay));\n\t}\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.MILLISECONDS = MILLISECONDS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.SECONDS = SECONDS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.MINUTES = MINUTES;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.HOURS = HOURS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.DAYS = DAYS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.WEEKS = WEEKS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.MONTHS = MONTHS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.YEARS = YEARS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.DECADES = DECADES;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.CENTURIES = CENTURIES;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.MILLENNIA = MILLENNIA;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.DEFAULTS = DEFAULTS;\n\n\t/**\n\t * @public\n\t * @const\n\t * @type {number}\n\t */\n\tcountdown.ALL = MILLENNIA|CENTURIES|DECADES|YEARS|MONTHS|WEEKS|DAYS|HOURS|MINUTES|SECONDS|MILLISECONDS;\n\n\t/**\n\t * Customize the format settings.\n\t * @public\n\t * @param {Object} format settings object\n\t */\n\tvar setFormat = countdown.setFormat = function(format) {\n\t\tif (!format) { return; }\n\n\t\tif ('singular' in format || 'plural' in format) {\n\t\t\tvar singular = format.singular || [];\n\t\t\tif (singular.split) {\n\t\t\t\tsingular = singular.split('|');\n\t\t\t}\n\t\t\tvar plural = format.plural || [];\n\t\t\tif (plural.split) {\n\t\t\t\tplural = plural.split('|');\n\t\t\t}\n\n\t\t\tfor (var i=LABEL_MILLISECONDS; i<=LABEL_MILLENNIA; i++) {\n\t\t\t\t// override any specified units\n\t\t\t\tLABELS_SINGLUAR[i] = singular[i] || LABELS_SINGLUAR[i];\n\t\t\t\tLABELS_PLURAL[i] = plural[i] || LABELS_PLURAL[i];\n\t\t\t}\n\t\t}\n\n\t\tif ('string' === typeof format.last) {\n\t\t\tLABEL_LAST = format.last;\n\t\t}\n\t\tif ('string' === typeof format.delim) {\n\t\t\tLABEL_DELIM = format.delim;\n\t\t}\n\t\tif ('string' === typeof format.empty) {\n\t\t\tLABEL_NOW = format.empty;\n\t\t}\n\t\tif ('function' === typeof format.formatNumber) {\n\t\t\tformatNumber = format.formatNumber;\n\t\t}\n\t\tif ('function' === typeof format.formatter) {\n\t\t\tformatter = format.formatter;\n\t\t}\n\t};\n\n\t/**\n\t * Revert to the default formatting.\n\t * @public\n\t */\n\tvar resetFormat = countdown.resetFormat = function() {\n\t\tLABELS_SINGLUAR = ' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium'.split('|');\n\t\tLABELS_PLURAL = ' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia'.split('|');\n\t\tLABEL_LAST = ' and ';\n\t\tLABEL_DELIM = ', ';\n\t\tLABEL_NOW = '';\n\t\tformatNumber = function(value) { return value; };\n\t\tformatter = plurality;\n\t};\n\n\t/**\n\t * Override the unit labels.\n\t * @public\n\t * @param {string|Array=} singular a pipe ('|') delimited list of singular unit name overrides\n\t * @param {string|Array=} plural a pipe ('|') delimited list of plural unit name overrides\n\t * @param {string=} last a delimiter before the last unit (default: ' and ')\n\t * @param {string=} delim a delimiter to use between all other units (default: ', ')\n\t * @param {string=} empty a label to use when all units are zero (default: '')\n\t * @param {function(number):string=} formatNumber a function which formats numbers as a string\n\t * @param {function(number,number):string=} formatter a function which formats a number/unit pair as a string\n\t * @deprecated since version 2.6.0\n\t */\n\tcountdown.setLabels = function(singular, plural, last, delim, empty, formatNumber, formatter) {\n\t\tsetFormat({\n\t\t\tsingular: singular,\n\t\t\tplural: plural,\n\t\t\tlast: last,\n\t\t\tdelim: delim,\n\t\t\tempty: empty,\n\t\t\tformatNumber: formatNumber,\n\t\t\tformatter: formatter\n\t\t});\n\t};\n\n\t/**\n\t * Revert to the default unit labels.\n\t * @public\n\t * @deprecated since version 2.6.0\n\t */\n\tcountdown.resetLabels = resetFormat;\n\n\tresetFormat();\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tmodule.exports = countdown;\n\n\t} else if (typeof window !== 'undefined' && typeof window.define === 'function' && typeof window.define.amd !== 'undefined') {\n\t\twindow.define('countdown', [], function() {\n\t\t\treturn countdown;\n\t\t});\n\t}\n\n\treturn countdown;\n\n})();\n"
  },
  {
    "path": "demo.html",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Countdown.js Demo</title>\n\n\t<link href=\"test/styles.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head>\n<body>\n\n\t<h1><a href=\"/\">Countdown.js</a></h1>\n\n\t<div>\n\t\t<h2 id=\"counter\">Countdown.js Demo</h2>\n\t\t<div style=\"float:right\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\" data-count=\"none\"></a></div>\n\n\t\t<form>\n\t\t\t<h3>Countdown date</h3>\n\t\t\t<fieldset id=\"countdown-start\"><span>\n\t\t\t\t<input id=\"year\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"-9999\"\n\t\t\t\t\tmax=\"9999\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"1991\"\n\t\t\t\t\tsize=\"4\"\n\t\t\t\t\tplaceholder=\"year\"\n\t\t\t\t/><label for=\"month\">-</label><input id=\"month\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"1\"\n\t\t\t\t\tmax=\"12\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"8\"\n\t\t\t\t\tsize=\"2\"\n\t\t\t\t\tplaceholder=\"month\"\n\t\t\t\t/><label for=\"date\">-</label><input id=\"date\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"1\"\n\t\t\t\t\tmax=\"31\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"6\"\n\t\t\t\t\tsize=\"2\"\n\t\t\t\t\tplaceholder=\"day\"\n\t\t\t\t/></span>\n\t\t\t\t<span><label for=\"hours\">&nbsp;</label><input id=\"hours\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"23\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"14\"\n\t\t\t\t\tsize=\"2\"\n\t\t\t\t\tplaceholder=\"hrs\"\n\t\t\t\t/><label for=\"minutes\">:</label><input id=\"minutes\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"59\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"56\"\n\t\t\t\t\tsize=\"2\"\n\t\t\t\t\tplaceholder=\"min\"\n\t\t\t\t/><label for=\"seconds\">:</label><input id=\"seconds\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"59\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"20\"\n\t\t\t\t\tsize=\"2\"\n\t\t\t\t\tplaceholder=\"sec\"\n\t\t\t\t/><label for=\"milliseconds\">.</label><input id=\"milliseconds\"\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tmin=\"0\"\n\t\t\t\t\tmax=\"999\"\n\t\t\t\t\tstep=\"1\"\n\t\t\t\t\tvalue=\"0\"\n\t\t\t\t\tsize=\"3\"\n\t\t\t\t\tplaceholder=\"ms\"\n\t\t\t\t/></span>\n\t\t\t\t<span id=\"timezone\">&nbsp;UTC</span>\n\t\t\t</fieldset>\n\n\t\t\t<h3>Display units</h3>\n\t\t\t<fieldset id=\"countdown-units\"><span>\n\t\t\t\t<label for=\"units-millennia\"><input id=\"units-millennia\" type=\"checkbox\" />millennia</label>\n\t\t\t\t<label for=\"units-centuries\"><input id=\"units-centuries\" type=\"checkbox\" />centuries</label>\n\t\t\t\t<label for=\"units-decades\"><input id=\"units-decades\" type=\"checkbox\" />decades</label>\n\t\t\t</span><span>\n\t\t\t\t<label for=\"units-years\"><input id=\"units-years\" type=\"checkbox\" />years</label>\n\t\t\t\t<label for=\"units-months\"><input id=\"units-months\" type=\"checkbox\" />months</label>\n\t\t\t\t<label for=\"units-weeks\"><input id=\"units-weeks\" type=\"checkbox\" />weeks</label>\n\t\t\t\t<label for=\"units-days\"><input id=\"units-days\" type=\"checkbox\" />days</label>\n\t\t\t</span><span>\n\t\t\t\t<label for=\"units-hours\"><input id=\"units-hours\" type=\"checkbox\" />hours</label>\n\t\t\t\t<label for=\"units-minutes\"><input id=\"units-minutes\" type=\"checkbox\" />minutes</label>\n\t\t\t\t<label for=\"units-seconds\"><input id=\"units-seconds\" type=\"checkbox\" />seconds</label>\n\t\t\t\t<label for=\"units-milliseconds\"><input id=\"units-milliseconds\" type=\"checkbox\" />milliseconds</label>\n\t\t\t</span><span>\n\t\t\t\t<label for=\"empty-label\">empty</label><input id=\"empty-label\" type=\"text\" value=\"now!\" />\n\t\t\t</span><span>\n\t\t\t\t<label for=\"max-units\">max</label><input id=\"max-units\" type=\"number\" min=\"0\" max=\"11\" value=\"11\" />\n\t\t\t\t<label for=\"frac-digits\">digits</label><input id=\"frac-digits\" type=\"number\" min=\"0\" max=\"20\" value=\"0\" />\n\t\t\t<span></fieldset>\n\t\t</form>\n\n\t\t<h3>Timespan object</h3>\n\t\t<pre id=\"timespan\"></pre>\n\t</div>\n\n\t<script src=\"lib/json/json2.js\" type=\"text/javascript\"></script>\n\t<script src=\"lib/raf/requestAnimationFrame.js\" type=\"text/javascript\"></script>\n\t<script src=\"countdown.demo.js\" type=\"text/javascript\"></script>\n\t<script src=\"demo.js\" type=\"text/javascript\"></script>\n\n\t<footer>\n\t\t<div style=\"float:left\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\"></a></div>\n\t\tCopyright &copy; 2006-2012 <a href=\"http://mck.me\">Stephen M. McKamey</a>\n\t</footer>\n\t<script src=\"/ga.js\" type=\"text/javascript\" defer></script>\n\t<script src=\"http://platform.twitter.com/widgets.js\" type=\"text/javascript\" defer=\"defer\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "demo.js",
    "content": "(function() {\n\tfunction byId(id) {\n\t\treturn document.getElementById(id);\n\t}\n\n\tfunction formatTens(n) {\n\t\t// format integers to have at least two digits\n\t\treturn (n < 10) ? '0'+n : ''+n;\n\t}\n\n\t// initialize units\n\tfor (var key in countdown) {\n\t\tif (countdown.hasOwnProperty(key)) {\n\t\t\tvar unit = byId('units-'+key.toLowerCase());\n\t\t\tif (unit) {\n\t\t\t\tunit.value = countdown[key];\n\t\t\t\tunit.checked = countdown[key] & countdown.DEFAULTS;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Mayan Calendar: 1356088271111\n\n\t// https://groups.google.com/group/alt.hypertext/msg/06dad279804cb3ba?dmode=source\n\tvar DEMO_MS = 681490580000,\n\t\tDEMO_PAST = 'The World Wide Web debuted',\n\t\tDEMO_FUTURE = 'The World Wide Web debuts';\n\n\t// adjust initial demo time for local timezone\n\tbyId('hours').value -= new Date(DEMO_MS).getTimezoneOffset() / 60;\n\n\tfunction update() {\n\t\tvar units = ~countdown.ALL,\n\t\t\tchx = byId('countdown-units').getElementsByTagName('input'),\n\t\t\tempty = byId('empty-label').value || null,\n\t\t\tmax = +(byId('max-units').value),\n\t\t\tdigits = +(byId('frac-digits').value);\n\n\t\tfor (var i=0, count=chx.length; i<count; i++) {\n\t\t\tif (chx[i].checked) {\n\t\t\t\tunits |= +(chx[i].value);\n\t\t\t}\n\t\t}\n\n\t\tvar yyyy = +(byId('year').value),\n\t\t\tMM = +(byId('month').value)-1,\n\t\t\tdd = +(byId('date').value),\n\t\t\tHH = +(byId('hours').value),\n\t\t\tmm = +(byId('minutes').value),\n\t\t\tss = +(byId('seconds').value),\n\t\t\tfff = +(byId('milliseconds').value);\n\n\t\tvar start = new Date(yyyy, MM, dd, HH, mm, ss, fff),\n\t\t\tts = countdown(start, null, units, max, digits);\n\n\t\tvar counter = byId('counter'),\n\t\t\ttimespan = byId('timespan'),\n\t\t\tmsg = ts.toHTML('strong', empty);\n\n\t\tif (start.getTime() === DEMO_MS) {\n\t\t\tmsg = (ts.value > 0) ?\n\t\t\t\tDEMO_PAST+' '+msg+' ago!' :\n\t\t\t\tDEMO_FUTURE+' in '+msg+'!';\n\t\t}\n\n\t\tcounter.innerHTML = msg;\n\t\ttimespan.innerHTML = JSON.stringify(ts, null, '  ');\n\n\t\t// update timezone label\n\t\tvar tz = start.getTimezoneOffset();\n\t\tif (tz) {\n\t\t\tvar tzh = Math.floor(Math.abs(tz) / 60),\n\t\t\t\ttzm = (Math.abs(tz) % 60);\n\t\t\tbyId('timezone').innerHTML = 'UTC'+((tz > 0) ? '-' : '+')+formatTens(tzh)+':'+formatTens(tzm);\n\n\t\t} else {\n\t\t\tbyId('timezone').innerHTML = 'UTC';\n\t\t}\n\n\t\trequestAnimationFrame(update, timespan.parentNode);\n\t}\n\tupdate();\n})();\n"
  },
  {
    "path": "externs.js",
    "content": "/**\n * @fileoverview External module declarations.\n * @externs\n */\n\n/**\n * @type {Object|undefined}\n */\nvar module;\n\n/**\n * @type {Object|undefined}\n */\nvar exports;\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Countdown.js</title>\n\n\t<link href=\"test/styles.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head>\n<body>\n\n\t<h1><a href=\"http://countdownjs.org\">Countdown.js</a></h1>\n\t<div style=\"float:right\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\" data-count=\"none\"></a></div>\n\t<p class=\"summary\">A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.</p>\n\n\t<div style=\"overflow:hidden;\">\n\t\t<div class=\"box\">\n\t\t\t<h2>Docs</h2>\n\t\t\t<ul>\n\t\t\t\t<li><a href=\"demo.html\" class=\"minimal glow\">demonstration</a>\n\t\t\t\t\t<span class=\"action-info\">watch it go</span></li>\n\t\t\t\t<li><a href=\"test/unit.html\" class=\"minimal\">unit tests</a>\n\t\t\t\t\t<span class=\"action-info\">test it out</span></li>\n\t\t\t\t<li><a href=\"readme.html\" class=\"minimal\">read me</a>\n\t\t\t\t\t<span class=\"action-info\">what? why? how?</span></li>\n\t\t\t</ul>\n\t\t</div>\n\n\t\t<div class=\"box\">\n\t\t\t<h2>Downloads</h2>\n\t\t\t<ul>\n\t\t\t\t<li><a href=\"https://raw.githubusercontent.com/mckamey/countdownjs/master/countdown.js\" class=\"minimal\">countdown.js</a>\n\t\t\t\t\t<span class=\"action-info\">full script</span></li>\n\t\t\t\t<li><a href=\"https://raw.githubusercontent.com/mckamey/countdownjs/master/countdown.min.js\" class=\"minimal\">countdown.min.js</a>\n\t\t\t\t\t<span class=\"action-info\">minified script</span></li>\n\t\t\t\t<li><a href=\"https://raw.githubusercontent.com/mckamey/countdownjs/master/LICENSE.txt\" class=\"minimal\">LICENSE.txt</a>\n\t\t\t\t\t<span class=\"action-info\">MIT License</span></li>\n\t\t\t</ul>\n\t\t</div>\n\n\t\t<div class=\"box\">\n\t\t\t<h2>Links</h2>\n\t\t\t<ul>\n\t\t\t\t<li><a href=\"http://countdownjs.org\" class=\"minimal\">countdownjs.org</a>\n\t\t\t\t\t<span class=\"action-info\">the website</span></li>\n\t\t\t\t<li><a href=\"https://github.com/mckamey/countdownjs\" class=\"minimal\">source code</a>\n\t\t\t\t\t<span class=\"action-info\">the latest bits</span></li>\n\t\t\t</ul>\n\t\t</div>\n\t</div>\n\n\t<footer>\n\t\t<div style=\"float:left\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\"></a></div>\n\t\tCopyright &copy; 2006-2012 <a href=\"http://mck.me\">Stephen M. McKamey</a>\n\t</footer>\n\t<script src=\"/ga.js\" type=\"text/javascript\" defer></script>\n\t<script src=\"http://platform.twitter.com/widgets.js\" type=\"text/javascript\" defer=\"defer\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "lib/closure/COPYING",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "lib/closure/README",
    "content": "/*\n * Copyright 2009 The Closure Compiler Authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Contents\n//\n\nThe Closure Compiler performs checking, instrumentation, and\noptimizations on JavaScript code. The purpose of this README is to\nexplain how to build and run the Closure Compiler.\n\nThe Closure Compiler requires Java 6 or higher.\nhttp://www.java.com/\n\n\n//\n// Building The Closure Compiler\n//\n\nThere are three ways to get a Closure Compiler executable.\n\n1) Use one we built for you.\n\nPre-built Closure binaries can be found at\nhttp://code.google.com/p/closure-compiler/downloads/list\n\n\n2) Check out the source and build it with Apache Ant.\n\nFirst, check out the full source tree of the Closure Compiler. There\nare instructions on how to do this at the project site.\nhttp://code.google.com/p/closure-compiler/source/checkout\n\nApache Ant is a cross-platform build tool.\nhttp://ant.apache.org/\n\nAt the root of the source tree, there is an Ant file named\nbuild.xml. To use it, navigate to the same directory and type the\ncommand\n\nant jar\n\nThis will produce a jar file called \"build/compiler.jar\".\n\n\n3) Check out the source and build it with Eclipse.\n\nEclipse is a cross-platform IDE.\nhttp://www.eclipse.org/\n\nUnder Eclipse's File menu, click \"New > Project ...\" and create a\n\"Java Project.\"  You will see an options screen. Give the project a\nname, select \"Create project from existing source,\" and choose the\nroot of the checked-out source tree as the existing directory. Verify\nthat you are using JRE version 6 or higher.\n\nEclipse can use the build.xml file to discover rules. When you\nnavigate to the build.xml file, you will see all the build rules in\nthe \"Outline\" pane. Run the \"jar\" rule to build the compiler in\nbuild/compiler.jar.\n\n\n//\n// Running The Closure Compiler\n//\n\nOnce you have the jar binary, running the Closure Compiler is straightforward.\n\nOn the command line, type\n\njava -jar compiler.jar\n\nThis starts the compiler in interactive mode. Type\n\nvar x = 17 + 25;\n\nthen hit \"Enter\", then hit \"Ctrl-Z\" (on Windows) or \"Ctrl-D\" (on Mac or Linux)\nand \"Enter\" again. The Compiler will respond:\n\nvar x=42;\n\nThe Closure Compiler has many options for reading input from a file,\nwriting output to a file, checking your code, and running\noptimizations. To learn more, type\n\njava -jar compiler.jar --help\n\nYou can read more detailed documentation about the many flags at\nhttp://code.google.com/closure/compiler/docs/gettingstarted_app.html\n\n\n//\n// Compiling Multiple Scripts\n//\n\nIf you have multiple scripts, you should compile them all together with\none compile command.\n\njava -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js\n\nThe Closure Compiler will concatenate the files in the order they're\npassed at the command line.\n\nIf you need to compile many, many scripts together, you may start to\nrun into problems with managing dependencies between scripts. You\nshould check out the Closure Library. It contains functions for\nenforcing dependencies between scripts, and a tool called calcdeps.py\nthat knows how to give scripts to the Closure Compiler in the right\norder.\n\nhttp://code.google.com/p/closure-library/\n\n//\n// Licensing\n//\n\nUnless otherwise stated, all source files are licensed under\nthe Apache License, Version 2.0.\n\n\n-----\nCode under:\nsrc/com/google/javascript/rhino\ntest/com/google/javascript/rhino\n\nURL: http://www.mozilla.org/rhino\nVersion:  1.5R3, with heavy modifications\nLicense:  Netscape Public License and MPL / GPL dual license\n\nDescription: A partial copy of Mozilla Rhino. Mozilla Rhino is an\nimplementation of JavaScript for the JVM.  The JavaScript parser and\nthe parse tree data structures were extracted and modified\nsignificantly for use by Google's JavaScript compiler.\n\nLocal Modifications: The packages have been renamespaced. All code not\nrelevant to parsing has been removed. A JsDoc parser and static typing\nsystem have been added.\n\n\n-----\nCode in:\nlib/rhino\n\nRhino\nURL: http://www.mozilla.org/rhino\nVersion:  Trunk\nLicense:  Netscape Public License and MPL / GPL dual license\n\nDescription: Mozilla Rhino is an implementation of JavaScript for the JVM.\n\nLocal Modifications: Minor changes to parsing JSDoc that usually get pushed\nup-stream to Rhino trunk.\n\n\n-----\nCode in:\nlib/args4j.jar\n\nArgs4j\nURL: https://args4j.dev.java.net/\nVersion: 2.0.16\nLicense: MIT\n\nDescription:\nargs4j is a small Java class library that makes it easy to parse command line\noptions/arguments in your CUI application.\n\nLocal Modifications: None.\n\n\n-----\nCode in:\nlib/guava.jar\n\nGuava Libraries\nURL: http://code.google.com/p/guava-libraries/\nVersion:  14.0\nLicense: Apache License 2.0\n\nDescription: Google's core Java libraries.\n\nLocal Modifications: None.\n\n\n-----\nCode in:\nlib/jsr305.jar\n\nAnnotations for software defect detection\nURL: http://code.google.com/p/jsr-305/\nVersion: svn revision 47\nLicense: BSD License\n\nDescription: Annotations for software defect detection.\n\nLocal Modifications: None.\n\n\n-----\nCode in:\nlib/jarjar.jar\n\nJar Jar Links\nURL: http://jarjar.googlecode.com/\nVersion: 1.1\nLicense: Apache License 2.0\n\nDescription:\nA utility for repackaging Java libraries.\n\nLocal Modifications: None.\n\n\n----\nCode in:\nlib/junit.jar\n\nJUnit\nURL:  http://sourceforge.net/projects/junit/\nVersion:  4.10\nLicense:  Common Public License 1.0\n\nDescription: A framework for writing and running automated tests in Java.\n\nLocal Modifications: None.\n\n\n---\nCode in:\nlib/protobuf-java.jar\n\nProtocol Buffers\nURL: http://code.google.com/p/protobuf/\nVersion: 2.4.1\nLicense: New BSD License\n\nDescription: Supporting libraries for protocol buffers,\nan encoding of structured data.\n\nLocal Modifications: None\n\n\n---\nCode in:\nlib/ant.jar\nlib/ant-launcher.jar\n\nURL: http://ant.apache.org/bindownload.cgi\nVersion: 1.8.1\nLicense: Apache License 2.0\nDescription:\n  Ant is a Java based build tool. In theory it is kind of like \"make\"\n  without make's wrinkles and with the full portability of pure java code.\n\nLocal Modifications: None\n\n\n---\nCode in:\nlib/json.jar\nURL: http://json.org/java/index.html\nVersion: JSON version 20090211\nLicense: MIT license\nDescription:\nJSON is a set of java files for use in transmitting data in JSON format.\n\nLocal Modifications: None\n\n---\nCode in:\ntools/maven-ant-tasks-2.1.3.jar\nURL: http://maven.apache.org\nVersion 2.1.3\nLicense: Apache License 2.0\nDescription:\n  Maven Ant tasks are used to manage dependencies and to install/deploy to\n  maven repositories.\n\nLocal Modifications: None\n"
  },
  {
    "path": "lib/closure/docs.url",
    "content": "[InternetShortcut]\r\nURL=http://code.google.com/closure/compiler/docs/js-for-compiler.html\r\n"
  },
  {
    "path": "lib/closure/download.url",
    "content": "[InternetShortcut]\r\nURL=http://code.google.com/closure/compiler/\r\n"
  },
  {
    "path": "lib/jslint/download.url",
    "content": "[InternetShortcut]\r\nURL=http://www.jslint.com/lint.html\r\n"
  },
  {
    "path": "lib/jslint/jslint.js",
    "content": "// jslint.js\n// 2010-10-16\n\n/*\nCopyright (c) 2002 Douglas Crockford  (www.JSLint.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nThe Software shall be used for Good, not Evil.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n/*\n    JSLINT is a global function. It takes two parameters.\n\n        var myResult = JSLINT(source, option);\n\n    The first parameter is either a string or an array of strings. If it is a\n    string, it will be split on '\\n' or '\\r'. If it is an array of strings, it\n    is assumed that each string represents one line. The source can be a\n    JavaScript text, or HTML text, or a Konfabulator text.\n\n    The second parameter is an optional object of options which control the\n    operation of JSLINT. Most of the options are booleans: They are all are\n    optional and have a default value of false.\n\n    If it checks out, JSLINT returns true. Otherwise, it returns false.\n\n    If false, you can inspect JSLINT.errors to find out the problems.\n    JSLINT.errors is an array of objects containing these members:\n\n    {\n        line      : The line (relative to 0) at which the lint was found\n        character : The character (relative to 0) at which the lint was found\n        reason    : The problem\n        evidence  : The text line in which the problem occurred\n        raw       : The raw message before the details were inserted\n        a         : The first detail\n        b         : The second detail\n        c         : The third detail\n        d         : The fourth detail\n    }\n\n    If a fatal error was found, a null will be the last element of the\n    JSLINT.errors array.\n\n    You can request a Function Report, which shows all of the functions\n    and the parameters and vars that they use. This can be used to find\n    implied global variables and other problems. The report is in HTML and\n    can be inserted in an HTML <body>.\n\n        var myReport = JSLINT.report(limited);\n\n    If limited is true, then the report will be limited to only errors.\n\n    You can request a data structure which contains JSLint's results.\n\n        var myData = JSLINT.data();\n\n    It returns a structure with this form:\n\n    {\n        errors: [\n            {\n                line: NUMBER,\n                character: NUMBER,\n                reason: STRING,\n                evidence: STRING\n            }\n        ],\n        functions: [\n            name: STRING,\n            line: NUMBER,\n            last: NUMBER,\n            param: [\n                STRING\n            ],\n            closure: [\n                STRING\n            ],\n            var: [\n                STRING\n            ],\n            exception: [\n                STRING\n            ],\n            outer: [\n                STRING\n            ],\n            unused: [\n                STRING\n            ],\n            global: [\n                STRING\n            ],\n            label: [\n                STRING\n            ]\n        ],\n        globals: [\n            STRING\n        ],\n        member: {\n            STRING: NUMBER\n        },\n        unuseds: [\n            {\n                name: STRING,\n                line: NUMBER\n            }\n        ],\n        implieds: [\n            {\n                name: STRING,\n                line: NUMBER\n            }\n        ],\n        urls: [\n            STRING\n        ],\n        json: BOOLEAN\n    }\n\n    Empty arrays will not be included.\n\n*/\n\n/*jslint\n    evil: true, nomen: false, onevar: false, regexp: false, strict: true\n*/\n\n/*members \"\\b\", \"\\t\", \"\\n\", \"\\f\", \"\\r\", \"!=\", \"!==\", \"\\\"\", \"%\",\n    \"(begin)\", \"(breakage)\", \"(context)\", \"(error)\", \"(global)\",\n    \"(identifier)\", \"(last)\", \"(line)\", \"(loopage)\", \"(name)\", \"(onevar)\",\n    \"(params)\", \"(scope)\", \"(statement)\", \"(verb)\", \"*\", \"+\", \"++\", \"-\",\n    \"--\", \"\\/\", \"<\", \"<=\", \"==\", \"===\", \">\", \">=\", ADSAFE, ActiveXObject,\n    Array, Boolean, COM, CScript, Canvas, CustomAnimation, Date, Debug, E,\n    Enumerator, Error, EvalError, FadeAnimation, Flash, FormField, Frame,\n    Function, HotKey, Image, JSON, LN10, LN2, LOG10E, LOG2E, MAX_VALUE,\n    MIN_VALUE, Math, MenuItem, MoveAnimation, NEGATIVE_INFINITY, Number,\n    Object, Option, PI, POSITIVE_INFINITY, Point, RangeError, Rectangle,\n    ReferenceError, RegExp, ResizeAnimation, RotateAnimation, SQRT1_2,\n    SQRT2, ScrollBar, String, Style, SyntaxError, System, Text, TextArea,\n    Timer, TypeError, URIError, URL, VBArray, WScript, Web, Window, XMLDOM,\n    XMLHttpRequest, \"\\\\\", a, abbr, acronym, addEventListener, address,\n    adsafe, alert, aliceblue, animator, antiquewhite, appleScript, applet,\n    apply, approved, aqua, aquamarine, area, arguments, arity, article,\n    aside, audio, autocomplete, azure, b, background,\n    \"background-attachment\", \"background-color\", \"background-image\",\n    \"background-position\", \"background-repeat\", base, bdo, beep, beige, big,\n    bisque, bitwise, black, blanchedalmond, block, blockquote, blue,\n    blueviolet, blur, body, border, \"border-bottom\", \"border-bottom-color\",\n    \"border-bottom-style\", \"border-bottom-width\", \"border-collapse\",\n    \"border-color\", \"border-left\", \"border-left-color\", \"border-left-style\",\n    \"border-left-width\", \"border-right\", \"border-right-color\",\n    \"border-right-style\", \"border-right-width\", \"border-spacing\",\n    \"border-style\", \"border-top\", \"border-top-color\", \"border-top-style\",\n    \"border-top-width\", \"border-width\", bottom, br, brown, browser,\n    burlywood, button, bytesToUIString, c, cadetblue, call, callee, caller,\n    canvas, cap, caption, \"caption-side\", cases, center, charAt, charCodeAt,\n    character, chartreuse, chocolate, chooseColor, chooseFile, chooseFolder,\n    cite, clear, clearInterval, clearTimeout, clip, close, closeWidget,\n    closed, closure, cm, code, col, colgroup, color, command, comment,\n    condition, confirm, console, constructor, content, convertPathToHFS,\n    convertPathToPlatform, coral, cornflowerblue, cornsilk,\n    \"counter-increment\", \"counter-reset\", create, crimson, css, cursor,\n    cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen,\n    darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred,\n    darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise,\n    darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent,\n    deeppink, deepskyblue, defaultStatus, defineClass, del, deserialize,\n    details, devel, dfn, dialog, dimension, dimgray, dir, direction,\n    display, div, dl, document, dodgerblue, dt, edition, else, em, embed,\n    empty, \"empty-cells\", encodeURI, encodeURIComponent, entityify, eqeqeq,\n    errors, es5, escape, eval, event, evidence, evil, ex, exception, exec, exps,\n    fieldset, figure, filesystem, firebrick, first, float, floor,\n    floralwhite, focus, focusWidget, font, \"font-face\", \"font-family\",\n    \"font-size\", \"font-size-adjust\", \"font-stretch\", \"font-style\",\n    \"font-variant\", \"font-weight\", footer, forestgreen, forin, form,\n    fragment, frame, frames, frameset, from, fromCharCode, fuchsia, fud,\n    funct, function, functions, g, gainsboro, gc, getComputedStyle,\n    ghostwhite, global, globals, gold, goldenrod, gray, green, greenyellow,\n    h1, h2, h3, h4, h5, h6, hasOwnProperty, head, header, height, help,\n    hgroup, history, honeydew, hotpink, hr, 'hta:application', html,\n    i, iTunes, id, identifier,\n    iframe, img, immed, implieds, in, include, indent, indexOf, indianred,\n    indigo, init, input, ins, isAlpha, isApplicationRunning, isDigit,\n    isFinite, isNaN, ivory, join, jslint, json, kbd, keygen, khaki,\n    konfabulatorVersion, label, labelled, lang, last, lavender,\n    lavenderblush, lawngreen, laxbreak, lbp, led, left, legend,\n    lemonchiffon, length, \"letter-spacing\", li, lib, lightblue, lightcoral,\n    lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon,\n    lightseagreen, lightskyblue, lightslategray, lightsteelblue,\n    lightyellow, lime, limegreen, line, \"line-height\", linen, link,\n    \"list-style\", \"list-style-image\", \"list-style-position\",\n    \"list-style-type\", load, loadClass, location, log, m, magenta, map,\n    margin, \"margin-bottom\", \"margin-left\", \"margin-right\", \"margin-top\",\n    mark, \"marker-offset\", maroon, match, \"max-height\", \"max-width\", maxerr,\n    maxlen, md5, media, mediumaquamarine, mediumblue, mediumorchid,\n    mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen,\n    mediumturquoise, mediumvioletred, member, menu, message, meta, meter,\n    midnightblue, \"min-height\", \"min-width\", mintcream, mistyrose, mm,\n    moccasin, moveBy, moveTo, name, nav, navajowhite, navigator, navy, new,\n    newcap, noframes, nomen, noscript, nud, object, ol, oldlace, olive,\n    olivedrab, on, onbeforeunload, onblur, onerror, onevar, onfocus, onload,\n    onresize, onunload, opacity, open, openURL, opener, opera, optgroup,\n    option, orange, orangered, orchid, outer, outline, \"outline-color\",\n    \"outline-style\", \"outline-width\", output, overflow, \"overflow-x\",\n    \"overflow-y\", p, padding, \"padding-bottom\", \"padding-left\",\n    \"padding-right\", \"padding-top\", page, \"page-break-after\",\n    \"page-break-before\", palegoldenrod, palegreen, paleturquoise,\n    palevioletred, papayawhip, param, parent, parseFloat, parseInt,\n    passfail, pc, peachpuff, peru, pink, play, plum, plusplus, pop,\n    popupMenu, position, powderblue, pre, predef, preferenceGroups,\n    preferences, print, progress, prompt, prototype, pt, purple, push, px,\n    q, quit, quotes, random, range, raw, reach, readFile, readUrl, reason,\n    red, regexp, reloadWidget, removeEventListener, replace, report,\n    reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, rhino, right,\n    rosybrown, royalblue, rp, rt, ruby, runCommand, runCommandInBg,\n    saddlebrown, safe, salmon, samp, sandybrown, saveAs, savePreferences,\n    screen, script, scroll, scrollBy, scrollTo, seagreen, seal, search,\n    seashell, section, select, serialize, setInterval, setTimeout, shift,\n    showWidgetPreferences, sienna, silver, skyblue, slateblue, slategray,\n    sleep, slice, small, snow, sort, source, span, spawn, speak, split,\n    springgreen, src, stack, statement, status, steelblue, strict, strong,\n    style, styleproperty, sub, substr, sup, supplant, suppressUpdates, sync,\n    system, table, \"table-layout\", tan, tbody, td, teal, tellWidget, test,\n    \"text-align\", \"text-decoration\", \"text-indent\", \"text-shadow\",\n    \"text-transform\", textarea, tfoot, th, thead, thistle, time, title,\n    toLowerCase, toString, toUpperCase, toint32, token, tomato, top, tr, tt,\n    turquoise, type, u, ul, undef, unescape, \"unicode-bidi\", unused,\n    unwatch, updateNow, urls, value, valueOf, var, version,\n    \"vertical-align\", video, violet, visibility, watch, wheat, white,\n    \"white-space\", whitesmoke, widget, width, windows, \"word-spacing\",\n    \"word-wrap\", yahooCheckLogin, yahooLogin, yahooLogout, yellow,\n    yellowgreen, \"z-index\"\n*/\n\n// We build the application inside a function so that we produce only a single\n// global variable. The function will be invoked, its return value is the JSLINT\n// application itself.\n\n\"use strict\";\n\nvar JSLINT = (function () {\n    var adsafe_id,      // The widget's ADsafe id.\n        adsafe_may,     // The widget may load approved scripts.\n        adsafe_went,    // ADSAFE.go has been called.\n        anonname,       // The guessed name for anonymous functions.\n        approved,       // ADsafe approved urls.\n\n        atrule = {\n            media      : true,\n            'font-face': true,\n            page       : true\n        },\n\n// These are operators that should not be used with the ! operator.\n\n        bang = {\n            '<': true,\n            '<=': true,\n            '==': true,\n            '===': true,\n            '!==': true,\n            '!=': true,\n            '>': true,\n            '>=': true,\n            '+': true,\n            '-': true,\n            '*': true,\n            '/': true,\n            '%': true\n        },\n\n// These are members that should not be permitted in the safe subset.\n\n        banned = {              // the member names that ADsafe prohibits.\n            'arguments'     : true,\n            callee          : true,\n            caller          : true,\n            constructor     : true,\n            'eval'          : true,\n            prototype       : true,\n            stack           : true,\n            unwatch         : true,\n            valueOf         : true,\n            watch           : true\n        },\n\n\n// These are the JSLint boolean options.\n\n        boolOptions = {\n            adsafe     : true, // if ADsafe should be enforced\n            bitwise    : true, // if bitwise operators should not be allowed\n            browser    : true, // if the standard browser globals should be predefined\n            cap        : true, // if upper case HTML should be allowed\n            css        : true, // if CSS workarounds should be tolerated\n            debug      : true, // if debugger statements should be allowed\n            devel      : true, // if logging should be allowed (console, alert, etc.)\n            eqeqeq     : true, // if === should be required\n            es5        : true, // if ES5 syntax should be allowed\n            evil       : true, // if eval should be allowed\n            forin      : true, // if for in statements must filter\n            fragment   : true, // if HTML fragments should be allowed\n            immed      : true, // if immediate invocations must be wrapped in parens\n            laxbreak   : true, // if line breaks should not be checked\n            newcap     : true, // if constructor names must be capitalized\n            nomen      : true, // if names should be checked\n            on         : true, // if HTML event handlers should be allowed\n            onevar     : true, // if only one var statement per function should be allowed\n            passfail   : true, // if the scan should stop on first error\n            plusplus   : true, // if increment/decrement should not be allowed\n            regexp     : true, // if the . should not be allowed in regexp literals\n            rhino      : true, // if the Rhino environment globals should be predefined\n            undef      : true, // if variables should be declared before used\n            safe       : true, // if use of some browser features should be restricted\n            windows    : true, // if MS Windows-specigic globals should be predefined\n            strict     : true, // require the \"use strict\"; pragma\n            sub        : true, // if all forms of subscript notation are tolerated\n            white      : true, // if strict whitespace rules apply\n            widget     : true  // if the Yahoo Widgets globals should be predefined\n        },\n\n// browser contains a set of global names which are commonly provided by a\n// web browser environment.\n\n        browser = {\n            addEventListener: false,\n            blur            : false,\n            clearInterval   : false,\n            clearTimeout    : false,\n            close           : false,\n            closed          : false,\n            defaultStatus   : false,\n            document        : false,\n            event           : false,\n            focus           : false,\n            frames          : false,\n            getComputedStyle: false,\n            history         : false,\n            Image           : false,\n            length          : false,\n            location        : false,\n            moveBy          : false,\n            moveTo          : false,\n            name            : false,\n            navigator       : false,\n            onbeforeunload  : true,\n            onblur          : true,\n            onerror         : true,\n            onfocus         : true,\n            onload          : true,\n            onresize        : true,\n            onunload        : true,\n            open            : false,\n            opener          : false,\n            Option          : false,\n            parent          : false,\n            print           : false,\n            removeEventListener: false,\n            resizeBy        : false,\n            resizeTo        : false,\n            screen          : false,\n            scroll          : false,\n            scrollBy        : false,\n            scrollTo        : false,\n            setInterval     : false,\n            setTimeout      : false,\n            status          : false,\n            top             : false,\n            XMLHttpRequest  : false\n        },\n\n        cssAttributeData,\n        cssAny,\n\n        cssColorData = {\n            \"aliceblue\"             : true,\n            \"antiquewhite\"          : true,\n            \"aqua\"                  : true,\n            \"aquamarine\"            : true,\n            \"azure\"                 : true,\n            \"beige\"                 : true,\n            \"bisque\"                : true,\n            \"black\"                 : true,\n            \"blanchedalmond\"        : true,\n            \"blue\"                  : true,\n            \"blueviolet\"            : true,\n            \"brown\"                 : true,\n            \"burlywood\"             : true,\n            \"cadetblue\"             : true,\n            \"chartreuse\"            : true,\n            \"chocolate\"             : true,\n            \"coral\"                 : true,\n            \"cornflowerblue\"        : true,\n            \"cornsilk\"              : true,\n            \"crimson\"               : true,\n            \"cyan\"                  : true,\n            \"darkblue\"              : true,\n            \"darkcyan\"              : true,\n            \"darkgoldenrod\"         : true,\n            \"darkgray\"              : true,\n            \"darkgreen\"             : true,\n            \"darkkhaki\"             : true,\n            \"darkmagenta\"           : true,\n            \"darkolivegreen\"        : true,\n            \"darkorange\"            : true,\n            \"darkorchid\"            : true,\n            \"darkred\"               : true,\n            \"darksalmon\"            : true,\n            \"darkseagreen\"          : true,\n            \"darkslateblue\"         : true,\n            \"darkslategray\"         : true,\n            \"darkturquoise\"         : true,\n            \"darkviolet\"            : true,\n            \"deeppink\"              : true,\n            \"deepskyblue\"           : true,\n            \"dimgray\"               : true,\n            \"dodgerblue\"            : true,\n            \"firebrick\"             : true,\n            \"floralwhite\"           : true,\n            \"forestgreen\"           : true,\n            \"fuchsia\"               : true,\n            \"gainsboro\"             : true,\n            \"ghostwhite\"            : true,\n            \"gold\"                  : true,\n            \"goldenrod\"             : true,\n            \"gray\"                  : true,\n            \"green\"                 : true,\n            \"greenyellow\"           : true,\n            \"honeydew\"              : true,\n            \"hotpink\"               : true,\n            \"indianred\"             : true,\n            \"indigo\"                : true,\n            \"ivory\"                 : true,\n            \"khaki\"                 : true,\n            \"lavender\"              : true,\n            \"lavenderblush\"         : true,\n            \"lawngreen\"             : true,\n            \"lemonchiffon\"          : true,\n            \"lightblue\"             : true,\n            \"lightcoral\"            : true,\n            \"lightcyan\"             : true,\n            \"lightgoldenrodyellow\"  : true,\n            \"lightgreen\"            : true,\n            \"lightpink\"             : true,\n            \"lightsalmon\"           : true,\n            \"lightseagreen\"         : true,\n            \"lightskyblue\"          : true,\n            \"lightslategray\"        : true,\n            \"lightsteelblue\"        : true,\n            \"lightyellow\"           : true,\n            \"lime\"                  : true,\n            \"limegreen\"             : true,\n            \"linen\"                 : true,\n            \"magenta\"               : true,\n            \"maroon\"                : true,\n            \"mediumaquamarine\"      : true,\n            \"mediumblue\"            : true,\n            \"mediumorchid\"          : true,\n            \"mediumpurple\"          : true,\n            \"mediumseagreen\"        : true,\n            \"mediumslateblue\"       : true,\n            \"mediumspringgreen\"     : true,\n            \"mediumturquoise\"       : true,\n            \"mediumvioletred\"       : true,\n            \"midnightblue\"          : true,\n            \"mintcream\"             : true,\n            \"mistyrose\"             : true,\n            \"moccasin\"              : true,\n            \"navajowhite\"           : true,\n            \"navy\"                  : true,\n            \"oldlace\"               : true,\n            \"olive\"                 : true,\n            \"olivedrab\"             : true,\n            \"orange\"                : true,\n            \"orangered\"             : true,\n            \"orchid\"                : true,\n            \"palegoldenrod\"         : true,\n            \"palegreen\"             : true,\n            \"paleturquoise\"         : true,\n            \"palevioletred\"         : true,\n            \"papayawhip\"            : true,\n            \"peachpuff\"             : true,\n            \"peru\"                  : true,\n            \"pink\"                  : true,\n            \"plum\"                  : true,\n            \"powderblue\"            : true,\n            \"purple\"                : true,\n            \"red\"                   : true,\n            \"rosybrown\"             : true,\n            \"royalblue\"             : true,\n            \"saddlebrown\"           : true,\n            \"salmon\"                : true,\n            \"sandybrown\"            : true,\n            \"seagreen\"              : true,\n            \"seashell\"              : true,\n            \"sienna\"                : true,\n            \"silver\"                : true,\n            \"skyblue\"               : true,\n            \"slateblue\"             : true,\n            \"slategray\"             : true,\n            \"snow\"                  : true,\n            \"springgreen\"           : true,\n            \"steelblue\"             : true,\n            \"tan\"                   : true,\n            \"teal\"                  : true,\n            \"thistle\"               : true,\n            \"tomato\"                : true,\n            \"turquoise\"             : true,\n            \"violet\"                : true,\n            \"wheat\"                 : true,\n            \"white\"                 : true,\n            \"whitesmoke\"            : true,\n            \"yellow\"                : true,\n            \"yellowgreen\"           : true\n        },\n\n        cssBorderStyle,\n        cssBreak,\n\n        cssLengthData = {\n            '%': true,\n            'cm': true,\n            'em': true,\n            'ex': true,\n            'in': true,\n            'mm': true,\n            'pc': true,\n            'pt': true,\n            'px': true\n        },\n\n        cssOverflow,\n\n        devel = {\n            alert           : false,\n            confirm         : false,\n            console         : false,\n            Debug           : false,\n            opera           : false,\n            prompt          : false\n        },\n\n        escapes = {\n            '\\b': '\\\\b',\n            '\\t': '\\\\t',\n            '\\n': '\\\\n',\n            '\\f': '\\\\f',\n            '\\r': '\\\\r',\n            '\"' : '\\\\\"',\n            '/' : '\\\\/',\n            '\\\\': '\\\\\\\\'\n        },\n\n        funct,          // The current function\n\n        functionicity = [\n            'closure', 'exception', 'global', 'label',\n            'outer', 'unused', 'var'\n        ],\n\n        functions,      // All of the functions\n\n        global,         // The global scope\n        htmltag = {\n            a:        {},\n            abbr:     {},\n            acronym:  {},\n            address:  {},\n            applet:   {},\n            area:     {empty: true, parent: ' map '},\n            article:  {},\n            aside:    {},\n            audio:    {},\n            b:        {},\n            base:     {empty: true, parent: ' head '},\n            bdo:      {},\n            big:      {},\n            blockquote: {},\n            body:     {parent: ' html noframes '},\n            br:       {empty: true},\n            button:   {},\n            canvas:   {parent: ' body p div th td '},\n            caption:  {parent: ' table '},\n            center:   {},\n            cite:     {},\n            code:     {},\n            col:      {empty: true, parent: ' table colgroup '},\n            colgroup: {parent: ' table '},\n            command:  {parent: ' menu '},\n            datalist: {},\n            dd:       {parent: ' dl '},\n            del:      {},\n            details:  {},\n            dialog:   {},\n            dfn:      {},\n            dir:      {},\n            div:      {},\n            dl:       {},\n            dt:       {parent: ' dl '},\n            em:       {},\n            embed:    {},\n            fieldset: {},\n            figure:   {},\n            font:     {},\n            footer:   {},\n            form:     {},\n            frame:    {empty: true, parent: ' frameset '},\n            frameset: {parent: ' html frameset '},\n            h1:       {},\n            h2:       {},\n            h3:       {},\n            h4:       {},\n            h5:       {},\n            h6:       {},\n            head:     {parent: ' html '},\n            header:   {},\n            hgroup:   {},\n            hr:       {empty: true},\n            'hta:application':\n                      {empty: true, parent: ' head '},\n            html:     {parent: '*'},\n            i:        {},\n            iframe:   {},\n            img:      {empty: true},\n            input:    {empty: true},\n            ins:      {},\n            kbd:      {},\n            keygen:   {},\n            label:    {},\n            legend:   {parent: ' details fieldset figure '},\n            li:       {parent: ' dir menu ol ul '},\n            link:     {empty: true, parent: ' head '},\n            map:      {},\n            mark:     {},\n            menu:     {},\n            meta:     {empty: true, parent: ' head noframes noscript '},\n            meter:    {},\n            nav:      {},\n            noframes: {parent: ' html body '},\n            noscript: {parent: ' body head noframes '},\n            object:   {},\n            ol:       {},\n            optgroup: {parent: ' select '},\n            option:   {parent: ' optgroup select '},\n            output:   {},\n            p:        {},\n            param:    {empty: true, parent: ' applet object '},\n            pre:      {},\n            progress: {},\n            q:        {},\n            rp:       {},\n            rt:       {},\n            ruby:     {},\n            samp:     {},\n            script:   {empty: true, parent: ' body div frame head iframe p pre span '},\n            section:  {},\n            select:   {},\n            small:    {},\n            span:     {},\n            source:   {},\n            strong:   {},\n            style:    {parent: ' head ', empty: true},\n            sub:      {},\n            sup:      {},\n            table:    {},\n            tbody:    {parent: ' table '},\n            td:       {parent: ' tr '},\n            textarea: {},\n            tfoot:    {parent: ' table '},\n            th:       {parent: ' tr '},\n            thead:    {parent: ' table '},\n            time:     {},\n            title:    {parent: ' head '},\n            tr:       {parent: ' table tbody thead tfoot '},\n            tt:       {},\n            u:        {},\n            ul:       {},\n            'var':    {},\n            video:    {}\n        },\n\n        ids,            // HTML ids\n        implied,        // Implied globals\n        inblock,\n        indent,\n        jsonmode,\n        lines,\n        lookahead,\n        member,\n        membersOnly,\n        nexttoken,\n        noreach,\n        option,\n        predefined,     // Global variables defined by option\n        prereg,\n        prevtoken,\n\n        rhino = {\n            defineClass : false,\n            deserialize : false,\n            gc          : false,\n            help        : false,\n            load        : false,\n            loadClass   : false,\n            print       : false,\n            quit        : false,\n            readFile    : false,\n            readUrl     : false,\n            runCommand  : false,\n            seal        : false,\n            serialize   : false,\n            spawn       : false,\n            sync        : false,\n            toint32     : false,\n            version     : false\n        },\n\n        scope,      // The current scope\n\n        windows = {\n            ActiveXObject: false,\n            CScript      : false,\n            Debug        : false,\n            Enumerator   : false,\n            System       : false,\n            VBArray      : false,\n            WScript      : false\n        },\n\n        src,\n        stack,\n\n// standard contains the global names that are provided by the\n// ECMAScript standard.\n\n        standard = {\n            Array               : false,\n            Boolean             : false,\n            Date                : false,\n            decodeURI           : false,\n            decodeURIComponent  : false,\n            encodeURI           : false,\n            encodeURIComponent  : false,\n            Error               : false,\n            'eval'              : false,\n            EvalError           : false,\n            Function            : false,\n            hasOwnProperty      : false,\n            isFinite            : false,\n            isNaN               : false,\n            JSON                : false,\n            Math                : false,\n            Number              : false,\n            Object              : false,\n            parseInt            : false,\n            parseFloat          : false,\n            RangeError          : false,\n            ReferenceError      : false,\n            RegExp              : false,\n            String              : false,\n            SyntaxError         : false,\n            TypeError           : false,\n            URIError            : false\n        },\n\n        standard_member = {\n            E                   : true,\n            LN2                 : true,\n            LN10                : true,\n            LOG2E               : true,\n            LOG10E              : true,\n            PI                  : true,\n            SQRT1_2             : true,\n            SQRT2               : true,\n            MAX_VALUE           : true,\n            MIN_VALUE           : true,\n            NEGATIVE_INFINITY   : true,\n            POSITIVE_INFINITY   : true\n        },\n\n        strict_mode,\n        syntax = {},\n        tab,\n        token,\n        urls,\n        warnings,\n\n// widget contains the global names which are provided to a Yahoo\n// (fna Konfabulator) widget.\n\n        widget = {\n            alert                   : true,\n            animator                : true,\n            appleScript             : true,\n            beep                    : true,\n            bytesToUIString         : true,\n            Canvas                  : true,\n            chooseColor             : true,\n            chooseFile              : true,\n            chooseFolder            : true,\n            closeWidget             : true,\n            COM                     : true,\n            convertPathToHFS        : true,\n            convertPathToPlatform   : true,\n            CustomAnimation         : true,\n            escape                  : true,\n            FadeAnimation           : true,\n            filesystem              : true,\n            Flash                   : true,\n            focusWidget             : true,\n            form                    : true,\n            FormField               : true,\n            Frame                   : true,\n            HotKey                  : true,\n            Image                   : true,\n            include                 : true,\n            isApplicationRunning    : true,\n            iTunes                  : true,\n            konfabulatorVersion     : true,\n            log                     : true,\n            md5                     : true,\n            MenuItem                : true,\n            MoveAnimation           : true,\n            openURL                 : true,\n            play                    : true,\n            Point                   : true,\n            popupMenu               : true,\n            preferenceGroups        : true,\n            preferences             : true,\n            print                   : true,\n            prompt                  : true,\n            random                  : true,\n            Rectangle               : true,\n            reloadWidget            : true,\n            ResizeAnimation         : true,\n            resolvePath             : true,\n            resumeUpdates           : true,\n            RotateAnimation         : true,\n            runCommand              : true,\n            runCommandInBg          : true,\n            saveAs                  : true,\n            savePreferences         : true,\n            screen                  : true,\n            ScrollBar               : true,\n            showWidgetPreferences   : true,\n            sleep                   : true,\n            speak                   : true,\n            Style                   : true,\n            suppressUpdates         : true,\n            system                  : true,\n            tellWidget              : true,\n            Text                    : true,\n            TextArea                : true,\n            Timer                   : true,\n            unescape                : true,\n            updateNow               : true,\n            URL                     : true,\n            Web                     : true,\n            widget                  : true,\n            Window                  : true,\n            XMLDOM                  : true,\n            XMLHttpRequest          : true,\n            yahooCheckLogin         : true,\n            yahooLogin              : true,\n            yahooLogout             : true\n        },\n\n//  xmode is used to adapt to the exceptions in html parsing.\n//  It can have these states:\n//      false   .js script file\n//      html\n//      outer\n//      script\n//      style\n//      scriptstring\n//      styleproperty\n\n        xmode,\n        xquote,\n\n// unsafe comment or string\n        ax = /@cc|<\\/?|script|\\]*s\\]|<\\s*!|&lt/i,\n// unsafe characters that are silently deleted by one or more browsers\n        cx = /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/,\n// token\n        tx = /^\\s*([(){}\\[.,:;'\"~\\?\\]#@]|==?=?|\\/(\\*(jslint|members?|global)?|=|\\/)?|\\*[\\/=]?|\\+(?:=|\\++)?|-(?:=|-+)?|%=?|&[&=]?|\\|[|=]?|>>?>?=?|<([\\/=!]|\\!(\\[|--)?|<=?)?|\\^=?|\\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\\.[0-9]*)?([eE][+\\-]?[0-9]+)?)/,\n// html token\n        hx = /^\\s*(['\"=>\\/&#]|<(?:\\/|\\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\\-:]*|[0-9]+|--)/,\n// characters in strings that need escapement\n        nx = /[\\u0000-\\u001f&<\"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/,\n        nxg = /[\\u0000-\\u001f&<\"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n// outer html token\n        ox = /[>&]|<[\\/!]?|--/,\n// star slash\n        lx = /\\*\\/|\\/\\*/,\n// identifier\n        ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,\n// javascript url\n        jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\\s*:/i,\n// url badness\n        ux = /&|\\+|\\u00AD|\\.\\.|\\/\\*|%[^;]|base64|url|expression|data|mailto/i,\n// style\n        sx = /^\\s*([{:#%.=,>+\\[\\]@()\"';]|\\*=?|\\$=|\\|=|\\^=|~=|[a-zA-Z_][a-zA-Z0-9_\\-]*|[0-9]+|<\\/|\\/\\*)/,\n        ssx = /^\\s*([@#!\"'};:\\-%.=,+\\[\\]()*_]|[a-zA-Z][a-zA-Z0-9._\\-]*|\\/\\*?|\\d+(?:\\.\\d+)?|<\\/)/,\n// attributes characters\n        qx = /[^a-zA-Z0-9+\\-_\\/ ]/,\n// query characters for ids\n        dx = /[\\[\\]\\/\\\\\"'*<>.&:(){}+=#]/,\n\n        rx = {\n            outer: hx,\n            html: hx,\n            style: sx,\n            styleproperty: ssx\n        };\n\n    function F() {}\n\n    if (typeof Object.create !== 'function') {\n        Object.create = function (o) {\n            F.prototype = o;\n            return new F();\n        };\n    }\n\n\n    function is_own(object, name) {\n        return Object.prototype.hasOwnProperty.call(object, name);\n    }\n\n\n    function combine(t, o) {\n        var n;\n        for (n in o) {\n            if (is_own(o, n)) {\n                t[n] = o[n];\n            }\n        }\n    }\n\n    String.prototype.entityify = function () {\n        return this\n            .replace(/&/g, '&amp;')\n            .replace(/</g, '&lt;')\n            .replace(/>/g, '&gt;');\n    };\n\n    String.prototype.isAlpha = function () {\n        return (this >= 'a' && this <= 'z\\uffff') ||\n            (this >= 'A' && this <= 'Z\\uffff');\n    };\n\n\n    String.prototype.isDigit = function () {\n        return (this >= '0' && this <= '9');\n    };\n\n\n    String.prototype.supplant = function (o) {\n        return this.replace(/\\{([^{}]*)\\}/g, function (a, b) {\n            var r = o[b];\n            return typeof r === 'string' || typeof r === 'number' ? r : a;\n        });\n    };\n\n    String.prototype.name = function () {\n\n// If the string looks like an identifier, then we can return it as is.\n// If the string contains no control characters, no quote characters, and no\n// backslash characters, then we can simply slap some quotes around it.\n// Otherwise we must also replace the offending characters with safe\n// sequences.\n\n        if (ix.test(this)) {\n            return this;\n        }\n        if (nx.test(this)) {\n            return '\"' + this.replace(nxg, function (a) {\n                var c = escapes[a];\n                if (c) {\n                    return c;\n                }\n                return '\\\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);\n            }) + '\"';\n        }\n        return '\"' + this + '\"';\n    };\n\n\n    function assume() {\n        if (!option.safe) {\n            if (option.rhino) {\n                combine(predefined, rhino);\n            }\n            if (option.devel) {\n                combine(predefined, devel);\n            }\n            if (option.browser) {\n                combine(predefined, browser);\n            }\n            if (option.windows) {\n                combine(predefined, windows);\n            }\n            if (option.widget) {\n                combine(predefined, widget);\n            }\n        }\n    }\n\n\n// Produce an error warning.\n\n    function quit(m, l, ch) {\n        throw {\n            name: 'JSLintError',\n            line: l,\n            character: ch,\n            message: m + \" (\" + Math.floor((l / lines.length) * 100) +\n                    \"% scanned).\"\n        };\n    }\n\n    function warning(m, t, a, b, c, d) {\n        var ch, l, w;\n        t = t || nexttoken;\n        if (t.id === '(end)') {  // `~\n            t = token;\n        }\n        l = t.line || 0;\n        ch = t.from || 0;\n        w = {\n            id: '(error)',\n            raw: m,\n            evidence: lines[l - 1] || '',\n            line: l,\n            character: ch,\n            a: a,\n            b: b,\n            c: c,\n            d: d\n        };\n        w.reason = m.supplant(w);\n        JSLINT.errors.push(w);\n        if (option.passfail) {\n            quit('Stopping. ', l, ch);\n        }\n        warnings += 1;\n        if (warnings >= option.maxerr) {\n            quit(\"Too many errors.\", l, ch);\n        }\n        return w;\n    }\n\n    function warningAt(m, l, ch, a, b, c, d) {\n        return warning(m, {\n            line: l,\n            from: ch\n        }, a, b, c, d);\n    }\n\n    function error(m, t, a, b, c, d) {\n        var w = warning(m, t, a, b, c, d);\n        quit(\"Stopping, unable to continue.\", w.line, w.character);\n    }\n\n    function errorAt(m, l, ch, a, b, c, d) {\n        return error(m, {\n            line: l,\n            from: ch\n        }, a, b, c, d);\n    }\n\n\n\n// lexical analysis\n\n    var lex = (function lex() {\n        var character, from, line, s;\n\n// Private lex methods\n\n        function nextLine() {\n            var at;\n            if (line >= lines.length) {\n                return false;\n            }\n            character = 1;\n            s = lines[line];\n            line += 1;\n            at = s.search(/ \\t/);\n            if (at >= 0) {\n                warningAt(\"Mixed spaces and tabs.\", line, at + 1);\n            }\n            s = s.replace(/\\t/g, tab);\n            at = s.search(cx);\n            if (at >= 0) {\n                warningAt(\"Unsafe character.\", line, at);\n            }\n            if (option.maxlen && option.maxlen < s.length) {\n                warningAt(\"Line too long.\", line, s.length);\n            }\n            return true;\n        }\n\n// Produce a token object.  The token inherits from a syntax symbol.\n\n        function it(type, value) {\n            var i, t;\n            if (type === '(color)' || type === '(range)') {\n                t = {type: type};\n            } else if (type === '(punctuator)' ||\n                    (type === '(identifier)' && is_own(syntax, value))) {\n                t = syntax[value] || syntax['(error)'];\n            } else {\n                t = syntax[type];\n            }\n            t = Object.create(t);\n            if (type === '(string)' || type === '(range)') {\n                if (jx.test(value)) {\n                    warningAt(\"Script URL.\", line, from);\n                }\n            }\n            if (type === '(identifier)') {\n                t.identifier = true;\n                if (value === '__iterator__' || value === '__proto__') {\n                    errorAt(\"Reserved name '{a}'.\",\n                        line, from, value);\n                } else if (option.nomen &&\n                        (value.charAt(0) === '_' ||\n                         value.charAt(value.length - 1) === '_')) {\n                    warningAt(\"Unexpected {a} in '{b}'.\", line, from,\n                        \"dangling '_'\", value);\n                }\n            }\n            t.value = value;\n            t.line = line;\n            t.character = character;\n            t.from = from;\n            i = t.id;\n            if (i !== '(endline)') {\n                prereg = i &&\n                    (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) ||\n                    i === 'return');\n            }\n            return t;\n        }\n\n// Public lex methods\n\n        return {\n            init: function (source) {\n                if (typeof source === 'string') {\n                    lines = source\n                        .replace(/\\r\\n/g, '\\n')\n                        .replace(/\\r/g, '\\n')\n                        .split('\\n');\n                } else {\n                    lines = source;\n                }\n                line = 0;\n                nextLine();\n                from = 1;\n            },\n\n            range: function (begin, end) {\n                var c, value = '';\n                from = character;\n                if (s.charAt(0) !== begin) {\n                    errorAt(\"Expected '{a}' and instead saw '{b}'.\",\n                            line, character, begin, s.charAt(0));\n                }\n                for (;;) {\n                    s = s.slice(1);\n                    character += 1;\n                    c = s.charAt(0);\n                    switch (c) {\n                    case '':\n                        errorAt(\"Missing '{a}'.\", line, character, c);\n                        break;\n                    case end:\n                        s = s.slice(1);\n                        character += 1;\n                        return it('(range)', value);\n                    case xquote:\n                    case '\\\\':\n                        warningAt(\"Unexpected '{a}'.\", line, character, c);\n                    }\n                    value += c;\n                }\n\n            },\n\n// token -- this is called by advance to get the next token.\n\n            token: function () {\n                var b, c, captures, d, depth, high, i, l, low, q, t;\n\n                function match(x) {\n                    var r = x.exec(s), r1;\n                    if (r) {\n                        l = r[0].length;\n                        r1 = r[1];\n                        c = r1.charAt(0);\n                        s = s.substr(l);\n                        from = character + l - r1.length;\n                        character += l;\n                        return r1;\n                    }\n                }\n\n                function string(x) {\n                    var c, j, r = '';\n\n                    if (jsonmode && x !== '\"') {\n                        warningAt(\"Strings must use doublequote.\",\n                                line, character);\n                    }\n\n                    if (xquote === x || (xmode === 'scriptstring' && !xquote)) {\n                        return it('(punctuator)', x);\n                    }\n\n                    function esc(n) {\n                        var i = parseInt(s.substr(j + 1, n), 16);\n                        j += n;\n                        if (i >= 32 && i <= 126 &&\n                                i !== 34 && i !== 92 && i !== 39) {\n                            warningAt(\"Unnecessary escapement.\", line, character);\n                        }\n                        character += n;\n                        c = String.fromCharCode(i);\n                    }\n                    j = 0;\n                    for (;;) {\n                        while (j >= s.length) {\n                            j = 0;\n                            if (xmode !== 'html' || !nextLine()) {\n                                errorAt(\"Unclosed string.\", line, from);\n                            }\n                        }\n                        c = s.charAt(j);\n                        if (c === x) {\n                            character += 1;\n                            s = s.substr(j + 1);\n                            return it('(string)', r, x);\n                        }\n                        if (c < ' ') {\n                            if (c === '\\n' || c === '\\r') {\n                                break;\n                            }\n                            warningAt(\"Control character in string: {a}.\",\n                                    line, character + j, s.slice(0, j));\n                        } else if (c === xquote) {\n                            warningAt(\"Bad HTML string\", line, character + j);\n                        } else if (c === '<') {\n                            if (option.safe && xmode === 'html') {\n                                warningAt(\"ADsafe string violation.\",\n                                        line, character + j);\n                            } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) {\n                                warningAt(\"Expected '<\\\\/' and instead saw '</'.\", line, character);\n                            } else if (s.charAt(j + 1) === '!' && (xmode || option.safe)) {\n                                warningAt(\"Unexpected '<!' in a string.\", line, character);\n                            }\n                        } else if (c === '\\\\') {\n                            if (xmode === 'html') {\n                                if (option.safe) {\n                                    warningAt(\"ADsafe string violation.\",\n                                            line, character + j);\n                                }\n                            } else if (xmode === 'styleproperty') {\n                                j += 1;\n                                character += 1;\n                                c = s.charAt(j);\n                                if (c !== x) {\n                                    warningAt(\"Escapement in style string.\",\n                                            line, character + j);\n                                }\n                            } else {\n                                j += 1;\n                                character += 1;\n                                c = s.charAt(j);\n                                switch (c) {\n                                case xquote:\n                                    warningAt(\"Bad HTML string\", line,\n                                        character + j);\n                                    break;\n                                case '\\\\':\n                                case '\\'':\n                                case '\"':\n                                case '/':\n                                    break;\n                                case 'b':\n                                    c = '\\b';\n                                    break;\n                                case 'f':\n                                    c = '\\f';\n                                    break;\n                                case 'n':\n                                    c = '\\n';\n                                    break;\n                                case 'r':\n                                    c = '\\r';\n                                    break;\n                                case 't':\n                                    c = '\\t';\n                                    break;\n                                case 'u':\n                                    esc(4);\n                                    break;\n                                case 'v':\n                                    c = '\\v';\n                                    break;\n                                case 'x':\n                                    if (jsonmode) {\n                                        warningAt(\"Avoid \\\\x-.\", line, character);\n                                    }\n                                    esc(2);\n                                    break;\n                                default:\n                                    warningAt(\"Bad escapement.\", line, character);\n                                }\n                            }\n                        }\n                        r += c;\n                        character += 1;\n                        j += 1;\n                    }\n                }\n\n                for (;;) {\n                    if (!s) {\n                        return it(nextLine() ? '(endline)' : '(end)', '');\n                    }\n                    while (xmode === 'outer') {\n                        i = s.search(ox);\n                        if (i === 0) {\n                            break;\n                        } else if (i > 0) {\n                            character += 1;\n                            s = s.slice(i);\n                            break;\n                        } else {\n                            if (!nextLine()) {\n                                return it('(end)', '');\n                            }\n                        }\n                    }\n//                     t = match(rx[xmode] || tx);\n//                     if (!t) {\n//                         if (xmode === 'html') {\n//                             return it('(error)', s.charAt(0));\n//                         } else {\n//                             t = '';\n//                             c = '';\n//                             while (s && s < '!') {\n//                                 s = s.substr(1);\n//                             }\n//                             if (s) {\n//                                 errorAt(\"Unexpected '{a}'.\",\n//                                         line, character, s.substr(0, 1));\n//                             }\n//                         }\n                    t = match(rx[xmode] || tx);\n                    if (!t) {\n                        t = '';\n                        c = '';\n                        while (s && s < '!') {\n                            s = s.substr(1);\n                        }\n                        if (s) {\n                            if (xmode === 'html') {\n                                return it('(error)', s.charAt(0));\n                            } else {\n                                errorAt(\"Unexpected '{a}'.\",\n                                        line, character, s.substr(0, 1));\n                            }\n                        }\n                    } else {\n\n    //      identifier\n\n                        if (c.isAlpha() || c === '_' || c === '$') {\n                            return it('(identifier)', t);\n                        }\n\n    //      number\n\n                        if (c.isDigit()) {\n                            if (xmode !== 'style' && !isFinite(Number(t))) {\n                                warningAt(\"Bad number '{a}'.\",\n                                    line, character, t);\n                            }\n                            if (xmode !== 'style' &&\n                                     xmode !== 'styleproperty' &&\n                                     s.substr(0, 1).isAlpha()) {\n                                warningAt(\"Missing space after '{a}'.\",\n                                        line, character, t);\n                            }\n                            if (c === '0') {\n                                d = t.substr(1, 1);\n                                if (d.isDigit()) {\n                                    if (token.id !== '.' && xmode !== 'styleproperty') {\n                                        warningAt(\"Don't use extra leading zeros '{a}'.\",\n                                            line, character, t);\n                                    }\n                                } else if (jsonmode && (d === 'x' || d === 'X')) {\n                                    warningAt(\"Avoid 0x-. '{a}'.\",\n                                            line, character, t);\n                                }\n                            }\n                            if (t.substr(t.length - 1) === '.') {\n                                warningAt(\n        \"A trailing decimal point can be confused with a dot '{a}'.\",\n                                        line, character, t);\n                            }\n                            return it('(number)', t);\n                        }\n                        switch (t) {\n\n    //      string\n\n                        case '\"':\n                        case \"'\":\n                            return string(t);\n\n    //      // comment\n\n                        case '//':\n                            if (src || (xmode && xmode !== 'script')) {\n                                warningAt(\"Unexpected comment.\", line, character);\n                            } else if (xmode === 'script' && /<\\s*\\//i.test(s)) {\n                                warningAt(\"Unexpected <\\/ in comment.\", line, character);\n                            } else if ((option.safe || xmode === 'script') && ax.test(s)) {\n                                warningAt(\"Dangerous comment.\", line, character);\n                            }\n                            s = '';\n                            token.comment = true;\n                            break;\n\n    //      /* comment\n\n                        case '/*':\n                            if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) {\n                                warningAt(\"Unexpected comment.\", line, character);\n                            }\n                            if (option.safe && ax.test(s)) {\n                                warningAt(\"ADsafe comment violation.\", line, character);\n                            }\n                            for (;;) {\n                                i = s.search(lx);\n                                if (i >= 0) {\n                                    break;\n                                }\n                                if (!nextLine()) {\n                                    errorAt(\"Unclosed comment.\", line, character);\n                                } else {\n                                    if (option.safe && ax.test(s)) {\n                                        warningAt(\"ADsafe comment violation.\",\n                                                line, character);\n                                    }\n                                }\n                            }\n                            character += i + 2;\n                            if (s.substr(i, 1) === '/') {\n                                errorAt(\"Nested comment.\", line, character);\n                            }\n                            s = s.substr(i + 2);\n                            token.comment = true;\n                            break;\n\n    //      /*members /*jslint /*global\n\n                        case '/*members':\n                        case '/*member':\n                        case '/*jslint':\n                        case '/*global':\n                        case '*/':\n                            return {\n                                value: t,\n                                type: 'special',\n                                line: line,\n                                character: character,\n                                from: from\n                            };\n\n                        case '':\n                            break;\n    //      /\n                        case '/':\n                            if (token.id === '/=') {\n                                errorAt(\n\"A regular expression literal can be confused with '/='.\", line, from);\n                            }\n                            if (prereg) {\n                                depth = 0;\n                                captures = 0;\n                                l = 0;\n                                for (;;) {\n                                    b = true;\n                                    c = s.charAt(l);\n                                    l += 1;\n                                    switch (c) {\n                                    case '':\n                                        errorAt(\"Unclosed regular expression.\",\n                                                line, from);\n                                        return;\n                                    case '/':\n                                        if (depth > 0) {\n                                            warningAt(\"Unescaped '{a}'.\",\n                                                    line, from + l, '/');\n                                        }\n                                        c = s.substr(0, l - 1);\n                                        q = {\n                                            g: true,\n                                            i: true,\n                                            m: true\n                                        };\n                                        while (q[s.charAt(l)] === true) {\n                                            q[s.charAt(l)] = false;\n                                            l += 1;\n                                        }\n                                        character += l;\n                                        s = s.substr(l);\n                                        q = s.charAt(0);\n                                        if (q === '/' || q === '*') {\n                                            errorAt(\"Confusing regular expression.\",\n                                                    line, from);\n                                        }\n                                        return it('(regexp)', c);\n                                    case '\\\\':\n                                        c = s.charAt(l);\n                                        if (c < ' ') {\n                                            warningAt(\n\"Unexpected control character in regular expression.\", line, from + l);\n                                        } else if (c === '<') {\n                                            warningAt(\n\"Unexpected escaped character '{a}' in regular expression.\", line, from + l, c);\n                                        }\n                                        l += 1;\n                                        break;\n                                    case '(':\n                                        depth += 1;\n                                        b = false;\n                                        if (s.charAt(l) === '?') {\n                                            l += 1;\n                                            switch (s.charAt(l)) {\n                                            case ':':\n                                            case '=':\n                                            case '!':\n                                                l += 1;\n                                                break;\n                                            default:\n                                                warningAt(\n\"Expected '{a}' and instead saw '{b}'.\", line, from + l, ':', s.charAt(l));\n                                            }\n                                        } else {\n                                            captures += 1;\n                                        }\n                                        break;\n                                    case '|':\n                                        b = false;\n                                        break;\n                                    case ')':\n                                        if (depth === 0) {\n                                            warningAt(\"Unescaped '{a}'.\",\n                                                    line, from + l, ')');\n                                        } else {\n                                            depth -= 1;\n                                        }\n                                        break;\n                                    case ' ':\n                                        q = 1;\n                                        while (s.charAt(l) === ' ') {\n                                            l += 1;\n                                            q += 1;\n                                        }\n                                        if (q > 1) {\n                                            warningAt(\n\"Spaces are hard to count. Use {{a}}.\", line, from + l, q);\n                                        }\n                                        break;\n                                    case '[':\n                                        c = s.charAt(l);\n                                        if (c === '^') {\n                                            l += 1;\n                                            if (option.regexp) {\n                                                warningAt(\"Insecure '{a}'.\",\n                                                        line, from + l, c);\n                                            } else if (s.charAt(l) === ']') {\n                                                errorAt(\"Unescaped '{a}'.\",\n                                                    line, from, '^');\n                                            }\n                                        }\n                                        q = false;\n                                        if (c === ']') {\n                                            warningAt(\"Empty class.\", line,\n                                                    from + l - 1);\n                                            q = true;\n                                        }\nklass:                                  do {\n                                            c = s.charAt(l);\n                                            l += 1;\n                                            switch (c) {\n                                            case '[':\n                                            case '^':\n                                                warningAt(\"Unescaped '{a}'.\",\n                                                        line, from + l, c);\n                                                q = true;\n                                                break;\n                                            case '-':\n                                                if (q) {\n                                                    q = false;\n                                                } else {\n                                                    warningAt(\"Unescaped '{a}'.\",\n                                                            line, from + l, '-');\n                                                    q = true;\n                                                }\n                                                break;\n                                            case ']':\n                                                if (!q) {\n                                                    warningAt(\"Unescaped '{a}'.\",\n                                                            line, from + l - 1, '-');\n                                                }\n                                                break klass;\n                                            case '\\\\':\n                                                c = s.charAt(l);\n                                                if (c < ' ') {\n                                                    warningAt(\n\"Unexpected control character in regular expression.\", line, from + l);\n                                                } else if (c === '<') {\n                                                    warningAt(\n\"Unexpected escaped character '{a}' in regular expression.\", line, from + l, c);\n                                                }\n                                                l += 1;\n                                                q = true;\n                                                break;\n                                            case '/':\n                                                warningAt(\"Unescaped '{a}'.\",\n                                                        line, from + l - 1, '/');\n                                                q = true;\n                                                break;\n                                            case '<':\n                                                if (xmode === 'script') {\n                                                    c = s.charAt(l);\n                                                    if (c === '!' || c === '/') {\n                                                        warningAt(\n\"HTML confusion in regular expression '<{a}'.\", line, from + l, c);\n                                                    }\n                                                }\n                                                q = true;\n                                                break;\n                                            default:\n                                                q = true;\n                                            }\n                                        } while (c);\n                                        break;\n                                    case '.':\n                                        if (option.regexp) {\n                                            warningAt(\"Insecure '{a}'.\", line,\n                                                    from + l, c);\n                                        }\n                                        break;\n                                    case ']':\n                                    case '?':\n                                    case '{':\n                                    case '}':\n                                    case '+':\n                                    case '*':\n                                        warningAt(\"Unescaped '{a}'.\", line,\n                                                from + l, c);\n                                        break;\n                                    case '<':\n                                        if (xmode === 'script') {\n                                            c = s.charAt(l);\n                                            if (c === '!' || c === '/') {\n                                                warningAt(\n\"HTML confusion in regular expression '<{a}'.\", line, from + l, c);\n                                            }\n                                        }\n                                    }\n                                    if (b) {\n                                        switch (s.charAt(l)) {\n                                        case '?':\n                                        case '+':\n                                        case '*':\n                                            l += 1;\n                                            if (s.charAt(l) === '?') {\n                                                l += 1;\n                                            }\n                                            break;\n                                        case '{':\n                                            l += 1;\n                                            c = s.charAt(l);\n                                            if (c < '0' || c > '9') {\n                                                warningAt(\n\"Expected a number and instead saw '{a}'.\", line, from + l, c);\n                                            }\n                                            l += 1;\n                                            low = +c;\n                                            for (;;) {\n                                                c = s.charAt(l);\n                                                if (c < '0' || c > '9') {\n                                                    break;\n                                                }\n                                                l += 1;\n                                                low = +c + (low * 10);\n                                            }\n                                            high = low;\n                                            if (c === ',') {\n                                                l += 1;\n                                                high = Infinity;\n                                                c = s.charAt(l);\n                                                if (c >= '0' && c <= '9') {\n                                                    l += 1;\n                                                    high = +c;\n                                                    for (;;) {\n                                                        c = s.charAt(l);\n                                                        if (c < '0' || c > '9') {\n                                                            break;\n                                                        }\n                                                        l += 1;\n                                                        high = +c + (high * 10);\n                                                    }\n                                                }\n                                            }\n                                            if (s.charAt(l) !== '}') {\n                                                warningAt(\n\"Expected '{a}' and instead saw '{b}'.\", line, from + l, '}', c);\n                                            } else {\n                                                l += 1;\n                                            }\n                                            if (s.charAt(l) === '?') {\n                                                l += 1;\n                                            }\n                                            if (low > high) {\n                                                warningAt(\n\"'{a}' should not be greater than '{b}'.\", line, from + l, low, high);\n                                            }\n                                        }\n                                    }\n                                }\n                                c = s.substr(0, l - 1);\n                                character += l;\n                                s = s.substr(l);\n                                return it('(regexp)', c);\n                            }\n                            return it('(punctuator)', t);\n\n    //      punctuator\n\n                        case '<!--':\n                            l = line;\n                            c = character;\n                            for (;;) {\n                                i = s.indexOf('--');\n                                if (i >= 0) {\n                                    break;\n                                }\n                                i = s.indexOf('<!');\n                                if (i >= 0) {\n                                    errorAt(\"Nested HTML comment.\",\n                                        line, character + i);\n                                }\n                                if (!nextLine()) {\n                                    errorAt(\"Unclosed HTML comment.\", l, c);\n                                }\n                            }\n                            l = s.indexOf('<!');\n                            if (l >= 0 && l < i) {\n                                errorAt(\"Nested HTML comment.\",\n                                    line, character + l);\n                            }\n                            character += i;\n                            if (s[i + 2] !== '>') {\n                                errorAt(\"Expected -->.\", line, character);\n                            }\n                            character += 3;\n                            s = s.slice(i + 3);\n                            break;\n                        case '#':\n                            if (xmode === 'html' || xmode === 'styleproperty') {\n                                for (;;) {\n                                    c = s.charAt(0);\n                                    if ((c < '0' || c > '9') &&\n                                            (c < 'a' || c > 'f') &&\n                                            (c < 'A' || c > 'F')) {\n                                        break;\n                                    }\n                                    character += 1;\n                                    s = s.substr(1);\n                                    t += c;\n                                }\n                                if (t.length !== 4 && t.length !== 7) {\n                                    warningAt(\"Bad hex color '{a}'.\", line,\n                                        from + l, t);\n                                }\n                                return it('(color)', t);\n                            }\n                            return it('(punctuator)', t);\n                        default:\n                            if (xmode === 'outer' && c === '&') {\n                                character += 1;\n                                s = s.substr(1);\n                                for (;;) {\n                                    c = s.charAt(0);\n                                    character += 1;\n                                    s = s.substr(1);\n                                    if (c === ';') {\n                                        break;\n                                    }\n                                    if (!((c >= '0' && c <= '9') ||\n                                            (c >= 'a' && c <= 'z') ||\n                                            c === '#')) {\n                                        errorAt(\"Bad entity\", line, from + l,\n                                        character);\n                                    }\n                                }\n                                break;\n                            }\n                            return it('(punctuator)', t);\n                        }\n                    }\n                }\n            }\n        };\n    }());\n\n\n    function addlabel(t, type) {\n\n        if (option.safe && funct['(global)'] &&\n                typeof predefined[t] !== 'boolean') {\n            warning('ADsafe global: ' + t + '.', token);\n        } else if (t === 'hasOwnProperty') {\n            warning(\"'hasOwnProperty' is a really bad name.\");\n        }\n\n// Define t in the current function in the current scope.\n\n        if (is_own(funct, t) && !funct['(global)']) {\n            warning(funct[t] === true ?\n                \"'{a}' was used before it was defined.\" :\n                \"'{a}' is already defined.\",\n                nexttoken, t);\n        }\n        funct[t] = type;\n        if (funct['(global)']) {\n            global[t] = funct;\n            if (is_own(implied, t)) {\n                warning(\"'{a}' was used before it was defined.\", nexttoken, t);\n                delete implied[t];\n            }\n        } else {\n            scope[t] = funct;\n        }\n    }\n\n\n    function doOption() {\n        var b, obj, filter, o = nexttoken.value, t, v;\n        switch (o) {\n        case '*/':\n            error(\"Unbegun comment.\");\n            break;\n        case '/*members':\n        case '/*member':\n            o = '/*members';\n            if (!membersOnly) {\n                membersOnly = {};\n            }\n            obj = membersOnly;\n            break;\n        case '/*jslint':\n            if (option.safe) {\n                warning(\"ADsafe restriction.\");\n            }\n            obj = option;\n            filter = boolOptions;\n            break;\n        case '/*global':\n            if (option.safe) {\n                warning(\"ADsafe restriction.\");\n            }\n            obj = predefined;\n            break;\n        default:\n        }\n        t = lex.token();\nloop:   for (;;) {\n            for (;;) {\n                if (t.type === 'special' && t.value === '*/') {\n                    break loop;\n                }\n                if (t.id !== '(endline)' && t.id !== ',') {\n                    break;\n                }\n                t = lex.token();\n            }\n            if (t.type !== '(string)' && t.type !== '(identifier)' &&\n                    o !== '/*members') {\n                error(\"Bad option.\", t);\n            }\n            v = lex.token();\n            if (v.id === ':') {\n                v = lex.token();\n                if (obj === membersOnly) {\n                    error(\"Expected '{a}' and instead saw '{b}'.\",\n                            t, '*/', ':');\n                }\n                if (t.value === 'indent' && o === '/*jslint') {\n                    b = +v.value;\n                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||\n                            Math.floor(b) !== b) {\n                        error(\"Expected a small integer and instead saw '{a}'.\",\n                                v, v.value);\n                    }\n                    obj.white = true;\n                    obj.indent = b;\n                } else if (t.value === 'maxerr' && o === '/*jslint') {\n                    b = +v.value;\n                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||\n                            Math.floor(b) !== b) {\n                        error(\"Expected a small integer and instead saw '{a}'.\",\n                                v, v.value);\n                    }\n                    obj.maxerr = b;\n                } else if (t.value === 'maxlen' && o === '/*jslint') {\n                    b = +v.value;\n                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||\n                            Math.floor(b) !== b) {\n                        error(\"Expected a small integer and instead saw '{a}'.\",\n                                v, v.value);\n                    }\n                    obj.maxlen = b;\n                } else if (v.value === 'true') {\n                    obj[t.value] = true;\n                } else if (v.value === 'false') {\n                    obj[t.value] = false;\n                } else {\n                    error(\"Bad option value.\", v);\n                }\n                t = lex.token();\n            } else {\n                if (o === '/*jslint') {\n                    error(\"Missing option value.\", t);\n                }\n                obj[t.value] = false;\n                t = v;\n            }\n        }\n        if (filter) {\n            assume();\n        }\n    }\n\n\n// We need a peek function. If it has an argument, it peeks that much farther\n// ahead. It is used to distinguish\n//     for ( var i in ...\n// from\n//     for ( var i = ...\n\n    function peek(p) {\n        var i = p || 0, j = 0, t;\n\n        while (j <= i) {\n            t = lookahead[j];\n            if (!t) {\n                t = lookahead[j] = lex.token();\n            }\n            j += 1;\n        }\n        return t;\n    }\n\n\n\n// Produce the next token. It looks for programming errors.\n\n    function advance(id, t) {\n        switch (token.id) {\n        case '(number)':\n            if (nexttoken.id === '.') {\n                warning(\n\"A dot following a number can be confused with a decimal point.\", token);\n            }\n            break;\n        case '-':\n            if (nexttoken.id === '-' || nexttoken.id === '--') {\n                warning(\"Confusing minusses.\");\n            }\n            break;\n        case '+':\n            if (nexttoken.id === '+' || nexttoken.id === '++') {\n                warning(\"Confusing plusses.\");\n            }\n            break;\n        }\n        if (token.type === '(string)' || token.identifier) {\n            anonname = token.value;\n        }\n\n        if (id && nexttoken.id !== id) {\n            if (t) {\n                if (nexttoken.id === '(end)') {\n                    warning(\"Unmatched '{a}'.\", t, t.id);\n                } else {\n                    warning(\n\"Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.\",\n                            nexttoken, id, t.id, t.line, nexttoken.value);\n                }\n            } else if (nexttoken.type !== '(identifier)' ||\n                            nexttoken.value !== id) {\n                warning(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, id, nexttoken.value);\n            }\n        }\n        prevtoken = token;\n        token = nexttoken;\n        for (;;) {\n            nexttoken = lookahead.shift() || lex.token();\n            if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {\n                return;\n            }\n            if (nexttoken.type === 'special') {\n                doOption();\n            } else {\n                if (nexttoken.id !== '(endline)') {\n                    break;\n                }\n            }\n        }\n    }\n\n\n// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it\n// is looking for ad hoc lint patterns. We add to Pratt's model .fud, which is\n// like nud except that it is only used on the first token of a statement.\n// Having .fud makes it much easier to define JavaScript. I retained Pratt's\n// nomenclature.\n\n// .nud     Null denotation\n// .fud     First null denotation\n// .led     Left denotation\n//  lbp     Left binding power\n//  rbp     Right binding power\n\n// They are key to the parsing method called Top Down Operator Precedence.\n\n    function parse(rbp, initial) {\n        var left;\n        if (nexttoken.id === '(end)') {\n            error(\"Unexpected early end of program.\", token);\n        }\n        advance();\n        if (option.safe && typeof predefined[token.value] === 'boolean' &&\n                (nexttoken.id !== '(' && nexttoken.id !== '.')) {\n            warning('ADsafe violation.', token);\n        }\n        if (initial) {\n            anonname = 'anonymous';\n            funct['(verb)'] = token.value;\n        }\n        if (initial === true && token.fud) {\n            left = token.fud();\n        } else {\n            if (token.nud) {\n                left = token.nud();\n            } else {\n                if (nexttoken.type === '(number)' && token.id === '.') {\n                    warning(\n\"A leading decimal point can be confused with a dot: '.{a}'.\",\n                            token, nexttoken.value);\n                    advance();\n                    return token;\n                } else {\n                    error(\"Expected an identifier and instead saw '{a}'.\",\n                            token, token.id);\n                }\n            }\n            while (rbp < nexttoken.lbp) {\n                advance();\n                if (token.led) {\n                    left = token.led(left);\n                } else {\n                    error(\"Expected an operator and instead saw '{a}'.\",\n                        token, token.id);\n                }\n            }\n        }\n        return left;\n    }\n\n\n// Functions for conformance of style.\n\n    function adjacent(left, right) {\n        left = left || token;\n        right = right || nexttoken;\n        if (option.white || xmode === 'styleproperty' || xmode === 'style') {\n            if (left.character !== right.from && left.line === right.line) {\n                warning(\"Unexpected space after '{a}'.\", right, left.value);\n            }\n        }\n    }\n\n    function nobreak(left, right) {\n        left = left || token;\n        right = right || nexttoken;\n        if (left.character !== right.from || left.line !== right.line) {\n            warning(\"Unexpected space before '{a}'.\", right, right.value);\n        }\n    }\n\n    function nospace(left, right) {\n        left = left || token;\n        right = right || nexttoken;\n        if (option.white && !left.comment) {\n            if (left.line === right.line) {\n                adjacent(left, right);\n            }\n        }\n    }\n\n\n    function nonadjacent(left, right) {\n        if (option.white) {\n            left = left || token;\n            right = right || nexttoken;\n            if (left.line === right.line && left.character === right.from) {\n                warning(\"Missing space after '{a}'.\",\n                        nexttoken, left.value);\n            }\n        }\n    }\n\n    function nobreaknonadjacent(left, right) {\n        left = left || token;\n        right = right || nexttoken;\n        if (!option.laxbreak && left.line !== right.line) {\n            warning(\"Bad line breaking before '{a}'.\", right, right.id);\n        } else if (option.white) {\n            left = left || token;\n            right = right || nexttoken;\n            if (left.character === right.from) {\n                warning(\"Missing space after '{a}'.\",\n                        nexttoken, left.value);\n            }\n        }\n    }\n\n    function indentation(bias) {\n        var i;\n        if (option.white && nexttoken.id !== '(end)') {\n            i = indent + (bias || 0);\n            if (nexttoken.from !== i) {\n                warning(\n\"Expected '{a}' to have an indentation at {b} instead at {c}.\",\n                        nexttoken, nexttoken.value, i, nexttoken.from);\n            }\n        }\n    }\n\n    function nolinebreak(t) {\n        t = t || token;\n        if (t.line !== nexttoken.line) {\n            warning(\"Line breaking error '{a}'.\", t, t.value);\n        }\n    }\n\n\n    function comma() {\n        if (token.line !== nexttoken.line) {\n            if (!option.laxbreak) {\n                warning(\"Bad line breaking before '{a}'.\", token, nexttoken.id);\n            }\n        } else if (token.character !== nexttoken.from && option.white) {\n            warning(\"Unexpected space after '{a}'.\", nexttoken, token.value);\n        }\n        advance(',');\n        nonadjacent(token, nexttoken);\n    }\n\n\n// Functional constructors for making the symbols that will be inherited by\n// tokens.\n\n    function symbol(s, p) {\n        var x = syntax[s];\n        if (!x || typeof x !== 'object') {\n            syntax[s] = x = {\n                id: s,\n                lbp: p,\n                value: s\n            };\n        }\n        return x;\n    }\n\n\n    function delim(s) {\n        return symbol(s, 0);\n    }\n\n\n    function stmt(s, f) {\n        var x = delim(s);\n        x.identifier = x.reserved = true;\n        x.fud = f;\n        return x;\n    }\n\n\n    function blockstmt(s, f) {\n        var x = stmt(s, f);\n        x.block = true;\n        return x;\n    }\n\n\n    function reserveName(x) {\n        var c = x.id.charAt(0);\n        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {\n            x.identifier = x.reserved = true;\n        }\n        return x;\n    }\n\n\n    function prefix(s, f) {\n        var x = symbol(s, 150);\n        reserveName(x);\n        x.nud = (typeof f === 'function') ? f : function () {\n            this.right = parse(150);\n            this.arity = 'unary';\n            if (this.id === '++' || this.id === '--') {\n                if (option.plusplus) {\n                    warning(\"Unexpected use of '{a}'.\", this, this.id);\n                } else if ((!this.right.identifier || this.right.reserved) &&\n                        this.right.id !== '.' && this.right.id !== '[') {\n                    warning(\"Bad operand.\", this);\n                }\n            }\n            return this;\n        };\n        return x;\n    }\n\n\n    function type(s, f) {\n        var x = delim(s);\n        x.type = s;\n        x.nud = f;\n        return x;\n    }\n\n\n    function reserve(s, f) {\n        var x = type(s, f);\n        x.identifier = x.reserved = true;\n        return x;\n    }\n\n\n    function reservevar(s, v) {\n        return reserve(s, function () {\n            if (typeof v === 'function') {\n                v(this);\n            }\n            return this;\n        });\n    }\n\n\n    function infix(s, f, p, w) {\n        var x = symbol(s, p);\n        reserveName(x);\n        x.led = function (left) {\n            if (!w) {\n                nobreaknonadjacent(prevtoken, token);\n                nonadjacent(token, nexttoken);\n            }\n            if (typeof f === 'function') {\n                return f(left, this);\n            } else {\n                this.left = left;\n                this.right = parse(p);\n                return this;\n            }\n        };\n        return x;\n    }\n\n\n    function relation(s, f) {\n        var x = symbol(s, 100);\n        x.led = function (left) {\n            nobreaknonadjacent(prevtoken, token);\n            nonadjacent(token, nexttoken);\n            var right = parse(100);\n            if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {\n                warning(\"Use the isNaN function to compare with NaN.\", this);\n            } else if (f) {\n                f.apply(this, [left, right]);\n            }\n            if (left.id === '!') {\n                warning(\"Confusing use of '{a}'.\", left, '!');\n            }\n            if (right.id === '!') {\n                warning(\"Confusing use of '{a}'.\", left, '!');\n            }\n            this.left = left;\n            this.right = right;\n            return this;\n        };\n        return x;\n    }\n\n\n    function isPoorRelation(node) {\n        return node &&\n              ((node.type === '(number)' && +node.value === 0) ||\n               (node.type === '(string)' && node.value === '') ||\n                node.type === 'true' ||\n                node.type === 'false' ||\n                node.type === 'undefined' ||\n                node.type === 'null');\n    }\n\n\n    function assignop(s, f) {\n        symbol(s, 20).exps = true;\n        return infix(s, function (left, that) {\n            var l;\n            that.left = left;\n            if (predefined[left.value] === false &&\n                    scope[left.value]['(global)'] === true) {\n                warning(\"Read only.\", left);\n            } else if (left['function']) {\n                warning(\"'{a}' is a function.\", left, left.value);\n            }\n            if (option.safe) {\n                l = left;\n                do {\n                    if (typeof predefined[l.value] === 'boolean') {\n                        warning('ADsafe violation.', l);\n                    }\n                    l = l.left;\n                } while (l);\n            }\n            if (left) {\n                if (left.id === '.' || left.id === '[') {\n                    if (!left.left || left.left.value === 'arguments') {\n                        warning('Bad assignment.', that);\n                    }\n                    that.right = parse(19);\n                    return that;\n                } else if (left.identifier && !left.reserved) {\n                    if (funct[left.value] === 'exception') {\n                        warning(\"Do not assign to the exception parameter.\", left);\n                    }\n                    that.right = parse(19);\n                    return that;\n                }\n                if (left === syntax['function']) {\n                    warning(\n\"Expected an identifier in an assignment and instead saw a function invocation.\",\n                                token);\n                }\n            }\n            error(\"Bad assignment.\", that);\n        }, 20);\n    }\n\n\n    function bitwise(s, f, p) {\n        var x = symbol(s, p);\n        reserveName(x);\n        x.led = (typeof f === 'function') ? f : function (left) {\n            if (option.bitwise) {\n                warning(\"Unexpected use of '{a}'.\", this, this.id);\n            }\n            this.left = left;\n            this.right = parse(p);\n            return this;\n        };\n        return x;\n    }\n\n\n    function bitwiseassignop(s) {\n        symbol(s, 20).exps = true;\n        return infix(s, function (left, that) {\n            if (option.bitwise) {\n                warning(\"Unexpected use of '{a}'.\", that, that.id);\n            }\n            nonadjacent(prevtoken, token);\n            nonadjacent(token, nexttoken);\n            if (left) {\n                if (left.id === '.' || left.id === '[' ||\n                        (left.identifier && !left.reserved)) {\n                    parse(19);\n                    return that;\n                }\n                if (left === syntax['function']) {\n                    warning(\n\"Expected an identifier in an assignment, and instead saw a function invocation.\",\n                                token);\n                }\n                return that;\n            }\n            error(\"Bad assignment.\", that);\n        }, 20);\n    }\n\n\n    function suffix(s, f) {\n        var x = symbol(s, 150);\n        x.led = function (left) {\n            if (option.plusplus) {\n                warning(\"Unexpected use of '{a}'.\", this, this.id);\n            } else if ((!left.identifier || left.reserved) &&\n                    left.id !== '.' && left.id !== '[') {\n                warning(\"Bad operand.\", this);\n            }\n            this.left = left;\n            return this;\n        };\n        return x;\n    }\n\n\n    function optionalidentifier() {\n        if (nexttoken.identifier) {\n            advance();\n            if (option.safe && banned[token.value]) {\n                warning(\"ADsafe violation: '{a}'.\", token, token.value);\n            } else if (token.reserved && !option.es5) {\n                warning(\"Expected an identifier and instead saw '{a}' (a reserved word).\",\n                        token, token.id);\n            }\n            return token.value;\n        }\n    }\n\n\n    function identifier() {\n        var i = optionalidentifier();\n        if (i) {\n            return i;\n        }\n        if (token.id === 'function' && nexttoken.id === '(') {\n            warning(\"Missing name in function statement.\");\n        } else {\n            error(\"Expected an identifier and instead saw '{a}'.\",\n                    nexttoken, nexttoken.value);\n        }\n    }\n\n\n    function reachable(s) {\n        var i = 0, t;\n        if (nexttoken.id !== ';' || noreach) {\n            return;\n        }\n        for (;;) {\n            t = peek(i);\n            if (t.reach) {\n                return;\n            }\n            if (t.id !== '(endline)') {\n                if (t.id === 'function') {\n                    warning(\n\"Inner functions should be listed at the top of the outer function.\", t);\n                    break;\n                }\n                warning(\"Unreachable '{a}' after '{b}'.\", t, t.value, s);\n                break;\n            }\n            i += 1;\n        }\n    }\n\n\n    function statement(noindent) {\n        var i = indent, r, s = scope, t = nexttoken;\n\n// We don't like the empty statement.\n\n        if (t.id === ';') {\n            warning(\"Unnecessary semicolon.\", t);\n            advance(';');\n            return;\n        }\n\n// Is this a labelled statement?\n\n        if (t.identifier && !t.reserved && peek().id === ':') {\n            advance();\n            advance(':');\n            scope = Object.create(s);\n            addlabel(t.value, 'label');\n            if (!nexttoken.labelled) {\n                warning(\"Label '{a}' on {b} statement.\",\n                        nexttoken, t.value, nexttoken.value);\n            }\n            if (jx.test(t.value + ':')) {\n                warning(\"Label '{a}' looks like a javascript url.\",\n                        t, t.value);\n            }\n            nexttoken.label = t.value;\n            t = nexttoken;\n        }\n\n// Parse the statement.\n\n        if (!noindent) {\n            indentation();\n        }\n        r = parse(0, true);\n\n// Look for the final semicolon.\n\n        if (!t.block) {\n            if (!r || !r.exps) {\n                warning(\n\"Expected an assignment or function call and instead saw an expression.\",\n                        token);\n            } else if (r.id === '(' && r.left.id === 'new') {\n                warning(\"Do not use 'new' for side effects.\");\n            }\n            if (nexttoken.id !== ';') {\n                warningAt(\"Missing semicolon.\", token.line,\n                        token.from + token.value.length);\n            } else {\n                adjacent(token, nexttoken);\n                advance(';');\n                nonadjacent(token, nexttoken);\n            }\n        }\n\n// Restore the indentation.\n\n        indent = i;\n        scope = s;\n        return r;\n    }\n\n\n    function use_strict() {\n        if (nexttoken.value === 'use strict') {\n            advance();\n            advance(';');\n            strict_mode = true;\n            option.newcap = true;\n            option.undef = true;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n\n    function statements(begin) {\n        var a = [], f, p;\n        if (begin && !use_strict() && option.strict) {\n            warning('Missing \"use strict\" statement.', nexttoken);\n        }\n        if (option.adsafe) {\n            switch (begin) {\n            case 'script':\n                if (!adsafe_may) {\n                    if (nexttoken.value !== 'ADSAFE' ||\n                            peek(0).id !== '.' ||\n                            (peek(1).value !== 'id' &&\n                            peek(1).value !== 'go')) {\n                        error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.',\n                            nexttoken);\n                    }\n                }\n                if (nexttoken.value === 'ADSAFE' &&\n                        peek(0).id === '.' &&\n                        peek(1).value === 'id') {\n                    if (adsafe_may) {\n                        error('ADsafe violation.', nexttoken);\n                    }\n                    advance('ADSAFE');\n                    advance('.');\n                    advance('id');\n                    advance('(');\n                    if (nexttoken.value !== adsafe_id) {\n                        error('ADsafe violation: id does not match.', nexttoken);\n                    }\n                    advance('(string)');\n                    advance(')');\n                    advance(';');\n                    adsafe_may = true;\n                }\n                break;\n            case 'lib':\n                if (nexttoken.value === 'ADSAFE') {\n                    advance('ADSAFE');\n                    advance('.');\n                    advance('lib');\n                    advance('(');\n                    advance('(string)');\n                    comma();\n                    f = parse(0);\n                    if (f.id !== 'function') {\n                        error('The second argument to lib must be a function.', f);\n                    }\n                    p = f.funct['(params)'];\n                    p = p && p.join(', ');\n                    if (p && p !== 'lib') {\n                        error(\"Expected '{a}' and instead saw '{b}'.\",\n                            f, '(lib)', '(' + p + ')');\n                    }\n                    advance(')');\n                    advance(';');\n                    return a;\n                } else {\n                    error(\"ADsafe lib violation.\");\n                }\n            }\n        }\n        while (!nexttoken.reach && nexttoken.id !== '(end)') {\n            if (nexttoken.id === ';') {\n                warning(\"Unnecessary semicolon.\");\n                advance(';');\n            } else {\n                a.push(statement());\n            }\n        }\n        return a;\n    }\n\n\n    function block(f) {\n        var a, b = inblock, old_indent = indent, s = scope, t;\n        inblock = f;\n        scope = Object.create(scope);\n        nonadjacent(token, nexttoken);\n        t = nexttoken;\n        if (nexttoken.id === '{') {\n            advance('{');\n            if (nexttoken.id !== '}' || token.line !== nexttoken.line) {\n                indent += option.indent;\n                while (!f && nexttoken.from > indent) {\n                    indent += option.indent;\n                }\n                if (!f) {\n                    use_strict();\n                }\n                a = statements();\n                indent -= option.indent;\n                indentation();\n            }\n            advance('}', t);\n            indent = old_indent;\n        } else {\n            warning(\"Expected '{a}' and instead saw '{b}'.\",\n                    nexttoken, '{', nexttoken.value);\n            noreach = true;\n            a = [statement()];\n            noreach = false;\n        }\n        funct['(verb)'] = null;\n        scope = s;\n        inblock = b;\n        if (f && (!a || a.length === 0)) {\n            warning(\"Empty block.\");\n        }\n        return a;\n    }\n\n\n    function countMember(m) {\n        if (membersOnly && typeof membersOnly[m] !== 'boolean') {\n            warning(\"Unexpected /*member '{a}'.\", token, m);\n        }\n        if (typeof member[m] === 'number') {\n            member[m] += 1;\n        } else {\n            member[m] = 1;\n        }\n    }\n\n\n    function note_implied(token) {\n        var name = token.value, line = token.line, a = implied[name];\n        if (typeof a === 'function') {\n            a = false;\n        }\n        if (!a) {\n            a = [line];\n            implied[name] = a;\n        } else if (a[a.length - 1] !== line) {\n            a.push(line);\n        }\n    }\n\n\n// CSS parsing.\n\n\n    function cssName() {\n        if (nexttoken.identifier) {\n            advance();\n            return true;\n        }\n    }\n\n\n    function cssNumber() {\n        if (nexttoken.id === '-') {\n            advance('-');\n            adjacent();\n            nolinebreak();\n        }\n        if (nexttoken.type === '(number)') {\n            advance('(number)');\n            return true;\n        }\n    }\n\n\n    function cssString() {\n        if (nexttoken.type === '(string)') {\n            advance();\n            return true;\n        }\n    }\n\n\n    function cssColor() {\n        var i, number, value;\n        if (nexttoken.identifier) {\n            value = nexttoken.value;\n            if (value === 'rgb' || value === 'rgba') {\n                advance();\n                advance('(');\n                for (i = 0; i < 3; i += 1) {\n                    if (i) {\n                        advance(',');\n                    }\n                    number = nexttoken.value;\n                    if (nexttoken.type !== '(number)' || number < 0) {\n                        warning(\"Expected a positive number and instead saw '{a}'\",\n                            nexttoken, number);\n                        advance();\n                    } else {\n                        advance();\n                        if (nexttoken.id === '%') {\n                            advance('%');\n                            if (number > 100) {\n                                warning(\"Expected a percentage and instead saw '{a}'\",\n                                    token, number);\n                            }\n                        } else {\n                            if (number > 255) {\n                                warning(\"Expected a small number and instead saw '{a}'\",\n                                    token, number);\n                            }\n                        }\n                    }\n                }\n                if (value === 'rgba') {\n                    advance(',');\n                    number = +nexttoken.value;\n                    if (nexttoken.type !== '(number)' || number < 0 || number > 1) {\n                        warning(\"Expected a number between 0 and 1 and instead saw '{a}'\",\n                            nexttoken, number);\n                    }\n                    advance();\n                    if (nexttoken.id === '%') {\n                        warning(\"Unexpected '%'.\");\n                        advance('%');\n                    }\n                }\n                advance(')');\n                return true;\n            } else if (cssColorData[nexttoken.value] === true) {\n                advance();\n                return true;\n            }\n        } else if (nexttoken.type === '(color)') {\n            advance();\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssLength() {\n        if (nexttoken.id === '-') {\n            advance('-');\n            adjacent();\n            nolinebreak();\n        }\n        if (nexttoken.type === '(number)') {\n            advance();\n            if (nexttoken.type !== '(string)' &&\n                    cssLengthData[nexttoken.value] === true) {\n                adjacent();\n                advance();\n            } else if (+token.value !== 0) {\n                warning(\"Expected a linear unit and instead saw '{a}'.\",\n                    nexttoken, nexttoken.value);\n            }\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssLineHeight() {\n        if (nexttoken.id === '-') {\n            advance('-');\n            adjacent();\n        }\n        if (nexttoken.type === '(number)') {\n            advance();\n            if (nexttoken.type !== '(string)' &&\n                    cssLengthData[nexttoken.value] === true) {\n                adjacent();\n                advance();\n            }\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssWidth() {\n        if (nexttoken.identifier) {\n            switch (nexttoken.value) {\n            case 'thin':\n            case 'medium':\n            case 'thick':\n                advance();\n                return true;\n            }\n        } else {\n            return cssLength();\n        }\n    }\n\n\n    function cssMargin() {\n        if (nexttoken.identifier) {\n            if (nexttoken.value === 'auto') {\n                advance();\n                return true;\n            }\n        } else {\n            return cssLength();\n        }\n    }\n\n    function cssAttr() {\n        if (nexttoken.identifier && nexttoken.value === 'attr') {\n            advance();\n            advance('(');\n            if (!nexttoken.identifier) {\n                warning(\"Expected a name and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n            }\n            advance();\n            advance(')');\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssCommaList() {\n        while (nexttoken.id !== ';') {\n            if (!cssName() && !cssString()) {\n                warning(\"Expected a name and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n            }\n            if (nexttoken.id !== ',') {\n                return true;\n            }\n            comma();\n        }\n    }\n\n\n    function cssCounter() {\n        if (nexttoken.identifier && nexttoken.value === 'counter') {\n            advance();\n            advance('(');\n            advance();\n            if (nexttoken.id === ',') {\n                comma();\n                if (nexttoken.type !== '(string)') {\n                    warning(\"Expected a string and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n            }\n            advance(')');\n            return true;\n        }\n        if (nexttoken.identifier && nexttoken.value === 'counters') {\n            advance();\n            advance('(');\n            if (!nexttoken.identifier) {\n                warning(\"Expected a name and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n            }\n            advance();\n            if (nexttoken.id === ',') {\n                comma();\n                if (nexttoken.type !== '(string)') {\n                    warning(\"Expected a string and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n            }\n            if (nexttoken.id === ',') {\n                comma();\n                if (nexttoken.type !== '(string)') {\n                    warning(\"Expected a string and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n            }\n            advance(')');\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssShape() {\n        var i;\n        if (nexttoken.identifier && nexttoken.value === 'rect') {\n            advance();\n            advance('(');\n            for (i = 0; i < 4; i += 1) {\n                if (!cssLength()) {\n                    warning(\"Expected a number and instead saw '{a}'.\",\n                        nexttoken, nexttoken.value);\n                    break;\n                }\n            }\n            advance(')');\n            return true;\n        }\n        return false;\n    }\n\n\n    function cssUrl() {\n        var c, url;\n        if (nexttoken.identifier && nexttoken.value === 'url') {\n            nexttoken = lex.range('(', ')');\n            url = nexttoken.value;\n            c = url.charAt(0);\n            if (c === '\"' || c === '\\'') {\n                if (url.slice(-1) !== c) {\n                    warning(\"Bad url string.\");\n                } else {\n                    url = url.slice(1, -1);\n                    if (url.indexOf(c) >= 0) {\n                        warning(\"Bad url string.\");\n                    }\n                }\n            }\n            if (!url) {\n                warning(\"Missing url.\");\n            }\n            advance();\n            if (option.safe && ux.test(url)) {\n                error(\"ADsafe URL violation.\");\n            }\n            urls.push(url);\n            return true;\n        }\n        return false;\n    }\n\n\n    cssAny = [cssUrl, function () {\n        for (;;) {\n            if (nexttoken.identifier) {\n                switch (nexttoken.value.toLowerCase()) {\n                case 'url':\n                    cssUrl();\n                    break;\n                case 'expression':\n                    warning(\"Unexpected expression '{a}'.\",\n                        nexttoken, nexttoken.value);\n                    advance();\n                    break;\n                default:\n                    advance();\n                }\n            } else {\n                if (nexttoken.id === ';' || nexttoken.id === '!'  ||\n                        nexttoken.id === '(end)' || nexttoken.id === '}') {\n                    return true;\n                }\n                advance();\n            }\n        }\n    }];\n\n\n    cssBorderStyle = [\n        'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'ridge',\n        'inset', 'outset'\n    ];\n\n    cssBreak = [\n        'auto', 'always', 'avoid', 'left', 'right'\n    ];\n\n    cssOverflow = [\n        'auto', 'hidden', 'scroll', 'visible'\n    ];\n\n    cssAttributeData = {\n        background: [\n            true, 'background-attachment', 'background-color',\n            'background-image', 'background-position', 'background-repeat'\n        ],\n        'background-attachment': ['scroll', 'fixed'],\n        'background-color': ['transparent', cssColor],\n        'background-image': ['none', cssUrl],\n        'background-position': [\n            2, [cssLength, 'top', 'bottom', 'left', 'right', 'center']\n        ],\n        'background-repeat': [\n            'repeat', 'repeat-x', 'repeat-y', 'no-repeat'\n        ],\n        'border': [true, 'border-color', 'border-style', 'border-width'],\n        'border-bottom': [\n            true, 'border-bottom-color', 'border-bottom-style',\n            'border-bottom-width'\n        ],\n        'border-bottom-color': cssColor,\n        'border-bottom-style': cssBorderStyle,\n        'border-bottom-width': cssWidth,\n        'border-collapse': ['collapse', 'separate'],\n        'border-color': ['transparent', 4, cssColor],\n        'border-left': [\n            true, 'border-left-color', 'border-left-style', 'border-left-width'\n        ],\n        'border-left-color': cssColor,\n        'border-left-style': cssBorderStyle,\n        'border-left-width': cssWidth,\n        'border-right': [\n            true, 'border-right-color', 'border-right-style',\n            'border-right-width'\n        ],\n        'border-right-color': cssColor,\n        'border-right-style': cssBorderStyle,\n        'border-right-width': cssWidth,\n        'border-spacing': [2, cssLength],\n        'border-style': [4, cssBorderStyle],\n        'border-top': [\n            true, 'border-top-color', 'border-top-style', 'border-top-width'\n        ],\n        'border-top-color': cssColor,\n        'border-top-style': cssBorderStyle,\n        'border-top-width': cssWidth,\n        'border-width': [4, cssWidth],\n        bottom: [cssLength, 'auto'],\n        'caption-side' : ['bottom', 'left', 'right', 'top'],\n        clear: ['both', 'left', 'none', 'right'],\n        clip: [cssShape, 'auto'],\n        color: cssColor,\n        content: [\n            'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',\n            cssString, cssUrl, cssCounter, cssAttr\n        ],\n        'counter-increment': [\n            cssName, 'none'\n        ],\n        'counter-reset': [\n            cssName, 'none'\n        ],\n        cursor: [\n            cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move',\n            'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize',\n            'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'\n        ],\n        direction: ['ltr', 'rtl'],\n        display: [\n            'block', 'compact', 'inline', 'inline-block', 'inline-table',\n            'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption',\n            'table-cell', 'table-column', 'table-column-group',\n            'table-footer-group', 'table-header-group', 'table-row',\n            'table-row-group'\n        ],\n        'empty-cells': ['show', 'hide'],\n        'float': ['left', 'none', 'right'],\n        font: [\n            'caption', 'icon', 'menu', 'message-box', 'small-caption',\n            'status-bar', true, 'font-size', 'font-style', 'font-weight',\n            'font-family'\n        ],\n        'font-family': cssCommaList,\n        'font-size': [\n            'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large',\n            'xx-large', 'larger', 'smaller', cssLength\n        ],\n        'font-size-adjust': ['none', cssNumber],\n        'font-stretch': [\n            'normal', 'wider', 'narrower', 'ultra-condensed',\n            'extra-condensed', 'condensed', 'semi-condensed',\n            'semi-expanded', 'expanded', 'extra-expanded'\n        ],\n        'font-style': [\n            'normal', 'italic', 'oblique'\n        ],\n        'font-variant': [\n            'normal', 'small-caps'\n        ],\n        'font-weight': [\n            'normal', 'bold', 'bolder', 'lighter', cssNumber\n        ],\n        height: [cssLength, 'auto'],\n        left: [cssLength, 'auto'],\n        'letter-spacing': ['normal', cssLength],\n        'line-height': ['normal', cssLineHeight],\n        'list-style': [\n            true, 'list-style-image', 'list-style-position', 'list-style-type'\n        ],\n        'list-style-image': ['none', cssUrl],\n        'list-style-position': ['inside', 'outside'],\n        'list-style-type': [\n            'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero',\n            'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha',\n            'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana',\n            'hiragana-iroha', 'katakana-oroha', 'none'\n        ],\n        margin: [4, cssMargin],\n        'margin-bottom': cssMargin,\n        'margin-left': cssMargin,\n        'margin-right': cssMargin,\n        'margin-top': cssMargin,\n        'marker-offset': [cssLength, 'auto'],\n        'max-height': [cssLength, 'none'],\n        'max-width': [cssLength, 'none'],\n        'min-height': cssLength,\n        'min-width': cssLength,\n        opacity: cssNumber,\n        outline: [true, 'outline-color', 'outline-style', 'outline-width'],\n        'outline-color': ['invert', cssColor],\n        'outline-style': [\n            'dashed', 'dotted', 'double', 'groove', 'inset', 'none',\n            'outset', 'ridge', 'solid'\n        ],\n        'outline-width': cssWidth,\n        overflow: cssOverflow,\n        'overflow-x': cssOverflow,\n        'overflow-y': cssOverflow,\n        padding: [4, cssLength],\n        'padding-bottom': cssLength,\n        'padding-left': cssLength,\n        'padding-right': cssLength,\n        'padding-top': cssLength,\n        'page-break-after': cssBreak,\n        'page-break-before': cssBreak,\n        position: ['absolute', 'fixed', 'relative', 'static'],\n        quotes: [8, cssString],\n        right: [cssLength, 'auto'],\n        'table-layout': ['auto', 'fixed'],\n        'text-align': ['center', 'justify', 'left', 'right'],\n        'text-decoration': [\n            'none', 'underline', 'overline', 'line-through', 'blink'\n        ],\n        'text-indent': cssLength,\n        'text-shadow': ['none', 4, [cssColor, cssLength]],\n        'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],\n        top: [cssLength, 'auto'],\n        'unicode-bidi': ['normal', 'embed', 'bidi-override'],\n        'vertical-align': [\n            'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle',\n            'text-bottom', cssLength\n        ],\n        visibility: ['visible', 'hidden', 'collapse'],\n        'white-space': [\n            'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit'\n        ],\n        width: [cssLength, 'auto'],\n        'word-spacing': ['normal', cssLength],\n        'word-wrap': ['break-word', 'normal'],\n        'z-index': ['auto', cssNumber]\n    };\n\n    function styleAttribute() {\n        var v;\n        while (nexttoken.id === '*' || nexttoken.id === '#' ||\n                nexttoken.value === '_') {\n            if (!option.css) {\n                warning(\"Unexpected '{a}'.\", nexttoken, nexttoken.value);\n            }\n            advance();\n        }\n        if (nexttoken.id === '-') {\n            if (!option.css) {\n                warning(\"Unexpected '{a}'.\", nexttoken, nexttoken.value);\n            }\n            advance('-');\n            if (!nexttoken.identifier) {\n                warning(\n\"Expected a non-standard style attribute and instead saw '{a}'.\",\n                    nexttoken, nexttoken.value);\n            }\n            advance();\n            return cssAny;\n        } else {\n            if (!nexttoken.identifier) {\n                warning(\"Excepted a style attribute, and instead saw '{a}'.\",\n                    nexttoken, nexttoken.value);\n            } else {\n                if (is_own(cssAttributeData, nexttoken.value)) {\n                    v = cssAttributeData[nexttoken.value];\n                } else {\n                    v = cssAny;\n                    if (!option.css) {\n                        warning(\"Unrecognized style attribute '{a}'.\",\n                                nexttoken, nexttoken.value);\n                    }\n                }\n            }\n            advance();\n            return v;\n        }\n    }\n\n\n    function styleValue(v) {\n        var i = 0,\n            n,\n            once,\n            match,\n            round,\n            start = 0,\n            vi;\n        switch (typeof v) {\n        case 'function':\n            return v();\n        case 'string':\n            if (nexttoken.identifier && nexttoken.value === v) {\n                advance();\n                return true;\n            }\n            return false;\n        }\n        for (;;) {\n            if (i >= v.length) {\n                return false;\n            }\n            vi = v[i];\n            i += 1;\n            if (vi === true) {\n                break;\n            } else if (typeof vi === 'number') {\n                n = vi;\n                vi = v[i];\n                i += 1;\n            } else {\n                n = 1;\n            }\n            match = false;\n            while (n > 0) {\n                if (styleValue(vi)) {\n                    match = true;\n                    n -= 1;\n                } else {\n                    break;\n                }\n            }\n            if (match) {\n                return true;\n            }\n        }\n        start = i;\n        once = [];\n        for (;;) {\n            round = false;\n            for (i = start; i < v.length; i += 1) {\n                if (!once[i]) {\n                    if (styleValue(cssAttributeData[v[i]])) {\n                        match = true;\n                        round = true;\n                        once[i] = true;\n                        break;\n                    }\n                }\n            }\n            if (!round) {\n                return match;\n            }\n        }\n    }\n\n    function styleChild() {\n        if (nexttoken.id === '(number)') {\n            advance();\n            if (nexttoken.value === 'n' && nexttoken.identifier) {\n                adjacent();\n                advance();\n                if (nexttoken.id === '+') {\n                    adjacent();\n                    advance('+');\n                    adjacent();\n                    advance('(number)');\n                }\n            }\n            return;\n        } else {\n            switch (nexttoken.value) {\n            case 'odd':\n            case 'even':\n                if (nexttoken.identifier) {\n                    advance();\n                    return;\n                }\n            }\n        }\n        warning(\"Unexpected token '{a}'.\", nexttoken, nexttoken.value);\n    }\n\n    function substyle() {\n        var v;\n        for (;;) {\n            if (nexttoken.id === '}' || nexttoken.id === '(end)' ||\n                    xquote && nexttoken.id === xquote) {\n                return;\n            }\n            while (nexttoken.id === ';') {\n                warning(\"Misplaced ';'.\");\n                advance(';');\n            }\n            v = styleAttribute();\n            advance(':');\n            if (nexttoken.identifier && nexttoken.value === 'inherit') {\n                advance();\n            } else {\n                if (!styleValue(v)) {\n                    warning(\"Unexpected token '{a}'.\", nexttoken,\n                        nexttoken.value);\n                    advance();\n                }\n            }\n            if (nexttoken.id === '!') {\n                advance('!');\n                adjacent();\n                if (nexttoken.identifier && nexttoken.value === 'important') {\n                    advance();\n                } else {\n                    warning(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, 'important', nexttoken.value);\n                }\n            }\n            if (nexttoken.id === '}' || nexttoken.id === xquote) {\n                warning(\"Missing '{a}'.\", nexttoken, ';');\n            } else {\n                advance(';');\n            }\n        }\n    }\n\n    function styleSelector() {\n        if (nexttoken.identifier) {\n            if (!is_own(htmltag, option.cap ?\n                    nexttoken.value.toLowerCase() : nexttoken.value)) {\n                warning(\"Expected a tagName, and instead saw {a}.\",\n                    nexttoken, nexttoken.value);\n            }\n            advance();\n        } else {\n            switch (nexttoken.id) {\n            case '>':\n            case '+':\n                advance();\n                styleSelector();\n                break;\n            case ':':\n                advance(':');\n                switch (nexttoken.value) {\n                case 'active':\n                case 'after':\n                case 'before':\n                case 'checked':\n                case 'disabled':\n                case 'empty':\n                case 'enabled':\n                case 'first-child':\n                case 'first-letter':\n                case 'first-line':\n                case 'first-of-type':\n                case 'focus':\n                case 'hover':\n                case 'last-child':\n                case 'last-of-type':\n                case 'link':\n                case 'only-of-type':\n                case 'root':\n                case 'target':\n                case 'visited':\n                    advance();\n                    break;\n                case 'lang':\n                    advance();\n                    advance('(');\n                    if (!nexttoken.identifier) {\n                        warning(\"Expected a lang code, and instead saw :{a}.\",\n                            nexttoken, nexttoken.value);\n                    }\n                    advance(')');\n                    break;\n                case 'nth-child':\n                case 'nth-last-child':\n                case 'nth-last-of-type':\n                case 'nth-of-type':\n                    advance();\n                    advance('(');\n                    styleChild();\n                    advance(')');\n                    break;\n                case 'not':\n                    advance();\n                    advance('(');\n                    if (nexttoken.id === ':' && peek(0).value === 'not') {\n                        warning(\"Nested not.\");\n                    }\n                    styleSelector();\n                    advance(')');\n                    break;\n                default:\n                    warning(\"Expected a pseudo, and instead saw :{a}.\",\n                        nexttoken, nexttoken.value);\n                }\n                break;\n            case '#':\n                advance('#');\n                if (!nexttoken.identifier) {\n                    warning(\"Expected an id, and instead saw #{a}.\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n                break;\n            case '*':\n                advance('*');\n                break;\n            case '.':\n                advance('.');\n                if (!nexttoken.identifier) {\n                    warning(\"Expected a class, and instead saw #.{a}.\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n                break;\n            case '[':\n                advance('[');\n                if (!nexttoken.identifier) {\n                    warning(\"Expected an attribute, and instead saw [{a}].\",\n                        nexttoken, nexttoken.value);\n                }\n                advance();\n                if (nexttoken.id === '=' || nexttoken.value === '~=' ||\n                        nexttoken.value === '$=' ||\n                        nexttoken.value === '|=' ||\n                        nexttoken.id === '*=' ||\n                        nexttoken.id === '^=') {\n                    advance();\n                    if (nexttoken.type !== '(string)') {\n                        warning(\"Expected a string, and instead saw {a}.\",\n                            nexttoken, nexttoken.value);\n                    }\n                    advance();\n                }\n                advance(']');\n                break;\n            default:\n                error(\"Expected a CSS selector, and instead saw {a}.\",\n                    nexttoken, nexttoken.value);\n            }\n        }\n    }\n\n    function stylePattern() {\n        var name;\n        if (nexttoken.id === '{') {\n            warning(\"Expected a style pattern, and instead saw '{a}'.\", nexttoken,\n                nexttoken.id);\n        } else if (nexttoken.id === '@') {\n            advance('@');\n            name = nexttoken.value;\n            if (nexttoken.identifier && atrule[name] === true) {\n                advance();\n                return name;\n            }\n            warning(\"Expected an at-rule, and instead saw @{a}.\", nexttoken, name);\n        }\n        for (;;) {\n            styleSelector();\n            if (nexttoken.id === '</' || nexttoken.id === '{' ||\n                    nexttoken.id === '(end)') {\n                return '';\n            }\n            if (nexttoken.id === ',') {\n                comma();\n            }\n        }\n    }\n\n    function styles() {\n        var i;\n        while (nexttoken.id === '@') {\n            i = peek();\n            if (i.identifier && i.value === 'import') {\n                advance('@');\n                advance();\n                if (!cssUrl()) {\n                    warning(\"Expected '{a}' and instead saw '{b}'.\", nexttoken,\n                        'url', nexttoken.value);\n                    advance();\n                }\n                advance(';');\n            } else {\n                break;\n            }\n        }\n        while (nexttoken.id !== '</' && nexttoken.id !== '(end)') {\n            stylePattern();\n            xmode = 'styleproperty';\n            if (nexttoken.id === ';') {\n                advance(';');\n            } else {\n                advance('{');\n                substyle();\n                xmode = 'style';\n                advance('}');\n            }\n        }\n    }\n\n\n// HTML parsing.\n\n    function doBegin(n) {\n        if (n !== 'html' && !option.fragment) {\n            if (n === 'div' && option.adsafe) {\n                error(\"ADSAFE: Use the fragment option.\");\n            } else {\n                error(\"Expected '{a}' and instead saw '{b}'.\",\n                    token, 'html', n);\n            }\n        }\n        if (option.adsafe) {\n            if (n === 'html') {\n                error(\n\"Currently, ADsafe does not operate on whole HTML documents. It operates on <div> fragments and .js files.\", token);\n            }\n            if (option.fragment) {\n                if (n !== 'div') {\n                    error(\"ADsafe violation: Wrap the widget in a div.\", token);\n                }\n            } else {\n                error(\"Use the fragment option.\", token);\n            }\n        }\n        option.browser = true;\n        assume();\n    }\n\n    function doAttribute(n, a, v) {\n        var u, x;\n        if (a === 'id') {\n            u = typeof v === 'string' ? v.toUpperCase() : '';\n            if (ids[u] === true) {\n                warning(\"Duplicate id='{a}'.\", nexttoken, v);\n            }\n            if (!/^[A-Za-z][A-Za-z0-9._:\\-]*$/.test(v)) {\n                warning(\"Bad id: '{a}'.\", nexttoken, v);\n            } else if (option.adsafe) {\n                if (adsafe_id) {\n                    if (v.slice(0, adsafe_id.length) !== adsafe_id) {\n                        warning(\"ADsafe violation: An id must have a '{a}' prefix\",\n                                nexttoken, adsafe_id);\n                    } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {\n                        warning(\"ADSAFE violation: bad id.\");\n                    }\n                } else {\n                    adsafe_id = v;\n                    if (!/^[A-Z]+_$/.test(v)) {\n                        warning(\"ADSAFE violation: bad id.\");\n                    }\n                }\n            }\n            x = v.search(dx);\n            if (x >= 0) {\n                warning(\"Unexpected character '{a}' in {b}.\", token, v.charAt(x), a);\n            }\n            ids[u] = true;\n        } else if (a === 'class' || a === 'type' || a === 'name') {\n            x = v.search(qx);\n            if (x >= 0) {\n                warning(\"Unexpected character '{a}' in {b}.\", token, v.charAt(x), a);\n            }\n            ids[u] = true;\n        } else if (a === 'href' || a === 'background' ||\n                a === 'content' || a === 'data' ||\n                a.indexOf('src') >= 0 || a.indexOf('url') >= 0) {\n            if (option.safe && ux.test(v)) {\n                error(\"ADsafe URL violation.\");\n            }\n            urls.push(v);\n        } else if (a === 'for') {\n            if (option.adsafe) {\n                if (adsafe_id) {\n                    if (v.slice(0, adsafe_id.length) !== adsafe_id) {\n                        warning(\"ADsafe violation: An id must have a '{a}' prefix\",\n                                nexttoken, adsafe_id);\n                    } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) {\n                        warning(\"ADSAFE violation: bad id.\");\n                    }\n                } else {\n                    warning(\"ADSAFE violation: bad id.\");\n                }\n            }\n        } else if (a === 'name') {\n            if (option.adsafe && v.indexOf('_') >= 0) {\n                warning(\"ADsafe name violation.\");\n            }\n        }\n    }\n\n    function doTag(n, a) {\n        var i, t = htmltag[n], x;\n        src = false;\n        if (!t) {\n            error(\"Unrecognized tag '<{a}>'.\",\n                    nexttoken,\n                    n === n.toLowerCase() ? n :\n                        n + ' (capitalization error)');\n        }\n        if (stack.length > 0) {\n            if (n === 'html') {\n                error(\"Too many <html> tags.\", token);\n            }\n            x = t.parent;\n            if (x) {\n                if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) {\n                    error(\"A '<{a}>' must be within '<{b}>'.\",\n                            token, n, x);\n                }\n            } else if (!option.adsafe && !option.fragment) {\n                i = stack.length;\n                do {\n                    if (i <= 0) {\n                        error(\"A '<{a}>' must be within '<{b}>'.\",\n                                token, n, 'body');\n                    }\n                    i -= 1;\n                } while (stack[i].name !== 'body');\n            }\n        }\n        switch (n) {\n        case 'div':\n            if (option.adsafe && stack.length === 1 && !adsafe_id) {\n                warning(\"ADSAFE violation: missing ID_.\");\n            }\n            break;\n        case 'script':\n            xmode = 'script';\n            advance('>');\n            indent = nexttoken.from;\n            if (a.lang) {\n                warning(\"lang is deprecated.\", token);\n            }\n            if (option.adsafe && stack.length !== 1) {\n                warning(\"ADsafe script placement violation.\", token);\n            }\n            if (a.src) {\n                if (option.adsafe && (!adsafe_may || !approved[a.src])) {\n                    warning(\"ADsafe unapproved script source.\", token);\n                }\n                if (a.type) {\n                    warning(\"type is unnecessary.\", token);\n                }\n            } else {\n                if (adsafe_went) {\n                    error(\"ADsafe script violation.\", token);\n                }\n                statements('script');\n            }\n            xmode = 'html';\n            advance('</');\n            if (!nexttoken.identifier && nexttoken.value !== 'script') {\n                warning(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, 'script', nexttoken.value);\n            }\n            advance();\n            xmode = 'outer';\n            break;\n        case 'style':\n            xmode = 'style';\n            advance('>');\n            styles();\n            xmode = 'html';\n            advance('</');\n            if (!nexttoken.identifier && nexttoken.value !== 'style') {\n                warning(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, 'style', nexttoken.value);\n            }\n            advance();\n            xmode = 'outer';\n            break;\n        case 'input':\n            switch (a.type) {\n            case 'radio':\n            case 'checkbox':\n            case 'button':\n            case 'reset':\n            case 'submit':\n                break;\n            case 'text':\n            case 'file':\n            case 'password':\n            case 'file':\n            case 'hidden':\n            case 'image':\n                if (option.adsafe && a.autocomplete !== 'off') {\n                    warning(\"ADsafe autocomplete violation.\");\n                }\n                break;\n            default:\n                warning(\"Bad input type.\");\n            }\n            break;\n        case 'applet':\n        case 'body':\n        case 'embed':\n        case 'frame':\n        case 'frameset':\n        case 'head':\n        case 'iframe':\n        case 'noembed':\n        case 'noframes':\n        case 'object':\n        case 'param':\n            if (option.adsafe) {\n                warning(\"ADsafe violation: Disallowed tag: \" + n);\n            }\n            break;\n        }\n    }\n\n\n    function closetag(n) {\n        return '</' + n + '>';\n    }\n\n    function html() {\n        var a, attributes, e, n, q, t, v, w = option.white, wmode;\n        xmode = 'html';\n        xquote = '';\n        stack = null;\n        for (;;) {\n            switch (nexttoken.value) {\n            case '<':\n                xmode = 'html';\n                advance('<');\n                attributes = {};\n                t = nexttoken;\n                if (!t.identifier) {\n                    warning(\"Bad identifier {a}.\", t, t.value);\n                }\n                n = t.value;\n                if (option.cap) {\n                    n = n.toLowerCase();\n                }\n                t.name = n;\n                advance();\n                if (!stack) {\n                    stack = [];\n                    doBegin(n);\n                }\n                v = htmltag[n];\n                if (typeof v !== 'object') {\n                    error(\"Unrecognized tag '<{a}>'.\", t, n);\n                }\n                e = v.empty;\n                t.type = n;\n                for (;;) {\n                    if (nexttoken.id === '/') {\n                        advance('/');\n                        if (nexttoken.id !== '>') {\n                            warning(\"Expected '{a}' and instead saw '{b}'.\",\n                                    nexttoken, '>', nexttoken.value);\n                        }\n                        break;\n                    }\n                    if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') {\n                        break;\n                    }\n                    if (!nexttoken.identifier) {\n                        if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {\n                            error(\"Missing '>'.\", nexttoken);\n                        }\n                        warning(\"Bad identifier.\");\n                    }\n                    option.white = true;\n                    nonadjacent(token, nexttoken);\n                    a = nexttoken.value;\n                    option.white = w;\n                    advance();\n                    if (!option.cap && a !== a.toLowerCase()) {\n                        warning(\"Attribute '{a}' not all lower case.\", nexttoken, a);\n                    }\n                    a = a.toLowerCase();\n                    xquote = '';\n                    if (is_own(attributes, a)) {\n                        warning(\"Attribute '{a}' repeated.\", nexttoken, a);\n                    }\n                    if (a.slice(0, 2) === 'on') {\n                        if (!option.on) {\n                            warning(\"Avoid HTML event handlers.\");\n                        }\n                        xmode = 'scriptstring';\n                        advance('=');\n                        q = nexttoken.id;\n                        if (q !== '\"' && q !== \"'\") {\n                            error(\"Missing quote.\");\n                        }\n                        xquote = q;\n                        wmode = option.white;\n                        option.white = false;\n                        advance(q);\n                        statements('on');\n                        option.white = wmode;\n                        if (nexttoken.id !== q) {\n                            error(\"Missing close quote on script attribute.\");\n                        }\n                        xmode = 'html';\n                        xquote = '';\n                        advance(q);\n                        v = false;\n                    } else if (a === 'style') {\n                        xmode = 'scriptstring';\n                        advance('=');\n                        q = nexttoken.id;\n                        if (q !== '\"' && q !== \"'\") {\n                            error(\"Missing quote.\");\n                        }\n                        xmode = 'styleproperty';\n                        xquote = q;\n                        advance(q);\n                        substyle();\n                        xmode = 'html';\n                        xquote = '';\n                        advance(q);\n                        v = false;\n                    } else {\n                        if (nexttoken.id === '=') {\n                            advance('=');\n                            v = nexttoken.value;\n                            if (!nexttoken.identifier &&\n                                    nexttoken.id !== '\"' &&\n                                    nexttoken.id !== '\\'' &&\n                                    nexttoken.type !== '(string)' &&\n                                    nexttoken.type !== '(number)' &&\n                                    nexttoken.type !== '(color)') {\n                                warning(\"Expected an attribute value and instead saw '{a}'.\", token, a);\n                            }\n                            advance();\n                        } else {\n                            v = true;\n                        }\n                    }\n                    attributes[a] = v;\n                    doAttribute(n, a, v);\n                }\n                doTag(n, attributes);\n                if (!e) {\n                    stack.push(t);\n                }\n                xmode = 'outer';\n                advance('>');\n                break;\n            case '</':\n                xmode = 'html';\n                advance('</');\n                if (!nexttoken.identifier) {\n                    warning(\"Bad identifier.\");\n                }\n                n = nexttoken.value;\n                if (option.cap) {\n                    n = n.toLowerCase();\n                }\n                advance();\n                if (!stack) {\n                    error(\"Unexpected '{a}'.\", nexttoken, closetag(n));\n                }\n                t = stack.pop();\n                if (!t) {\n                    error(\"Unexpected '{a}'.\", nexttoken, closetag(n));\n                }\n                if (t.name !== n) {\n                    error(\"Expected '{a}' and instead saw '{b}'.\",\n                            nexttoken, closetag(t.name), closetag(n));\n                }\n                if (nexttoken.id !== '>') {\n                    error(\"Missing '{a}'.\", nexttoken, '>');\n                }\n                xmode = 'outer';\n                advance('>');\n                break;\n            case '<!':\n                if (option.safe) {\n                    warning(\"ADsafe HTML violation.\");\n                }\n                xmode = 'html';\n                for (;;) {\n                    advance();\n                    if (nexttoken.id === '>' || nexttoken.id === '(end)') {\n                        break;\n                    }\n                    if (nexttoken.value.indexOf('--') >= 0) {\n                        error(\"Unexpected --.\");\n                    }\n                    if (nexttoken.value.indexOf('<') >= 0) {\n                        error(\"Unexpected <.\");\n                    }\n                    if (nexttoken.value.indexOf('>') >= 0) {\n                        error(\"Unexpected >.\");\n                    }\n                }\n                xmode = 'outer';\n                advance('>');\n                break;\n            case '(end)':\n                return;\n            default:\n                if (nexttoken.id === '(end)') {\n                    error(\"Missing '{a}'.\", nexttoken,\n                            '</' + stack[stack.length - 1].value + '>');\n                } else {\n                    advance();\n                }\n            }\n            if (stack && stack.length === 0 && (option.adsafe ||\n                    !option.fragment || nexttoken.id === '(end)')) {\n                break;\n            }\n        }\n        if (nexttoken.id !== '(end)') {\n            error(\"Unexpected material after the end.\");\n        }\n    }\n\n\n// Build the syntax table by declaring the syntactic elements of the language.\n\n    type('(number)', function () {\n        return this;\n    });\n    type('(string)', function () {\n        return this;\n    });\n\n    syntax['(identifier)'] = {\n        type: '(identifier)',\n        lbp: 0,\n        identifier: true,\n        nud: function () {\n            var v = this.value,\n                s = scope[v],\n                f;\n            if (typeof s === 'function') {\n\n// Protection against accidental inheritance.\n\n                s = undefined;\n            } else if (typeof s === 'boolean') {\n                f = funct;\n                funct = functions[0];\n                addlabel(v, 'var');\n                s = funct;\n                funct = f;\n            }\n\n// The name is in scope and defined in the current function.\n\n            if (funct === s) {\n\n//      Change 'unused' to 'var', and reject labels.\n\n                switch (funct[v]) {\n                case 'unused':\n                    funct[v] = 'var';\n                    break;\n                case 'unction':\n                    funct[v] = 'function';\n                    this['function'] = true;\n                    break;\n                case 'function':\n                    this['function'] = true;\n                    break;\n                case 'label':\n                    warning(\"'{a}' is a statement label.\", token, v);\n                    break;\n                }\n\n// The name is not defined in the function.  If we are in the global scope,\n// then we have an undefined variable.\n\n            } else if (funct['(global)']) {\n                if (option.undef && predefined[v] !== 'boolean') {\n                    warning(\"'{a}' is not defined.\", token, v);\n                }\n                note_implied(token);\n\n// If the name is already defined in the current\n// function, but not as outer, then there is a scope error.\n\n            } else {\n                switch (funct[v]) {\n                case 'closure':\n                case 'function':\n                case 'var':\n                case 'unused':\n                    warning(\"'{a}' used out of scope.\", token, v);\n                    break;\n                case 'label':\n                    warning(\"'{a}' is a statement label.\", token, v);\n                    break;\n                case 'outer':\n                case 'global':\n                    break;\n                default:\n\n// If the name is defined in an outer function, make an outer entry, and if\n// it was unused, make it var.\n\n                    if (s === true) {\n                        funct[v] = true;\n                    } else if (s === null) {\n                        warning(\"'{a}' is not allowed.\", token, v);\n                        note_implied(token);\n                    } else if (typeof s !== 'object') {\n                        if (option.undef) {\n                            warning(\"'{a}' is not defined.\", token, v);\n                        } else {\n                            funct[v] = true;\n                        }\n                        note_implied(token);\n                    } else {\n                        switch (s[v]) {\n                        case 'function':\n                        case 'unction':\n                            this['function'] = true;\n                            s[v] = 'closure';\n                            funct[v] = s['(global)'] ? 'global' : 'outer';\n                            break;\n                        case 'var':\n                        case 'unused':\n                            s[v] = 'closure';\n                            funct[v] = s['(global)'] ? 'global' : 'outer';\n                            break;\n                        case 'closure':\n                        case 'parameter':\n                            funct[v] = s['(global)'] ? 'global' : 'outer';\n                            break;\n                        case 'label':\n                            warning(\"'{a}' is a statement label.\", token, v);\n                        }\n                    }\n                }\n            }\n            return this;\n        },\n        led: function () {\n            error(\"Expected an operator and instead saw '{a}'.\",\n                nexttoken, nexttoken.value);\n        }\n    };\n\n    type('(regexp)', function () {\n        return this;\n    });\n\n\n// ECMAScript parser\n\n    delim('(endline)');\n    delim('(begin)');\n    delim('(end)').reach = true;\n    delim('</').reach = true;\n    delim('<!');\n    delim('<!--');\n    delim('-->');\n    delim('(error)').reach = true;\n    delim('}').reach = true;\n    delim(')');\n    delim(']');\n    delim('\"').reach = true;\n    delim(\"'\").reach = true;\n    delim(';');\n    delim(':').reach = true;\n    delim(',');\n    delim('#');\n    delim('@');\n    reserve('else');\n    reserve('case').reach = true;\n    reserve('catch');\n    reserve('default').reach = true;\n    reserve('finally');\n    reservevar('arguments', function (x) {\n        if (strict_mode && funct['(global)']) {\n            warning(\"Strict violation.\", x);\n        } else if (option.safe) {\n            warning(\"ADsafe violation.\", x);\n        }\n    });\n    reservevar('eval', function (x) {\n        if (option.safe) {\n            warning(\"ADsafe violation.\", x);\n        }\n    });\n    reservevar('false');\n    reservevar('Infinity');\n    reservevar('NaN');\n    reservevar('null');\n    reservevar('this', function (x) {\n        if (strict_mode && ((funct['(statement)'] &&\n                funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) {\n            warning(\"Strict violation.\", x);\n        } else if (option.safe) {\n            warning(\"ADsafe violation.\", x);\n        }\n    });\n    reservevar('true');\n    reservevar('undefined');\n    assignop('=', 'assign', 20);\n    assignop('+=', 'assignadd', 20);\n    assignop('-=', 'assignsub', 20);\n    assignop('*=', 'assignmult', 20);\n    assignop('/=', 'assigndiv', 20).nud = function () {\n        error(\"A regular expression literal can be confused with '/='.\");\n    };\n    assignop('%=', 'assignmod', 20);\n    bitwiseassignop('&=', 'assignbitand', 20);\n    bitwiseassignop('|=', 'assignbitor', 20);\n    bitwiseassignop('^=', 'assignbitxor', 20);\n    bitwiseassignop('<<=', 'assignshiftleft', 20);\n    bitwiseassignop('>>=', 'assignshiftright', 20);\n    bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20);\n    infix('?', function (left, that) {\n        that.left = left;\n        that.right = parse(10);\n        advance(':');\n        that['else'] = parse(10);\n        return that;\n    }, 30);\n\n    infix('||', 'or', 40);\n    infix('&&', 'and', 50);\n    bitwise('|', 'bitor', 70);\n    bitwise('^', 'bitxor', 80);\n    bitwise('&', 'bitand', 90);\n    relation('==', function (left, right) {\n        if (option.eqeqeq) {\n            warning(\"Expected '{a}' and instead saw '{b}'.\",\n                    this, '===', '==');\n        } else if (isPoorRelation(left)) {\n            warning(\"Use '{a}' to compare with '{b}'.\",\n                this, '===', left.value);\n        } else if (isPoorRelation(right)) {\n            warning(\"Use '{a}' to compare with '{b}'.\",\n                this, '===', right.value);\n        }\n        return this;\n    });\n    relation('===');\n    relation('!=', function (left, right) {\n        if (option.eqeqeq) {\n            warning(\"Expected '{a}' and instead saw '{b}'.\",\n                    this, '!==', '!=');\n        } else if (isPoorRelation(left)) {\n            warning(\"Use '{a}' to compare with '{b}'.\",\n                    this, '!==', left.value);\n        } else if (isPoorRelation(right)) {\n            warning(\"Use '{a}' to compare with '{b}'.\",\n                    this, '!==', right.value);\n        }\n        return this;\n    });\n    relation('!==');\n    relation('<');\n    relation('>');\n    relation('<=');\n    relation('>=');\n    bitwise('<<', 'shiftleft', 120);\n    bitwise('>>', 'shiftright', 120);\n    bitwise('>>>', 'shiftrightunsigned', 120);\n    infix('in', 'in', 120);\n    infix('instanceof', 'instanceof', 120);\n    infix('+', function (left, that) {\n        var right = parse(130);\n        if (left && right && left.id === '(string)' && right.id === '(string)') {\n            left.value += right.value;\n            left.character = right.character;\n            if (jx.test(left.value)) {\n                warning(\"JavaScript URL.\", left);\n            }\n            return left;\n        }\n        that.left = left;\n        that.right = right;\n        return that;\n    }, 130);\n    prefix('+', 'num');\n    prefix('+++', function () {\n        warning(\"Confusing pluses.\");\n        this.right = parse(150);\n        this.arity = 'unary';\n        return this;\n    });\n    infix('+++', function (left) {\n        warning(\"Confusing pluses.\");\n        this.left = left;\n        this.right = parse(130);\n        return this;\n    }, 130);\n    infix('-', 'sub', 130);\n    prefix('-', 'neg');\n    prefix('---', function () {\n        warning(\"Confusing minuses.\");\n        this.right = parse(150);\n        this.arity = 'unary';\n        return this;\n    });\n    infix('---', function (left) {\n        warning(\"Confusing minuses.\");\n        this.left = left;\n        this.right = parse(130);\n        return this;\n    }, 130);\n    infix('*', 'mult', 140);\n    infix('/', 'div', 140);\n    infix('%', 'mod', 140);\n\n    suffix('++', 'postinc');\n    prefix('++', 'preinc');\n    syntax['++'].exps = true;\n\n    suffix('--', 'postdec');\n    prefix('--', 'predec');\n    syntax['--'].exps = true;\n    prefix('delete', function () {\n        var p = parse(0);\n        if (!p || (p.id !== '.' && p.id !== '[')) {\n            warning(\"Variables should not be deleted.\");\n        }\n        this.first = p;\n        return this;\n    }).exps = true;\n\n\n    prefix('~', function () {\n        if (option.bitwise) {\n            warning(\"Unexpected '{a}'.\", this, '~');\n        }\n        parse(150);\n        return this;\n    });\n    prefix('!', function () {\n        this.right = parse(150);\n        this.arity = 'unary';\n        if (bang[this.right.id] === true) {\n            warning(\"Confusing use of '{a}'.\", this, '!');\n        }\n        return this;\n    });\n    prefix('typeof', 'typeof');\n    prefix('new', function () {\n        var c = parse(155), i;\n        if (c && c.id !== 'function') {\n            if (c.identifier) {\n                c['new'] = true;\n                switch (c.value) {\n                case 'Object':\n                    warning(\"Use the object literal notation {}.\", token);\n                    break;\n                case 'Array':\n                    if (nexttoken.id !== '(') {\n                        warning(\"Use the array literal notation [].\", token);\n                    } else {\n                        advance('(');\n                        if (nexttoken.id === ')') {\n                            warning(\"Use the array literal notation [].\", token);\n                        } else {\n                            i = parse(0);\n                            c.dimension = i;\n                            if ((i.id === '(number)' && /[.+\\-Ee]/.test(i.value)) ||\n                                    (i.id === '-' && !i.right) ||\n                                    i.id === '(string)' || i.id === '[' ||\n                                    i.id === '{' || i.id === 'true' ||\n                                    i.id === 'false' ||\n                                    i.id === 'null' || i.id === 'undefined' ||\n                                    i.id === 'Infinity') {\n                                warning(\"Use the array literal notation [].\", token);\n                            }\n                            if (nexttoken.id !== ')') {\n                                error(\"Use the array literal notation [].\", token);\n                            }\n                        }\n                        advance(')');\n                    }\n                    this.first = c;\n                    return this;\n                case 'Number':\n                case 'String':\n                case 'Boolean':\n                case 'Math':\n                case 'JSON':\n                    warning(\"Do not use {a} as a constructor.\", token, c.value);\n                    break;\n                case 'Function':\n                    if (!option.evil) {\n                        warning(\"The Function constructor is eval.\");\n                    }\n                    break;\n                case 'Date':\n                case 'RegExp':\n                    break;\n                default:\n                    if (c.id !== 'function') {\n                        i = c.value.substr(0, 1);\n                        if (option.newcap && (i < 'A' || i > 'Z')) {\n                            warning(\n                    \"A constructor name should start with an uppercase letter.\",\n                                token);\n                        }\n                    }\n                }\n            } else {\n                if (c.id !== '.' && c.id !== '[' && c.id !== '(') {\n                    warning(\"Bad constructor.\", token);\n                }\n            }\n        } else {\n            warning(\"Weird construction. Delete 'new'.\", this);\n        }\n        adjacent(token, nexttoken);\n        if (nexttoken.id !== '(') {\n            warning(\"Missing '()' invoking a constructor.\");\n        }\n        this.first = c;\n        return this;\n    });\n    syntax['new'].exps = true;\n\n    infix('.', function (left, that) {\n        adjacent(prevtoken, token);\n        nobreak();\n        var m = identifier();\n        if (typeof m === 'string') {\n            countMember(m);\n        }\n        that.left = left;\n        that.right = m;\n        if (left && left.value === 'arguments' &&\n                (m === 'callee' || m === 'caller')) {\n            warning(\"Avoid arguments.{a}.\", left, m);\n        } else if (!option.evil && left && left.value === 'document' &&\n                (m === 'write' || m === 'writeln')) {\n            warning(\"document.write can be a form of eval.\", left);\n        } else if (option.adsafe) {\n            if (left && left.value === 'ADSAFE') {\n                if (m === 'id' || m === 'lib') {\n                    warning(\"ADsafe violation.\", that);\n                } else if (m === 'go') {\n                    if (xmode !== 'script') {\n                        warning(\"ADsafe violation.\", that);\n                    } else if (adsafe_went || nexttoken.id !== '(' ||\n                            peek(0).id !== '(string)' ||\n                            peek(0).value !== adsafe_id ||\n                            peek(1).id !== ',') {\n                        error(\"ADsafe violation: go.\", that);\n                    }\n                    adsafe_went = true;\n                    adsafe_may = false;\n                }\n            }\n        }\n        if (!option.evil && (m === 'eval' || m === 'execScript')) {\n            warning('eval is evil.');\n        } else if (option.safe) {\n            for (;;) {\n                if (banned[m] === true) {\n                    warning(\"ADsafe restricted word '{a}'.\", token, m);\n                }\n                if (typeof predefined[left.value] !== 'boolean' ||\n                        nexttoken.id === '(') {\n                    break;\n                }\n                if (standard_member[m] === true) {\n                    if (nexttoken.id === '.') {\n                        warning(\"ADsafe violation.\", that);\n                    }\n                    break;\n                }\n                if (nexttoken.id !== '.') {\n                    warning(\"ADsafe violation.\", that);\n                    break;\n                }\n                advance('.');\n                token.left = that;\n                token.right = m;\n                that = token;\n                m = identifier();\n                if (typeof m === 'string') {\n                    countMember(m);\n                }\n            }\n        }\n        return that;\n    }, 160, true);\n\n    infix('(', function (left, that) {\n        nobreak(prevtoken, token);\n        nospace();\n        if (option.immed && !left.immed && left.id === 'function') {\n            warning(\"Wrap an immediate function invocation in parentheses \" +\n                \"to assist the reader in understanding that the expression \" +\n                \"is the result of a function, and not the function itself.\");\n        }\n        var n = 0,\n            p = [];\n        if (left) {\n            if (left.type === '(identifier)') {\n                if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {\n                    if (left.value !== 'Number' && left.value !== 'String' &&\n                            left.value !== 'Boolean' &&\n                            left.value !== 'Date') {\n                        if (left.value === 'Math') {\n                            warning(\"Math is not a function.\", left);\n                        } else if (option.newcap) {\n                            warning(\n\"Missing 'new' prefix when invoking a constructor.\", left);\n                        }\n                    }\n                }\n            } else if (left.id === '.') {\n                if (option.safe && left.left.value === 'Math' &&\n                        left.right === 'random') {\n                    warning(\"ADsafe violation.\", left);\n                }\n            }\n        }\n        if (nexttoken.id !== ')') {\n            for (;;) {\n                p[p.length] = parse(10);\n                n += 1;\n                if (nexttoken.id !== ',') {\n                    break;\n                }\n                comma();\n            }\n        }\n        advance(')');\n        nospace(prevtoken, token);\n        if (typeof left === 'object') {\n            if (left.value === 'parseInt' && n === 1) {\n                warning(\"Missing radix parameter.\", left);\n            }\n            if (!option.evil) {\n                if (left.value === 'eval' || left.value === 'Function' ||\n                        left.value === 'execScript') {\n                    warning(\"eval is evil.\", left);\n                } else if (p[0] && p[0].id === '(string)' &&\n                       (left.value === 'setTimeout' ||\n                        left.value === 'setInterval')) {\n                    warning(\n    \"Implied eval is evil. Pass a function instead of a string.\", left);\n                }\n            }\n            if (!left.identifier && left.id !== '.' && left.id !== '[' &&\n                    left.id !== '(' && left.id !== '&&' && left.id !== '||' &&\n                    left.id !== '?') {\n                warning(\"Bad invocation.\", left);\n            }\n        }\n        that.left = left;\n        return that;\n    }, 155, true).exps = true;\n\n    prefix('(', function () {\n        nospace();\n        if (nexttoken.id === 'function') {\n            nexttoken.immed = true;\n        }\n        var v = parse(0);\n        advance(')', this);\n        nospace(prevtoken, token);\n        if (option.immed && v.id === 'function') {\n            if (nexttoken.id === '(') {\n                warning(\n\"Move the invocation into the parens that contain the function.\", nexttoken);\n            } else {\n                warning(\n\"Do not wrap function literals in parens unless they are to be immediately invoked.\",\n                        this);\n            }\n        }\n        return v;\n    });\n\n    infix('[', function (left, that) {\n        nobreak(prevtoken, token);\n        nospace();\n        var e = parse(0), s;\n        if (e && e.type === '(string)') {\n            if (option.safe && banned[e.value] === true) {\n                warning(\"ADsafe restricted word '{a}'.\", that, e.value);\n            } else if (!option.evil &&\n                    (e.value === 'eval' || e.value === 'execScript')) {\n                warning(\"eval is evil.\", that);\n            } else if (option.safe &&\n                    (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) {\n                warning(\"ADsafe restricted subscript '{a}'.\", that, e.value);\n            }\n            countMember(e.value);\n            if (!option.sub && ix.test(e.value)) {\n                s = syntax[e.value];\n                if (!s || !s.reserved) {\n                    warning(\"['{a}'] is better written in dot notation.\",\n                            e, e.value);\n                }\n            }\n        } else if (!e || e.type !== '(number)' || e.value < 0) {\n            if (option.safe) {\n                warning('ADsafe subscripting.');\n            }\n        }\n        advance(']', that);\n        nospace(prevtoken, token);\n        that.left = left;\n        that.right = e;\n        return that;\n    }, 160, true);\n\n    prefix('[', function () {\n        var b = token.line !== nexttoken.line;\n        this.first = [];\n        if (b) {\n            indent += option.indent;\n            if (nexttoken.from === indent + option.indent) {\n                indent += option.indent;\n            }\n        }\n        while (nexttoken.id !== '(end)') {\n            while (nexttoken.id === ',') {\n                warning(\"Extra comma.\");\n                advance(',');\n            }\n            if (nexttoken.id === ']') {\n                break;\n            }\n            if (b && token.line !== nexttoken.line) {\n                indentation();\n            }\n            this.first.push(parse(10));\n            if (nexttoken.id === ',') {\n                comma();\n                if (nexttoken.id === ']' && !option.es5) {\n                    warning(\"Extra comma.\", token);\n                    break;\n                }\n            } else {\n                break;\n            }\n        }\n        if (b) {\n            indent -= option.indent;\n            indentation();\n        }\n        advance(']', this);\n        return this;\n    }, 160);\n\n\n    function property_name() {\n        var id = optionalidentifier(true);\n        if (!id) {\n            if (nexttoken.id === '(string)') {\n                id = nexttoken.value;\n                if (option.adsafe &&\n                        (id.charAt(0) === '_' ||\n                         id.charAt(id.length - 1) === '_')) {\n                    warning(\"Unexpected {a} in '{b}'.\", token,\n                        \"dangling '_'\", id);\n                }\n                advance();\n            } else if (nexttoken.id === '(number)') {\n                id = nexttoken.value.toString();\n                advance();\n            }\n        }\n        return id;\n    }\n\n\n    function functionparams() {\n        var i, t = nexttoken, p = [];\n        advance('(');\n        nospace();\n        if (nexttoken.id === ')') {\n            advance(')');\n            nospace(prevtoken, token);\n            return;\n        }\n        for (;;) {\n            i = identifier();\n            p.push(i);\n            addlabel(i, 'parameter');\n            if (nexttoken.id === ',') {\n                comma();\n            } else {\n                advance(')', t);\n                nospace(prevtoken, token);\n                return p;\n            }\n        }\n    }\n\n\n    function doFunction(i, statement) {\n        var f, s = scope;\n        scope = Object.create(s);\n        funct = {\n            '(name)'     : i || '\"' + anonname + '\"',\n            '(line)'     : nexttoken.line,\n            '(context)'  : funct,\n            '(breakage)' : 0,\n            '(loopage)'  : 0,\n            '(scope)'    : scope,\n            '(statement)': statement\n        };\n        f = funct;\n        token.funct = funct;\n        functions.push(funct);\n        if (i) {\n            addlabel(i, 'function');\n        }\n        funct['(params)'] = functionparams();\n\n        block(false);\n        scope = s;\n        funct['(last)'] = token.line;\n        funct = funct['(context)'];\n        return f;\n    }\n\n\n    (function (x) {\n        x.nud = function () {\n            var b, f, i, j, p, seen = {}, t;\n            b = token.line !== nexttoken.line;\n            if (b) {\n                indent += option.indent;\n                if (nexttoken.from === indent + option.indent) {\n                    indent += option.indent;\n                }\n            }\n            for (;;) {\n                if (nexttoken.id === '}') {\n                    break;\n                }\n                if (b) {\n                    indentation();\n                }\n                if (nexttoken.value === 'get' && peek().id !== ':') {\n                    advance('get');\n                    if (!option.es5) {\n                        error(\"get/set are ES5 features.\");\n                    }\n                    i = property_name();\n                    if (!i) {\n                        error(\"Missing property name.\");\n                    }\n                    t = nexttoken;\n                    adjacent(token, nexttoken);\n                    f = doFunction(i);\n                    if (funct['(loopage)']) {\n                        warning(\"Don't make functions within a loop.\", t);\n                    }\n                    p = f['(params)'];\n                    if (p) {\n                        warning(\"Unexpected parameter '{a}' in get {b} function.\", t, p[0], i);\n                    }\n                    adjacent(token, nexttoken);\n                    advance(',');\n                    indentation();\n                    advance('set');\n                    j = property_name();\n                    if (i !== j) {\n                        error(\"Expected {a} and instead saw {b}.\", token, i, j);\n                    }\n                    t = nexttoken;\n                    adjacent(token, nexttoken);\n                    f = doFunction(i);\n                    p = f['(params)'];\n                    if (!p || p.length !== 1 || p[0] !== 'value') {\n                        warning(\"Expected (value) in set {a} function.\", t, i);\n                    }\n                } else {\n                    i = property_name();\n                    if (typeof i !== 'string') {\n                        break;\n                    }\n                    advance(':');\n                    nonadjacent(token, nexttoken);\n                    parse(10);\n                }\n                if (seen[i] === true) {\n                    warning(\"Duplicate member '{a}'.\", nexttoken, i);\n                }\n                seen[i] = true;\n                countMember(i);\n                if (nexttoken.id === ',') {\n                    comma();\n                    if (nexttoken.id === ',') {\n                        warning(\"Extra comma.\", token);\n                    } else if (nexttoken.id === '}' && !option.es5) {\n                        warning(\"Extra comma.\", token);\n                    }\n                } else {\n                    break;\n                }\n            }\n            if (b) {\n                indent -= option.indent;\n                indentation();\n            }\n            advance('}', this);\n            return this;\n        };\n        x.fud = function () {\n            error(\"Expected to see a statement and instead saw a block.\", token);\n        };\n    }(delim('{')));\n\n\n    var varstatement = function varstatement(prefix) {\n\n// JavaScript does not have block scope. It only has function scope. So,\n// declaring a variable in a block can have unexpected consequences.\n\n        var id, name, value;\n\n        if (funct['(onevar)'] && option.onevar) {\n            warning(\"Too many var statements.\");\n        } else if (!funct['(global)']) {\n            funct['(onevar)'] = true;\n        }\n        this.first = [];\n        for (;;) {\n            nonadjacent(token, nexttoken);\n            id = identifier();\n            if (funct['(global)'] && predefined[id] === false) {\n                warning(\"Redefinition of '{a}'.\", token, id);\n            }\n            addlabel(id, 'unused');\n            if (prefix) {\n                break;\n            }\n            name = token;\n            this.first.push(token);\n            if (nexttoken.id === '=') {\n                nonadjacent(token, nexttoken);\n                advance('=');\n                nonadjacent(token, nexttoken);\n                if (nexttoken.id === 'undefined') {\n                    warning(\"It is not necessary to initialize '{a}' to 'undefined'.\", token, id);\n                }\n                if (peek(0).id === '=' && nexttoken.identifier) {\n                    error(\"Variable {a} was not declared correctly.\",\n                            nexttoken, nexttoken.value);\n                }\n                value = parse(0);\n                name.first = value;\n            }\n            if (nexttoken.id !== ',') {\n                break;\n            }\n            comma();\n        }\n        return this;\n    };\n\n\n    stmt('var', varstatement).exps = true;\n\n\n    blockstmt('function', function () {\n        if (inblock) {\n            warning(\n\"Function statements cannot be placed in blocks. Use a function expression or move the statement to the top of the outer function.\", token);\n\n        }\n        var i = identifier();\n        adjacent(token, nexttoken);\n        addlabel(i, 'unction');\n        doFunction(i, true);\n        if (nexttoken.id === '(' && nexttoken.line === token.line) {\n            error(\n\"Function statements are not invocable. Wrap the whole function invocation in parens.\");\n        }\n        return this;\n    });\n\n    prefix('function', function () {\n        var i = optionalidentifier();\n        if (i) {\n            adjacent(token, nexttoken);\n        } else {\n            nonadjacent(token, nexttoken);\n        }\n        doFunction(i);\n        if (funct['(loopage)']) {\n            warning(\"Don't make functions within a loop.\");\n        }\n        return this;\n    });\n\n    blockstmt('if', function () {\n        var t = nexttoken;\n        advance('(');\n        nonadjacent(this, t);\n        nospace();\n        parse(20);\n        if (nexttoken.id === '=') {\n            warning(\"Expected a conditional expression and instead saw an assignment.\");\n            advance('=');\n            parse(20);\n        }\n        advance(')', t);\n        nospace(prevtoken, token);\n        block(true);\n        if (nexttoken.id === 'else') {\n            nonadjacent(token, nexttoken);\n            advance('else');\n            if (nexttoken.id === 'if' || nexttoken.id === 'switch') {\n                statement(true);\n            } else {\n                block(true);\n            }\n        }\n        return this;\n    });\n\n    blockstmt('try', function () {\n        var b, e, s;\n        if (option.adsafe) {\n            warning(\"ADsafe try violation.\", this);\n        }\n        block(false);\n        if (nexttoken.id === 'catch') {\n            advance('catch');\n            nonadjacent(token, nexttoken);\n            advance('(');\n            s = scope;\n            scope = Object.create(s);\n            e = nexttoken.value;\n            if (nexttoken.type !== '(identifier)') {\n                warning(\"Expected an identifier and instead saw '{a}'.\",\n                    nexttoken, e);\n            } else {\n                addlabel(e, 'exception');\n            }\n            advance();\n            advance(')');\n            block(false);\n            b = true;\n            scope = s;\n        }\n        if (nexttoken.id === 'finally') {\n            advance('finally');\n            block(false);\n            return;\n        } else if (!b) {\n            error(\"Expected '{a}' and instead saw '{b}'.\",\n                    nexttoken, 'catch', nexttoken.value);\n        }\n        return this;\n    });\n\n    blockstmt('while', function () {\n        var t = nexttoken;\n        funct['(breakage)'] += 1;\n        funct['(loopage)'] += 1;\n        advance('(');\n        nonadjacent(this, t);\n        nospace();\n        parse(20);\n        if (nexttoken.id === '=') {\n            warning(\"Expected a conditional expression and instead saw an assignment.\");\n            advance('=');\n            parse(20);\n        }\n        advance(')', t);\n        nospace(prevtoken, token);\n        block(true);\n        funct['(breakage)'] -= 1;\n        funct['(loopage)'] -= 1;\n        return this;\n    }).labelled = true;\n\n    reserve('with');\n\n    blockstmt('switch', function () {\n        var t = nexttoken,\n            g = false;\n        funct['(breakage)'] += 1;\n        advance('(');\n        nonadjacent(this, t);\n        nospace();\n        this.condition = parse(20);\n        advance(')', t);\n        nospace(prevtoken, token);\n        nonadjacent(token, nexttoken);\n        t = nexttoken;\n        advance('{');\n        nonadjacent(token, nexttoken);\n        indent += option.indent;\n        this.cases = [];\n        for (;;) {\n            switch (nexttoken.id) {\n            case 'case':\n                switch (funct['(verb)']) {\n                case 'break':\n                case 'case':\n                case 'continue':\n                case 'return':\n                case 'switch':\n                case 'throw':\n                    break;\n                default:\n                    warning(\n                        \"Expected a 'break' statement before 'case'.\",\n                        token);\n                }\n                indentation(-option.indent);\n                advance('case');\n                this.cases.push(parse(20));\n                g = true;\n                advance(':');\n                funct['(verb)'] = 'case';\n                break;\n            case 'default':\n                switch (funct['(verb)']) {\n                case 'break':\n                case 'continue':\n                case 'return':\n                case 'throw':\n                    break;\n                default:\n                    warning(\n                        \"Expected a 'break' statement before 'default'.\",\n                        token);\n                }\n                indentation(-option.indent);\n                advance('default');\n                g = true;\n                advance(':');\n                break;\n            case '}':\n                indent -= option.indent;\n                indentation();\n                advance('}', t);\n                if (this.cases.length === 1 || this.condition.id === 'true' ||\n                        this.condition.id === 'false') {\n                    warning(\"This 'switch' should be an 'if'.\", this);\n                }\n                funct['(breakage)'] -= 1;\n                funct['(verb)'] = undefined;\n                return;\n            case '(end)':\n                error(\"Missing '{a}'.\", nexttoken, '}');\n                return;\n            default:\n                if (g) {\n                    switch (token.id) {\n                    case ',':\n                        error(\"Each value should have its own case label.\");\n                        return;\n                    case ':':\n                        statements();\n                        break;\n                    default:\n                        error(\"Missing ':' on a case clause.\", token);\n                    }\n                } else {\n                    error(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, 'case', nexttoken.value);\n                }\n            }\n        }\n    }).labelled = true;\n\n    stmt('debugger', function () {\n        if (!option.debug) {\n            warning(\"All 'debugger' statements should be removed.\");\n        }\n        return this;\n    }).exps = true;\n\n    (function () {\n        var x = stmt('do', function () {\n            funct['(breakage)'] += 1;\n            funct['(loopage)'] += 1;\n            this.first = block(true);\n            advance('while');\n            var t = nexttoken;\n            nonadjacent(token, t);\n            advance('(');\n            nospace();\n            parse(20);\n            if (nexttoken.id === '=') {\n                warning(\"Expected a conditional expression and instead saw an assignment.\");\n                advance('=');\n                parse(20);\n            }\n            advance(')', t);\n            nospace(prevtoken, token);\n            funct['(breakage)'] -= 1;\n            funct['(loopage)'] -= 1;\n            return this;\n        });\n        x.labelled = true;\n        x.exps = true;\n    }());\n\n    blockstmt('for', function () {\n        var f = option.forin, s, t = nexttoken;\n        funct['(breakage)'] += 1;\n        funct['(loopage)'] += 1;\n        advance('(');\n        nonadjacent(this, t);\n        nospace();\n        if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') {\n            if (nexttoken.id === 'var') {\n                advance('var');\n                varstatement(true);\n            } else {\n                switch (funct[nexttoken.value]) {\n                case 'unused':\n                    funct[nexttoken.value] = 'var';\n                    break;\n                case 'var':\n                    break;\n                default:\n                    warning(\"Bad for in variable '{a}'.\",\n                            nexttoken, nexttoken.value);\n                }\n                advance();\n            }\n            advance('in');\n            parse(20);\n            advance(')', t);\n            s = block(true);\n            if (!f && (s.length > 1 || typeof s[0] !== 'object' ||\n                    s[0].value !== 'if')) {\n                warning(\"The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.\", this);\n            }\n            funct['(breakage)'] -= 1;\n            funct['(loopage)'] -= 1;\n            return this;\n        } else {\n            if (nexttoken.id !== ';') {\n                if (nexttoken.id === 'var') {\n                    advance('var');\n                    varstatement();\n                } else {\n                    for (;;) {\n                        parse(0, 'for');\n                        if (nexttoken.id !== ',') {\n                            break;\n                        }\n                        comma();\n                    }\n                }\n            }\n            nolinebreak(token);\n            advance(';');\n            if (nexttoken.id !== ';') {\n                parse(20);\n                if (nexttoken.id === '=') {\n                    warning(\"Expected a conditional expression and instead saw an assignment.\");\n                    advance('=');\n                    parse(20);\n                }\n            }\n            nolinebreak(token);\n            advance(';');\n            if (nexttoken.id === ';') {\n                error(\"Expected '{a}' and instead saw '{b}'.\",\n                        nexttoken, ')', ';');\n            }\n            if (nexttoken.id !== ')') {\n                for (;;) {\n                    parse(0, 'for');\n                    if (nexttoken.id !== ',') {\n                        break;\n                    }\n                    comma();\n                }\n            }\n            advance(')', t);\n            nospace(prevtoken, token);\n            block(true);\n            funct['(breakage)'] -= 1;\n            funct['(loopage)'] -= 1;\n            return this;\n        }\n    }).labelled = true;\n\n\n    stmt('break', function () {\n        var v = nexttoken.value;\n        if (funct['(breakage)'] === 0) {\n            warning(\"Unexpected '{a}'.\", nexttoken, this.value);\n        }\n        nolinebreak(this);\n        if (nexttoken.id !== ';') {\n            if (token.line === nexttoken.line) {\n                if (funct[v] !== 'label') {\n                    warning(\"'{a}' is not a statement label.\", nexttoken, v);\n                } else if (scope[v] !== funct) {\n                    warning(\"'{a}' is out of scope.\", nexttoken, v);\n                }\n                this.first = nexttoken;\n                advance();\n            }\n        }\n        reachable('break');\n        return this;\n    }).exps = true;\n\n\n    stmt('continue', function () {\n        var v = nexttoken.value;\n        if (funct['(breakage)'] === 0) {\n            warning(\"Unexpected '{a}'.\", nexttoken, this.value);\n        }\n        nolinebreak(this);\n        if (nexttoken.id !== ';') {\n            if (token.line === nexttoken.line) {\n                if (funct[v] !== 'label') {\n                    warning(\"'{a}' is not a statement label.\", nexttoken, v);\n                } else if (scope[v] !== funct) {\n                    warning(\"'{a}' is out of scope.\", nexttoken, v);\n                }\n                this.first = nexttoken;\n                advance();\n            }\n        } else if (!funct['(loopage)']) {\n            warning(\"Unexpected '{a}'.\", nexttoken, this.value);\n        }\n        reachable('continue');\n        return this;\n    }).exps = true;\n\n\n    stmt('return', function () {\n        nolinebreak(this);\n        if (nexttoken.id === '(regexp)') {\n            warning(\"Wrap the /regexp/ literal in parens to disambiguate the slash operator.\");\n        }\n        if (nexttoken.id !== ';' && !nexttoken.reach) {\n            nonadjacent(token, nexttoken);\n            this.first = parse(20);\n        }\n        reachable('return');\n        return this;\n    }).exps = true;\n\n\n    stmt('throw', function () {\n        nolinebreak(this);\n        nonadjacent(token, nexttoken);\n        this.first = parse(20);\n        reachable('throw');\n        return this;\n    }).exps = true;\n\n    reserve('void');\n\n//  Superfluous reserved words\n\n    reserve('class');\n    reserve('const');\n    reserve('enum');\n    reserve('export');\n    reserve('extends');\n    reserve('import');\n    reserve('super');\n\n    reserve('let');\n    reserve('yield');\n    reserve('implements');\n    reserve('interface');\n    reserve('package');\n    reserve('private');\n    reserve('protected');\n    reserve('public');\n    reserve('static');\n\n\n// Parse JSON\n\n    function jsonValue() {\n\n        function jsonObject() {\n            var o = {}, t = nexttoken;\n            advance('{');\n            if (nexttoken.id !== '}') {\n                for (;;) {\n                    if (nexttoken.id === '(end)') {\n                        error(\"Missing '}' to match '{' from line {a}.\",\n                                nexttoken, t.line);\n                    } else if (nexttoken.id === '}') {\n                        warning(\"Unexpected comma.\", token);\n                        break;\n                    } else if (nexttoken.id === ',') {\n                        error(\"Unexpected comma.\", nexttoken);\n                    } else if (nexttoken.id !== '(string)') {\n                        warning(\"Expected a string and instead saw {a}.\",\n                                nexttoken, nexttoken.value);\n                    }\n                    if (o[nexttoken.value] === true) {\n                        warning(\"Duplicate key '{a}'.\",\n                                nexttoken, nexttoken.value);\n                    } else if (nexttoken.value === '__proto__') {\n                        warning(\"Stupid key '{a}'.\",\n                                nexttoken, nexttoken.value);\n                    } else {\n                        o[nexttoken.value] = true;\n                    }\n                    advance();\n                    advance(':');\n                    jsonValue();\n                    if (nexttoken.id !== ',') {\n                        break;\n                    }\n                    advance(',');\n                }\n            }\n            advance('}');\n        }\n\n        function jsonArray() {\n            var t = nexttoken;\n            advance('[');\n            if (nexttoken.id !== ']') {\n                for (;;) {\n                    if (nexttoken.id === '(end)') {\n                        error(\"Missing ']' to match '[' from line {a}.\",\n                                nexttoken, t.line);\n                    } else if (nexttoken.id === ']') {\n                        warning(\"Unexpected comma.\", token);\n                        break;\n                    } else if (nexttoken.id === ',') {\n                        error(\"Unexpected comma.\", nexttoken);\n                    }\n                    jsonValue();\n                    if (nexttoken.id !== ',') {\n                        break;\n                    }\n                    advance(',');\n                }\n            }\n            advance(']');\n        }\n\n        switch (nexttoken.id) {\n        case '{':\n            jsonObject();\n            break;\n        case '[':\n            jsonArray();\n            break;\n        case 'true':\n        case 'false':\n        case 'null':\n        case '(number)':\n        case '(string)':\n            advance();\n            break;\n        case '-':\n            advance('-');\n            if (token.character !== nexttoken.from) {\n                warning(\"Unexpected space after '-'.\", token);\n            }\n            adjacent(token, nexttoken);\n            advance('(number)');\n            break;\n        default:\n            error(\"Expected a JSON value.\", nexttoken);\n        }\n    }\n\n\n// The actual JSLINT function itself.\n\n    var itself = function (s, o) {\n        var a, i;\n        JSLINT.errors = [];\n        predefined = Object.create(standard);\n        if (o) {\n            a = o.predef;\n            if (a instanceof Array) {\n                for (i = 0; i < a.length; i += 1) {\n                    predefined[a[i]] = true;\n                }\n            }\n            if (o.adsafe) {\n                o.safe = true;\n            }\n            if (o.safe) {\n                o.browser =\n                o.css     =\n                o.debug   =\n                o.devel   =\n                o.evil    =\n                o.forin   =\n                o.on      =\n                o.rhino   =\n                o.windows =\n                o.sub     =\n                o.widget  = false;\n\n                o.eqeqeq  =\n                o.nomen   =\n                o.safe    =\n                o.strict  =\n                o.undef   = true;\n\n                predefined.Date =\n                predefined['eval'] =\n                predefined.Function =\n                predefined.Object = null;\n\n                predefined.ADSAFE =\n                predefined.lib = false;\n            }\n            option = o;\n        } else {\n            option = {};\n        }\n        option.indent = option.indent || 4;\n        option.maxerr = option.maxerr || 50;\n        adsafe_id = '';\n        adsafe_may = false;\n        adsafe_went = false;\n        approved = {};\n        if (option.approved) {\n            for (i = 0; i < option.approved.length; i += 1) {\n                approved[option.approved[i]] = option.approved[i];\n            }\n        } else {\n            approved.test = 'test';\n        }\n        tab = '';\n        for (i = 0; i < option.indent; i += 1) {\n            tab += ' ';\n        }\n        indent = 1;\n        global = Object.create(predefined);\n        scope = global;\n        funct = {\n            '(global)': true,\n            '(name)': '(global)',\n            '(scope)': scope,\n            '(breakage)': 0,\n            '(loopage)': 0\n        };\n        functions = [funct];\n        ids = {};\n        urls = [];\n        src = false;\n        xmode = false;\n        stack = null;\n        member = {};\n        membersOnly = null;\n        implied = {};\n        inblock = false;\n        lookahead = [];\n        jsonmode = false;\n        warnings = 0;\n        lex.init(s);\n        prereg = true;\n        strict_mode = false;\n\n        prevtoken = token = nexttoken = syntax['(begin)'];\n        assume();\n\n        try {\n            advance();\n            if (nexttoken.value.charAt(0) === '<') {\n                html();\n                if (option.adsafe && !adsafe_went) {\n                    warning(\"ADsafe violation: Missing ADSAFE.go.\", this);\n                }\n            } else {\n                switch (nexttoken.id) {\n                case '{':\n                case '[':\n                    option.laxbreak = true;\n                    jsonmode = true;\n                    jsonValue();\n                    break;\n                case '@':\n                case '*':\n                case '#':\n                case '.':\n                case ':':\n                    xmode = 'style';\n                    advance();\n                    if (token.id !== '@' || !nexttoken.identifier ||\n                            nexttoken.value !== 'charset' || token.line !== 1 ||\n                            token.from !== 1) {\n                        error(\"A css file should begin with @charset 'UTF-8';\");\n                    }\n                    advance();\n                    if (nexttoken.type !== '(string)' &&\n                            nexttoken.value !== 'UTF-8') {\n                        error(\"A css file should begin with @charset 'UTF-8';\");\n                    }\n                    advance();\n                    advance(';');\n                    styles();\n                    break;\n\n                default:\n                    if (option.adsafe && option.fragment) {\n                        error(\"Expected '{a}' and instead saw '{b}'.\",\n                            nexttoken, '<div>', nexttoken.value);\n                    }\n                    statements('lib');\n                }\n            }\n            advance('(end)');\n        } catch (e) {\n            if (e) {\n                JSLINT.errors.push({\n                    reason    : e.message,\n                    line      : e.line || nexttoken.line,\n                    character : e.character || nexttoken.from\n                }, null);\n            }\n        }\n        return JSLINT.errors.length === 0;\n    };\n\n    function is_array(o) {\n        return Object.prototype.toString.apply(o) === '[object Array]';\n    }\n\n    function to_array(o) {\n        var a = [], k;\n        for (k in o) {\n            if (is_own(o, k)) {\n                a.push(k);\n            }\n        }\n        return a;\n    }\n\n\n// Data summary.\n\n    itself.data = function () {\n\n        var data = {functions: []}, fu, globals, implieds = [], f, i, j,\n            members = [], n, unused = [], v;\n        if (itself.errors.length) {\n            data.errors = itself.errors;\n        }\n\n        if (jsonmode) {\n            data.json = true;\n        }\n\n        for (n in implied) {\n            if (is_own(implied, n)) {\n                implieds.push({\n                    name: n,\n                    line: implied[n]\n                });\n            }\n        }\n        if (implieds.length > 0) {\n            data.implieds = implieds;\n        }\n\n        if (urls.length > 0) {\n            data.urls = urls;\n        }\n\n        globals = to_array(scope);\n        if (globals.length > 0) {\n            data.globals = globals;\n        }\n\n        for (i = 1; i < functions.length; i += 1) {\n            f = functions[i];\n            fu = {};\n            for (j = 0; j < functionicity.length; j += 1) {\n                fu[functionicity[j]] = [];\n            }\n            for (n in f) {\n                if (is_own(f, n) && n.charAt(0) !== '(') {\n                    v = f[n];\n                    if (v === 'unction') {\n                        v = 'unused';\n                    }\n                    if (is_array(fu[v])) {\n                        fu[v].push(n);\n                        if (v === 'unused') {\n                            unused.push({\n                                name: n,\n                                line: f['(line)'],\n                                'function': f['(name)']\n                            });\n                        }\n                    }\n                }\n            }\n            for (j = 0; j < functionicity.length; j += 1) {\n                if (fu[functionicity[j]].length === 0) {\n                    delete fu[functionicity[j]];\n                }\n            }\n            fu.name = f['(name)'];\n            fu.param = f['(params)'];\n            fu.line = f['(line)'];\n            fu.last = f['(last)'];\n            data.functions.push(fu);\n        }\n\n        if (unused.length > 0) {\n            data.unused = unused;\n        }\n\n        members = [];\n        for (n in member) {\n            if (typeof member[n] === 'number') {\n                data.member = member;\n                break;\n            }\n        }\n\n        return data;\n    };\n\n    itself.report = function (option) {\n        var data = itself.data();\n\n        var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s;\n\n        function detail(h, array) {\n            var b, i, singularity;\n            if (array) {\n                o.push('<div><i>' + h + '</i> ');\n                array = array.sort();\n                for (i = 0; i < array.length; i += 1) {\n                    if (array[i] !== singularity) {\n                        singularity = array[i];\n                        o.push((b ? ', ' : '') + singularity);\n                        b = true;\n                    }\n                }\n                o.push('</div>');\n            }\n        }\n\n\n        if (data.errors || data.implieds || data.unused) {\n            err = true;\n            o.push('<div id=errors><i>Error:</i>');\n            if (data.errors) {\n                for (i = 0; i < data.errors.length; i += 1) {\n                    c = data.errors[i];\n                    if (c) {\n                        e = c.evidence || '';\n                        o.push('<p>Problem' + (isFinite(c.line) ? ' at line ' +\n                                c.line + ' character ' + c.character : '') +\n                                ': ' + c.reason.entityify() +\n                                '</p><p class=evidence>' +\n                                (e && (e.length > 80 ? e.slice(0, 77) + '...' :\n                                e).entityify()) + '</p>');\n                    }\n                }\n            }\n\n            if (data.implieds) {\n                s = [];\n                for (i = 0; i < data.implieds.length; i += 1) {\n                    s[i] = '<code>' + data.implieds[i].name + '</code>&nbsp;<i>' +\n                        data.implieds[i].line + '</i>';\n                }\n                o.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>');\n            }\n\n            if (data.unused) {\n                s = [];\n                for (i = 0; i < data.unused.length; i += 1) {\n                    s[i] = '<code><u>' + data.unused[i].name + '</u></code>&nbsp;<i>' +\n                        data.unused[i].line + '</i> <code>' +\n                        data.unused[i]['function'] + '</code>';\n                }\n                o.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>');\n            }\n            if (data.json) {\n                o.push('<p>JSON: bad.</p>');\n            }\n            o.push('</div>');\n        }\n\n        if (!option) {\n\n            o.push('<br><div id=functions>');\n\n            if (data.urls) {\n                detail(\"URLs<br>\", data.urls, '<br>');\n            }\n\n            if (xmode === 'style') {\n                o.push('<p>CSS.</p>');\n            } else if (data.json && !err) {\n                o.push('<p>JSON: good.</p>');\n            } else if (data.globals) {\n                o.push('<div><i>Global</i> ' +\n                        data.globals.sort().join(', ') + '</div>');\n            } else {\n                o.push('<div><i>No new global variables introduced.</i></div>');\n            }\n\n            for (i = 0; i < data.functions.length; i += 1) {\n                f = data.functions[i];\n\n                o.push('<br><div class=function><i>' + f.line + '-' +\n                        f.last + '</i> ' + (f.name || '') + '(' +\n                        (f.param ? f.param.join(', ') : '') + ')</div>');\n                detail('<big><b>Unused</b></big>', f.unused);\n                detail('Closure', f.closure);\n                detail('Variable', f['var']);\n                detail('Exception', f.exception);\n                detail('Outer', f.outer);\n                detail('Global', f.global);\n                detail('Label', f.label);\n            }\n\n            if (data.member) {\n                a = to_array(data.member);\n                if (a.length) {\n                    a = a.sort();\n                    m = '<br><pre id=members>/*members ';\n                    l = 10;\n                    for (i = 0; i < a.length; i += 1) {\n                        k = a[i];\n                        n = k.name();\n                        if (l + n.length > 72) {\n                            o.push(m + '<br>');\n                            m = '    ';\n                            l = 1;\n                        }\n                        l += n.length + 2;\n                        if (data.member[k] === 1) {\n                            n = '<i>' + n + '</i>';\n                        }\n                        if (i < a.length - 1) {\n                            n += ', ';\n                        }\n                        m += n;\n                    }\n                    o.push(m + '<br>*/</pre>');\n                }\n                o.push('</div>');\n            }\n        }\n        return o.join('');\n    };\n    itself.jslint = itself;\n\n    itself.edition = '2010-10-16';\n\n    return itself;\n\n}());\n"
  },
  {
    "path": "lib/jslint/rhino.js",
    "content": "// rhino.js\n// 2009-09-11\n/*\nCopyright (c) 2002 Douglas Crockford  (www.JSLint.com) Rhino Edition\n*/\n\n// This is the Rhino companion to fulljslint.js.\n\n/*global JSLINT */\n/*jslint rhino: true, strict: false */\n\n(function (a) {\n    var e, i, input;\n    if (!a[0]) {\n        print(\"Usage: jslint.js file.js\");\n        quit(1);\n    }\n    input = readFile(a[0]);\n    if (!input) {\n        print(\"jslint: Couldn't open file '\" + a[0] + \"'.\");\n        quit(1);\n    }\n    if (!JSLINT(input, {bitwise: true, eqeqeq: true, immed: true,\n            newcap: true, nomen: true, onevar: true, plusplus: true,\n            regexp: true, rhino: true, undef: true, white: true})) {\n        for (i = 0; i < JSLINT.errors.length; i += 1) {\n            e = JSLINT.errors[i];\n            if (e) {\n                print('Lint at line ' + e.line + ' character ' +\n                        e.character + ': ' + e.reason);\n                print((e.evidence || '').\n                        replace(/^\\s*(\\S*(\\s+\\S+)*)\\s*$/, \"$1\"));\n                print('');\n            }\n        }\n        quit(2);\n    } else {\n        print(\"jslint: No problems found in \" + a[0]);\n        quit();\n    }\n}(arguments));"
  },
  {
    "path": "lib/json/json2.js",
    "content": "/*\n    http://www.JSON.org/json2.js\n    2011-02-23\n\n    Public Domain.\n\n    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n\n    See http://www.JSON.org/js.html\n\n\n    This code should be minified before deployment.\n    See http://javascript.crockford.com/jsmin.html\n\n    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO\n    NOT CONTROL.\n\n\n    This file creates a global JSON object containing two methods: stringify\n    and parse.\n\n        JSON.stringify(value, replacer, space)\n            value       any JavaScript value, usually an object or array.\n\n            replacer    an optional parameter that determines how object\n                        values are stringified for objects. It can be a\n                        function or an array of strings.\n\n            space       an optional parameter that specifies the indentation\n                        of nested structures. If it is omitted, the text will\n                        be packed without extra whitespace. If it is a number,\n                        it will specify the number of spaces to indent at each\n                        level. If it is a string (such as '\\t' or '&nbsp;'),\n                        it contains the characters used to indent at each level.\n\n            This method produces a JSON text from a JavaScript value.\n\n            When an object value is found, if the object contains a toJSON\n            method, its toJSON method will be called and the result will be\n            stringified. A toJSON method does not serialize: it returns the\n            value represented by the name/value pair that should be serialized,\n            or undefined if nothing should be serialized. The toJSON method\n            will be passed the key associated with the value, and this will be\n            bound to the value\n\n            For example, this would serialize Dates as ISO strings.\n\n                Date.prototype.toJSON = function (key) {\n                    function f(n) {\n                        // Format integers to have at least two digits.\n                        return n < 10 ? '0' + n : n;\n                    }\n\n                    return this.getUTCFullYear()   + '-' +\n                         f(this.getUTCMonth() + 1) + '-' +\n                         f(this.getUTCDate())      + 'T' +\n                         f(this.getUTCHours())     + ':' +\n                         f(this.getUTCMinutes())   + ':' +\n                         f(this.getUTCSeconds())   + 'Z';\n                };\n\n            You can provide an optional replacer method. It will be passed the\n            key and value of each member, with this bound to the containing\n            object. The value that is returned from your method will be\n            serialized. If your method returns undefined, then the member will\n            be excluded from the serialization.\n\n            If the replacer parameter is an array of strings, then it will be\n            used to select the members to be serialized. It filters the results\n            such that only members with keys listed in the replacer array are\n            stringified.\n\n            Values that do not have JSON representations, such as undefined or\n            functions, will not be serialized. Such values in objects will be\n            dropped; in arrays they will be replaced with null. You can use\n            a replacer function to replace those with JSON values.\n            JSON.stringify(undefined) returns undefined.\n\n            The optional space parameter produces a stringification of the\n            value that is filled with line breaks and indentation to make it\n            easier to read.\n\n            If the space parameter is a non-empty string, then that string will\n            be used for indentation. If the space parameter is a number, then\n            the indentation will be that many spaces.\n\n            Example:\n\n            text = JSON.stringify(['e', {pluribus: 'unum'}]);\n            // text is '[\"e\",{\"pluribus\":\"unum\"}]'\n\n\n            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\\t');\n            // text is '[\\n\\t\"e\",\\n\\t{\\n\\t\\t\"pluribus\": \"unum\"\\n\\t}\\n]'\n\n            text = JSON.stringify([new Date()], function (key, value) {\n                return this[key] instanceof Date ?\n                    'Date(' + this[key] + ')' : value;\n            });\n            // text is '[\"Date(---current time---)\"]'\n\n\n        JSON.parse(text, reviver)\n            This method parses a JSON text to produce an object or array.\n            It can throw a SyntaxError exception.\n\n            The optional reviver parameter is a function that can filter and\n            transform the results. It receives each of the keys and values,\n            and its return value is used instead of the original value.\n            If it returns what it received, then the structure is not modified.\n            If it returns undefined then the member is deleted.\n\n            Example:\n\n            // Parse the text. Values that look like ISO date strings will\n            // be converted to Date objects.\n\n            myData = JSON.parse(text, function (key, value) {\n                var a;\n                if (typeof value === 'string') {\n                    a =\n/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*)?)Z$/.exec(value);\n                    if (a) {\n                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],\n                            +a[5], +a[6]));\n                    }\n                }\n                return value;\n            });\n\n            myData = JSON.parse('[\"Date(09/09/2001)\"]', function (key, value) {\n                var d;\n                if (typeof value === 'string' &&\n                        value.slice(0, 5) === 'Date(' &&\n                        value.slice(-1) === ')') {\n                    d = new Date(value.slice(5, -1));\n                    if (d) {\n                        return d;\n                    }\n                }\n                return value;\n            });\n\n\n    This is a reference implementation. You are free to copy, modify, or\n    redistribute.\n*/\n\n/*jslint evil: true, strict: false, regexp: false */\n\n/*members \"\", \"\\b\", \"\\t\", \"\\n\", \"\\f\", \"\\r\", \"\\\"\", JSON, \"\\\\\", apply,\n    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,\n    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,\n    lastIndex, length, parse, prototype, push, replace, slice, stringify,\n    test, toJSON, toString, valueOf\n*/\n\n\n// Create a JSON object only if one does not already exist. We create the\n// methods in a closure to avoid creating global variables.\n\nvar JSON;\nif (!JSON) {\n    JSON = {};\n}\n\n(function () {\n    \"use strict\";\n\n    function f(n) {\n        // Format integers to have at least two digits.\n        return n < 10 ? '0' + n : n;\n    }\n\n    if (typeof Date.prototype.toJSON !== 'function') {\n\n        Date.prototype.toJSON = function (key) {\n\n            return isFinite(this.valueOf()) ?\n                this.getUTCFullYear()     + '-' +\n                f(this.getUTCMonth() + 1) + '-' +\n                f(this.getUTCDate())      + 'T' +\n                f(this.getUTCHours())     + ':' +\n                f(this.getUTCMinutes())   + ':' +\n                f(this.getUTCSeconds())   + 'Z' : null;\n        };\n\n        String.prototype.toJSON      =\n            Number.prototype.toJSON  =\n            Boolean.prototype.toJSON = function (key) {\n                return this.valueOf();\n            };\n    }\n\n    var cx = /[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        escapable = /[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        gap,\n        indent,\n        meta = {    // table of character substitutions\n            '\\b': '\\\\b',\n            '\\t': '\\\\t',\n            '\\n': '\\\\n',\n            '\\f': '\\\\f',\n            '\\r': '\\\\r',\n            '\"' : '\\\\\"',\n            '\\\\': '\\\\\\\\'\n        },\n        rep;\n\n\n    function quote(string) {\n\n// If the string contains no control characters, no quote characters, and no\n// backslash characters, then we can safely slap some quotes around it.\n// Otherwise we must also replace the offending characters with safe escape\n// sequences.\n\n        escapable.lastIndex = 0;\n        return escapable.test(string) ? '\"' + string.replace(escapable, function (a) {\n            var c = meta[a];\n            return typeof c === 'string' ? c :\n                '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n        }) + '\"' : '\"' + string + '\"';\n    }\n\n\n    function str(key, holder) {\n\n// Produce a string from holder[key].\n\n        var i,          // The loop counter.\n            k,          // The member key.\n            v,          // The member value.\n            length,\n            mind = gap,\n            partial,\n            value = holder[key];\n\n// If the value has a toJSON method, call it to obtain a replacement value.\n\n        if (value && typeof value === 'object' &&\n                typeof value.toJSON === 'function') {\n            value = value.toJSON(key);\n        }\n\n// If we were called with a replacer function, then call the replacer to\n// obtain a replacement value.\n\n        if (typeof rep === 'function') {\n            value = rep.call(holder, key, value);\n        }\n\n// What happens next depends on the value's type.\n\n        switch (typeof value) {\n        case 'string':\n            return quote(value);\n\n        case 'number':\n\n// JSON numbers must be finite. Encode non-finite numbers as null.\n\n            return isFinite(value) ? String(value) : 'null';\n\n        case 'boolean':\n        case 'null':\n\n// If the value is a boolean or null, convert it to a string. Note:\n// typeof null does not produce 'null'. The case is included here in\n// the remote chance that this gets fixed someday.\n\n            return String(value);\n\n// If the type is 'object', we might be dealing with an object or an array or\n// null.\n\n        case 'object':\n\n// Due to a specification blunder in ECMAScript, typeof null is 'object',\n// so watch out for that case.\n\n            if (!value) {\n                return 'null';\n            }\n\n// Make an array to hold the partial results of stringifying this object value.\n\n            gap += indent;\n            partial = [];\n\n// Is the value an array?\n\n            if (Object.prototype.toString.apply(value) === '[object Array]') {\n\n// The value is an array. Stringify every element. Use null as a placeholder\n// for non-JSON values.\n\n                length = value.length;\n                for (i = 0; i < length; i += 1) {\n                    partial[i] = str(i, value) || 'null';\n                }\n\n// Join all of the elements together, separated with commas, and wrap them in\n// brackets.\n\n                v = partial.length === 0 ? '[]' : gap ?\n                    '[\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + ']' :\n                    '[' + partial.join(',') + ']';\n                gap = mind;\n                return v;\n            }\n\n// If the replacer is an array, use it to select the members to be stringified.\n\n            if (rep && typeof rep === 'object') {\n                length = rep.length;\n                for (i = 0; i < length; i += 1) {\n                    if (typeof rep[i] === 'string') {\n                        k = rep[i];\n                        v = str(k, value);\n                        if (v) {\n                            partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                        }\n                    }\n                }\n            } else {\n\n// Otherwise, iterate through all of the keys in the object.\n\n                for (k in value) {\n                    if (Object.prototype.hasOwnProperty.call(value, k)) {\n                        v = str(k, value);\n                        if (v) {\n                            partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                        }\n                    }\n                }\n            }\n\n// Join all of the member texts together, separated with commas,\n// and wrap them in braces.\n\n            v = partial.length === 0 ? '{}' : gap ?\n                '{\\n' + gap + partial.join(',\\n' + gap) + '\\n' + mind + '}' :\n                '{' + partial.join(',') + '}';\n            gap = mind;\n            return v;\n        }\n    }\n\n// If the JSON object does not yet have a stringify method, give it one.\n\n    if (typeof JSON.stringify !== 'function') {\n        JSON.stringify = function (value, replacer, space) {\n\n// The stringify method takes a value and an optional replacer, and an optional\n// space parameter, and returns a JSON text. The replacer can be a function\n// that can replace values, or an array of strings that will select the keys.\n// A default replacer method can be provided. Use of the space parameter can\n// produce text that is more easily readable.\n\n            var i;\n            gap = '';\n            indent = '';\n\n// If the space parameter is a number, make an indent string containing that\n// many spaces.\n\n            if (typeof space === 'number') {\n                for (i = 0; i < space; i += 1) {\n                    indent += ' ';\n                }\n\n// If the space parameter is a string, it will be used as the indent string.\n\n            } else if (typeof space === 'string') {\n                indent = space;\n            }\n\n// If there is a replacer, it must be a function or an array.\n// Otherwise, throw an error.\n\n            rep = replacer;\n            if (replacer && typeof replacer !== 'function' &&\n                    (typeof replacer !== 'object' ||\n                    typeof replacer.length !== 'number')) {\n                throw new Error('JSON.stringify');\n            }\n\n// Make a fake root object containing our value under the key of ''.\n// Return the result of stringifying the value.\n\n            return str('', {'': value});\n        };\n    }\n\n\n// If the JSON object does not yet have a parse method, give it one.\n\n    if (typeof JSON.parse !== 'function') {\n        JSON.parse = function (text, reviver) {\n\n// The parse method takes a text and an optional reviver function, and returns\n// a JavaScript value if the text is a valid JSON text.\n\n            var j;\n\n            function walk(holder, key) {\n\n// The walk method is used to recursively walk the resulting structure so\n// that modifications can be made.\n\n                var k, v, value = holder[key];\n                if (value && typeof value === 'object') {\n                    for (k in value) {\n                        if (Object.prototype.hasOwnProperty.call(value, k)) {\n                            v = walk(value, k);\n                            if (v !== undefined) {\n                                value[k] = v;\n                            } else {\n                                delete value[k];\n                            }\n                        }\n                    }\n                }\n                return reviver.call(holder, key, value);\n            }\n\n\n// Parsing happens in four stages. In the first stage, we replace certain\n// Unicode characters with escape sequences. JavaScript handles many characters\n// incorrectly, either silently deleting them, or treating them as line endings.\n\n            text = String(text);\n            cx.lastIndex = 0;\n            if (cx.test(text)) {\n                text = text.replace(cx, function (a) {\n                    return '\\\\u' +\n                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n                });\n            }\n\n// In the second stage, we run the text against regular expressions that look\n// for non-JSON patterns. We are especially concerned with '()' and 'new'\n// because they can cause invocation, and '=' because it can cause mutation.\n// But just to be safe, we want to reject all unexpected forms.\n\n// We split the second stage into 4 regexp operations in order to work around\n// crippling inefficiencies in IE's and Safari's regexp engines. First we\n// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we\n// replace all simple value tokens with ']' characters. Third, we delete all\n// open brackets that follow a colon or comma or that begin the text. Finally,\n// we look to see that the remaining characters are only whitespace or ']' or\n// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.\n\n            if (/^[\\],:{}\\s]*$/\n                    .test(text.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')\n                        .replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']')\n                        .replace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n\n// In the third stage we use the eval function to compile the text into a\n// JavaScript structure. The '{' operator is subject to a syntactic ambiguity\n// in JavaScript: it can begin a block or an object literal. We wrap the text\n// in parens to eliminate the ambiguity.\n\n                j = eval('(' + text + ')');\n\n// In the optional fourth stage, we recursively walk the new structure, passing\n// each name/value pair to a reviver function for possible transformation.\n\n                return typeof reviver === 'function' ?\n                    walk({'': j}, '') : j;\n            }\n\n// If the text is not JSON parseable, then a SyntaxError is thrown.\n\n            throw new SyntaxError('JSON.parse');\n        };\n    }\n}());\n"
  },
  {
    "path": "lib/qunit/MIT-LICENSE.txt",
    "content": "Copyright (c) 2010 John Resig, http://jquery.com\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "lib/qunit/download.url",
    "content": "[InternetShortcut]\r\nURL=http://github.com/jquery/qunit/\r\nIDList=\r\nHotKey=0\r\n[{000214A0-0000-0000-C000-000000000046}]\r\nProp3=19,2\r\n"
  },
  {
    "path": "lib/qunit/qunit.css",
    "content": "/**\n * QUnit - A JavaScript Unit Testing Framework\n *\n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2011 John Resig, Jörn Zaefferer\n * Dual licensed under the MIT (MIT-LICENSE.txt)\n * or GPL (GPL-LICENSE.txt) licenses.\n */\n\n/** Font Family and Sizes */\n\n#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {\n\tfont-family: \"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial, sans-serif;\n}\n\n#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }\n#qunit-tests { font-size: smaller; }\n\n\n/** Resets */\n\n#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {\n\tmargin: 0;\n\tpadding: 0;\n}\n\n\n/** Header */\n\n#qunit-header {\n\tpadding: 0.5em 0 0.5em 1em;\n\n\tcolor: #8699a4;\n\tbackground-color: #0d3349;\n\n\tfont-size: 1.5em;\n\tline-height: 1em;\n\tfont-weight: normal;\n\n\tborder-radius: 15px 15px 0 0;\n\t-moz-border-radius: 15px 15px 0 0;\n\t-webkit-border-top-right-radius: 15px;\n\t-webkit-border-top-left-radius: 15px;\n}\n\n#qunit-header a {\n\ttext-decoration: none;\n\tcolor: #c2ccd1;\n}\n\n#qunit-header a:hover,\n#qunit-header a:focus {\n\tcolor: #fff;\n}\n\n#qunit-banner {\n\theight: 5px;\n}\n\n#qunit-testrunner-toolbar {\n\tpadding: 0.5em 0 0.5em 2em;\n\tcolor: #5E740B;\n\tbackground-color: #eee;\n}\n\n#qunit-userAgent {\n\tpadding: 0.5em 0 0.5em 2.5em;\n\tbackground-color: #2b81af;\n\tcolor: #fff;\n\ttext-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;\n}\n\n\n/** Tests: Pass/Fail */\n\n#qunit-tests {\n\tlist-style-position: inside;\n}\n\n#qunit-tests li {\n\tpadding: 0.4em 0.5em 0.4em 2.5em;\n\tborder-bottom: 1px solid #fff;\n\tlist-style-position: inside;\n}\n\n#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  {\n\tdisplay: none;\n}\n\n#qunit-tests li strong {\n\tcursor: pointer;\n}\n\n#qunit-tests li a {\n\tpadding: 0.5em;\n\tcolor: #c2ccd1;\n\ttext-decoration: none;\n}\n#qunit-tests li a:hover,\n#qunit-tests li a:focus {\n\tcolor: #000;\n}\n\n#qunit-tests ol {\n\tmargin-top: 0.5em;\n\tpadding: 0.5em;\n\n\tbackground-color: #fff;\n\n\tborder-radius: 15px;\n\t-moz-border-radius: 15px;\n\t-webkit-border-radius: 15px;\n\n\tbox-shadow: inset 0px 2px 13px #999;\n\t-moz-box-shadow: inset 0px 2px 13px #999;\n\t-webkit-box-shadow: inset 0px 2px 13px #999;\n}\n\n#qunit-tests table {\n\tborder-collapse: collapse;\n\tmargin-top: .2em;\n}\n\n#qunit-tests th {\n\ttext-align: right;\n\tvertical-align: top;\n\tpadding: 0 .5em 0 0;\n}\n\n#qunit-tests td {\n\tvertical-align: top;\n}\n\n#qunit-tests pre {\n\tmargin: 0;\n\twhite-space: pre-wrap;\n\tword-wrap: break-word;\n}\n\n#qunit-tests del {\n\tbackground-color: #e0f2be;\n\tcolor: #374e0c;\n\ttext-decoration: none;\n}\n\n#qunit-tests ins {\n\tbackground-color: #ffcaca;\n\tcolor: #500;\n\ttext-decoration: none;\n}\n\n/*** Test Counts */\n\n#qunit-tests b.counts                       { color: black; }\n#qunit-tests b.passed                       { color: #5E740B; }\n#qunit-tests b.failed                       { color: #710909; }\n\n#qunit-tests li li {\n\tmargin: 0.5em;\n\tpadding: 0.4em 0.5em 0.4em 0.5em;\n\tbackground-color: #fff;\n\tborder-bottom: none;\n\tlist-style-position: inside;\n}\n\n/*** Passing Styles */\n\n#qunit-tests li li.pass {\n\tcolor: #5E740B;\n\tbackground-color: #fff;\n\tborder-left: 26px solid #C6E746;\n}\n\n#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }\n#qunit-tests .pass .test-name               { color: #366097; }\n\n#qunit-tests .pass .test-actual,\n#qunit-tests .pass .test-expected           { color: #999999; }\n\n#qunit-banner.qunit-pass                    { background-color: #C6E746; }\n\n/*** Failing Styles */\n\n#qunit-tests li li.fail {\n\tcolor: #710909;\n\tbackground-color: #fff;\n\tborder-left: 26px solid #EE5757;\n\twhite-space: pre;\n}\n\n#qunit-tests > li:last-child {\n\tborder-radius: 0 0 15px 15px;\n\t-moz-border-radius: 0 0 15px 15px;\n\t-webkit-border-bottom-right-radius: 15px;\n\t-webkit-border-bottom-left-radius: 15px;\n}\n\n#qunit-tests .fail                          { color: #000000; background-color: #EE5757; }\n#qunit-tests .fail .test-name,\n#qunit-tests .fail .module-name             { color: #000000; }\n\n#qunit-tests .fail .test-actual             { color: #EE5757; }\n#qunit-tests .fail .test-expected           { color: green;   }\n\n#qunit-banner.qunit-fail                    { background-color: #EE5757; }\n\n\n/** Result */\n\n#qunit-testresult {\n\tpadding: 0.5em 0.5em 0.5em 2.5em;\n\n\tcolor: #2b81af;\n\tbackground-color: #D2E0E6;\n\n\tborder-bottom: 1px solid white;\n}\n\n/** Fixture */\n\n#qunit-fixture {\n\tposition: absolute;\n\ttop: -10000px;\n\tleft: -10000px;\n}\n"
  },
  {
    "path": "lib/qunit/qunit.js",
    "content": "/**\n * QUnit - A JavaScript Unit Testing Framework\n *\n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2011 John Resig, Jörn Zaefferer\n * Dual licensed under the MIT (MIT-LICENSE.txt)\n * or GPL (GPL-LICENSE.txt) licenses.\n */\n\n(function(window) {\n\nvar defined = {\n\tsetTimeout: typeof window.setTimeout !== \"undefined\",\n\tsessionStorage: (function() {\n\t\ttry {\n\t\t\treturn !!sessionStorage.getItem;\n\t\t} catch(e) {\n\t\t\treturn false;\n\t\t}\n\t})()\n};\n\nvar testId = 0;\n\nvar Test = function(name, testName, expected, testEnvironmentArg, async, callback) {\n\tthis.name = name;\n\tthis.testName = testName;\n\tthis.expected = expected;\n\tthis.testEnvironmentArg = testEnvironmentArg;\n\tthis.async = async;\n\tthis.callback = callback;\n\tthis.assertions = [];\n};\nTest.prototype = {\n\tinit: function() {\n\t\tvar tests = id(\"qunit-tests\");\n\t\tif (tests) {\n\t\t\tvar b = document.createElement(\"strong\");\n\t\t\t\tb.innerHTML = \"Running \" + this.name;\n\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\tli.appendChild( b );\n\t\t\t\tli.className = \"running\";\n\t\t\t\tli.id = this.id = \"test-output\" + testId++;\n\t\t\ttests.appendChild( li );\n\t\t}\n\t},\n\tsetup: function() {\n\t\tif (this.module != config.previousModule) {\n\t\t\tif ( config.previousModule ) {\n\t\t\t\trunLoggingCallbacks('moduleDone', QUnit, {\n\t\t\t\t\tname: config.previousModule,\n\t\t\t\t\tfailed: config.moduleStats.bad,\n\t\t\t\t\tpassed: config.moduleStats.all - config.moduleStats.bad,\n\t\t\t\t\ttotal: config.moduleStats.all\n\t\t\t\t} );\n\t\t\t}\n\t\t\tconfig.previousModule = this.module;\n\t\t\tconfig.moduleStats = { all: 0, bad: 0 };\n\t\t\trunLoggingCallbacks( 'moduleStart', QUnit, {\n\t\t\t\tname: this.module\n\t\t\t} );\n\t\t}\n\n\t\tconfig.current = this;\n\t\tthis.testEnvironment = extend({\n\t\t\tsetup: function() {},\n\t\t\tteardown: function() {}\n\t\t}, this.moduleTestEnvironment);\n\t\tif (this.testEnvironmentArg) {\n\t\t\textend(this.testEnvironment, this.testEnvironmentArg);\n\t\t}\n\n\t\trunLoggingCallbacks( 'testStart', QUnit, {\n\t\t\tname: this.testName,\n\t\t\tmodule: this.module\n\t\t});\n\n\t\t// allow utility functions to access the current test environment\n\t\t// TODO why??\n\t\tQUnit.current_testEnvironment = this.testEnvironment;\n\n\t\ttry {\n\t\t\tif ( !config.pollution ) {\n\t\t\t\tsaveGlobal();\n\t\t\t}\n\n\t\t\tthis.testEnvironment.setup.call(this.testEnvironment);\n\t\t} catch(e) {\n\t\t\tQUnit.ok( false, \"Setup failed on \" + this.testName + \": \" + e.message );\n\t\t}\n\t},\n\trun: function() {\n\t\tif ( this.async ) {\n\t\t\tQUnit.stop();\n\t\t}\n\n\t\tif ( config.notrycatch ) {\n\t\t\tthis.callback.call(this.testEnvironment);\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tthis.callback.call(this.testEnvironment);\n\t\t} catch(e) {\n\t\t\tfail(\"Test \" + this.testName + \" died, exception and test follows\", e, this.callback);\n\t\t\tQUnit.ok( false, \"Died on test #\" + (this.assertions.length + 1) + \": \" + e.message + \" - \" + QUnit.jsDump.parse(e) );\n\t\t\t// else next test will carry the responsibility\n\t\t\tsaveGlobal();\n\n\t\t\t// Restart the tests if they're blocking\n\t\t\tif ( config.blocking ) {\n\t\t\t\tstart();\n\t\t\t}\n\t\t}\n\t},\n\tteardown: function() {\n\t\ttry {\n\t\t\tthis.testEnvironment.teardown.call(this.testEnvironment);\n\t\t\tcheckPollution();\n\t\t} catch(e) {\n\t\t\tQUnit.ok( false, \"Teardown failed on \" + this.testName + \": \" + e.message );\n\t\t}\n\t},\n\tfinish: function() {\n\t\tif ( this.expected && this.expected != this.assertions.length ) {\n\t\t\tQUnit.ok( false, \"Expected \" + this.expected + \" assertions, but \" + this.assertions.length + \" were run\" );\n\t\t}\n\n\t\tvar good = 0, bad = 0,\n\t\t\ttests = id(\"qunit-tests\");\n\n\t\tconfig.stats.all += this.assertions.length;\n\t\tconfig.moduleStats.all += this.assertions.length;\n\n\t\tif ( tests ) {\n\t\t\tvar ol = document.createElement(\"ol\");\n\n\t\t\tfor ( var i = 0; i < this.assertions.length; i++ ) {\n\t\t\t\tvar assertion = this.assertions[i];\n\n\t\t\t\tvar li = document.createElement(\"li\");\n\t\t\t\tli.className = assertion.result ? \"pass\" : \"fail\";\n\t\t\t\tli.innerHTML = assertion.message || (assertion.result ? \"okay\" : \"failed\");\n\t\t\t\tol.appendChild( li );\n\n\t\t\t\tif ( assertion.result ) {\n\t\t\t\t\tgood++;\n\t\t\t\t} else {\n\t\t\t\t\tbad++;\n\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// store result when possible\n\t\t\tif ( QUnit.config.reorder && defined.sessionStorage ) {\n\t\t\t\tif (bad) {\n\t\t\t\t\tsessionStorage.setItem(\"qunit-\" + this.module + \"-\" + this.testName, bad);\n\t\t\t\t} else {\n\t\t\t\t\tsessionStorage.removeItem(\"qunit-\" + this.module + \"-\" + this.testName);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (bad == 0) {\n\t\t\t\tol.style.display = \"none\";\n\t\t\t}\n\n\t\t\tvar b = document.createElement(\"strong\");\n\t\t\tb.innerHTML = this.name + \" <b class='counts'>(<b class='failed'>\" + bad + \"</b>, <b class='passed'>\" + good + \"</b>, \" + this.assertions.length + \")</b>\";\n\n\t\t\tvar a = document.createElement(\"a\");\n\t\t\ta.innerHTML = \"Rerun\";\n\t\t\ta.href = QUnit.url({ filter: getText([b]).replace(/\\([^)]+\\)$/, \"\").replace(/(^\\s*|\\s*$)/g, \"\") });\n\n\t\t\taddEvent(b, \"click\", function() {\n\t\t\t\tvar next = b.nextSibling.nextSibling,\n\t\t\t\t\tdisplay = next.style.display;\n\t\t\t\tnext.style.display = display === \"none\" ? \"block\" : \"none\";\n\t\t\t});\n\n\t\t\taddEvent(b, \"dblclick\", function(e) {\n\t\t\t\tvar target = e && e.target ? e.target : window.event.srcElement;\n\t\t\t\tif ( target.nodeName.toLowerCase() == \"span\" || target.nodeName.toLowerCase() == \"b\" ) {\n\t\t\t\t\ttarget = target.parentNode;\n\t\t\t\t}\n\t\t\t\tif ( window.location && target.nodeName.toLowerCase() === \"strong\" ) {\n\t\t\t\t\twindow.location = QUnit.url({ filter: getText([target]).replace(/\\([^)]+\\)$/, \"\").replace(/(^\\s*|\\s*$)/g, \"\") });\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tvar li = id(this.id);\n\t\t\tli.className = bad ? \"fail\" : \"pass\";\n\t\t\tli.removeChild( li.firstChild );\n\t\t\tli.appendChild( b );\n\t\t\tli.appendChild( a );\n\t\t\tli.appendChild( ol );\n\n\t\t} else {\n\t\t\tfor ( var i = 0; i < this.assertions.length; i++ ) {\n\t\t\t\tif ( !this.assertions[i].result ) {\n\t\t\t\t\tbad++;\n\t\t\t\t\tconfig.stats.bad++;\n\t\t\t\t\tconfig.moduleStats.bad++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tQUnit.reset();\n\t\t} catch(e) {\n\t\t\tfail(\"reset() failed, following Test \" + this.testName + \", exception and reset fn follows\", e, QUnit.reset);\n\t\t}\n\n\t\trunLoggingCallbacks( 'testDone', QUnit, {\n\t\t\tname: this.testName,\n\t\t\tmodule: this.module,\n\t\t\tfailed: bad,\n\t\t\tpassed: this.assertions.length - bad,\n\t\t\ttotal: this.assertions.length\n\t\t} );\n\t},\n\n\tqueue: function() {\n\t\tvar test = this;\n\t\tsynchronize(function() {\n\t\t\ttest.init();\n\t\t});\n\t\tfunction run() {\n\t\t\t// each of these can by async\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.setup();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.run();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.teardown();\n\t\t\t});\n\t\t\tsynchronize(function() {\n\t\t\t\ttest.finish();\n\t\t\t});\n\t\t}\n\t\t// defer when previous test run passed, if storage is available\n\t\tvar bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem(\"qunit-\" + this.module + \"-\" + this.testName);\n\t\tif (bad) {\n\t\t\trun();\n\t\t} else {\n\t\t\tsynchronize(run);\n\t\t};\n\t}\n\n};\n\nvar QUnit = {\n\n\t// call on start of module test to prepend name to all tests\n\tmodule: function(name, testEnvironment) {\n\t\tconfig.currentModule = name;\n\t\tconfig.currentModuleTestEnviroment = testEnvironment;\n\t},\n\n\tasyncTest: function(testName, expected, callback) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = 0;\n\t\t}\n\n\t\tQUnit.test(testName, expected, callback, true);\n\t},\n\n\ttest: function(testName, expected, callback, async) {\n\t\tvar name = '<span class=\"test-name\">' + testName + '</span>', testEnvironmentArg;\n\n\t\tif ( arguments.length === 2 ) {\n\t\t\tcallback = expected;\n\t\t\texpected = null;\n\t\t}\n\t\t// is 2nd argument a testEnvironment?\n\t\tif ( expected && typeof expected === 'object') {\n\t\t\ttestEnvironmentArg = expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\tif ( config.currentModule ) {\n\t\t\tname = '<span class=\"module-name\">' + config.currentModule + \"</span>: \" + name;\n\t\t}\n\n\t\tif ( !validTest(config.currentModule + \": \" + testName) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar test = new Test(name, testName, expected, testEnvironmentArg, async, callback);\n\t\ttest.module = config.currentModule;\n\t\ttest.moduleTestEnvironment = config.currentModuleTestEnviroment;\n\t\ttest.queue();\n\t},\n\n\t/**\n\t * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.\n\t */\n\texpect: function(asserts) {\n\t\tconfig.current.expected = asserts;\n\t},\n\n\t/**\n\t * Asserts true.\n\t * @example ok( \"asdfasdf\".length > 5, \"There must be at least 5 chars\" );\n\t */\n\tok: function(a, msg) {\n\t\ta = !!a;\n\t\tvar details = {\n\t\t\tresult: a,\n\t\t\tmessage: msg\n\t\t};\n\t\tmsg = escapeInnerText(msg);\n\t\trunLoggingCallbacks( 'log', QUnit, details );\n\t\tconfig.current.assertions.push({\n\t\t\tresult: a,\n\t\t\tmessage: msg\n\t\t});\n\t},\n\n\t/**\n\t * Checks that the first two arguments are equal, with an optional message.\n\t * Prints out both actual and expected values.\n\t *\n\t * Prefered to ok( actual == expected, message )\n\t *\n\t * @example equal( format(\"Received {0} bytes.\", 2), \"Received 2 bytes.\" );\n\t *\n\t * @param Object actual\n\t * @param Object expected\n\t * @param String message (optional)\n\t */\n\tequal: function(actual, expected, message) {\n\t\tQUnit.push(expected == actual, actual, expected, message);\n\t},\n\n\tnotEqual: function(actual, expected, message) {\n\t\tQUnit.push(expected != actual, actual, expected, message);\n\t},\n\n\tdeepEqual: function(actual, expected, message) {\n\t\tQUnit.push(QUnit.equiv(actual, expected), actual, expected, message);\n\t},\n\n\tnotDeepEqual: function(actual, expected, message) {\n\t\tQUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);\n\t},\n\n\tstrictEqual: function(actual, expected, message) {\n\t\tQUnit.push(expected === actual, actual, expected, message);\n\t},\n\n\tnotStrictEqual: function(actual, expected, message) {\n\t\tQUnit.push(expected !== actual, actual, expected, message);\n\t},\n\n\traises: function(block, expected, message) {\n\t\tvar actual, ok = false;\n\n\t\tif (typeof expected === 'string') {\n\t\t\tmessage = expected;\n\t\t\texpected = null;\n\t\t}\n\n\t\ttry {\n\t\t\tblock();\n\t\t} catch (e) {\n\t\t\tactual = e;\n\t\t}\n\n\t\tif (actual) {\n\t\t\t// we don't want to validate thrown error\n\t\t\tif (!expected) {\n\t\t\t\tok = true;\n\t\t\t// expected is a regexp\n\t\t\t} else if (QUnit.objectType(expected) === \"regexp\") {\n\t\t\t\tok = expected.test(actual);\n\t\t\t// expected is a constructor\n\t\t\t} else if (actual instanceof expected) {\n\t\t\t\tok = true;\n\t\t\t// expected is a validation function which returns true is validation passed\n\t\t\t} else if (expected.call({}, actual) === true) {\n\t\t\t\tok = true;\n\t\t\t}\n\t\t}\n\n\t\tQUnit.ok(ok, message);\n\t},\n\n\tstart: function() {\n\t\tconfig.semaphore--;\n\t\tif (config.semaphore > 0) {\n\t\t\t// don't start until equal number of stop-calls\n\t\t\treturn;\n\t\t}\n\t\tif (config.semaphore < 0) {\n\t\t\t// ignore if start is called more often then stop\n\t\t\tconfig.semaphore = 0;\n\t\t}\n\t\t// A slight delay, to avoid any current callbacks\n\t\tif ( defined.setTimeout ) {\n\t\t\twindow.setTimeout(function() {\n\t\t\t\tif (config.semaphore > 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif ( config.timeout ) {\n\t\t\t\t\tclearTimeout(config.timeout);\n\t\t\t\t}\n\n\t\t\t\tconfig.blocking = false;\n\t\t\t\tprocess();\n\t\t\t}, 13);\n\t\t} else {\n\t\t\tconfig.blocking = false;\n\t\t\tprocess();\n\t\t}\n\t},\n\n\tstop: function(timeout) {\n\t\tconfig.semaphore++;\n\t\tconfig.blocking = true;\n\n\t\tif ( timeout && defined.setTimeout ) {\n\t\t\tclearTimeout(config.timeout);\n\t\t\tconfig.timeout = window.setTimeout(function() {\n\t\t\t\tQUnit.ok( false, \"Test timed out\" );\n\t\t\t\tQUnit.start();\n\t\t\t}, timeout);\n\t\t}\n\t}\n};\n\n//We want access to the constructor's prototype\n(function() {\n\tfunction F(){};\n\tF.prototype = QUnit;\n\tQUnit = new F();\n\t//Make F QUnit's constructor so that we can add to the prototype later\n\tQUnit.constructor = F;\n})();\n\n// Backwards compatibility, deprecated\nQUnit.equals = QUnit.equal;\nQUnit.same = QUnit.deepEqual;\n\n// Maintain internal state\nvar config = {\n\t// The queue of tests to run\n\tqueue: [],\n\n\t// block until document ready\n\tblocking: true,\n\n\t// when enabled, show only failing tests\n\t// gets persisted through sessionStorage and can be changed in UI via checkbox\n\thidepassed: false,\n\n\t// by default, run previously failed tests first\n\t// very useful in combination with \"Hide passed tests\" checked\n\treorder: true,\n\n\t// by default, modify document.title when suite is done\n\taltertitle: true,\n\n\turlConfig: ['noglobals', 'notrycatch'],\n\n\t//logging callback queues\n\tbegin: [],\n\tdone: [],\n\tlog: [],\n\ttestStart: [],\n\ttestDone: [],\n\tmoduleStart: [],\n\tmoduleDone: []\n};\n\n// Load paramaters\n(function() {\n\tvar location = window.location || { search: \"\", protocol: \"file:\" },\n\t\tparams = location.search.slice( 1 ).split( \"&\" ),\n\t\tlength = params.length,\n\t\turlParams = {},\n\t\tcurrent;\n\n\tif ( params[ 0 ] ) {\n\t\tfor ( var i = 0; i < length; i++ ) {\n\t\t\tcurrent = params[ i ].split( \"=\" );\n\t\t\tcurrent[ 0 ] = decodeURIComponent( current[ 0 ] );\n\t\t\t// allow just a key to turn on a flag, e.g., test.html?noglobals\n\t\t\tcurrent[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;\n\t\t\turlParams[ current[ 0 ] ] = current[ 1 ];\n\t\t}\n\t}\n\n\tQUnit.urlParams = urlParams;\n\tconfig.filter = urlParams.filter;\n\n\t// Figure out if we're running the tests from a server or not\n\tQUnit.isLocal = !!(location.protocol === 'file:');\n})();\n\n// Expose the API as global variables, unless an 'exports'\n// object exists, in that case we assume we're in CommonJS\nif ( typeof exports === \"undefined\" || typeof require === \"undefined\" ) {\n\textend(window, QUnit);\n\twindow.QUnit = QUnit;\n} else {\n\textend(exports, QUnit);\n\texports.QUnit = QUnit;\n}\n\n// define these after exposing globals to keep them in these QUnit namespace only\nextend(QUnit, {\n\tconfig: config,\n\n\t// Initialize the configuration options\n\tinit: function() {\n\t\textend(config, {\n\t\t\tstats: { all: 0, bad: 0 },\n\t\t\tmoduleStats: { all: 0, bad: 0 },\n\t\t\tstarted: +new Date,\n\t\t\tupdateRate: 1000,\n\t\t\tblocking: false,\n\t\t\tautostart: true,\n\t\t\tautorun: false,\n\t\t\tfilter: \"\",\n\t\t\tqueue: [],\n\t\t\tsemaphore: 0\n\t\t});\n\n\t\tvar tests = id( \"qunit-tests\" ),\n\t\t\tbanner = id( \"qunit-banner\" ),\n\t\t\tresult = id( \"qunit-testresult\" );\n\n\t\tif ( tests ) {\n\t\t\ttests.innerHTML = \"\";\n\t\t}\n\n\t\tif ( banner ) {\n\t\t\tbanner.className = \"\";\n\t\t}\n\n\t\tif ( result ) {\n\t\t\tresult.parentNode.removeChild( result );\n\t\t}\n\n\t\tif ( tests ) {\n\t\t\tresult = document.createElement( \"p\" );\n\t\t\tresult.id = \"qunit-testresult\";\n\t\t\tresult.className = \"result\";\n\t\t\ttests.parentNode.insertBefore( result, tests );\n\t\t\tresult.innerHTML = 'Running...<br/>&nbsp;';\n\t\t}\n\t},\n\n\t/**\n\t * Resets the test setup. Useful for tests that modify the DOM.\n\t *\n\t * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.\n\t */\n\treset: function() {\n\t\tif ( window.jQuery ) {\n\t\t\tjQuery( \"#qunit-fixture\" ).html( config.fixture );\n\t\t} else {\n\t\t\tvar main = id( 'qunit-fixture' );\n\t\t\tif ( main ) {\n\t\t\t\tmain.innerHTML = config.fixture;\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Trigger an event on an element.\n\t *\n\t * @example triggerEvent( document.body, \"click\" );\n\t *\n\t * @param DOMElement elem\n\t * @param String type\n\t */\n\ttriggerEvent: function( elem, type, event ) {\n\t\tif ( document.createEvent ) {\n\t\t\tevent = document.createEvent(\"MouseEvents\");\n\t\t\tevent.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,\n\t\t\t\t0, 0, 0, 0, 0, false, false, false, false, 0, null);\n\t\t\telem.dispatchEvent( event );\n\n\t\t} else if ( elem.fireEvent ) {\n\t\t\telem.fireEvent(\"on\"+type);\n\t\t}\n\t},\n\n\t// Safe object type checking\n\tis: function( type, obj ) {\n\t\treturn QUnit.objectType( obj ) == type;\n\t},\n\n\tobjectType: function( obj ) {\n\t\tif (typeof obj === \"undefined\") {\n\t\t\t\treturn \"undefined\";\n\n\t\t// consider: typeof null === object\n\t\t}\n\t\tif (obj === null) {\n\t\t\t\treturn \"null\";\n\t\t}\n\n\t\tvar type = Object.prototype.toString.call( obj )\n\t\t\t.match(/^\\[object\\s(.*)\\]$/)[1] || '';\n\n\t\tswitch (type) {\n\t\t\t\tcase 'Number':\n\t\t\t\t\t\tif (isNaN(obj)) {\n\t\t\t\t\t\t\t\treturn \"nan\";\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treturn \"number\";\n\t\t\t\t\t\t}\n\t\t\t\tcase 'String':\n\t\t\t\tcase 'Boolean':\n\t\t\t\tcase 'Array':\n\t\t\t\tcase 'Date':\n\t\t\t\tcase 'RegExp':\n\t\t\t\tcase 'Function':\n\t\t\t\t\t\treturn type.toLowerCase();\n\t\t}\n\t\tif (typeof obj === \"object\") {\n\t\t\t\treturn \"object\";\n\t\t}\n\t\treturn undefined;\n\t},\n\n\tpush: function(result, actual, expected, message) {\n\t\tvar details = {\n\t\t\tresult: result,\n\t\t\tmessage: message,\n\t\t\tactual: actual,\n\t\t\texpected: expected\n\t\t};\n\n\t\tmessage = escapeInnerText(message) || (result ? \"okay\" : \"failed\");\n\t\tmessage = '<span class=\"test-message\">' + message + \"</span>\";\n\t\texpected = escapeInnerText(QUnit.jsDump.parse(expected));\n\t\tactual = escapeInnerText(QUnit.jsDump.parse(actual));\n\t\tvar output = message + '<table><tr class=\"test-expected\"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';\n\t\tif (actual != expected) {\n\t\t\toutput += '<tr class=\"test-actual\"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';\n\t\t\toutput += '<tr class=\"test-diff\"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';\n\t\t}\n\t\tif (!result) {\n\t\t\tvar source = sourceFromStacktrace();\n\t\t\tif (source) {\n\t\t\t\tdetails.source = source;\n\t\t\t\toutput += '<tr class=\"test-source\"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';\n\t\t\t}\n\t\t}\n\t\toutput += \"</table>\";\n\n\t\trunLoggingCallbacks( 'log', QUnit, details );\n\n\t\tconfig.current.assertions.push({\n\t\t\tresult: !!result,\n\t\t\tmessage: output\n\t\t});\n\t},\n\n\turl: function( params ) {\n\t\tparams = extend( extend( {}, QUnit.urlParams ), params );\n\t\tvar querystring = \"?\",\n\t\t\tkey;\n\t\tfor ( key in params ) {\n\t\t\tquerystring += encodeURIComponent( key ) + \"=\" +\n\t\t\t\tencodeURIComponent( params[ key ] ) + \"&\";\n\t\t}\n\t\treturn window.location.pathname + querystring.slice( 0, -1 );\n\t},\n\n\textend: extend,\n\tid: id,\n\taddEvent: addEvent\n});\n\n//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later\n//Doing this allows us to tell if the following methods have been overwritten on the actual\n//QUnit object, which is a deprecated way of using the callbacks.\nextend(QUnit.constructor.prototype, {\n\t// Logging callbacks; all receive a single argument with the listed properties\n\t// run test/logs.html for any related changes\n\tbegin: registerLoggingCallback('begin'),\n\t// done: { failed, passed, total, runtime }\n\tdone: registerLoggingCallback('done'),\n\t// log: { result, actual, expected, message }\n\tlog: registerLoggingCallback('log'),\n\t// testStart: { name }\n\ttestStart: registerLoggingCallback('testStart'),\n\t// testDone: { name, failed, passed, total }\n\ttestDone: registerLoggingCallback('testDone'),\n\t// moduleStart: { name }\n\tmoduleStart: registerLoggingCallback('moduleStart'),\n\t// moduleDone: { name, failed, passed, total }\n\tmoduleDone: registerLoggingCallback('moduleDone')\n});\n\nif ( typeof document === \"undefined\" || document.readyState === \"complete\" ) {\n\tconfig.autorun = true;\n}\n\nQUnit.load = function() {\n\trunLoggingCallbacks( 'begin', QUnit, {} );\n\n\t// Initialize the config, saving the execution queue\n\tvar oldconfig = extend({}, config);\n\tQUnit.init();\n\textend(config, oldconfig);\n\n\tconfig.blocking = false;\n\n\tvar urlConfigHtml = '', len = config.urlConfig.length;\n\tfor ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) {\n\t\tconfig[val] = QUnit.urlParams[val];\n\t\turlConfigHtml += '<label><input name=\"' + val + '\" type=\"checkbox\"' + ( config[val] ? ' checked=\"checked\"' : '' ) + '>' + val + '</label>';\n\t}\n\n\tvar userAgent = id(\"qunit-userAgent\");\n\tif ( userAgent ) {\n\t\tuserAgent.innerHTML = navigator.userAgent;\n\t}\n\tvar banner = id(\"qunit-header\");\n\tif ( banner ) {\n\t\tbanner.innerHTML = '<a href=\"' + QUnit.url({ filter: undefined }) + '\"> ' + banner.innerHTML + '</a> ' + urlConfigHtml;\n\t\taddEvent( banner, \"change\", function( event ) {\n\t\t\tvar params = {};\n\t\t\tparams[ event.target.name ] = event.target.checked ? true : undefined;\n\t\t\twindow.location = QUnit.url( params );\n\t\t});\n\t}\n\n\tvar toolbar = id(\"qunit-testrunner-toolbar\");\n\tif ( toolbar ) {\n\t\tvar filter = document.createElement(\"input\");\n\t\tfilter.type = \"checkbox\";\n\t\tfilter.id = \"qunit-filter-pass\";\n\t\taddEvent( filter, \"click\", function() {\n\t\t\tvar ol = document.getElementById(\"qunit-tests\");\n\t\t\tif ( filter.checked ) {\n\t\t\t\tol.className = ol.className + \" hidepass\";\n\t\t\t} else {\n\t\t\t\tvar tmp = \" \" + ol.className.replace( /[\\n\\t\\r]/g, \" \" ) + \" \";\n\t\t\t\tol.className = tmp.replace(/ hidepass /, \" \");\n\t\t\t}\n\t\t\tif ( defined.sessionStorage ) {\n\t\t\t\tif (filter.checked) {\n\t\t\t\t\tsessionStorage.setItem(\"qunit-filter-passed-tests\", \"true\");\n\t\t\t\t} else {\n\t\t\t\t\tsessionStorage.removeItem(\"qunit-filter-passed-tests\");\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tif ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem(\"qunit-filter-passed-tests\") ) {\n\t\t\tfilter.checked = true;\n\t\t\tvar ol = document.getElementById(\"qunit-tests\");\n\t\t\tol.className = ol.className + \" hidepass\";\n\t\t}\n\t\ttoolbar.appendChild( filter );\n\n\t\tvar label = document.createElement(\"label\");\n\t\tlabel.setAttribute(\"for\", \"qunit-filter-pass\");\n\t\tlabel.innerHTML = \"Hide passed tests\";\n\t\ttoolbar.appendChild( label );\n\t}\n\n\tvar main = id('qunit-fixture');\n\tif ( main ) {\n\t\tconfig.fixture = main.innerHTML;\n\t}\n\n\tif (config.autostart) {\n\t\tQUnit.start();\n\t}\n};\n\naddEvent(window, \"load\", QUnit.load);\n\nfunction done() {\n\tconfig.autorun = true;\n\n\t// Log the last module results\n\tif ( config.currentModule ) {\n\t\trunLoggingCallbacks( 'moduleDone', QUnit, {\n\t\t\tname: config.currentModule,\n\t\t\tfailed: config.moduleStats.bad,\n\t\t\tpassed: config.moduleStats.all - config.moduleStats.bad,\n\t\t\ttotal: config.moduleStats.all\n\t\t} );\n\t}\n\n\tvar banner = id(\"qunit-banner\"),\n\t\ttests = id(\"qunit-tests\"),\n\t\truntime = +new Date - config.started,\n\t\tpassed = config.stats.all - config.stats.bad,\n\t\thtml = [\n\t\t\t'Tests completed in ',\n\t\t\truntime,\n\t\t\t' milliseconds.<br/>',\n\t\t\t'<span class=\"passed\">',\n\t\t\tpassed,\n\t\t\t'</span> tests of <span class=\"total\">',\n\t\t\tconfig.stats.all,\n\t\t\t'</span> passed, <span class=\"failed\">',\n\t\t\tconfig.stats.bad,\n\t\t\t'</span> failed.'\n\t\t].join('');\n\n\tif ( banner ) {\n\t\tbanner.className = (config.stats.bad ? \"qunit-fail\" : \"qunit-pass\");\n\t}\n\n\tif ( tests ) {\n\t\tid( \"qunit-testresult\" ).innerHTML = html;\n\t}\n\n\tif ( config.altertitle && typeof document !== \"undefined\" && document.title ) {\n\t\t// show ✖ for good, ✔ for bad suite result in title\n\t\t// use escape sequences in case file gets loaded with non-utf-8-charset\n\t\tdocument.title = [\n\t\t\t(config.stats.bad ? \"\\u2716\" : \"\\u2714\"),\n\t\t\tdocument.title.replace(/^[\\u2714\\u2716] /i, \"\")\n\t\t].join(\" \");\n\t}\n\n\trunLoggingCallbacks( 'done', QUnit, {\n\t\tfailed: config.stats.bad,\n\t\tpassed: passed,\n\t\ttotal: config.stats.all,\n\t\truntime: runtime\n\t} );\n}\n\nfunction validTest( name ) {\n\tvar filter = config.filter,\n\t\trun = false;\n\n\tif ( !filter ) {\n\t\treturn true;\n\t}\n\n\tvar not = filter.charAt( 0 ) === \"!\";\n\tif ( not ) {\n\t\tfilter = filter.slice( 1 );\n\t}\n\n\tif ( name.indexOf( filter ) !== -1 ) {\n\t\treturn !not;\n\t}\n\n\tif ( not ) {\n\t\trun = true;\n\t}\n\n\treturn run;\n}\n\n// so far supports only Firefox, Chrome and Opera (buggy)\n// could be extended in the future to use something like https://github.com/csnover/TraceKit\nfunction sourceFromStacktrace() {\n\ttry {\n\t\tthrow new Error();\n\t} catch ( e ) {\n\t\tif (e.stacktrace) {\n\t\t\t// Opera\n\t\t\treturn e.stacktrace.split(\"\\n\")[6];\n\t\t} else if (e.stack) {\n\t\t\t// Firefox, Chrome\n\t\t\treturn e.stack.split(\"\\n\")[4];\n\t\t} else if (e.sourceURL) {\n\t\t\t// Safari, PhantomJS\n\t\t\t// TODO sourceURL points at the 'throw new Error' line above, useless\n\t\t\t//return e.sourceURL + \":\" + e.line;\n\t\t}\n\t}\n}\n\nfunction escapeInnerText(s) {\n\tif (!s) {\n\t\treturn \"\";\n\t}\n\ts = s + \"\";\n\treturn s.replace(/[\\&<>]/g, function(s) {\n\t\tswitch(s) {\n\t\t\tcase \"&\": return \"&amp;\";\n\t\t\tcase \"<\": return \"&lt;\";\n\t\t\tcase \">\": return \"&gt;\";\n\t\t\tdefault: return s;\n\t\t}\n\t});\n}\n\nfunction synchronize( callback ) {\n\tconfig.queue.push( callback );\n\n\tif ( config.autorun && !config.blocking ) {\n\t\tprocess();\n\t}\n}\n\nfunction process() {\n\tvar start = (new Date()).getTime();\n\n\twhile ( config.queue.length && !config.blocking ) {\n\t\tif ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {\n\t\t\tconfig.queue.shift()();\n\t\t} else {\n\t\t\twindow.setTimeout( process, 13 );\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!config.blocking && !config.queue.length) {\n\t\tdone();\n\t}\n}\n\nfunction saveGlobal() {\n\tconfig.pollution = [];\n\n\tif ( config.noglobals ) {\n\t\tfor ( var key in window ) {\n\t\t\tconfig.pollution.push( key );\n\t\t}\n\t}\n}\n\nfunction checkPollution( name ) {\n\tvar old = config.pollution;\n\tsaveGlobal();\n\n\tvar newGlobals = diff( config.pollution, old );\n\tif ( newGlobals.length > 0 ) {\n\t\tok( false, \"Introduced global variable(s): \" + newGlobals.join(\", \") );\n\t}\n\n\tvar deletedGlobals = diff( old, config.pollution );\n\tif ( deletedGlobals.length > 0 ) {\n\t\tok( false, \"Deleted global variable(s): \" + deletedGlobals.join(\", \") );\n\t}\n}\n\n// returns a new Array with the elements that are in a but not in b\nfunction diff( a, b ) {\n\tvar result = a.slice();\n\tfor ( var i = 0; i < result.length; i++ ) {\n\t\tfor ( var j = 0; j < b.length; j++ ) {\n\t\t\tif ( result[i] === b[j] ) {\n\t\t\t\tresult.splice(i, 1);\n\t\t\t\ti--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction fail(message, exception, callback) {\n\tif ( typeof console !== \"undefined\" && console.error && console.warn ) {\n\t\tconsole.error(message);\n\t\tconsole.error(exception);\n\t\tconsole.warn(callback.toString());\n\n\t} else if ( window.opera && opera.postError ) {\n\t\topera.postError(message, exception, callback.toString);\n\t}\n}\n\nfunction extend(a, b) {\n\tfor ( var prop in b ) {\n\t\tif ( b[prop] === undefined ) {\n\t\t\tdelete a[prop];\n\t\t} else {\n\t\t\ta[prop] = b[prop];\n\t\t}\n\t}\n\n\treturn a;\n}\n\nfunction addEvent(elem, type, fn) {\n\tif ( elem.addEventListener ) {\n\t\telem.addEventListener( type, fn, false );\n\t} else if ( elem.attachEvent ) {\n\t\telem.attachEvent( \"on\" + type, fn );\n\t} else {\n\t\tfn();\n\t}\n}\n\nfunction id(name) {\n\treturn !!(typeof document !== \"undefined\" && document && document.getElementById) &&\n\t\tdocument.getElementById( name );\n}\n\nfunction registerLoggingCallback(key){\n\treturn function(callback){\n\t\tconfig[key].push( callback );\n\t};\n}\n\n// Supports deprecated method of completely overwriting logging callbacks\nfunction runLoggingCallbacks(key, scope, args) {\n\t//debugger;\n\tvar callbacks;\n\tif ( QUnit.hasOwnProperty(key) ) {\n\t\tQUnit[key].call(scope, args);\n\t} else {\n\t\tcallbacks = config[key];\n\t\tfor( var i = 0; i < callbacks.length; i++ ) {\n\t\t\tcallbacks[i].call( scope, args );\n\t\t}\n\t}\n}\n\n// Test for equality any JavaScript type.\n// Author: Philippe Rathé <prathe@gmail.com>\nQUnit.equiv = function () {\n\n\tvar innerEquiv; // the real equiv function\n\tvar callers = []; // stack to decide between skip/abort functions\n\tvar parents = []; // stack to avoiding loops from circular referencing\n\n\t// Call the o related callback with the given arguments.\n\tfunction bindCallbacks(o, callbacks, args) {\n\t\tvar prop = QUnit.objectType(o);\n\t\tif (prop) {\n\t\t\tif (QUnit.objectType(callbacks[prop]) === \"function\") {\n\t\t\t\treturn callbacks[prop].apply(callbacks, args);\n\t\t\t} else {\n\t\t\t\treturn callbacks[prop]; // or undefined\n\t\t\t}\n\t\t}\n\t}\n\n\tvar callbacks = function () {\n\n\t\t// for string, boolean, number and null\n\t\tfunction useStrictEquality(b, a) {\n\t\t\tif (b instanceof a.constructor || a instanceof b.constructor) {\n\t\t\t\t// to catch short annotaion VS 'new' annotation of a\n\t\t\t\t// declaration\n\t\t\t\t// e.g. var i = 1;\n\t\t\t\t// var j = new Number(1);\n\t\t\t\treturn a == b;\n\t\t\t} else {\n\t\t\t\treturn a === b;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t\"string\" : useStrictEquality,\n\t\t\t\"boolean\" : useStrictEquality,\n\t\t\t\"number\" : useStrictEquality,\n\t\t\t\"null\" : useStrictEquality,\n\t\t\t\"undefined\" : useStrictEquality,\n\n\t\t\t\"nan\" : function(b) {\n\t\t\t\treturn isNaN(b);\n\t\t\t},\n\n\t\t\t\"date\" : function(b, a) {\n\t\t\t\treturn QUnit.objectType(b) === \"date\"\n\t\t\t\t\t\t&& a.valueOf() === b.valueOf();\n\t\t\t},\n\n\t\t\t\"regexp\" : function(b, a) {\n\t\t\t\treturn QUnit.objectType(b) === \"regexp\"\n\t\t\t\t\t\t&& a.source === b.source && // the regex itself\n\t\t\t\t\t\ta.global === b.global && // and its modifers\n\t\t\t\t\t\t\t\t\t\t\t\t\t// (gmi) ...\n\t\t\t\t\t\ta.ignoreCase === b.ignoreCase\n\t\t\t\t\t\t&& a.multiline === b.multiline;\n\t\t\t},\n\n\t\t\t// - skip when the property is a method of an instance (OOP)\n\t\t\t// - abort otherwise,\n\t\t\t// initial === would have catch identical references anyway\n\t\t\t\"function\" : function() {\n\t\t\t\tvar caller = callers[callers.length - 1];\n\t\t\t\treturn caller !== Object && typeof caller !== \"undefined\";\n\t\t\t},\n\n\t\t\t\"array\" : function(b, a) {\n\t\t\t\tvar i, j, loop;\n\t\t\t\tvar len;\n\n\t\t\t\t// b could be an object literal here\n\t\t\t\tif (!(QUnit.objectType(b) === \"array\")) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tlen = a.length;\n\t\t\t\tif (len !== b.length) { // safe and faster\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// track reference to avoid circular references\n\t\t\t\tparents.push(a);\n\t\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\t\tloop = false;\n\t\t\t\t\tfor (j = 0; j < parents.length; j++) {\n\t\t\t\t\t\tif (parents[j] === a[i]) {\n\t\t\t\t\t\t\tloop = true;// dont rewalk array\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (!loop && !innerEquiv(a[i], b[i])) {\n\t\t\t\t\t\tparents.pop();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tparents.pop();\n\t\t\t\treturn true;\n\t\t\t},\n\n\t\t\t\"object\" : function(b, a) {\n\t\t\t\tvar i, j, loop;\n\t\t\t\tvar eq = true; // unless we can proove it\n\t\t\t\tvar aProperties = [], bProperties = []; // collection of\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t// strings\n\n\t\t\t\t// comparing constructors is more strict than using\n\t\t\t\t// instanceof\n\t\t\t\tif (a.constructor !== b.constructor) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// stack constructor before traversing properties\n\t\t\t\tcallers.push(a.constructor);\n\t\t\t\t// track reference to avoid circular references\n\t\t\t\tparents.push(a);\n\n\t\t\t\tfor (i in a) { // be strict: don't ensures hasOwnProperty\n\t\t\t\t\t\t\t\t// and go deep\n\t\t\t\t\tloop = false;\n\t\t\t\t\tfor (j = 0; j < parents.length; j++) {\n\t\t\t\t\t\tif (parents[j] === a[i])\n\t\t\t\t\t\t\tloop = true; // don't go down the same path\n\t\t\t\t\t\t\t\t\t\t\t// twice\n\t\t\t\t\t}\n\t\t\t\t\taProperties.push(i); // collect a's properties\n\n\t\t\t\t\tif (!loop && !innerEquiv(a[i], b[i])) {\n\t\t\t\t\t\teq = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcallers.pop(); // unstack, we are done\n\t\t\t\tparents.pop();\n\n\t\t\t\tfor (i in b) {\n\t\t\t\t\tbProperties.push(i); // collect b's properties\n\t\t\t\t}\n\n\t\t\t\t// Ensures identical properties name\n\t\t\t\treturn eq\n\t\t\t\t\t\t&& innerEquiv(aProperties.sort(), bProperties\n\t\t\t\t\t\t\t\t.sort());\n\t\t\t}\n\t\t};\n\t}();\n\n\tinnerEquiv = function() { // can take multiple arguments\n\t\tvar args = Array.prototype.slice.apply(arguments);\n\t\tif (args.length < 2) {\n\t\t\treturn true; // end transition\n\t\t}\n\n\t\treturn (function(a, b) {\n\t\t\tif (a === b) {\n\t\t\t\treturn true; // catch the most you can\n\t\t\t} else if (a === null || b === null || typeof a === \"undefined\"\n\t\t\t\t\t|| typeof b === \"undefined\"\n\t\t\t\t\t|| QUnit.objectType(a) !== QUnit.objectType(b)) {\n\t\t\t\treturn false; // don't lose time with error prone cases\n\t\t\t} else {\n\t\t\t\treturn bindCallbacks(a, callbacks, [ b, a ]);\n\t\t\t}\n\n\t\t\t// apply transition with (1..n) arguments\n\t\t})(args[0], args[1])\n\t\t\t\t&& arguments.callee.apply(this, args.splice(1,\n\t\t\t\t\t\targs.length - 1));\n\t};\n\n\treturn innerEquiv;\n\n}();\n\n/**\n * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |\n * http://flesler.blogspot.com Licensed under BSD\n * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008\n *\n * @projectDescription Advanced and extensible data dumping for Javascript.\n * @version 1.0.0\n * @author Ariel Flesler\n * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}\n */\nQUnit.jsDump = (function() {\n\tfunction quote( str ) {\n\t\treturn '\"' + str.toString().replace(/\"/g, '\\\\\"') + '\"';\n\t};\n\tfunction literal( o ) {\n\t\treturn o + '';\n\t};\n\tfunction join( pre, arr, post ) {\n\t\tvar s = jsDump.separator(),\n\t\t\tbase = jsDump.indent(),\n\t\t\tinner = jsDump.indent(1);\n\t\tif ( arr.join )\n\t\t\tarr = arr.join( ',' + s + inner );\n\t\tif ( !arr )\n\t\t\treturn pre + post;\n\t\treturn [ pre, inner + arr, base + post ].join(s);\n\t};\n\tfunction array( arr, stack ) {\n\t\tvar i = arr.length, ret = Array(i);\n\t\tthis.up();\n\t\twhile ( i-- )\n\t\t\tret[i] = this.parse( arr[i] , undefined , stack);\n\t\tthis.down();\n\t\treturn join( '[', ret, ']' );\n\t};\n\n\tvar reName = /^function (\\w+)/;\n\n\tvar jsDump = {\n\t\tparse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance\n\t\t\tstack = stack || [ ];\n\t\t\tvar parser = this.parsers[ type || this.typeOf(obj) ];\n\t\t\ttype = typeof parser;\n\t\t\tvar inStack = inArray(obj, stack);\n\t\t\tif (inStack != -1) {\n\t\t\t\treturn 'recursion('+(inStack - stack.length)+')';\n\t\t\t}\n\t\t\t//else\n\t\t\tif (type == 'function')  {\n\t\t\t\t\tstack.push(obj);\n\t\t\t\t\tvar res = parser.call( this, obj, stack );\n\t\t\t\t\tstack.pop();\n\t\t\t\t\treturn res;\n\t\t\t}\n\t\t\t// else\n\t\t\treturn (type == 'string') ? parser : this.parsers.error;\n\t\t},\n\t\ttypeOf:function( obj ) {\n\t\t\tvar type;\n\t\t\tif ( obj === null ) {\n\t\t\t\ttype = \"null\";\n\t\t\t} else if (typeof obj === \"undefined\") {\n\t\t\t\ttype = \"undefined\";\n\t\t\t} else if (QUnit.is(\"RegExp\", obj)) {\n\t\t\t\ttype = \"regexp\";\n\t\t\t} else if (QUnit.is(\"Date\", obj)) {\n\t\t\t\ttype = \"date\";\n\t\t\t} else if (QUnit.is(\"Function\", obj)) {\n\t\t\t\ttype = \"function\";\n\t\t\t} else if (typeof obj.setInterval !== undefined && typeof obj.document !== \"undefined\" && typeof obj.nodeType === \"undefined\") {\n\t\t\t\ttype = \"window\";\n\t\t\t} else if (obj.nodeType === 9) {\n\t\t\t\ttype = \"document\";\n\t\t\t} else if (obj.nodeType) {\n\t\t\t\ttype = \"node\";\n\t\t\t} else if (typeof obj === \"object\" && typeof obj.length === \"number\" && obj.length >= 0) {\n\t\t\t\ttype = \"array\";\n\t\t\t} else {\n\t\t\t\ttype = typeof obj;\n\t\t\t}\n\t\t\treturn type;\n\t\t},\n\t\tseparator:function() {\n\t\t\treturn this.multiline ?\tthis.HTML ? '<br />' : '\\n' : this.HTML ? '&nbsp;' : ' ';\n\t\t},\n\t\tindent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing\n\t\t\tif ( !this.multiline )\n\t\t\t\treturn '';\n\t\t\tvar chr = this.indentChar;\n\t\t\tif ( this.HTML )\n\t\t\t\tchr = chr.replace(/\\t/g,'   ').replace(/ /g,'&nbsp;');\n\t\t\treturn Array( this._depth_ + (extra||0) ).join(chr);\n\t\t},\n\t\tup:function( a ) {\n\t\t\tthis._depth_ += a || 1;\n\t\t},\n\t\tdown:function( a ) {\n\t\t\tthis._depth_ -= a || 1;\n\t\t},\n\t\tsetParser:function( name, parser ) {\n\t\t\tthis.parsers[name] = parser;\n\t\t},\n\t\t// The next 3 are exposed so you can use them\n\t\tquote:quote,\n\t\tliteral:literal,\n\t\tjoin:join,\n\t\t//\n\t\t_depth_: 1,\n\t\t// This is the list of parsers, to modify them, use jsDump.setParser\n\t\tparsers:{\n\t\t\twindow: '[Window]',\n\t\t\tdocument: '[Document]',\n\t\t\terror:'[ERROR]', //when no parser is found, shouldn't happen\n\t\t\tunknown: '[Unknown]',\n\t\t\t'null':'null',\n\t\t\t'undefined':'undefined',\n\t\t\t'function':function( fn ) {\n\t\t\t\tvar ret = 'function',\n\t\t\t\t\tname = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE\n\t\t\t\tif ( name )\n\t\t\t\t\tret += ' ' + name;\n\t\t\t\tret += '(';\n\n\t\t\t\tret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join('');\n\t\t\t\treturn join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' );\n\t\t\t},\n\t\t\tarray: array,\n\t\t\tnodelist: array,\n\t\t\targuments: array,\n\t\t\tobject:function( map, stack ) {\n\t\t\t\tvar ret = [ ];\n\t\t\t\tQUnit.jsDump.up();\n\t\t\t\tfor ( var key in map ) {\n\t\t\t\t    var val = map[key];\n\t\t\t\t\tret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack));\n                }\n\t\t\t\tQUnit.jsDump.down();\n\t\t\t\treturn join( '{', ret, '}' );\n\t\t\t},\n\t\t\tnode:function( node ) {\n\t\t\t\tvar open = QUnit.jsDump.HTML ? '&lt;' : '<',\n\t\t\t\t\tclose = QUnit.jsDump.HTML ? '&gt;' : '>';\n\n\t\t\t\tvar tag = node.nodeName.toLowerCase(),\n\t\t\t\t\tret = open + tag;\n\n\t\t\t\tfor ( var a in QUnit.jsDump.DOMAttrs ) {\n\t\t\t\t\tvar val = node[QUnit.jsDump.DOMAttrs[a]];\n\t\t\t\t\tif ( val )\n\t\t\t\t\t\tret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' );\n\t\t\t\t}\n\t\t\t\treturn ret + close + open + '/' + tag + close;\n\t\t\t},\n\t\t\tfunctionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function\n\t\t\t\tvar l = fn.length;\n\t\t\t\tif ( !l ) return '';\n\n\t\t\t\tvar args = Array(l);\n\t\t\t\twhile ( l-- )\n\t\t\t\t\targs[l] = String.fromCharCode(97+l);//97 is 'a'\n\t\t\t\treturn ' ' + args.join(', ') + ' ';\n\t\t\t},\n\t\t\tkey:quote, //object calls it internally, the key part of an item in a map\n\t\t\tfunctionCode:'[code]', //function calls it internally, it's the content of the function\n\t\t\tattribute:quote, //node calls it internally, it's an html attribute value\n\t\t\tstring:quote,\n\t\t\tdate:quote,\n\t\t\tregexp:literal, //regex\n\t\t\tnumber:literal,\n\t\t\t'boolean':literal\n\t\t},\n\t\tDOMAttrs:{//attributes to dump from nodes, name=>realName\n\t\t\tid:'id',\n\t\t\tname:'name',\n\t\t\t'class':'className'\n\t\t},\n\t\tHTML:false,//if true, entities are escaped ( <, >, \\t, space and \\n )\n\t\tindentChar:'  ',//indentation unit\n\t\tmultiline:true //if true, items in a collection, are separated by a \\n, else just a space.\n\t};\n\n\treturn jsDump;\n})();\n\n// from Sizzle.js\nfunction getText( elems ) {\n\tvar ret = \"\", elem;\n\n\tfor ( var i = 0; elems[i]; i++ ) {\n\t\telem = elems[i];\n\n\t\t// Get the text from text nodes and CDATA nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 4 ) {\n\t\t\tret += elem.nodeValue;\n\n\t\t// Traverse everything else, except comment nodes\n\t\t} else if ( elem.nodeType !== 8 ) {\n\t\t\tret += getText( elem.childNodes );\n\t\t}\n\t}\n\n\treturn ret;\n};\n\n//from jquery.js\nfunction inArray( elem, array ) {\n\tif ( array.indexOf ) {\n\t\treturn array.indexOf( elem );\n\t}\n\n\tfor ( var i = 0, length = array.length; i < length; i++ ) {\n\t\tif ( array[ i ] === elem ) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n/*\n * Javascript Diff Algorithm\n *  By John Resig (http://ejohn.org/)\n *  Modified by Chu Alan \"sprite\"\n *\n * Released under the MIT license.\n *\n * More Info:\n *  http://ejohn.org/projects/javascript-diff-algorithm/\n *\n * Usage: QUnit.diff(expected, actual)\n *\n * QUnit.diff(\"the quick brown fox jumped over\", \"the quick fox jumps over\") == \"the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over\"\n */\nQUnit.diff = (function() {\n\tfunction diff(o, n) {\n\t\tvar ns = {};\n\t\tvar os = {};\n\n\t\tfor (var i = 0; i < n.length; i++) {\n\t\t\tif (ns[n[i]] == null)\n\t\t\t\tns[n[i]] = {\n\t\t\t\t\trows: [],\n\t\t\t\t\to: null\n\t\t\t\t};\n\t\t\tns[n[i]].rows.push(i);\n\t\t}\n\n\t\tfor (var i = 0; i < o.length; i++) {\n\t\t\tif (os[o[i]] == null)\n\t\t\t\tos[o[i]] = {\n\t\t\t\t\trows: [],\n\t\t\t\t\tn: null\n\t\t\t\t};\n\t\t\tos[o[i]].rows.push(i);\n\t\t}\n\n\t\tfor (var i in ns) {\n\t\t\tif (ns[i].rows.length == 1 && typeof(os[i]) != \"undefined\" && os[i].rows.length == 1) {\n\t\t\t\tn[ns[i].rows[0]] = {\n\t\t\t\t\ttext: n[ns[i].rows[0]],\n\t\t\t\t\trow: os[i].rows[0]\n\t\t\t\t};\n\t\t\t\to[os[i].rows[0]] = {\n\t\t\t\t\ttext: o[os[i].rows[0]],\n\t\t\t\t\trow: ns[i].rows[0]\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < n.length - 1; i++) {\n\t\t\tif (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null &&\n\t\t\tn[i + 1] == o[n[i].row + 1]) {\n\t\t\t\tn[i + 1] = {\n\t\t\t\t\ttext: n[i + 1],\n\t\t\t\t\trow: n[i].row + 1\n\t\t\t\t};\n\t\t\t\to[n[i].row + 1] = {\n\t\t\t\t\ttext: o[n[i].row + 1],\n\t\t\t\t\trow: i + 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = n.length - 1; i > 0; i--) {\n\t\t\tif (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null &&\n\t\t\tn[i - 1] == o[n[i].row - 1]) {\n\t\t\t\tn[i - 1] = {\n\t\t\t\t\ttext: n[i - 1],\n\t\t\t\t\trow: n[i].row - 1\n\t\t\t\t};\n\t\t\t\to[n[i].row - 1] = {\n\t\t\t\t\ttext: o[n[i].row - 1],\n\t\t\t\t\trow: i - 1\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\to: o,\n\t\t\tn: n\n\t\t};\n\t}\n\n\treturn function(o, n) {\n\t\to = o.replace(/\\s+$/, '');\n\t\tn = n.replace(/\\s+$/, '');\n\t\tvar out = diff(o == \"\" ? [] : o.split(/\\s+/), n == \"\" ? [] : n.split(/\\s+/));\n\n\t\tvar str = \"\";\n\n\t\tvar oSpace = o.match(/\\s+/g);\n\t\tif (oSpace == null) {\n\t\t\toSpace = [\" \"];\n\t\t}\n\t\telse {\n\t\t\toSpace.push(\" \");\n\t\t}\n\t\tvar nSpace = n.match(/\\s+/g);\n\t\tif (nSpace == null) {\n\t\t\tnSpace = [\" \"];\n\t\t}\n\t\telse {\n\t\t\tnSpace.push(\" \");\n\t\t}\n\n\t\tif (out.n.length == 0) {\n\t\t\tfor (var i = 0; i < out.o.length; i++) {\n\t\t\t\tstr += '<del>' + out.o[i] + oSpace[i] + \"</del>\";\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (out.n[0].text == null) {\n\t\t\t\tfor (n = 0; n < out.o.length && out.o[n].text == null; n++) {\n\t\t\t\t\tstr += '<del>' + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (var i = 0; i < out.n.length; i++) {\n\t\t\t\tif (out.n[i].text == null) {\n\t\t\t\t\tstr += '<ins>' + out.n[i] + nSpace[i] + \"</ins>\";\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvar pre = \"\";\n\n\t\t\t\t\tfor (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) {\n\t\t\t\t\t\tpre += '<del>' + out.o[n] + oSpace[n] + \"</del>\";\n\t\t\t\t\t}\n\t\t\t\t\tstr += \" \" + out.n[i].text + nSpace[i] + pre;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn str;\n\t};\n})();\n\n})(this);\n"
  },
  {
    "path": "lib/raf/requestAnimationFrame.js",
    "content": "// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating\n\n// requestAnimationFrame polyfill by Erik Möller\n// fixes from Paul Irish and Tino Zijdel\n\n(function() {\n    var lastTime = 0;\n    var vendors = ['ms', 'moz', 'webkit', 'o'];\n    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {\n        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];\n        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] \n                                   || window[vendors[x]+'CancelRequestAnimationFrame'];\n    }\n \n    if (!window.requestAnimationFrame)\n        window.requestAnimationFrame = function(callback, element) {\n            var currTime = new Date().getTime();\n            var timeToCall = Math.max(0, 16 - (currTime - lastTime));\n            var id = window.setTimeout(function() { callback(currTime + timeToCall); }, \n              timeToCall);\n            lastTime = currTime + timeToCall;\n            return id;\n        };\n \n    if (!window.cancelAnimationFrame)\n        window.cancelAnimationFrame = function(id) {\n            clearTimeout(id);\n        };\n}());"
  },
  {
    "path": "lib/rhino/LICENSE.txt",
    "content": "The majority of Rhino is MPL 1.1 / GPL 2.0 dual licensed:\n\nThe Mozilla Public License (http://www.mozilla.org/MPL/MPL-1.1.txt):\n============================================================================\n\t\t\t    MOZILLA PUBLIC LICENSE\n\t\t\t\t  Version 1.1\n\n\t\t\t\t---------------\n\n  1. Definitions.\n\n       1.0.1. \"Commercial Use\" means distribution or otherwise making the\n       Covered Code available to a third party.\n\n       1.1. \"Contributor\" means each entity that creates or contributes to\n       the creation of Modifications.\n\n       1.2. \"Contributor Version\" means the combination of the Original\n       Code, prior Modifications used by a Contributor, and the Modifications\n       made by that particular Contributor.\n\n       1.3. \"Covered Code\" means the Original Code or Modifications or the\n       combination of the Original Code and Modifications, in each case\n       including portions thereof.\n\n       1.4. \"Electronic Distribution Mechanism\" means a mechanism generally\n       accepted in the software development community for the electronic\n       transfer of data.\n\n       1.5. \"Executable\" means Covered Code in any form other than Source\n       Code.\n\n       1.6. \"Initial Developer\" means the individual or entity identified\n       as the Initial Developer in the Source Code notice required by Exhibit\n       A.\n\n       1.7. \"Larger Work\" means a work which combines Covered Code or\n       portions thereof with code not governed by the terms of this License.\n\n       1.8. \"License\" means this document.\n\n       1.8.1. \"Licensable\" means having the right to grant, to the maximum\n       extent possible, whether at the time of the initial grant or\n       subsequently acquired, any and all of the rights conveyed herein.\n\n       1.9. \"Modifications\" means any addition to or deletion from the\n       substance or structure of either the Original Code or any previous\n       Modifications. When Covered Code is released as a series of files, a\n       Modification is:\n\t    A. Any addition to or deletion from the contents of a file\n\t    containing Original Code or previous Modifications.\n\n\t    B. Any new file that contains any part of the Original Code or\n\t    previous Modifications.\n\n       1.10. \"Original Code\" means Source Code of computer software code\n       which is described in the Source Code notice required by Exhibit A as\n       Original Code, and which, at the time of its release under this\n       License is not already Covered Code governed by this License.\n\n       1.10.1. \"Patent Claims\" means any patent claim(s), now owned or\n       hereafter acquired, including without limitation,  method, process,\n       and apparatus claims, in any patent Licensable by grantor.\n\n       1.11. \"Source Code\" means the preferred form of the Covered Code for\n       making modifications to it, including all modules it contains, plus\n       any associated interface definition files, scripts used to control\n       compilation and installation of an Executable, or source code\n       differential comparisons against either the Original Code or another\n       well known, available Covered Code of the Contributor's choice. The\n       Source Code can be in a compressed or archival form, provided the\n       appropriate decompression or de-archiving software is widely available\n       for no charge.\n\n       1.12. \"You\" (or \"Your\")  means an individual or a legal entity\n       exercising rights under, and complying with all of the terms of, this\n       License or a future version of this License issued under Section 6.1.\n       For legal entities, \"You\" includes any entity which controls, is\n       controlled by, or is under common control with You. For purposes of\n       this definition, \"control\" means (a) the power, direct or indirect,\n       to cause the direction or management of such entity, whether by\n       contract or otherwise, or (b) ownership of more than fifty percent\n       (50%) of the outstanding shares or beneficial ownership of such\n       entity.\n\n  2. Source Code License.\n\n       2.1. The Initial Developer Grant.\n       The Initial Developer hereby grants You a world-wide, royalty-free,\n       non-exclusive license, subject to third party intellectual property\n       claims:\n\t    (a)  under intellectual property rights (other than patent or\n\t    trademark) Licensable by Initial Developer to use, reproduce,\n\t    modify, display, perform, sublicense and distribute the Original\n\t    Code (or portions thereof) with or without Modifications, and/or\n\t    as part of a Larger Work; and\n\n\t    (b) under Patents Claims infringed by the making, using or\n\t    selling of Original Code, to make, have made, use, practice,\n\t    sell, and offer for sale, and/or otherwise dispose of the\n\t    Original Code (or portions thereof).\n\n\t    (c) the licenses granted in this Section 2.1(a) and (b) are\n\t    effective on the date Initial Developer first distributes\n\t    Original Code under the terms of this License.\n\n\t    (d) Notwithstanding Section 2.1(b) above, no patent license is\n\t    granted: 1) for code that You delete from the Original Code; 2)\n\t    separate from the Original Code;  or 3) for infringements caused\n\t    by: i) the modification of the Original Code or ii) the\n\t    combination of the Original Code with other software or devices.\n\n       2.2. Contributor Grant.\n       Subject to third party intellectual property claims, each Contributor\n       hereby grants You a world-wide, royalty-free, non-exclusive license\n\n\t    (a)  under intellectual property rights (other than patent or\n\t    trademark) Licensable by Contributor, to use, reproduce, modify,\n\t    display, perform, sublicense and distribute the Modifications\n\t    created by such Contributor (or portions thereof) either on an\n\t    unmodified basis, with other Modifications, as Covered Code\n\t    and/or as part of a Larger Work; and\n\n\t    (b) under Patent Claims infringed by the making, using, or\n\t    selling of  Modifications made by that Contributor either alone\n\t    and/or in combination with its Contributor Version (or portions\n\t    of such combination), to make, use, sell, offer for sale, have\n\t    made, and/or otherwise dispose of: 1) Modifications made by that\n\t    Contributor (or portions thereof); and 2) the combination of\n\t    Modifications made by that Contributor with its Contributor\n\t    Version (or portions of such combination).\n\n\t    (c) the licenses granted in Sections 2.2(a) and 2.2(b) are\n\t    effective on the date Contributor first makes Commercial Use of\n\t    the Covered Code.\n\n\t    (d)    Notwithstanding Section 2.2(b) above, no patent license is\n\t    granted: 1) for any code that Contributor has deleted from the\n\t    Contributor Version; 2)  separate from the Contributor Version;\n\t    3)  for infringements caused by: i) third party modifications of\n\t    Contributor Version or ii)  the combination of Modifications made\n\t    by that Contributor with other software  (except as part of the\n\t    Contributor Version) or other devices; or 4) under Patent Claims\n\t    infringed by Covered Code in the absence of Modifications made by\n\t    that Contributor.\n\n  3. Distribution Obligations.\n\n       3.1. Application of License.\n       The Modifications which You create or to which You contribute are\n       governed by the terms of this License, including without limitation\n       Section 2.2. The Source Code version of Covered Code may be\n       distributed only under the terms of this License or a future version\n       of this License released under Section 6.1, and You must include a\n       copy of this License with every copy of the Source Code You\n       distribute. You may not offer or impose any terms on any Source Code\n       version that alters or restricts the applicable version of this\n       License or the recipients' rights hereunder. However, You may include\n       an additional document offering the additional rights described in\n       Section 3.5.\n\n       3.2. Availability of Source Code.\n       Any Modification which You create or to which You contribute must be\n       made available in Source Code form under the terms of this License\n       either on the same media as an Executable version or via an accepted\n       Electronic Distribution Mechanism to anyone to whom you made an\n       Executable version available; and if made available via Electronic\n       Distribution Mechanism, must remain available for at least twelve (12)\n       months after the date it initially became available, or at least six\n       (6) months after a subsequent version of that particular Modification\n       has been made available to such recipients. You are responsible for\n       ensuring that the Source Code version remains available even if the\n       Electronic Distribution Mechanism is maintained by a third party.\n\n       3.3. Description of Modifications.\n       You must cause all Covered Code to which You contribute to contain a\n       file documenting the changes You made to create that Covered Code and\n       the date of any change. You must include a prominent statement that\n       the Modification is derived, directly or indirectly, from Original\n       Code provided by the Initial Developer and including the name of the\n       Initial Developer in (a) the Source Code, and (b) in any notice in an\n       Executable version or related documentation in which You describe the\n       origin or ownership of the Covered Code.\n\n       3.4. Intellectual Property Matters\n\t    (a) Third Party Claims.\n\t    If Contributor has knowledge that a license under a third party's\n\t    intellectual property rights is required to exercise the rights\n\t    granted by such Contributor under Sections 2.1 or 2.2,\n\t    Contributor must include a text file with the Source Code\n\t    distribution titled \"LEGAL\" which describes the claim and the\n\t    party making the claim in sufficient detail that a recipient will\n\t    know whom to contact. If Contributor obtains such knowledge after\n\t    the Modification is made available as described in Section 3.2,\n\t    Contributor shall promptly modify the LEGAL file in all copies\n\t    Contributor makes available thereafter and shall take other steps\n\t    (such as notifying appropriate mailing lists or newsgroups)\n\t    reasonably calculated to inform those who received the Covered\n\t    Code that new knowledge has been obtained.\n\n\t    (b) Contributor APIs.\n\t    If Contributor's Modifications include an application programming\n\t    interface and Contributor has knowledge of patent licenses which\n\t    are reasonably necessary to implement that API, Contributor must\n\t    also include this information in the LEGAL file.\n\n\t\t (c)    Representations.\n\t    Contributor represents that, except as disclosed pursuant to\n\t    Section 3.4(a) above, Contributor believes that Contributor's\n\t    Modifications are Contributor's original creation(s) and/or\n\t    Contributor has sufficient rights to grant the rights conveyed by\n\t    this License.\n\n       3.5. Required Notices.\n       You must duplicate the notice in Exhibit A in each file of the Source\n       Code.  If it is not possible to put such notice in a particular Source\n       Code file due to its structure, then You must include such notice in a\n       location (such as a relevant directory) where a user would be likely\n       to look for such a notice.  If You created one or more Modification(s)\n       You may add your name as a Contributor to the notice described in\n       Exhibit A.  You must also duplicate this License in any documentation\n       for the Source Code where You describe recipients' rights or ownership\n       rights relating to Covered Code.  You may choose to offer, and to\n       charge a fee for, warranty, support, indemnity or liability\n       obligations to one or more recipients of Covered Code. However, You\n       may do so only on Your own behalf, and not on behalf of the Initial\n       Developer or any Contributor. You must make it absolutely clear than\n       any such warranty, support, indemnity or liability obligation is\n       offered by You alone, and You hereby agree to indemnify the Initial\n       Developer and every Contributor for any liability incurred by the\n       Initial Developer or such Contributor as a result of warranty,\n       support, indemnity or liability terms You offer.\n\n       3.6. Distribution of Executable Versions.\n       You may distribute Covered Code in Executable form only if the\n       requirements of Section 3.1-3.5 have been met for that Covered Code,\n       and if You include a notice stating that the Source Code version of\n       the Covered Code is available under the terms of this License,\n       including a description of how and where You have fulfilled the\n       obligations of Section 3.2. The notice must be conspicuously included\n       in any notice in an Executable version, related documentation or\n       collateral in which You describe recipients' rights relating to the\n       Covered Code. You may distribute the Executable version of Covered\n       Code or ownership rights under a license of Your choice, which may\n       contain terms different from this License, provided that You are in\n       compliance with the terms of this License and that the license for the\n       Executable version does not attempt to limit or alter the recipient's\n       rights in the Source Code version from the rights set forth in this\n       License. If You distribute the Executable version under a different\n       license You must make it absolutely clear that any terms which differ\n       from this License are offered by You alone, not by the Initial\n       Developer or any Contributor. You hereby agree to indemnify the\n       Initial Developer and every Contributor for any liability incurred by\n       the Initial Developer or such Contributor as a result of any such\n       terms You offer.\n\n       3.7. Larger Works.\n       You may create a Larger Work by combining Covered Code with other code\n       not governed by the terms of this License and distribute the Larger\n       Work as a single product. In such a case, You must make sure the\n       requirements of this License are fulfilled for the Covered Code.\n\n  4. Inability to Comply Due to Statute or Regulation.\n\n       If it is impossible for You to comply with any of the terms of this\n       License with respect to some or all of the Covered Code due to\n       statute, judicial order, or regulation then You must: (a) comply with\n       the terms of this License to the maximum extent possible; and (b)\n       describe the limitations and the code they affect. Such description\n       must be included in the LEGAL file described in Section 3.4 and must\n       be included with all distributions of the Source Code. Except to the\n       extent prohibited by statute or regulation, such description must be\n       sufficiently detailed for a recipient of ordinary skill to be able to\n       understand it.\n\n  5. Application of this License.\n\n       This License applies to code to which the Initial Developer has\n       attached the notice in Exhibit A and to related Covered Code.\n\n  6. Versions of the License.\n\n       6.1. New Versions.\n       Netscape Communications Corporation (\"Netscape\") may publish revised\n       and/or new versions of the License from time to time. Each version\n       will be given a distinguishing version number.\n\n       6.2. Effect of New Versions.\n       Once Covered Code has been published under a particular version of the\n       License, You may always continue to use it under the terms of that\n       version. You may also choose to use such Covered Code under the terms\n       of any subsequent version of the License published by Netscape. No one\n       other than Netscape has the right to modify the terms applicable to\n       Covered Code created under this License.\n\n       6.3. Derivative Works.\n       If You create or use a modified version of this License (which you may\n       only do in order to apply it to code which is not already Covered Code\n       governed by this License), You must (a) rename Your license so that\n       the phrases \"Mozilla\", \"MOZILLAPL\", \"MOZPL\", \"Netscape\",\n       \"MPL\", \"NPL\" or any confusingly similar phrase do not appear in your\n       license (except to note that your license differs from this License)\n       and (b) otherwise make it clear that Your version of the license\n       contains terms which differ from the Mozilla Public License and\n       Netscape Public License. (Filling in the name of the Initial\n       Developer, Original Code or Contributor in the notice described in\n       Exhibit A shall not of themselves be deemed to be modifications of\n       this License.)\n\n  7. DISCLAIMER OF WARRANTY.\n\n       COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN \"AS IS\" BASIS,\n       WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,\n       WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF\n       DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.\n       THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE\n       IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,\n       YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE\n       COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER\n       OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF\n       ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.\n\n  8. TERMINATION.\n\n       8.1.  This License and the rights granted hereunder will terminate\n       automatically if You fail to comply with terms herein and fail to cure\n       such breach within 30 days of becoming aware of the breach. All\n       sublicenses to the Covered Code which are properly granted shall\n       survive any termination of this License. Provisions which, by their\n       nature, must remain in effect beyond the termination of this License\n       shall survive.\n\n       8.2.  If You initiate litigation by asserting a patent infringement\n       claim (excluding declatory judgment actions) against Initial Developer\n       or a Contributor (the Initial Developer or Contributor against whom\n       You file such action is referred to as \"Participant\")  alleging that:\n\n       (a)  such Participant's Contributor Version directly or indirectly\n       infringes any patent, then any and all rights granted by such\n       Participant to You under Sections 2.1 and/or 2.2 of this License\n       shall, upon 60 days notice from Participant terminate prospectively,\n       unless if within 60 days after receipt of notice You either: (i)\n       agree in writing to pay Participant a mutually agreeable reasonable\n       royalty for Your past and future use of Modifications made by such\n       Participant, or (ii) withdraw Your litigation claim with respect to\n       the Contributor Version against such Participant.  If within 60 days\n       of notice, a reasonable royalty and payment arrangement are not\n       mutually agreed upon in writing by the parties or the litigation claim\n       is not withdrawn, the rights granted by Participant to You under\n       Sections 2.1 and/or 2.2 automatically terminate at the expiration of\n       the 60 day notice period specified above.\n\n       (b)  any software, hardware, or device, other than such Participant's\n       Contributor Version, directly or indirectly infringes any patent, then\n       any rights granted to You by such Participant under Sections 2.1(b)\n       and 2.2(b) are revoked effective as of the date You first made, used,\n       sold, distributed, or had made, Modifications made by that\n       Participant.\n\n       8.3.  If You assert a patent infringement claim against Participant\n       alleging that such Participant's Contributor Version directly or\n       indirectly infringes any patent where such claim is resolved (such as\n       by license or settlement) prior to the initiation of patent\n       infringement litigation, then the reasonable value of the licenses\n       granted by such Participant under Sections 2.1 or 2.2 shall be taken\n       into account in determining the amount or value of any payment or\n       license.\n\n       8.4.  In the event of termination under Sections 8.1 or 8.2 above,\n       all end user license agreements (excluding distributors and resellers)\n       which have been validly granted by You or any distributor hereunder\n       prior to termination shall survive termination.\n\n  9. LIMITATION OF LIABILITY.\n\n       UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT\n       (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL\n       DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,\n       OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR\n       ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY\n       CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,\n       WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER\n       COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN\n       INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF\n       LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY\n       RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW\n       PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE\n       EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO\n       THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.\n\n  10. U.S. GOVERNMENT END USERS.\n\n       The Covered Code is a \"commercial item,\" as that term is defined in\n       48 C.F.R. 2.101 (Oct. 1995), consisting of \"commercial computer\n       software\" and \"commercial computer software documentation,\" as such\n       terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48\n       C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),\n       all U.S. Government End Users acquire Covered Code with only those\n       rights set forth herein.\n\n  11. MISCELLANEOUS.\n\n       This License represents the complete agreement concerning subject\n       matter hereof. If any provision of this License is held to be\n       unenforceable, such provision shall be reformed only to the extent\n       necessary to make it enforceable. This License shall be governed by\n       California law provisions (except to the extent applicable law, if\n       any, provides otherwise), excluding its conflict-of-law provisions.\n       With respect to disputes in which at least one party is a citizen of,\n       or an entity chartered or registered to do business in the United\n       States of America, any litigation relating to this License shall be\n       subject to the jurisdiction of the Federal Courts of the Northern\n       District of California, with venue lying in Santa Clara County,\n       California, with the losing party responsible for costs, including\n       without limitation, court costs and reasonable attorneys' fees and\n       expenses. The application of the United Nations Convention on\n       Contracts for the International Sale of Goods is expressly excluded.\n       Any law or regulation which provides that the language of a contract\n       shall be construed against the drafter shall not apply to this\n       License.\n\n  12. RESPONSIBILITY FOR CLAIMS.\n\n       As between Initial Developer and the Contributors, each party is\n       responsible for claims and damages arising, directly or indirectly,\n       out of its utilization of rights under this License and You agree to\n       work with Initial Developer and Contributors to distribute such\n       responsibility on an equitable basis. Nothing herein is intended or\n       shall be deemed to constitute any admission of liability.\n\n  13. MULTIPLE-LICENSED CODE.\n\n       Initial Developer may designate portions of the Covered Code as\n       \"Multiple-Licensed\".  \"Multiple-Licensed\" means that the Initial\n       Developer permits you to utilize portions of the Covered Code under\n       Your choice of the NPL or the alternative licenses, if any, specified\n       by the Initial Developer in the file described in Exhibit A.\n\n  EXHIBIT A -Mozilla Public License.\n\n       ``The contents of this file are subject to the Mozilla Public License\n       Version 1.1 (the \"License\"); you may not use this file except in\n       compliance with the License. You may obtain a copy of the License at\n       http://www.mozilla.org/MPL/\n\n       Software distributed under the License is distributed on an \"AS IS\"\n       basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the\n       License for the specific language governing rights and limitations\n       under the License.\n\n       The Original Code is ______________________________________.\n\n       The Initial Developer of the Original Code is ________________________.\n       Portions created by ______________________ are Copyright (C) ______\n       _______________________. All Rights Reserved.\n\n       Contributor(s): ______________________________________.\n\n       Alternatively, the contents of this file may be used under the terms\n       of the _____ license (the  \"[___] License\"), in which case the\n       provisions of [______] License are applicable instead of those\n       above.  If you wish to allow use of your version of this file only\n       under the terms of the [____] License and not to allow others to use\n       your version of this file under the MPL, indicate your decision by\n       deleting  the provisions above and replace  them with the notice and\n       other provisions required by the [___] License.  If you do not delete\n       the provisions above, a recipient may use your version of this file\n       under either the MPL or the [___] License.\"\n\n       [NOTE: The text of this Exhibit A may differ slightly from the text of\n       the notices in the Source Code files of the Original Code. You should\n       use the text of this Exhibit A rather than the text found in the\n       Original Code Source Code for Your Modifications.]\n============================================================================\n\n============================================================================\n\t  GNU GENERAL PUBLIC LICENSE\n\t     Version 2, June 1991\n\n   Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n   Everyone is permitted to copy and distribute verbatim copies\n   of this license document, but changing it is not allowed.\n\n\t    Preamble\n\n    The licenses for most software are designed to take away your\n  freedom to share and change it.  By contrast, the GNU General Public\n  License is intended to guarantee your freedom to share and change free\n  software--to make sure the software is free for all its users.  This\n  General Public License applies to most of the Free Software\n  Foundation's software and to any other program whose authors commit to\n  using it.  (Some other Free Software Foundation software is covered by\n  the GNU Lesser General Public License instead.)  You can apply it to\n  your programs, too.\n\n    When we speak of free software, we are referring to freedom, not\n  price.  Our General Public Licenses are designed to make sure that you\n  have the freedom to distribute copies of free software (and charge for\n  this service if you wish), that you receive source code or can get it\n  if you want it, that you can change the software or use pieces of it\n  in new free programs; and that you know you can do these things.\n\n    To protect your rights, we need to make restrictions that forbid\n  anyone to deny you these rights or to ask you to surrender the rights.\n  These restrictions translate to certain responsibilities for you if you\n  distribute copies of the software, or if you modify it.\n\n    For example, if you distribute copies of such a program, whether\n  gratis or for a fee, you must give the recipients all the rights that\n  you have.  You must make sure that they, too, receive or can get the\n  source code.  And you must show them these terms so they know their\n  rights.\n\n    We protect your rights with two steps: (1) copyright the software, and\n  (2) offer you this license which gives you legal permission to copy,\n  distribute and/or modify the software.\n\n    Also, for each author's protection and ours, we want to make certain\n  that everyone understands that there is no warranty for this free\n  software.  If the software is modified by someone else and passed on, we\n  want its recipients to know that what they have is not the original, so\n  that any problems introduced by others will not reflect on the original\n  authors' reputations.\n\n    Finally, any free program is threatened constantly by software\n  patents.  We wish to avoid the danger that redistributors of a free\n  program will individually obtain patent licenses, in effect making the\n  program proprietary.  To prevent this, we have made it clear that any\n  patent must be licensed for everyone's free use or not licensed at all.\n\n    The precise terms and conditions for copying, distribution and\n  modification follow.\n\n\t  GNU GENERAL PUBLIC LICENSE\n     TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n    0. This License applies to any program or other work which contains\n  a notice placed by the copyright holder saying it may be distributed\n  under the terms of this General Public License.  The \"Program\", below,\n  refers to any such program or work, and a \"work based on the Program\"\n  means either the Program or any derivative work under copyright law:\n  that is to say, a work containing the Program or a portion of it,\n  either verbatim or with modifications and/or translated into another\n  language.  (Hereinafter, translation is included without limitation in\n  the term \"modification\".)  Each licensee is addressed as \"you\".\n\n  Activities other than copying, distribution and modification are not\n  covered by this License; they are outside its scope.  The act of\n  running the Program is not restricted, and the output from the Program\n  is covered only if its contents constitute a work based on the\n  Program (independent of having been made by running the Program).\n  Whether that is true depends on what the Program does.\n\n    1. You may copy and distribute verbatim copies of the Program's\n  source code as you receive it, in any medium, provided that you\n  conspicuously and appropriately publish on each copy an appropriate\n  copyright notice and disclaimer of warranty; keep intact all the\n  notices that refer to this License and to the absence of any warranty;\n  and give any other recipients of the Program a copy of this License\n  along with the Program.\n\n  You may charge a fee for the physical act of transferring a copy, and\n  you may at your option offer warranty protection in exchange for a fee.\n\n    2. You may modify your copy or copies of the Program or any portion\n  of it, thus forming a work based on the Program, and copy and\n  distribute such modifications or work under the terms of Section 1\n  above, provided that you also meet all of these conditions:\n\n      a) You must cause the modified files to carry prominent notices\n      stating that you changed the files and the date of any change.\n\n      b) You must cause any work that you distribute or publish, that in\n      whole or in part contains or is derived from the Program or any\n      part thereof, to be licensed as a whole at no charge to all third\n      parties under the terms of this License.\n\n      c) If the modified program normally reads commands interactively\n      when run, you must cause it, when started running for such\n      interactive use in the most ordinary way, to print or display an\n      announcement including an appropriate copyright notice and a\n      notice that there is no warranty (or else, saying that you provide\n      a warranty) and that users may redistribute the program under\n      these conditions, and telling the user how to view a copy of this\n      License.  (Exception: if the Program itself is interactive but\n      does not normally print such an announcement, your work based on\n      the Program is not required to print an announcement.)\n\n  These requirements apply to the modified work as a whole.  If\n  identifiable sections of that work are not derived from the Program,\n  and can be reasonably considered independent and separate works in\n  themselves, then this License, and its terms, do not apply to those\n  sections when you distribute them as separate works.  But when you\n  distribute the same sections as part of a whole which is a work based\n  on the Program, the distribution of the whole must be on the terms of\n  this License, whose permissions for other licensees extend to the\n  entire whole, and thus to each and every part regardless of who wrote it.\n\n  Thus, it is not the intent of this section to claim rights or contest\n  your rights to work written entirely by you; rather, the intent is to\n  exercise the right to control the distribution of derivative or\n  collective works based on the Program.\n\n  In addition, mere aggregation of another work not based on the Program\n  with the Program (or with a work based on the Program) on a volume of\n  a storage or distribution medium does not bring the other work under\n  the scope of this License.\n\n    3. You may copy and distribute the Program (or a work based on it,\n  under Section 2) in object code or executable form under the terms of\n  Sections 1 and 2 above provided that you also do one of the following:\n\n      a) Accompany it with the complete corresponding machine-readable\n      source code, which must be distributed under the terms of Sections\n      1 and 2 above on a medium customarily used for software interchange; or,\n\n      b) Accompany it with a written offer, valid for at least three\n      years, to give any third party, for a charge no more than your\n      cost of physically performing source distribution, a complete\n      machine-readable copy of the corresponding source code, to be\n      distributed under the terms of Sections 1 and 2 above on a medium\n      customarily used for software interchange; or,\n\n      c) Accompany it with the information you received as to the offer\n      to distribute corresponding source code.  (This alternative is\n      allowed only for noncommercial distribution and only if you\n      received the program in object code or executable form with such\n      an offer, in accord with Subsection b above.)\n\n  The source code for a work means the preferred form of the work for\n  making modifications to it.  For an executable work, complete source\n  code means all the source code for all modules it contains, plus any\n  associated interface definition files, plus the scripts used to\n  control compilation and installation of the executable.  However, as a\n  special exception, the source code distributed need not include\n  anything that is normally distributed (in either source or binary\n  form) with the major components (compiler, kernel, and so on) of the\n  operating system on which the executable runs, unless that component\n  itself accompanies the executable.\n\n  If distribution of executable or object code is made by offering\n  access to copy from a designated place, then offering equivalent\n  access to copy the source code from the same place counts as\n  distribution of the source code, even though third parties are not\n  compelled to copy the source along with the object code.\n\n    4. You may not copy, modify, sublicense, or distribute the Program\n  except as expressly provided under this License.  Any attempt\n  otherwise to copy, modify, sublicense or distribute the Program is\n  void, and will automatically terminate your rights under this License.\n  However, parties who have received copies, or rights, from you under\n  this License will not have their licenses terminated so long as such\n  parties remain in full compliance.\n\n    5. You are not required to accept this License, since you have not\n  signed it.  However, nothing else grants you permission to modify or\n  distribute the Program or its derivative works.  These actions are\n  prohibited by law if you do not accept this License.  Therefore, by\n  modifying or distributing the Program (or any work based on the\n  Program), you indicate your acceptance of this License to do so, and\n  all its terms and conditions for copying, distributing or modifying\n  the Program or works based on it.\n\n    6. Each time you redistribute the Program (or any work based on the\n  Program), the recipient automatically receives a license from the\n  original licensor to copy, distribute or modify the Program subject to\n  these terms and conditions.  You may not impose any further\n  restrictions on the recipients' exercise of the rights granted herein.\n  You are not responsible for enforcing compliance by third parties to\n  this License.\n\n    7. If, as a consequence of a court judgment or allegation of patent\n  infringement or for any other reason (not limited to patent issues),\n  conditions are imposed on you (whether by court order, agreement or\n  otherwise) that contradict the conditions of this License, they do not\n  excuse you from the conditions of this License.  If you cannot\n  distribute so as to satisfy simultaneously your obligations under this\n  License and any other pertinent obligations, then as a consequence you\n  may not distribute the Program at all.  For example, if a patent\n  license would not permit royalty-free redistribution of the Program by\n  all those who receive copies directly or indirectly through you, then\n  the only way you could satisfy both it and this License would be to\n  refrain entirely from distribution of the Program.\n\n  If any portion of this section is held invalid or unenforceable under\n  any particular circumstance, the balance of the section is intended to\n  apply and the section as a whole is intended to apply in other\n  circumstances.\n\n  It is not the purpose of this section to induce you to infringe any\n  patents or other property right claims or to contest validity of any\n  such claims; this section has the sole purpose of protecting the\n  integrity of the free software distribution system, which is\n  implemented by public license practices.  Many people have made\n  generous contributions to the wide range of software distributed\n  through that system in reliance on consistent application of that\n  system; it is up to the author/donor to decide if he or she is willing\n  to distribute software through any other system and a licensee cannot\n  impose that choice.\n\n  This section is intended to make thoroughly clear what is believed to\n  be a consequence of the rest of this License.\n\n    8. If the distribution and/or use of the Program is restricted in\n  certain countries either by patents or by copyrighted interfaces, the\n  original copyright holder who places the Program under this License\n  may add an explicit geographical distribution limitation excluding\n  those countries, so that distribution is permitted only in or among\n  countries not thus excluded.  In such case, this License incorporates\n  the limitation as if written in the body of this License.\n\n    9. The Free Software Foundation may publish revised and/or new versions\n  of the General Public License from time to time.  Such new versions will\n  be similar in spirit to the present version, but may differ in detail to\n  address new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the Program\n  specifies a version number of this License which applies to it and \"any\n  later version\", you have the option of following the terms and conditions\n  either of that version or of any later version published by the Free\n  Software Foundation.  If the Program does not specify a version number of\n  this License, you may choose any version ever published by the Free Software\n  Foundation.\n\n    10. If you wish to incorporate parts of the Program into other free\n  programs whose distribution conditions are different, write to the author\n  to ask for permission.  For software which is copyrighted by the Free\n  Software Foundation, write to the Free Software Foundation; we sometimes\n  make exceptions for this.  Our decision will be guided by the two goals\n  of preserving the free status of all derivatives of our free software and\n  of promoting the sharing and reuse of software generally.\n\n\t    NO WARRANTY\n\n    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n  FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\n  OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n  PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n  OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\n  TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\n  PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n  REPAIR OR CORRECTION.\n\n    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n  WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n  REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n  INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n  OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n  TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n  YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n  PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n  POSSIBILITY OF SUCH DAMAGES.\n\n\t   END OF TERMS AND CONDITIONS\n\n\tHow to Apply These Terms to Your New Programs\n\n    If you develop a new program, and you want it to be of the greatest\n  possible use to the public, the best way to achieve this is to make it\n  free software which everyone can redistribute and change under these terms.\n\n    To do so, attach the following notices to the program.  It is safest\n  to attach them to the start of each source file to most effectively\n  convey the exclusion of warranty; and each file should have at least\n  the \"copyright\" line and a pointer to where the full notice is found.\n\n      <one line to give the program's name and a brief idea of what it does.>\n      Copyright (C) <year>  <name of author>\n\n      This program is free software; you can redistribute it and/or modify\n      it under the terms of the GNU General Public License as published by\n      the Free Software Foundation; either version 2 of the License, or\n      (at your option) any later version.\n\n      This program is distributed in the hope that it will be useful,\n      but WITHOUT ANY WARRANTY; without even the implied warranty of\n      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n      GNU General Public License for more details.\n\n      You should have received a copy of the GNU General Public License along\n      with this program; if not, write to the Free Software Foundation, Inc.,\n      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\n  Also add information on how to contact you by electronic and paper mail.\n\n  If the program is interactive, make it output a short notice like this\n  when it starts in an interactive mode:\n\n      Gnomovision version 69, Copyright (C) year name of author\n      Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n      This is free software, and you are welcome to redistribute it\n      under certain conditions; type `show c' for details.\n\n  The hypothetical commands `show w' and `show c' should show the appropriate\n  parts of the General Public License.  Of course, the commands you use may\n  be called something other than `show w' and `show c'; they could even be\n  mouse-clicks or menu items--whatever suits your program.\n\n  You should also get your employer (if you work as a programmer) or your\n  school, if any, to sign a \"copyright disclaimer\" for the program, if\n  necessary.  Here is a sample; alter the names:\n\n    Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n    `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n    <signature of Ty Coon>, 1 April 1989\n    Ty Coon, President of Vice\n\n  This General Public License does not permit incorporating your program into\n  proprietary programs.  If your program is a subroutine library, you may\n  consider it more useful to permit linking proprietary applications with the\n  library.  If this is what you want to do, use the GNU Lesser General\n  Public License instead of this License.\n============================================================================\n\nAdditionally, some files (currently the contents of\ntoolsrc/org/mozilla/javascript/tools/debugger/treetable/) are available\nonly under the following license:\n\n============================================================================\n * Copyright 1997, 1998 Sun Microsystems, Inc.  All Rights Reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n *   - Redistributions of source code must retain the above copyright\n *     notice, this list of conditions and the following disclaimer.\n *\n *   - Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *\n *   - Neither the name of Sun Microsystems nor the names of its\n *     contributors may be used to endorse or promote products derived\n *     from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\n * IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\n * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n============================================================================\n"
  },
  {
    "path": "lib/rhino/download.url",
    "content": "[InternetShortcut]\r\nURL=http://www.mozilla.org/rhino/download.html\r\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"countdown\",\n  \"version\": \"2.6.1\",\n  \"description\": \"A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.\",\n  \"homepage\": \"http://countdownjs.org\",\n  \"author\": \"Stephen McKamey (http://mck.me)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/mckamey/countdownjs.git\"\n  },\n  \"files\": [ \"countdown.js\", \"README.md\", \"LICENSE.txt\" ],\n  \"main\": \"countdown.js\",\n  \"keywords\": [\n    \"countdown\",\n    \"timer\",\n    \"clock\",\n    \"date\",\n    \"time\",\n    \"timespan\",\n    \"year\",\n    \"month\",\n    \"week\",\n    \"day\",\n    \"hour\",\n    \"minute\",\n    \"second\"\n  ]\n}\n"
  },
  {
    "path": "readme.html",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Countdown.js</title>\n\n\t<link href=\"test/styles.css\" rel=\"stylesheet\" type=\"text/css\" />\n</head>\n<body>\n\n<h1><a href=\"/\">Countdown.js</a></h1>\n<div style=\"float:right\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\" data-count=\"none\"></a></div>\n\n<p>A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.</p>\n\n<hr />\n\n<h2>The Motivation</h2>\n\n<p>While seemingly a trivial problem, the human descriptions for a span of time tend to be fuzzier than a computer naturally computes.\nMore specifically, months are an inherently messed up unit of time.\nFor instance, when a human says \"in 1 month\" how long do they mean? Banks often interpret this as <em>thirty days</em> but that is only correct one third of the time.\nPeople casually talk about a month being <em>four weeks long</em> but there is only one month in a year which is four weeks long and it is only that long about three quarters of the time.\nEven intuitively defining these terms can be problematic. For instance, what is the date one month after January 31st, 2001?\nJavaScript will happily call this March 3rd, 2001. Humans will typically debate either February 28th, 2001 or March 1st, 2001.\nIt seems there isn't a \"right\" answer, per se.</p>\n\n<h2>The Algorithm</h2>\n\n<p><em>Countdown.js</em> emphasizes producing intuitively correct description of timespans which are consistent as time goes on.\nTo do this, <em>Countdown.js</em> uses the concept of \"today's date next month\" to mean \"a month from now\".\nAs the days go by, <em>Countdown.js</em> produces consecutively increasing or decreasing counts without inconsistent jumps.\nThe range of accuracy is only limited by the underlying system clock.</p>\n\n<p><em>Countdown.js</em> approaches finding the difference between two times like an elementary school subtraction problem.\nEach unit acts like a base-10 place where any overflow is carried to the next highest unit, and any underflow is borrowed from the next highest unit.\nIn base-10 subtraction, every column is worth 10 times the previous column.\nWith time, it is a little more complex since the conversions between the units of time are not the same and months are an inconsistent number of days.\nInternally, <em>Countdown.js</em> maintains the concept of a \"reference month\" which determines how many days a given month or year represents.\nIn the final step of the algorithm, <em>Countdown.js</em> then prunes the set of time units down to only those requested, forcing larger units down to smaller.</p>\n\n<div id=\"v2.4.0-timezone\" class=\"breaking-change\">\n<h3>Time Zones &amp; Daylight Savings Time</h3>\n\n<p>As of v2.4, <em>Countdown.js</em> performs all calculations with respect to the <strong>viewer's local time zone</strong>.\nEarlier versions performed difference calculations in UTC, which is generally the correct way to do math on time.\nIn this situation, however, an issue with using UTC happens when either of the two dates being worked with is within one time zone offset of a month boundary.\nIf the UTC interpretation of that instant in time is in a different month than that of the local time zone, then the viewer's perception is that the calculated time span is incorrect.\nThis is the heart of the problem that <em>Countdown.js</em> attempts to solve: talking about spans of time can be ambiguous.\nNearly all bugs reported for <em>Countdown.js</em> have been because the viewer expects something different due to their particular time zone.</p>\n\n<p>JavaScript (<a href=\"http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.7\">ECMA-262</a>) only works with dates as UTC or the local time zone, not arbitrary time zones.\nBy design, all JS Date objects represent an instant in time (milliseconds since midnight Jan 1, 1970 <strong>in UTC</strong>) interpreted as the user's local time.\nSince most humans think about local time not UTC, it the most makes sense to perform this time span algorithm in reference to local time.</p>\n\n<p>Daylight Savings Time further complicates things, creating hours which get repeated and hours which cannot exist.\n<em>Countdown.js</em> effectively ignores these edge cases and talks about time preferring human intuition about time over surprise exactness.\nExample: A viewer asks for the description from noon the day before a daylight savings begins to noon the day after.\nA computer would answer \"23 hours\" whereas a human would confidently answer \"1 day\" even after being reminded to \"Spring Forward\".\nThe computer is technically more accurate but this is not the value that humans actually expect or desire.\nHumans pretend that time is simple and makes sense. Unfortunately, humans made time far more complex than it needed to be with time zones and daylight savings.\nUTC simplifies time but at the cost of being inconsistent with human experience.</p>\n</div>\n\n<hr />\n\n<h2>The API</h2>\n\n<p>A simple but flexible API is the goal of <em>Countdown.js</em>. There is one global function with a set of static constants:</p>\n\n<pre><code>var timespan = countdown(start|callback, end|callback, units, max, digits);</code></pre>\n\n<p>The parameters are a starting Date, ending Date, an optional set of units, an optional maximum number of units, and an optional maximum number of decimal places on the smallest unit. <code>units</code> defaults to <code>countdown.DEFAULTS</code>, <code>max</code> defaults to <code>NaN</code> (all specified units), <code>digits</code> defaults to <code>0</code>.</p>\n\n<pre><code>countdown.ALL =\n    countdown.MILLENNIA |\n    countdown.CENTURIES |\n    countdown.DECADES |\n    countdown.YEARS |\n    countdown.MONTHS |\n    countdown.WEEKS |\n    countdown.DAYS |\n    countdown.HOURS |\n    countdown.MINUTES |\n    countdown.SECONDS |\n    countdown.MILLISECONDS;\n\ncountdown.DEFAULTS =\n    countdown.YEARS |\n    countdown.MONTHS |\n    countdown.DAYS |\n    countdown.HOURS |\n    countdown.MINUTES |\n    countdown.SECONDS;</code></pre>\n\n<p>This allows a very minimal call to accept the defaults and get the time since/until a single date. For example:</p>\n\n<pre><code>countdown( new Date(2000, 0, 1) ).toString();</code></pre>\n\n<p>This will produce a human readable description like:</p>\n\n<pre><code>11 years, 8 months, 4 days, 10 hours, 12 minutes and 43 seconds</code></pre>\n\n<h3>The <code>start</code> / <code>end</code> arguments</h3>\n\n<p>The parameters <code>start</code> and <code>end</code> can be one of several values:</p>\n\n<ol>\n\t<li><code>null</code> which indicates \"now\".</li>\n\t<li>a JavaScript <code>Date</code> object.</li>\n\t<li>a <code>number</code> specifying the number of milliseconds since midnight Jan 1, 1970 UTC (i.e., the \"UNIX epoch\").</li>\n\t<li>a callback <code>function</code> accepting one timespan argument.</li>\n</ol>\n\n<div id=\"v2.4.0-dates\" class=\"breaking-change\">\n<p>To reference a specific instant in time, either use a <code>number</code> offset from the epoch, or a JavaScript <code>Date</code> object instantiated with the specific offset from the epoch.\nIn JavaScript, if a <code>Date</code> object is instantiated using year/month/date/etc values, then those values are interpreted in reference to the browser's local time zone and daylight savings settings.</p>\n</div>\n\n<p>If <code>start</code> and <code>end</code> are both specified, then repeated calls to <code>countdown(&hellip;)</code> will always return the same result.\nIf one date argument is left <code>null</code> while the other is provided, then repeated calls will count up if the provided date is in the past, and it will count down if the provided date is in the future.\nFor example,</p>\n\n<pre><code>var daysSinceLastWorkplaceAccident = countdown(507314280000, null, countdown.DAYS);</code></pre>\n\n<p>If a callback function is supplied, then an interval timer will be started with a frequency based upon the smallest unit (e.g., if <code>countdown.SECONDS</code> is the smallest unit, the callback will be invoked once per second). Rather than returning a Timespan object, the timer's ID will be returned to allow canceling by passing into <code>window.clearInterval(id)</code>. For example, to show a timer since the page first loaded:</p>\n\n<pre><code>var timerId =\n  countdown(\n    new Date(),\n    function(ts) {\n      document.getElementById('pageTimer').innerHTML = ts.toHTML(\"strong\");\n    },\n    countdown.HOURS|countdown.MINUTES|countdown.SECONDS);\n\n// later on this timer may be stopped\nwindow.clearInterval(timerId);</code></pre>\n\n<h3>The <code>units</code> argument</h3>\n\n<p>The static units constants can be combined using <a href=\"https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators\">standard bitwise operators</a>. For example, to explicitly include \"months or days\" use bitwise-OR:</p>\n\n<pre><code>countdown.MONTHS | countdown.DAYS</code></pre>\n\n<p>To explicitly exclude units like \"not weeks and not milliseconds\" combine bitwise-NOT and bitwise-AND:</p>\n\n<pre><code>~countdown.WEEKS &amp; ~countdown.MILLISECONDS</code></pre>\n\n<p><a href=\"http://en.wikipedia.org/wiki/De_Morgan's_laws\">Equivalently</a>, to specify everything but \"not weeks or milliseconds\" wrap bitwise-NOT around bitwise-OR:</p>\n\n<pre><code>~(countdown.WEEKS | countdown.MILLISECONDS)</code></pre>\n\n<h3>The <code>max</code> argument</h3>\n\n<p>The next optional argument <code>max</code> specifies a maximum number of unit labels to display. This allows specifying which units are interesting but only displaying the <code>max</code> most significant units.</p>\n\n<pre><code>countdown(start, end, units).toString() =&gt; \"5 years, 1 month, 19 days, 12 hours and 17 minutes\"</code></pre>\n\n<p>Specifying <code>max</code> as <code>2</code> ensures that only the two most significant units are displayed <strong>(note the rounding of the least significant unit)</strong>:</p>\n\n<pre><code>countdown(start, end, units, 2).toString() =&gt; \"5 years and 2 months\"</code></pre>\n\n<p>Negative or zero values of <code>max</code> are ignored.</p>\n\n<div id=\"v2.3.0-max\" class=\"breaking-change\">\n\n<h4>Breaking change in v2.3.0!</h4>\n\n<p>Previously, the <code>max</code> number of unit labels argument used to be specified when formatting in <code>timespan.toString(&hellip;)</code> and <code>timespan.toHTML(&hellip;)</code>. v2.3.0 moves it to <code>countdown(&hellip;)</code>, which improves efficiency as well as enabling fractional units (<a href=\"#v2.3.0-digits\">see below</a>).</p>\n\n</div>\n\n<h3>The <code>digits</code> argument</h3>\n\n<p>The final optional argument <code>digits</code> allows fractional values on the smallest unit.</p>\n\n<pre><code>countdown(start, end, units, max).toString() =&gt; \"5 years and 2 months\"</code></pre>\n\n<p>Specifying <code>digits</code> as <code>2</code> allows up to 2 digits beyond the decimal point to be displayed <strong>(note the rounding of the least significant unit)</strong>:</p>\n\n<pre><code>countdown(start, end, units, max, 2).toString() =&gt; \"5 years and 1.65 months\"</code></pre>\n\n<p><code>digits</code> must be between <code>0</code> and <code>20</code>, inclusive.</p>\n\n<div id=\"v2.3.0-digits\" class=\"breaking-change\">\n\n<h4>Rounding</h4>\n\n<p>With the calculations of fractional units in v2.3.0, the smallest displayed unit now properly rounds. Previously, the equivalent of <code>\"1.99 years\"</code> would be truncated to <code>\"1 year\"</code>, as of v2.3.0 it will display as <code>\"2 years\"</code>.</p>\n<p>Typically, this is the intended interpretation but there are a few circumstances where people expect the truncated behavior. For example, people often talk about their age as the lowest possible interpretation. e.g., they claim \"39-years-old\" right up until the morning of their 40th birthday (some people do even for years after!). In these cases, after calling <code>countdown(start,end,units,max,20)</code> with the largest possible number of <code>digits</code>, you might want to set <code>ts.years = Math.floor(ts.years)</code> before calling <code>ts.toString()</code>. The vain might want you to set <code>ts.years = Math.min(ts.years, 39)</code>!</p>\n\n</div>\n\n<h3>Timespan result</h3>\n\n<p>The return value is a Timespan object which always contains the following fields:</p>\n\n<ul>\n\t<li><code>Date start</code>: the starting date object used for the calculation</li>\n\t<li><code>Date end</code>: the ending date object used for the calculation</li>\n\t<li><code>Number units</code>: the units specified</li>\n\t<li><code>Number value</code>: total milliseconds difference (i.e., <code>end</code> - <code>start</code>). If <code>end &lt; start</code> then <code>value</code> will be negative.</li>\n</ul>\n\n<p>Typically the <code>end</code> occurs after <code>start</code>, but if the arguments were reversed, the only difference is <code>Timespan.value</code> will be negative. The sign of <code>value</code> can be used to determine if the event occurs in the future or in the past.</p>\n\n<p>The following time unit fields are only present if their corresponding units were requested:</p>\n\n<ul>\n\t<li><code>Number millennia</code></li>\n\t<li><code>Number centuries</code></li>\n\t<li><code>Number decades</code></li>\n\t<li><code>Number years</code></li>\n\t<li><code>Number months</code></li>\n\t<li><code>Number days</code></li>\n\t<li><code>Number hours</code></li>\n\t<li><code>Number minutes</code></li>\n\t<li><code>Number seconds</code></li>\n\t<li><code>Number milliseconds</code></li>\n</ul>\n\n<p>Finally, Timespan has two formatting methods each with some optional parameters. If the difference between <code>start</code> and <code>end</code> is less than the requested granularity of units, then <code>toString(&hellip;)</code> and <code>toHTML(&hellip;)</code> will return the empty label (defaults to an empty string).</p>\n\n<ul>\n\n<li><code>String toString(emptyLabel)</code>: formats the Timespan object as an English sentence. e.g., using the same input:\n\n<pre><code>ts.toString() =&gt; \"5 years, 1 month, 19 days, 12 hours and 17 minutes\"</code></pre></li>\n\n<li><code>String toHTML(tagName, emptyLabel)</code>: formats the Timespan object as an English sentence, with the specified HTML tag wrapped around each unit. If no tag name is provided, \"<code>span</code>\" is used. e.g., using the same input:\n\n<pre><code>ts.toHTML() =&gt; \"&lt;span&gt;5 years&lt;/span&gt;, &lt;span&gt;1 month&lt;/span&gt;, &lt;span&gt;19 days&lt;/span&gt;, &lt;span&gt;12 hours&lt;/span&gt; and &lt;span&gt;17 minutes&lt;/span&gt;\"\n\nts.toHTML(\"em\") =&gt; \"&lt;em&gt;5 years&lt;/em&gt;, &lt;em&gt;1 month&lt;/em&gt;, &lt;em&gt;19 days&lt;/em&gt;, &lt;em&gt;12 hours&lt;/em&gt; and &lt;em&gt;17 minutes&lt;/em&gt;\"</code></pre></li>\n\n</ul>\n\n<h3>Localization</h3>\n\n<p>Very basic localization is supported via the static <code>setLabels</code> and <code>resetLabels</code> methods. These change the functionality for all timespans on the page.</p>\n\n<pre><code>countdown.resetLabels();\n\ncountdown.setLabels(singular, plural, last, delim, empty, formatter);\n</code></pre>\n\n<p>The arguments:<br>\n<ul>\n\t<li><code>singular</code> is a pipe (<code>'|'</code>) delimited ascending list of singular unit name overrides\n\t<li><code>plural</code> is a pipe (<code>'|'</code>) delimited ascending list of plural unit name overrides\n\t<li><code>last</code> is a delimiter before the last unit (default: <code>' and '</code>)\n\t<li><code>delim</code> is a delimiter to use between all other units (default: <code>', '</code>)\n\t<li><code>empty</code> is a label to use when all units are zero (default: <code>''</code>)\n\t<li><code>formatter</code> is a function which takes a <code>number</code> and returns a <code>string</code> (default uses <code>Number.toString()</code>),<br>\n\tallowing customization of the way numbers are formatted, e.g., commas every 3 digits or some unique style that is specific to your locale.\n</ul>\nNote that the spacing is part of the labels.</p>\n\n<p>The following examples would translate the output into Brazilian Portuguese and French, respectively:</p>\n\n<pre><code>countdown.setLabels(\n\t' milissegundo| segundo| minuto| hora| dia| semana| mês| ano| década| século| milênio',\n\t' milissegundos| segundos| minutos| horas| dias| semanas| meses| anos| décadas| séculos| milênios',\n\t' e ',\n\t' + ',\n\t'agora');\n\ncountdown.setLabels(\n\t' milliseconde| seconde| minute| heure| jour| semaine| mois| année| décennie| siècle| millénaire',\n\t' millisecondes| secondes| minutes| heures| jours| semaines| mois| années| décennies| siècles| millénaires',\n\t' et ',\n\t', ',\n\t'maintenant');\n</code></pre>\n\n<p>If you only wanted to override some of the labels just leave the other pipe-delimited places empty. Similarly, leave off any of the delimiter arguments which do not need overriding.</p>\n\n<pre><code>countdown.setLabels(\n\t'||| hr| d',\n\t'ms| sec|||| wks|| yrs',\n\t', and finally ');\n\nts.toString() =&gt; \"1 millennium, 2 centuries, 5 yrs, 1 month, 7 wks, 19 days, 1 hr, 2 minutes, 17 sec, and finally 1 millisecond\"\n</code></pre>\n\n<p>If you only wanted to override the empty label:</p>\n\n<pre><code>countdown.setLabels(\n\t\tnull,\n\t\tnull,\n\t\tnull,\n\t\tnull,\n\t\t'Now.');\n\nts.toString() =&gt; \"Now.\"\n</code></pre>\n\n<p>The following would be effectively the same as calling <code>countdown.resetLabels()</code>:</p>\n\n<pre><code>countdown.setLabels(\n\t' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium',\n\t' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia',\n\t' and ',\n\t', ',\n\t'',\n\tfunction(n){ return n.toString(); });\n</code></pre>\n\n<h2>License</h2>\n\n<p>Distributed under the terms of <a href=\"https://raw.githubusercontent.com/mckamey/countdownjs/master/LICENSE.txt\">The MIT license</a>.</p>\n\n<footer>\n\t<div style=\"float:left\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\"></a></div>\n\tCopyright &copy; 2006-2014 <a href=\"http://mck.me\">Stephen M. McKamey</a>\n</footer>\n<script src=\"/ga.js\" type=\"text/javascript\" defer></script>\n<script src=\"http://platform.twitter.com/widgets.js\" type=\"text/javascript\" defer=\"defer\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "test/fonts/SIL Open Font License 1.1.txt",
    "content": "This Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded, \nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE."
  },
  {
    "path": "test/formatTests.js",
    "content": "try{\r\n\r\nmodule('Timespan.toString()');\r\n\r\ntest('Default empty label override', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ empty: 'Now.' });\r\n\r\n\tvar expected = 'Now.';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Empty label override', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tvar expected = 'Now!';\r\n\r\n\tvar actual = input.toString('Now!');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Default empty label override, overridden', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ empty: 'Now.' });\r\n\r\n\tvar expected = 'Right now!';\r\n\r\n\tvar actual = input.toString('Right now!');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Zero', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tvar expected = '';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 ms', function() {\r\n\r\n\tvar input = countdown(0, 1, countdown.ALL);\r\n\r\n\tvar expected = '1 millisecond';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('2 ms', function() {\r\n\r\n\tvar input = countdown(0, 2, countdown.ALL);\r\n\r\n\tvar expected = '2 milliseconds';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 sec, 2 ms', function() {\r\n\r\n\tvar input = countdown(1000, 2002, countdown.ALL);\r\n\r\n\tvar expected = '1 second and 2 milliseconds';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('2 sec, 1 ms', function() {\r\n\r\n\tvar input = countdown(10000, 12001, countdown.ALL);\r\n\r\n\tvar expected = '2 seconds and 1 millisecond';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 day, reversed', function() {\r\n\r\n\tvar start = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tvar end = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tend.setDate( end.getDate()+1 );\r\n\tvar input = countdown(end, start, countdown.ALL);\r\n\r\n\tvar expected = '1 day';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('15 days', function() {\r\n\r\n\tvar start = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tvar end = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tend.setDate( end.getDate()+15 );\r\n\r\n\tvar input = countdown(end, start, countdown.ALL);\r\n\r\n\tvar expected = '2 weeks and 1 day';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('32 days', function() {\r\n\r\n\tvar start = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tvar end = new Date(1970, 0, 1, 0, 0, 0, 0);\r\n\tend.setDate( end.getDate()+32 );\r\n\r\n\tvar input = countdown(end, start, countdown.ALL);\r\n\r\n\tvar expected = '1 month and 1 day';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Millennium, week', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(10 * 100 * 365.25 * 24 * 60 * 60 * 1000);// millennium, week\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '1 millennium and 1 week';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('One of each', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '1 millennium, 1 century, 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute, 1 second and 1 millisecond';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Two of each', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(22 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(2 * 365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 + 29) * (24 * 60 * 60 * 1000) + // month\r\n\t\t(2 * 60 * 60 * 1000) + // hour\r\n\t\t(2 * 60 * 1000) + // min\r\n\t\t(2 * 1000) + // sec\r\n\t\t2); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '2 millennia, 2 centuries, 2 years, 2 months, 2 weeks, 2 days, 2 hours, 2 minutes, 2 seconds and 2 milliseconds';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\nmodule('Timespan.toString(number)');\r\n\r\ntest('Millennium, week; 1 max', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(10 * 100 * 365.25 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL, 1);\r\n\r\n\tvar expected = '1 millennium';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Millennium, week; 2 max', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(10 * 100 * 365.25 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL, 2);\r\n\r\n\tvar expected = '1 millennium and 1 week';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('One of each; 3 max', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL, 3);\r\n\r\n\tvar expected = '1 millennium, 1 century and 1 year';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('One of each; zero max', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL, 0);\r\n\r\n\tvar expected = '1 millennium, 1 century, 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute, 1 second and 1 millisecond';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('One of each; -2 max', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL, -2);\r\n\r\n\tvar expected = '1 millennium, 1 century, 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute, 1 second and 1 millisecond';\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Almost 2 minutes, full 3 digits', function() {\r\n\r\n\tvar input = countdown(new Date(915220800000), new Date(915220919999), countdown.DEFAULTS, 0, 3);\r\n\r\n\tvar expected = \"1 minute and 59.999 seconds\";\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Almost 2 minutes, rounded 2 digits', function() {\r\n\r\n\tvar input = countdown(new Date(915220800000), new Date(915220919999), countdown.DEFAULTS, 0, 2);\r\n\r\n\tvar expected = \"2 minutes\";\r\n\r\n\tvar actual = input.toString();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\nmodule('Timespan.toHTML(tag)');\r\n\r\ntest('Default empty label override', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ empty: 'Now.' });\r\n\r\n\tvar expected = '<span>Now.</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '<span>Now.</span>');\r\n});\r\n\r\ntest('Empty label override', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tvar expected = '<span>Now!</span>';\r\n\r\n\tvar actual = input.toHTML(null, 'Now!');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Default empty label override, overridden', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ empty: 'Now.' });\r\n\r\n\tvar expected = '<span>Right now!</span>';\r\n\r\n\tvar actual = input.toHTML(null, 'Right now!');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Zero', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tvar expected = '';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Empty label and tag override', function() {\r\n\r\n\tvar input = countdown(0, 0, countdown.ALL);\r\n\r\n\tvar expected = '<em>Now!</em>';\r\n\r\n\tvar actual = input.toHTML('em', 'Now!');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 ms', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(1);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>1 millisecond</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('2 days, reversed', function() {\r\n\r\n\tvar start = new Date(2 * 24 * 60 * 60 * 1000);\r\n\tvar end = new Date(0);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>2 days</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('8 days', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(8 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>1 week</span> and <span>1 day</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('70 days', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(70 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>2 months</span>, <span>1 week</span> and <span>4 days</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('366 days, non-leap year', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(366 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>1 year</span> and <span>1 day</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('366 days, leap year', function() {\r\n\r\n\tvar start = new Date(2000, 0, 1);\r\n\tvar end = new Date(start.getTime() + 366 * 24 * 60 * 60 * 1000);\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected = '<span>1 year</span>';\r\n\r\n\tvar actual = input.toHTML();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('One of each', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tvar expected =\r\n\t\t'<em>1 millennium</em>, ' +\r\n\t\t'<em>1 century</em>, ' +\r\n\t\t'<em>1 year</em>, ' +\r\n\t\t'<em>1 month</em>, ' +\r\n\t\t'<em>1 week</em>, ' +\r\n\t\t'<em>1 day</em>, ' +\r\n\t\t'<em>1 hour</em>, ' +\r\n\t\t'<em>1 minute</em>, ' +\r\n\t\t'<em>1 second</em> and ' +\r\n\t\t'<em>1 millisecond</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Singular overrides', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ singular: ' a| b| c| d| e| f| g| h| i| j| k' });\r\n\r\n\tvar expected =\r\n\t\t'<em>1 k</em>, ' +\r\n\t\t'<em>1 j</em>, ' +\r\n\t\t'<em>1 h</em>, ' +\r\n\t\t'<em>1 g</em>, ' +\r\n\t\t'<em>1 f</em>, ' +\r\n\t\t'<em>1 e</em>, ' +\r\n\t\t'<em>1 d</em>, ' +\r\n\t\t'<em>1 c</em>, ' +\r\n\t\t'<em>1 b</em> and ' +\r\n\t\t'<em>1 a</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>1 millennium</em>, ' +\r\n\t\t'<em>1 century</em>, ' +\r\n\t\t'<em>1 year</em>, ' +\r\n\t\t'<em>1 month</em>, ' +\r\n\t\t'<em>1 week</em>, ' +\r\n\t\t'<em>1 day</em>, ' +\r\n\t\t'<em>1 hour</em>, ' +\r\n\t\t'<em>1 minute</em>, ' +\r\n\t\t'<em>1 second</em> and ' +\r\n\t\t'<em>1 millisecond</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Plural overrides', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(22 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(2 * 365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 + 29) * (24 * 60 * 60 * 1000) + // month\r\n\t\t(2 * 60 * 60 * 1000) + // hour\r\n\t\t(2 * 60 * 1000) + // min\r\n\t\t(2 * 1000) + // sec\r\n\t\t2); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ plural: ' A| B| C| D| E| F| G| H| I| J| K', last: ' &amp; ', delim: ' &amp; ' });\r\n\r\n\tvar expected =\r\n\t\t'<em>2 K</em> &amp; ' +\r\n\t\t'<em>2 J</em> &amp; ' +\r\n\t\t'<em>2 H</em> &amp; ' +\r\n\t\t'<em>2 G</em> &amp; ' +\r\n\t\t'<em>2 F</em> &amp; ' +\r\n\t\t'<em>2 E</em> &amp; ' +\r\n\t\t'<em>2 D</em> &amp; ' +\r\n\t\t'<em>2 C</em> &amp; ' +\r\n\t\t'<em>2 B</em> &amp; ' +\r\n\t\t'<em>2 A</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>2 millennia</em>, ' +\r\n\t\t'<em>2 centuries</em>, ' +\r\n\t\t'<em>2 years</em>, ' +\r\n\t\t'<em>2 months</em>, ' +\r\n\t\t'<em>2 weeks</em>, ' +\r\n\t\t'<em>2 days</em>, ' +\r\n\t\t'<em>2 hours</em>, ' +\r\n\t\t'<em>2 minutes</em>, ' +\r\n\t\t'<em>2 seconds</em> and ' +\r\n\t\t'<em>2 milliseconds</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Partial singular overrides', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(11 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 * 24 * 60 * 60 * 1000) + // month\r\n\t\t(60 * 60 * 1000) + // hour\r\n\t\t(60 * 1000) + // min\r\n\t\t1000 + // sec\r\n\t\t1); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({\r\n\t\tsingular: ' a|| c|| e|| g|| i|| k',\r\n\t\tplural: '| B|| D|| F|| H|| J|',\r\n\t\tlast: ' + finally ',\r\n\t\tdelim: ' + '\r\n\t});\r\n\r\n\tvar expected =\r\n\t\t'<em>1 k</em> + ' +\r\n\t\t'<em>1 century</em> + ' +\r\n\t\t'<em>1 year</em> + ' +\r\n\t\t'<em>1 g</em> + ' +\r\n\t\t'<em>1 week</em> + ' +\r\n\t\t'<em>1 e</em> + ' +\r\n\t\t'<em>1 hour</em> + ' +\r\n\t\t'<em>1 c</em> + ' +\r\n\t\t'<em>1 second</em> + finally ' +\r\n\t\t'<em>1 a</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>1 millennium</em>, ' +\r\n\t\t'<em>1 century</em>, ' +\r\n\t\t'<em>1 year</em>, ' +\r\n\t\t'<em>1 month</em>, ' +\r\n\t\t'<em>1 week</em>, ' +\r\n\t\t'<em>1 day</em>, ' +\r\n\t\t'<em>1 hour</em>, ' +\r\n\t\t'<em>1 minute</em>, ' +\r\n\t\t'<em>1 second</em> and ' +\r\n\t\t'<em>1 millisecond</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Partial plural overrides', function() {\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(22 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennium, century, week, day\r\n\t\t(2 * 365 * 24 * 60 * 60 * 1000) + // year\r\n\t\t(31 + 29) * (24 * 60 * 60 * 1000) + // month\r\n\t\t(2 * 60 * 60 * 1000) + // hour\r\n\t\t(2 * 60 * 1000) + // min\r\n\t\t(2 * 1000) + // sec\r\n\t\t2); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({\r\n\t\tsingular: ' a|| c|| e|| g|| i|| k',\r\n\t\tplural: '| B|| D|| F|| H|| J|',\r\n\t\tlast: ', '\r\n\t});\r\n\r\n\tvar expected =\r\n\t\t'<em>2 millennia</em>, ' +\r\n\t\t'<em>2 J</em>, ' +\r\n\t\t'<em>2 H</em>, ' +\r\n\t\t'<em>2 months</em>, ' +\r\n\t\t'<em>2 F</em>, ' +\r\n\t\t'<em>2 days</em>, ' +\r\n\t\t'<em>2 D</em>, ' +\r\n\t\t'<em>2 minutes</em>, ' +\r\n\t\t'<em>2 B</em>, ' +\r\n\t\t'<em>2 milliseconds</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>2 millennia</em>, ' +\r\n\t\t'<em>2 centuries</em>, ' +\r\n\t\t'<em>2 years</em>, ' +\r\n\t\t'<em>2 months</em>, ' +\r\n\t\t'<em>2 weeks</em>, ' +\r\n\t\t'<em>2 days</em>, ' +\r\n\t\t'<em>2 hours</em>, ' +\r\n\t\t'<em>2 minutes</em>, ' +\r\n\t\t'<em>2 seconds</em> and ' +\r\n\t\t'<em>2 milliseconds</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Custom number formatter', function() {\r\n\r\n\tvar sillyFormatter = function(num) {\r\n\t\tnum = ''+num;\r\n\t\tvar str = '';\r\n\t\tfor (var i=0, len=num.length; i<len; i++) {\r\n\t\t\tswitch (num.charAt(i)) {\r\n\t\t\t\tcase '0': str += 'zero'; break;\r\n\t\t\t\tcase '1': str += 'one'; break;\r\n\t\t\t\tcase '2': str += 'two'; break;\r\n\t\t\t\tcase '3': str += 'three'; break;\r\n\t\t\t\tcase '4': str += 'four'; break;\r\n\t\t\t\tcase '5': str += 'five'; break;\r\n\t\t\t\tcase '6': str += 'six'; break;\r\n\t\t\t\tcase '7': str += 'seven'; break;\r\n\t\t\t\tcase '8': str += 'eight'; break;\r\n\t\t\t\tcase '9': str += 'nine'; break;\r\n\t\t\t\tcase '.': str += 'dot'; break;\r\n\t\t\t\tcase '-': str += 'dash'; break;\r\n\t\t\t\tdefault: str += '?!?'; break;\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn str;\r\n\t};\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(87 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennia, centuries, weeks, days\r\n\t\t(2 * 365 * 24 * 60 * 60 * 1000) + // years\r\n\t\t(4 * 31 * 24 * 60 * 60 * 1000) + // months\r\n\t\t(4 * 60 * 60 * 1000) + // hours\r\n\t\t(31 * 60 * 1000) + // mins\r\n\t\t(55 * 1000) + // secs\r\n\t\t900); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({ formatNumber: sillyFormatter });\r\n\r\n\tvar expected =\r\n\t\t'<em>eight millennia</em>, ' +\r\n\t\t'<em>seven centuries</em>, ' +\r\n\t\t'<em>two years</em>, ' +\r\n\t\t'<em>six months</em>, ' +\r\n\t\t'<em>one week</em>, ' +\r\n\t\t'<em>four hours</em>, ' +\r\n\t\t'<em>threeone minutes</em>, ' +\r\n\t\t'<em>fivefive seconds</em> and ' +\r\n\t\t'<em>ninezerozero milliseconds</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>8 millennia</em>, ' +\r\n\t\t'<em>7 centuries</em>, ' +\r\n\t\t'<em>2 years</em>, ' +\r\n\t\t'<em>6 months</em>, ' +\r\n\t\t'<em>1 week</em>, ' +\r\n\t\t'<em>4 hours</em>, ' +\r\n\t\t'<em>31 minutes</em>, ' +\r\n\t\t'<em>55 seconds</em> and ' +\r\n\t\t'<em>900 milliseconds</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Custom unit formatter', function() {\r\n\r\n\tvar ONE = ' миллисекунда| секунда| минута| час| день| неделя| месяц| год| декада| столетие| тысячелетие'.split('|');\r\n\tvar TWO = ' миллисекунды| секунды| минуты| часа| дня| недели| месяца| года| декады| столетия| тысячелетия'.split('|');\r\n\tvar MANY = ' миллисекунд| секунд| минут| часов| дней| недель| месяцев| лет| декад| столетий| тысячелетий'.split('|');\r\n\r\n\tvar russianUnits = function(value, unit) {\r\n\t\tif (Math.floor((value % 100) / 10) !== 1) {\r\n\t\t\tvar mod = value % 10;\r\n\t\t\tif (mod === 1) {\r\n\t\t\t\t// singular or plurals ending in 1 except 11\r\n\t\t\t\treturn value+ONE[unit];\r\n\t\t\t}\r\n\t\t\tif (mod >= 2 && mod <= 4) {\r\n\t\t\t\t// plurals ending in 2-4 except 12-14\r\n\t\t\t\treturn value+TWO[unit];\r\n\t\t\t}\r\n\t\t}\r\n\t\t// general plurals\r\n\t\treturn value+MANY[unit];\r\n\t};\r\n\r\n\tvar start = new Date(0);\r\n\tvar end = new Date(\r\n\t\t(87 * 100) * (365.25 * 24 * 60 * 60 * 1000) + // millennia, centuries, weeks, days\r\n\t\t(2 * 365 * 24 * 60 * 60 * 1000) + // years\r\n\t\t(4 * 31 * 24 * 60 * 60 * 1000) + // months\r\n\t\t(4 * 60 * 60 * 1000) + // hours\r\n\t\t(13 * 60 * 1000) + // mins\r\n\t\t(55 * 1000) + // secs\r\n\t\t900); // ms\r\n\r\n\t// account for local timezone\r\n\tstart.setMinutes(start.getMinutes() + start.getTimezoneOffset());\r\n\tend.setMinutes(end.getMinutes() + end.getTimezoneOffset());\r\n\r\n\tvar input = countdown(start, end, countdown.ALL);\r\n\r\n\tcountdown.setFormat({\r\n\t\tlast: ' и ',\r\n\t\tdelim: ', ',\r\n\t\tformatter: russianUnits\r\n\t});\r\n\r\n\tvar expected =\r\n\t\t'<em>8 тысячелетий</em>, ' +\r\n\t\t'<em>7 столетий</em>, ' +\r\n\t\t'<em>2 года</em>, ' +\r\n\t\t'<em>6 месяцев</em>, ' +\r\n\t\t'<em>1 неделя</em>, ' +\r\n\t\t'<em>4 часа</em>, ' +\r\n\t\t'<em>13 минут</em>, ' +\r\n\t\t'<em>55 секунд</em> и ' +\r\n\t\t'<em>900 миллисекунд</em>';\r\n\r\n\tvar actual = input.toHTML('em');\r\n\r\n\tcountdown.resetFormat();\r\n\r\n\tsame(actual, expected, '');\r\n\r\n\texpected =\r\n\t\t'<em>8 millennia</em>, ' +\r\n\t\t'<em>7 centuries</em>, ' +\r\n\t\t'<em>2 years</em>, ' +\r\n\t\t'<em>6 months</em>, ' +\r\n\t\t'<em>1 week</em>, ' +\r\n\t\t'<em>4 hours</em>, ' +\r\n\t\t'<em>13 minutes</em>, ' +\r\n\t\t'<em>55 seconds</em> and ' +\r\n\t\t'<em>900 milliseconds</em>';\r\n\r\n\tactual = input.toHTML('em');\r\n\tsame(actual, expected, '');\r\n});\r\n\r\n}catch(ex){alert(ex);}"
  },
  {
    "path": "test/lint.js",
    "content": "load(\"lib/jslint/jslint.js\");\n\nvar src = readFile(\"countdown.js\");\n\nJSLINT(src, { browser: true, undef: true, eqeqeq: true, regexp: true, newcap: true, maxerr: 100 });\n\nvar ok = {\n\t\"Use '===' to compare with 'null'.\": true,\n\t\"Use '!==' to compare with 'null'.\": true\n};\n\nvar e = JSLINT.errors, found = 0, w;\n\nfor (var i = 0; i < e.length; i++) {\n\tw = e[i];\n\n\tif (!ok[ w.reason ]) {\n\t\tfound++;\n\t\tprint(\"\\n\" + w.evidence + \"\\n\");\n\t\tprint(\"    Problem at line \" + w.line + \" character \" + w.character + \": \" + w.reason);\n\t}\n}\n\nif (found > 0) {\n\tprint(\"\\n\" + found + \" Error(s) found.\");\n\n} else {\n\tprint(\"JSLint check passed.\");\n}\n"
  },
  {
    "path": "test/styles.css",
    "content": "article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}audio:not([controls]){display:none;}[hidden]{display:none;}html{font-size:100%;overflow-y:scroll;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}body{margin:0;}body,button,input,select,textarea{font-family:sans-serif;}a{color:#00e;}a:visited{color:#551a8b;}a:focus{outline:thin dotted;}a:hover,a:active{outline:0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}blockquote{margin:1em 40px;}dfn{font-style:italic;}mark{background:#ff0;color:#000;}pre,code,kbd,samp{font-family:monospace,serif;_font-family:Consolas,'courier new',monospace;font-size:1em;}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word;}q{quotes:none;}q:before,q:after{content:'';content:none;}small{font-size:75%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}ul,ol{margin:1em 0;padding:0 0 0 40px;}dd{margin:0 0 0 40px;}nav ul,nav ol{list-style:none;list-style-image:none;}img{border:0;-ms-interpolation-mode:bicubic;}svg:not(:root){overflow:hidden;}figure{margin:0;}form{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;*margin-left:-7px;}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}button,input{line-height:normal;*overflow:visible;}table button,table input{*overflow:auto;}button,input[type=\"button\"],input[type=\"reset\"],input[type=\"submit\"]{cursor:pointer;-webkit-appearance:button;}input[type=\"checkbox\"],input[type=\"radio\"]{box-sizing:border-box;padding:0;}input[type=\"search\"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type=\"search\"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;}\r\n\r\n@font-face {\r\n\tfont-family: 'LeagueGothicRegular';\r\n\tsrc: url('fonts/League_Gothic-webfont.eot');\t\t\t\t\t\t\t/* IE9 Compat Modes */\r\n\tsrc: url('fonts/League_Gothic-webfont.eot?iefix') format('eot'),\t\t/* IE6-IE8 */\r\n\t\t url('fonts/League_Gothic-webfont.woff') format('woff'),\t\t\t/* Modern Browsers */\r\n\t\t url('fonts/League_Gothic-webfont.ttf')\t format('truetype'),\t\t/* Safari, Android, iOS */\r\n\t\t url('fonts/League_Gothic-webfont.svg#svgFontName') format('svg');\t/* Legacy iOS */\r\n\tfont-weight: normal;\r\n\tfont-style: normal;\r\n}\r\n\r\nbody, input {\r\n\tfont-size: 100%;\r\n}\r\nbody {\r\n\tbackground-color: #F8F8F8;\r\n\tfont-family: \"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Arial;\r\n\tmargin: auto;\r\n\twidth: 80%;\r\n\tmin-width: 600px;\r\n}\r\n\r\nh1, h1 a:link, h1 a:hover, h1 a:active, h1 a:visited {\r\n\tcolor: #999;\r\n\tfont-family: LeagueGothicRegular, 'League Gothic', \"Helvetica Neue Light\", \"HelveticaNeue-Light\", \"Helvetica Neue\", Calibri, Helvetica, Tahoma, Arial, sans-serif;\r\n\ttext-decoration: none;\r\n}\r\nh1 {\r\n\tfont-size: 1000%;\r\n\tmargin: 0 auto 0.25em;\r\n\tpadding: 0;\r\n\ttext-align: center;\r\n\ttext-shadow: 0px -1px 1px silver;\r\n\twhite-space: nowrap;\r\n}\r\nh2 {\r\n\tfont-size: 150%;\r\n}\r\nh3 {\r\n\tfont-size: 125%;\r\n}\r\n\r\na:link, a:hover, a:active, a:visited {\r\n\tcolor: #006699;\r\n}\r\n\r\ndl {\r\n\tmargin: 0;\r\n\tpadding: 0.75em 0.25em;\r\n}\r\n\r\ndl dt {\r\n\tfont-weight: bold;\r\n\tmargin: 0 0 0 0.5em;\r\n\tpadding: 0.25em 0 0.25em 0.5em;\r\n}\r\n\r\ndl dd {\r\n\tlist-style-type: none;\r\n\tmargin: 0 0 0 1.5em;\r\n\tpadding: 0.25em 0 0.25em 1.5em;\r\n}\r\n\r\npre {\r\n\tbackground: #F7F7F7;\r\n\tborder: 1px solid #DDD;\r\n\tborder-radius: 3px;\r\n\t-mox-border-radius: 3px;\r\n\t-webkit-border-radius: 3px;\r\n\tpadding: 9px 12px;\r\n}\r\n\r\nfooter {\r\n\tborder-top: 1px solid #F1F1F1;\r\n\tclear: both;\r\n\tmargin: 4em auto;\r\n\tpadding-top: 1em;\r\n\ttext-align: right;\r\n}\r\n\r\n.breaking-change {\r\n\tborder: 1px solid #990000;\r\n\tborder-radius: 3px;\r\n\t-mox-border-radius: 3px;\r\n\t-webkit-border-radius: 3px;\r\n\tfont-style: italic;\r\n\tpadding: 0 1em;\r\n\tmargin: 1em 0;\r\n}\r\n\r\n.breaking-change:target {\r\n\t-webkit-animation: target-fade 5s 1;\r\n\t-moz-animation: target-fade 5s 1;\r\n\t-o-animation: target-fade 5s 1;\r\n\tanimation: target-fade 5s 1;\r\n}\r\n\r\n@-webkit-keyframes target-fade {\r\n\t0% { background-color: #FFFF66; }\r\n\t100% { background-color: rgba(0,0,0,0); }\r\n}\r\n@-moz-keyframes target-fade {\r\n\t0% { background-color: #FFFF66; }\r\n\t100% { background-color: rgba(0,0,0,0); }\r\n}\r\n@-o-keyframes target-fade {\r\n\t0% { background-color: #FFFF66; }\r\n\t100% { background-color: rgba(0,0,0,0); }\r\n}\r\n@keyframes target-fade {\r\n\t0% { background-color: #FFFF66; }\r\n\t100% { background-color: rgba(0,0,0,0); }\r\n}\r\n\r\n#counter {\r\n\tborder: 0;\r\n\tfont-size: 250%;\r\n\tfont-weight: normal;\r\n\tmin-height: 3em;\r\n\ttext-align: center;\r\n}\r\n#counter strong {\r\n\twhite-space: nowrap;\r\n}\r\n\r\n#countdown-start,\r\n#countdown-units {\r\n\tbackground-color: #EEE;\r\n\tborder-radius: 4px;\r\n\t-moz-border-radius: 4px;\r\n\t-webkit-border-radius: 4px;\r\n\tfont-size: 150%;\r\n\tpadding: 0.25em 0.5em;\r\n/*\twidth: 100%;*/\r\n}\r\n#countdown-start span,\r\n#countdown-units span {\r\n\tline-height: 1.5em;\r\n\twhite-space: nowrap;\r\n}\r\n#countdown-start,\r\n#countdown-units {\r\n\ttext-align: center;\r\n}\r\n#countdown-start label {\r\n\tfont-weight: bold;\r\n\tpadding: 0 0.25em;\r\n}\r\n#countdown-start input {\r\n\tborder: 1px solid silver;\r\n\tborder-radius: 4px;\r\n\t-moz-border-radius: 4px;\r\n\t-webkit-border-radius: 4px;\r\n\ttext-align: center;\r\n}\r\n#countdown-units span {\r\n\tdisplay: block;\r\n}\r\n#countdown-units label {\r\n\tfont-weight: bold;\r\n\tpadding: 0.25em;\r\n}\r\n#countdown-units input {\r\n\tborder: 1px solid silver;\r\n\tborder-radius: 4px;\r\n\t-moz-border-radius: 4px;\r\n\t-webkit-border-radius: 4px;\r\n\tmargin-right: 0.5em;\r\n\ttext-align: center;\r\n}\r\n\r\n/* http://ubuwaits.github.com/css3-buttons/ */\r\n\r\na.minimal:link, a.minimal:visited {\r\n\tbackground: #e3e3e3;\r\n\tborder: 1px solid #ccc;\r\n\tborder-radius: 3px;\r\n\t-moz-border-radius: 3px;\r\n\t-webkit-border-radius: 3px;\r\n\tbox-shadow: inset 0px 0px 1px 1px #f6f6f6;\r\n\t-moz-box-shadow: inset 0px 0px 1px 1px #f6f6f6;\r\n\t-webkit-box-shadow: inset 0px 0px 1px 1px #f6f6f6;\r\n\tcolor: #333;\r\n\tdisplay: inline-block;\r\n\tfont-family: \"helvetica neue\", helvetica, arial, sans-serif;\r\n\tfont-size: 12px;\r\n\tfont-weight: bold;\r\n\tline-height: 1;\r\n\tpadding: 8px 0 9px;\r\n\ttext-align: center;\r\n\ttext-decoration: none;\r\n\ttext-shadow: 0 1px 0px #fff;\r\n\twidth: 150px;\r\n}\r\n\r\na.minimal:hover {\r\n\tbackground: #d9d9d9;\r\n\tbox-shadow: inset 0px 0px 1px 1px #eaeaea;\r\n\t-moz-box-shadow: inset 0px 0px 1px 1px #eaeaea;\r\n\t-webkit-box-shadow: inset 0px 0px 1px 1px #eaeaea;\r\n\tcolor: #222;\r\n}\r\n\r\na.minimal:active {\r\n\tbackground: #d0d0d0;\r\n\tbox-shadow: inset 0px 0px 1px 1px #e3e3e3;\r\n\t-moz-box-shadow: inset 0px 0px 1px 1px #e3e3e3;\r\n\t-webkit-box-shadow: inset 0px 0px 1px 1px #e3e3e3;\r\n\tcolor: #000;\r\n}\r\n\r\n.summary {\r\n\tfont-size: 12pt;\r\n\ttext-align: center;\r\n}\r\n\r\n.box {\r\n\tbackground-color: #FFF;\r\n\tborder: 1px solid silver;\r\n\tborder-radius: 4px;\r\n\t-moz-border-radius: 4px;\r\n\t-webkit-border-radius: 4px;\r\n\tbox-shadow: 0px 0px 4px #DDDDDD;\r\n\t-moz-box-shadow: 0px 0px 4px #DDDDDD;\r\n\t-webkit-box-shadow: 0px 0px 4px #DDDDDD;\r\n\tfloat: left;\r\n\theight: 200px;\r\n\tline-height: 1.75em;\r\n\tmargin: 0.5em 0 0.5em 0.5em;\r\n\tpadding: 0.125em 0.5em;\r\n\r\n\twidth: 45%;\r\n}\r\n\r\n.box h2 {\r\n\tborder-bottom: none;\r\n\tcolor: #666;\r\n\tfont-size: 30px;\r\n\tfont-weight: bold;\r\n\ttext-align: center;\r\n\ttext-shadow: 0 -1px 1px silver;\r\n\ttext-transform: lowercase;\r\n}\r\n\r\n.box ul, .box li {\r\n\tlist-style-type: none;\r\n\tmargin: 0;\r\n\tpadding: 0;\r\n\twhite-space: nowrap;\r\n}\r\n\r\n.box a.minimal {\r\n\tmargin: 0.25em;\r\n\ttext-transform: lowercase;\r\n}\r\n\r\n.box a.glow {\r\n\tbox-shadow: 0px 0px 4px #FFCC00;\r\n\t-moz-box-shadow: 0px 0px 4px #FFCC00;\r\n\t-webkit-box-shadow: 0px 0px 4px #FFCC00;\r\n}\r\n\r\n.action-info {\r\n\tfont-style: italic;\r\n\twhite-space: nowrap;\r\n}\r\n"
  },
  {
    "path": "test/timespanTests.js",
    "content": "try{\r\n\r\n/**\r\n * Mocks up a Timespan object for unit tests\r\n * \r\n * @private\r\n * @param {Timespan|Object} map properties to convert to a Timespan\r\n * @return {Timespan}\r\n */\r\ncountdown.mock = function(map) {\r\n\tvar ts = countdown();\r\n\tfor (var key in map) {\r\n\t\tif (map.hasOwnProperty(key)) {\r\n\t\t\tts[key] = map[key];\r\n\t\t}\r\n\t}\r\n\treturn ts;\r\n};\r\n\r\nvar formatTZ = function(date) {\r\n\tvar tz = -(date instanceof Date ? date : new Date()).getTimezoneOffset();\r\n\tif (!tz) {\r\n\t\treturn 'UTC';\r\n\t}\r\n\r\n\tvar tzStr = ''+Math.abs(tz % 60);\r\n\tif (tzStr.length < 2) {\r\n\t\ttzStr = '0'+tzStr;\r\n\t}\r\n\ttzStr = Math.abs(tz / 60) + tzStr;\r\n\tif (tzStr.length < 4) {\r\n\t\ttzStr = '0'+tzStr;\r\n\t}\r\n\r\n\treturn 'UTC'+((tz < 0) ? '-' : '+') +tzStr;\r\n};\r\n\r\nmodule('countdown(...)');\r\n\r\ntest('Zero', function() {\r\n\r\n\tvar start = 0;\r\n\tvar end = 0;\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(0),\r\n\t\tend: new Date(0),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: 0,\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 ms', function() {\r\n\r\n\tvar start = 0;\r\n\tvar end = 1;\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(0),\r\n\t\tend: new Date(1),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: 1,\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 1\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 sec', function() {\r\n\r\n\tvar start = 10000;\r\n\tvar end = 11000;\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(10000),\r\n\t\tend: new Date(11000),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: 1000,\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 1,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('5 min, reversed', function() {\r\n\r\n\tvar start = (5 * 60 * 1000);\r\n\tvar end = 0;\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(5 * 60 * 1000),\r\n\t\tend: new Date(0),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: -(5 * 60 * 1000),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 5,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Constant 1 month span, daily over 5 years', function() {\r\n\r\n\tvar start = new Date(1999, 10, 1, 12, 0, 0);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: start,\r\n\t\tend: start,\r\n\t\tvalue: 0,\r\n\t\tunits: countdown.ALL,\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 1,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tfor (var t=0, range=5*365.2425; t<range; t++) {\r\n\t\t// end should always be one month away\r\n\t\tvar end = new Date(start.getTime());\r\n\t\tend.setMonth( end.getMonth()+1 );\r\n\r\n\t\t// skip situations like 'Jan 31st + month'\r\n\t\tif (end.getMonth() === start.getMonth()+1) {\r\n\t\t\texpected.start = start;\r\n\t\t\texpected.end = end;\r\n\t\t\texpected.value = end.getTime() - start.getTime();\r\n\t\r\n\t\t\tvar actual = countdown(start, end, countdown.ALL);\r\n\t\r\n\t\t\tsame(actual, expected, '');\r\n\t\t}\r\n\r\n\t\t// add a day\r\n\t\tstart.setDate( start.getDate()+1 );\r\n\t}\r\n});\r\n\r\ntest('Contiguous daily countdown over 83 weeks', function() {\r\n\r\n\tvar UNITS = countdown.WEEKS | countdown.DAYS | countdown.HOURS;\r\n\tvar start = new Date(2007, 10, 10, 5, 30, 0);\r\n\tvar end = new Date(2009, 5, 14, 16, 0, 0);\r\n\r\n\tvar expected = { weeks: 83, days: 1, hours: 10.5 };\r\n\r\n\twhile (start.getTime() < end.getTime()) {\r\n\r\n\t\tvar actual = countdown(start, end, UNITS, 0, 20);\r\n\t\tactual = {\r\n\t\t\tweeks: actual.weeks,\r\n\t\t\tdays: actual.days,\r\n\t\t\thours: actual.hours\r\n\t\t};\r\n\r\n\t\tsame(actual, expected, '');\r\n\r\n\t\t// add a day\r\n\t\tstart.setDate( start.getDate()+1 );\r\n\r\n\t\t// store\r\n\t\tif (actual.days) {\r\n\t\t\texpected = {\r\n\t\t\t\tweeks: actual.weeks,\r\n\t\t\t\tdays: actual.days-1,\r\n\t\t\t\thours: actual.hours\r\n\t\t\t};\r\n\t\t} else {\r\n\t\t\texpected = {\r\n\t\t\t\tweeks: actual.weeks-1,\r\n\t\t\t\tdays: 6,\r\n\t\t\t\thours: actual.hours\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n});\r\n\r\ntest('Contiguous daily countdown over 1 year 7 months', function() {\r\n\r\n\tvar DAY_IN_MS = 24 * 60 * 60 * 1000;\r\n\tvar UNITS = countdown.MONTHS | countdown.DAYS | countdown.HOURS;\r\n\tvar start = new Date(2006, 10, 10, 5, 30, 0);\r\n\tvar end = new Date(2008, 5, 14, 16, 0, 0);\r\n\r\n\tvar expected = { months: 19, days: 4, hours: 10.5 };\r\n\r\n\twhile (start.getTime() < end.getTime()) {\r\n\r\n\t\tvar actual = countdown(start, end, UNITS, 0, 1);\r\n\t\tactual = {\r\n\t\t\tmonths: actual.months,\r\n\t\t\tdays: actual.days,\r\n\t\t\thours: actual.hours\r\n\t\t};\r\n\r\n\t\tsame(actual, expected, '');\r\n\r\n\t\t// add a day\r\n\t\tstart.setDate( start.getDate()+1 );\r\n\r\n\t\t// set up next iteration\r\n\t\tif (actual.days) {\r\n\t\t\texpected = {\r\n\t\t\t\tmonths: actual.months,\r\n\t\t\t\tdays: actual.days-1,\r\n\t\t\t\thours: actual.hours\r\n\t\t\t};\r\n\t\t} else {\r\n\t\t\tvar daysInStart = Math.round((new Date(start.getFullYear(), start.getMonth()+1, 15).getTime() - new Date(start.getFullYear(), start.getMonth(), 15).getTime()) / DAY_IN_MS);\r\n\t\t\texpected = {\r\n\t\t\t\tmonths: actual.months-1,\r\n\t\t\t\t// the number of days in start month minus one\r\n\t\t\t\tdays: daysInStart-1,\r\n\t\t\t\thours: actual.hours\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n});\r\n\r\ntest('Contiguous weekly countdown over 7 months, out of bounds max & digits', function() {\r\n\r\n\tvar DAY_IN_MS = 24 * 60 * 60 * 1000;\r\n\tvar UNITS = countdown.MONTHS | countdown.WEEKS | countdown.DAYS | countdown.HOURS;\r\n\tvar start = new Date(1999, 10, 10, 5, 30, 0);\r\n\tvar end = new Date(2001, 5, 14, 16, 0, 0);\r\n\r\n\t// calc by days since easier\r\n\tvar prev = { months: 19, days: 4, hours: 10.5 };\r\n\r\n\twhile (start.getTime() < end.getTime()) {\r\n\r\n\t\tvar actual = countdown(start, end, UNITS, -1, 100);\r\n\t\tactual = {\r\n\t\t\tmonths: actual.months,\r\n\t\t\tweeks: actual.weeks,\r\n\t\t\tdays: actual.days,\r\n\t\t\thours: actual.hours\r\n\t\t};\r\n\r\n\t\t// convert to weeks just before compare\r\n\t\tvar expected = {\r\n\t\t\tmonths: prev.months,\r\n\t\t\tweeks: Math.floor(prev.days/7),\r\n\t\t\tdays: prev.days % 7,\r\n\t\t\thours: prev.hours\r\n\t\t};\r\n\r\n\t\tsame(actual, expected, '');\r\n\r\n\t\t// add a day\r\n\t\tstart.setDate( start.getDate()+1 );\r\n\r\n\t\t// set up next iteration\r\n\t\tif (prev.days) {\r\n\t\t\tprev = {\r\n\t\t\t\tmonths: prev.months,\r\n\t\t\t\tdays: prev.days-1,\r\n\t\t\t\thours: prev.hours\r\n\t\t\t};\r\n\t\t} else {\r\n\t\t\tvar daysInStart = Math.round((new Date(start.getFullYear(), start.getMonth()+1, 15).getTime() - new Date(start.getFullYear(), start.getMonth(), 15).getTime()) / DAY_IN_MS);\r\n\t\t\tprev = {\r\n\t\t\t\tmonths: prev.months-1,\r\n\t\t\t\t// the number of days in start month minus one\r\n\t\t\t\tdays: daysInStart-1,\r\n\t\t\t\thours: prev.hours\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n});\r\n\r\ntest('Contiguous daily count up over 10 years', function() {\r\n\r\n\tvar DAY_IN_MS = 24 * 60 * 60 * 1000;\r\n\tvar UNITS = countdown.MONTHS | countdown.DAYS;\r\n\tvar start = new Date(1995, 0, 1, 12, 0, 0);\r\n\tvar end = new Date(start.getTime());\r\n\tvar goalTime = new Date(2005, 0, 1, 12, 0, 0).getTime();\r\n\r\n\tvar expected = { months: 0, days: 0 };\r\n\r\n\twhile (end.getTime() < goalTime) {\r\n\r\n\t\tvar actual = countdown(start, end, UNITS);\r\n\t\tactual = {\r\n\t\t\tmonths: actual.months,\r\n\t\t\tdays: actual.days\r\n\t\t};\r\n\r\n\t\tsame(actual, expected, '');\r\n\r\n\t\tvar daysInEnd = Math.round((new Date(end.getFullYear(), end.getMonth()+1, 15).getTime() - new Date(end.getFullYear(), end.getMonth(), 15).getTime()) / DAY_IN_MS);\r\n\r\n\t\t// add a day\r\n\t\tend.setDate( end.getDate + 1 );\r\n\r\n\t\t// set up next iteration\r\n\t\t// compare to the number of days in before month end\r\n\t\tif (actual.days < daysInEnd-1) {\r\n\t\t\texpected = {\r\n\t\t\t\tmonths: actual.months,\r\n\t\t\t\tdays: actual.days+1\r\n\t\t\t};\r\n\t\t} else {\r\n\t\t\texpected = {\r\n\t\t\t\tmonths: actual.months+1,\r\n\t\t\t\tdays: 0\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n});\r\n\r\ntest('Underflow bug ('+formatTZ(2011, 11, 1)+')', function() {\r\n\r\n\tvar start = new Date(2011, 11, 1, 0, 0, 0, 0);\r\n\tvar end = new Date(2011, 11, 31, 23, 59, 59, 999);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(start.getTime()),\r\n\t\tend: new Date(end.getTime()),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 4,\r\n\t\tdays: 2,\r\n\t\thours: 23,\r\n\t\tminutes: 59,\r\n\t\tseconds: 59,\r\n\t\tmilliseconds: 999\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Extra day bug ('+formatTZ(2014, 6, 3)+')', function() {\r\n\r\n\tvar start = new Date(2014, 6, 1, 0, 0, 0, 0);\r\n\tvar end = new Date(2014, 6, 3, 17, 52, 49, 209);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(start.getTime()),\r\n\t\tend: new Date(end.getTime()),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 2,\r\n\t\thours: 17,\r\n\t\tminutes: 52,\r\n\t\tseconds: 49,\r\n\t\tmilliseconds: 209\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Daylight Savings Time ('+formatTZ(1318451880000)+')', function() {\r\n\r\n\tvar start = new Date(2011, 9, 12, 13, 38, 0);\r\n\tvar end = new Date(2013, 11, 2, 14, 0, 0);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(start.getTime()),\r\n\t\tend: new Date(end.getTime()),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 2,\r\n\t\tmonths: 1,\r\n\t\tweeks: 3,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 22,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Reference month ordering', function() {\r\n\r\n\tvar start = new Date(2015, 1, 1, 0, 0, 0);\r\n\tvar end = new Date(2014, 9, 27, 12, 00, 0);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(start.getTime()),\r\n\t\tend: new Date(end.getTime()),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 3,\r\n\t\tweeks: 0,\r\n\t\tdays: 4,\r\n\t\thours: 12,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Before leap day', function() {\r\n\r\n\tvar start = new Date(2012, 1, 28, 13, 14, 30, 109);\r\n\tvar end = new Date(2012, 1, 29, 17, 46, 22, 111); // Leap day 2012\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(2012, 1, 28, 13, 14, 30, 109),\r\n\t\tend: new Date(2012, 1, 29, 17, 46, 22, 111),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 1,\r\n\t\thours: 4,\r\n\t\tminutes: 31,\r\n\t\tseconds: 52,\r\n\t\tmilliseconds: 2\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('After leap day (local)', function() {\r\n\r\n\tvar start = new Date(2012, 1, 29, 17, 46, 22, 111); // Leap day 2012\r\n\tvar end = new Date(2012, 2, 1, 13, 14, 30, 109);\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(2012, 1, 29, 17, 46, 22, 111),\r\n\t\tend: new Date(2012, 2, 1, 13, 14, 30, 109),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 19,\r\n\t\tminutes: 28,\r\n\t\tseconds: 7,\r\n\t\tmilliseconds: 998\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('After leap day (UTC)', function() {\r\n\r\n\tvar start = new Date(1330537582111); // 2012-02-29T17:46:22.111Z, Leap day 2012\r\n\tvar end = new Date(1330607670109);   // 2012-03-01T13:14:30.109Z\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(1330537582111),\r\n\t\tend: new Date(1330607670109),\r\n\t\tunits: countdown.ALL,\r\n\t\tvalue: end.getTime() - start.getTime(),\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 19,\r\n\t\tminutes: 28,\r\n\t\tseconds: 7,\r\n\t\tmilliseconds: 998\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.ALL);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Almost 2 minutes, rounded', function() {\r\n\r\n\tvar start = new Date(915220800000); // 1999-01-01T20:00:00.000Z\r\n\tvar end = new Date(915220919999);   // 1999-01-01T20:01:59.999Z\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(915220800000),\r\n\t\tend: new Date(915220919999),\r\n\t\tunits: countdown.DEFAULTS,\r\n\t\tvalue: 119999,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 2,\r\n\t\tseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.DEFAULTS);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Almost 2 minutes, rounded 2 digits', function() {\r\n\r\n\tvar start = new Date(915220800000); // 1999-01-01T20:00:00.000Z\r\n\tvar end = new Date(915220919999);   // 1999-01-01T20:01:59.999Z\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(915220800000),\r\n\t\tend: new Date(915220919999),\r\n\t\tunits: countdown.DEFAULTS,\r\n\t\tvalue: 119999,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 2,\r\n\t\tseconds: 0\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.DEFAULTS, NaN, 2);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Almost 2 minutes, full 3 digits', function() {\r\n\r\n\tvar start = new Date(915220800000); // 1999-01-01T20:00:00.000Z\r\n\tvar end = new Date(915220919999);   // 1999-01-01T20:01:59.999Z\r\n\r\n\tvar expected = countdown.mock({\r\n\t\tstart: new Date(915220800000),\r\n\t\tend: new Date(915220919999),\r\n\t\tunits: countdown.DEFAULTS,\r\n\t\tvalue: 119999,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 1,\r\n\t\tseconds: 59.999\r\n\t});\r\n\r\n\tvar actual = countdown(start, end, countdown.DEFAULTS, 0, 3);\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\nmodule('Timespan.addTo(date)');\r\n\r\ntest('Zero', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar expected = 0;\r\n\tvar actual = timespan.addTo(0).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 ms', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 1\r\n\t});\r\n\r\n\tvar expected = 1;\r\n\tvar actual = timespan.addTo(0).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 sec', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 0,\r\n\t\tseconds: 1,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar expected = 11000;\r\n\tvar actual = timespan.addTo(10000).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('1 sec, value', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tvalue: 1000\r\n\t});\r\n\r\n\tvar expected = 11000;\r\n\tvar actual = timespan.addTo(10000).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('5 min, reversed', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 0,\r\n\t\tmonths: 0,\r\n\t\tweeks: 0,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: -5,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar expected = 0;\r\n\tvar actual = timespan.addTo((5 * 60 * 1000)).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('5 min, reversed, value', function() {\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tvalue: -(5 * 60 * 1000)\r\n\t});\r\n\r\n\tvar expected = 0;\r\n\tvar actual = timespan.addTo((5 * 60 * 1000)).getTime();\r\n\r\n\tsame(actual, expected, '');\r\n});\r\n\r\ntest('Daylight Savings Time ('+formatTZ(1318451880000)+')', function() {\r\n\r\n\tvar start = new Date(2011, 9, 12, 13, 38, 0);\r\n\tvar end = new Date(2013, 11, 2, 14, 0, 0);\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tmillennia: 0,\r\n\t\tcenturies: 0,\r\n\t\tdecades: 0,\r\n\t\tyears: 2,\r\n\t\tmonths: 1,\r\n\t\tweeks: 3,\r\n\t\tdays: 0,\r\n\t\thours: 0,\r\n\t\tminutes: 22,\r\n\t\tseconds: 0,\r\n\t\tmilliseconds: 0\r\n\t});\r\n\r\n\tvar expected = end.getTime();\r\n\tvar actual = timespan.addTo(start).getTime();\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\ntest('Daylight Savings Time ('+formatTZ(1318451880000)+'), value', function() {\r\n\r\n\tvar start = new Date(2011, 9, 12, 13, 38, 0);\r\n\tvar end = new Date(2013, 11, 2, 14, 0, 0);\r\n\r\n\tvar timespan = countdown.mock({\r\n\t\tvalue: end.getTime() - start.getTime()\r\n\t});\r\n\r\n\tvar expected = end.getTime();\r\n\tvar actual = timespan.addTo(start).getTime();\r\n\r\n\tsame(actual, expected, ''+start+' => '+end);\r\n});\r\n\r\n}catch(ex){alert(ex);}"
  },
  {
    "path": "test/unit.html",
    "content": "<!DOCTYPE html>\n<html class=\"no-js\">\n<head>\n\t<meta charset=\"UTF-8\">\n\t<title>Countdown.js Unit Tests</title>\n\t<link href=\"../lib/qunit/qunit.css\" rel=\"stylesheet\" type=\"text/css\" />\n\n\t<script type=\"text/javascript\">\n\t\twindow.onerror = function(msg, url, line) {\n\t\t\talert([msg, url, line].join(', '));\n\t\t};\n\t</script>\n\t<script src=\"../lib/json/json2.js\" type=\"text/javascript\"></script>\n\t<script src=\"../lib/qunit/qunit.js\" type=\"text/javascript\"></script>\n\t<script src=\"../countdown.demo.js\" type=\"text/javascript\"></script>\n\t<script src=\"timespanTests.js\" type=\"text/javascript\"></script>\n\t<script src=\"formatTests.js\" type=\"text/javascript\"></script>\n</head>\n<body>\n\t<h1 id=\"qunit-header\">\n\t\t<div style=\"float:right\"><a href=\"http://twitter.com/share\" class=\"twitter-share-button\" data-url=\"http://countdownjs.org\" data-text=\"Countdown.js: A simple JavaScript API for producing an accurate, intuitive description of the timespan between dates\"></a></div>\n\t\t<a href=\"../\">Countdown.js</a> Unit Tests\n\t</h1>  \n\t<h2 id=\"qunit-banner\"></h2>  \n\t<h2 id=\"qunit-userAgent\"></h2>  \n\t<ol id=\"qunit-tests\"></ol> \n\n\t<script src=\"/ga.js\" type=\"text/javascript\" defer></script>\n\t<script src=\"http://platform.twitter.com/widgets.js\" type=\"text/javascript\" defer=\"defer\"></script>\n</body>\n</html>"
  }
]