Full Code of mckamey/countdownjs for AI

master 1902fb23d21d cached
35 files
450.6 KB
111.1k tokens
130 symbols
1 requests
Download .txt
Showing preview only (467K chars total). Download the full file or copy to clipboard to get everything.
Repository: mckamey/countdownjs
Branch: master
Commit: 1902fb23d21d
Files: 35
Total size: 450.6 KB

Directory structure:
gitextract_1o3rx81s/

├── .gitignore
├── LICENSE.txt
├── README.md
├── bower.json
├── build.xml
├── countdown.js
├── demo.html
├── demo.js
├── externs.js
├── index.html
├── lib/
│   ├── closure/
│   │   ├── COPYING
│   │   ├── README
│   │   ├── compiler.jar
│   │   ├── docs.url
│   │   └── download.url
│   ├── jslint/
│   │   ├── download.url
│   │   ├── jslint.js
│   │   └── rhino.js
│   ├── json/
│   │   └── json2.js
│   ├── qunit/
│   │   ├── MIT-LICENSE.txt
│   │   ├── download.url
│   │   ├── qunit.css
│   │   └── qunit.js
│   ├── raf/
│   │   └── requestAnimationFrame.js
│   └── rhino/
│       ├── LICENSE.txt
│       ├── download.url
│       └── js.jar
├── package.json
├── readme.html
└── test/
    ├── fonts/
    │   └── SIL Open Font License 1.1.txt
    ├── formatTests.js
    ├── lint.js
    ├── styles.css
    ├── timespanTests.js
    └── unit.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
\.DS_Store
\.classpath
\.project
\.settings
\.iml
\.ipr
\.iws
*\.bat
*\.psd
*\.ai
*\.sh
ga.js
robots.txt
antixss\.js
countdown\.demo\.js

================================================
FILE: LICENSE.txt
================================================
The MIT License

Copyright (c) 2006-2012 Stephen M. McKamey

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# [Countdown.js][1]

A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.

----

## The Motivation

While seemingly a trivial problem, the human descriptions for a span of time tend to be fuzzier than a computer naturally computes.
More specifically, months are an inherently messed up unit of time.
For 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.
People 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.
Even intuitively defining these terms can be problematic. For instance, what is the date one month after January 31st, 2001?
JavaScript will happily call this March 3rd, 2001. Humans will typically debate either February 28th, 2001 or March 1st, 2001.
It seems there isn't a "right" answer, per se.

## The Algorithm

*Countdown.js* emphasizes producing intuitively correct description of timespans which are consistent as time goes on.
To do this, *Countdown.js* uses the concept of "today's date next month" to mean "a month from now".
As the days go by, *Countdown.js* produces consecutively increasing or decreasing counts without inconsistent jumps.
The range of accuracy is only limited by the underlying system clock.

*Countdown.js* approaches finding the difference between two times like an elementary school subtraction problem.
Each 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.
In base-10 subtraction, every column is worth 10 times the previous column.
With 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.
Internally, *Countdown.js* maintains the concept of a "reference month" which determines how many days a given month or year represents.
In 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.

### Time Zones & Daylight Savings Time

As of v2.4, *Countdown.js* performs all calculations with respect to the **viewer's local time zone**.
Earlier versions performed difference calculations in UTC, which is generally the correct way to do math on time.
In 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.
If 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.
This is the heart of the problem that *Countdown.js* attempts to solve: talking about spans of time can be ambiguous.
Nearly all bugs reported for *Countdown.js* have been because the viewer expects something different due to their particular time zone.

JavaScript ([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.
By 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.
Since most humans think about local time not UTC, it the most makes sense to perform this time span algorithm in reference to local time.

Daylight Savings Time further complicates things, creating hours which get repeated and hours which cannot exist.
*Countdown.js* effectively ignores these edge cases and talks about time preferring human intuition about time over surprise exactness.
Example: A viewer asks for the description from noon the day before a daylight savings begins to noon the day after.
A computer would answer "23 hours" whereas a human would confidently answer "1 day" even after being reminded to "Spring Forward".
The computer is technically more accurate but this is not the value that humans actually expect or desire.
Humans 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.
UTC simplifies time but at the cost of being inconsistent with human experience.

----

## The API

A simple but flexible API is the goal of *Countdown.js*. There is one global function with a set of static constants:

    var timespan = countdown(start|callback, end|callback, units, max, digits);

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. `units` defaults to `countdown.DEFAULTS`, `max` defaults to `NaN` (all specified units), `digits` defaults to `0`.

	countdown.ALL =
		countdown.MILLENNIA |
		countdown.CENTURIES |
		countdown.DECADES |
		countdown.YEARS |
		countdown.MONTHS |
		countdown.WEEKS |
		countdown.DAYS |
		countdown.HOURS |
		countdown.MINUTES |
		countdown.SECONDS |
		countdown.MILLISECONDS;

	countdown.DEFAULTS =
		countdown.YEARS |
		countdown.MONTHS |
		countdown.DAYS |
		countdown.HOURS |
		countdown.MINUTES |
		countdown.SECONDS;

This allows a very minimal call to accept the defaults and get the time since/until a single date. For example:

	countdown( new Date(2000, 0, 1) ).toString();

This will produce a human readable description like:

	11 years, 8 months, 4 days, 10 hours, 12 minutes and 43 seconds

### The `start` / `end` arguments

The parameters `start` and `end` can be one of several values:

1. `null` which indicates "now".
2. a JavaScript `Date` object.
3. a `number` specifying the number of milliseconds since midnight Jan 1, 1970 UTC (i.e., the "UNIX epoch").
4. a callback `function` accepting one timespan argument.

To 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.
In 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.

If `start` and `end` are both specified, then repeated calls to `countdown(...)` will always return the same result.
If 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.
For example,

	var daysSinceLastWorkplaceAccident = countdown(507314280000, null, countdown.DAYS);

If 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:

	var timerId =
	  countdown(
	    new Date(),
	    function(ts) {
	      document.getElementById('pageTimer').innerHTML = ts.toHTML("strong");
	    },
	    countdown.HOURS|countdown.MINUTES|countdown.SECONDS);
	
	// later on this timer may be stopped
	window.clearInterval(timerId);

### The `units` argument

The 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:

	countdown.MONTHS | countdown.DAYS

To explicitly exclude units like "not weeks and not milliseconds" combine bitwise-NOT and bitwise-AND:

	~countdown.WEEKS & ~countdown.MILLISECONDS

[Equivalently](http://en.wikipedia.org/wiki/De_Morgan's_laws), to specify everything but "not weeks or milliseconds" wrap bitwise-NOT around bitwise-OR:

	~(countdown.WEEKS | countdown.MILLISECONDS)

### The `max` argument

The 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.

	countdown(start, end, units).toString() => "5 years, 1 month, 19 days, 12 hours and 17 minutes"

Specifying `max` as `2` ensures that only the two most significant units are displayed **(note the rounding of the least significant unit)**:

	countdown(start, end, units, 2).toString() => "5 years and 2 months"

Negative or zero values of `max` are ignored.

----
#### Breaking change in v2.3.0!
Previously, 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).

----

### The `digits` argument

The final optional argument `digits` allows fractional values on the smallest unit.

	countdown(start, end, units, max).toString() => "5 years and 2 months"

Specifying `digits` as `2` allows up to 2 digits beyond the decimal point to be displayed **(note the rounding of the least significant unit)**:

	countdown(start, end, units, max, 2).toString() => "5 years and 1.65 months"

`digits` must be between `0` and `20`, inclusive.

----
#### Rounding

With 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"`.

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 `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)`!

----

### Timespan result

The return value is a Timespan object which always contains the following fields:

- `Date start`: the starting date object used for the calculation
- `Date end`: the ending date object used for the calculation
- `Number units`: the units specified
- `Number value`: total milliseconds difference (i.e., `end` - `start`). If `end < start` then `value` will be negative.

Typically 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.

The following time unit fields are only present if their corresponding units were requested:

- `Number millennia`
- `Number centuries`
- `Number decades`
- `Number years`
- `Number months`
- `Number days`
- `Number hours`
- `Number minutes`
- `Number seconds`
- `Number milliseconds`

Finally, 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).

`String toString(emptyLabel)`: formats the Timespan object as an English sentence. e.g., using the same input:

	ts.toString() => "5 years, 1 month, 19 days, 12 hours and 17 minutes"

`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:

	ts.toHTML() => "<span>5 years</span>, <span>1 month</span>, <span>19 days</span>, <span>12 hours</span> and <span>17 minutes</span>"

	ts.toHTML("em") => "<em>5 years</em>, <em>1 month</em>, <em>19 days</em>, <em>12 hours</em> and <em>17 minutes</em>"

----

### Localization

Very basic localization is supported via the static `setLabels` and `resetLabels` methods. These change the functionality for all timespans on the page.

	countdown.resetLabels();

	countdown.setLabels(singular, plural, last, delim, empty, formatter);

The arguments:

- `singular` is a pipe (`'|'`) delimited ascending list of singular unit name overrides
- `plural` is a pipe (`'|'`) delimited ascending list of plural unit name overrides
- `last` is a delimiter before the last unit (default: `' and '`)
- `delim` is a delimiter to use between all other units (default: `', '`),
- `empty` is a label to use when all units are zero (default: `''`)
- `formatter` is a function which takes a `number` and returns a `string` (default uses `Number.toString()`),  
  allowing customization of the way numbers are formatted, e.g., commas every 3 digits or some unique style that is specific to your locale.

Notice that the spacing is part of the labels.

The following examples would translate the output into Brazilian Portuguese and French, respectively:

	countdown.setLabels(
		' milissegundo| segundo| minuto| hora| dia| semana| mês| ano| década| século| milênio',
		' milissegundos| segundos| minutos| horas| dias| semanas| meses| anos| décadas| séculos| milênios',
		' e ',
		' + ',
		'agora');

	countdown.setLabels(
		' milliseconde| seconde| minute| heure| jour| semaine| mois| année| décennie| siècle| millénaire',
		' millisecondes| secondes| minutes| heures| jours| semaines| mois| années| décennies| siècles| millénaires',
		' et ',
		', ',
		'maintenant');

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.

	countdown.setLabels(
		'||| hr| d',
		'ms| sec|||| wks|| yrs',
		', and finally ');

	ts.toString() => "1 millennium, 2 centuries, 5 yrs, 1 month, 7 wks, 19 days, 1 hr, 2 minutes, 17 sec, and finally 1 millisecond"

If you only wanted to override the empty label:

	countdown.setLabels(
		null,
		null,
		null,
		null,
		'Now.');

	ts.toString() => "Now."

The following would be effectively the same as calling `countdown.resetLabels()`:

	countdown.setLabels(
		' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium',
		' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia',
		' and ',
		', ',
		'',
		function(n){ return n.toString(); });

----

## License

Distributed under the terms of [The MIT license][2].

----

Copyright (c) 2006-2014 [Stephen M. McKamey][3]

  [1]: http://countdownjs.org
  [2]: https://raw.githubusercontent.com/mckamey/countdownjs/master/LICENSE.txt
  [3]: http://mck.me


================================================
FILE: bower.json
================================================
{
  "name": "countdownjs",
  "version": "2.6.1",
  "description": "A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.",
  "main": "countdown.js",
  "keywords": [
    "countdown",
    "timer",
    "clock",
    "date",
    "time",
    "timespan",
    "year",
    "month",
    "week",
    "day",
    "hour",
    "minute",
    "second"
  ],
  "authors": [
    "Stephen McKamey (http://mck.me)"
  ],
  "license": "MIT",
  "homepage": "http://countdownjs.org",
  "ignore": [
  	"./!(countdown.js)"
  ]
}


================================================
FILE: build.xml
================================================
<?xml version="1.0"?>
<project basedir="." default="all">
	<taskdef name="closure" classname="com.google.javascript.jscomp.ant.CompileTask" classpath="lib/closure/compiler.jar" />

	<target name="init">
		<echo message="Building..." />
		<property name="lib.dir" value="${basedir}/lib" />
		<property name="src.dir" value="${basedir}" />
		<property name="test.dir" value="${basedir}/test" />
		<property name="build.dir" value="${basedir}" />
	</target>

	<target name="jslint" depends="init" description="Check JS with JSLint">
		<echo message="Running JSLint static analysis..." />
		<java jar="${lib.dir}/rhino/js.jar" fork="true">
			<arg value="${test.dir}/lint.js" />
		</java>
		<echo message="JSLint static analysis done." />
	</target>

	<target name="compile.js" depends="jslint" description="Compile JS">
		<echo message="Compiling script..." />
		<closure compilationLevel="simple" warning="verbose" debug="false" output="${build.dir}/countdown.min.js">
			<sources dir="${build.dir}">
				<file name="externs.js"/>
				<file name="countdown.js"/>
			</sources>
		</closure>
		<echo message="Script compilation done." />
	</target>

	<target name="all" depends="compile.js">
		<echo message="Build completed." />
		<concat destfile="${build.dir}/countdown.demo.js">
			<fileset file="${build.dir}/antixss.js" />
			<fileset file="${build.dir}/countdown.min.js" />
		</concat>
	</target>

</project>

================================================
FILE: countdown.js
================================================
/*global window module */
/**
 * @license countdown.js v2.6.1 http://countdownjs.org
 * Copyright (c)2006-2014 Stephen M. McKamey.
 * Licensed under The MIT License.
 */
/*jshint bitwise:false */

/**
 * API entry
 * @public
 * @param {function(Object)|Date|number} start the starting date
 * @param {function(Object)|Date|number} end the ending date
 * @param {number} units the units to populate
 * @return {Object|number}
 */
var countdown = (

function() {
	/*jshint smarttabs:true */

	'use strict';

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MILLISECONDS	= 0x001;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var SECONDS			= 0x002;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MINUTES			= 0x004;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var HOURS			= 0x008;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var DAYS			= 0x010;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var WEEKS			= 0x020;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MONTHS			= 0x040;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var YEARS			= 0x080;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var DECADES			= 0x100;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var CENTURIES		= 0x200;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MILLENNIA		= 0x400;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var DEFAULTS		= YEARS|MONTHS|DAYS|HOURS|MINUTES|SECONDS;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MILLISECONDS_PER_SECOND = 1000;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var SECONDS_PER_MINUTE = 60;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MINUTES_PER_HOUR = 60;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var HOURS_PER_DAY = 24;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MILLISECONDS_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MILLISECONDS_PER_SECOND;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var DAYS_PER_WEEK = 7;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var MONTHS_PER_YEAR = 12;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var YEARS_PER_DECADE = 10;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var DECADES_PER_CENTURY = 10;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var CENTURIES_PER_MILLENNIUM = 10;

	/**
	 * @private
	 * @param {number} x number
	 * @return {number}
	 */
	var ceil = Math.ceil;

	/**
	 * @private
	 * @param {number} x number
	 * @return {number}
	 */
	var floor = Math.floor;

	/**
	 * @private
	 * @param {Date} ref reference date
	 * @param {number} shift number of months to shift
	 * @return {number} number of days shifted
	 */
	function borrowMonths(ref, shift) {
		var prevTime = ref.getTime();

		// increment month by shift
		ref.setMonth( ref.getMonth() + shift );

		// this is the trickiest since months vary in length
		return Math.round( (ref.getTime() - prevTime) / MILLISECONDS_PER_DAY );
	}

	/**
	 * @private
	 * @param {Date} ref reference date
	 * @return {number} number of days
	 */
	function daysPerMonth(ref) {
		var a = ref.getTime();

		// increment month by 1
		var b = new Date(a);
		b.setMonth( ref.getMonth() + 1 );

		// this is the trickiest since months vary in length
		return Math.round( (b.getTime() - a) / MILLISECONDS_PER_DAY );
	}

	/**
	 * @private
	 * @param {Date} ref reference date
	 * @return {number} number of days
	 */
	function daysPerYear(ref) {
		var a = ref.getTime();

		// increment year by 1
		var b = new Date(a);
		b.setFullYear( ref.getFullYear() + 1 );

		// this is the trickiest since years (periodically) vary in length
		return Math.round( (b.getTime() - a) / MILLISECONDS_PER_DAY );
	}

	/**
	 * Applies the Timespan to the given date.
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @param {Date=} date
	 * @return {Date}
	 */
	function addToDate(ts, date) {
		date = (date instanceof Date) || ((date !== null) && isFinite(date)) ? new Date(+date) : new Date();
		if (!ts) {
			return date;
		}

		// if there is a value field, use it directly
		var value = +ts.value || 0;
		if (value) {
			date.setTime(date.getTime() + value);
			return date;
		}

		value = +ts.milliseconds || 0;
		if (value) {
			date.setMilliseconds(date.getMilliseconds() + value);
		}

		value = +ts.seconds || 0;
		if (value) {
			date.setSeconds(date.getSeconds() + value);
		}

		value = +ts.minutes || 0;
		if (value) {
			date.setMinutes(date.getMinutes() + value);
		}

		value = +ts.hours || 0;
		if (value) {
			date.setHours(date.getHours() + value);
		}

		value = +ts.weeks || 0;
		if (value) {
			value *= DAYS_PER_WEEK;
		}

		value += +ts.days || 0;
		if (value) {
			date.setDate(date.getDate() + value);
		}

		value = +ts.months || 0;
		if (value) {
			date.setMonth(date.getMonth() + value);
		}

		value = +ts.millennia || 0;
		if (value) {
			value *= CENTURIES_PER_MILLENNIUM;
		}

		value += +ts.centuries || 0;
		if (value) {
			value *= DECADES_PER_CENTURY;
		}

		value += +ts.decades || 0;
		if (value) {
			value *= YEARS_PER_DECADE;
		}

		value += +ts.years || 0;
		if (value) {
			date.setFullYear(date.getFullYear() + value);
		}

		return date;
	}

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_MILLISECONDS	= 0;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_SECONDS		= 1;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_MINUTES		= 2;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_HOURS			= 3;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_DAYS			= 4;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_WEEKS			= 5;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_MONTHS		= 6;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_YEARS			= 7;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_DECADES		= 8;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_CENTURIES		= 9;

	/**
	 * @private
	 * @const
	 * @type {number}
	 */
	var LABEL_MILLENNIA		= 10;

	/**
	 * @private
	 * @type {Array}
	 */
	var LABELS_SINGLUAR;

	/**
	 * @private
	 * @type {Array}
	 */
	var LABELS_PLURAL;

	/**
	 * @private
	 * @type {string}
	 */
	var LABEL_LAST;

	/**
	 * @private
	 * @type {string}
	 */
	var LABEL_DELIM;

	/**
	 * @private
	 * @type {string}
	 */
	var LABEL_NOW;

	/**
	 * Formats a number & unit as a string
	 * 
	 * @param {number} value
	 * @param {number} unit
	 * @return {string}
	 */
	var formatter;

	/**
	 * Formats a number as a string
	 * 
	 * @private
	 * @param {number} value
	 * @return {string}
	 */
	var formatNumber;

	/**
	 * @private
	 * @param {number} value
	 * @param {number} unit unit index into label list
	 * @return {string}
	 */
	function plurality(value, unit) {
		return formatNumber(value)+((value === 1) ? LABELS_SINGLUAR[unit] : LABELS_PLURAL[unit]);
	}

	/**
	 * Formats the entries with singular or plural labels
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @return {Array}
	 */
	var formatList;

	/**
	 * Timespan representation of a duration of time
	 * 
	 * @private
	 * @this {Timespan}
	 * @constructor
	 */
	function Timespan() {}

	/**
	 * Formats the Timespan as a sentence
	 * 
	 * @param {string=} emptyLabel the string to use when no values returned
	 * @return {string}
	 */
	Timespan.prototype.toString = function(emptyLabel) {
		var label = formatList(this);

		var count = label.length;
		if (!count) {
			return emptyLabel ? ''+emptyLabel : LABEL_NOW;
		}
		if (count === 1) {
			return label[0];
		}

		var last = LABEL_LAST+label.pop();
		return label.join(LABEL_DELIM)+last;
	};

	/**
	 * Formats the Timespan as a sentence in HTML
	 * 
	 * @param {string=} tag HTML tag name to wrap each value
	 * @param {string=} emptyLabel the string to use when no values returned
	 * @return {string}
	 */
	Timespan.prototype.toHTML = function(tag, emptyLabel) {
		tag = tag || 'span';
		var label = formatList(this);

		var count = label.length;
		if (!count) {
			emptyLabel = emptyLabel || LABEL_NOW;
			return emptyLabel ? '<'+tag+'>'+emptyLabel+'</'+tag+'>' : emptyLabel;
		}
		for (var i=0; i<count; i++) {
			// wrap each unit in tag
			label[i] = '<'+tag+'>'+label[i]+'</'+tag+'>';
		}
		if (count === 1) {
			return label[0];
		}

		var last = LABEL_LAST+label.pop();
		return label.join(LABEL_DELIM)+last;
	};

	/**
	 * Applies the Timespan to the given date
	 * 
	 * @param {Date=} date the date to which the timespan is added.
	 * @return {Date}
	 */
	Timespan.prototype.addTo = function(date) {
		return addToDate(this, date);
	};

	/**
	 * Formats the entries as English labels
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @return {Array}
	 */
	formatList = function(ts) {
		var list = [];

		var value = ts.millennia;
		if (value) {
			list.push(formatter(value, LABEL_MILLENNIA));
		}

		value = ts.centuries;
		if (value) {
			list.push(formatter(value, LABEL_CENTURIES));
		}

		value = ts.decades;
		if (value) {
			list.push(formatter(value, LABEL_DECADES));
		}

		value = ts.years;
		if (value) {
			list.push(formatter(value, LABEL_YEARS));
		}

		value = ts.months;
		if (value) {
			list.push(formatter(value, LABEL_MONTHS));
		}

		value = ts.weeks;
		if (value) {
			list.push(formatter(value, LABEL_WEEKS));
		}

		value = ts.days;
		if (value) {
			list.push(formatter(value, LABEL_DAYS));
		}

		value = ts.hours;
		if (value) {
			list.push(formatter(value, LABEL_HOURS));
		}

		value = ts.minutes;
		if (value) {
			list.push(formatter(value, LABEL_MINUTES));
		}

		value = ts.seconds;
		if (value) {
			list.push(formatter(value, LABEL_SECONDS));
		}

		value = ts.milliseconds;
		if (value) {
			list.push(formatter(value, LABEL_MILLISECONDS));
		}

		return list;
	};

	/**
	 * Borrow any underflow units, carry any overflow units
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @param {string} toUnit
	 */
	function rippleRounded(ts, toUnit) {
		switch (toUnit) {
			case 'seconds':
				if (ts.seconds !== SECONDS_PER_MINUTE || isNaN(ts.minutes)) {
					return;
				}
				// ripple seconds up to minutes
				ts.minutes++;
				ts.seconds = 0;

				/* falls through */
			case 'minutes':
				if (ts.minutes !== MINUTES_PER_HOUR || isNaN(ts.hours)) {
					return;
				}
				// ripple minutes up to hours
				ts.hours++;
				ts.minutes = 0;

				/* falls through */
			case 'hours':
				if (ts.hours !== HOURS_PER_DAY || isNaN(ts.days)) {
					return;
				}
				// ripple hours up to days
				ts.days++;
				ts.hours = 0;

				/* falls through */
			case 'days':
				if (ts.days !== DAYS_PER_WEEK || isNaN(ts.weeks)) {
					return;
				}
				// ripple days up to weeks
				ts.weeks++;
				ts.days = 0;

				/* falls through */
			case 'weeks':
				if (ts.weeks !== daysPerMonth(ts.refMonth)/DAYS_PER_WEEK || isNaN(ts.months)) {
					return;
				}
				// ripple weeks up to months
				ts.months++;
				ts.weeks = 0;

				/* falls through */
			case 'months':
				if (ts.months !== MONTHS_PER_YEAR || isNaN(ts.years)) {
					return;
				}
				// ripple months up to years
				ts.years++;
				ts.months = 0;

				/* falls through */
			case 'years':
				if (ts.years !== YEARS_PER_DECADE || isNaN(ts.decades)) {
					return;
				}
				// ripple years up to decades
				ts.decades++;
				ts.years = 0;

				/* falls through */
			case 'decades':
				if (ts.decades !== DECADES_PER_CENTURY || isNaN(ts.centuries)) {
					return;
				}
				// ripple decades up to centuries
				ts.centuries++;
				ts.decades = 0;

				/* falls through */
			case 'centuries':
				if (ts.centuries !== CENTURIES_PER_MILLENNIUM || isNaN(ts.millennia)) {
					return;
				}
				// ripple centuries up to millennia
				ts.millennia++;
				ts.centuries = 0;
				/* falls through */
			}
	}

	/**
	 * Ripple up partial units one place
	 * 
	 * @private
	 * @param {Timespan} ts timespan
	 * @param {number} frac accumulated fractional value
	 * @param {string} fromUnit source unit name
	 * @param {string} toUnit target unit name
	 * @param {number} conversion multiplier between units
	 * @param {number} digits max number of decimal digits to output
	 * @return {number} new fractional value
	 */
	function fraction(ts, frac, fromUnit, toUnit, conversion, digits) {
		if (ts[fromUnit] >= 0) {
			frac += ts[fromUnit];
			delete ts[fromUnit];
		}

		frac /= conversion;
		if (frac + 1 <= 1) {
			// drop if below machine epsilon
			return 0;
		}

		if (ts[toUnit] >= 0) {
			// ensure does not have more than specified number of digits
			ts[toUnit] = +(ts[toUnit] + frac).toFixed(digits);
			rippleRounded(ts, toUnit);
			return 0;
		}

		return frac;
	}

	/**
	 * Ripple up partial units to next existing
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @param {number} digits max number of decimal digits to output
	 */
	function fractional(ts, digits) {
		var frac = fraction(ts, 0, 'milliseconds', 'seconds', MILLISECONDS_PER_SECOND, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'seconds', 'minutes', SECONDS_PER_MINUTE, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'minutes', 'hours', MINUTES_PER_HOUR, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'hours', 'days', HOURS_PER_DAY, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'days', 'weeks', DAYS_PER_WEEK, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'weeks', 'months', daysPerMonth(ts.refMonth)/DAYS_PER_WEEK, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'months', 'years', daysPerYear(ts.refMonth)/daysPerMonth(ts.refMonth), digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'years', 'decades', YEARS_PER_DECADE, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'decades', 'centuries', DECADES_PER_CENTURY, digits);
		if (!frac) { return; }

		frac = fraction(ts, frac, 'centuries', 'millennia', CENTURIES_PER_MILLENNIUM, digits);

		// should never reach this with remaining fractional value
		if (frac) { throw new Error('Fractional unit overflow'); }
	}

	/**
	 * Borrow any underflow units, carry any overflow units
	 * 
	 * @private
	 * @param {Timespan} ts
	 */
	function ripple(ts) {
		var x;

		if (ts.milliseconds < 0) {
			// ripple seconds down to milliseconds
			x = ceil(-ts.milliseconds / MILLISECONDS_PER_SECOND);
			ts.seconds -= x;
			ts.milliseconds += x * MILLISECONDS_PER_SECOND;

		} else if (ts.milliseconds >= MILLISECONDS_PER_SECOND) {
			// ripple milliseconds up to seconds
			ts.seconds += floor(ts.milliseconds / MILLISECONDS_PER_SECOND);
			ts.milliseconds %= MILLISECONDS_PER_SECOND;
		}

		if (ts.seconds < 0) {
			// ripple minutes down to seconds
			x = ceil(-ts.seconds / SECONDS_PER_MINUTE);
			ts.minutes -= x;
			ts.seconds += x * SECONDS_PER_MINUTE;

		} else if (ts.seconds >= SECONDS_PER_MINUTE) {
			// ripple seconds up to minutes
			ts.minutes += floor(ts.seconds / SECONDS_PER_MINUTE);
			ts.seconds %= SECONDS_PER_MINUTE;
		}

		if (ts.minutes < 0) {
			// ripple hours down to minutes
			x = ceil(-ts.minutes / MINUTES_PER_HOUR);
			ts.hours -= x;
			ts.minutes += x * MINUTES_PER_HOUR;

		} else if (ts.minutes >= MINUTES_PER_HOUR) {
			// ripple minutes up to hours
			ts.hours += floor(ts.minutes / MINUTES_PER_HOUR);
			ts.minutes %= MINUTES_PER_HOUR;
		}

		if (ts.hours < 0) {
			// ripple days down to hours
			x = ceil(-ts.hours / HOURS_PER_DAY);
			ts.days -= x;
			ts.hours += x * HOURS_PER_DAY;

		} else if (ts.hours >= HOURS_PER_DAY) {
			// ripple hours up to days
			ts.days += floor(ts.hours / HOURS_PER_DAY);
			ts.hours %= HOURS_PER_DAY;
		}

		while (ts.days < 0) {
			// NOTE: never actually seen this loop more than once

			// ripple months down to days
			ts.months--;
			ts.days += borrowMonths(ts.refMonth, 1);
		}

		// weeks is always zero here

		if (ts.days >= DAYS_PER_WEEK) {
			// ripple days up to weeks
			ts.weeks += floor(ts.days / DAYS_PER_WEEK);
			ts.days %= DAYS_PER_WEEK;
		}

		if (ts.months < 0) {
			// ripple years down to months
			x = ceil(-ts.months / MONTHS_PER_YEAR);
			ts.years -= x;
			ts.months += x * MONTHS_PER_YEAR;

		} else if (ts.months >= MONTHS_PER_YEAR) {
			// ripple months up to years
			ts.years += floor(ts.months / MONTHS_PER_YEAR);
			ts.months %= MONTHS_PER_YEAR;
		}

		// years is always non-negative here
		// decades, centuries and millennia are always zero here

		if (ts.years >= YEARS_PER_DECADE) {
			// ripple years up to decades
			ts.decades += floor(ts.years / YEARS_PER_DECADE);
			ts.years %= YEARS_PER_DECADE;

			if (ts.decades >= DECADES_PER_CENTURY) {
				// ripple decades up to centuries
				ts.centuries += floor(ts.decades / DECADES_PER_CENTURY);
				ts.decades %= DECADES_PER_CENTURY;

				if (ts.centuries >= CENTURIES_PER_MILLENNIUM) {
					// ripple centuries up to millennia
					ts.millennia += floor(ts.centuries / CENTURIES_PER_MILLENNIUM);
					ts.centuries %= CENTURIES_PER_MILLENNIUM;
				}
			}
		}
	}

	/**
	 * Remove any units not requested
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @param {number} units the units to populate
	 * @param {number} max number of labels to output
	 * @param {number} digits max number of decimal digits to output
	 */
	function pruneUnits(ts, units, max, digits) {
		var count = 0;

		// Calc from largest unit to smallest to prevent underflow
		if (!(units & MILLENNIA) || (count >= max)) {
			// ripple millennia down to centuries
			ts.centuries += ts.millennia * CENTURIES_PER_MILLENNIUM;
			delete ts.millennia;

		} else if (ts.millennia) {
			count++;
		}

		if (!(units & CENTURIES) || (count >= max)) {
			// ripple centuries down to decades
			ts.decades += ts.centuries * DECADES_PER_CENTURY;
			delete ts.centuries;

		} else if (ts.centuries) {
			count++;
		}

		if (!(units & DECADES) || (count >= max)) {
			// ripple decades down to years
			ts.years += ts.decades * YEARS_PER_DECADE;
			delete ts.decades;

		} else if (ts.decades) {
			count++;
		}

		if (!(units & YEARS) || (count >= max)) {
			// ripple years down to months
			ts.months += ts.years * MONTHS_PER_YEAR;
			delete ts.years;

		} else if (ts.years) {
			count++;
		}

		if (!(units & MONTHS) || (count >= max)) {
			// ripple months down to days
			if (ts.months) {
				ts.days += borrowMonths(ts.refMonth, ts.months);
			}
			delete ts.months;

			if (ts.days >= DAYS_PER_WEEK) {
				// ripple day overflow back up to weeks
				ts.weeks += floor(ts.days / DAYS_PER_WEEK);
				ts.days %= DAYS_PER_WEEK;
			}

		} else if (ts.months) {
			count++;
		}

		if (!(units & WEEKS) || (count >= max)) {
			// ripple weeks down to days
			ts.days += ts.weeks * DAYS_PER_WEEK;
			delete ts.weeks;

		} else if (ts.weeks) {
			count++;
		}

		if (!(units & DAYS) || (count >= max)) {
			//ripple days down to hours
			ts.hours += ts.days * HOURS_PER_DAY;
			delete ts.days;

		} else if (ts.days) {
			count++;
		}

		if (!(units & HOURS) || (count >= max)) {
			// ripple hours down to minutes
			ts.minutes += ts.hours * MINUTES_PER_HOUR;
			delete ts.hours;

		} else if (ts.hours) {
			count++;
		}

		if (!(units & MINUTES) || (count >= max)) {
			// ripple minutes down to seconds
			ts.seconds += ts.minutes * SECONDS_PER_MINUTE;
			delete ts.minutes;

		} else if (ts.minutes) {
			count++;
		}

		if (!(units & SECONDS) || (count >= max)) {
			// ripple seconds down to milliseconds
			ts.milliseconds += ts.seconds * MILLISECONDS_PER_SECOND;
			delete ts.seconds;

		} else if (ts.seconds) {
			count++;
		}

		// nothing to ripple milliseconds down to
		// so ripple back up to smallest existing unit as a fractional value
		if (!(units & MILLISECONDS) || (count >= max)) {
			fractional(ts, digits);
		}
	}

	/**
	 * Populates the Timespan object
	 * 
	 * @private
	 * @param {Timespan} ts
	 * @param {?Date} start the starting date
	 * @param {?Date} end the ending date
	 * @param {number} units the units to populate
	 * @param {number} max number of labels to output
	 * @param {number} digits max number of decimal digits to output
	 */
	function populate(ts, start, end, units, max, digits) {
		var now = new Date();

		ts.start = start = start || now;
		ts.end = end = end || now;
		ts.units = units;

		ts.value = end.getTime() - start.getTime();
		if (ts.value < 0) {
			// swap if reversed
			var tmp = end;
			end = start;
			start = tmp;
		}

		// reference month for determining days in month
		ts.refMonth = new Date(start.getFullYear(), start.getMonth(), 15, 12, 0, 0);
		try {
			// reset to initial deltas
			ts.millennia = 0;
			ts.centuries = 0;
			ts.decades = 0;
			ts.years = end.getFullYear() - start.getFullYear();
			ts.months = end.getMonth() - start.getMonth();
			ts.weeks = 0;
			ts.days = end.getDate() - start.getDate();
			ts.hours = end.getHours() - start.getHours();
			ts.minutes = end.getMinutes() - start.getMinutes();
			ts.seconds = end.getSeconds() - start.getSeconds();
			ts.milliseconds = end.getMilliseconds() - start.getMilliseconds();

			ripple(ts);
			pruneUnits(ts, units, max, digits);

		} finally {
			delete ts.refMonth;
		}

		return ts;
	}

	/**
	 * Determine an appropriate refresh rate based upon units
	 * 
	 * @private
	 * @param {number} units the units to populate
	 * @return {number} milliseconds to delay
	 */
	function getDelay(units) {
		if (units & MILLISECONDS) {
			// refresh very quickly
			return MILLISECONDS_PER_SECOND / 30; //30Hz
		}

		if (units & SECONDS) {
			// refresh every second
			return MILLISECONDS_PER_SECOND; //1Hz
		}

		if (units & MINUTES) {
			// refresh every minute
			return MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE;
		}

		if (units & HOURS) {
			// refresh hourly
			return MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
		}
		
		if (units & DAYS) {
			// refresh daily
			return MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY;
		}

		// refresh the rest weekly
		return MILLISECONDS_PER_SECOND * SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK;
	}

	/**
	 * API entry point
	 * 
	 * @public
	 * @param {Date|number|Timespan|null|function(Timespan,number)} start the starting date
	 * @param {Date|number|Timespan|null|function(Timespan,number)} end the ending date
	 * @param {number=} units the units to populate
	 * @param {number=} max number of labels to output
	 * @param {number=} digits max number of decimal digits to output
	 * @return {Timespan|number}
	 */
	function countdown(start, end, units, max, digits) {
		var callback;

		// ensure some units or use defaults
		units = +units || DEFAULTS;
		// max must be positive
		max = (max > 0) ? max : NaN;
		// clamp digits to an integer between [0, 20]
		digits = (digits > 0) ? (digits < 20) ? Math.round(digits) : 20 : 0;

		// ensure start date
		var startTS = null;
		if ('function' === typeof start) {
			callback = start;
			start = null;

		} else if (!(start instanceof Date)) {
			if ((start !== null) && isFinite(start)) {
				start = new Date(+start);
			} else {
				if ('object' === typeof startTS) {
					startTS = /** @type{Timespan} */(start);
				}
				start = null;
			}
		}

		// ensure end date
		var endTS = null;
		if ('function' === typeof end) {
			callback = end;
			end = null;

		} else if (!(end instanceof Date)) {
			if ((end !== null) && isFinite(end)) {
				end = new Date(+end);
			} else {
				if ('object' === typeof end) {
					endTS = /** @type{Timespan} */(end);
				}
				end = null;
			}
		}

		// must wait to interpret timespans until after resolving dates
		if (startTS) {
			start = addToDate(startTS, end);
		}
		if (endTS) {
			end = addToDate(endTS, start);
		}

		if (!start && !end) {
			// used for unit testing
			return new Timespan();
		}

		if (!callback) {
			return populate(new Timespan(), /** @type{Date} */(start), /** @type{Date} */(end), /** @type{number} */(units), /** @type{number} */(max), /** @type{number} */(digits));
		}

		// base delay off units
		var delay = getDelay(units),
			timerId,
			fn = function() {
				callback(
					populate(new Timespan(), /** @type{Date} */(start), /** @type{Date} */(end), /** @type{number} */(units), /** @type{number} */(max), /** @type{number} */(digits)),
					timerId
				);
			};

		fn();
		return (timerId = setInterval(fn, delay));
	}

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.MILLISECONDS = MILLISECONDS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.SECONDS = SECONDS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.MINUTES = MINUTES;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.HOURS = HOURS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.DAYS = DAYS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.WEEKS = WEEKS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.MONTHS = MONTHS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.YEARS = YEARS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.DECADES = DECADES;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.CENTURIES = CENTURIES;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.MILLENNIA = MILLENNIA;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.DEFAULTS = DEFAULTS;

	/**
	 * @public
	 * @const
	 * @type {number}
	 */
	countdown.ALL = MILLENNIA|CENTURIES|DECADES|YEARS|MONTHS|WEEKS|DAYS|HOURS|MINUTES|SECONDS|MILLISECONDS;

	/**
	 * Customize the format settings.
	 * @public
	 * @param {Object} format settings object
	 */
	var setFormat = countdown.setFormat = function(format) {
		if (!format) { return; }

		if ('singular' in format || 'plural' in format) {
			var singular = format.singular || [];
			if (singular.split) {
				singular = singular.split('|');
			}
			var plural = format.plural || [];
			if (plural.split) {
				plural = plural.split('|');
			}

			for (var i=LABEL_MILLISECONDS; i<=LABEL_MILLENNIA; i++) {
				// override any specified units
				LABELS_SINGLUAR[i] = singular[i] || LABELS_SINGLUAR[i];
				LABELS_PLURAL[i] = plural[i] || LABELS_PLURAL[i];
			}
		}

		if ('string' === typeof format.last) {
			LABEL_LAST = format.last;
		}
		if ('string' === typeof format.delim) {
			LABEL_DELIM = format.delim;
		}
		if ('string' === typeof format.empty) {
			LABEL_NOW = format.empty;
		}
		if ('function' === typeof format.formatNumber) {
			formatNumber = format.formatNumber;
		}
		if ('function' === typeof format.formatter) {
			formatter = format.formatter;
		}
	};

	/**
	 * Revert to the default formatting.
	 * @public
	 */
	var resetFormat = countdown.resetFormat = function() {
		LABELS_SINGLUAR = ' millisecond| second| minute| hour| day| week| month| year| decade| century| millennium'.split('|');
		LABELS_PLURAL = ' milliseconds| seconds| minutes| hours| days| weeks| months| years| decades| centuries| millennia'.split('|');
		LABEL_LAST = ' and ';
		LABEL_DELIM = ', ';
		LABEL_NOW = '';
		formatNumber = function(value) { return value; };
		formatter = plurality;
	};

	/**
	 * Override the unit labels.
	 * @public
	 * @param {string|Array=} singular a pipe ('|') delimited list of singular unit name overrides
	 * @param {string|Array=} plural a pipe ('|') delimited list of plural unit name overrides
	 * @param {string=} last a delimiter before the last unit (default: ' and ')
	 * @param {string=} delim a delimiter to use between all other units (default: ', ')
	 * @param {string=} empty a label to use when all units are zero (default: '')
	 * @param {function(number):string=} formatNumber a function which formats numbers as a string
	 * @param {function(number,number):string=} formatter a function which formats a number/unit pair as a string
	 * @deprecated since version 2.6.0
	 */
	countdown.setLabels = function(singular, plural, last, delim, empty, formatNumber, formatter) {
		setFormat({
			singular: singular,
			plural: plural,
			last: last,
			delim: delim,
			empty: empty,
			formatNumber: formatNumber,
			formatter: formatter
		});
	};

	/**
	 * Revert to the default unit labels.
	 * @public
	 * @deprecated since version 2.6.0
	 */
	countdown.resetLabels = resetFormat;

	resetFormat();

	if (typeof module !== 'undefined' && module.exports) {
		module.exports = countdown;

	} else if (typeof window !== 'undefined' && typeof window.define === 'function' && typeof window.define.amd !== 'undefined') {
		window.define('countdown', [], function() {
			return countdown;
		});
	}

	return countdown;

})();


================================================
FILE: demo.html
================================================
<!DOCTYPE html>
<html class="no-js">
<head>
	<meta charset="UTF-8">
	<title>Countdown.js Demo</title>

	<link href="test/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

	<h1><a href="/">Countdown.js</a></h1>

	<div>
		<h2 id="counter">Countdown.js Demo</h2>
		<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>

		<form>
			<h3>Countdown date</h3>
			<fieldset id="countdown-start"><span>
				<input id="year"
					type="number"
					min="-9999"
					max="9999"
					step="1"
					value="1991"
					size="4"
					placeholder="year"
				/><label for="month">-</label><input id="month"
					type="number"
					min="1"
					max="12"
					step="1"
					value="8"
					size="2"
					placeholder="month"
				/><label for="date">-</label><input id="date"
					type="number"
					min="1"
					max="31"
					step="1"
					value="6"
					size="2"
					placeholder="day"
				/></span>
				<span><label for="hours">&nbsp;</label><input id="hours"
					type="number"
					min="0"
					max="23"
					step="1"
					value="14"
					size="2"
					placeholder="hrs"
				/><label for="minutes">:</label><input id="minutes"
					type="number"
					min="0"
					max="59"
					step="1"
					value="56"
					size="2"
					placeholder="min"
				/><label for="seconds">:</label><input id="seconds"
					type="number"
					min="0"
					max="59"
					step="1"
					value="20"
					size="2"
					placeholder="sec"
				/><label for="milliseconds">.</label><input id="milliseconds"
					type="number"
					min="0"
					max="999"
					step="1"
					value="0"
					size="3"
					placeholder="ms"
				/></span>
				<span id="timezone">&nbsp;UTC</span>
			</fieldset>

			<h3>Display units</h3>
			<fieldset id="countdown-units"><span>
				<label for="units-millennia"><input id="units-millennia" type="checkbox" />millennia</label>
				<label for="units-centuries"><input id="units-centuries" type="checkbox" />centuries</label>
				<label for="units-decades"><input id="units-decades" type="checkbox" />decades</label>
			</span><span>
				<label for="units-years"><input id="units-years" type="checkbox" />years</label>
				<label for="units-months"><input id="units-months" type="checkbox" />months</label>
				<label for="units-weeks"><input id="units-weeks" type="checkbox" />weeks</label>
				<label for="units-days"><input id="units-days" type="checkbox" />days</label>
			</span><span>
				<label for="units-hours"><input id="units-hours" type="checkbox" />hours</label>
				<label for="units-minutes"><input id="units-minutes" type="checkbox" />minutes</label>
				<label for="units-seconds"><input id="units-seconds" type="checkbox" />seconds</label>
				<label for="units-milliseconds"><input id="units-milliseconds" type="checkbox" />milliseconds</label>
			</span><span>
				<label for="empty-label">empty</label><input id="empty-label" type="text" value="now!" />
			</span><span>
				<label for="max-units">max</label><input id="max-units" type="number" min="0" max="11" value="11" />
				<label for="frac-digits">digits</label><input id="frac-digits" type="number" min="0" max="20" value="0" />
			<span></fieldset>
		</form>

		<h3>Timespan object</h3>
		<pre id="timespan"></pre>
	</div>

	<script src="lib/json/json2.js" type="text/javascript"></script>
	<script src="lib/raf/requestAnimationFrame.js" type="text/javascript"></script>
	<script src="countdown.demo.js" type="text/javascript"></script>
	<script src="demo.js" type="text/javascript"></script>

	<footer>
		<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>
		Copyright &copy; 2006-2012 <a href="http://mck.me">Stephen M. McKamey</a>
	</footer>
	<script src="/ga.js" type="text/javascript" defer></script>
	<script src="http://platform.twitter.com/widgets.js" type="text/javascript" defer="defer"></script>
</body>
</html>


================================================
FILE: demo.js
================================================
(function() {
	function byId(id) {
		return document.getElementById(id);
	}

	function formatTens(n) {
		// format integers to have at least two digits
		return (n < 10) ? '0'+n : ''+n;
	}

	// initialize units
	for (var key in countdown) {
		if (countdown.hasOwnProperty(key)) {
			var unit = byId('units-'+key.toLowerCase());
			if (unit) {
				unit.value = countdown[key];
				unit.checked = countdown[key] & countdown.DEFAULTS;
			}
		}
	}

	// Mayan Calendar: 1356088271111

	// https://groups.google.com/group/alt.hypertext/msg/06dad279804cb3ba?dmode=source
	var DEMO_MS = 681490580000,
		DEMO_PAST = 'The World Wide Web debuted',
		DEMO_FUTURE = 'The World Wide Web debuts';

	// adjust initial demo time for local timezone
	byId('hours').value -= new Date(DEMO_MS).getTimezoneOffset() / 60;

	function update() {
		var units = ~countdown.ALL,
			chx = byId('countdown-units').getElementsByTagName('input'),
			empty = byId('empty-label').value || null,
			max = +(byId('max-units').value),
			digits = +(byId('frac-digits').value);

		for (var i=0, count=chx.length; i<count; i++) {
			if (chx[i].checked) {
				units |= +(chx[i].value);
			}
		}

		var yyyy = +(byId('year').value),
			MM = +(byId('month').value)-1,
			dd = +(byId('date').value),
			HH = +(byId('hours').value),
			mm = +(byId('minutes').value),
			ss = +(byId('seconds').value),
			fff = +(byId('milliseconds').value);

		var start = new Date(yyyy, MM, dd, HH, mm, ss, fff),
			ts = countdown(start, null, units, max, digits);

		var counter = byId('counter'),
			timespan = byId('timespan'),
			msg = ts.toHTML('strong', empty);

		if (start.getTime() === DEMO_MS) {
			msg = (ts.value > 0) ?
				DEMO_PAST+' '+msg+' ago!' :
				DEMO_FUTURE+' in '+msg+'!';
		}

		counter.innerHTML = msg;
		timespan.innerHTML = JSON.stringify(ts, null, '  ');

		// update timezone label
		var tz = start.getTimezoneOffset();
		if (tz) {
			var tzh = Math.floor(Math.abs(tz) / 60),
				tzm = (Math.abs(tz) % 60);
			byId('timezone').innerHTML = 'UTC'+((tz > 0) ? '-' : '+')+formatTens(tzh)+':'+formatTens(tzm);

		} else {
			byId('timezone').innerHTML = 'UTC';
		}

		requestAnimationFrame(update, timespan.parentNode);
	}
	update();
})();


================================================
FILE: externs.js
================================================
/**
 * @fileoverview External module declarations.
 * @externs
 */

/**
 * @type {Object|undefined}
 */
var module;

/**
 * @type {Object|undefined}
 */
var exports;


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html class="no-js">
<head>
	<meta charset="UTF-8">
	<title>Countdown.js</title>

	<link href="test/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

	<h1><a href="http://countdownjs.org">Countdown.js</a></h1>
	<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>
	<p class="summary">A simple JavaScript API for producing an accurate, intuitive description of the timespan between two Date instances.</p>

	<div style="overflow:hidden;">
		<div class="box">
			<h2>Docs</h2>
			<ul>
				<li><a href="demo.html" class="minimal glow">demonstration</a>
					<span class="action-info">watch it go</span></li>
				<li><a href="test/unit.html" class="minimal">unit tests</a>
					<span class="action-info">test it out</span></li>
				<li><a href="readme.html" class="minimal">read me</a>
					<span class="action-info">what? why? how?</span></li>
			</ul>
		</div>

		<div class="box">
			<h2>Downloads</h2>
			<ul>
				<li><a href="https://raw.githubusercontent.com/mckamey/countdownjs/master/countdown.js" class="minimal">countdown.js</a>
					<span class="action-info">full script</span></li>
				<li><a href="https://raw.githubusercontent.com/mckamey/countdownjs/master/countdown.min.js" class="minimal">countdown.min.js</a>
					<span class="action-info">minified script</span></li>
				<li><a href="https://raw.githubusercontent.com/mckamey/countdownjs/master/LICENSE.txt" class="minimal">LICENSE.txt</a>
					<span class="action-info">MIT License</span></li>
			</ul>
		</div>

		<div class="box">
			<h2>Links</h2>
			<ul>
				<li><a href="http://countdownjs.org" class="minimal">countdownjs.org</a>
					<span class="action-info">the website</span></li>
				<li><a href="https://github.com/mckamey/countdownjs" class="minimal">source code</a>
					<span class="action-info">the latest bits</span></li>
			</ul>
		</div>
	</div>

	<footer>
		<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>
		Copyright &copy; 2006-2012 <a href="http://mck.me">Stephen M. McKamey</a>
	</footer>
	<script src="/ga.js" type="text/javascript" defer></script>
	<script src="http://platform.twitter.com/widgets.js" type="text/javascript" defer="defer"></script>
</body>
</html>


================================================
FILE: lib/closure/COPYING
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: lib/closure/README
================================================
/*
 * Copyright 2009 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Contents
//

The Closure Compiler performs checking, instrumentation, and
optimizations on JavaScript code. The purpose of this README is to
explain how to build and run the Closure Compiler.

The Closure Compiler requires Java 6 or higher.
http://www.java.com/


//
// Building The Closure Compiler
//

There are three ways to get a Closure Compiler executable.

1) Use one we built for you.

Pre-built Closure binaries can be found at
http://code.google.com/p/closure-compiler/downloads/list


2) Check out the source and build it with Apache Ant.

First, check out the full source tree of the Closure Compiler. There
are instructions on how to do this at the project site.
http://code.google.com/p/closure-compiler/source/checkout

Apache Ant is a cross-platform build tool.
http://ant.apache.org/

At the root of the source tree, there is an Ant file named
build.xml. To use it, navigate to the same directory and type the
command

ant jar

This will produce a jar file called "build/compiler.jar".


3) Check out the source and build it with Eclipse.

Eclipse is a cross-platform IDE.
http://www.eclipse.org/

Under Eclipse's File menu, click "New > Project ..." and create a
"Java Project."  You will see an options screen. Give the project a
name, select "Create project from existing source," and choose the
root of the checked-out source tree as the existing directory. Verify
that you are using JRE version 6 or higher.

Eclipse can use the build.xml file to discover rules. When you
navigate to the build.xml file, you will see all the build rules in
the "Outline" pane. Run the "jar" rule to build the compiler in
build/compiler.jar.


//
// Running The Closure Compiler
//

Once you have the jar binary, running the Closure Compiler is straightforward.

On the command line, type

java -jar compiler.jar

This starts the compiler in interactive mode. Type

var x = 17 + 25;

then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux)
and "Enter" again. The Compiler will respond:

var x=42;

The Closure Compiler has many options for reading input from a file,
writing output to a file, checking your code, and running
optimizations. To learn more, type

java -jar compiler.jar --help

You can read more detailed documentation about the many flags at
http://code.google.com/closure/compiler/docs/gettingstarted_app.html


//
// Compiling Multiple Scripts
//

If you have multiple scripts, you should compile them all together with
one compile command.

java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js

The Closure Compiler will concatenate the files in the order they're
passed at the command line.

If you need to compile many, many scripts together, you may start to
run into problems with managing dependencies between scripts. You
should check out the Closure Library. It contains functions for
enforcing dependencies between scripts, and a tool called calcdeps.py
that knows how to give scripts to the Closure Compiler in the right
order.

http://code.google.com/p/closure-library/

//
// Licensing
//

Unless otherwise stated, all source files are licensed under
the Apache License, Version 2.0.


-----
Code under:
src/com/google/javascript/rhino
test/com/google/javascript/rhino

URL: http://www.mozilla.org/rhino
Version:  1.5R3, with heavy modifications
License:  Netscape Public License and MPL / GPL dual license

Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an
implementation of JavaScript for the JVM.  The JavaScript parser and
the parse tree data structures were extracted and modified
significantly for use by Google's JavaScript compiler.

Local Modifications: The packages have been renamespaced. All code not
relevant to parsing has been removed. A JsDoc parser and static typing
system have been added.


-----
Code in:
lib/rhino

Rhino
URL: http://www.mozilla.org/rhino
Version:  Trunk
License:  Netscape Public License and MPL / GPL dual license

Description: Mozilla Rhino is an implementation of JavaScript for the JVM.

Local Modifications: Minor changes to parsing JSDoc that usually get pushed
up-stream to Rhino trunk.


-----
Code in:
lib/args4j.jar

Args4j
URL: https://args4j.dev.java.net/
Version: 2.0.16
License: MIT

Description:
args4j is a small Java class library that makes it easy to parse command line
options/arguments in your CUI application.

Local Modifications: None.


-----
Code in:
lib/guava.jar

Guava Libraries
URL: http://code.google.com/p/guava-libraries/
Version:  14.0
License: Apache License 2.0

Description: Google's core Java libraries.

Local Modifications: None.


-----
Code in:
lib/jsr305.jar

Annotations for software defect detection
URL: http://code.google.com/p/jsr-305/
Version: svn revision 47
License: BSD License

Description: Annotations for software defect detection.

Local Modifications: None.


-----
Code in:
lib/jarjar.jar

Jar Jar Links
URL: http://jarjar.googlecode.com/
Version: 1.1
License: Apache License 2.0

Description:
A utility for repackaging Java libraries.

Local Modifications: None.


----
Code in:
lib/junit.jar

JUnit
URL:  http://sourceforge.net/projects/junit/
Version:  4.10
License:  Common Public License 1.0

Description: A framework for writing and running automated tests in Java.

Local Modifications: None.


---
Code in:
lib/protobuf-java.jar

Protocol Buffers
URL: http://code.google.com/p/protobuf/
Version: 2.4.1
License: New BSD License

Description: Supporting libraries for protocol buffers,
an encoding of structured data.

Local Modifications: None


---
Code in:
lib/ant.jar
lib/ant-launcher.jar

URL: http://ant.apache.org/bindownload.cgi
Version: 1.8.1
License: Apache License 2.0
Description:
  Ant is a Java based build tool. In theory it is kind of like "make"
  without make's wrinkles and with the full portability of pure java code.

Local Modifications: None


---
Code in:
lib/json.jar
URL: http://json.org/java/index.html
Version: JSON version 20090211
License: MIT license
Description:
JSON is a set of java files for use in transmitting data in JSON format.

Local Modifications: None

---
Code in:
tools/maven-ant-tasks-2.1.3.jar
URL: http://maven.apache.org
Version 2.1.3
License: Apache License 2.0
Description:
  Maven Ant tasks are used to manage dependencies and to install/deploy to
  maven repositories.

Local Modifications: None


================================================
FILE: lib/closure/docs.url
================================================
[InternetShortcut]
URL=http://code.google.com/closure/compiler/docs/js-for-compiler.html


================================================
FILE: lib/closure/download.url
================================================
[InternetShortcut]
URL=http://code.google.com/closure/compiler/


================================================
FILE: lib/jslint/download.url
================================================
[InternetShortcut]
URL=http://www.jslint.com/lint.html


================================================
FILE: lib/jslint/jslint.js
================================================
// jslint.js
// 2010-10-16

/*
Copyright (c) 2002 Douglas Crockford  (www.JSLint.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
    JSLINT is a global function. It takes two parameters.

        var myResult = JSLINT(source, option);

    The first parameter is either a string or an array of strings. If it is a
    string, it will be split on '\n' or '\r'. If it is an array of strings, it
    is assumed that each string represents one line. The source can be a
    JavaScript text, or HTML text, or a Konfabulator text.

    The second parameter is an optional object of options which control the
    operation of JSLINT. Most of the options are booleans: They are all are
    optional and have a default value of false.

    If it checks out, JSLINT returns true. Otherwise, it returns false.

    If false, you can inspect JSLINT.errors to find out the problems.
    JSLINT.errors is an array of objects containing these members:

    {
        line      : The line (relative to 0) at which the lint was found
        character : The character (relative to 0) at which the lint was found
        reason    : The problem
        evidence  : The text line in which the problem occurred
        raw       : The raw message before the details were inserted
        a         : The first detail
        b         : The second detail
        c         : The third detail
        d         : The fourth detail
    }

    If a fatal error was found, a null will be the last element of the
    JSLINT.errors array.

    You can request a Function Report, which shows all of the functions
    and the parameters and vars that they use. This can be used to find
    implied global variables and other problems. The report is in HTML and
    can be inserted in an HTML <body>.

        var myReport = JSLINT.report(limited);

    If limited is true, then the report will be limited to only errors.

    You can request a data structure which contains JSLint's results.

        var myData = JSLINT.data();

    It returns a structure with this form:

    {
        errors: [
            {
                line: NUMBER,
                character: NUMBER,
                reason: STRING,
                evidence: STRING
            }
        ],
        functions: [
            name: STRING,
            line: NUMBER,
            last: NUMBER,
            param: [
                STRING
            ],
            closure: [
                STRING
            ],
            var: [
                STRING
            ],
            exception: [
                STRING
            ],
            outer: [
                STRING
            ],
            unused: [
                STRING
            ],
            global: [
                STRING
            ],
            label: [
                STRING
            ]
        ],
        globals: [
            STRING
        ],
        member: {
            STRING: NUMBER
        },
        unuseds: [
            {
                name: STRING,
                line: NUMBER
            }
        ],
        implieds: [
            {
                name: STRING,
                line: NUMBER
            }
        ],
        urls: [
            STRING
        ],
        json: BOOLEAN
    }

    Empty arrays will not be included.

*/

/*jslint
    evil: true, nomen: false, onevar: false, regexp: false, strict: true
*/

/*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%",
    "(begin)", "(breakage)", "(context)", "(error)", "(global)",
    "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)",
    "(params)", "(scope)", "(statement)", "(verb)", "*", "+", "++", "-",
    "--", "\/", "<", "<=", "==", "===", ">", ">=", ADSAFE, ActiveXObject,
    Array, Boolean, COM, CScript, Canvas, CustomAnimation, Date, Debug, E,
    Enumerator, Error, EvalError, FadeAnimation, Flash, FormField, Frame,
    Function, HotKey, Image, JSON, LN10, LN2, LOG10E, LOG2E, MAX_VALUE,
    MIN_VALUE, Math, MenuItem, MoveAnimation, NEGATIVE_INFINITY, Number,
    Object, Option, PI, POSITIVE_INFINITY, Point, RangeError, Rectangle,
    ReferenceError, RegExp, ResizeAnimation, RotateAnimation, SQRT1_2,
    SQRT2, ScrollBar, String, Style, SyntaxError, System, Text, TextArea,
    Timer, TypeError, URIError, URL, VBArray, WScript, Web, Window, XMLDOM,
    XMLHttpRequest, "\\", a, abbr, acronym, addEventListener, address,
    adsafe, alert, aliceblue, animator, antiquewhite, appleScript, applet,
    apply, approved, aqua, aquamarine, area, arguments, arity, article,
    aside, audio, autocomplete, azure, b, background,
    "background-attachment", "background-color", "background-image",
    "background-position", "background-repeat", base, bdo, beep, beige, big,
    bisque, bitwise, black, blanchedalmond, block, blockquote, blue,
    blueviolet, blur, body, border, "border-bottom", "border-bottom-color",
    "border-bottom-style", "border-bottom-width", "border-collapse",
    "border-color", "border-left", "border-left-color", "border-left-style",
    "border-left-width", "border-right", "border-right-color",
    "border-right-style", "border-right-width", "border-spacing",
    "border-style", "border-top", "border-top-color", "border-top-style",
    "border-top-width", "border-width", bottom, br, brown, browser,
    burlywood, button, bytesToUIString, c, cadetblue, call, callee, caller,
    canvas, cap, caption, "caption-side", cases, center, charAt, charCodeAt,
    character, chartreuse, chocolate, chooseColor, chooseFile, chooseFolder,
    cite, clear, clearInterval, clearTimeout, clip, close, closeWidget,
    closed, closure, cm, code, col, colgroup, color, command, comment,
    condition, confirm, console, constructor, content, convertPathToHFS,
    convertPathToPlatform, coral, cornflowerblue, cornsilk,
    "counter-increment", "counter-reset", create, crimson, css, cursor,
    cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, darkgreen,
    darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, darkred,
    darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise,
    darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent,
    deeppink, deepskyblue, defaultStatus, defineClass, del, deserialize,
    details, devel, dfn, dialog, dimension, dimgray, dir, direction,
    display, div, dl, document, dodgerblue, dt, edition, else, em, embed,
    empty, "empty-cells", encodeURI, encodeURIComponent, entityify, eqeqeq,
    errors, es5, escape, eval, event, evidence, evil, ex, exception, exec, exps,
    fieldset, figure, filesystem, firebrick, first, float, floor,
    floralwhite, focus, focusWidget, font, "font-face", "font-family",
    "font-size", "font-size-adjust", "font-stretch", "font-style",
    "font-variant", "font-weight", footer, forestgreen, forin, form,
    fragment, frame, frames, frameset, from, fromCharCode, fuchsia, fud,
    funct, function, functions, g, gainsboro, gc, getComputedStyle,
    ghostwhite, global, globals, gold, goldenrod, gray, green, greenyellow,
    h1, h2, h3, h4, h5, h6, hasOwnProperty, head, header, height, help,
    hgroup, history, honeydew, hotpink, hr, 'hta:application', html,
    i, iTunes, id, identifier,
    iframe, img, immed, implieds, in, include, indent, indexOf, indianred,
    indigo, init, input, ins, isAlpha, isApplicationRunning, isDigit,
    isFinite, isNaN, ivory, join, jslint, json, kbd, keygen, khaki,
    konfabulatorVersion, label, labelled, lang, last, lavender,
    lavenderblush, lawngreen, laxbreak, lbp, led, left, legend,
    lemonchiffon, length, "letter-spacing", li, lib, lightblue, lightcoral,
    lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon,
    lightseagreen, lightskyblue, lightslategray, lightsteelblue,
    lightyellow, lime, limegreen, line, "line-height", linen, link,
    "list-style", "list-style-image", "list-style-position",
    "list-style-type", load, loadClass, location, log, m, magenta, map,
    margin, "margin-bottom", "margin-left", "margin-right", "margin-top",
    mark, "marker-offset", maroon, match, "max-height", "max-width", maxerr,
    maxlen, md5, media, mediumaquamarine, mediumblue, mediumorchid,
    mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen,
    mediumturquoise, mediumvioletred, member, menu, message, meta, meter,
    midnightblue, "min-height", "min-width", mintcream, mistyrose, mm,
    moccasin, moveBy, moveTo, name, nav, navajowhite, navigator, navy, new,
    newcap, noframes, nomen, noscript, nud, object, ol, oldlace, olive,
    olivedrab, on, onbeforeunload, onblur, onerror, onevar, onfocus, onload,
    onresize, onunload, opacity, open, openURL, opener, opera, optgroup,
    option, orange, orangered, orchid, outer, outline, "outline-color",
    "outline-style", "outline-width", output, overflow, "overflow-x",
    "overflow-y", p, padding, "padding-bottom", "padding-left",
    "padding-right", "padding-top", page, "page-break-after",
    "page-break-before", palegoldenrod, palegreen, paleturquoise,
    palevioletred, papayawhip, param, parent, parseFloat, parseInt,
    passfail, pc, peachpuff, peru, pink, play, plum, plusplus, pop,
    popupMenu, position, powderblue, pre, predef, preferenceGroups,
    preferences, print, progress, prompt, prototype, pt, purple, push, px,
    q, quit, quotes, random, range, raw, reach, readFile, readUrl, reason,
    red, regexp, reloadWidget, removeEventListener, replace, report,
    reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, rhino, right,
    rosybrown, royalblue, rp, rt, ruby, runCommand, runCommandInBg,
    saddlebrown, safe, salmon, samp, sandybrown, saveAs, savePreferences,
    screen, script, scroll, scrollBy, scrollTo, seagreen, seal, search,
    seashell, section, select, serialize, setInterval, setTimeout, shift,
    showWidgetPreferences, sienna, silver, skyblue, slateblue, slategray,
    sleep, slice, small, snow, sort, source, span, spawn, speak, split,
    springgreen, src, stack, statement, status, steelblue, strict, strong,
    style, styleproperty, sub, substr, sup, supplant, suppressUpdates, sync,
    system, table, "table-layout", tan, tbody, td, teal, tellWidget, test,
    "text-align", "text-decoration", "text-indent", "text-shadow",
    "text-transform", textarea, tfoot, th, thead, thistle, time, title,
    toLowerCase, toString, toUpperCase, toint32, token, tomato, top, tr, tt,
    turquoise, type, u, ul, undef, unescape, "unicode-bidi", unused,
    unwatch, updateNow, urls, value, valueOf, var, version,
    "vertical-align", video, violet, visibility, watch, wheat, white,
    "white-space", whitesmoke, widget, width, windows, "word-spacing",
    "word-wrap", yahooCheckLogin, yahooLogin, yahooLogout, yellow,
    yellowgreen, "z-index"
*/

// We build the application inside a function so that we produce only a single
// global variable. The function will be invoked, its return value is the JSLINT
// application itself.

"use strict";

var JSLINT = (function () {
    var adsafe_id,      // The widget's ADsafe id.
        adsafe_may,     // The widget may load approved scripts.
        adsafe_went,    // ADSAFE.go has been called.
        anonname,       // The guessed name for anonymous functions.
        approved,       // ADsafe approved urls.

        atrule = {
            media      : true,
            'font-face': true,
            page       : true
        },

// These are operators that should not be used with the ! operator.

        bang = {
            '<': true,
            '<=': true,
            '==': true,
            '===': true,
            '!==': true,
            '!=': true,
            '>': true,
            '>=': true,
            '+': true,
            '-': true,
            '*': true,
            '/': true,
            '%': true
        },

// These are members that should not be permitted in the safe subset.

        banned = {              // the member names that ADsafe prohibits.
            'arguments'     : true,
            callee          : true,
            caller          : true,
            constructor     : true,
            'eval'          : true,
            prototype       : true,
            stack           : true,
            unwatch         : true,
            valueOf         : true,
            watch           : true
        },


// These are the JSLint boolean options.

        boolOptions = {
            adsafe     : true, // if ADsafe should be enforced
            bitwise    : true, // if bitwise operators should not be allowed
            browser    : true, // if the standard browser globals should be predefined
            cap        : true, // if upper case HTML should be allowed
            css        : true, // if CSS workarounds should be tolerated
            debug      : true, // if debugger statements should be allowed
            devel      : true, // if logging should be allowed (console, alert, etc.)
            eqeqeq     : true, // if === should be required
            es5        : true, // if ES5 syntax should be allowed
            evil       : true, // if eval should be allowed
            forin      : true, // if for in statements must filter
            fragment   : true, // if HTML fragments should be allowed
            immed      : true, // if immediate invocations must be wrapped in parens
            laxbreak   : true, // if line breaks should not be checked
            newcap     : true, // if constructor names must be capitalized
            nomen      : true, // if names should be checked
            on         : true, // if HTML event handlers should be allowed
            onevar     : true, // if only one var statement per function should be allowed
            passfail   : true, // if the scan should stop on first error
            plusplus   : true, // if increment/decrement should not be allowed
            regexp     : true, // if the . should not be allowed in regexp literals
            rhino      : true, // if the Rhino environment globals should be predefined
            undef      : true, // if variables should be declared before used
            safe       : true, // if use of some browser features should be restricted
            windows    : true, // if MS Windows-specigic globals should be predefined
            strict     : true, // require the "use strict"; pragma
            sub        : true, // if all forms of subscript notation are tolerated
            white      : true, // if strict whitespace rules apply
            widget     : true  // if the Yahoo Widgets globals should be predefined
        },

// browser contains a set of global names which are commonly provided by a
// web browser environment.

        browser = {
            addEventListener: false,
            blur            : false,
            clearInterval   : false,
            clearTimeout    : false,
            close           : false,
            closed          : false,
            defaultStatus   : false,
            document        : false,
            event           : false,
            focus           : false,
            frames          : false,
            getComputedStyle: false,
            history         : false,
            Image           : false,
            length          : false,
            location        : false,
            moveBy          : false,
            moveTo          : false,
            name            : false,
            navigator       : false,
            onbeforeunload  : true,
            onblur          : true,
            onerror         : true,
            onfocus         : true,
            onload          : true,
            onresize        : true,
            onunload        : true,
            open            : false,
            opener          : false,
            Option          : false,
            parent          : false,
            print           : false,
            removeEventListener: false,
            resizeBy        : false,
            resizeTo        : false,
            screen          : false,
            scroll          : false,
            scrollBy        : false,
            scrollTo        : false,
            setInterval     : false,
            setTimeout      : false,
            status          : false,
            top             : false,
            XMLHttpRequest  : false
        },

        cssAttributeData,
        cssAny,

        cssColorData = {
            "aliceblue"             : true,
            "antiquewhite"          : true,
            "aqua"                  : true,
            "aquamarine"            : true,
            "azure"                 : true,
            "beige"                 : true,
            "bisque"                : true,
            "black"                 : true,
            "blanchedalmond"        : true,
            "blue"                  : true,
            "blueviolet"            : true,
            "brown"                 : true,
            "burlywood"             : true,
            "cadetblue"             : true,
            "chartreuse"            : true,
            "chocolate"             : true,
            "coral"                 : true,
            "cornflowerblue"        : true,
            "cornsilk"              : true,
            "crimson"               : true,
            "cyan"                  : true,
            "darkblue"              : true,
            "darkcyan"              : true,
            "darkgoldenrod"         : true,
            "darkgray"              : true,
            "darkgreen"             : true,
            "darkkhaki"             : true,
            "darkmagenta"           : true,
            "darkolivegreen"        : true,
            "darkorange"            : true,
            "darkorchid"            : true,
            "darkred"               : true,
            "darksalmon"            : true,
            "darkseagreen"          : true,
            "darkslateblue"         : true,
            "darkslategray"         : true,
            "darkturquoise"         : true,
            "darkviolet"            : true,
            "deeppink"              : true,
            "deepskyblue"           : true,
            "dimgray"               : true,
            "dodgerblue"            : true,
            "firebrick"             : true,
            "floralwhite"           : true,
            "forestgreen"           : true,
            "fuchsia"               : true,
            "gainsboro"             : true,
            "ghostwhite"            : true,
            "gold"                  : true,
            "goldenrod"             : true,
            "gray"                  : true,
            "green"                 : true,
            "greenyellow"           : true,
            "honeydew"              : true,
            "hotpink"               : true,
            "indianred"             : true,
            "indigo"                : true,
            "ivory"                 : true,
            "khaki"                 : true,
            "lavender"              : true,
            "lavenderblush"         : true,
            "lawngreen"             : true,
            "lemonchiffon"          : true,
            "lightblue"             : true,
            "lightcoral"            : true,
            "lightcyan"             : true,
            "lightgoldenrodyellow"  : true,
            "lightgreen"            : true,
            "lightpink"             : true,
            "lightsalmon"           : true,
            "lightseagreen"         : true,
            "lightskyblue"          : true,
            "lightslategray"        : true,
            "lightsteelblue"        : true,
            "lightyellow"           : true,
            "lime"                  : true,
            "limegreen"             : true,
            "linen"                 : true,
            "magenta"               : true,
            "maroon"                : true,
            "mediumaquamarine"      : true,
            "mediumblue"            : true,
            "mediumorchid"          : true,
            "mediumpurple"          : true,
            "mediumseagreen"        : true,
            "mediumslateblue"       : true,
            "mediumspringgreen"     : true,
            "mediumturquoise"       : true,
            "mediumvioletred"       : true,
            "midnightblue"          : true,
            "mintcream"             : true,
            "mistyrose"             : true,
            "moccasin"              : true,
            "navajowhite"           : true,
            "navy"                  : true,
            "oldlace"               : true,
            "olive"                 : true,
            "olivedrab"             : true,
            "orange"                : true,
            "orangered"             : true,
            "orchid"                : true,
            "palegoldenrod"         : true,
            "palegreen"             : true,
            "paleturquoise"         : true,
            "palevioletred"         : true,
            "papayawhip"            : true,
            "peachpuff"             : true,
            "peru"                  : true,
            "pink"                  : true,
            "plum"                  : true,
            "powderblue"            : true,
            "purple"                : true,
            "red"                   : true,
            "rosybrown"             : true,
            "royalblue"             : true,
            "saddlebrown"           : true,
            "salmon"                : true,
            "sandybrown"            : true,
            "seagreen"              : true,
            "seashell"              : true,
            "sienna"                : true,
            "silver"                : true,
            "skyblue"               : true,
            "slateblue"             : true,
            "slategray"             : true,
            "snow"                  : true,
            "springgreen"           : true,
            "steelblue"             : true,
            "tan"                   : true,
            "teal"                  : true,
            "thistle"               : true,
            "tomato"                : true,
            "turquoise"             : true,
            "violet"                : true,
            "wheat"                 : true,
            "white"                 : true,
            "whitesmoke"            : true,
            "yellow"                : true,
            "yellowgreen"           : true
        },

        cssBorderStyle,
        cssBreak,

        cssLengthData = {
            '%': true,
            'cm': true,
            'em': true,
            'ex': true,
            'in': true,
            'mm': true,
            'pc': true,
            'pt': true,
            'px': true
        },

        cssOverflow,

        devel = {
            alert           : false,
            confirm         : false,
            console         : false,
            Debug           : false,
            opera           : false,
            prompt          : false
        },

        escapes = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '/' : '\\/',
            '\\': '\\\\'
        },

        funct,          // The current function

        functionicity = [
            'closure', 'exception', 'global', 'label',
            'outer', 'unused', 'var'
        ],

        functions,      // All of the functions

        global,         // The global scope
        htmltag = {
            a:        {},
            abbr:     {},
            acronym:  {},
            address:  {},
            applet:   {},
            area:     {empty: true, parent: ' map '},
            article:  {},
            aside:    {},
            audio:    {},
            b:        {},
            base:     {empty: true, parent: ' head '},
            bdo:      {},
            big:      {},
            blockquote: {},
            body:     {parent: ' html noframes '},
            br:       {empty: true},
            button:   {},
            canvas:   {parent: ' body p div th td '},
            caption:  {parent: ' table '},
            center:   {},
            cite:     {},
            code:     {},
            col:      {empty: true, parent: ' table colgroup '},
            colgroup: {parent: ' table '},
            command:  {parent: ' menu '},
            datalist: {},
            dd:       {parent: ' dl '},
            del:      {},
            details:  {},
            dialog:   {},
            dfn:      {},
            dir:      {},
            div:      {},
            dl:       {},
            dt:       {parent: ' dl '},
            em:       {},
            embed:    {},
            fieldset: {},
            figure:   {},
            font:     {},
            footer:   {},
            form:     {},
            frame:    {empty: true, parent: ' frameset '},
            frameset: {parent: ' html frameset '},
            h1:       {},
            h2:       {},
            h3:       {},
            h4:       {},
            h5:       {},
            h6:       {},
            head:     {parent: ' html '},
            header:   {},
            hgroup:   {},
            hr:       {empty: true},
            'hta:application':
                      {empty: true, parent: ' head '},
            html:     {parent: '*'},
            i:        {},
            iframe:   {},
            img:      {empty: true},
            input:    {empty: true},
            ins:      {},
            kbd:      {},
            keygen:   {},
            label:    {},
            legend:   {parent: ' details fieldset figure '},
            li:       {parent: ' dir menu ol ul '},
            link:     {empty: true, parent: ' head '},
            map:      {},
            mark:     {},
            menu:     {},
            meta:     {empty: true, parent: ' head noframes noscript '},
            meter:    {},
            nav:      {},
            noframes: {parent: ' html body '},
            noscript: {parent: ' body head noframes '},
            object:   {},
            ol:       {},
            optgroup: {parent: ' select '},
            option:   {parent: ' optgroup select '},
            output:   {},
            p:        {},
            param:    {empty: true, parent: ' applet object '},
            pre:      {},
            progress: {},
            q:        {},
            rp:       {},
            rt:       {},
            ruby:     {},
            samp:     {},
            script:   {empty: true, parent: ' body div frame head iframe p pre span '},
            section:  {},
            select:   {},
            small:    {},
            span:     {},
            source:   {},
            strong:   {},
            style:    {parent: ' head ', empty: true},
            sub:      {},
            sup:      {},
            table:    {},
            tbody:    {parent: ' table '},
            td:       {parent: ' tr '},
            textarea: {},
            tfoot:    {parent: ' table '},
            th:       {parent: ' tr '},
            thead:    {parent: ' table '},
            time:     {},
            title:    {parent: ' head '},
            tr:       {parent: ' table tbody thead tfoot '},
            tt:       {},
            u:        {},
            ul:       {},
            'var':    {},
            video:    {}
        },

        ids,            // HTML ids
        implied,        // Implied globals
        inblock,
        indent,
        jsonmode,
        lines,
        lookahead,
        member,
        membersOnly,
        nexttoken,
        noreach,
        option,
        predefined,     // Global variables defined by option
        prereg,
        prevtoken,

        rhino = {
            defineClass : false,
            deserialize : false,
            gc          : false,
            help        : false,
            load        : false,
            loadClass   : false,
            print       : false,
            quit        : false,
            readFile    : false,
            readUrl     : false,
            runCommand  : false,
            seal        : false,
            serialize   : false,
            spawn       : false,
            sync        : false,
            toint32     : false,
            version     : false
        },

        scope,      // The current scope

        windows = {
            ActiveXObject: false,
            CScript      : false,
            Debug        : false,
            Enumerator   : false,
            System       : false,
            VBArray      : false,
            WScript      : false
        },

        src,
        stack,

// standard contains the global names that are provided by the
// ECMAScript standard.

        standard = {
            Array               : false,
            Boolean             : false,
            Date                : false,
            decodeURI           : false,
            decodeURIComponent  : false,
            encodeURI           : false,
            encodeURIComponent  : false,
            Error               : false,
            'eval'              : false,
            EvalError           : false,
            Function            : false,
            hasOwnProperty      : false,
            isFinite            : false,
            isNaN               : false,
            JSON                : false,
            Math                : false,
            Number              : false,
            Object              : false,
            parseInt            : false,
            parseFloat          : false,
            RangeError          : false,
            ReferenceError      : false,
            RegExp              : false,
            String              : false,
            SyntaxError         : false,
            TypeError           : false,
            URIError            : false
        },

        standard_member = {
            E                   : true,
            LN2                 : true,
            LN10                : true,
            LOG2E               : true,
            LOG10E              : true,
            PI                  : true,
            SQRT1_2             : true,
            SQRT2               : true,
            MAX_VALUE           : true,
            MIN_VALUE           : true,
            NEGATIVE_INFINITY   : true,
            POSITIVE_INFINITY   : true
        },

        strict_mode,
        syntax = {},
        tab,
        token,
        urls,
        warnings,

// widget contains the global names which are provided to a Yahoo
// (fna Konfabulator) widget.

        widget = {
            alert                   : true,
            animator                : true,
            appleScript             : true,
            beep                    : true,
            bytesToUIString         : true,
            Canvas                  : true,
            chooseColor             : true,
            chooseFile              : true,
            chooseFolder            : true,
            closeWidget             : true,
            COM                     : true,
            convertPathToHFS        : true,
            convertPathToPlatform   : true,
            CustomAnimation         : true,
            escape                  : true,
            FadeAnimation           : true,
            filesystem              : true,
            Flash                   : true,
            focusWidget             : true,
            form                    : true,
            FormField               : true,
            Frame                   : true,
            HotKey                  : true,
            Image                   : true,
            include                 : true,
            isApplicationRunning    : true,
            iTunes                  : true,
            konfabulatorVersion     : true,
            log                     : true,
            md5                     : true,
            MenuItem                : true,
            MoveAnimation           : true,
            openURL                 : true,
            play                    : true,
            Point                   : true,
            popupMenu               : true,
            preferenceGroups        : true,
            preferences             : true,
            print                   : true,
            prompt                  : true,
            random                  : true,
            Rectangle               : true,
            reloadWidget            : true,
            ResizeAnimation         : true,
            resolvePath             : true,
            resumeUpdates           : true,
            RotateAnimation         : true,
            runCommand              : true,
            runCommandInBg          : true,
            saveAs                  : true,
            savePreferences         : true,
            screen                  : true,
            ScrollBar               : true,
            showWidgetPreferences   : true,
            sleep                   : true,
            speak                   : true,
            Style                   : true,
            suppressUpdates         : true,
            system                  : true,
            tellWidget              : true,
            Text                    : true,
            TextArea                : true,
            Timer                   : true,
            unescape                : true,
            updateNow               : true,
            URL                     : true,
            Web                     : true,
            widget                  : true,
            Window                  : true,
            XMLDOM                  : true,
            XMLHttpRequest          : true,
            yahooCheckLogin         : true,
            yahooLogin              : true,
            yahooLogout             : true
        },

//  xmode is used to adapt to the exceptions in html parsing.
//  It can have these states:
//      false   .js script file
//      html
//      outer
//      script
//      style
//      scriptstring
//      styleproperty

        xmode,
        xquote,

// unsafe comment or string
        ax = /@cc|<\/?|script|\]*s\]|<\s*!|&lt/i,
// unsafe characters that are silently deleted by one or more browsers
        cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
// token
        tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/,
// html token
        hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/,
// characters in strings that need escapement
        nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
        nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
// outer html token
        ox = /[>&]|<[\/!]?|--/,
// star slash
        lx = /\*\/|\/\*/,
// identifier
        ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
// javascript url
        jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i,
// url badness
        ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i,
// style
        sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/,
        ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/,
// attributes characters
        qx = /[^a-zA-Z0-9+\-_\/ ]/,
// query characters for ids
        dx = /[\[\]\/\\"'*<>.&:(){}+=#]/,

        rx = {
            outer: hx,
            html: hx,
            style: sx,
            styleproperty: ssx
        };

    function F() {}

    if (typeof Object.create !== 'function') {
        Object.create = function (o) {
            F.prototype = o;
            return new F();
        };
    }


    function is_own(object, name) {
        return Object.prototype.hasOwnProperty.call(object, name);
    }


    function combine(t, o) {
        var n;
        for (n in o) {
            if (is_own(o, n)) {
                t[n] = o[n];
            }
        }
    }

    String.prototype.entityify = function () {
        return this
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;');
    };

    String.prototype.isAlpha = function () {
        return (this >= 'a' && this <= 'z\uffff') ||
            (this >= 'A' && this <= 'Z\uffff');
    };


    String.prototype.isDigit = function () {
        return (this >= '0' && this <= '9');
    };


    String.prototype.supplant = function (o) {
        return this.replace(/\{([^{}]*)\}/g, function (a, b) {
            var r = o[b];
            return typeof r === 'string' || typeof r === 'number' ? r : a;
        });
    };

    String.prototype.name = function () {

// If the string looks like an identifier, then we can return it as is.
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can simply slap some quotes around it.
// Otherwise we must also replace the offending characters with safe
// sequences.

        if (ix.test(this)) {
            return this;
        }
        if (nx.test(this)) {
            return '"' + this.replace(nxg, function (a) {
                var c = escapes[a];
                if (c) {
                    return c;
                }
                return '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
            }) + '"';
        }
        return '"' + this + '"';
    };


    function assume() {
        if (!option.safe) {
            if (option.rhino) {
                combine(predefined, rhino);
            }
            if (option.devel) {
                combine(predefined, devel);
            }
            if (option.browser) {
                combine(predefined, browser);
            }
            if (option.windows) {
                combine(predefined, windows);
            }
            if (option.widget) {
                combine(predefined, widget);
            }
        }
    }


// Produce an error warning.

    function quit(m, l, ch) {
        throw {
            name: 'JSLintError',
            line: l,
            character: ch,
            message: m + " (" + Math.floor((l / lines.length) * 100) +
                    "% scanned)."
        };
    }

    function warning(m, t, a, b, c, d) {
        var ch, l, w;
        t = t || nexttoken;
        if (t.id === '(end)') {  // `~
            t = token;
        }
        l = t.line || 0;
        ch = t.from || 0;
        w = {
            id: '(error)',
            raw: m,
            evidence: lines[l - 1] || '',
            line: l,
            character: ch,
            a: a,
            b: b,
            c: c,
            d: d
        };
        w.reason = m.supplant(w);
        JSLINT.errors.push(w);
        if (option.passfail) {
            quit('Stopping. ', l, ch);
        }
        warnings += 1;
        if (warnings >= option.maxerr) {
            quit("Too many errors.", l, ch);
        }
        return w;
    }

    function warningAt(m, l, ch, a, b, c, d) {
        return warning(m, {
            line: l,
            from: ch
        }, a, b, c, d);
    }

    function error(m, t, a, b, c, d) {
        var w = warning(m, t, a, b, c, d);
        quit("Stopping, unable to continue.", w.line, w.character);
    }

    function errorAt(m, l, ch, a, b, c, d) {
        return error(m, {
            line: l,
            from: ch
        }, a, b, c, d);
    }



// lexical analysis

    var lex = (function lex() {
        var character, from, line, s;

// Private lex methods

        function nextLine() {
            var at;
            if (line >= lines.length) {
                return false;
            }
            character = 1;
            s = lines[line];
            line += 1;
            at = s.search(/ \t/);
            if (at >= 0) {
                warningAt("Mixed spaces and tabs.", line, at + 1);
            }
            s = s.replace(/\t/g, tab);
            at = s.search(cx);
            if (at >= 0) {
                warningAt("Unsafe character.", line, at);
            }
            if (option.maxlen && option.maxlen < s.length) {
                warningAt("Line too long.", line, s.length);
            }
            return true;
        }

// Produce a token object.  The token inherits from a syntax symbol.

        function it(type, value) {
            var i, t;
            if (type === '(color)' || type === '(range)') {
                t = {type: type};
            } else if (type === '(punctuator)' ||
                    (type === '(identifier)' && is_own(syntax, value))) {
                t = syntax[value] || syntax['(error)'];
            } else {
                t = syntax[type];
            }
            t = Object.create(t);
            if (type === '(string)' || type === '(range)') {
                if (jx.test(value)) {
                    warningAt("Script URL.", line, from);
                }
            }
            if (type === '(identifier)') {
                t.identifier = true;
                if (value === '__iterator__' || value === '__proto__') {
                    errorAt("Reserved name '{a}'.",
                        line, from, value);
                } else if (option.nomen &&
                        (value.charAt(0) === '_' ||
                         value.charAt(value.length - 1) === '_')) {
                    warningAt("Unexpected {a} in '{b}'.", line, from,
                        "dangling '_'", value);
                }
            }
            t.value = value;
            t.line = line;
            t.character = character;
            t.from = from;
            i = t.id;
            if (i !== '(endline)') {
                prereg = i &&
                    (('(,=:[!&|?{};'.indexOf(i.charAt(i.length - 1)) >= 0) ||
                    i === 'return');
            }
            return t;
        }

// Public lex methods

        return {
            init: function (source) {
                if (typeof source === 'string') {
                    lines = source
                        .replace(/\r\n/g, '\n')
                        .replace(/\r/g, '\n')
                        .split('\n');
                } else {
                    lines = source;
                }
                line = 0;
                nextLine();
                from = 1;
            },

            range: function (begin, end) {
                var c, value = '';
                from = character;
                if (s.charAt(0) !== begin) {
                    errorAt("Expected '{a}' and instead saw '{b}'.",
                            line, character, begin, s.charAt(0));
                }
                for (;;) {
                    s = s.slice(1);
                    character += 1;
                    c = s.charAt(0);
                    switch (c) {
                    case '':
                        errorAt("Missing '{a}'.", line, character, c);
                        break;
                    case end:
                        s = s.slice(1);
                        character += 1;
                        return it('(range)', value);
                    case xquote:
                    case '\\':
                        warningAt("Unexpected '{a}'.", line, character, c);
                    }
                    value += c;
                }

            },

// token -- this is called by advance to get the next token.

            token: function () {
                var b, c, captures, d, depth, high, i, l, low, q, t;

                function match(x) {
                    var r = x.exec(s), r1;
                    if (r) {
                        l = r[0].length;
                        r1 = r[1];
                        c = r1.charAt(0);
                        s = s.substr(l);
                        from = character + l - r1.length;
                        character += l;
                        return r1;
                    }
                }

                function string(x) {
                    var c, j, r = '';

                    if (jsonmode && x !== '"') {
                        warningAt("Strings must use doublequote.",
                                line, character);
                    }

                    if (xquote === x || (xmode === 'scriptstring' && !xquote)) {
                        return it('(punctuator)', x);
                    }

                    function esc(n) {
                        var i = parseInt(s.substr(j + 1, n), 16);
                        j += n;
                        if (i >= 32 && i <= 126 &&
                                i !== 34 && i !== 92 && i !== 39) {
                            warningAt("Unnecessary escapement.", line, character);
                        }
                        character += n;
                        c = String.fromCharCode(i);
                    }
                    j = 0;
                    for (;;) {
                        while (j >= s.length) {
                            j = 0;
                            if (xmode !== 'html' || !nextLine()) {
                                errorAt("Unclosed string.", line, from);
                            }
                        }
                        c = s.charAt(j);
                        if (c === x) {
                            character += 1;
                            s = s.substr(j + 1);
                            return it('(string)', r, x);
                        }
                        if (c < ' ') {
                            if (c === '\n' || c === '\r') {
                                break;
                            }
                            warningAt("Control character in string: {a}.",
                                    line, character + j, s.slice(0, j));
                        } else if (c === xquote) {
                            warningAt("Bad HTML string", line, character + j);
                        } else if (c === '<') {
                            if (option.safe && xmode === 'html') {
                                warningAt("ADsafe string violation.",
                                        line, character + j);
                            } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) {
                                warningAt("Expected '<\\/' and instead saw '</'.", line, character);
                            } else if (s.charAt(j + 1) === '!' && (xmode || option.safe)) {
                                warningAt("Unexpected '<!' in a string.", line, character);
                            }
                        } else if (c === '\\') {
                            if (xmode === 'html') {
                                if (option.safe) {
                                    warningAt("ADsafe string violation.",
                                            line, character + j);
                                }
                            } else if (xmode === 'styleproperty') {
                                j += 1;
                                character += 1;
                                c = s.charAt(j);
                                if (c !== x) {
                                    warningAt("Escapement in style string.",
                                            line, character + j);
                                }
                            } else {
                                j += 1;
                                character += 1;
                                c = s.charAt(j);
                                switch (c) {
                                case xquote:
                                    warningAt("Bad HTML string", line,
                                        character + j);
                                    break;
                                case '\\':
                                case '\'':
                                case '"':
                                case '/':
                                    break;
                                case 'b':
                                    c = '\b';
                                    break;
                                case 'f':
                                    c = '\f';
                                    break;
                                case 'n':
                                    c = '\n';
                                    break;
                                case 'r':
                                    c = '\r';
                                    break;
                                case 't':
                                    c = '\t';
                                    break;
                                case 'u':
                                    esc(4);
                                    break;
                                case 'v':
                                    c = '\v';
                                    break;
                                case 'x':
                                    if (jsonmode) {
                                        warningAt("Avoid \\x-.", line, character);
                                    }
                                    esc(2);
                                    break;
                                default:
                                    warningAt("Bad escapement.", line, character);
                                }
                            }
                        }
                        r += c;
                        character += 1;
                        j += 1;
                    }
                }

                for (;;) {
                    if (!s) {
                        return it(nextLine() ? '(endline)' : '(end)', '');
                    }
                    while (xmode === 'outer') {
                        i = s.search(ox);
                        if (i === 0) {
                            break;
                        } else if (i > 0) {
                            character += 1;
                            s = s.slice(i);
                            break;
                        } else {
                            if (!nextLine()) {
                                return it('(end)', '');
                            }
                        }
                    }
//                     t = match(rx[xmode] || tx);
//                     if (!t) {
//                         if (xmode === 'html') {
//                             return it('(error)', s.charAt(0));
//                         } else {
//                             t = '';
//                             c = '';
//                             while (s && s < '!') {
//                                 s = s.substr(1);
//                             }
//                             if (s) {
//                                 errorAt("Unexpected '{a}'.",
//                                         line, character, s.substr(0, 1));
//                             }
//                         }
                    t = match(rx[xmode] || tx);
                    if (!t) {
                        t = '';
                        c = '';
                        while (s && s < '!') {
                            s = s.substr(1);
                        }
                        if (s) {
                            if (xmode === 'html') {
                                return it('(error)', s.charAt(0));
                            } else {
                                errorAt("Unexpected '{a}'.",
                                        line, character, s.substr(0, 1));
                            }
                        }
                    } else {

    //      identifier

                        if (c.isAlpha() || c === '_' || c === '$') {
                            return it('(identifier)', t);
                        }

    //      number

                        if (c.isDigit()) {
                            if (xmode !== 'style' && !isFinite(Number(t))) {
                                warningAt("Bad number '{a}'.",
                                    line, character, t);
                            }
                            if (xmode !== 'style' &&
                                     xmode !== 'styleproperty' &&
                                     s.substr(0, 1).isAlpha()) {
                                warningAt("Missing space after '{a}'.",
                                        line, character, t);
                            }
                            if (c === '0') {
                                d = t.substr(1, 1);
                                if (d.isDigit()) {
                                    if (token.id !== '.' && xmode !== 'styleproperty') {
                                        warningAt("Don't use extra leading zeros '{a}'.",
                                            line, character, t);
                                    }
                                } else if (jsonmode && (d === 'x' || d === 'X')) {
                                    warningAt("Avoid 0x-. '{a}'.",
                                            line, character, t);
                                }
                            }
                            if (t.substr(t.length - 1) === '.') {
                                warningAt(
        "A trailing decimal point can be confused with a dot '{a}'.",
                                        line, character, t);
                            }
                            return it('(number)', t);
                        }
                        switch (t) {

    //      string

                        case '"':
                        case "'":
                            return string(t);

    //      // comment

                        case '//':
                            if (src || (xmode && xmode !== 'script')) {
                                warningAt("Unexpected comment.", line, character);
                            } else if (xmode === 'script' && /<\s*\//i.test(s)) {
                                warningAt("Unexpected <\/ in comment.", line, character);
                            } else if ((option.safe || xmode === 'script') && ax.test(s)) {
                                warningAt("Dangerous comment.", line, character);
                            }
                            s = '';
                            token.comment = true;
                            break;

    //      /* comment

                        case '/*':
                            if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) {
                                warningAt("Unexpected comment.", line, character);
                            }
                            if (option.safe && ax.test(s)) {
                                warningAt("ADsafe comment violation.", line, character);
                            }
                            for (;;) {
                                i = s.search(lx);
                                if (i >= 0) {
                                    break;
                                }
                                if (!nextLine()) {
                                    errorAt("Unclosed comment.", line, character);
                                } else {
                                    if (option.safe && ax.test(s)) {
                                        warningAt("ADsafe comment violation.",
                                                line, character);
                                    }
                                }
                            }
                            character += i + 2;
                            if (s.substr(i, 1) === '/') {
                                errorAt("Nested comment.", line, character);
                            }
                            s = s.substr(i + 2);
                            token.comment = true;
                            break;

    //      /*members /*jslint /*global

                        case '/*members':
                        case '/*member':
                        case '/*jslint':
                        case '/*global':
                        case '*/':
                            return {
                                value: t,
                                type: 'special',
                                line: line,
                                character: character,
                                from: from
                            };

                        case '':
                            break;
    //      /
                        case '/':
                            if (token.id === '/=') {
                                errorAt(
"A regular expression literal can be confused with '/='.", line, from);
                            }
                            if (prereg) {
                                depth = 0;
                                captures = 0;
                                l = 0;
                                for (;;) {
                                    b = true;
                                    c = s.charAt(l);
                                    l += 1;
                                    switch (c) {
                                    case '':
                                        errorAt("Unclosed regular expression.",
                                                line, from);
                                        return;
                                    case '/':
                                        if (depth > 0) {
                                            warningAt("Unescaped '{a}'.",
                                                    line, from + l, '/');
                                        }
                                        c = s.substr(0, l - 1);
                                        q = {
                                            g: true,
                                            i: true,
                                            m: true
                                        };
                                        while (q[s.charAt(l)] === true) {
                                            q[s.charAt(l)] = false;
                                            l += 1;
                                        }
                                        character += l;
                                        s = s.substr(l);
                                        q = s.charAt(0);
                                        if (q === '/' || q === '*') {
                                            errorAt("Confusing regular expression.",
                                                    line, from);
                                        }
                                        return it('(regexp)', c);
                                    case '\\':
                                        c = s.charAt(l);
                                        if (c < ' ') {
                                            warningAt(
"Unexpected control character in regular expression.", line, from + l);
                                        } else if (c === '<') {
                                            warningAt(
"Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
                                        }
                                        l += 1;
                                        break;
                                    case '(':
                                        depth += 1;
                                        b = false;
                                        if (s.charAt(l) === '?') {
                                            l += 1;
                                            switch (s.charAt(l)) {
                                            case ':':
                                            case '=':
                                            case '!':
                                                l += 1;
                                                break;
                                            default:
                                                warningAt(
"Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l));
                                            }
                                        } else {
                                            captures += 1;
                                        }
                                        break;
                                    case '|':
                                        b = false;
                                        break;
                                    case ')':
                                        if (depth === 0) {
                                            warningAt("Unescaped '{a}'.",
                                                    line, from + l, ')');
                                        } else {
                                            depth -= 1;
                                        }
                                        break;
                                    case ' ':
                                        q = 1;
                                        while (s.charAt(l) === ' ') {
                                            l += 1;
                                            q += 1;
                                        }
                                        if (q > 1) {
                                            warningAt(
"Spaces are hard to count. Use {{a}}.", line, from + l, q);
                                        }
                                        break;
                                    case '[':
                                        c = s.charAt(l);
                                        if (c === '^') {
                                            l += 1;
                                            if (option.regexp) {
                                                warningAt("Insecure '{a}'.",
                                                        line, from + l, c);
                                            } else if (s.charAt(l) === ']') {
                                                errorAt("Unescaped '{a}'.",
                                                    line, from, '^');
                                            }
                                        }
                                        q = false;
                                        if (c === ']') {
                                            warningAt("Empty class.", line,
                                                    from + l - 1);
                                            q = true;
                                        }
klass:                                  do {
                                            c = s.charAt(l);
                                            l += 1;
                                            switch (c) {
                                            case '[':
                                            case '^':
                                                warningAt("Unescaped '{a}'.",
                                                        line, from + l, c);
                                                q = true;
                                                break;
                                            case '-':
                                                if (q) {
                                                    q = false;
                                                } else {
                                                    warningAt("Unescaped '{a}'.",
                                                            line, from + l, '-');
                                                    q = true;
                                                }
                                                break;
                                            case ']':
                                                if (!q) {
                                                    warningAt("Unescaped '{a}'.",
                                                            line, from + l - 1, '-');
                                                }
                                                break klass;
                                            case '\\':
                                                c = s.charAt(l);
                                                if (c < ' ') {
                                                    warningAt(
"Unexpected control character in regular expression.", line, from + l);
                                                } else if (c === '<') {
                                                    warningAt(
"Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
                                                }
                                                l += 1;
                                                q = true;
                                                break;
                                            case '/':
                                                warningAt("Unescaped '{a}'.",
                                                        line, from + l - 1, '/');
                                                q = true;
                                                break;
                                            case '<':
                                                if (xmode === 'script') {
                                                    c = s.charAt(l);
                                                    if (c === '!' || c === '/') {
                                                        warningAt(
"HTML confusion in regular expression '<{a}'.", line, from + l, c);
                                                    }
                                                }
                                                q = true;
                                                break;
                                            default:
                                                q = true;
                                            }
                                        } while (c);
                                        break;
                                    case '.':
                                        if (option.regexp) {
                                            warningAt("Insecure '{a}'.", line,
                                                    from + l, c);
                                        }
                                        break;
                                    case ']':
                                    case '?':
                                    case '{':
                                    case '}':
                                    case '+':
                                    case '*':
                                        warningAt("Unescaped '{a}'.", line,
                                                from + l, c);
                                        break;
                                    case '<':
                                        if (xmode === 'script') {
                                            c = s.charAt(l);
                                            if (c === '!' || c === '/') {
                                                warningAt(
"HTML confusion in regular expression '<{a}'.", line, from + l, c);
                                            }
                                        }
                                    }
                                    if (b) {
                                        switch (s.charAt(l)) {
                                        case '?':
                                        case '+':
                                        case '*':
                                            l += 1;
                                            if (s.charAt(l) === '?') {
                                                l += 1;
                                            }
                                            break;
                                        case '{':
                                            l += 1;
                                            c = s.charAt(l);
                                            if (c < '0' || c > '9') {
                                                warningAt(
"Expected a number and instead saw '{a}'.", line, from + l, c);
                                            }
                                            l += 1;
                                            low = +c;
                                            for (;;) {
                                                c = s.charAt(l);
                                                if (c < '0' || c > '9') {
                                                    break;
                                                }
                                                l += 1;
                                                low = +c + (low * 10);
                                            }
                                            high = low;
                                            if (c === ',') {
                                                l += 1;
                                                high = Infinity;
                                                c = s.charAt(l);
                                                if (c >= '0' && c <= '9') {
                                                    l += 1;
                                                    high = +c;
                                                    for (;;) {
                                                        c = s.charAt(l);
                                                        if (c < '0' || c > '9') {
                                                            break;
                                                        }
                                                        l += 1;
                                                        high = +c + (high * 10);
                                                    }
                                                }
                                            }
                                            if (s.charAt(l) !== '}') {
                                                warningAt(
"Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c);
                                            } else {
                                                l += 1;
                                            }
                                            if (s.charAt(l) === '?') {
                                                l += 1;
                                            }
                                            if (low > high) {
                                                warningAt(
"'{a}' should not be greater than '{b}'.", line, from + l, low, high);
                                            }
                                        }
                                    }
                                }
                                c = s.substr(0, l - 1);
                                character += l;
                                s = s.substr(l);
                                return it('(regexp)', c);
                            }
                            return it('(punctuator)', t);

    //      punctuator

                        case '<!--':
                            l = line;
                            c = character;
                            for (;;) {
                                i = s.indexOf('--');
                                if (i >= 0) {
                                    break;
                                }
                                i = s.indexOf('<!');
                                if (i >= 0) {
                                    errorAt("Nested HTML comment.",
                                        line, character + i);
                                }
                                if (!nextLine()) {
                                    errorAt("Unclosed HTML comment.", l, c);
                                }
                            }
                            l = s.indexOf('<!');
                            if (l >= 0 && l < i) {
                                errorAt("Nested HTML comment.",
                                    line, character + l);
                            }
                            character += i;
                            if (s[i + 2] !== '>') {
                                errorAt("Expected -->.", line, character);
                            }
                            character += 3;
                            s = s.slice(i + 3);
                            break;
                        case '#':
                            if (xmode === 'html' || xmode === 'styleproperty') {
                                for (;;) {
                                    c = s.charAt(0);
                                    if ((c < '0' || c > '9') &&
                                            (c < 'a' || c > 'f') &&
                                            (c < 'A' || c > 'F')) {
                                        break;
                                    }
                                    character += 1;
                                    s = s.substr(1);
                                    t += c;
                                }
                                if (t.length !== 4 && t.length !== 7) {
                                    warningAt("Bad hex color '{a}'.", line,
                                        from + l, t);
                                }
                                return it('(color)', t);
                            }
                            return it('(punctuator)', t);
                        default:
                            if (xmode === 'outer' && c === '&') {
                                character += 1;
                                s = s.substr(1);
                                for (;;) {
                                    c = s.charAt(0);
                                    character += 1;
                                    s = s.substr(1);
                                    if (c === ';') {
                                        break;
                                    }
                                    if (!((c >= '0' && c <= '9') ||
                                            (c >= 'a' && c <= 'z') ||
                                            c === '#')) {
                                        errorAt("Bad entity", line, from + l,
                                        character);
                                    }
                                }
                                break;
                            }
                            return it('(punctuator)', t);
                        }
                    }
                }
            }
        };
    }());


    function addlabel(t, type) {

        if (option.safe && funct['(global)'] &&
                typeof predefined[t] !== 'boolean') {
            warning('ADsafe global: ' + t + '.', token);
        } else if (t === 'hasOwnProperty') {
            warning("'hasOwnProperty' is a really bad name.");
        }

// Define t in the current function in the current scope.

        if (is_own(funct, t) && !funct['(global)']) {
            warning(funct[t] === true ?
                "'{a}' was used before it was defined." :
                "'{a}' is already defined.",
                nexttoken, t);
        }
        funct[t] = type;
        if (funct['(global)']) {
            global[t] = funct;
            if (is_own(implied, t)) {
                warning("'{a}' was used before it was defined.", nexttoken, t);
                delete implied[t];
            }
        } else {
            scope[t] = funct;
        }
    }


    function doOption() {
        var b, obj, filter, o = nexttoken.value, t, v;
        switch (o) {
        case '*/':
            error("Unbegun comment.");
            break;
        case '/*members':
        case '/*member':
            o = '/*members';
            if (!membersOnly) {
                membersOnly = {};
            }
            obj = membersOnly;
            break;
        case '/*jslint':
            if (option.safe) {
                warning("ADsafe restriction.");
            }
            obj = option;
            filter = boolOptions;
            break;
        case '/*global':
            if (option.safe) {
                warning("ADsafe restriction.");
            }
            obj = predefined;
            break;
        default:
        }
        t = lex.token();
loop:   for (;;) {
            for (;;) {
                if (t.type === 'special' && t.value === '*/') {
                    break loop;
                }
                if (t.id !== '(endline)' && t.id !== ',') {
                    break;
                }
                t = lex.token();
            }
            if (t.type !== '(string)' && t.type !== '(identifier)' &&
                    o !== '/*members') {
                error("Bad option.", t);
            }
            v = lex.token();
            if (v.id === ':') {
                v = lex.token();
                if (obj === membersOnly) {
                    error("Expected '{a}' and instead saw '{b}'.",
                            t, '*/', ':');
                }
                if (t.value === 'indent' && o === '/*jslint') {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                            Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                                v, v.value);
                    }
                    obj.white = true;
                    obj.indent = b;
                } else if (t.value === 'maxerr' && o === '/*jslint') {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                            Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                                v, v.value);
                    }
                    obj.maxerr = b;
                } else if (t.value === 'maxlen' && o === '/*jslint') {
                    b = +v.value;
                    if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
                            Math.floor(b) !== b) {
                        error("Expected a small integer and instead saw '{a}'.",
                                v, v.value);
                    }
                    obj.maxlen = b;
                } else if (v.value === 'true') {
                    obj[t.value] = true;
                } else if (v.value === 'false') {
                    obj[t.value] = false;
                } else {
                    error("Bad option value.", v);
                }
                t = lex.token();
            } else {
                if (o === '/*jslint') {
                    error("Missing option value.", t);
                }
                obj[t.value] = false;
                t = v;
            }
        }
        if (filter) {
            assume();
        }
    }


// We need a peek function. If it has an argument, it peeks that much farther
// ahead. It is used to distinguish
//     for ( var i in ...
// from
//     for ( var i = ...

    function peek(p) {
        var i = p || 0, j = 0, t;

        while (j <= i) {
            t = lookahead[j];
            if (!t) {
                t = lookahead[j] = lex.token();
            }
            j += 1;
        }
        return t;
    }



// Produce the next token. It looks for programming errors.

    function advance(id, t) {
        switch (token.id) {
        case '(number)':
            if (nexttoken.id === '.') {
                warning(
"A dot following a number can be confused with a decimal point.", token);
            }
            break;
        case '-':
            if (nexttoken.id === '-' || nexttoken.id === '--') {
                warning("Confusing minusses.");
            }
            break;
        case '+':
            if (nexttoken.id === '+' || nexttoken.id === '++') {
                warning("Confusing plusses.");
            }
            break;
        }
        if (token.type === '(string)' || token.identifier) {
            anonname = token.value;
        }

        if (id && nexttoken.id !== id) {
            if (t) {
                if (nexttoken.id === '(end)') {
                    warning("Unmatched '{a}'.", t, t.id);
                } else {
                    warning(
"Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
                            nexttoken, id, t.id, t.line, nexttoken.value);
                }
            } else if (nexttoken.type !== '(identifier)' ||
                            nexttoken.value !== id) {
                warning("Expected '{a}' and instead saw '{b}'.",
                        nexttoken, id, nexttoken.value);
            }
        }
        prevtoken = token;
        token = nexttoken;
        for (;;) {
            nexttoken = lookahead.shift() || lex.token();
            if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
                return;
            }
            if (nexttoken.type === 'special') {
                doOption();
            } else {
                if (nexttoken.id !== '(endline)') {
                    break;
                }
            }
        }
    }


// This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
// is looking for ad hoc lint patterns. We add to Pratt's model .fud, which is
// like nud except that it is only used on the first token of a statement.
// Having .fud makes it much easier to define JavaScript. I retained Pratt's
// nomenclature.

// .nud     Null denotation
// .fud     First null denotation
// .led     Left denotation
//  lbp     Left binding power
//  rbp     Right binding power

// They are key to the parsing method called Top Down Operator Precedence.

    function parse(rbp, initial) {
        var left;
        if (nexttoken.id === '(end)') {
            error("Unexpected early end of program.", token);
        }
        advance();
        if (option.safe && typeof predefined[token.value] === 'boolean' &&
                (nexttoken.id !== '(' && nexttoken.id !== '.')) {
            warning('ADsafe violation.', token);
        }
        if (initial) {
            anonname = 'anonymous';
            funct['(verb)'] = token.value;
        }
        if (initial === true && token.fud) {
            left = token.fud();
        } else {
            if (token.nud) {
                left = token.nud();
            } else {
                if (nexttoken.type === '(number)' && token.id === '.') {
                    warning(
"A leading decimal point can be confused with a dot: '.{a}'.",
                            token, nexttoken.value);
                    advance();
                    return token;
                } else {
                    error("Expected an identifier and instead saw '{a}'.",
                            token, token.id);
                }
            }
            while (rbp < nexttoken.lbp) {
                advance();
                if (token.led) {
                    left = token.led(left);
                } else {
                    error("Expected an operator and instead saw '{a}'.",
                        token, token.id);
                }
            }
        }
        return left;
    }


// Functions for conformance of style.

    function adjacent(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (option.white || xmode === 'styleproperty' || xmode === 'style') {
            if (left.character !== right.from && left.line === right.line) {
                warning("Unexpected space after '{a}'.", right, left.value);
            }
        }
    }

    function nobreak(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (left.character !== right.from || left.line !== right.line) {
            warning("Unexpected space before '{a}'.", right, right.value);
        }
    }

    function nospace(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (option.white && !left.comment) {
            if (left.line === right.line) {
                adjacent(left, right);
            }
        }
    }


    function nonadjacent(left, right) {
        if (option.white) {
            left = left || token;
            right = right || nexttoken;
            if (left.line === right.line && left.character === right.from) {
                warning("Missing space after '{a}'.",
                        nexttoken, left.value);
            }
        }
    }

    function nobreaknonadjacent(left, right) {
        left = left || token;
        right = right || nexttoken;
        if (!option.laxbreak && left.line !== right.line) {
            warning("Bad line breaking before '{a}'.", right, right.id);
        } else if (option.white) {
            left = left || token;
            right = right || nexttoken;
            if (left.character === right.from) {
                warning("Missing space after '{a}'.",
                        nexttoken, left.value);
            }
        }
    }

    function indentation(bias) {
        var i;
        if (option.white && nexttoken.id !== '(end)') {
            i = indent + (bias || 0);
            if (nexttoken.from !== i) {
                warning(
"Expected '{a}' to have an indentation at {b} instead at {c}.",
                        nexttoken, nexttoken.value, i, nexttoken.from);
            }
        }
    }

    function nolinebreak(t) {
        t = t || token;
        if (t.line !== nexttoken.line) {
            warning("Line breaking error '{a}'.", t, t.value);
        }
    }


    function comma() {
        if (token.line !== nexttoken.line) {
            if (!option.laxbreak) {
                warning("Bad line breaking before '{a}'.", token, nexttoken.id);
            }
        } else if (token.character !== nexttoken.from && option.white) {
            warning("Unexpected space after '{a}'.", nexttoken, token.value);
        }
        advance(',');
        nonadjacent(token, nexttoken);
    }


// Functional constructors for making the symbols that will be inherited by
// tokens.

    function symbol(s, p) {
        var x = syntax[s];
        if (!x || typeof x !== 'object') {
            syntax[s] = x = {
                id: s,
                lbp: p,
                value: s
            };
        }
        return x;
    }


    function delim(s) {
        return symbol(s, 0);
    }


    function stmt(s, f) {
        var x = delim(s);
        x.identifier = x.reserved = true;
        x.fud = f;
        return x;
    }


    function blockstmt(s, f) {
        var x = stmt(s, f);
        x.block = true;
        return x;
    }


    function reserveName(x) {
        var c = x.id.charAt(0);
        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
            x.identifier = x.reserved = true;
        }
        return x;
    }


    function prefix(s, f) {
        var x = symbol(s, 150);
        reserveName(x);
        x.nud = (typeof f === 'function') ? f : function () {
            this.right = parse(150);
            this.arity = 'unary';
            if (this.id === '++' || this.id === '--') {
                if (option.plusplus) {
                    warning("Unexpected use of '{a}'.", this, this.id);
                } else if ((!this.right.identifier || this.right.reserved) &&
                        this.right.id !== '.' && this.right.id !== '[') {
                    warning("Bad operand.", this);
                }
            }
            return this;
        };
        return x;
    }


    function type(s, f) {
        var x = delim(s);
        x.type = s;
        x.nud = f;
        return x;
    }


    function reserve(s, f) {
        var x = type(s, f);
        x.identifier = x.reserved = true;
        return x;
    }


    function reservevar(s, v) {
        return reserve(s, function () {
            if (typeof v === 'function') {
                v(this);
            }
            return this;
        });
    }


    function infix(s, f, p, w) {
        var x = symbol(s, p);
        reserveName(x);
        x.led = function (left) {
            if (!w) {
                nobreaknonadjacent(prevtoken, token);
                nonadjacent(token, nexttoken);
            }
            if (typeof f === 'function') {
                return f(left, this);
            } else {
                this.left = left;
                this.right = parse(p);
                return this;
            }
        };
        return x;
    }


    function relation(s, f) {
        var x = symbol(s, 100);
        x.led = function (left) {
            nobreaknonadjacent(prevtoken, token);
            nonadjacent(token, nexttoken);
            var right = parse(100);
            if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {
                warning("Use the isNaN function to compare with NaN.", this);
            } else if (f) {
                f.apply(this, [left, right]);
            }
            if (left.id === '!') {
                warning("Confusing use of '{a}'.", left, '!');
            }
            if (right.id === '!') {
                warning("Confusing use of '{a}'.", left, '!');
            }
            this.left = left;
            this.right = right;
            return this;
        };
        return x;
    }


    function isPoorRelation(node) {
        return node &&
              ((node.type === '(number)' && +node.value === 0) ||
               (node.type === '(string)' && node.value === '') ||
                node.type === 'true' ||
                node.type === 'false' ||
                node.type === 'undefined' ||
                node.type === 'null');
    }


    function assignop(s, f) {
        symbol(s, 20).exps = true;
        return infix(s, function (left, that) {
            var l;
            that.left = left;
            if (predefined[left.value] === false &&
                    scope[left.value]['(global)'] === true) {
                warning("Read only.", left);
            } else if (left['function']) {
                warning("'{a}' is a function.", left, left.value);
            }
            if (option.safe) {
                l = left;
                do {
                    if (typeof predefined[l.value] === 'boolean') {
                        warning('ADsafe violation.', l);
                    }
                    l = l.left;
                } while (l);
            }
            if (left) {
                if (left.id === '.' || left.id === '[') {
                    if (!left.left || left.left.value === 'arguments') {
                        warning('Bad assignment.', that);
                    }
                    that.right = parse(19);
                    return that;
                } else if (left.identifier && !left.reserved) {
                    if (funct[left.value] === 'exception') {
                        warning("Do not assign to the exception parameter.", left);
                    }
                    that.right = parse(19);
                    return that;
                }
                if (left === syntax['function']) {
                    warning(
"Expected an identifier in an assignment and instead saw a function invocation.",
                                token);
                }
            }
            error("Bad assignment.", that);
        }, 20);
    }


    function bitwise(s, f, p) {
        var x = symbol(s, p);
        reserveName(x);
        x.led = (typeof f === 'function') ? f : function (left) {
            if (option.bitwise) {
                warning("Unexpected use of '{a}'.", this, this.id);
            }
            this.left = left;
            this.right = parse(p);
            return this;
        };
        return x;
    }


    function bitwiseassignop(s) {
        symbol(s, 20).exps = true;
        return infix(s, function (left, that) {
            if (option.bitwise) {
                warning("Unexpected use of '{a}'.", that, that.id);
            }
            nonadjacent(prevtoken, token);
            nonadjacent(token, nexttoken);
            if (left) {
                if (left.id === '.' || left.id === '[' ||
                        (left.identifier && !left.reserved)) {
                    parse(19);
                    return that;
                }
                if (left === syntax['function']) {
                    warning(
"Expected an identifier in an assignment, and instead saw a function invocation.",
                                token);
                }
                return that;
            }
            error("Bad assignment.", that);
        }, 20);
    }


    function suffix(s, f) {
        var x = symbol(s, 150);
        x.led = function (left) {
            if (option.plusplus) {
                warning("Unexpected use of '{a}'.", this, this.id);
            } else if ((!left.identifier || left.reserved) &&
                    left.id !== '.' && left.id !== '[') {
                warning("Bad operand.", this);
            }
            this.left = left;
            return this;
        };
        return x;
    }


    function optionalidentifier() {
        if (nexttoken.identifier) {
            advance();
            if (option.safe && banned[token.value]) {
                warning("ADsafe violation: '{a}'.", token, token.value);
            } else if (token.reserved && !option.es5) {
                warning("Expected an identifier and instead saw '{a}' (a reserved word).",
                        token, token.id);
            }
            return token.value;
        }
    }


    function identifier() {
        var i = optionalidentifier();
        if (i) {
            return i;
        }
        if (token.id === 'function' && nexttoken.id === '(') {
            warning("Missing name in function statement.");
        } else {
            error("Expected an identifier and instead saw '{a}'.",
                    nexttoken, nexttoken.value);
        }
    }


    function reachable(s) {
        var i = 0, t;
        if (nexttoken.id !== ';' || noreach) {
            return;
        }
        for (;;) {
            t = peek(i);
            if (t.reach) {
                return;
            }
            if (t.id !== '(endline)') {
                if (t.id === 'function') {
                    warning(
"Inner functions should be listed at the top of the outer function.", t);
                    break;
                }
                warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
                break;
            }
            i += 1;
        }
    }


    function statement(noindent) {
        var i = indent, r, s = scope, t = nexttoken;

// We don't like the empty statement.

        if (t.id === ';') {
            warning("Unnecessary semicolon.", t);
            advance(';');
            return;
        }

// Is this a labelled statement?

        if (t.identifier && !t.reserved && peek().id === ':') {
            advance();
            advance(':');
            scope = Object.create(s);
            addlabel(t.value, 'label');
            if (!nexttoken.labelled) {
                warning("Label '{a}' on {b} statement.",
                        nexttoken, t.value, nexttoken.value);
            }
            if (jx.test(t.value + ':')) {
                warning("Label '{a}' looks like a javascript url.",
                        t, t.value);
            }
            nexttoken.label = t.value;
            t = nexttoken;
        }

// Parse the statement.

        if (!noindent) {
            indentation();
        }
        r = parse(0, true);

// Look for the final semicolon.

        if (!t.block) {
            if (!r || !r.exps) {
                warning(
"Expected an assignment or function call and instead saw an expression.",
                        token);
            } else if (r.id === '(' && r.left.id === 'new') {
                warning("Do not use 'new' for side effects.");
            }
            if (nexttoken.id !== ';') {
                warningAt("Missing semicolon.", token.line,
                        token.from + token.value.length);
            } else {
                adjacent(token, nexttoken);
                advance(';');
                nonadjacent(token, nexttoken);
            }
        }

// Restore the indentation.

        indent = i;
        scope = s;
        return r;
    }


    function use_strict() {
        if (nexttoken.value === 'use strict') {
            advance();
            advance(';');
            strict_mode = true;
            option.newcap = true;
            option.undef = true;
            return true;
        } else {
            return false;
        }
    }


    function statements(begin) {
        var a = [], f, p;
        if (begin && !use_strict() && option.strict) {
            warning('Missing "use strict" statement.', nexttoken);
        }
        if (option.adsafe) {
            switch (begin) {
            case 'script':
                if (!adsafe_may) {
                    if (nexttoken.value !== 'ADSAFE' ||
                            peek(0).id !== '.' ||
                            (peek(1).value !== 'id' &&
                            peek(1).value !== 'go')) {
                        error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.',
                            nexttoken);
                    }
                }
                if (nexttoken.value === 'ADSAFE' &&
                        peek(0).id === '.' &&
                        peek(1).value === 'id') {
                    if (adsafe_may) {
                        error('ADsafe violation.', nexttoken);
                    }
                    advance('ADSAFE');
                    advance('.');
                    advance('id');
                    advance('(');
                    if (nexttoken.value !== adsafe_id) {
                        error('ADsafe violation: id does not match.', nexttoken);
                    }
                    advance('(string)');
                    advance(')');
                    advance(';');
                    adsafe_may = true;
                }
                break;
            case 'lib':
                if (nexttoken.value === 'ADSAFE') {
                    advance('ADSAFE');
                    advance('.');
                    advance('lib');
                    advance('(');
                    advance('(string)');
                    comma();
                    f = parse(0);
                    if (f.id !== 'function') {
                        error('The second argument to lib must be a function.', f);
                    }
                    p = f.funct['(params)'];
                    p = p && p.join(', ');
                    if (p && p !== 'lib') {
                        error("Expected '{a}' and instead saw '{b}'.",
                            f, '(lib)', '(' + p + ')');
                    }
                    advance(')');
                    advance(';');
                    return a;
                } else {
                    error("ADsafe lib violation.");
                }
            }
        }
        while (!nexttoken.reach && nexttoken.id !== '(end)') {
            if (nexttoken.id === ';') {
                warning("Unnecessary semicolon.");
                advance(';');
            } else {
                a.push(statement());
            }
        }
        return a;
    }


    function block(f) {
        var a, b = inblock, old_indent = indent, s = scope, t;
        inblock = f;
        scope = Object.create(scope);
        nonadjacent(token, nexttoken);
        t = nexttoken;
        if (nexttoken.id === '{') {
            advance('{');
            if (nexttoken.id !== '}' || token.line !== nexttoken.line) {
                indent += option.indent;
                while (!f && nexttoken.from > indent) {
                    indent += option.indent;
                }
                if (!f) {
                    use_strict();
                }
                a = statements();
                indent -= option.indent;
                indentation();
            }
            advance('}', t);
            indent = old_indent;
        } else {
            warning("Expected '{a}' and instead saw '{b}'.",
                    nexttoken, '{', nexttoken.value);
            noreach = true;
            a = [statement()];
            noreach = false;
        }
        funct['(verb)'] = null;
        scope = s;
        inblock = b;
        if (f && (!a || a.length === 0)) {
            warning("Empty block.");
        }
        return a;
    }


    function countMember(m) {
        if (membersOnly && typeof membersOnly[m] !== 'boolean') {
            warning("Unexpected /*member '{a}'.", token, m);
        }
        if (typeof member[m] === 'number') {
            member[m] += 1;
        } else {
            member[m] = 1;
        }
    }


    function note_implied(token) {
        var name = token.value, line = token.line, a = implied[name];
        if (typeof a === 'function') {
            a = false;
        }
        if (!a) {
            a = [line];
            implied[name] = a;
        } else if (a[a.length - 1] !== line) {
            a.push(line);
        }
    }


// CSS parsing.


    function cssName() {
        if (nexttoken.identifier) {
            advance();
            return true;
        }
    }


    function cssNumber() {
        if (nexttoken.id === '-') {
            advance('-');
            adjacent();
            nolinebreak();
        }
        if (nexttoken.type === '(number)') {
            advance('(number)');
            return true;
        }
    }


    function cssString() {
        if (nexttoken.type === '(string)') {
            advance();
            return true;
        }
    }


    function cssColor() {
        var i, number, value;
        if (nexttoken.identifier) {
            value = nexttoken.value;
            if (value === 'rgb' || value === 'rgba') {
                advance();
                advance('(');
                for (i = 0; i < 3; i += 1) {
                    if (i) {
                        advance(',');
                    }
                    number = nexttoken.value;
                    if (nexttoken.type !== '(number)' || number < 0) {
                        warning("Expected a positive number and instead saw '{a}'",
                            nexttoken, number);
                        advance();
                    } else {
                        advance();
                        if (nexttoken.id === '%') {
                            advance('%');
                            if (number > 100) {
                                warning("Expected a percentage and instead saw '{a}'",
                                    token, number);
                            }
                        } else {
                            if (number > 255) {
                                warning("Expected a small number and instead saw '{a}'",
                                    token, number);
                            }
                        }
                    }
                }
                if (value === 'rgba') {
                    advance(',');
                    number = +nexttoken.value;
                    if (nexttoken.type !== '(number)' || number < 0 || number > 1) {
                        warning("Expected a number between 0 and 1 and instead saw '{a}'",
                            nexttoken, number);
                    }
                    advance();
                    if (nexttoken.id === '%') {
                        warning("Unexpected '%'.");
                        advance('%');
                    }
                }
                advance(')');
                return true;
            } else if (cssColorData[nexttoken.value] === true) {
                advance();
                return true;
            }
        } else if (nexttoken.type === '(color)') {
            advance();
            return true;
        }
        return false;
    }


    function cssLength() {
        if (nexttoken.id === '-') {
            advance('-');
            adjacent();
            nolinebreak();
        }
        if (nexttoken.type === '(number)') {
            advance();
            if (nexttoken.type !== '(string)' &&
                    cssLengthData[nexttoken.value] === true) {
                adjacent();
                advance();
            } else if (+token.value !== 0) {
                warning("Expected a linear unit and instead saw '{a}'.",
                    nexttoken, nexttoken.value);
            }
            return true;
        }
        return false;
    }


    function cssLineHeight() {
        if (nexttoken.id === '-') {
            advance('-');
            adjacent();
        }
        if (nexttoken.type === '(number)') {
            advance();
            if (nexttoken.type !== '(string)' &&
                    cssLengthData[nexttoken.value] === true) {
                adjacent();
                advance();
            }
            return true;
        }
        return false;
    }


    function cssWidth() {
        if (nexttoken.identifier) {
            switch (nexttoken.value) {
            case 'thin':
            case 'medium':
            case 'thick':
                advance();
                return true;
            }
        } else {
            return cssLength();
        }
    }


    function cssMargin() {
        if (nexttoken.identifier) {
            if (nexttoken.value === 'auto') {
                advance();
                return true;
            }
        } else {
            return cssLength();
        }
    }

    function cssAttr() {
        if (nexttoken.identifier && nexttoken.value === 'attr') {
            advance();
            advance('(');
            if (!nexttoken.identifier) {
                warning("Expected a name and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
            }
            advance();
            advance(')');
            return true;
        }
        return false;
    }


    function cssCommaList() {
        while (nexttoken.id !== ';') {
            if (!cssName() && !cssString()) {
                warning("Expected a name and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
            }
            if (nexttoken.id !== ',') {
                return true;
            }
            comma();
        }
    }


    function cssCounter() {
        if (nexttoken.identifier && nexttoken.value === 'counter') {
            advance();
            advance('(');
            advance();
            if (nexttoken.id === ',') {
                comma();
                if (nexttoken.type !== '(string)') {
                    warning("Expected a string and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
                }
                advance();
            }
            advance(')');
            return true;
        }
        if (nexttoken.identifier && nexttoken.value === 'counters') {
            advance();
            advance('(');
            if (!nexttoken.identifier) {
                warning("Expected a name and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
            }
            advance();
            if (nexttoken.id === ',') {
                comma();
                if (nexttoken.type !== '(string)') {
                    warning("Expected a string and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
                }
                advance();
            }
            if (nexttoken.id === ',') {
                comma();
                if (nexttoken.type !== '(string)') {
                    warning("Expected a string and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
                }
                advance();
            }
            advance(')');
            return true;
        }
        return false;
    }


    function cssShape() {
        var i;
        if (nexttoken.identifier && nexttoken.value === 'rect') {
            advance();
            advance('(');
            for (i = 0; i < 4; i += 1) {
                if (!cssLength()) {
                    warning("Expected a number and instead saw '{a}'.",
                        nexttoken, nexttoken.value);
                    break;
                }
            }
            advance(')');
            return true;
        }
        return false;
    }


    function cssUrl() {
        var c, url;
        if (nexttoken.identifier && nexttoken.value === 'url') {
            nexttoken = lex.range('(', ')');
            url = nexttoken.value;
            c = url.charAt(0);
            if (c === '"' || c === '\'') {
                if (url.slice(-1) !== c) {
                    warning("Bad url string.");
                } else {
                    url = url.slice(1, -1);
                    if (url.indexOf(c) >= 0) {
                        warning("Bad url string.");
                    }
                }
            }
            if (!url) {
                warning("Missing url.");
            }
            advance();
            if (option.safe && ux.test(url)) {
                error("ADsafe URL violation.");
            }
            urls.push(url);
            return true;
        }
        return false;
    }


    cssAny = [cssUrl, function () {
        for (;;) {
            if (nexttoken.identifier) {
                switch (nexttoken.value.toLowerCase()) {
                case 'url':
                    cssUrl();
                    break;
                case 'expression':
                    warning("Unexpected expression '{a}'.",
                        nexttoken, nexttoken.value);
                    advance();
                    break;
                default:
                    advance();
                }
            } else {
                if (nexttoken.id === ';' || nexttoken.id === '!'  ||
                        nexttoken.id === '(end)' || nexttoken.id === '}') {
                    return true;
                }
                advance();
            }
        }
    }];


    cssBorderStyle = [
        'none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'ridge',
        'inset', 'outset'
    ];

    cssBreak = [
        'auto', 'always', 'avoid', 'left', 'right'
    ];

    cssOverflow = [
        'auto', 'hidden', 'scroll', 'visible'
    ];

    cssAttributeData = {
        background: [
            true, 'background-attachment', 'background-color',
            'background-image', 'background-position', 'background-repeat'
        ],
        'background-attachment': ['scroll', 'fixed'],
        'background-color': ['transparent', cssColor],
        'background-image': ['none', cssUrl],
        'background-position': [
            2, [cssLength, 'top', 'bottom', 'left', 'right', 'center']
        ],
        'background-repeat': [
            'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
        ],
        'border': [true, 'border-color', 'border-style', 'border-width'],
        'border-bottom': [
            true, 'border-bottom-color', 'border-bottom-style',
            'border-bottom-width'
        ],
        'border-bottom-color': cssColor,
        'border-bottom-style': cssBorderStyle,
        'border-bottom-width': cssWidth,
        'border-collapse': ['collapse', 'separate'],
        'border-color': ['transparent', 4, cssColor],
        'border-left': [
            true, 'border-left-color', 'border-left-style', 'border-left-width'
        ],
        'border-left-color': cssColor,
        'border-left-style': cssBorderStyle,
        'border-left-width': cssWidth,
        'border-right': [
            true, 'border-right-color', 'border-right-style',
            'border-right-width'
        ],
        'border-right-color': cssColor,
        'border-right-style': cssBorderStyle,
        'border-right-width': cssWidth,
        'border-spacing': [2, cssLength],
        'border-style': [4, cssBorderStyle],
        'border-top': [
            true, 'border-top-color', 'border-top-style', 'border-top-width'
        ],
        'border-top-color': cssColor,
        'border-top-style': cssBorderStyle,
        'border-top-width': cssWidth,
        'border-width': [4, cssWidth],
        bottom: [cssLength, 'auto'],
        'caption-side' : ['bottom', 'left', 'right', 'top'],
        clear: ['both', 'left', 'none', 'right'],
        clip: [cssShape, 'auto'],
        color: cssColor,
        content: [
            'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote',
            cssString, cssUrl, cssCounter, cssAttr
        ],
        'counter-increment': [
            cssName, 'none'
        ],
        'counter-reset': [
            cssName, 'none'
        ],
        cursor: [
            cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move',
            'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize',
            'se-resize', 'sw-resize', 'w-resize', 'text', 'wait'
        ],
        direction: ['ltr', 'rtl'],
        display: [
            'block', 'compact', 'inline', 'inline-block', 'inline-table',
            'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption',
            'table-cell', 'table-column', 'table-column-group',
            'table-footer-group', 'table-header-group', 'table-row',
            'table-row-group'
        ],
        'empty-cells': ['show', 'hide'],
        'float': ['left', 'none', 'right'],
        font: [
            'caption', 'icon', 'menu', 'message-box', 'small-caption',
            'status-bar', true, 'font-size', 'font-style', 'font-weight',
            'font-family'
        ],
        'font-family': cssCommaList,
        'font-size': [
            'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large',
            'xx-large', 'larger', 'smaller', cssLength
        ],
        'font-size-adjust': ['none', cssNumber],
        'font-stretch': [
            'normal', 'wider', 'narrower', 'ultra-condensed',
            'extra-condensed', 'condensed', 'semi-condensed',
            'semi-expanded', 'expanded', 'extra-expanded'
        ],
        'font-style': [
            'normal', 'italic', 'oblique'
        ],
        'font-variant': [
            'normal', 'small-caps'
        ],
        'font-weight': [
            'normal', 'bold', 'bolder', 'lighter', cssNumber
        ],
        height: [cssLength, 'auto'],
        left: [cssLength, 'auto'],
        'letter-spacing': ['normal', cssLength],
        'line-height': ['normal', cssLineHeight],
        'list-style': [
            true, 'list-style-image', 'list-style-position', 'list-style-type'
        ],
        'list-style-image': ['none', cssUrl],
        'list-style-position': ['inside', 'outside'],
        'list-style-type': [
            'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero',
            'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha',
            'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana',
            'hiragana-iroha', 'katakana-oroha', 'none'
        ],
        margin: [4, cssMargin],
        'margin-bottom': cssMargin,
        'margin-left': cssMargin,
        'margin-right': cssMargin,
        'margin-top': cssMargin,
        'marker-offset': [cssLength, 'auto'],
        'max-height': [cssLength, 'none'],
        'max-width': [cssLength, 'none'],
        'min-height': cssLength,
        'min-width': cssLength,
        opacity: cssNumber,
        outline: [true, 'outline-color', 'outline-style', 'outline-width'],
        'outline-color': ['invert', cssColor],
        'outline-style': [
            'dashed', 'dotted', 'double', 'groove', 'inset', 'none',
            'outset', 'ridge', 'solid'
        ],
        'outline-width': cssWidth,
        overflow: cssOverflow,
        'overflow-x': cssOverflow,
        'overflow-y': cssOverflow,
        padding: [4, cssLength],
        'padding-bottom': cssLength,
        'padding-left': cssLength,
        'padding-right': cssLength,
        'padding-top': cssLength,
        'page-break-after': cssBreak,
        'page-break-before': cssBreak,
        position: ['absolute', 'fixed', 'relative', 'static'],
        quotes: [8, cssString],
        right: [cssLength, 'auto'],
        'table-layout': ['auto', 'fixed'],
        'text-align': ['center', 'justify', 'left', 'right'],
        'text-decoration': [
            'none', 'underline', 'overline', 'line-through', 'blink'
        ],
        'text-indent': cssLength,
        'text-shadow': ['none', 4, [cssColor, cssLength]],
        'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'],
        top: [cssLength, 'auto'],
        'unicode-bidi': ['normal', 'embed', 'bidi-override'],
        'vertical-align': [
            'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle',
            'text-bottom', cssLength
        ],
        visibility: ['visible', 'hidden', 'collapse'],
        'white-space': [
            'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit'
        ],
        width: [cssLength, 'auto'],
        'word-spacing': ['normal', cssLength],
        'word-wrap': ['break-word', 'normal'],
        'z-index': ['auto', cssNumber]
    };

    function styleAttribute() {
        var v;
        while (nexttoken.id === '*' || nexttoken.id === '#' ||
                nexttoken.value === '_') {
            if (!option.css) {
                warning("Unexpected '{a}'.", nexttoken, nexttoken.value);
            }
            advance();
        }
        if (nexttoken.id === '-') {
            if (!option.css) {
                warning("Unexpected '{a}'.", nexttoken, nexttoken.value);
            }
            advance('-');
            if (!nexttoken.identifier) {
                warning(
"Expected a non-standard style attribute and instead saw '{a}'.",
                    nexttoken, nexttoken.value);
            }
            advance();
            return cssAny;
        } else {
            if (!nexttoken.identifier) {
                warning("Excepted a style attribute, and instead saw '{a}'.",
                    nexttoken, nexttoken.value);
            } else {
                if (is_own(cssAttributeData, nexttoken.value)) {
                    v = cssAttributeData[nexttoken.value];
                } else {
                    v = cssAny;
                    if (!option.css) {
                        warning("Unrecognized style attribute '{a}'.",
                                nexttoken, nexttoken.value);
                    }
                }
            }
            advance();
            return v;
        }
    }


    function styleValue(v) {
        var i = 0,
            n,
            once,
            match,
            round,
            start = 0,
            vi;
        switch (typeof v) {
        case 'function':
            return v();
        case 'string':
            if (nexttoken.identifier && nexttoken.value === v) {
                advance();
                return true;
            }
            return false;
        }
        for (;;) {
            if (i >= v.length) {
                return false;
            }
            vi = v[i];
            i += 1;
            if (vi === true) {
                break;
            } else if (typeof vi === 'number') {
                n = vi;
                vi = v[i];
                i += 1;
            } else {
                n = 1;
            }
            match = false;
            while (n > 0) {
                if (styleValue(vi)) {
                    match = true;
                    n -= 1;
                } else {
                    break;
                }
            }
            if (match) {
                return true;
            }
        }
        start = i;
        once = [];
        for (;;) {
            round = false;
            for (i = start; i < v.length; i += 1) {
                if (!once[i]) {
                    if (styleValue(cssAttributeData[v[i]])) {
                        match = true;
                        round = true;
                        once[i] = true;
                        break;
                    }
                }
            }
            if (!round) {
                return match;
            }
        }
    }

    function styleChild() {
        if (nexttoken.id === '(number)') {
            advance();
            if (nexttoken.value === 'n' && nexttoken.identifier) {
                adjacent();
                advance();
                if (nexttoken.id === '+') {
                    adjacent();
                    advance('+');
                    adjacent();
                    advance('(number)');
                }
            }
            return;
        } else {
            switch (nexttoken.value) {
            case 'odd':
            case 'even':
                if (nexttoken.identifier) {
                    advance();
                    return;
                }
            }
        }
        warning("Unexpected token '{a}'.", nexttoken, nexttoken.value);
    }

    function substyle() {
        var v;
        for (;;) {
            if (nexttoken.id === '}' || nexttoken.id === '(end)' ||
                    xquote && nexttoken.id === xquote) {
                return;
            }
            while (nexttoken.id === ';') {
                warning("Misplaced ';'.");
                advance(';');
            }
            v = styleAttribute();
            advance(':');
            if (nexttoken.identifier && nexttoken.value === 'inherit') {
                advance();
            } else {
                if (!styleValue(v)) {
                    warning("
Download .txt
gitextract_1o3rx81s/

├── .gitignore
├── LICENSE.txt
├── README.md
├── bower.json
├── build.xml
├── countdown.js
├── demo.html
├── demo.js
├── externs.js
├── index.html
├── lib/
│   ├── closure/
│   │   ├── COPYING
│   │   ├── README
│   │   ├── compiler.jar
│   │   ├── docs.url
│   │   └── download.url
│   ├── jslint/
│   │   ├── download.url
│   │   ├── jslint.js
│   │   └── rhino.js
│   ├── json/
│   │   └── json2.js
│   ├── qunit/
│   │   ├── MIT-LICENSE.txt
│   │   ├── download.url
│   │   ├── qunit.css
│   │   └── qunit.js
│   ├── raf/
│   │   └── requestAnimationFrame.js
│   └── rhino/
│       ├── LICENSE.txt
│       ├── download.url
│       └── js.jar
├── package.json
├── readme.html
└── test/
    ├── fonts/
    │   └── SIL Open Font License 1.1.txt
    ├── formatTests.js
    ├── lint.js
    ├── styles.css
    ├── timespanTests.js
    └── unit.html
Download .txt
SYMBOL INDEX (130 symbols across 5 files)

FILE: countdown.js
  function borrowMonths (line 198) | function borrowMonths(ref, shift) {
  function daysPerMonth (line 213) | function daysPerMonth(ref) {
  function daysPerYear (line 229) | function daysPerYear(ref) {
  function addToDate (line 248) | function addToDate(ts, date) {
  function plurality (line 450) | function plurality(value, unit) {
  function Timespan (line 470) | function Timespan() {}
  function rippleRounded (line 606) | function rippleRounded(ts, toUnit) {
  function fraction (line 703) | function fraction(ts, frac, fromUnit, toUnit, conversion, digits) {
  function fractional (line 732) | function fractional(ts, digits) {
  function ripple (line 772) | function ripple(ts) {
  function pruneUnits (line 882) | function pruneUnits(ts, units, max, digits) {
  function populate (line 1002) | function populate(ts, start, end, units, max, digits) {
  function getDelay (line 1050) | function getDelay(units) {
  function countdown (line 1091) | function countdown(start, end, units, max, digits) {

FILE: demo.js
  function byId (line 2) | function byId(id) {
  function formatTens (line 6) | function formatTens(n) {
  function update (line 32) | function update() {

FILE: lib/jslint/jslint.js
  function F (line 936) | function F() {}
  function is_own (line 946) | function is_own(object, name) {
  function combine (line 951) | function combine(t, o) {
  function assume (line 1009) | function assume() {
  function quit (line 1032) | function quit(m, l, ch) {
  function warning (line 1042) | function warning(m, t, a, b, c, d) {
  function warningAt (line 1073) | function warningAt(m, l, ch, a, b, c, d) {
  function error (line 1080) | function error(m, t, a, b, c, d) {
  function errorAt (line 1085) | function errorAt(m, l, ch, a, b, c, d) {
  function nextLine (line 1101) | function nextLine() {
  function it (line 1126) | function it(type, value) {
  function match (line 1217) | function match(x) {
  function string (line 1230) | function string(x) {
  function addlabel (line 1848) | function addlabel(t, type) {
  function doOption (line 1878) | function doOption() {
  function peek (line 1982) | function peek(p) {
  function advance (line 1999) | function advance(id, t) {
  function parse (line 2069) | function parse(rbp, initial) {
  function adjacent (line 2116) | function adjacent(left, right) {
  function nobreak (line 2126) | function nobreak(left, right) {
  function nospace (line 2134) | function nospace(left, right) {
  function nonadjacent (line 2145) | function nonadjacent(left, right) {
  function nobreaknonadjacent (line 2156) | function nobreaknonadjacent(left, right) {
  function indentation (line 2171) | function indentation(bias) {
  function nolinebreak (line 2183) | function nolinebreak(t) {
  function comma (line 2191) | function comma() {
  function symbol (line 2207) | function symbol(s, p) {
  function delim (line 2220) | function delim(s) {
  function stmt (line 2225) | function stmt(s, f) {
  function blockstmt (line 2233) | function blockstmt(s, f) {
  function reserveName (line 2240) | function reserveName(x) {
  function prefix (line 2249) | function prefix(s, f) {
  function type (line 2269) | function type(s, f) {
  function reserve (line 2277) | function reserve(s, f) {
  function reservevar (line 2284) | function reservevar(s, v) {
  function infix (line 2294) | function infix(s, f, p, w) {
  function relation (line 2314) | function relation(s, f) {
  function isPoorRelation (line 2339) | function isPoorRelation(node) {
  function assignop (line 2350) | function assignop(s, f) {
  function bitwise (line 2395) | function bitwise(s, f, p) {
  function bitwiseassignop (line 2410) | function bitwiseassignop(s) {
  function suffix (line 2436) | function suffix(s, f) {
  function optionalidentifier (line 2452) | function optionalidentifier() {
  function identifier (line 2466) | function identifier() {
  function reachable (line 2480) | function reachable(s) {
  function statement (line 2504) | function statement(noindent) {
  function use_strict (line 2569) | function use_strict() {
  function statements (line 2583) | function statements(begin) {
  function block (line 2657) | function block(f) {
  function countMember (line 2696) | function countMember(m) {
  function note_implied (line 2708) | function note_implied(token) {
  function cssName (line 2725) | function cssName() {
  function cssNumber (line 2733) | function cssNumber() {
  function cssString (line 2746) | function cssString() {
  function cssColor (line 2754) | function cssColor() {
  function cssLength (line 2813) | function cssLength() {
  function cssLineHeight (line 2835) | function cssLineHeight() {
  function cssWidth (line 2853) | function cssWidth() {
  function cssMargin (line 2868) | function cssMargin() {
  function cssAttr (line 2879) | function cssAttr() {
  function cssCommaList (line 2895) | function cssCommaList() {
  function cssCounter (line 2909) | function cssCounter() {
  function cssShape (line 2956) | function cssShape() {
  function cssUrl (line 2975) | function cssUrl() {
  function styleAttribute (line 3215) | function styleAttribute() {
  function styleValue (line 3257) | function styleValue(v) {
  function styleChild (line 3323) | function styleChild() {
  function substyle (line 3350) | function substyle() {
  function styleSelector (line 3390) | function styleSelector() {
  function stylePattern (line 3509) | function stylePattern() {
  function styles (line 3535) | function styles() {
  function doBegin (line 3569) | function doBegin(n) {
  function doAttribute (line 3595) | function doAttribute(n, a, v) {
  function doTag (line 3657) | function doTag(n, a) {
  function closetag (line 3779) | function closetag(n) {
  function html (line 3783) | function html() {
  function property_name (line 4614) | function property_name() {
  function functionparams (line 4635) | function functionparams() {
  function doFunction (line 4659) | function doFunction(i, statement) {
  function jsonValue (line 5244) | function jsonValue() {
  function is_array (line 5488) | function is_array(o) {
  function to_array (line 5492) | function to_array(o) {
  function detail (line 5594) | function detail(h, array) {

FILE: lib/json/json2.js
  function f (line 170) | function f(n) {
  function quote (line 211) | function quote(string) {
  function str (line 227) | function str(key, holder) {
  function walk (line 408) | function walk(holder, key) {

FILE: lib/qunit/qunit.js
  function run (line 228) | function run() {
  function F (line 437) | function F(){}
  function done (line 784) | function done() {
  function validTest (line 839) | function validTest( name ) {
  function sourceFromStacktrace (line 865) | function sourceFromStacktrace() {
  function escapeInnerText (line 883) | function escapeInnerText(s) {
  function synchronize (line 898) | function synchronize( callback ) {
  function process (line 906) | function process() {
  function saveGlobal (line 922) | function saveGlobal() {
  function checkPollution (line 932) | function checkPollution( name ) {
  function diff (line 948) | function diff( a, b ) {
  function fail (line 962) | function fail(message, exception, callback) {
  function extend (line 973) | function extend(a, b) {
  function addEvent (line 985) | function addEvent(elem, type, fn) {
  function id (line 995) | function id(name) {
  function registerLoggingCallback (line 1000) | function registerLoggingCallback(key){
  function runLoggingCallbacks (line 1007) | function runLoggingCallbacks(key, scope, args) {
  function bindCallbacks (line 1029) | function bindCallbacks(o, callbacks, args) {
  function useStrictEquality (line 1043) | function useStrictEquality(b, a) {
  function quote (line 1206) | function quote( str ) {
  function literal (line 1209) | function literal( o ) {
  function join (line 1212) | function join( pre, arr, post ) {
  function array (line 1222) | function array( arr, stack ) {
  function getText (line 1380) | function getText( elems ) {
  function inArray (line 1400) | function inArray( elem, array ) {
  function diff (line 1429) | function diff(o, n) {
Condensed preview — 35 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (491K chars).
[
  {
    "path": ".gitignore",
    "chars": 150,
    "preview": "\\.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\nan"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1105,
    "preview": "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 pe"
  },
  {
    "path": "README.md",
    "chars": 14851,
    "preview": "# [Countdown.js][1]\n\nA simple JavaScript API for producing an accurate, intuitive description of the timespan between tw"
  },
  {
    "path": "bower.json",
    "chars": 564,
    "preview": "{\n  \"name\": \"countdownjs\",\n  \"version\": \"2.6.1\",\n  \"description\": \"A simple JavaScript API for producing an accurate, in"
  },
  {
    "path": "build.xml",
    "chars": 1451,
    "preview": "<?xml version=\"1.0\"?>\r\n<project basedir=\".\" default=\"all\">\r\n\t<taskdef name=\"closure\" classname=\"com.google.javascript.j"
  },
  {
    "path": "countdown.js",
    "chars": 28455,
    "preview": "/*global window module */\n/**\n * @license countdown.js v2.6.1 http://countdownjs.org\n * Copyright (c)2006-2014 Stephen M"
  },
  {
    "path": "demo.html",
    "chars": 4229,
    "preview": "<!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"
  },
  {
    "path": "demo.js",
    "chars": 2204,
    "preview": "(function() {\n\tfunction byId(id) {\n\t\treturn document.getElementById(id);\n\t}\n\n\tfunction formatTens(n) {\n\t\t// format integ"
  },
  {
    "path": "externs.js",
    "chars": 166,
    "preview": "/**\n * @fileoverview External module declarations.\n * @externs\n */\n\n/**\n * @type {Object|undefined}\n */\nvar module;\n\n/**"
  },
  {
    "path": "index.html",
    "chars": 2629,
    "preview": "<!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/styl"
  },
  {
    "path": "lib/closure/COPYING",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "lib/closure/README",
    "chars": 6965,
    "preview": "/*\n * Copyright 2009 The Closure Compiler Authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "lib/closure/docs.url",
    "chars": 91,
    "preview": "[InternetShortcut]\r\nURL=http://code.google.com/closure/compiler/docs/js-for-compiler.html\r\n"
  },
  {
    "path": "lib/closure/download.url",
    "chars": 66,
    "preview": "[InternetShortcut]\r\nURL=http://code.google.com/closure/compiler/\r\n"
  },
  {
    "path": "lib/jslint/download.url",
    "chars": 57,
    "preview": "[InternetShortcut]\r\nURL=http://www.jslint.com/lint.html\r\n"
  },
  {
    "path": "lib/jslint/jslint.js",
    "chars": 201995,
    "preview": "// jslint.js\n// 2010-10-16\n\n/*\nCopyright (c) 2002 Douglas Crockford  (www.JSLint.com)\n\nPermission is hereby granted, fre"
  },
  {
    "path": "lib/jslint/rhino.js",
    "chars": 1177,
    "preview": "// rhino.js\n// 2009-09-11\n/*\nCopyright (c) 2002 Douglas Crockford  (www.JSLint.com) Rhino Edition\n*/\n\n// This is the Rhi"
  },
  {
    "path": "lib/json/json2.js",
    "chars": 17413,
    "preview": "/*\n    http://www.JSON.org/json2.js\n    2011-02-23\n\n    Public Domain.\n\n    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOU"
  },
  {
    "path": "lib/qunit/MIT-LICENSE.txt",
    "chars": 1073,
    "preview": "Copyright (c) 2010 John Resig, http://jquery.com\n\nPermission is hereby granted, free of charge, to any person obtaining\n"
  },
  {
    "path": "lib/qunit/download.url",
    "chars": 130,
    "preview": "[InternetShortcut]\r\nURL=http://github.com/jquery/qunit/\r\nIDList=\r\nHotKey=0\r\n[{000214A0-0000-0000-C000-000000000046}]\r\nPr"
  },
  {
    "path": "lib/qunit/qunit.css",
    "chars": 4509,
    "preview": "/**\n * QUnit - A JavaScript Unit Testing Framework\n *\n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2011 John Resi"
  },
  {
    "path": "lib/qunit/qunit.js",
    "chars": 39437,
    "preview": "/**\n * QUnit - A JavaScript Unit Testing Framework\n *\n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2011 John Resi"
  },
  {
    "path": "lib/raf/requestAnimationFrame.js",
    "chars": 1241,
    "preview": "// http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n// http://my.opera.com/emoller/blog/2011/12/20/r"
  },
  {
    "path": "lib/rhino/LICENSE.txt",
    "chars": 46817,
    "preview": "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."
  },
  {
    "path": "lib/rhino/download.url",
    "chars": 68,
    "preview": "[InternetShortcut]\r\nURL=http://www.mozilla.org/rhino/download.html\r\n"
  },
  {
    "path": "package.json",
    "chars": 666,
    "preview": "{\n  \"name\": \"countdown\",\n  \"version\": \"2.6.1\",\n  \"description\": \"A simple JavaScript API for producing an accurate, intu"
  },
  {
    "path": "readme.html",
    "chars": 18399,
    "preview": "<!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/styl"
  },
  {
    "path": "test/fonts/SIL Open Font License 1.1.txt",
    "chars": 4301,
    "preview": "This Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also a"
  },
  {
    "path": "test/formatTests.js",
    "chars": 23380,
    "preview": "try{\r\n\r\nmodule('Timespan.toString()');\r\n\r\ntest('Default empty label override', function() {\r\n\r\n\tvar input = countdown(0,"
  },
  {
    "path": "test/lint.js",
    "chars": 628,
    "preview": "load(\"lib/jslint/jslint.js\");\n\nvar src = readFile(\"countdown.js\");\n\nJSLINT(src, { browser: true, undef: true, eqeqeq: tr"
  },
  {
    "path": "test/styles.css",
    "chars": 7830,
    "preview": "article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}audio,canvas,video{display:inlin"
  },
  {
    "path": "test/timespanTests.js",
    "chars": 16737,
    "preview": "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 "
  },
  {
    "path": "test/unit.html",
    "chars": 1281,
    "preview": "<!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="
  }
]

// ... and 2 more files (download for full content)

About this extraction

This page contains the full source code of the mckamey/countdownjs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 35 files (450.6 KB), approximately 111.1k tokens, and a symbol index with 130 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!